6

I am seeing a problem while restoring QMainWindow state having QCombobox in floating toolbar. After restoring floating toolbar, my QCombobox is not able to get focus until i click on toolbar handle and move it. Following is gif showing problem, Using QT 5.13. enter image description here

File floating_toolbar.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = floating_toolbar
TEMPLATE = app


DEFINES += QT_DEPRECATED_WARNINGS

 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        mainwindow.cpp

HEADERS += \
        mainwindow.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

File : main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

File : mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    void closeEvent(QCloseEvent *event);
    void readSettings();
    bool eventFilter(QObject* xObj, QEvent* xEvent);
    ~MainWindow();

    public slots:
    void mCheck();
};

#endif // MAINWINDOW_H

File : mainwindow.cpp

#include "mainwindow.h"
#include <QToolBar>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLayout>
#include <QSettings>
#include <QEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QApplication>



MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QToolBar* lToolbar = new QToolBar(this);
    QComboBox* lComobox = new QComboBox(this);
    lComobox->setEditable(true);

    lToolbar->setWindowTitle("MyToolBar");
    lToolbar->setObjectName("NiceBaby");
    lToolbar->addWidget(lComobox);
    //lToolbar->addAction("check", lComobox, SLOT(clearEditText()));


    addToolBar(lToolbar);
    lToolbar->installEventFilter(this);
    readSettings();


}

void MainWindow::mCheck()
{

}
void MainWindow::closeEvent(QCloseEvent *event)
{
    QSettings settings("MyCompany", "MyApp");
    settings.setValue("windowState", saveState());
    QMainWindow::closeEvent(event);
}
void MainWindow::readSettings()
{
    QSettings settings("MyCompany", "MyApp");
    restoreState(settings.value("windowState").toByteArray());
}

MainWindow::~MainWindow()
{

}

bool MainWindow::eventFilter(QObject* xObj, QEvent* xEvent)
{
    //qDebug()<<xEvent->type();

    return QMainWindow::eventFilter(xObj, xEvent);
}
SaurabhS
  • 633
  • 1
  • 7
  • 18
  • Which distro are you using? Gnome or KDE? – KcFnMi Sep 02 '19 at 10:54
  • 1
    Its KDE on RHEL6.5 – SaurabhS Sep 02 '19 at 15:24
  • Please let me know if same happens on other distros i.e. Ubuntu. And it might help if you post the link to download the exact same RHEL6.5 you are using. – KcFnMi Sep 07 '19 at 03:31
  • I idea that occured to me was to simulate a click, more on https://stackoverflow.com/questions/35777414/how-can-i-simulate-mouse-clicks-by-posting-events-to-the-qt-event-system, did you tried something like that? – KcFnMi Sep 07 '19 at 03:39
  • Could you post your MCVE code? Looks like maybe a bug... and QWidgets in a toolbar can act pretty strangely sometimes, depending on platform. The `QToolBar` code is a bit convoluted due interactions with `QMainWindow` and platform integration requirements. Looks like something is stealing the focus until the toolbar gets "activated" (or something) by clicking on the handle. – Maxim Paperno Sep 08 '19 at 12:26
  • I tried on Ubuntu 16.04.1 LTS , Same behavior. – SaurabhS Sep 10 '19 at 07:41
  • Confirmed also in Linux Mint 19.2 (Ubuntu 18.4 base I believe). `Linux-RHEL_7_4-X86_64`. Tested Qt 5.13.1, 5.12.4, and 5.7.1. Not an issue in Windows, with either MSVC or MinGW. Seems to be pretty clearly a focus issue -- eg. the context menu still works and you can paste text into the box, and even select text. But if you try typing the focus is elsewhere. I put a `QLineEdit` into the main window and it keeps focus instead... you highlight text in the toolbar combobox and start typing and the input goes into the QLineEdit inside the main window instead. – Maxim Paperno Sep 10 '19 at 22:16
  • Confirmed also in Arch Linux with KDE (kwin 5.16.4), QtCreator 4.9.2, Qt 5.13.0. Same behavior. – matteo martelli Sep 11 '19 at 13:08
  • @OMG, so do you not find my answer acceptable? No bounty, no acceptance... is there something I'm missing? Did you find a better solution? – Maxim Paperno Oct 01 '19 at 20:19
  • @MaximPaperno your workaround worked for me but unfortunately at that time bounty was expired. Anything i can do now? – SaurabhS Oct 03 '19 at 06:00
  • You could accept the answer. I don't care much about the points, but it does clearly indicate that the answer is the "one that helped most" (at least at the time) to any future visitors. Up to you, of course. https://stackoverflow.com/help/someone-answers (On a more personal level: there's no way to tell who voted on an answer, so an answer giver typically has no idea if the asker found the answer useful at all (or even looked at it) w/out some kind of acknowledgement, eg. acceptance or a comment, etc.) – Maxim Paperno Oct 08 '19 at 09:22

2 Answers2

2

OK, a workaround is to reset the window flags on the toolbar when it is first shown and is floating. I tracked this down by seeing what happens once a toolbar is dropped after being dragged (but not plugged into main window). (It calls setWindowState() and all that does in this situation is hide the toolbar, call updateWindowFlags(), and show it again).

This could be handled from the QMainWindow::showEvent() or from the eventFilter installed onto the QToolBar. I think it's simpler from the former.

UPDATE: This problem actually happens whenever the toolbar is first shown even if not at app startup, e.g. from the toggle view menu by the user once app starts. I updated the code below to fix that issue as well. And see notes below about another issue with minimizing the main window.

I added this to the MainWindow class from the MCVE:

  protected:
    void showEvent(QShowEvent *e) override {
      QMainWindow::showEvent(e);
#ifdef Q_OS_LINUX
      if (lToolbar->isFloating() 
          // remove the next condition and the toolsbar will get hidden the 2nd time main window is minimized.
          && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint)  
          ) {
        const bool vis = !lToolbar->isHidden();
        qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
        lToolbar->hide();
        lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
        if (vis)
          lToolbar->show();
#endif
    }

    QToolBar* lToolbar;  // Use this in MainWindow constructor to save the instance pointer.

I also noticed another issue with the initially-floating toolbar. When the main window is minimized, the toolbar doesn't get hidden but stays where it was on the screen. Regardless of what is in the toolbar (eg. no combo box, just QActions). This workaround could also sort-of address that issue (see code comment), but only the 2nd time the window is minimized. Needs a better workaround for the first minimize.

Can others confirm this? Potentially a larger issue than the editable combo and I'd be surprised if no one noticed before.

I guess this should be filed as a Qt bug either way.

UPDATE2: This version also fixes the minimize issue. I guess something happens after the QMainWindow::showEvent() that changes how the toolbar behaves. Which explains why the above workaround works only after the 1st minimize. So scheduling the toolbar "fix" for later works around that also.

class MainWindow : public QMainWindow
{
...
#ifdef Q_OS_LINUX
  protected:
    void showEvent(QShowEvent *e) override
    {
      QMainWindow::showEvent(e);
      if (lToolbar->isFloating() && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
        //  QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
        QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
        // If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
        //adjustToolbar();
      }
    }

  private slots:
    void adjustToolbar() const
    {
      const bool vis = !lToolbar->isHidden();
      qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
      lToolbar->hide();
      lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
      if (vis)
        lToolbar->show();
    }
#endif

  private:
    QToolBar* lToolbar;
};

ADDED: A QToolBar subclass which applies the workaround on its own, nothing special needed in the QMainWindow. The minimize fix still only works when the adjustToolbar() function is queued or if restoreState() is only called after show() (see code comments).

class ToolBar : public QToolBar
{
    Q_OBJECT
  public:
    using QToolBar::QToolBar;

#ifdef Q_OS_LINUX
  protected:
    void showEvent(QShowEvent *e) override
    {
      QToolBar::showEvent(e);
      if (isFloating() && windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
        //  QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
        QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
        // If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
        //adjustToolbar();
      }
    }

  private slots:
    void adjustToolbar()
    {
      const bool vis = !isHidden();
      hide();
      setWindowFlag(Qt::X11BypassWindowManagerHint, false);
      if (vis)
        show();
    }
#endif
};

UPDATE3: The minimizing issue also exists with floating QDockWidget if the QMainWindow state is restored before it is shown. In fact with "older" Qt versions the floating widget doesn't show up at all (doesn't with <= 5.9.5 but does with >= 5.12.4, don't have anything in between to try ATM). So the proper approach is to show() the main window first and then restoreState(). Unfortunately this doesn't seem to work for QToolBar.

UPDATE4: Filed as QTBUG-78293

Maxim Paperno
  • 4,485
  • 2
  • 18
  • 22
0

It seems to work normally on macOS:

enter image description here

KcFnMi
  • 5,516
  • 10
  • 62
  • 136