From e996c74164f6a161c030d3aff1348b1ef7f5da6f Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Thu, 19 Oct 2017 10:51:55 +0700 Subject: QCocoaMenu: Derive custom NSMenu class, make delegate a singleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Out of the box, this saves one delegate instance per NSMenu. It also weak-couples the NSMenu instance with its owning QCocoaMenu, making it safer to inspect from the menu delegate. In the future, this will be helpful for debugging by just overriding any NSMenu method. Change-Id: I7eb801009b97f6a8ee2003306c0e152621bbce54 Reviewed-by: Jake Petroules Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/qcocoamenu.mm | 222 +----------------------------- 1 file changed, 7 insertions(+), 215 deletions(-) (limited to 'src/plugins/platforms/cocoa/qcocoamenu.mm') diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index a54284dbae..7a550b163b 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -38,222 +38,15 @@ ****************************************************************************/ #include "qcocoamenu.h" +#include "qcocoansmenu.h" #include "qcocoahelpers.h" #include -#include -#include -#include #include "qcocoaapplication.h" #include "qcocoamenuloader.h" #include "qcocoamenubar.h" #include "qcocoawindow.h" -#import "qnsview.h" - -QT_BEGIN_NAMESPACE - -NSString *qt_mac_removePrivateUnicode(NSString* string) -{ - int len = [string length]; - if (len) { - QVarLengthArray characters(len); - bool changed = false; - for (int i = 0; i { - QCocoaMenu *m_menu; -} - -- (id) initWithMenu:(QCocoaMenu*) m; -- (NSMenuItem *)findItem:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier; - -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate); - -@implementation QCocoaMenuDelegate - -- (id) initWithMenu:(QCocoaMenu*) m -{ - if ((self = [super init])) - m_menu = m; - - return self; -} - -- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu -{ - Q_ASSERT(m_menu->nsMenu() == menu); - return menu.numberOfItems; -} - -- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel -{ - Q_UNUSED(index); - Q_ASSERT(m_menu->nsMenu() == menu); - if (shouldCancel) { - // TODO detach all submenus - return NO; - } - - QCocoaMenuItem *menuItem = reinterpret_cast(item.tag); - if (m_menu->items().contains(menuItem)) { - if (QCocoaMenu *itemSubmenu = menuItem->menu()) - itemSubmenu->setAttachedItem(item); - } - return YES; -} - -- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item -{ - Q_UNUSED(menu); - if (item && [item tag]) { - QCocoaMenuItem *cocoaItem = reinterpret_cast([item tag]); - cocoaItem->hovered(); - } -} - -- (void) menuWillOpen:(NSMenu*)m -{ - Q_UNUSED(m); - m_menu->setIsOpen(true); - emit m_menu->aboutToShow(); -} - -- (void) menuDidClose:(NSMenu*)m -{ - Q_UNUSED(m); - m_menu->setIsOpen(false); - // wrong, but it's the best we can do - emit m_menu->aboutToHide(); -} - -- (void) itemFired:(NSMenuItem*) item -{ - QCocoaMenuItem *cocoaItem = reinterpret_cast([item tag]); - // Menu-holding items also get a target to play nicely - // with NSMenuValidation but should not trigger. - if (cocoaItem->menu()) - return; - QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData); - QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; - static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated); - activatedSignal.invoke(cocoaItem, Qt::QueuedConnection); -} - -- (BOOL)validateMenuItem:(NSMenuItem*)menuItem -{ - QCocoaMenuItem *cocoaItem = reinterpret_cast(menuItem.tag); - // Menu-holding items are always enabled, as it's conventional in Cocoa - if (!cocoaItem || cocoaItem->menu()) - return YES; - - return cocoaItem->isEnabled(); -} - -- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action -{ - /* - Check if the menu actually has a keysequence defined for this key event. - If it does, then we will first send the key sequence to the QWidget that has focus - since (in Qt's eyes) it needs to a chance at the key event first (QEvent::ShortcutOverride). - If the widget accepts the key event, we then return YES, but set the target and action to be nil, - which means that the action should not be triggered, and instead dispatch the event ourselves. - In every other case we return NO, which means that Cocoa can do as it pleases - (i.e., fire the menu action). - */ - - // Change the private unicode keys to the ones used in setting the "Key Equivalents" - NSString *characters = qt_mac_removePrivateUnicode([event characters]); - // Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ... - const NSUInteger mask = NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask; - if (NSMenuItem *menuItem = [self findItem:menu forKey:characters forModifiers:([event modifierFlags] & mask)]) { - if (!menuItem.target) { - // This item was modified by QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder - // and it looks like we're running a modal session for NSOpenPanel/NSSavePanel. - // QCocoaFileDialogHelper is actually the only place we use this and we run NSOpenPanel modal - // (modal sheet, window modal, application modal). - // Whatever the current first responder is, let's give it a chance - // and do not touch the Qt's focusObject (which is different from some native view - // having a focus inside NSSave/OpenPanel. - *target = nil; - *action = menuItem.action; - return YES; - } - - QObject *object = qApp->focusObject(); - if (object) { - QChar ch; - int keyCode; - ulong nativeModifiers = [event modifierFlags]; - Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers]; - NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers]; - NSString *characters = [event characters]; - - if ([charactersIgnoringModifiers length] > 0) { // convert the first character into a key code - if ((modifiers & Qt::ControlModifier) && ([characters length] != 0)) { - ch = QChar([characters characterAtIndex:0]); - } else { - ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); - } - keyCode = qt_mac_cocoaKey2QtKey(ch); - } else { - // might be a dead key - ch = QChar::ReplacementCharacter; - keyCode = Qt::Key_unknown; - } - - QKeyEvent accel_ev(QEvent::ShortcutOverride, (keyCode & (~Qt::KeyboardModifierMask)), - Qt::KeyboardModifiers(modifiers & Qt::KeyboardModifierMask)); - accel_ev.ignore(); - QCoreApplication::sendEvent(object, &accel_ev); - if (accel_ev.isAccepted()) { - [[NSApp keyWindow] sendEvent: event]; - *target = nil; - *action = nil; - return YES; - } - } - } - return NO; -} - -- (NSMenuItem *)findItem:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier -{ - for (NSMenuItem *item in [menu itemArray]) { - if (![item isEnabled] || [item isHidden] || [item isSeparatorItem]) - continue; - if ([item hasSubmenu]) { - if (NSMenuItem *nested = [self findItem:[item submenu] forKey:key forModifiers:modifier]) - return nested; - } - - NSString *menuKey = [item keyEquivalent]; - if (menuKey - && NSOrderedSame == [menuKey compare:key] - && modifier == [item keyEquivalentModifierMask]) - return item; - } - return nil; -} - -@end QT_BEGIN_NAMESPACE @@ -267,9 +60,7 @@ QCocoaMenu::QCocoaMenu() : { QMacAutoReleasePool pool; - m_nativeMenu = [[NSMenu alloc] initWithTitle:@"Untitled"]; - [m_nativeMenu setAutoenablesItems:YES]; - m_nativeMenu.delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this]; + m_nativeMenu = [[QCocoaNSMenu alloc] initWithQPAMenu:this]; } QCocoaMenu::~QCocoaMenu() @@ -279,10 +70,6 @@ QCocoaMenu::~QCocoaMenu() item->setMenuParent(0); } - QMacAutoReleasePool pool; - NSObject *delegate = m_nativeMenu.delegate; - m_nativeMenu.delegate = nil; - [delegate release]; [m_nativeMenu release]; } @@ -307,6 +94,11 @@ void QCocoaMenu::setFont(const QFont &font) } } +NSMenu *QCocoaMenu::nsMenu() const +{ + return static_cast(m_nativeMenu); +} + void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) { QMacAutoReleasePool pool; -- cgit v1.2.3