How to set focus to non-modal QDialog when application becomes active?

Viewed 431

I have a MainWindow with a non-modal QDialog and a QPushButton just for the purpose of showing the dialog. When the application is minimised and maximised again, I want the dialog, if visible, to regain keyboard focus.

Here is the code:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT
    QDialog *dialog;
public:
    explicit MainWindow(QWidget *parent = nullptr);
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QPushButton>
#include <QDialog>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    dialog(new QDialog(this))
{
    QPushButton *button = new QPushButton("show dialog", this);
    connect(button, &QPushButton::clicked, dialog, &QDialog::show);
    setCentralWidget(button);
}

Sounds very simple but I have tried various approaches without any luck:

Approach 1: Listen to QApplication::applicationStateChanged() signal and set focus to dialog when application state becomes active.

Approach 2: Listen to dialog's show and window activate events and setting focus when triggered.

Approach 3: Listen to main window's show and window activate events and setting focus when triggered.

Here is the code with the above approaches implemented:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT
    QDialog *dialog;
public:
    explicit MainWindow(QWidget *parent = nullptr);
    virtual bool event(QEvent *event) override;
    virtual bool eventFilter(QObject *watched, QEvent *event) override;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QPushButton>
#include <QDialog>
#include <QEvent>
#include <QApplication>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    dialog(new QDialog(this))
{
    QPushButton *button = new QPushButton("show dialog", this);
    connect(button, &QPushButton::clicked, dialog, &QDialog::show);
    setCentralWidget(button);

    dialog->installEventFilter(this);

    //set focus on dialog when app becomes active
    connect(qApp, &QApplication::applicationStateChanged, this, [this](Qt::ApplicationState state){
        if (dialog->isVisible() && state == Qt::ApplicationActive) {
            dialog->setFocus();
        }
    });
}

bool MainWindow::event(QEvent *event)
{
    //set focus on dialog when main window is shown or activated
    if (event->type() == QEvent::Show || event->type() == QEvent::WindowActivate) {
        if (dialog->isVisible()) {
            dialog->setFocus();
        }
    }

    return QMainWindow::event(event);
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    //set focus on dialog when dialog is shown or activated
    if (watched == dialog) {
        if (event->type() == QEvent::Show || event->type() == QEvent::WindowActivate) {
            dialog->setFocus();
        }
    }

    return QMainWindow::eventFilter(watched, event);
}
1 Answers

An approach that works is listening to the QApplication::applicationStateChanged() signal and calling QWindow::requestActivate() when app becomes active:

connect(qApp, &QApplication::applicationStateChanged, this, [this](Qt::ApplicationState state){
    if (dialog->isVisible() && state == Qt::ApplicationActive) {
        if (dialog->windowHandle()) dialog->windowHandle()->requestActivate();
    }
});

Not sure why QWindow::requestActivate() works but QWidget::setFocus() doesn't when both functions are supposed to give keyboard focus, according to the docs. If anyone knows please comment below.

Related