summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qnsview_menus.mm
Commit message (Collapse)AuthorAgeFilesLines
* macOS: Don't clam to support action messages from non-Qt menu itemsTor Arne Vestbø2023-05-041-47/+67
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | As described in 3bedeb837ef68e0062668406e7662ed9ffc5268a, the way menu items on macOS are typically set up they have an action, e.g. copy:, but no target, and the system then takes care of finding the right target at runtime, starting with the first responder, walking the responder chain, and then moving on to other NSWindows, before ending up in the NSApplication and its delegate. As we (still) don't have a mechanism in Qt to forward generic actions, such as the cut/copy/paste, or selectAll, so we rely on mapping the actions back to QCocoaNSMenuItem that we can trace back to a QPlatformMenuItem that we in turn emit activated() for. Normally this works fine, but in the case where the Qt app is embedded in a native UI, which has its own menu items with cut/copy/paste, we'll get callbacks into QNSView for actions triggered by a generic NSMenuItem. In that case, we need to bail out, but we want to do so in a way that lets AppKit continue to walk the responder chain. This is possible by implementing supplementalTargetForAction:sender:, where we have access to the sender. If sender doesn't match the expected QCocoaNSMenuItem we let AppKit find a better match up the chain. Since the target we return needs to ultimately respond to the selectors and/or forward them, we can't point the target back to ourselves, nor can we point it to the application delegate directly, as the menu items need to be validated in the context of the view, so a new per-view QNSViewMenuHelper class has been added to take the role of forwarding the menu actions. The logic for forwarding the resulting actions to the application delegate has been simplified and hardened a bit as well. A possible scenario with this new approach is that the Qt app has a line edit focused, and the user tries to activate the menu item for Paste, but the item is grayed out because we can not support the action. This is of course confusing for the user, but less so than having an active menu item that then doesn't do anything when activated. Another scenario is that a responder later in the chain does respond to the paste action, and the menu item will end up pasting into something that is not the first responder. This might also be confusing for the user, but it's generally recommended that implementers of actions like paste only allow the action if the view is the first responder, and this is something views have to deal with anyways, so it doesn't change anything that we're now bailing out earlier in not accepting the paste. One benefit of allowing AppKit to find a better target for the action is that if no target is found, and the user presses the key equivalent of the disabled menu item, the key event will be delivered as a normal keyDown to our QNSView, which we do forward, allowing the Qt app to respond to the action, even though the action came from a generic menu item. With our old approach this would not happen, as we would claim to support the action for our QNSView, but then drop it on the floor when AppKit tried to deliver it to us. Change-Id: I609db42df6a107a49e287f435e8808812c83d43e Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
* macOS: Bail out if handling cut/copy/paste action from non-Qt NSMenuItemTor Arne Vestbø2023-03-201-3/+15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The typical way to set up menus on macOS, which we follow, is to create NSMenuItems with an action selector set (copy:), but without a target. This will result in AppKit finding a target dynamically, starting with the first responder, walking the responder chain, and then moving on to other NSWindows, before ending up in the NSApplication and its delegate. Unfortunately, we don't have a mechanism in Qt to forward generic actions, such as the cut/copy/paste, or selectAll, so we rely on mapping the actions back to QCocoaNSMenuItem that we can trace back to a QPlatformMenuItem that we in turn emit activated() for. Normally this works fine, but in the case where the Qt app is embedded in a native UI, which has its own menu items with cut/copy/paste, we'll get callbacks into QNSView for actions triggered by a generic NSMenuItem. In that case, we need to bail out, but we must do so without calling [super forwardInvocation:invocation], as that will just try to invoke the action on ourselves again. It's unfortunately too late to try to redirect the action to another responder, that might have handled the action, and it's questionable whether that would be the behavior we'd want, as that would possibly result in e.g. pasting text into another window than the Qt one that currently has an active cursor in a text entry. Fixes: QTBUG-111916 Pick-to: 6.5 Change-Id: I56318e4f7efd779cd20bf577aec8c2de04a6a142 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
* macOS: Add logging category for menu machineryTor Arne Vestbø2023-03-201-0/+3
| | | | | | | | The machinery is quit fragile, so any logging will help here. Pick-to: 6.5 Change-Id: I1906c0e33b4afbf649a20bfe2aa7210b6822087e Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
* Use SPDX license identifiersLucie Gérard2022-05-161-38/+2
| | | | | | | | | | | | | Replace the current license disclaimer in files by a SPDX-License-Identifier. Files that have to be modified by hand are modified. License files are organized under LICENSES directory. Task-number: QTBUG-67283 Change-Id: Id880c92784c40f3bbde861c0d93f58151c18b9f1 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io>
* Don't disable menu items that belong to the active modal windowVolker Hilsheimer2021-04-121-3/+6
| | | | | | | | | | | | | | | | | | A popup/context menu created via QQuickPlatformMenu doesn't belong to any menubar, so by disabling items in a menu that doesn't belong to the currently active menubar (5b9f6862b1), we disabled all menu items in a QQuickPlatformMenu when a modal window was active. For such unrooted menus, use the QCocoaMenuObject data structure to record which window it is shown for, and only disable items if that window is not also the current modal window. Amends 5b9f6862b1aa474a392203c69f6db678d633cecf. Fixes: QTBUG-92040 Pick-to: 6.1 6.0 5.15 Change-Id: I56b6d579e5e94689b43ca84d4637e35dc2cbeb4c Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
* Cocoa: When validating menus, account for modal window only if activeAndy Shaw2021-01-291-4/+3
| | | | | | | | | | | | When a modal window is WindowModal then it is possible for another top level window to be active and therefore the menus shown may be valid for the window. So we can still allow the menu items to be validated in the context of that window. Fixes: QTBUG-74088 Pick-to: 6.0 5.15 Change-Id: Ifb9c3fe12654b2972e0e3c368dc093fae1ed4cc8 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
* macOS: Clean up header includes to use quotes or brackets as appropriateTor Arne Vestbø2020-04-151-5/+5
| | | | | | | | The includes can be sorted and unified even more, but that's left for another rainy day. Change-Id: I4d5670d6d8389f69d2631b83b8f421d1f685a0f9 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
* Mac: disable application menu items during modal sessionVolker Hilsheimer2020-01-241-1/+2
| | | | | | | | | | | The default menu items in the application menu are not part of a traversable menu object hierarchy, so we never find a menubar. Since that is only the case for those items, we can disable them during any modal session. Change-Id: Ie8d8db274176237de664c6e5ebfe5015e13800e4 Fixes: QTBUG-80273 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
* macOS: Simplify Objective-C namespacingTor Arne Vestbø2019-10-151-2/+2
| | | | | | | | | | | | | | | | We only need to use the QT_MANGLE_NAMESPACE macro when declaring the interface of the class. As long as we couple that with an alias declaration using QT_NAMESPACE_ALIAS_OBJC_CLASS, any further uses of the class name can be un-namespaced, including declaring categories on the class. The only snag with QT_NAMESPACE_ALIAS_OBJC_CLASS is that it can only be used once per class and translation unit, so forward declarations get hairy, but we can avoid that by just including the headers instead. Change-Id: I333bcd18fe1e18d81fbd560b0941c98b1c32460e Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
* Cocoa Menus: Add support for menu items in window-less appsGabriel de Dietrich2018-05-231-13/+3
| | | | | | | | | | | | | | | | | Since we moved the menu items validation and target/action to QNSView (thus relying on the responder chain), we need to take care of case when the applications that doesn't have any window open. By adding similar methods to QCocoaApplicationDelegate, the last responder, we ensure the menu items will be validated and will trigger properly. This is particularly necessary for dock menu items, which live separately from any top-level widget. Dock menu added to Menurama which won't quit when its last window is closed. This way we can test that dock menu items will trigger in the absence of any window. Change-Id: I56d864eb9da1f8dd5adb2a3b6c3dd5304c723117 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
* Cocoa Menus: Clean up codeGabriel de Dietrich2018-04-261-6/+8
| | | | | | | | | | | | In this edition: * Use Objective-C properties where appropriate. * Use recently introduced qt_objc_cast(). * Remove uses of foreach. * Update copyright headers. Change-Id: I2a07a7b6cab27b833e4deaeedf9563463ff55914 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
* Cocoa Menus: Use the responder chain for menu items target/actionGabriel de Dietrich2018-04-191-0/+141
We start by setting the menu item target to nil. Then, -[qt_itemFired:] action is now in QNSView, which itself is naturally inserted in the responder chain. This removes the need to track and change the menu item's target/action when we're displaying a native dialog. Part of this is possible because we now derive our own QCocoaNSMenuItem class from NSMenuItem. We use -[respondsToSelector:] to decide whether the QNSView in the responder chain should respond to cut:, copy:, etc. And we only return YES when the view is first responder. The invocation to these action is forwarded to the same views' -[qt_itemFired:]. Message forwarding is done via forwardInvocation:, but experiments have shown that it can be done by the sole means of respondsToSelector: and direct invocation from cut:, copy:, etc. See the usage of the macro QT_COCOA_DYNAMIC_MENU_ITEM_ACTION. Menu validation also happens in QNSView and looks for modal windows. Therefore, -[worksWhenModal] is no longer necessary. Also, since the target is no longer set, the logic as documented in NSMenuItem.target won't work anymore. Most items from QCocoaMenuLoader also become QCocoaNSMenuItem and get the same target/action, which removes a bit of duplicated (and outdated) code. A particular case is the Quit item, which gets the terminate: action set until an actual menu item is added. Tested with texedit and standard dialogs examples together with menus, menurama and bigmenucreator manual tests. We also renamed some functions and variables to reflect common naming practices. Change-Id: I9b51d3be3467a666d8c3dcf8585edbc821e0282e Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>