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

Application menu loses entries on macOS when destruction of windows is deferred

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 6.9.0
    • None
    • macOS Sequoia 15.5 (24F74)
    • macOS

    Description

      Problem description

      This is similar to issue QTBUG-80795 however this is a more extreme example where the application has multiple windows and opening one destroys the previous one (app has only one main window at a time). The problem is the same as in the other issue: application menu loses the Exit/About/Preferences menu items when the new window is opened (and the one is automatically closed). However it happens 100% of the time as the deferred destruction of the previous window always knocks the menu bar out of sync.

      Sample application

      This is best seen by running the sample application. Instructions:

      Modify ./configure.sh if -DCMAKE_PREFIX_PATH=/opt/qt-6.9.0 is not correct.
      Run ./configure.sh to configure the project.
      Then run ./build.sh to build it.
      Run using
          QT_LOGGING_RULES="qt.qpa.menus=true" ./build/QtMenuBarSample
      to enable debug logging from Qt.
      

      The application contains one button: Open which opens a new window and closes the initial window. Closing the newly-opened window re-opens the initial window.

      To support this "one main window with initial window" workflow there's two changes to otherwise standard windows. The base window has

      setAttribute(Qt::WA_DeleteOnClose, true);

      and the MainWindow closeEvent handling has some special handling so that the app does not quit when the window is closed.

      Potential root cause

      The menu disappearance is caused by the window lifecycle not being handled by Qt's menu bar logic, namely void QCocoaMenuBar::updateMenuBarImmediately() not being called at the right time / in the right order. src/plugins/platforms/cocoa/qcocoamenubar.mm#L263

      In the sample code we've got the BaseWindow parent class which has children windows InitialWindow and MainWindow. When the main window is opened from within the initial window, the destruction of the initial window happens after the initialisation of the main window. I suspect that the menu bar gets re-synced but at the wrong time. An example timeline:

      1. User clicks button to open the main window. 👇 ⌨️
          
      2. The InitialWindow starts self destructing. ⏲️
          
      3. The new menu bar of the main window gets initialised. ✅
          
      4. The InitialWindow is being destroyed 🧨 , invokes the destructor of the QMenuBar .
          
      5. The destruction of QMenuBar invokes updateMenuBarImmediately which re-syncs the application menu of the global menu bar to be empty. ❌
          
      6. The InitialWindow is destroyed 🪦

      Workaround

      We can force Qt to trigger a redraw of the menu bar right after the destruction of the previous window. This way the menu bar is in an invalid state for an unperceivable duration of time.

      While we can't call the private updateMenuBarImmediately, a redraw of the menu bar can be achieved by altering the menu bar in a significant way (for example, adding new menu item, changing parent, etc).  For example,

      connect(this, &BaseWindow::destroyed, this, [this]()
      {         
          ui.menuBar->setParent(nullptr);
          ui.menuBar->setParent(this);
      });
      

      does the job.

      In the app, uncomment lines 17:23 of BaseWindow.cpp.

       

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            eustaceb Justinas Bikulcius
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes