Details
-
Bug
-
Resolution: Incomplete
-
Not Evaluated
-
None
-
4.8.1
-
None
-
MacOSX cocoa (tested on 10.7.3, but not restricted to that platform)
Description
If you use (cocoa-based) Qt in a plugin for a non Qt application, shortcuts that are present and enabled in a menu item will always execute the menu item (even if the focus widget overrides them).
In our software, this problem triggers for the copy/past shortcuts in QLineEdit fields. We have a normal document window with Cmd+X/C/V as cut/copy/past shortscuts in the menu items. As well as an 'inspector' tool window that allows to edit properties. The document window is not under our control (cocoa app), the 'inspector' window is a plugin written using Qt.
Using the shortcuts for cut/copy/paste in the edit fields in the 'inspector' window only works when the corresponding menu item is disabled (ex: no selection in the document).
I made a small sample application (attached) that can be build with Qt Creator to reproduce this issue. It has a cocoa based menu item (Cmd+X) in the main menu bar and some qt based menu items in the main window. If the menu item is triggered, a message is displayed. The window has two components, a QLineEdit and QCheckBox. If the checkbox has the focus Cmd+X/C/V brings up the message.
If the line edit has focus, Cmd+C/V does copy/paste. However Cmd+X still shows the message instead of cutting the selected text.
After some digging I found out that Cmd+Key shortcuts are called keyEquivalents in cocoa and are handled differently from normal keyDown events. The key equivalent is first passed to the key window (and sub views), if that one doesn't handle it, it is passed to the menu items, and if none of those handle it, it is passed to the first responder (and down the responder chain).
(see https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html#//apple_ref/doc/uid/10000060i-CH7-SW1,
Handling Key Equivalents).
In normal cocoa, the NSView objects listens to key equivalents.
However Qt doesn't use those native components, instead the menu item forwards this message to the focus widget when necessary (see QCocoaMenu, menuHasKeyEquivalent).
This means that when the menu isn't managed by Qt, key equivalents are never delivered to the focus widget. The problem can be fixed for Qt managed windows by forwarding the key equivalents when they are passed to the window instead of waiting until they are passed to the menu.
Adding the following method to qcocoasharedwindowmethods_mac.h does the trick:
(you need to make qt_sendSpontaneousEvent, qt_dispatchKeyEvent and cocoaKey2QtKey available to make it build though)
- (BOOL)performKeyEquivalent:(NSEvent *)event
{
// Qt doesn't use cocoa native widgets, so key equivalents
// like Cmd+X/C/V for 'cut/copy/paste' for widgets like QLineEdit
// are not handled by default. We pass these along to the focus widget if necessary.
// note: Similar code in QCocoaMenu, menuHasKeyEquivalent,
// however that code isn't triggered when the menu items aren't handled by Qt
// (for example when using Qt as a plugin).
QWidget *widget = 0;
if (qApp->activePopupWidget())
widget = (qApp->activePopupWidget()->focusWidget()
? qApp->activePopupWidget()->focusWidget()
: qApp->activePopupWidget());
else if (QApplicationPrivate::focus_widget)
widget = QApplicationPrivate::focus_widget;
// If we could not find any receivers, pass it to the active window
if (!widget)
widget = qApp->activeWindow();
if ( widget )
{
NSString *keyChars = [event charactersIgnoringModifiers];
if ([keyChars length] == 1)
{
QChar ch([keyChars characterAtIndex:0]);
if (ch.isLower())
ch = ch.toUpper();
Qt::Key qtKey = cocoaKey2QtKey(ch);
Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
QKeyEvent accel_ev(QEvent::ShortcutOverride, qtKey, keyMods);
accel_ev.ignore();
qt_sendSpontaneousEvent(widget, &accel_ev);
if (accel_ev.isAccepted())
}
}
return [super performKeyEquivalent:event];
}