QComboBox delegate: Best way to show the popup menu immediately?


  • Moderators

    Hi all,

    I've subclassed QStyledItemDelegate to provide a QComboBox editor (tutorial: http://qt-project.org/wiki/Combo_Boxes_in_Item_Views )

    The problem is, 3 clicks are needed to bring up the popup menu. We can reduce this to 2 clicks if the popup menu is shown as soon as the editor is created and shown.

    The cleanest way I can think of doing this is to use a single-shot timer to call showPopup() after creating the QComboBox. The timer is required because we need to wait for the QComboBox to be positioned + shown before calling showPopup(), or else the menu will pop up in the wrong place.

    @
    QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
    {
    Q_UNUSED(option);
    Q_UNUSED(index);

    auto cb = new QComboBox(parent);
    cb->addItems({"A", "B", "C"});
    
    // ASSUMPTION: The QComboBox will be shown in the correct
    // location before control returns to the event loop, thus
    // ensuring that that the timer will fire at the right time.
    QTimer::singleShot(0, [cb]
    {
        cb->showPopup();
    });
    return cb;
    

    }
    @

    Is there a nicer way to do this? I'm also looking for a way to modify the behaviour of Qt's default delegate, which creates a QComboBox if QAbstractItemModel::data() returns a Boolean value. It would be great if I didn't have to implement my own delegate just for this.

    Thanks in advance!


  • Moderators

    I don't know if it's nicer (has some ugliness to it too) but another way is to install the delegate as an event filter for the created widget and listen for FocusIn event.The upside is that it doesn't make any event loop related assumptions:
    @
    bool ComboBoxItemDelegate::eventFilter(QObject src, QEvent evt) {
    if(evt->type() == QEvent::FocusIn) {
    auto cb = qobject_cast<QComboBox
    >(src);
    if(cb) {
    cb->showPopup();
    //important to handle it only the first time, otherwise will result in
    //focus glitches
    cb->removeEventFilter(this);
    }
    }
    return QStyledItemDelegate::eventFilter(src, evt);
    }
    @
    Now the ugly part is that the createEditor() method is const so to install the filter you need a const_cast. Oh well, that's what they're for I guess :/
    @
    ...
    auto cb = new QComboBox(parent);
    cb->installEventFilter(const_cast<ComboBoxItemDelegate
    >(this));
    cb->addItems({"A", "B", "C"});
    ...
    @
    As for the second question - inside createEditor() you could call the base implementation if needed and treat the resulting widget similarly. I don't know how to do it without a custom delegate, sorry.


  • Moderators

    Thanks Chris!

    Your event filter technique looks useful for handling other kinds of interactions with editors too (e.g. dismissing the editor when the mouse leaves the cell). I'll play around with it.

    It looks like I'll have to do some extra work no matter what, but we usually survive that :D


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.