diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoamenuitem.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenuitem.mm | 194 |
1 files changed, 110 insertions, 84 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index f8f9648822..e1572e1800 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com> ** Contact: https://www.qt.io/licensing/ ** @@ -41,6 +42,7 @@ #include "qcocoamenuitem.h" +#include "qcocoansmenu.h" #include "qcocoamenu.h" #include "qcocoamenubar.h" #include "messages.h" @@ -49,7 +51,6 @@ #include "qcocoaapplication.h" // for custom application category #include "qcocoamenuloader.h" #include <QtGui/private/qcoregraphics_p.h> -#include <QtCore/qregularexpression.h> #include <QtCore/QDebug> @@ -92,9 +93,9 @@ NSUInteger keySequenceModifierMask(const QKeySequence &accel) #endif QCocoaMenuItem::QCocoaMenuItem() : - m_native(NULL), + m_native(nil), m_itemView(nil), - m_menu(NULL), + m_menu(nullptr), m_role(NoRole), m_iconSize(16), m_textSynced(false), @@ -112,9 +113,9 @@ QCocoaMenuItem::~QCocoaMenuItem() QMacAutoReleasePool pool; if (m_menu && m_menu->menuParent() == this) - m_menu->setMenuParent(0); + m_menu->setMenuParent(nullptr); if (m_merged) { - [m_native setHidden:YES]; + m_native.hidden = YES; } else { if (m_menu && m_menu->attachedItem() == m_native) m_menu->setAttachedItem(nil); @@ -140,7 +141,7 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) return; if (m_menu && m_menu->menuParent() == this) { - m_menu->setMenuParent(0); + m_menu->setMenuParent(nullptr); // Free the menu from its parent's influence m_menu->propagateEnabledState(true); if (m_native && m_menu->attachedItem() == m_native) @@ -210,82 +211,75 @@ void QCocoaMenuItem::setNativeContents(WId item) return; [m_itemView release]; m_itemView = [itemView retain]; - [m_itemView setAutoresizesSubviews:YES]; - [m_itemView setAutoresizingMask:NSViewWidthSizable]; - [m_itemView setHidden:NO]; - [m_itemView setNeedsDisplay:YES]; + m_itemView.autoresizesSubviews = YES; + m_itemView.autoresizingMask = NSViewWidthSizable; + m_itemView.hidden = NO; + m_itemView.needsDisplay = YES; } NSMenuItem *QCocoaMenuItem::sync() { - if (m_isSeparator != [m_native isSeparatorItem]) { + if (m_isSeparator != m_native.separatorItem) { [m_native release]; - if (m_isSeparator) { - m_native = [[NSMenuItem separatorItem] retain]; - [m_native setTag:reinterpret_cast<NSInteger>(this)]; - } else + if (m_isSeparator) + m_native = [[QCocoaNSMenuItem separatorItemWithPlatformMenuItem:this] retain]; + else m_native = nil; } if ((m_role != NoRole && !m_textSynced) || m_merged) { - NSMenuItem *mergeItem = nil; + QCocoaMenuBar *menubar = nullptr; + if (m_role == TextHeuristicRole) { + // Recognized menu roles are only found in the first menus below the menubar + QObject *p = menuParent(); + int depth = 1; + while (depth < 3 && p && !(menubar = qobject_cast<QCocoaMenuBar *>(p))) { + ++depth; + QCocoaMenuObject *menuObject = dynamic_cast<QCocoaMenuObject *>(p); + Q_ASSERT(menuObject); + p = menuObject->menuParent(); + } + + if (menubar && depth < 3) + m_detectedRole = detectMenuRole(m_text); + else + m_detectedRole = NoRole; + } + QCocoaMenuLoader *loader = [QCocoaMenuLoader sharedMenuLoader]; - switch (m_role) { - case ApplicationSpecificRole: - mergeItem = [loader appSpecificMenuItem:reinterpret_cast<NSInteger>(this)]; - break; + NSMenuItem *mergeItem = nil; + const auto role = effectiveRole(); + switch (role) { case AboutRole: mergeItem = [loader aboutMenuItem]; break; case AboutQtRole: mergeItem = [loader aboutQtMenuItem]; break; + case PreferencesRole: + mergeItem = [loader preferencesMenuItem]; + break; + case ApplicationSpecificRole: + mergeItem = [loader appSpecificMenuItem:this]; + break; case QuitRole: mergeItem = [loader quitMenuItem]; break; - case PreferencesRole: - mergeItem = [loader preferencesMenuItem]; + case CutRole: + case CopyRole: + case PasteRole: + case SelectAllRole: + if (menubar) + mergeItem = menubar->itemForRole(role); break; - case TextHeuristicRole: { - QObject *p = menuParent(); - int depth = 1; - QCocoaMenuBar *menubar = 0; - while (depth < 3 && p && !(menubar = qobject_cast<QCocoaMenuBar *>(p))) { - ++depth; - QCocoaMenuObject *menuObject = dynamic_cast<QCocoaMenuObject *>(p); - Q_ASSERT(menuObject); - p = menuObject->menuParent(); - } - if (depth == 3 || !menubar) - break; // Menu item too deep in the hierarchy, or not connected to any menubar - - m_detectedRole = detectMenuRole(m_text); - switch (m_detectedRole) { - case QPlatformMenuItem::AboutRole: - if (m_text.indexOf(QRegularExpression(QString::fromLatin1("qt$"), - QRegularExpression::CaseInsensitiveOption)) == -1) - mergeItem = [loader aboutMenuItem]; - else - mergeItem = [loader aboutQtMenuItem]; - break; - case QPlatformMenuItem::PreferencesRole: - mergeItem = [loader preferencesMenuItem]; - break; - case QPlatformMenuItem::QuitRole: - mergeItem = [loader quitMenuItem]; - break; - default: - if (m_detectedRole >= CutRole && m_detectedRole < RoleCount && menubar) - mergeItem = menubar->itemForRole(m_detectedRole); - if (!m_text.isEmpty()) - m_textSynced = true; - break; - } + case NoRole: + // The heuristic couldn't resolve the menu role + m_textSynced = false; break; - } - default: - qWarning() << "Menu item" << m_text << "has unsupported role" << m_role; + if (!m_text.isEmpty()) + m_textSynced = true; + break; } if (mergeItem) { @@ -294,7 +288,8 @@ NSMenuItem *QCocoaMenuItem::sync() [mergeItem retain]; [m_native release]; m_native = mergeItem; - [m_native setTag:reinterpret_cast<NSInteger>(this)]; + if (auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(m_native)) + nativeItem.platformMenuItem = this; } else if (m_merged) { // was previously merged, but no longer [m_native release]; @@ -306,14 +301,14 @@ NSMenuItem *QCocoaMenuItem::sync() } if (!m_native) { - m_native = [[NSMenuItem alloc] initWithTitle:m_text.toNSString() - action:nil - keyEquivalent:@""]; - [m_native setTag:reinterpret_cast<NSInteger>(this)]; + m_native = [[QCocoaNSMenuItem alloc] initWithPlatformMenuItem:this]; + m_native.title = m_text.toNSString(); } - [m_native setHidden: !m_isVisible]; - [m_native setView:m_itemView]; + resolveTargetAction(); + + m_native.hidden = !m_isVisible; + m_native.view = m_itemView; QString text = mergeText(); #ifndef QT_NO_SHORTCUT @@ -331,39 +326,35 @@ NSMenuItem *QCocoaMenuItem::sync() NSFont *customMenuFont = [NSFont fontWithName:m_font.family().toNSString() size:m_font.pointSize()]; if (customMenuFont) { - NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil]; - NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil]; - NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys]; NSAttributedString *str = [[[NSAttributedString alloc] initWithString:finalString.toNSString() - attributes:attributes] autorelease]; - [m_native setAttributedTitle: str]; + attributes:@{NSFontAttributeName: customMenuFont}] autorelease]; + m_native.attributedTitle = str; useAttributedTitle = true; } } - if (!useAttributedTitle) { - [m_native setTitle:finalString.toNSString()]; - } + if (!useAttributedTitle) + m_native.title = finalString.toNSString(); #ifndef QT_NO_SHORTCUT if (accel.count() == 1) { - [m_native setKeyEquivalent:keySequenceToKeyEqivalent(accel)]; - [m_native setKeyEquivalentModifierMask:keySequenceModifierMask(accel)]; + m_native.keyEquivalent = keySequenceToKeyEqivalent(accel); + m_native.keyEquivalentModifierMask = keySequenceModifierMask(accel); } else #endif { - [m_native setKeyEquivalent:@""]; - [m_native setKeyEquivalentModifierMask:NSCommandKeyMask]; + m_native.keyEquivalent = @""; + m_native.keyEquivalentModifierMask = NSCommandKeyMask; } NSImage *img = nil; if (!m_icon.isNull()) { img = qt_mac_create_nsimage(m_icon, m_iconSize); - [img setSize:NSMakeSize(m_iconSize, m_iconSize)]; + img.size = CGSizeMake(m_iconSize, m_iconSize); } - [m_native setImage:img]; + m_native.image = img; [img release]; - [m_native setState:m_checked ? NSOnState : NSOffState]; + m_native.state = m_checked ? NSOnState : NSOffState; return m_native; } @@ -408,8 +399,8 @@ void QCocoaMenuItem::syncMerged() qWarning("Trying to sync a non-merged item"); return; } - [m_native setTag:reinterpret_cast<NSInteger>(this)]; - [m_native setHidden: !m_isVisible]; + + m_native.hidden = !m_isVisible; } void QCocoaMenuItem::setParentEnabled(bool enabled) @@ -434,4 +425,39 @@ void QCocoaMenuItem::setIconSize(int size) m_iconSize = size; } +void QCocoaMenuItem::resolveTargetAction() +{ + if (m_native.separatorItem) + return; + + // Some items created by QCocoaMenuLoader are not + // instances of QCocoaNSMenuItem and have their + // target/action set as Interface Builder would. + if (![m_native isMemberOfClass:[QCocoaNSMenuItem class]]) + return; + + // Use the responder chain and ensure native modal dialogs + // continue receiving cut/copy/paste/etc. key equivalents. + SEL roleAction; + switch (effectiveRole()) { + case CutRole: + roleAction = @selector(cut:); + break; + case CopyRole: + roleAction = @selector(copy:); + break; + case PasteRole: + roleAction = @selector(paste:); + break; + case SelectAllRole: + roleAction = @selector(selectAll:); + break; + default: + roleAction = @selector(qt_itemFired:); + } + + m_native.action = roleAction; + m_native.target = nil; +} + QT_END_NAMESPACE |