c++qt

The cursor behavior is inconsistent after selecting an item in QComboBox using different methods


I customized a dialog that contains a QComboBox control m_box and a QListWidget control m_lst. The m_box supports search matching. If an item from the dropdown list of m_box is selected by directly clicking with the mouse, the cursor will not be displayed in m_box, as shown in the example below.

enter image description here

If an item from m_box is selected using the search matching method with the up and down keys and the Enter key, the cursor will also not be displayed in m_box, as shown in the example below.

enter image description here

However, if matching items are displayed through search matching and an item from the dropdown list is selected with the mouse, the cursor will then be displayed in m_box, as shown in the example below.

enter image description here

I hope for consistent behavior across all three methods: as long as the selected item exists in the candidate items of m_box, the cursor should not be displayed in m_box, unless the mouse is clicked on m_box, at which point the cursor should appear. Here’s a minimal reproducible example:

#include <QApplication>
#include <QDialog>
#include <QComboBox>
#include <QListWidget>
#include <QCompleter>
#include <QStringList>
#include <QVBoxLayout>
#include <QDebug>

class CustomDialog : public QDialog
{
    Q_OBJECT
public:
    CustomDialog(QWidget* parent = nullptr) : QDialog(parent)
    {
        QStringList items;
        items << "item1" << "item2";
        m_box = new QComboBox(this);
        m_box->setEditable(true);
        m_box->addItems(items);
        QCompleter* completer = new QCompleter(items, m_box);
        completer->setFilterMode(Qt::MatchContains);
        m_box->setCompleter(completer);

        m_lst = new QListWidget(this);
        m_lst->addItems(items);

        QVBoxLayout* layout = new QVBoxLayout(this);
        layout->addWidget(m_box);
        layout->addWidget(m_lst);

        connect(m_box, SIGNAL(activated(int)), this, SLOT(onSelChangedBox(int)));
    }

public slots:
    void onSelChangedBox(int iSel);

private:
    QComboBox* m_box;
    QListWidget* m_lst;
};

void CustomDialog::onSelChangedBox(int iSel)
{
    qDebug() << "clear focus of m_box";
    m_lst->setFocus();
}

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);

    CustomDialog dlg;
    dlg.setFixedSize(300, 200);
    dlg.show();

    return a.exec();
}

#include "main.moc"

Note: If a second QComboBox is added and the item is selected using the third method, then clicking on the second QComboBox will result in two cursors appearing. In fact, at this point, the focus is no longer on the first QComboBox, but the cursor is still displayed. The code and example are as follows:

enter image description here

#include <QApplication>
#include <QDialog>
#include <QComboBox>
#include <QListWidget>
#include <QCompleter>
#include <QStringList>
#include <QVBoxLayout>
#include <QDebug>

class CustomDialog : public QDialog
{
    Q_OBJECT
public:
    CustomDialog(QWidget* parent = nullptr) : QDialog(parent)
    {
        QStringList items;
        items << "item1" << "item2";
        m_box1 = new QComboBox(this);
        m_box1->setEditable(true);
        m_box1->addItems(items);
        QCompleter* completer1 = new QCompleter(items, m_box1);
        completer1->setFilterMode(Qt::MatchContains);
        m_box1->setCompleter(completer1);

        m_box2 = new QComboBox(this);
        m_box2->setEditable(true);
        m_box2->addItems(items);
        QCompleter* completer2 = new QCompleter(items, m_box2);
        completer2->setFilterMode(Qt::MatchContains);
        m_box2->setCompleter(completer2);

        m_lst = new QListWidget(this);
        m_lst->addItems(items);

        QVBoxLayout* layout = new QVBoxLayout(this);
        layout->addWidget(m_box1);
        layout->addWidget(m_box2);
        layout->addWidget(m_lst);

        connect(m_box1, SIGNAL(activated(int)), this, SLOT(onSelChangedBox(int)));
        connect(m_box2, SIGNAL(activated(int)), this, SLOT(onSelChangedBox(int)));
    }

public slots:
    void onSelChangedBox(int iSel);

private:
    QComboBox* m_box1;
    QComboBox* m_box2;
    QListWidget* m_lst;
};

void CustomDialog::onSelChangedBox(int iSel)
{
    qDebug() << "clear focus of m_box";
    m_lst->setFocus();
}

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);

    CustomDialog dlg;
    dlg.setFixedSize(500, 500);
    dlg.show();

    return a.exec();
}

#include "main.moc"

Solution

  • This seems to be a Qt bug. It may be a good idea to report this issue using the official Qt bug reporting system.

    The problem is that the focus out event is eaten by the popup QCompleter.

    Easiest work around is to allow Qt to close the popup normally by delaying the focus change (as already suggested by @musicamante in his comments):

    void CustomDialog::onSelChangedBox(int iSel)
    {
        qDebug() << "clear focus of m_box";
        QTimer::singleShot(0, this, [this](){m_lst->setFocus();});
    }
    

    Another work around is to use QCompleter::InlineCompletion instead of the popup completion.