Fix shortcut override for menus
Since we use native Cocoa menus, we cannot rely on the normal shortcut handling. Shortcuts can be overridden by the currently focused object in Qt. In order to make that possible we need to send a QShortcutOverride event before accepting any key event. For menus the key event goes from the NSApp directly to the menu, so the shortcutOverride would not work. This is mostly an adaptation of the Qt 4 code. Task-number: QTBUG-30695 Change-Id: Icb4979309d2d6f9606eb9c8abc4130dc79926593 Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com>
This commit is contained in:
parent
8f125985db
commit
4ca105b9bb
@ -50,6 +50,27 @@
|
||||
#include "qcocoawindow.h"
|
||||
#import "qnsview.h"
|
||||
|
||||
NSString *qt_mac_removePrivateUnicode(NSString* string)
|
||||
{
|
||||
int len = [string length];
|
||||
if (len) {
|
||||
QVarLengthArray <unichar, 10> characters(len);
|
||||
bool changed = false;
|
||||
for (int i = 0; i<len; i++) {
|
||||
characters[i] = [string characterAtIndex:i];
|
||||
// check if they belong to key codes in private unicode range
|
||||
// currently we need to handle only the NSDeleteFunctionKey
|
||||
if (characters[i] == NSDeleteFunctionKey) {
|
||||
characters[i] = NSDeleteCharacter;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
return [NSString stringWithCharacters:characters.data() length:len];
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
|
||||
{
|
||||
return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
|
||||
@ -101,6 +122,80 @@ static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
|
||||
return cocoaItem->isEnabled();
|
||||
}
|
||||
|
||||
- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
|
||||
{
|
||||
/*
|
||||
Check if the menu actually has a keysequence defined for this key event.
|
||||
If it does, then we will first send the key sequence to the QWidget that has focus
|
||||
since (in Qt's eyes) it needs to a chance at the key event first (QEvent::ShortcutOverride).
|
||||
If the widget accepts the key event, we then return YES, but set the target and action to be nil,
|
||||
which means that the action should not be triggered, and instead dispatch the event ourselves.
|
||||
In every other case we return NO, which means that Cocoa can do as it pleases
|
||||
(i.e., fire the menu action).
|
||||
*/
|
||||
|
||||
// Change the private unicode keys to the ones used in setting the "Key Equivalents"
|
||||
NSString *characters = qt_mac_removePrivateUnicode([event characters]);
|
||||
if ([self hasShortcut:menu
|
||||
forKey:characters
|
||||
// Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ...
|
||||
forModifiers:([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask))
|
||||
]) {
|
||||
QObject *object = qApp->focusObject();
|
||||
if (object) {
|
||||
QChar ch;
|
||||
int keyCode;
|
||||
ulong nativeModifiers = [event modifierFlags];
|
||||
Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers];
|
||||
NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
|
||||
NSString *characters = [event characters];
|
||||
|
||||
if ([charactersIgnoringModifiers length] > 0) { // convert the first character into a key code
|
||||
if ((modifiers & Qt::ControlModifier) && ([characters length] != 0)) {
|
||||
ch = QChar([characters characterAtIndex:0]);
|
||||
} else {
|
||||
ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
|
||||
}
|
||||
keyCode = qt_mac_cocoaKey2QtKey(ch);
|
||||
} else {
|
||||
// might be a dead key
|
||||
ch = QChar::ReplacementCharacter;
|
||||
keyCode = Qt::Key_unknown;
|
||||
}
|
||||
|
||||
QKeyEvent accel_ev(QEvent::ShortcutOverride, (keyCode & (~Qt::KeyboardModifierMask)),
|
||||
Qt::KeyboardModifiers(keyCode & Qt::KeyboardModifierMask));
|
||||
accel_ev.ignore();
|
||||
QCoreApplication::sendEvent(object, &accel_ev);
|
||||
if (accel_ev.isAccepted()) {
|
||||
[[NSApp keyWindow] sendEvent: event];
|
||||
*target = nil;
|
||||
*action = nil;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
|
||||
{
|
||||
for (NSMenuItem *item in [menu itemArray]) {
|
||||
if (![item isEnabled] || [item isHidden] || [item isSeparatorItem])
|
||||
continue;
|
||||
if ([item hasSubmenu]
|
||||
&& [self hasShortcut:[item submenu] forKey:key forModifiers:modifier])
|
||||
return YES;
|
||||
|
||||
NSString *menuKey = [item keyEquivalent];
|
||||
if (menuKey
|
||||
&& NSOrderedSame == [menuKey compare:key]
|
||||
&& modifier == [item keyEquivalentModifierMask])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
@ -107,7 +107,7 @@ QT_END_NAMESPACE
|
||||
- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent;
|
||||
|
||||
- (int) convertKeyCode : (QChar)keyCode;
|
||||
- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags;
|
||||
+ (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags;
|
||||
- (void)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType;
|
||||
- (void)keyDown:(NSEvent *)theEvent;
|
||||
- (void)keyUp:(NSEvent *)theEvent;
|
||||
|
@ -494,7 +494,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
|
||||
nativeDrag->setLastMouseEvent(theEvent, self);
|
||||
|
||||
Qt::KeyboardModifiers keyboardModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
|
||||
Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]];
|
||||
QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers);
|
||||
}
|
||||
|
||||
@ -556,7 +556,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
[inputManager handleMouseEvent:theEvent];
|
||||
}
|
||||
} else {
|
||||
if ([self convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) {
|
||||
if ([QNSView convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) {
|
||||
m_buttons |= Qt::RightButton;
|
||||
m_sendUpAsRightButton = true;
|
||||
} else {
|
||||
@ -826,7 +826,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
|
||||
NSEventPhase phase = [theEvent phase];
|
||||
if (phase == NSEventPhaseBegan) {
|
||||
currentWheelModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
|
||||
currentWheelModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]];
|
||||
}
|
||||
|
||||
QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers);
|
||||
@ -838,7 +838,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
#endif
|
||||
{
|
||||
QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta,
|
||||
[self convertKeyModifiers:[theEvent modifierFlags]]);
|
||||
[QNSView convertKeyModifiers:[theEvent modifierFlags]]);
|
||||
}
|
||||
}
|
||||
#endif //QT_NO_WHEELEVENT
|
||||
@ -848,7 +848,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
return qt_mac_cocoaKey2QtKey(keyChar);
|
||||
}
|
||||
|
||||
- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
|
||||
+ (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
|
||||
{
|
||||
Qt::KeyboardModifiers qtMods =Qt::NoModifier;
|
||||
if (modifierFlags & NSShiftKeyMask)
|
||||
@ -868,7 +868,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
{
|
||||
ulong timestamp = [nsevent timestamp] * 1000;
|
||||
ulong nativeModifiers = [nsevent modifierFlags];
|
||||
Qt::KeyboardModifiers modifiers = [self convertKeyModifiers: nativeModifiers];
|
||||
Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers];
|
||||
NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
|
||||
NSString *characters = [nsevent characters];
|
||||
|
||||
@ -948,7 +948,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
{
|
||||
ulong timestamp = [nsevent timestamp] * 1000;
|
||||
ulong modifiers = [nsevent modifierFlags];
|
||||
Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
|
||||
Qt::KeyboardModifiers qmodifiers = [QNSView convertKeyModifiers:modifiers];
|
||||
|
||||
// calculate the delta and remember the current modifiers for next time
|
||||
static ulong m_lastKnownModifiers;
|
||||
@ -1278,7 +1278,7 @@ static QTouchDevice *touchDevice = 0;
|
||||
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
|
||||
|
||||
// update these so selecting move/copy/link works
|
||||
QGuiApplicationPrivate::modifier_buttons = [self convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
|
||||
QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
|
||||
|
||||
QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect());
|
||||
if ([sender draggingSource] != nil) {
|
||||
|
Loading…
Reference in New Issue
Block a user