helloSystem / Menu

Global menu bar written in Qt
43 stars 14 forks source link

QCompleter: Automatically select the first search result #14

Closed probonopd closed 3 years ago

probonopd commented 3 years ago

Automatically select the first search result once there is only one result left, so that one can just press Enter.

How it is right now:

image

How it should be:

image

https://stackoverflow.com/q/17782277 did not work for me so far:

https://github.com/helloSystem/Menu/blob/aaf84275a9eb1c3aed494bc30b8665b2afe9440c/src/appmenuwidget.cpp#L177-L180

probonopd commented 3 years ago
// Select the first (or the only) result of the completer
// FIXME: This does not work yet. Why?
// Also see: https://github.com/helloSystem/Menu/issues/14
class AutoSelectFirstFilter : public QObject
{
public:
    explicit AutoSelectFirstFilter(QLineEdit *parent) : QObject(parent)
    {}

    bool eventFilter(QObject *obj, QEvent *e) override
    {
        QAbstractItemView* l = static_cast<QAbstractItemView*>(obj);
        // Try to automatically select the first match of the completer;
        if (e->type() == QEvent::KeyPress)
        {
            qDebug() << "probono: AutoSelectFirstFilter triggered";
            QModelIndex i = l->model()->index(0,0);
            if(i.isValid()) {
                l->selectionModel()->select(i, QItemSelectionModel::Select); // FIXME: This does not work yet. Why?
            }
        }

        return QObject::eventFilter(obj, e);
    }
};

Maybe @keshavbhatt has an idea why this is not working?

keshavbhatt commented 3 years ago

Hi, I have figured out a way to achieve this :

Check this out if it helps.

#include <QCompleter>
#include <QDebug>
#include <QAbstractItemView>
#include <QKeyEvent>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QStringList completionData = {"mpv","olivia","olivia2","vlc"};
    QCompleter * completer = new QCompleter(completionData,ui->lineEdit);
    completer->popup()->installEventFilter(this);
    ui->lineEdit->setCompleter(completer);
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    if(watched == ui->lineEdit->completer()->popup() && event->type() == QEvent::Show){
        QCompleter *completer = ui->lineEdit->completer();
        if(completer->completionCount() == 1){
            completer->setCurrentRow(0); // this is not changing the currenrow selection
            //so here's a work around to achieve the behaviour
            QKeyEvent *event = new QKeyEvent ( QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
            QCoreApplication::postEvent (ui->lineEdit->completer()->popup(), event);
        }
    }
    return QMainWindow::eventFilter(watched,event);
}
keshavbhatt commented 3 years ago

I suggest you to have the behavior like: When user input something in lineEdit the result completer pops up -> if the user hit Enter key with nothing selected -> first item action should be executed.(Similar to unity7 dash search);

For this, you need a little tweaking in the above code. Ping if you need any help.

probonopd commented 3 years ago

@keshavbhatt trying it out right now. Thank you so much for your help again!

probonopd commented 3 years ago

I only seem to get one initial QEvent::Show, and thereafter for each additional keystroke only QEvent::KeyPress. Hence I seemingly need to use QEvent::KeyPress instead of QEvent::Show.

But then, once I send the QEvent::KeyPress, then I get a QEvent::KeyPress event back, which in turn ends in an endless loop...

probonopd commented 3 years ago

@keshavbhatt As you can see, I get QEvent::Show only when I type the first character, but not on the subsequent ones. But at the time when I press the first character, there is clearly more than one choice left.

Peek 2020-12-03 20-14

Code:

https://github.com/helloSystem/Menu/blob/c59920667f9956acb6f8b515c8e5ccd3ad17d17c/src/appmenuwidget.cpp#L89-L123

keshavbhatt commented 3 years ago

I will look into this.

probonopd commented 3 years ago

Got it to work. Thank you very much @keshavbhatt

probonopd commented 3 years ago

Finally cracked it:

// completer->setCurrentRow(0); // This is not changing the current row selection, but the following does
QListView* l = static_cast<QListView*>(completer->popup());
QModelIndex idx = completer->completionModel()->index(0, 0,QModelIndex());
l->setCurrentIndex(idx);