summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views')
-rw-r--r--chrome/browser/views/accelerator_table_gtk.cc2
-rw-r--r--chrome/browser/views/frame/browser_view.cc34
-rw-r--r--chrome/browser/views/frame/browser_view.h4
-rw-r--r--chrome/browser/views/toolbar_view.cc188
-rw-r--r--chrome/browser/views/toolbar_view.h43
5 files changed, 259 insertions, 12 deletions
diff --git a/chrome/browser/views/accelerator_table_gtk.cc b/chrome/browser/views/accelerator_table_gtk.cc
index 28a630b..f3a7a54 100644
--- a/chrome/browser/views/accelerator_table_gtk.cc
+++ b/chrome/browser/views/accelerator_table_gtk.cc
@@ -20,6 +20,8 @@ const AcceleratorMapping kAcceleratorMap[] = {
{ base::VKEY_L, false, true, false, IDC_FOCUS_LOCATION },
{ base::VKEY_D, false, false, true, IDC_FOCUS_LOCATION },
{ base::VKEY_F6, false, false, false, IDC_FOCUS_LOCATION },
+ { base::VKEY_F10, false, false, false, IDC_FOCUS_MENU_BAR },
+ { base::VKEY_MENU, false, false, false, IDC_FOCUS_MENU_BAR },
// Tab/window controls.
{ base::VKEY_T, false, true, false, IDC_NEW_TAB },
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 1970be1..f3108da 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -638,10 +638,10 @@ void BrowserView::TraverseNextAccessibleToolbar(bool forward) {
// TODO(mohamed) This needs to be smart, that applies to all toolbars.
// Currently it just traverses between bookmarks and toolbar.
if (!forward && toolbar_->IsVisible() && toolbar_->IsEnabled()) {
- toolbar_->InitiateTraversal(last_focused_view_storage_id_);
+ toolbar_->RequestFocus();
} else if (forward && bookmark_bar_view_->IsVisible() &&
bookmark_bar_view_->IsEnabled()) {
- bookmark_bar_view_->InitiateTraversal(last_focused_view_storage_id_);
+ bookmark_bar_view_->RequestFocus();
}
}
@@ -876,17 +876,31 @@ void BrowserView::UpdateToolbar(TabContents* contents,
}
void BrowserView::FocusToolbar() {
- // Remove existing views in the storage, traversal should be restarted.
+ // Start the traversal within the main toolbar, passing it the storage id
+ // of the view where focus should be returned if the user exits the toolbar.
+ SaveFocusedView();
+ toolbar_->InitiateTraversal(last_focused_view_storage_id_);
+}
+
+void BrowserView::FocusPageAndAppMenus() {
+ // Chrome doesn't have a traditional menu bar, but it has menu buttons in
+ // the main toolbar that play the same role. If the user presses a key
+ // that would typically focus the menu bar, tell the toolbar to focus
+ // the first menu button. Pass it the storage id of the view where
+ // focus should be returned if the user presses escape.
+ //
+ // Not used on the Mac, which has a normal menu bar.
+ SaveFocusedView();
+ toolbar_->EnterMenuBarEmulationMode(last_focused_view_storage_id_, NULL);
+}
+
+void BrowserView::SaveFocusedView() {
views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance();
if (view_storage->RetrieveView(last_focused_view_storage_id_))
view_storage->RemoveView(last_focused_view_storage_id_);
-
- // Store the last focused view into the storage, to handle existing traversal.
- view_storage->StoreView(last_focused_view_storage_id_,
- GetRootView()->GetFocusedView());
-
- // Start the traversal within the main toolbar.
- toolbar_->InitiateTraversal(last_focused_view_storage_id_);
+ views::View* focused_view = GetRootView()->GetFocusedView();
+ if (focused_view)
+ view_storage->StoreView(last_focused_view_storage_id_, focused_view);
}
void BrowserView::DestroyBrowser() {
diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h
index bf753e9..026fa53 100644
--- a/chrome/browser/views/frame/browser_view.h
+++ b/chrome/browser/views/frame/browser_view.h
@@ -270,6 +270,7 @@ class BrowserView : public BrowserBubbleHost,
virtual void UpdateStopGoState(bool is_loading, bool force);
virtual void UpdateToolbar(TabContents* contents, bool should_restore_state);
virtual void FocusToolbar();
+ virtual void FocusPageAndAppMenus();
virtual void DestroyBrowser();
virtual bool IsBookmarkBarVisible() const;
virtual bool IsBookmarkBarAnimating() const;
@@ -472,6 +473,9 @@ class BrowserView : public BrowserBubbleHost,
// Initialize the hung plugin detector.
void InitHangMonitor();
+ // Save the current focused view to view storage
+ void SaveFocusedView();
+
// Initialize class statics.
static void InitClass();
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index 9670c24..0153012 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -35,6 +35,7 @@
#include "chrome/browser/views/bookmark_menu_button.h"
#include "chrome/browser/views/browser_actions_container.h"
#include "chrome/browser/views/event_utils.h"
+#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/go_button.h"
#include "chrome/browser/views/location_bar_view.h"
#include "chrome/browser/views/toolbar_star_toggle.h"
@@ -52,6 +53,8 @@
#include "views/controls/label.h"
#include "views/controls/menu/menu_2.h"
#include "views/drag_utils.h"
+#include "views/focus/view_storage.h"
+#include "views/widget/tooltip_manager.h"
#include "views/window/non_client_view.h"
#include "views/window/window.h"
@@ -90,7 +93,9 @@ ToolbarView::ToolbarView(Browser* browser)
bookmark_menu_(NULL),
profile_(NULL),
browser_(browser),
- profiles_menu_contents_(NULL) {
+ profiles_menu_contents_(NULL),
+ last_focused_view_storage_id_(-1),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
SetID(VIEW_ID_TOOLBAR);
browser_->command_updater()->AddCommandObserver(IDC_BACK, this);
browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this);
@@ -108,6 +113,14 @@ ToolbarView::ToolbarView(Browser* browser)
}
}
+ToolbarView::~ToolbarView() {
+ if (page_menu_->HasFocus() || app_menu_->HasFocus()) {
+ views::FocusManager* focus_manager = GetFocusManager();
+ focus_manager->UnregisterAccelerators(this);
+ focus_manager->RemoveFocusChangeListener(this);
+ }
+}
+
void ToolbarView::Init(Profile* profile) {
back_menu_model_.reset(new BackForwardMenuModel(
browser_, BackForwardMenuModel::BACKWARD_MENU));
@@ -148,6 +161,66 @@ void ToolbarView::SetAppMenuModel(AppMenuModel* model) {
app_menu_menu_.reset(new views::Menu2(app_menu_model_.get()));
}
+void ToolbarView::EnterMenuBarEmulationMode(int last_focused_view_storage_id,
+ views::MenuButton* menu_to_focus) {
+ last_focused_view_storage_id_ = last_focused_view_storage_id;
+ if (!menu_to_focus)
+ menu_to_focus = page_menu_;
+
+ // If we're already in the menu bar emulation mode, just set the focus.
+ if (page_menu_->IsFocusable() && app_menu_->IsFocusable()) {
+ menu_to_focus->RequestFocus();
+ return;
+ }
+
+ // Make the menus focusable and set focus to the initial menu.
+ page_menu_->SetFocusable(true);
+ app_menu_->SetFocusable(true);
+ menu_to_focus->RequestFocus();
+
+ // Listen so we know when focus has moved to something other than one
+ // of these menus.
+ views::FocusManager* focus_manager = GetFocusManager();
+ focus_manager->AddFocusChangeListener(this);
+
+ // Add accelerators so that the usual keys used to interact with a
+ // menu bar work as expected.
+ views::Accelerator return_key(base::VKEY_RETURN, false, false, false);
+ focus_manager->RegisterAccelerator(return_key, this);
+ views::Accelerator space(base::VKEY_SPACE, false, false, false);
+ focus_manager->RegisterAccelerator(space, this);
+ views::Accelerator escape(base::VKEY_ESCAPE, false, false, false);
+ focus_manager->RegisterAccelerator(escape, this);
+ views::Accelerator down(base::VKEY_DOWN, false, false, false);
+ focus_manager->RegisterAccelerator(down, this);
+ views::Accelerator up(base::VKEY_UP, false, false, false);
+ focus_manager->RegisterAccelerator(up, this);
+ views::Accelerator left(base::VKEY_LEFT, false, false, false);
+ focus_manager->RegisterAccelerator(left, this);
+ views::Accelerator right(base::VKEY_RIGHT, false, false, false);
+ focus_manager->RegisterAccelerator(right, this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToolbarView, FocusChangeListener overrides:
+
+void ToolbarView::FocusWillChange(views::View* focused_before,
+ views::View* focused_now) {
+ // If the focus is switching to something outside the menu bar,
+ // take it out of the focus traversal.
+ if (focused_now != NULL &&
+ focused_now != page_menu_ &&
+ focused_now != app_menu_) {
+ // Post ExitMenuBarEmulationMode to the queue rather than running it
+ // right away, because otherwise we'll remove ourselves from the
+ // list of listeners while FocusManager is in the middle of iterating
+ // over that list.
+ MessageLoop::current()->PostTask(
+ FROM_HERE, method_factory_.NewRunnableMethod(
+ &ToolbarView::ExitMenuBarEmulationMode));
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, AccessibleToolbarView overrides:
@@ -324,6 +397,43 @@ void ToolbarView::ExecuteCommand(int command_id) {
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::View overrides:
+bool ToolbarView::AcceleratorPressed(
+ const views::Accelerator& accelerator) {
+ // The only accelerators we handle here are if the menus are focused.
+ views::View* focused_view = GetFocusManager()->GetFocusedView();
+ if (focused_view != page_menu_ && focused_view != app_menu_) {
+ ExitMenuBarEmulationMode();
+ return false;
+ }
+
+ // Safe to cast, given the check above.
+ views::MenuButton* menu = static_cast<views::MenuButton*>(focused_view);
+ switch (accelerator.GetKeyCode()) {
+ case base::VKEY_ESCAPE:
+ RestoreLastFocusedView();
+ return true;
+ case base::VKEY_LEFT:
+ case base::VKEY_RIGHT:
+ if (menu == app_menu_)
+ page_menu_->RequestFocus();
+ else
+ app_menu_->RequestFocus();
+ return true;
+ case base::VKEY_UP:
+ case base::VKEY_DOWN:
+ case base::VKEY_RETURN:
+ case base::VKEY_SPACE:
+ // Hide the tooltip before activating a menu button.
+ if (GetWidget()->GetTooltipManager())
+ GetWidget()->GetTooltipManager()->HideKeyboardTooltip();
+
+ ActivateMenuButton(menu);
+ return true;
+ default:
+ return false;
+ }
+}
+
gfx::Size ToolbarView::GetPreferredSize() {
if (IsDisplayModeNormal()) {
int min_width = kControlIndent + back_->GetPreferredSize().width() +
@@ -709,10 +819,86 @@ void ToolbarView::RunPageMenu(const gfx::Point& pt) {
page_menu_model_.reset(new PageMenuModel(this, browser_));
page_menu_menu_.reset(new views::Menu2(page_menu_model_.get()));
page_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
+ SwitchToOtherMenuIfNeeded(page_menu_menu_.get(), app_menu_);
}
void ToolbarView::RunAppMenu(const gfx::Point& pt) {
if (app_menu_model_->BuildProfileSubMenu())
app_menu_menu_->Rebuild();
app_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
+ SwitchToOtherMenuIfNeeded(app_menu_menu_.get(), page_menu_);
+}
+
+void ToolbarView::SwitchToOtherMenuIfNeeded(
+ views::Menu2* previous_menu, views::MenuButton* next_menu_button) {
+ // If the user tried to move to the right or left, switch from the
+ // app menu to the page menu. Switching to the next menu is delayed
+ // until the next event loop so that the call stack that initiated
+ // activating the first menu can return. (If we didn't do this, the
+ // call stack would grow each time the user switches menus, and
+ // the actions taken after the user finally exits a menu would cause
+ // flicker.)
+ views::MenuWrapper::MenuAction action = previous_menu->GetMenuAction();
+ if (action == views::MenuWrapper::MENU_ACTION_NEXT ||
+ action == views::MenuWrapper::MENU_ACTION_PREVIOUS) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, method_factory_.NewRunnableMethod(
+ &ToolbarView::ActivateMenuButton,
+ next_menu_button));
+ }
+}
+
+void ToolbarView::ActivateMenuButton(views::MenuButton* menu_button) {
+#if defined(OS_LINUX)
+ // Under GTK, opening a pop-up menu causes the main window to lose focus.
+ // Focus is automatically returned when the menu closes.
+ //
+ // Make sure that the menu button being activated has focus, so that
+ // when the user escapes from the menu without selecting anything, focus
+ // will be returned here.
+ if (!menu_button->HasFocus()) {
+ menu_button->RequestFocus();
+ GetFocusManager()->StoreFocusedView();
+ }
+#endif
+
+#if defined(OS_WIN)
+ // On Windows, we have to explicitly clear the focus before opening
+ // the pop-up menu, then set the focus again when it closes.
+ GetFocusManager()->ClearFocus();
+#endif
+
+ // Tell the menu button to activate, opening its pop-up menu.
+ menu_button->Activate();
+
+#if defined(OS_WIN)
+ EnterMenuBarEmulationMode(last_focused_view_storage_id_, menu_button);
+#endif
+}
+
+void ToolbarView::ExitMenuBarEmulationMode() {
+ if (page_menu_->HasFocus() || app_menu_->HasFocus())
+ RestoreLastFocusedView();
+
+ views::FocusManager* focus_manager = GetFocusManager();
+ focus_manager->UnregisterAccelerators(this);
+ focus_manager->RemoveFocusChangeListener(this);
+ page_menu_->SetFocusable(false);
+ app_menu_->SetFocusable(false);
+}
+
+void ToolbarView::RestoreLastFocusedView() {
+ views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance();
+ views::View* last_focused_view =
+ view_storage->RetrieveView(last_focused_view_storage_id_);
+ if (last_focused_view) {
+ last_focused_view->RequestFocus();
+ } else {
+ // Focus the location bar
+ views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName);
+ if (view) {
+ BrowserView* browser_view = static_cast<BrowserView*>(view);
+ browser_view->SetFocusToLocationBar();
+ }
+ }
}
diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h
index 23f5232..9425b56 100644
--- a/chrome/browser/views/toolbar_view.h
+++ b/chrome/browser/views/toolbar_view.h
@@ -36,6 +36,7 @@ class Menu2;
class ToolbarView : public AccessibleToolbarView,
public views::ViewMenuDelegate,
public views::DragController,
+ public views::FocusChangeListener,
public menus::SimpleMenuModel::Delegate,
public LocationBarView::Delegate,
public NotificationObserver,
@@ -44,7 +45,7 @@ class ToolbarView : public AccessibleToolbarView,
public BubblePositioner {
public:
explicit ToolbarView(Browser* browser);
- virtual ~ToolbarView() { }
+ virtual ~ToolbarView();
// Create the contents of the Browser Toolbar
void Init(Profile* profile);
@@ -62,6 +63,19 @@ class ToolbarView : public AccessibleToolbarView,
// Sets the app menu model.
void SetAppMenuModel(AppMenuModel* model);
+ // Focuses the page menu and enters a special mode where the page
+ // and app menus are focusable and allow for keyboard navigation just
+ // like a normal menu bar. As soon as focus leaves one of the menus,
+ // the special mode is exited.
+ //
+ // Pass it the storage id of the view where focus should be returned
+ // if the user escapes, and the menu button to focus initially. If
+ // |menu_to_focus| is NULL, it will focus the page menu by default.
+ //
+ // Not used on the Mac, which has a "normal" menu bar.
+ void EnterMenuBarEmulationMode(int last_focused_view_storage_id,
+ views::MenuButton* menu_to_focus);
+
// Accessors...
Browser* browser() const { return browser_; }
BrowserActionsContainer* browser_actions() const { return browser_actions_; }
@@ -71,6 +85,10 @@ class ToolbarView : public AccessibleToolbarView,
views::MenuButton* page_menu() const { return page_menu_; }
views::MenuButton* app_menu() const { return app_menu_; }
+ // Overridden from views::FocusChangeListener:
+ virtual void FocusWillChange(views::View* focused_before,
+ views::View* focused_now);
+
// Overridden from AccessibleToolbarView:
virtual bool IsAccessibleViewTraversable(views::View* view);
@@ -106,6 +124,7 @@ class ToolbarView : public AccessibleToolbarView,
virtual void ExecuteCommand(int command_id);
// Overridden from views::View:
+ virtual bool AcceleratorPressed(const views::Accelerator& accelerator);
virtual gfx::Size GetPreferredSize();
virtual void Layout();
virtual void Paint(gfx::Canvas* canvas);
@@ -138,6 +157,13 @@ class ToolbarView : public AccessibleToolbarView,
void RunPageMenu(const gfx::Point& pt);
void RunAppMenu(const gfx::Point& pt);
+ // Check if the menu exited with a code indicating the user wants to
+ // switch to the other menu, and then switch to that other menu.
+ void SwitchToOtherMenuIfNeeded(views::Menu2* previous_menu,
+ views::MenuButton* next_menu_button);
+
+ void ActivateMenuButton(views::MenuButton* menu_button);
+
// Types of display mode this toolbar can have.
enum DisplayMode {
DISPLAYMODE_NORMAL, // Normal toolbar with buttons, etc.
@@ -148,6 +174,14 @@ class ToolbarView : public AccessibleToolbarView,
return display_mode_ == DISPLAYMODE_NORMAL;
}
+ // Take the menus out of the focus traversal, unregister accelerators,
+ // and stop listening to focus change events.
+ void ExitMenuBarEmulationMode();
+
+ // Restore the view that was focused before EnterMenuBarEmulationMode
+ // was called.
+ void RestoreLastFocusedView();
+
scoped_ptr<BackForwardMenuModel> back_menu_model_;
scoped_ptr<BackForwardMenuModel> forward_menu_model_;
@@ -186,6 +220,13 @@ class ToolbarView : public AccessibleToolbarView,
// TODO(beng): build these into MenuButton.
scoped_ptr<views::Menu2> page_menu_menu_;
scoped_ptr<views::Menu2> app_menu_menu_;
+
+ // Storage id for the last view that was focused before focus
+ // was given to one of the toolbar views.
+ int last_focused_view_storage_id_;
+
+ // Used to post tasks to switch to the next/previous menu.
+ ScopedRunnableMethodFactory<ToolbarView> method_factory_;
};
#endif // CHROME_BROWSER_VIEWS_TOOLBAR_VIEW_H_