QCocoaNSMenuDelegate: Improve key-equivalent logic

By using NSEvent.characters instead of NSEvent.charactersIgnoringModifiers,
we may miss sending ShortcutOverride events.

For example, when the user presses Cmd-Opt-o, characters will be "ø"
(on a US keyboard layout) and therefore we'll be looking for the wrong
key-equivalent among the menu items. We only fall back on the modified
string when the search on the unmodified string fails.

As and addendum, we also skip any submenu when doing the key search.
This is not necessary since each menu delegate will get called eventually.

Change-Id: Id793315293a02c99e99d793ad812cff7b4a47821
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Gabriel de Dietrich 2017-10-20 20:48:24 +07:00
parent e996c74164
commit afb48f21c8
2 changed files with 42 additions and 20 deletions

View File

@ -64,7 +64,9 @@ typedef QPointer<QCocoaMenu> QCocoaMenuPointer;
+ (instancetype)sharedMenuDelegate; + (instancetype)sharedMenuDelegate;
- (NSMenuItem *)findItem:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier; - (NSMenuItem *)findItemInMenu:(NSMenu *)menu
forKey:(NSString *)key
modifiers:(NSUInteger)modifiers;
- (BOOL)validateMenuItem:(NSMenuItem *)item; // NSMenuValidation - (BOOL)validateMenuItem:(NSMenuItem *)item; // NSMenuValidation

View File

@ -197,12 +197,25 @@ static NSString *qt_mac_removePrivateUnicode(NSString* string)
CHECK_MENU_CLASS(menu); CHECK_MENU_CLASS(menu);
// Change the private unicode keys to the ones used in setting the "Key Equivalents"
NSString *characters = qt_mac_removePrivateUnicode([event characters]);
// Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ... // Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ...
const NSUInteger mask = NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask; static const NSUInteger mask = NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask;
if (NSMenuItem *menuItem = [self findItem:menu forKey:characters forModifiers:([event modifierFlags] & mask)]) {
if (!menuItem.target) { // Change the private unicode keys to the ones used in setting the "Key Equivalents"
NSString *characters = qt_mac_removePrivateUnicode(event.charactersIgnoringModifiers);
const auto modifiers = event.modifierFlags & mask;
NSMenuItem *keyEquivalentItem = [self findItemInMenu:menu
forKey:characters
modifiers:modifiers];
if (!keyEquivalentItem) {
// Maybe the modified character is what we're looking for after all
characters = qt_mac_removePrivateUnicode(event.characters);
keyEquivalentItem = [self findItemInMenu:menu
forKey:characters
modifiers:modifiers];
}
if (keyEquivalentItem) {
if (!keyEquivalentItem.target) {
// This item was modified by QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder // This item was modified by QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder
// and it looks like we're running a modal session for NSOpenPanel/NSSavePanel. // and it looks like we're running a modal session for NSOpenPanel/NSSavePanel.
// QCocoaFileDialogHelper is actually the only place we use this and we run NSOpenPanel modal // QCocoaFileDialogHelper is actually the only place we use this and we run NSOpenPanel modal
@ -211,7 +224,7 @@ static NSString *qt_mac_removePrivateUnicode(NSString* string)
// and do not touch the Qt's focusObject (which is different from some native view // and do not touch the Qt's focusObject (which is different from some native view
// having a focus inside NSSave/OpenPanel. // having a focus inside NSSave/OpenPanel.
*target = nil; *target = nil;
*action = menuItem.action; *action = keyEquivalentItem.action;
return YES; return YES;
} }
@ -249,25 +262,32 @@ static NSString *qt_mac_removePrivateUnicode(NSString* string)
} }
} }
} }
return NO; return NO;
} }
- (NSMenuItem *)findItem:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier - (NSMenuItem *)findItemInMenu:(NSMenu *)menu
forKey:(NSString *)key
modifiers:(NSUInteger)modifiers
{ {
for (NSMenuItem *item in [menu itemArray]) { // Find an item in 'menu' that has the same key equivalent as specified by
if (![item isEnabled] || [item isHidden] || [item isSeparatorItem]) // 'key' and 'modifiers'. We ignore disabled, hidden and separator items.
continue; // In a similar fashion, we don't need to recurse into submenus because their
if ([item hasSubmenu]) { // delegate will have [menuHasKeyEquivalent:...] invoked at some point.
if (NSMenuItem *nested = [self findItem:[item submenu] forKey:key forModifiers:modifier])
return nested;
}
NSString *menuKey = [item keyEquivalent]; for (NSMenuItem *item in menu.itemArray) {
if (menuKey if (!item.enabled || item.hidden || item.separatorItem)
&& NSOrderedSame == [menuKey compare:key] continue;
&& modifier == [item keyEquivalentModifierMask])
return item; if (item.hasSubmenu)
continue;
NSString *menuKey = item.keyEquivalent;
if (menuKey && NSOrderedSame == [menuKey compare:key]
&& modifiers == item.keyEquivalentModifierMask)
return item;
} }
return nil; return nil;
} }