summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/accessible_pane_view.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/accessible_pane_view.cc')
-rw-r--r--chrome/browser/views/accessible_pane_view.cc251
1 files changed, 251 insertions, 0 deletions
diff --git a/chrome/browser/views/accessible_pane_view.cc b/chrome/browser/views/accessible_pane_view.cc
new file mode 100644
index 0000000..b876940
--- /dev/null
+++ b/chrome/browser/views/accessible_pane_view.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2010 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 "base/logging.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/browser/views/location_bar/location_bar_view.h"
+#include "chrome/browser/views/accessible_pane_view.h"
+#include "views/controls/button/menu_button.h"
+#include "views/controls/native/native_view_host.h"
+#include "views/focus/focus_search.h"
+#include "views/focus/view_storage.h"
+#include "views/widget/tooltip_manager.h"
+#include "views/widget/widget.h"
+
+AccessiblePaneView::AccessiblePaneView()
+ : pane_has_focus_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ focus_manager_(NULL),
+ home_key_(app::VKEY_HOME, false, false, false),
+ end_key_(app::VKEY_END, false, false, false),
+ escape_key_(app::VKEY_ESCAPE, false, false, false),
+ left_key_(app::VKEY_LEFT, false, false, false),
+ right_key_(app::VKEY_RIGHT, false, false, false),
+ last_focused_view_storage_id_(-1) {
+ focus_search_.reset(new views::FocusSearch(this, true, true));
+}
+
+AccessiblePaneView::~AccessiblePaneView() {
+ if (pane_has_focus_) {
+ focus_manager_->RemoveFocusChangeListener(this);
+ }
+}
+
+bool AccessiblePaneView::SetPaneFocus(int view_storage_id,
+ views::View* initial_focus) {
+ if (!IsVisible())
+ return false;
+
+ // Save the storage id to the last focused view. This would be used to request
+ // focus to the view when the traversal is ended.
+ last_focused_view_storage_id_ = view_storage_id;
+
+ if (!focus_manager_)
+ focus_manager_ = GetFocusManager();
+
+ // Use the provided initial focus if it's visible and enabled, otherwise
+ // use the first focusable child.
+ if (!initial_focus ||
+ !IsParentOf(initial_focus) ||
+ !initial_focus->IsVisible() ||
+ !initial_focus->IsEnabled()) {
+ initial_focus = GetFirstFocusableChild();
+ }
+
+ // Return false if there are no focusable children.
+ if (!initial_focus)
+ return false;
+
+ // Set focus to the initial view. If it's a location bar, use a special
+ // method that tells it to select all, also.
+ if (initial_focus->GetClassName() == LocationBarView::kViewClassName) {
+ static_cast<LocationBarView*>(initial_focus)->FocusLocation(true);
+ } else {
+ focus_manager_->SetFocusedView(initial_focus);
+ }
+
+ // If we already have pane focus, we're done.
+ if (pane_has_focus_)
+ return true;
+
+ // Otherwise, set accelerators and start listening for focus change events.
+ pane_has_focus_ = true;
+ focus_manager_->RegisterAccelerator(home_key_, this);
+ focus_manager_->RegisterAccelerator(end_key_, this);
+ focus_manager_->RegisterAccelerator(escape_key_, this);
+ focus_manager_->RegisterAccelerator(left_key_, this);
+ focus_manager_->RegisterAccelerator(right_key_, this);
+ focus_manager_->AddFocusChangeListener(this);
+
+ return true;
+}
+
+bool AccessiblePaneView::SetPaneFocusAndFocusDefault(
+ int view_storage_id) {
+ return SetPaneFocus(view_storage_id, GetDefaultFocusableChild());
+}
+
+void AccessiblePaneView::RemovePaneFocus() {
+ focus_manager_->RemoveFocusChangeListener(this);
+ pane_has_focus_ = false;
+
+ focus_manager_->UnregisterAccelerator(home_key_, this);
+ focus_manager_->UnregisterAccelerator(end_key_, this);
+ focus_manager_->UnregisterAccelerator(escape_key_, this);
+ focus_manager_->UnregisterAccelerator(left_key_, this);
+ focus_manager_->UnregisterAccelerator(right_key_, this);
+}
+
+void AccessiblePaneView::LocationBarSelectAll() {
+ views::View* focused_view = GetFocusManager()->GetFocusedView();
+ if (focused_view &&
+ focused_view->GetClassName() == LocationBarView::kViewClassName) {
+ static_cast<LocationBarView*>(focused_view)->SelectAll();
+ }
+}
+
+void AccessiblePaneView::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) {
+ focus_manager_->SetFocusedViewWithReason(
+ last_focused_view, views::FocusManager::kReasonFocusRestore);
+ } else {
+ // Focus the location bar
+ views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName);
+ if (view) {
+ BrowserView* browser_view = static_cast<BrowserView*>(view);
+ browser_view->SetFocusToLocationBar(false);
+ }
+ }
+}
+
+views::View* AccessiblePaneView::GetFirstFocusableChild() {
+ FocusTraversable* dummy_focus_traversable;
+ views::View* dummy_focus_traversable_view;
+ return focus_search_->FindNextFocusableView(
+ NULL, false, views::FocusSearch::DOWN, false,
+ &dummy_focus_traversable, &dummy_focus_traversable_view);
+}
+
+views::View* AccessiblePaneView::GetLastFocusableChild() {
+ FocusTraversable* dummy_focus_traversable;
+ views::View* dummy_focus_traversable_view;
+ return focus_search_->FindNextFocusableView(
+ this, true, views::FocusSearch::DOWN, false,
+ &dummy_focus_traversable, &dummy_focus_traversable_view);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View overrides:
+
+views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
+ if (pane_has_focus_)
+ return this;
+ else
+ return NULL;
+}
+
+bool AccessiblePaneView::AcceleratorPressed(
+ const views::Accelerator& accelerator) {
+ // Special case: don't handle any accelerators for the location bar,
+ // so that it behaves exactly the same whether you focus it with Ctrl+L
+ // or F6 or Alt+D or Alt+Shift+T.
+ views::View* focused_view = focus_manager_->GetFocusedView();
+ if ((focused_view->GetClassName() == LocationBarView::kViewClassName ||
+ focused_view->GetClassName() == views::NativeViewHost::kViewClassName)) {
+ return false;
+ }
+
+ switch (accelerator.GetKeyCode()) {
+ case app::VKEY_ESCAPE:
+ RemovePaneFocus();
+ RestoreLastFocusedView();
+ return true;
+ case app::VKEY_LEFT:
+ focus_manager_->AdvanceFocus(true);
+ return true;
+ case app::VKEY_RIGHT:
+ focus_manager_->AdvanceFocus(false);
+ return true;
+ case app::VKEY_HOME:
+ focus_manager_->SetFocusedViewWithReason(
+ GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal);
+ return true;
+ case app::VKEY_END:
+ focus_manager_->SetFocusedViewWithReason(
+ GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal);
+ return true;
+ default:
+ return false;
+ }
+}
+
+void AccessiblePaneView::SetVisible(bool flag) {
+ if (IsVisible() && !flag && pane_has_focus_) {
+ RemovePaneFocus();
+ RestoreLastFocusedView();
+ }
+ View::SetVisible(flag);
+}
+
+AccessibilityTypes::Role AccessiblePaneView::GetAccessibleRole() {
+ return AccessibilityTypes::ROLE_PANE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusChangeListener overrides:
+
+void AccessiblePaneView::FocusWillChange(views::View* focused_before,
+ views::View* focused_now) {
+ if (!focused_now)
+ return;
+
+ views::FocusManager::FocusChangeReason reason =
+ focus_manager_->focus_change_reason();
+
+ if (focused_now->GetClassName() == LocationBarView::kViewClassName &&
+ reason == views::FocusManager::kReasonFocusTraversal) {
+ // Tabbing to the location bar should select all. Defer so that it happens
+ // after the focus.
+ MessageLoop::current()->PostTask(
+ FROM_HERE, method_factory_.NewRunnableMethod(
+ &AccessiblePaneView::LocationBarSelectAll));
+ }
+
+ if (!IsParentOf(focused_now) ||
+ reason == views::FocusManager::kReasonDirectFocusChange) {
+ // We should remove pane focus (i.e. make most of the controls
+ // not focusable again) either because the focus is leaving the pane,
+ // or because the focus changed within the pane due to the user
+ // directly focusing to a specific view (e.g., clicking on it).
+ //
+ // Defer rather than calling RemovePaneFocus right away, because we can't
+ // remove |this| as a focus change listener while FocusManager is in the
+ // middle of iterating over the list of listeners.
+ MessageLoop::current()->PostTask(
+ FROM_HERE, method_factory_.NewRunnableMethod(
+ &AccessiblePaneView::RemovePaneFocus));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusTraversable overrides:
+
+views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
+ DCHECK(pane_has_focus_);
+ return focus_search_.get();
+}
+
+views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
+ DCHECK(pane_has_focus_);
+ return NULL;
+}
+
+views::View* AccessiblePaneView::GetFocusTraversableParentView() {
+ DCHECK(pane_has_focus_);
+ return NULL;
+}