diff options
-rw-r--r-- | ash/accelerators/accelerator_controller.cc | 5 | ||||
-rw-r--r-- | ash/accelerators/accelerator_controller.h | 3 | ||||
-rw-r--r-- | ash/accelerators/accelerator_controller_unittest.cc | 11 | ||||
-rw-r--r-- | ash/accelerators/accelerator_filter.cc | 15 | ||||
-rw-r--r-- | ash/accelerators/accelerator_filter_unittest.cc | 21 | ||||
-rw-r--r-- | ash/accelerators/focus_manager_factory.cc | 40 | ||||
-rw-r--r-- | ash/accelerators/focus_manager_factory.h | 43 | ||||
-rw-r--r-- | ash/ash.gyp | 2 | ||||
-rw-r--r-- | ash/shell.cc | 6 | ||||
-rw-r--r-- | chrome/browser/ui/browser.cc | 23 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_view.cc | 52 | ||||
-rw-r--r-- | ui/views/focus/focus_manager.cc | 105 | ||||
-rw-r--r-- | ui/views/focus/focus_manager.h | 7 | ||||
-rw-r--r-- | ui/views/focus/focus_manager_delegate.h | 40 | ||||
-rw-r--r-- | ui/views/focus/focus_manager_factory.cc | 4 | ||||
-rw-r--r-- | ui/views/focus/focus_manager_unittest.cc | 2 | ||||
-rw-r--r-- | ui/views/views.gyp | 1 |
17 files changed, 284 insertions, 96 deletions
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc index 8ad8a89..2762f94 100644 --- a/ash/accelerators/accelerator_controller.cc +++ b/ash/accelerators/accelerator_controller.cc @@ -224,6 +224,11 @@ bool AcceleratorController::Process(const ui::Accelerator& accelerator) { return accelerator_manager_->Process(accelerator); } +bool AcceleratorController::IsRegistered( + const ui::Accelerator& accelerator) const { + return accelerator_manager_->GetCurrentTarget(accelerator) != NULL; +} + void AcceleratorController::SetBrightnessControlDelegate( scoped_ptr<BrightnessControlDelegate> brightness_control_delegate) { brightness_control_delegate_.swap(brightness_control_delegate); diff --git a/ash/accelerators/accelerator_controller.h b/ash/accelerators/accelerator_controller.h index 45ca052..1c8193d 100644 --- a/ash/accelerators/accelerator_controller.h +++ b/ash/accelerators/accelerator_controller.h @@ -56,6 +56,9 @@ class ASH_EXPORT AcceleratorController : public ui::AcceleratorTarget { // Returns true if an accelerator was activated. bool Process(const ui::Accelerator& accelerator); + // Returns true if the |accelerator| is registered. + bool IsRegistered(const ui::Accelerator& accelerator) const; + // Overridden from ui::AcceleratorTarget: virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual bool CanHandleAccelerators() const OVERRIDE; diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc index 80e9a4b..502aafc 100644 --- a/ash/accelerators/accelerator_controller_unittest.cc +++ b/ash/accelerators/accelerator_controller_unittest.cc @@ -363,6 +363,17 @@ TEST_F(AcceleratorControllerTest, Process) { EXPECT_FALSE(GetController()->Process(accelerator_b)); } +TEST_F(AcceleratorControllerTest, IsRegistered) { + const ui::Accelerator accelerator_a(ui::VKEY_A, false, false, false); + const ui::Accelerator accelerator_shift_a(ui::VKEY_A, true, false, false); + TestTarget target; + GetController()->Register(accelerator_a, &target); + EXPECT_TRUE(GetController()->IsRegistered(accelerator_a)); + EXPECT_FALSE(GetController()->IsRegistered(accelerator_shift_a)); + GetController()->UnregisterAll(&target); + EXPECT_FALSE(GetController()->IsRegistered(accelerator_a)); +} + #if defined(OS_WIN) || defined(USE_X11) TEST_F(AcceleratorControllerTest, ProcessOnce) { ui::Accelerator accelerator_a(ui::VKEY_A, false, false, false); diff --git a/ash/accelerators/accelerator_filter.cc b/ash/accelerators/accelerator_filter.cc index dcc95ba..bcecdfe2 100644 --- a/ash/accelerators/accelerator_filter.cc +++ b/ash/accelerators/accelerator_filter.cc @@ -12,9 +12,22 @@ #include "ui/base/accelerators/accelerator_manager.h" namespace { + const int kModifierFlagMask = (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN); + +// Returns true if an Ash accelerator should be processed now. +bool ShouldProcessAcceleratorsNow(aura::Window* target) { + if (!target) + return true; + if (target == ash::Shell::GetInstance()->GetRootWindow()) + return true; + // Unless |target| is the root window, return false to let the custom focus + // manager (see ash/shell.cc) handle Ash accelerators. + return false; +} + } // namespace namespace ash { @@ -39,6 +52,8 @@ bool AcceleratorFilter::PreHandleKeyEvent(aura::Window* target, return false; if (event->is_char()) return false; + if (!ShouldProcessAcceleratorsNow(target)) + return false; ui::Accelerator accelerator(event->key_code(), event->flags() & kModifierFlagMask); diff --git a/ash/accelerators/accelerator_filter_unittest.cc b/ash/accelerators/accelerator_filter_unittest.cc index d2e9aa5..7f3e44b 100644 --- a/ash/accelerators/accelerator_filter_unittest.cc +++ b/ash/accelerators/accelerator_filter_unittest.cc @@ -75,7 +75,7 @@ TEST_F(AcceleratorFilterTest, TestFilterWithoutFocus) { EXPECT_EQ(1, delegate->handle_take_screenshot_count()); } -// Tests if AcceleratorFilter works with a focused window. +// Tests if AcceleratorFilter works as expected with a focused window. TEST_F(AcceleratorFilterTest, TestFilterWithFocus) { aura::Window* default_container = Shell::GetInstance()->GetContainer( internal::kShellWindowId_DefaultContainer); @@ -92,11 +92,13 @@ TEST_F(AcceleratorFilterTest, TestFilterWithFocus) { scoped_ptr<ScreenshotDelegate>(delegate).Pass()); EXPECT_EQ(0, delegate->handle_take_screenshot_count()); + // AcceleratorFilter should ignore the key events since the root window is + // not focused. aura::test::EventGenerator generator(Shell::GetRootWindow()); generator.PressKey(ui::VKEY_PRINT, 0); - EXPECT_EQ(1, delegate->handle_take_screenshot_count()); + EXPECT_EQ(0, delegate->handle_take_screenshot_count()); generator.ReleaseKey(ui::VKEY_PRINT, 0); - EXPECT_EQ(1, delegate->handle_take_screenshot_count()); + EXPECT_EQ(0, delegate->handle_take_screenshot_count()); // Reset window before |test_delegate| gets deleted. window.reset(); @@ -104,16 +106,6 @@ TEST_F(AcceleratorFilterTest, TestFilterWithFocus) { // Tests if AcceleratorFilter ignores the flag for Caps Lock. TEST_F(AcceleratorFilterTest, TestCapsLockMask) { - aura::Window* default_container = Shell::GetInstance()->GetContainer( - internal::kShellWindowId_DefaultContainer); - aura::test::TestWindowDelegate test_delegate; - scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithDelegate( - &test_delegate, - -1, - gfx::Rect(), - default_container)); - wm::ActivateWindow(window.get()); - DummyScreenshotDelegate* delegate = new DummyScreenshotDelegate; GetController()->SetScreenshotDelegate( scoped_ptr<ScreenshotDelegate>(delegate).Pass()); @@ -131,9 +123,6 @@ TEST_F(AcceleratorFilterTest, TestCapsLockMask) { EXPECT_EQ(2, delegate->handle_take_screenshot_count()); generator.ReleaseKey(ui::VKEY_PRINT, ui::EF_CAPS_LOCK_DOWN); EXPECT_EQ(2, delegate->handle_take_screenshot_count()); - - // Reset window before |test_delegate| gets deleted. - window.reset(); } } // namespace test diff --git a/ash/accelerators/focus_manager_factory.cc b/ash/accelerators/focus_manager_factory.cc new file mode 100644 index 0000000..76dcd9f --- /dev/null +++ b/ash/accelerators/focus_manager_factory.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2012 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. + +#include "ash/accelerators/focus_manager_factory.h" + +#include "ash/accelerators/accelerator_controller.h" +#include "ash/shell.h" +#include "ui/views/focus/focus_manager.h" + +namespace ash { + +AshFocusManagerFactory::AshFocusManagerFactory() {} +AshFocusManagerFactory::~AshFocusManagerFactory() {} + +views::FocusManager* AshFocusManagerFactory::CreateFocusManager( + views::Widget* widget) { + return new views::FocusManager(widget, new Delegate); +} + +bool AshFocusManagerFactory::Delegate::ProcessAccelerator( + const ui::Accelerator& accelerator) { + AcceleratorController* controller = + Shell::GetInstance()->accelerator_controller(); + if (controller) + return controller->Process(accelerator); + return false; +} + +ui::AcceleratorTarget* +AshFocusManagerFactory::Delegate::GetCurrentTargetForAccelerator( + const ui::Accelerator& accelerator) const { + AcceleratorController* controller = + Shell::GetInstance()->accelerator_controller(); + if (controller && controller->IsRegistered(accelerator)) + return controller; + return NULL; +} + +} // namespace ash diff --git a/ash/accelerators/focus_manager_factory.h b/ash/accelerators/focus_manager_factory.h new file mode 100644 index 0000000..8469ff9 --- /dev/null +++ b/ash/accelerators/focus_manager_factory.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012 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. + +#ifndef ASH_ACCELERATORS_FOCUS_MANAGER_FACTORY_H_ +#define ASH_ACCELERATORS_FOCUS_MANAGER_FACTORY_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/views/focus/focus_manager_delegate.h" +#include "ui/views/focus/focus_manager_factory.h" + +namespace ash { + +// A factory class for creating a custom views::FocusManager object which +// supports Ash shortcuts. +class AshFocusManagerFactory : public views::FocusManagerFactory { + public: + AshFocusManagerFactory(); + virtual ~AshFocusManagerFactory(); + + protected: + // views::FocusManagerFactory overrides: + virtual views::FocusManager* CreateFocusManager( + views::Widget* widget) OVERRIDE; + + private: + class Delegate : public views::FocusManagerDelegate { + public: + // views::FocusManagerDelegate overrides: + virtual bool ProcessAccelerator( + const ui::Accelerator& accelerator) OVERRIDE; + virtual ui::AcceleratorTarget* GetCurrentTargetForAccelerator( + const ui::Accelerator& accelerator) const OVERRIDE; + }; + + DISALLOW_COPY_AND_ASSIGN(AshFocusManagerFactory); +}; + +} // namespace ash + +#endif // ASH_ACCELERATORS_FOCUS_MANAGER_FACTORY_H_ diff --git a/ash/ash.gyp b/ash/ash.gyp index 91b3992..5768776 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -48,6 +48,8 @@ 'accelerators/accelerator_filter.h', 'accelerators/accelerator_table.cc', 'accelerators/accelerator_table.h', + 'accelerators/focus_manager_factory.cc', + 'accelerators/focus_manager_factory.h', 'accelerators/nested_dispatcher_controller.cc', 'accelerators/nested_dispatcher_controller.h', 'ash_switches.cc', diff --git a/ash/shell.cc b/ash/shell.cc index 663d7e1..6d11117 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <string> +#include "ash/accelerators/focus_manager_factory.h" #include "ash/ash_switches.h" #include "ash/desktop_background/desktop_background_controller.h" #include "ash/desktop_background/desktop_background_resources.h" @@ -77,6 +78,7 @@ #include "ui/gfx/screen.h" #include "ui/gfx/size.h" #include "ui/ui_controls/ui_controls.h" +#include "ui/views/focus/focus_manager_factory.h" #include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/widget.h" @@ -546,6 +548,8 @@ Shell::Shell(ShellDelegate* delegate) } Shell::~Shell() { + views::FocusManagerFactory::Install(NULL); + RemoveRootWindowEventFilter(key_rewriter_filter_.get()); RemoveRootWindowEventFilter(partial_screenshot_filter_.get()); RemoveRootWindowEventFilter(input_method_filter_.get()); @@ -752,6 +756,8 @@ void Shell::Init() { window_cycle_controller_.reset(new WindowCycleController); monitor_controller_.reset(new internal::MonitorController); screen_dimmer_.reset(new internal::ScreenDimmer); + + views::FocusManagerFactory::Install(new AshFocusManagerFactory); } aura::Window* Shell::GetContainer(int container_id) { diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index f596aa1..e348072 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -2622,32 +2622,25 @@ bool Browser::ExecuteCommandIfEnabled(int id) { bool Browser::IsReservedCommandOrKey(int command_id, const NativeWebKeyboardEvent& event) { + // In Apps mode, no keys are reserved. + if (is_app()) + return false; + #if defined(OS_CHROMEOS) // Chrome OS's top row of keys produces F1-10. Make sure that web pages - // aren't able to block Chrome from performing the standard actions for F1-F4 - // (F5-7 are grabbed by other X clients and hence don't need this protection, - // and F8-10 are handled separately in Chrome via a GDK event filter, but - // let's future-proof this). + // aren't able to block Chrome from performing the standard actions for F1-F4. + // We should not handle F5-10 here since they are processed by Ash. See also: + // crbug.com/127333#c8 ui::KeyboardCode key_code = static_cast<ui::KeyboardCode>(event.windowsKeyCode); if (key_code == ui::VKEY_F1 || key_code == ui::VKEY_F2 || key_code == ui::VKEY_F3 || - key_code == ui::VKEY_F4 || - key_code == ui::VKEY_F5 || - key_code == ui::VKEY_F6 || - key_code == ui::VKEY_F7 || - key_code == ui::VKEY_F8 || - key_code == ui::VKEY_F9 || - key_code == ui::VKEY_F10) { + key_code == ui::VKEY_F4) { return true; } #endif - // In Apps mode, no keys are reserved. - if (is_app()) - return false; - if (window_->IsFullscreen() && command_id == IDC_FULLSCREEN) return true; return command_id == IDC_CLOSE_TAB || diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 81ee794..934caa5 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -1145,13 +1145,18 @@ void BrowserView::ShowAppMenu() { bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { - if (event.type != WebKit::WebInputEvent::RawKeyDown) + *is_keyboard_shortcut = false; + + if ((event.type != WebKit::WebInputEvent::RawKeyDown) && + (event.type != WebKit::WebInputEvent::KeyUp)) { return false; + } #if defined(OS_WIN) && !defined(USE_AURA) // As Alt+F4 is the close-app keyboard shortcut, it needs processing // immediately. if (event.windowsKeyCode == ui::VKEY_F4 && + event.type == WebKit::WebInputEvent::RawKeyDown && event.modifiers == NativeWebKeyboardEvent::AltKey) { DefWindowProc(event.os_event.hwnd, event.os_event.message, event.os_event.wParam, event.os_event.lParam); @@ -1170,16 +1175,25 @@ bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, NativeWebKeyboardEvent::ControlKey, (event.modifiers & NativeWebKeyboardEvent::AltKey) == NativeWebKeyboardEvent::AltKey); + if (event.type == WebKit::WebInputEvent::KeyUp) + accelerator.set_type(ui::ET_KEY_RELEASED); + + // What we have to do here is as follows: + // - If the |browser_| is for an app, do nothing. + // - If the |browser_| is not for an app, and the |accelerator| is not + // associated with the browser (e.g. an Ash shortcut), process it. + // - If the |browser_| is not for an app, and the |accelerator| is associated + // with the browser, and it is a reserved one (e.g. Ctrl-t), process it. + // - If the |browser_| is not for an app, and the |accelerator| is associated + // with the browser, and it is not a reserved one, do nothing. - // We first find out the browser command associated to the |event|. - // Then if the command is a reserved one, and should be processed - // immediately according to the |event|, the command will be executed - // immediately. Otherwise we just set |*is_keyboard_shortcut| properly and - // return false. + if (browser_->is_app()) { + // We don't have to flip |is_keyboard_shortcut| here. If we do that, the app + // might not be able to see a subsequent Char event. See OnHandleInputEvent + // in content/renderer/render_widget.cc for details. + return false; + } - // This piece of code is based on the fact that accelerators registered - // into the |focus_manager| may only trigger a browser command execution. - // // Here we need to retrieve the command id (if any) associated to the // keyboard event. Instead of looking up the command id in the // |accelerator_table_| by ourselves, we block the command execution of @@ -1187,21 +1201,27 @@ bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, // |focus_manager| as if we are activating an accelerator key. // Then we can retrieve the command id from the |browser_| object. browser_->SetBlockCommandExecution(true); - focus_manager->ProcessAccelerator(accelerator); - int id = browser_->GetLastBlockedCommand(NULL); + // If the |accelerator| is a non-browser shortcut (e.g. Ash shortcut), the + // command execution cannot be blocked and true is returned. However, it is + // okay as long as is_app() is false. See comments in this function. + const bool processed = focus_manager->ProcessAccelerator(accelerator); + const int id = browser_->GetLastBlockedCommand(NULL); browser_->SetBlockCommandExecution(false); - if (id == -1) - return false; - // Executing the command may cause |this| object to be destroyed. if (browser_->IsReservedCommandOrKey(id, event)) { UpdateAcceleratorMetrics(accelerator, id); return browser_->ExecuteCommandIfEnabled(id); } - DCHECK(is_keyboard_shortcut != NULL); - *is_keyboard_shortcut = true; + if (id != -1) { + // |accelerator| is a non-reserved browser shortcut (e.g. Ctrl+t). + if (event.type == WebKit::WebInputEvent::RawKeyDown) + *is_keyboard_shortcut = true; + } else if (processed) { + // |accelerator| is a non-browser shortcut (e.g. F5-F10 on Ash). + return true; + } return false; } diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc index 1467f25..1799831 100644 --- a/ui/views/focus/focus_manager.cc +++ b/ui/views/focus/focus_manager.cc @@ -11,6 +11,7 @@ #include "build/build_config.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/keycodes/keyboard_codes.h" +#include "ui/views/focus/focus_manager_delegate.h" #include "ui/views/focus/focus_search.h" #include "ui/views/focus/view_storage.h" #include "ui/views/focus/widget_focus_manager.h" @@ -20,8 +21,9 @@ namespace views { -FocusManager::FocusManager(Widget* widget) +FocusManager::FocusManager(Widget* widget, FocusManagerDelegate* delegate) : widget_(widget), + delegate_(delegate), focused_view_(NULL), accelerator_manager_(new ui::AcceleratorManager), focus_change_reason_(kReasonDirectFocusChange), @@ -54,22 +56,23 @@ bool FocusManager::OnKeyEvent(const KeyEvent& event) { if (event.type() == ui::ET_KEY_PRESSED) { // VKEY_MENU is triggered by key release event. // FocusManager::OnKeyEvent() returns false when the key has been consumed. - if (key_code == ui::VKEY_MENU) { + if ((key_code == ui::VKEY_MENU) && + (event.flags() & ~ui::EF_ALT_DOWN) == 0) { should_handle_menu_key_release_ = true; return false; } - // Pass through to the reset of OnKeyEvent. + // Pass through to the rest of OnKeyEvent. } else if (key_code == ui::VKEY_MENU && should_handle_menu_key_release_ && (event.flags() & ~ui::EF_ALT_DOWN) == 0) { // Trigger VKEY_MENU when only this key is pressed and released, and both // press and release events are not handled by others. ui::Accelerator accelerator(ui::VKEY_MENU, false, false, false); return ProcessAccelerator(accelerator); - } else { + } else if (event.type() != ui::ET_KEY_RELEASED) { return false; } #else - if (event.type() != ui::ET_KEY_PRESSED) + if (event.type() != ui::ET_KEY_PRESSED && event.type() != ui::ET_KEY_RELEASED) return false; #endif @@ -77,53 +80,57 @@ bool FocusManager::OnKeyEvent(const KeyEvent& event) { event.IsShiftDown(), event.IsControlDown(), event.IsAltDown()); + accelerator.set_type(event.type()); + if (event.type() == ui::ET_KEY_PRESSED) { #if defined(OS_WIN) - // If the focused view wants to process the key event as is, let it be. - // This is not used for linux/aura. - if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event) && - !accelerator_manager_->HasPriorityHandler(accelerator)) - return true; + // If the focused view wants to process the key event as is, let it be. + // This is not used for linux/aura. + if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event) && + !accelerator_manager_->HasPriorityHandler(accelerator)) + return true; #endif - // Intercept Tab related messages for focus traversal. - // Note that we don't do focus traversal if the root window is not part of the - // active window hierarchy as this would mean we have no focused view and - // would focus the first focusable view. + // Intercept Tab related messages for focus traversal. + // Note that we don't do focus traversal if the root window is not part of + // the active window hierarchy as this would mean we have no focused view + // and would focus the first focusable view. #if defined(OS_WIN) && !defined(USE_AURA) - HWND top_window = widget_->GetNativeView(); - HWND active_window = ::GetActiveWindow(); - if ((active_window == top_window || ::IsChild(active_window, top_window)) && - IsTabTraversalKeyEvent(event)) { - AdvanceFocus(event.IsShiftDown()); - return false; - } + HWND top_window = widget_->GetNativeView(); + HWND active_window = ::GetActiveWindow(); + if ((active_window == top_window || ::IsChild(active_window, top_window)) && + IsTabTraversalKeyEvent(event)) { + AdvanceFocus(event.IsShiftDown()); + return false; + } #else - if (IsTabTraversalKeyEvent(event)) { - AdvanceFocus(event.IsShiftDown()); - return false; - } + if (IsTabTraversalKeyEvent(event)) { + AdvanceFocus(event.IsShiftDown()); + return false; + } #endif - // Intercept arrow key messages to switch between grouped views. - if (focused_view_ && focused_view_->GetGroup() != -1 && - (key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN || - key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT)) { - bool next = (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN); - View::Views views; - focused_view_->parent()->GetViewsInGroup(focused_view_->GetGroup(), &views); - View::Views::const_iterator i( - std::find(views.begin(), views.end(), focused_view_)); - DCHECK(i != views.end()); - int index = static_cast<int>(i - views.begin()); - index += next ? 1 : -1; - if (index < 0) { - index = static_cast<int>(views.size()) - 1; - } else if (index >= static_cast<int>(views.size())) { - index = 0; + // Intercept arrow key messages to switch between grouped views. + if (focused_view_ && focused_view_->GetGroup() != -1 && + (key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN || + key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT)) { + bool next = (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN); + View::Views views; + focused_view_->parent()->GetViewsInGroup(focused_view_->GetGroup(), + &views); + View::Views::const_iterator i( + std::find(views.begin(), views.end(), focused_view_)); + DCHECK(i != views.end()); + int index = static_cast<int>(i - views.begin()); + index += next ? 1 : -1; + if (index < 0) { + index = static_cast<int>(views.size()) - 1; + } else if (index >= static_cast<int>(views.size())) { + index = 0; + } + SetFocusedViewWithReason(views[index], kReasonFocusTraversal); + return false; } - SetFocusedViewWithReason(views[index], kReasonFocusTraversal); - return false; } // Process keyboard accelerators. @@ -420,7 +427,11 @@ void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget* target) { } bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) { - return accelerator_manager_->Process(accelerator); + if (accelerator_manager_->Process(accelerator)) + return true; + if (delegate_.get()) + return delegate_->ProcessAccelerator(accelerator); + return false; } void FocusManager::MaybeResetMenuKeyState(const KeyEvent& key) { @@ -436,7 +447,11 @@ void FocusManager::MaybeResetMenuKeyState(const KeyEvent& key) { ui::AcceleratorTarget* FocusManager::GetCurrentTargetForAccelerator( const ui::Accelerator& accelerator) const { - return accelerator_manager_->GetCurrentTarget(accelerator); + ui::AcceleratorTarget* target = + accelerator_manager_->GetCurrentTarget(accelerator); + if (!target && delegate_.get()) + target = delegate_->GetCurrentTargetForAccelerator(accelerator); + return target; } bool FocusManager::HasPriorityHandler( diff --git a/ui/views/focus/focus_manager.h b/ui/views/focus/focus_manager.h index d8c60a6..cf6cb86 100644 --- a/ui/views/focus/focus_manager.h +++ b/ui/views/focus/focus_manager.h @@ -80,6 +80,7 @@ class AcceleratorManager; namespace views { +class FocusManagerDelegate; class FocusSearch; class RootView; class View; @@ -136,7 +137,7 @@ class VIEWS_EXPORT FocusManager { kReasonDirectFocusChange }; - explicit FocusManager(Widget* widget); + FocusManager(Widget* widget, FocusManagerDelegate* delegate); virtual ~FocusManager(); // Processes the passed key event for accelerators and tab traversal. @@ -270,6 +271,10 @@ class VIEWS_EXPORT FocusManager { // The top-level Widget this FocusManager is associated with. Widget* widget_; + // The object which handles an accelerator when |accelerator_manager_| doesn't + // handle it. + scoped_ptr<FocusManagerDelegate> delegate_; + // The view that currently is focused. View* focused_view_; diff --git a/ui/views/focus/focus_manager_delegate.h b/ui/views/focus/focus_manager_delegate.h new file mode 100644 index 0000000..1763ed2 --- /dev/null +++ b/ui/views/focus/focus_manager_delegate.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012 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. + +#ifndef UI_VIEWS_FOCUS_FOCUS_MANAGER_DELEGATE_H_ +#define UI_VIEWS_FOCUS_FOCUS_MANAGER_DELEGATE_H_ +#pragma once + +#include "ui/views/views_export.h" + +namespace ui { +class Accelerator; +class AcceleratorTarget; +} + +namespace views { + +// Delegate interface for views::FocusManager. +class VIEWS_EXPORT FocusManagerDelegate { + public: + virtual ~FocusManagerDelegate() {} + + // Activate the target associated with the specified accelerator. + // First, AcceleratorPressed handler of the most recently registered target + // is called, and if that handler processes the event (i.e. returns true), + // this method immediately returns. If not, we do the same thing on the next + // target, and so on. + // Returns true if an accelerator was activated. + virtual bool ProcessAccelerator(const ui::Accelerator& accelerator) = 0; + + // Returns the AcceleratorTarget that should be activated for the specified + // keyboard accelerator, or NULL if no view is registered for that keyboard + // accelerator. + virtual ui::AcceleratorTarget* GetCurrentTargetForAccelerator( + const ui::Accelerator& accelerator) const = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_FOCUS_FOCUS_MANAGER_DELEGATE_H_ diff --git a/ui/views/focus/focus_manager_factory.cc b/ui/views/focus/focus_manager_factory.cc index e3ec805..b8e1a7a 100644 --- a/ui/views/focus/focus_manager_factory.cc +++ b/ui/views/focus/focus_manager_factory.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -18,7 +18,7 @@ class DefaultFocusManagerFactory : public views::FocusManagerFactory { protected: virtual FocusManager* CreateFocusManager(views::Widget* widget) OVERRIDE { - return new FocusManager(widget); + return new FocusManager(widget, NULL /* delegate */); } private: diff --git a/ui/views/focus/focus_manager_unittest.cc b/ui/views/focus/focus_manager_unittest.cc index 01a33ab..06b8448 100644 --- a/ui/views/focus/focus_manager_unittest.cc +++ b/ui/views/focus/focus_manager_unittest.cc @@ -502,7 +502,7 @@ class FocusManagerDtorTest : public FocusManagerTest { class FocusManagerDtorTracked : public FocusManager { public: FocusManagerDtorTracked(Widget* widget, DtorTrackVector* dtor_tracker) - : FocusManager(widget), + : FocusManager(widget, NULL /* delegate */), dtor_tracker_(dtor_tracker) { } diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 47c1099..7d52dec 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -249,6 +249,7 @@ 'focus/external_focus_tracker.h', 'focus/focus_manager.cc', 'focus/focus_manager.h', + 'focus/focus_manager_delegate.h', 'focus/focus_manager_factory.cc', 'focus/focus_manager_factory.h', 'focus/focus_search.cc', |