diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index 3073cb6b448..0816730c54a 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -90,14 +90,18 @@ #include #include -@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader); +Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QCocoaNSMenuItem)); @interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject +@property (nonatomic, retain) NSMenu *dockMenu; + (instancetype)sharedDelegate; -- (void)setDockMenu:(NSMenu *)newMenu; - (void)setReflectionDelegate:(NSObject *)oldDelegate; - (void)removeAppleEventHandlers; - (bool)inLaunch; @end +@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) (MenuAPI) +- (void)qt_itemFired:(QT_MANGLE_NAMESPACE(QCocoaNSMenuItem) *)item; +@end + QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaApplicationDelegate); diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 53407d8b626..7601fb7e178 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -73,8 +73,12 @@ #import "qcocoaapplicationdelegate.h" -#import "qcocoamenuloader.h" #include "qcocoaintegration.h" +#include "qcocoamenu.h" +#include "qcocoamenuloader.h" +#include "qcocoamenuitem.h" +#include "qcocoansmenu.h" + #include #include #include @@ -87,7 +91,6 @@ QT_USE_NAMESPACE @implementation QCocoaApplicationDelegate { bool startedQuit; - NSMenu *dockMenu; NSObject *reflectionDelegate; bool inLaunch; } @@ -129,7 +132,7 @@ QT_USE_NAMESPACE - (void)dealloc { - [dockMenu release]; + [_dockMenu release]; if (reflectionDelegate) { [[NSApplication sharedApplication] setDelegate:reflectionDelegate]; [reflectionDelegate release]; @@ -139,20 +142,13 @@ QT_USE_NAMESPACE [super dealloc]; } -- (void)setDockMenu:(NSMenu*)newMenu -{ - [newMenu retain]; - [dockMenu release]; - dockMenu = newMenu; -} - - (NSMenu *)applicationDockMenu:(NSApplication *)sender { Q_UNUSED(sender); // Manually invoke the delegate's -menuWillOpen: method. // See QTBUG-39604 (and its fix) for details. - [[dockMenu delegate] menuWillOpen:dockMenu]; - return [[dockMenu retain] autorelease]; + [self.dockMenu.delegate menuWillOpen:self.dockMenu]; + return [[self.dockMenu retain] autorelease]; } - (BOOL)canQuit @@ -444,3 +440,48 @@ QT_USE_NAMESPACE } @end + +@implementation QCocoaApplicationDelegate (Menus) + +- (BOOL)validateMenuItem:(NSMenuItem*)item +{ + auto *nativeItem = qt_objc_cast(item); + if (!nativeItem) + return item.enabled; // FIXME Test with with Qt as plugin or embedded QWindow. + + auto *platformItem = nativeItem.platformMenuItem; + if (!platformItem) // Try a bit harder with orphan menu itens + return item.hasSubmenu || (item.enabled && (item.action != @selector(qt_itemFired:))); + + // Menu-holding items are always enabled, as it's conventional in Cocoa + if (platformItem->menu()) + return YES; + + return platformItem->isEnabled(); +} + +@end + +@implementation QCocoaApplicationDelegate (MenuAPI) + +- (void)qt_itemFired:(QCocoaNSMenuItem *)item +{ + if (item.hasSubmenu) + return; + + auto *nativeItem = qt_objc_cast(item); + Q_ASSERT_X(nativeItem, qPrintable(__FUNCTION__), "Triggered menu item is not a QCocoaNSMenuItem."); + auto *platformItem = nativeItem.platformMenuItem; + // Menu-holding items also get a target to play nicely + // with NSMenuValidation but should not trigger. + if (!platformItem || platformItem->menu()) + return; + + QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData); + QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; + + static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated); + activatedSignal.invoke(platformItem, Qt::QueuedConnection); +} + +@end diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index 2a6c25ed753..3c5f4e68db1 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -265,7 +265,7 @@ void QCocoaNativeInterface::setDockMenu(QPlatformMenu *platformMenu) QMacAutoReleasePool pool; QCocoaMenu *cocoaPlatformMenu = static_cast(platformMenu); NSMenu *menu = cocoaPlatformMenu->nsMenu(); - [[QCocoaApplicationDelegate sharedDelegate] setDockMenu:menu]; + [QCocoaApplicationDelegate sharedDelegate].dockMenu = menu; } void *QCocoaNativeInterface::qMenuToNSMenu(QPlatformMenu *platformMenu) diff --git a/src/plugins/platforms/cocoa/qnsview_menus.mm b/src/plugins/platforms/cocoa/qnsview_menus.mm index db3d356f2ec..f0489552aa9 100644 --- a/src/plugins/platforms/cocoa/qnsview_menus.mm +++ b/src/plugins/platforms/cocoa/qnsview_menus.mm @@ -39,6 +39,7 @@ // This file is included from qnsview.mm, and only used to organize the code +#include #include #include #include @@ -101,19 +102,8 @@ static bool selectorIsCutCopyPaste(SEL selector) - (void)qt_itemFired:(QCocoaNSMenuItem *)item { - auto *nativeItem = qt_objc_cast(item); - Q_ASSERT_X(nativeItem, qPrintable(__FUNCTION__), "Triggered menu item is not a QCocoaNSMenuItem."); - auto *platformItem = nativeItem.platformMenuItem; - // Menu-holding items also get a target to play nicely - // with NSMenuValidation but should not trigger. - if (!platformItem || platformItem->menu()) - return; - - QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData); - QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; - - static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated); - activatedSignal.invoke(platformItem, Qt::QueuedConnection); + auto *appDelegate = [QCocoaApplicationDelegate sharedDelegate]; + [appDelegate qt_itemFired:item]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector diff --git a/tests/manual/cocoa/menurama/main.cpp b/tests/manual/cocoa/menurama/main.cpp index 00594b6d1fc..e39c01ea4d3 100644 --- a/tests/manual/cocoa/menurama/main.cpp +++ b/tests/manual/cocoa/menurama/main.cpp @@ -29,9 +29,46 @@ #include "mainwindow.h" #include "menuramaapplication.h" +#include +#include + int main(int argc, char *argv[]) { MenuramaApplication a(argc, argv); + a.setQuitOnLastWindowClosed(false); + + auto *dockMenu = new QMenu(); + dockMenu->setAsDockMenu(); + dockMenu->addAction(QLatin1String("New Window"), [=] { + auto *w = new MainWindow; + w->setAttribute(Qt::WA_DeleteOnClose, true); + w->show(); + }); + auto *disabledAction = dockMenu->addAction(QLatin1String("Disabled Item"), [=] { + qDebug() << "Should not happen!"; + Q_UNREACHABLE(); + }); + disabledAction->setEnabled(false); + dockMenu->addAction(QLatin1String("Last Item Before Separator"), [=] { + qDebug() << "Last Item triggered"; + }); + auto *hiddenAction = dockMenu->addAction(QLatin1String("Invisible Item (FIXME rdar:39615815)"), [=] { + qDebug() << "Should not happen!"; + Q_UNREACHABLE(); + }); + hiddenAction->setVisible(false); + dockMenu->addSeparator(); + auto *toolsMenu = dockMenu->addMenu(QLatin1String("Menurama Tools")); + toolsMenu->addAction(QLatin1String("Hammer"), [=] { + qDebug() << "Bang! Bang!"; + }); + toolsMenu->addAction(QLatin1String("Wrench"), [=] { + qDebug() << "Clang! Clang!"; + }); + toolsMenu->addAction(QLatin1String("Screwdriver"), [=] { + qDebug() << "Squeak! Squeak!"; + }); + MainWindow w; w.show(); diff --git a/tests/manual/cocoa/menurama/mainwindow.ui b/tests/manual/cocoa/menurama/mainwindow.ui index 4fb3e3420eb..2c6ac52f58c 100644 --- a/tests/manual/cocoa/menurama/mainwindow.ui +++ b/tests/manual/cocoa/menurama/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 486 - 288 + 376 @@ -58,7 +58,7 @@ Click on "Dynamic Stuff" then move left and right to other menus. Disa - + @@ -71,7 +71,7 @@ Click on "Dynamic Stuff" then move left and right to other menus. Disa - + Adding hundreds of items should not block the UI for noticeable periods of time. Odd numbered items should be disabled, those with 2nd LSB on should be hidden. @@ -83,6 +83,16 @@ Click on "Dynamic Stuff" then move left and right to other menus. Disa + + + + Check out the dock menu. You can close this window to verify that its actions will trigger in the absence of any window. And you can add more windows from there too. + + + true + + +