FR: Option to allow ComboBox to reselect the same item

Presently, it is impossible to reselect the same item from a ComboBox - it is filtered out.

However, this can be a useful expected behavior, as in the case of reselecting a preset in order to blow out any edits. This has been discussed in multiple threads here.

Therefore, it seems it would be really easy to add this as an option. It’s basically a one line edit to the .cpp file and the inclusion of a bool variable.

Something like this:

ComboBox.h:

public:
	void setCanReselectSameItem(const bool state) { canReselectSameItem = state; }

private;
    bool canReselectSameItem = false;

ComboBox.cpp:

void ComboBox::setSelectedId (const int newItemId, const NotificationType notification)
{
    auto* item = getItemForId (newItemId);
    auto newItemText = item != nullptr ? item->text : String();
    
    //if (lastCurrentId != newItemId || label->getText() != newItemText)
    if (canReselectSameItem || (lastCurrentId != newItemId || label->getText() != newItemText))
    {
        label->setText (newItemText, dontSendNotification);
        lastCurrentId = newItemId;
        currentId = newItemId;

        repaint();  // for the benefit of the 'none selected' text

        sendChange (notification);
    }
}

Discussions:

I already used my two votes, but we would definitely like this, as well!

1 Like

OT, but afaik you should have 5 votes…?

Check on your User-Icon → Activity → votes

1 Like

Also OT, but related: at what point do we give up hope on requests that are using votes?

Most of mine are on requests that are 2 years old now.

I wish there was a public roadmap to see what the team is working on/will be tackling next, it could even just be a pinned topic with comments disabled that they update periodically.

3 Likes

These kind of questions are considered lèse-majesty and ignored generously instead of incarceration :wink:

3 Likes

@reuk Can we have this, please. It seems to be easy to implement.

2 Likes

Sorry to bump this but I’ve also just ran into this. It would be so easy to implement! I really see no reason not to and it certainly won’t break any existing code.

1 Like

Same here. I even think it would be a great idea if this is the default behavior when the user interacts with the ComboBox.

This works for me:

I made a derived class of juce::ComboBox and overrode showPopup(), and then edited comboBoxPopupMenuFinishedCallback() to allow for this behavior.

Unfortunately showPopup() uses a lot of private members which can’t be accessed so the function needs to be modified to avoid private members. I commented out the first two lines of showPopup() but it works fine for me.

I also made a few other edits but removed them for simplicity here:

Header:

#pragma once

namespace MusicDevs
{

class ComboBox : public juce::ComboBox
{
    
public:
    
    //==========================================================================
    ComboBox() {}
    
    ~ComboBox() { setLookAndFeel(nullptr); }
    
    //==========================================================================
    void setItemHeight(int newItemHeight) { itemHeight = newItemHeight; };
    
    void setMaximumNumberOfColumns(int newMaxNumColumns)
    {
        maximumNumColumns = newMaxNumColumns;
    }
    
    //==========================================================================
    /**
     *  @brief Pops up the combo box's list.
     *  This is virtual so that you can override it with your own custom popup
     *  mechanism if you need some really unusual behaviour.
     */
    void showPopup() override;
    
private:
    
    int itemHeight = 28;
    int maximumNumColumns = 1;
    
    //==========================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ComboBox);
};

} // namespace MusicDevs

CPP file:

#include "ComboBox.h"

namespace MusicDevs
{

//==============================================================================
static void comboBoxPopupMenuFinishedCallback (int result, ComboBox* combo)
{
    if (combo != nullptr)
    {
        combo->hidePopup();
        
        if (result == combo->getSelectedId())
        {
            combo->handleAsyncUpdate();
        }
        
        combo->setSelectedId(result);
    }
}

//==============================================================================
void ComboBox::showPopup()
{
    // if (! menuActive)
    //     menuActive = true;

    auto menu = getRootMenu();
    if (menu->getNumItems() > 0)
    {
        auto selectedId = getSelectedId();
        for (juce::PopupMenu::MenuItemIterator iterator (*menu, true);
             iterator.next();)
        {
            auto& item = iterator.getItem();
            if (item.itemID != 0)
                item.isTicked = (item.itemID == selectedId);
        }
    }
    else
    {
        menu->addItem (1, getTextWhenNothingSelected(), false, false);
    }
    auto& lf = getLookAndFeel();
    menu->setLookAndFeel (&lf);
    menu->showMenuAsync (juce::PopupMenu::Options()
                         .withTargetComponent (this)
                         .withItemThatMustBeVisible(getSelectedId())
                         .withMinimumWidth(getWidth())
                         .withMaximumNumColumns(maximumNumColumns)
                         .withStandardItemHeight(itemHeight),
        juce::ModalCallbackFunction::forComponent (
            comboBoxPopupMenuFinishedCallback, this));
}
//==============================================================================

} // namespace MusicDevs

Example:
ezgif-4-33c61ffb94

1 Like