From 5b5e8f78fecbe2bd9279bfa9ef10015cef8b1bc7 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 21 Jun 2019 13:37:40 +0200 Subject: [PATCH] NSMenuItem/NSMenu - set the submenu properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... in case the submenu is set from a slot, attached to the aboutToShow() signal. Normally, with a 'statically' pre-populated menu, we set 'submenu' property on a menu item from 'updateItem' callback in our menu delegate. After that, AppKit calls our delegate's willOpen call back and this is where we emit 'aboutToShow'. Unfortunately, if an application tries to create a nested menu 'dynamically' at this point, it never becomes 'submenu' of the item, since 'updateItem' was already handled at this point. We catch this case in QCocoaMenuItem and call setAttachedItem if needed. Fixes: QTBUG-76060 Change-Id: I676bf1d8529b9ddbfc90e4dff422b39668b7a5fa Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoamenu.h | 4 ++++ src/plugins/platforms/cocoa/qcocoamenu.mm | 10 ++++++++++ src/plugins/platforms/cocoa/qcocoamenuitem.mm | 8 ++++++++ src/plugins/platforms/cocoa/qcocoansmenu.mm | 2 ++ 4 files changed, 24 insertions(+) diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 34d8428188..a957710a88 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -92,6 +92,9 @@ public: bool isOpen() const; void setIsOpen(bool isOpen); + bool isAboutToShow() const; + void setIsAboutToShow(bool isAbout); + void timerEvent(QTimerEvent *e) override; void syncMenuItem_helper(QPlatformMenuItem *menuItem, bool menubarUpdate); @@ -111,6 +114,7 @@ private: bool m_parentEnabled:1; bool m_visible:1; bool m_isOpen:1; + bool m_isAboutToShow:1; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index f34988721d..8c4fca0d29 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -178,6 +178,16 @@ void QCocoaMenu::setIsOpen(bool isOpen) m_isOpen = isOpen; } +bool QCocoaMenu::isAboutToShow() const +{ + return m_isAboutToShow; +} + +void QCocoaMenu::setIsAboutToShow(bool isAbout) +{ + m_isAboutToShow = isAbout; +} + void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem) { QMacAutoReleasePool pool; diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index e54b6284e5..ef9b2659d2 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -140,6 +140,12 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) if (menu == m_menu) return; + bool setAttached = false; + if ([m_native.menu isKindOfClass:[QCocoaNSMenu class]]) { + auto parentMenu = static_cast(m_native.menu); + setAttached = parentMenu.platformMenu && parentMenu.platformMenu->isAboutToShow(); + } + if (m_menu && m_menu->menuParent() == this) { m_menu->setMenuParent(nullptr); // Free the menu from its parent's influence @@ -153,6 +159,8 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) if (m_menu) { m_menu->setMenuParent(this); m_menu->propagateEnabledState(isEnabled()); + if (setAttached) + m_menu->setAttachedItem(m_native); } else { // we previously had a menu, but no longer // clear out our item so the nexy sync() call builds a new one diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.mm b/src/plugins/platforms/cocoa/qcocoansmenu.mm index 65b0832d9f..c51460282a 100644 --- a/src/plugins/platforms/cocoa/qcocoansmenu.mm +++ b/src/plugins/platforms/cocoa/qcocoansmenu.mm @@ -195,7 +195,9 @@ static NSString *qt_mac_removePrivateUnicode(NSString *string) return; platformMenu->setIsOpen(true); + platformMenu->setIsAboutToShow(true); emit platformMenu->aboutToShow(); + platformMenu->setIsAboutToShow(false); } - (void)menuDidClose:(NSMenu *)menu