Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-96063

Windows first shown with showMinimized() result in spurious or incorrect window state transitions

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 5.15.2, 6.4
    • QPA: X11/XCB
    • None
    • Linux/X11

    Description

      If you create a window and then call showMinimized() on it, it will observe an unexpected series of window state changes. The following sample reproduces the problem with gnome (mutter):

       

      // Build. g++ -std=c++17 -Wall -Wextra -g -fPIC $(pkg-config --cflags --libs Qt5Core  Qt5Gui Qt5Widgets) -o example example.cc
      #include <QApplication>
      #include <QLabel>
      #include <QTimer>
      #include <QWindowStateChangeEvent>
      #include <iostream>
      
      class CustomWidget : public QLabel
      {
      public:
          CustomWidget() : QLabel("Hello world") {}
          void changeEvent(QEvent *event) override
          {
              auto *wsce = dynamic_cast<QWindowStateChangeEvent *>(event);
              if (wsce) {
                  std::cout << "Window state change. New state: " << windowState() << std::endl;
              }
              QLabel::changeEvent(event);
          }
      };
      
      int main(int argc, char **argv)
      {
          QApplication app(argc, argv);
          CustomWidget w;
          w.showMinimized();
      
          QTimer::singleShot(2000, [&] { app.quit(); });
          return app.exec();
      }
      
      

      When run, the program will output the following:

       

       

      Window state change. New state: 1
      Window state change. New state: 0
      Window state change. New state: 1

      That is, the window is minimized, then restored, and then minimized again.

      If, after showMinimized(), we try to show the window and call setWindowState to remove the minimized state, we actually end up in the wrong state:

       

      // Build. g++ -std=c++17 -Wall -Wextra -g -fPIC $(pkg-config --cflags --libs Qt5Core  Qt5Gui Qt5Widgets) -o example example.cc
      #include <QApplication>
      #include <QLabel>
      #include <QTimer>
      #include <QWindowStateChangeEvent>
      #include <iostream>
      
      class CustomWidget : public QLabel
      {
      public:
          CustomWidget() : QLabel("Hello world") {}
          void changeEvent(QEvent *event) override
          {
              auto *wsce = dynamic_cast<QWindowStateChangeEvent *>(event);
              if (wsce) {
                  std::cout << "Window state change. New state: " << windowState() << std::endl;
              }
              QLabel::changeEvent(event);
          }
      };
      
      int main(int argc, char **argv)
      {
          QApplication app(argc, argv);
          CustomWidget w;
          w.showMinimized();
          w.setWindowState(w.windowState() & ~Qt::WindowMinimized);
      
          QTimer::singleShot(2000, [&] { app.quit(); });
          return app.exec();
      }
      

      this outputs the following:

      Window state change. New state: 1
      Window state change. New state: 0
      Window state change. New state: 1
      Window state change. New state: 0
      Window state change. New state: 1

      So the window is created minimized, restored, minimized again, shown again and then finally minimized, actually ending up in the wrong window state.

      If we try to fix this by adding an activateWindow(), that just adds another state change at the end of the sequence:

      // Build. g++ -std=c++17 -Wall -Wextra -g -fPIC $(pkg-config --cflags --libs Qt5Core  Qt5Gui Qt5Widgets) -o example example.cc
      #include <QApplication>
      #include <QLabel>
      #include <QTimer>
      #include <QWindowStateChangeEvent>
      #include <iostream>
      
      class CustomWidget : public QLabel
      {
      public:
          CustomWidget() : QLabel("Hello world") {}
          void changeEvent(QEvent *event) override
          {
              auto *wsce = dynamic_cast<QWindowStateChangeEvent *>(event);
              if (wsce) {
                  std::cout << "Window state change. New state: " << windowState() << std::endl;
              }
              QLabel::changeEvent(event);
          }
      };
      
      int main(int argc, char **argv)
      {
          QApplication app(argc, argv);
          CustomWidget w;
          w.showMinimized();
          w.setWindowState(w.windowState() & ~Qt::WindowMinimized);
          w.activateWindow();
      
          QTimer::singleShot(2000, [&] { app.quit(); });
          return app.exec();
      }
      
      

      resulting in this output:

      Window state change. New state: 1
      Window state change. New state: 0
      Window state change. New state: 1
      Window state change. New state: 0
      Window state change. New state: 1
      Window state change. New state: 0

      The second case, when the window ends up in the wrong state seems like an unambiguous error. In the first and third cases, the window does end up in the right state, but all the spurious and unpredictable state changes make it almost impossible for applications to react to state changes in any sensible way.

      Note: This overlaps with QTBUG-82342, which was closed as a duplicate of another issue, but which I can still reproduce about 50% of the time.

       

       

       

       

      Attachments

        Activity

          People

            liaqi Liang Qi
            jalf jalf
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated: