| Index: ui/base/cocoa/command_dispatcher.mm
|
| diff --git a/ui/base/cocoa/command_dispatcher.mm b/ui/base/cocoa/command_dispatcher.mm
|
| index 46a351b4ce8c09b399011bf36313ac34db9fb6ec..2809dbe092e43609bba79f5a3165c211724160a1 100644
|
| --- a/ui/base/cocoa/command_dispatcher.mm
|
| +++ b/ui/base/cocoa/command_dispatcher.mm
|
| @@ -6,6 +6,19 @@
|
|
|
| #include "base/logging.h"
|
| #include "ui/base/cocoa/cocoa_base_utils.h"
|
| +#import "ui/base/cocoa/user_interface_item_command_handler.h"
|
| +
|
| +// Expose -[NSWindow hasKeyAppearance], which determines whether the traffic
|
| +// lights on the window are "lit". CommandDispatcher uses this property on a
|
| +// parent window to decide whether keys and commands should bubble up.
|
| +@interface NSWindow (PrivateAPI)
|
| +- (BOOL)hasKeyAppearance;
|
| +@end
|
| +
|
| +@interface CommandDispatcher ()
|
| +// The parent to bubble events to, or nil.
|
| +- (NSWindow<CommandDispatchingWindow>*)bubbleParent;
|
| +@end
|
|
|
| namespace {
|
|
|
| @@ -73,10 +86,13 @@ NSEvent* KeyEventForWindow(NSWindow* window, NSEvent* event) {
|
|
|
| // Give a CommandDispatcherTarget (e.g. a web site) a chance to handle the
|
| // event. If it doesn't want to handle it, it will call us back with
|
| - // -redispatchKeyEvent:.
|
| - NSResponder* r = [owner_ firstResponder];
|
| - if ([r conformsToProtocol:@protocol(CommandDispatcherTarget)])
|
| - return [r performKeyEquivalent:event];
|
| + // -redispatchKeyEvent:. Only allow this behavior when dispatching key events
|
| + // on the key window.
|
| + if ([owner_ isKeyWindow]) {
|
| + NSResponder* r = [owner_ firstResponder];
|
| + if ([r conformsToProtocol:@protocol(CommandDispatcherTarget)])
|
| + return [r performKeyEquivalent:event];
|
| + }
|
|
|
| if ([delegate_ prePerformKeyEquivalent:event window:owner_])
|
| return YES;
|
| @@ -84,7 +100,45 @@ NSEvent* KeyEventForWindow(NSWindow* window, NSEvent* event) {
|
| if ([owner_ defaultPerformKeyEquivalent:event])
|
| return YES;
|
|
|
| - return [delegate_ postPerformKeyEquivalent:event window:owner_];
|
| + if ([delegate_ postPerformKeyEquivalent:event window:owner_])
|
| + return YES;
|
| +
|
| + // Allow commands to "bubble up" to CommandDispatchers in parent windows, if
|
| + // they were not handled here.
|
| + return [[self bubbleParent] performKeyEquivalent:event];
|
| +}
|
| +
|
| +- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
|
| + forHandler:(id<UserInterfaceItemCommandHandler>)handler {
|
| + // Since this class implements these selectors, |super| will always say they
|
| + // are enabled. Only use [super] to validate other selectors. If there is no
|
| + // command handler, defer to AppController.
|
| + if ([item action] == @selector(commandDispatch:) ||
|
| + [item action] == @selector(commandDispatchUsingKeyModifiers:)) {
|
| + if (handler) {
|
| + // -dispatch:.. can't later decide to bubble events because
|
| + // -commandDispatch:.. is assumed to always succeed. So, if there is a
|
| + // |handler|, only validate against that for -commandDispatch:.
|
| + return [handler validateUserInterfaceItem:item window:owner_];
|
| + }
|
| +
|
| + id appController = [NSApp delegate];
|
| + DCHECK([appController
|
| + conformsToProtocol:@protocol(NSUserInterfaceValidations)]);
|
| + if ([appController validateUserInterfaceItem:item])
|
| + return YES;
|
| + }
|
| +
|
| + // Note this may validate an action bubbled up from a child window. However,
|
| + // if the child window also -respondsToSelector: (but validated it `NO`), the
|
| + // action will be dispatched to the child only, which may NSBeep().
|
| + // TODO(tapted): Fix this. E.g. bubble up validation via the bubbleParent's
|
| + // CommandDispatcher rather than the NSUserInterfaceValidations protocol, so
|
| + // that this step can be skipped.
|
| + if ([owner_ defaultValidateUserInterfaceItem:item])
|
| + return YES;
|
| +
|
| + return [[self bubbleParent] validateUserInterfaceItem:item];
|
| }
|
|
|
| - (BOOL)redispatchKeyEvent:(NSEvent*)event {
|
| @@ -128,4 +182,28 @@ NSEvent* KeyEventForWindow(NSWindow* window, NSEvent* event) {
|
| return NO;
|
| }
|
|
|
| +- (void)dispatch:(id)sender
|
| + forHandler:(id<UserInterfaceItemCommandHandler>)handler {
|
| + if (handler)
|
| + [handler commandDispatch:sender window:owner_];
|
| + else
|
| + [[self bubbleParent] commandDispatch:sender];
|
| +}
|
| +
|
| +- (void)dispatchUsingKeyModifiers:(id)sender
|
| + forHandler:(id<UserInterfaceItemCommandHandler>)handler {
|
| + if (handler)
|
| + [handler commandDispatchUsingKeyModifiers:sender window:owner_];
|
| + else
|
| + [[self bubbleParent] commandDispatchUsingKeyModifiers:sender];
|
| +}
|
| +
|
| +- (NSWindow<CommandDispatchingWindow>*)bubbleParent {
|
| + NSWindow* parent = [owner_ parentWindow];
|
| + if (parent && [parent hasKeyAppearance] &&
|
| + [parent conformsToProtocol:@protocol(CommandDispatchingWindow)])
|
| + return static_cast<NSWindow<CommandDispatchingWindow>*>(parent);
|
| + return nil;
|
| +}
|
| +
|
| @end
|
|
|