student
student

Reputation: 380

QComboBox edit LineEdit while PopUp is active

I have a QComboBox filled with some data. I wish to edit the lineEdit of the comboBox and when I do that to make the comboBox display his pop up while I am editing. The problem is I lost the focus of the lineEdit and I can write only one letter at a time.

This is what I am doing at a trivial level:

ui->comboBox->addItem("This");
ui->comboBox->addItem("is");
ui->comboBox->addItem("a");
ui->comboBox->addItem("comboBox");

ui->comboBox->setEditable(true);
connect(ui->comboBox, SIGNAL(currentTextChanged(QString)), this, SLOT(PrintTextLineEdit(QString)));

void MainWindow::PrintTextLineEdit(QString str)
{
  ui->comboBox->showPopup();
  ui->comboBox->lineEdit()->setFocus();
}

Also if I use the blockSignal on the lineEdit while I show the pop up is useless. Any suggestions?

EDIT

It seems I need to provide some extra detail. I need to be able to write an entire word at a time without to lose focus when I use ui->comboBox->showPopUp() in the currentTextChanged signal.

Or to put it simple: the cursor do not need to disappear from the QLineEdit after the signal is emitted and the popUp is shown.

Upvotes: 3

Views: 4867

Answers (5)

m7913d
m7913d

Reputation: 11064

Using the QCompleter feature of QLineEdit may be a better / less hacky solution:

QStringListModel *model; // or another QAbstractItemModel
model->setStringList({"This", "is", "a", "combobox"});

QLineEdit *lineEdit;
lineEdit->setCompleter(new QCompleter(model, lineEdit));
lineEdit->completer()->setFilterMode(Qt::MatchContains); // set filter mode as desired

A popup will be shown once the user starts editing the line edit. The only disadvantage of this approach is the absence of the drop down icon to show all possible (unfiltered) items.

Upvotes: 1

walter
walter

Reputation: 11

Your problem is caused by grabKeyboard. The keyboard is grabbed by the popups, so the solution is easy, just set the edit to regrab the keyboard, using grabKeyBoard()

Upvotes: 1

Tomas
Tomas

Reputation: 2210

You need to derive your own combo box class from QComboBox and override the showPopup() virtual method to return the focus back to the line edit.

void CMyComboBox::showPopup()
{
  QComboBox::showPopup();

  // Put the focus back later, after all pending events are processed.
  QTimer::singleShot(0, [this](){ lineEdit()->setFocus(); });
}

As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system's event queue have been processed.

EDIT:

This works (although one could consider that as a hack):

class CMyComboBox : public QComboBox
{
  public:
    CMyComboBox(QWidget* parent) 
      : QComboBox(parent) 
    {
      view()->installEventFilter(this);
    }

    // Event filter forwards view key events to the line edit.
    bool eventFilter(QObject *watched, QEvent *event)
    {
      if (event->type() == QEvent::KeyPress)
      {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        QKeyEvent* newEvent = new QKeyEvent(keyEvent->type(), keyEvent->key(), keyEvent->modifiers(), 
                                            keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());

        QFocusEvent* focusEvent = new QFocusEvent(QEvent::FocusIn, Qt::OtherFocusReason);
        QCoreApplication::postEvent(lineEdit(), focusEvent);
        QCoreApplication::postEvent(lineEdit(), newEvent);
      }

      return false;
    }
};

But personally, I would probably use a separated QMenu for displaying word list, not the popup menu of combo box.

Upvotes: 2

Stormenet
Stormenet

Reputation: 26468

When I look in the code of QCombobox i see the following inside the showPopup code:

container->show();
container->updateScrollers();
view()->setFocus(); // <<-- focus command here

view()->scrollTo(view()->currentIndex(),
                 style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)
                         ? QAbstractItemView::PositionAtCenter
                         : QAbstractItemView::EnsureVisible);

So perhaps if you do the following (not tested here) you might get the result you want:

ui->comboBox->addItem("This");
ui->comboBox->addItem("is");
ui->comboBox->addItem("a");
ui->comboBox->addItem("comboBox");

ui->comboBox->setEditable(true);
ui->comboBox->view()->setFocusPolicy(Qt::FocusPolicy::NoFocus); // don't allow focusing of the view of the popup
connect(ui->comboBox, SIGNAL(currentTextChanged(QString)), this, SLOT(PrintTextLineEdit(QString)));

void MainWindow::PrintTextLineEdit(QString str)
{
  ui->comboBox->showPopup();
  ui->comboBox->lineEdit()->setFocus();
}

Worth a try.

Upvotes: 0

HeyYO
HeyYO

Reputation: 2073

Each combobox has a default QCompleter that can show completion options in the popup. I think you can achieve what you want by setting this completers mode to PopupCompletion.

ui->comboBox->completer()->setCompletionMode(QCompleter::PopupCompletion);

In this case, combobox will show matching selections while typing. If you want it to list all items of combobox, I think you should implement a custom QCompleter that matches all items regardless of what user types.

Upvotes: 6

Related Questions