From d42cfeb84faf154b46f2811b2059946b396fcc12 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 19 Oct 2021 19:22:32 +0200 Subject: [PATCH] macOS: add default Edit menu items, if not added by AppKit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AppKit automatically appends "Start Dictation..." and "Emoji & Symbols" menu items, after a separator, to a menu in the menu bar that has the title "Edit" in the operating system language. Qt applications might however be translated to some other language, in which case the "Edit" menu is not recognized by AppKit, and the menu items won't be added. This is bad for accessibility and for users wanting to type emojis. If we have a menu that has the title "Edit" as translated in Qt's i18n system, then create those items manually. To prevent a duplication of the system- provided menu items, don't add the items if there already is one with the action being set to the relevant selector. Otherwise, perform the selector or call the NSApplication method ourselves. This then results in the relevant keyboard input through regular code paths. Fixes: QTBUG-79565 Change-Id: Ifd06036211756277550d398034689aca8e770133 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoamenubar.h | 2 + src/plugins/platforms/cocoa/qcocoamenubar.mm | 47 ++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h index 7186e48829..0054cdf6ad 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.h +++ b/src/plugins/platforms/cocoa/qcocoamenubar.h @@ -80,12 +80,14 @@ private: bool needsImmediateUpdate(); bool shouldDisable(QCocoaWindow *active) const; + void insertDefaultEditItems(QCocoaMenu *menu); NSMenuItem *nativeItemForMenu(QCocoaMenu *menu) const; QList > m_menus; NSMenu *m_nativeMenu; QPointer m_window; + QList> m_defaultEditMenuItems; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 2e0eeb06e3..524746f952 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -194,6 +194,11 @@ void QCocoaMenuBar::syncMenu_helper(QPlatformMenu *menu, bool menubarUpdate) for (QCocoaMenuItem *item : cocoaMenu->items()) cocoaMenu->syncMenuItem_helper(item, menubarUpdate); + const QString captionNoAmpersand = QString::fromNSString(cocoaMenu->nsMenu().title) + .remove(QLatin1Char('&')); + if (captionNoAmpersand == QCoreApplication::translate("QCocoaMenu", "Edit")) + insertDefaultEditItems(cocoaMenu); + BOOL shouldHide = YES; if (cocoaMenu->isVisible()) { // If the NSMenu has no visible items, or only separators, we should hide it @@ -400,6 +405,48 @@ QCocoaWindow *QCocoaMenuBar::cocoaWindow() const return m_window.data(); } +void QCocoaMenuBar::insertDefaultEditItems(QCocoaMenu *menu) +{ + if (menu->items().isEmpty()) + return; + + NSMenu *nsEditMenu = menu->nsMenu(); + if ([nsEditMenu itemAtIndex:nsEditMenu.numberOfItems - 1].action + == @selector(orderFrontCharacterPalette:)) { + for (auto defaultEditMenuItem : qAsConst(m_defaultEditMenuItems)) { + if (menu->items().contains(defaultEditMenuItem)) + menu->removeMenuItem(defaultEditMenuItem); + } + qDeleteAll(m_defaultEditMenuItems); + m_defaultEditMenuItems.clear(); + } else { + if (m_defaultEditMenuItems.isEmpty()) { + QCocoaMenuItem *separator = new QCocoaMenuItem; + separator->setIsSeparator(true); + + QCocoaMenuItem *dictationItem = new QCocoaMenuItem; + dictationItem->setText(QCoreApplication::translate("QCocoaMenuItem", "Start Dictation...")); + QObject::connect(dictationItem, &QPlatformMenuItem::activated, this, []{ + [NSApplication.sharedApplication performSelector:@selector(startDictation:)]; + }); + + QCocoaMenuItem *emojiItem = new QCocoaMenuItem; + emojiItem->setText(QCoreApplication::translate("QCocoaMenuItem", "Emoji && Symbols")); + emojiItem->setShortcut(QKeyCombination(Qt::MetaModifier|Qt::ControlModifier, Qt::Key_Space)); + QObject::connect(emojiItem, &QPlatformMenuItem::activated, this, []{ + [NSApplication.sharedApplication orderFrontCharacterPalette:nil]; + }); + + m_defaultEditMenuItems << separator << dictationItem << emojiItem; + } + for (auto defaultEditMenuItem : qAsConst(m_defaultEditMenuItems)) { + if (menu->items().contains(defaultEditMenuItem)) + menu->removeMenuItem(defaultEditMenuItem); + menu->insertMenuItem(defaultEditMenuItem, nullptr); + } + } +} + QT_END_NAMESPACE #include "moc_qcocoamenubar.cpp"