macOS: Bail out if handling cut/copy/paste action from non-Qt NSMenuItem

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>
This commit is contained in:
Tor Arne Vestbø 2023-03-16 17:22:57 +01:00
parent 391491c8b3
commit 3bedeb837e

View File

@ -91,10 +91,22 @@ static bool selectorIsCutCopyPaste(SEL selector)
NSObject *sender;
[invocation getArgument:&sender atIndex:2];
qCDebug(lcQpaMenus) << "Forwarding" << invocation.selector << "from" << sender;
if (auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(sender)) {
// We claim to respond to standard edit actions such as cut/copy/paste,
// but these might not be exclusively coming from menu items that we
// control. For example, when embedded into a native UI (as a plugin),
// the menu items might be part of the host application, and if we're
// the first responder, we'll be the target of these actions. As we
// don't have a mechanism in Qt to trigger generic actions, we have
// to bail out if we don't have a QCocoaNSMenuItem we can activate().
// Note that we skip the call to super as well, as that would just
// try to invoke the current action on ourselves again.
if (auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(sender))
[self qt_itemFired:nativeItem];
return;
}
else
qCDebug(lcQpaMenus) << "Ignoring action for menu item we didn't create";
return;
}
[super forwardInvocation:invocation];