Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4257)

Unified Diff: chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm

Issue 1259903002: DO NOT COMMIT: [Mac] Refactor keyboard shortcut handling so it can be reused in Views. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sync to new CommandDispatcher design. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
new file mode 100644
index 0000000000000000000000000000000000000000..dc0a804f7967ca7b01bf334b51eaad10576ca782
--- /dev/null
+++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -0,0 +1,319 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.h"
+
+#include "base/logging.h"
+#import "base/mac/foundation_util.h"
+#include "chrome/app/chrome_command_ids.h"
+#import "chrome/browser/app_controller_mac.h"
+#include "chrome/browser/fullscreen.h"
+#include "chrome/browser/global_keyboard_shortcuts_mac.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
+#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
+#include "content/public/browser/web_contents.h"
+#import "ui/base/cocoa/cocoa_base_utils.h"
+
+namespace {
+
+// Type of functions listed in global_keyboard_shortcuts_mac.h.
+typedef int (*KeyToCommandMapper)(bool, bool, bool, bool, int, unichar);
+
+// If the event is for a Browser window, and the key combination has an
+// associated command, execute the command.
+bool HandleExtraKeyboardShortcut(
+ NSEvent* event,
+ NSWindow* window,
+ KeyToCommandMapper command_for_keyboard_shortcut) {
+ // Extract info from |event|.
+ NSUInteger modifers = [event modifierFlags];
+ const bool command = modifers & NSCommandKeyMask;
+ const bool shift = modifers & NSShiftKeyMask;
+ const bool control = modifers & NSControlKeyMask;
+ const bool option = modifers & NSAlternateKeyMask;
+ const int key_code = [event keyCode];
+ const unichar key_char = KeyCharacterForEvent(event);
+
+ int cmd = command_for_keyboard_shortcut(command, shift, control, option,
+ key_code, key_char);
+
+ if (cmd == -1)
+ return false;
+
+ // Only handle event if this is a browser window.
+ Browser* browser = chrome::FindBrowserWithWindow(window);
+ if (!browser)
+ return false;
+
+ chrome::ExecuteCommand(browser, cmd);
+ return true;
+}
+
+bool HandleExtraWindowKeyboardShortcut(NSEvent* event, NSWindow* window) {
+ return HandleExtraKeyboardShortcut(event, window,
+ CommandForWindowKeyboardShortcut);
+}
+
+bool HandleDelayedWindowKeyboardShortcut(NSEvent* event, NSWindow* window) {
+ return HandleExtraKeyboardShortcut(event, window,
+ CommandForDelayedWindowKeyboardShortcut);
+}
+
+bool HandleExtraBrowserKeyboardShortcut(NSEvent* event, NSWindow* window) {
+ return HandleExtraKeyboardShortcut(event, window,
+ CommandForBrowserKeyboardShortcut);
+}
+
+// Update a toggle state for an item if modified. The item may be an NSMenuItem
+// or NSButton. Called by -validateUserInterfaceItem:.
+void UpdateToggleStateWithTag(NSInteger tag, id item, NSWindow* window) {
+ if (![item respondsToSelector:@selector(state)] ||
+ ![item respondsToSelector:@selector(setState:)])
+ return;
+
+ Browser* browser = chrome::FindBrowserWithWindow(window);
+ DCHECK(browser);
+
+ // On Windows this logic happens in bookmark_bar_view.cc. This simply updates
+ // the menu item; it does not display the bookmark bar itself.
+ if (tag == IDC_SHOW_BOOKMARK_BAR) {
+ bool toggled = browser->window()->IsBookmarkBarVisible();
+ NSInteger oldState = [item state];
+ NSInteger newState = toggled ? NSOnState : NSOffState;
+ if (oldState != newState)
+ [item setState:newState];
+ return;
+ }
+
+ // Update the checked/unchecked state of items in the encoding menu.
+ // On Windows, this logic is part of |EncodingMenuModel| in
+ // browser/ui/views/toolbar_view.h.
+ EncodingMenuController encoding_controller;
+ if (!encoding_controller.DoesCommandBelongToEncodingMenu(tag))
+ return;
+
+ Profile* profile = browser->profile();
+ DCHECK(profile);
+ content::WebContents* current_tab =
+ browser->tab_strip_model()->GetActiveWebContents();
+ if (!current_tab)
+ return;
+
+ const std::string encoding = current_tab->GetEncoding();
+
+ bool toggled = encoding_controller.IsItemChecked(profile, encoding, tag);
+ NSInteger oldState = [item state];
+ NSInteger newState = toggled ? NSOnState : NSOffState;
+ if (oldState != newState)
+ [item setState:newState];
+}
+
+} // namespace
+
+@implementation ChromeCommandDispatcherDelegate
+
+- (BOOL)handleExtraKeyboardShortcut:(NSEvent*)event window:(NSWindow*)window {
+ return HandleExtraBrowserKeyboardShortcut(event, window) ||
+ HandleExtraWindowKeyboardShortcut(event, window) ||
+ HandleDelayedWindowKeyboardShortcut(event, window);
+}
+
+- (BOOL)eventHandledByExtensionCommand:(NSEvent*)event
+ isRedispatch:(BOOL)isRedispatch {
+ // Some extension commands have higher priority than web content, and some
+ // have lower priority. Regardless of whether the event is being redispatched,
+ // let the extension system try to handle the event. In case this is a
+ // redispatched event, [event window] gives the correct window.
+ if ([event window]) {
+ BrowserWindowController* controller = [[event window] windowController];
+ if ([controller respondsToSelector:@selector(handledByExtensionCommand:
+ priority:)]) {
+ ui::AcceleratorManager::HandlerPriority priority =
+ isRedispatch ? ui::AcceleratorManager::kNormalPriority
+ : ui::AcceleratorManager::kHighPriority;
+ if ([controller handledByExtensionCommand:event priority:priority])
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (BOOL)prePerformKeyEquivalent:(NSEvent*)event window:(NSWindow*)window {
+ // Handle per-window shortcuts like cmd-1, but do not handle browser-level
+ // shortcuts like cmd-left (else, cmd-left would do history navigation even
+ // if e.g. the Omnibox has focus).
+ return HandleExtraWindowKeyboardShortcut(event, window);
+}
+
+- (BOOL)postPerformKeyEquivalent:(NSEvent*)event window:(NSWindow*)window {
+ // Handle per-window shortcuts like Esc after giving everybody else a chance
+ // to handle them
+ return HandleDelayedWindowKeyboardShortcut(event, window);
+}
+
+- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
+ window:(NSWindow*)window {
+ SEL action = [item action];
+ if (action != @selector(commandDispatch:) &&
+ action != @selector(commandDispatchUsingKeyModifiers:)) {
+ // By default, interface items are enabled if the object in the responder
+ // chain that implements the action does not implement
+ // -validateUserInterfaceItem. Since we only care about -commandDispatch,
+ // return YES for all other actions
+ return YES;
+ }
+
+ Browser* browser = chrome::FindBrowserWithWindow(window);
+ DCHECK(browser);
+ NSInteger tag = [item tag];
+ if (!chrome::SupportsCommand(browser, tag))
+ return NO;
+
+ // Generate return value (enabled state).
+ BOOL enable = chrome::IsCommandEnabled(browser, tag);
+ switch (tag) {
+ case IDC_CLOSE_TAB:
+ // Disable "close tab" if the receiving window is not tabbed.
+ // We simply check whether the item has a keyboard shortcut set here;
+ // app_controller_mac.mm actually determines whether the item should
+ // be enabled.
+ if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item))
+ enable &= !![[menuItem keyEquivalent] length];
+ break;
+ case IDC_FULLSCREEN: {
+ if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item)) {
+ BrowserWindowController* controller =
+ base::mac::ObjCCast<BrowserWindowController>(
+ [window windowController]);
+ if (chrome::mac::SupportsSystemFullscreen()) {
+ [menuItem setTitle:[controller titleForFullscreenMenuItem]];
+ } else {
+ [menuItem setHidden:YES];
+ }
+ }
+ break;
+ }
+ case IDC_PRESENTATION_MODE: {
+ if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item)) {
+ BrowserWindowController* controller =
+ base::mac::ObjCCast<BrowserWindowController>(
+ [window windowController]);
+ [menuItem setTitle:[controller titleForFullscreenMenuItem]];
+
+ if (chrome::mac::SupportsSystemFullscreen())
+ [menuItem setAlternate:YES];
+ }
+ break;
+ }
+ case IDC_SHOW_SIGNIN: {
+ Profile* original_profile = browser->profile()->GetOriginalProfile();
+ [AppController updateSigninItem:item
+ shouldShow:enable
+ currentProfile:original_profile];
+ break;
+ }
+ case IDC_BOOKMARK_PAGE: {
+ // Extensions have the ability to hide the bookmark page menu item.
+ // This only affects the bookmark page menu item under the main menu.
+ // The bookmark page menu item under the wrench menu has its
+ // visibility controlled by WrenchMenuModel.
+ bool shouldHide =
+ chrome::ShouldRemoveBookmarkThisPageUI(browser->profile());
+ NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item);
+ [menuItem setHidden:shouldHide];
+ break;
+ }
+ case IDC_BOOKMARK_ALL_TABS: {
+ // Extensions have the ability to hide the bookmark all tabs menu
+ // item. This only affects the bookmark page menu item under the main
+ // menu. The bookmark page menu item under the wrench menu has its
+ // visibility controlled by WrenchMenuModel.
+ bool shouldHide =
+ chrome::ShouldRemoveBookmarkOpenPagesUI(browser->profile());
+ NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item);
+ [menuItem setHidden:shouldHide];
+ break;
+ }
+ default:
+ // Special handling for the contents of the Text Encoding submenu. On
+ // Mac OS, instead of enabling/disabling the top-level menu item, we
+ // enable/disable the submenu's contents (per Apple's HIG).
+ EncodingMenuController encoding_controller;
+ if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) {
+ enable &=
+ chrome::IsCommandEnabled(browser, IDC_ENCODING_MENU) ? YES : NO;
+ }
+ }
+
+ // If the item is toggleable, find its toggle state and
+ // try to update it. This is a little awkward, but the alternative is
+ // to check after a commandDispatch, which seems worse.
+ UpdateToggleStateWithTag(tag, item, window);
+
+ return enable;
+}
+
+- (void)commandDispatch:(id)sender window:(NSWindow*)window {
+ DCHECK(sender);
+ // Identify the actual BWC to which the command should be dispatched. It might
+ // belong to a background window, yet this controller gets it because it is
+ // the foreground window's controller and thus in the responder chain. Some
+ // senders don't have this problem (for example, menus only operate on the
+ // foreground window), so this is only an issue for senders that are part of
+ // windows.
+ NSWindow* targetWindow = window;
+ if ([sender respondsToSelector:@selector(window)])
+ targetWindow = [sender window];
+ Browser* browser = chrome::FindBrowserWithWindow(targetWindow);
+ DCHECK(browser);
+
+ // When system fullscreen is available, it supercedes presentation mode.
+ int tag = [sender tag];
+ if (tag == IDC_PRESENTATION_MODE && chrome::mac::SupportsSystemFullscreen())
+ tag = IDC_FULLSCREEN;
+
+ chrome::ExecuteCommand(browser, tag);
+}
+
+- (void)commandDispatchUsingKeyModifiers:(id)sender window:(NSWindow*)window {
+ DCHECK(sender);
+
+ if (![sender isEnabled]) {
+ // This code is reachable e.g. if the user mashes the back button, queuing
+ // up a bunch of events before the button's enabled state is updated:
+ // http://crbug.com/63254
+ return;
+ }
+
+ // See comment above for why we do this.
+ NSWindow* targetWindow = window;
+ if ([sender respondsToSelector:@selector(window)])
+ targetWindow = [sender window];
+ Browser* browser = chrome::FindBrowserWithWindow(targetWindow);
+ DCHECK(browser);
+
+ NSInteger command = [sender tag];
+ NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
+ if ((command == IDC_RELOAD) &&
+ (modifierFlags & (NSShiftKeyMask | NSControlKeyMask))) {
+ command = IDC_RELOAD_IGNORING_CACHE;
+ // Mask off Shift and Control so they don't affect the disposition below.
+ modifierFlags &= ~(NSShiftKeyMask | NSControlKeyMask);
+ }
+ if (![[sender window] isMainWindow]) {
+ // Remove the command key from the flags, it means "keep the window in
+ // the background" in this case.
+ modifierFlags &= ~NSCommandKeyMask;
+ }
+ chrome::ExecuteCommandWithDisposition(
+ browser, command, ui::WindowOpenDispositionFromNSEventWithFlags(
+ [NSApp currentEvent], modifierFlags));
+}
+
+@end // ChromeCommandDispatchDelegate
« no previous file with comments | « chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.h ('k') | chrome/browser/ui/cocoa/chrome_event_processing_window.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698