| // 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 "ui/views/accessible_pane_view.h" |
| |
| #include "base/message_loop.h" |
| #include "ui/base/accessibility/accessible_view_state.h" |
| #include "ui/views/focus/focus_search.h" |
| #include "ui/views/focus/view_storage.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace views { |
| |
| AccessiblePaneView::AccessiblePaneView() |
| : pane_has_focus_(false), |
| ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), |
| focus_manager_(NULL), |
| home_key_(ui::VKEY_HOME, false, false, false), |
| end_key_(ui::VKEY_END, false, false, false), |
| escape_key_(ui::VKEY_ESCAPE, false, false, false), |
| left_key_(ui::VKEY_LEFT, false, false, false), |
| right_key_(ui::VKEY_RIGHT, false, false, false) { |
| focus_search_.reset(new views::FocusSearch(this, true, true)); |
| } |
| |
| AccessiblePaneView::~AccessiblePaneView() { |
| if (pane_has_focus_) { |
| focus_manager_->RemoveFocusChangeListener(this); |
| } |
| } |
| |
| bool AccessiblePaneView::SetPaneFocus(views::View* initial_focus) { |
| if (!visible()) |
| return false; |
| |
| if (!focus_manager_) |
| focus_manager_ = GetFocusManager(); |
| |
| focus_manager_->StoreFocusedView(); |
| |
| // Use the provided initial focus if it's visible and enabled, otherwise |
| // use the first focusable child. |
| if (!initial_focus || |
| !Contains(initial_focus) || |
| !initial_focus->visible() || |
| !initial_focus->enabled()) { |
| initial_focus = GetFirstFocusableChild(); |
| } |
| |
| // Return false if there are no focusable children. |
| if (!initial_focus) |
| return false; |
| |
| 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; |
| ui::AcceleratorManager::HandlerPriority normal = |
| ui::AcceleratorManager::kNormalPriority; |
| focus_manager_->RegisterAccelerator(home_key_, normal, this); |
| focus_manager_->RegisterAccelerator(end_key_, normal, this); |
| focus_manager_->RegisterAccelerator(escape_key_, normal, this); |
| focus_manager_->RegisterAccelerator(left_key_, normal, this); |
| focus_manager_->RegisterAccelerator(right_key_, normal, this); |
| focus_manager_->AddFocusChangeListener(this); |
| |
| return true; |
| } |
| |
| bool AccessiblePaneView::SetPaneFocusAndFocusDefault() { |
| return SetPaneFocus(GetDefaultFocusableChild()); |
| } |
| |
| views::View* AccessiblePaneView::GetDefaultFocusableChild() { |
| return NULL; |
| } |
| |
| 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); |
| } |
| |
| 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 ui::Accelerator& accelerator) { |
| |
| const views::View* focused_view = focus_manager_->GetFocusedView(); |
| if (!Contains(focused_view)) |
| return false; |
| |
| switch (accelerator.key_code()) { |
| case ui::VKEY_ESCAPE: |
| RemovePaneFocus(); |
| focus_manager_->RestoreFocusedView(); |
| return true; |
| case ui::VKEY_LEFT: |
| focus_manager_->AdvanceFocus(true); |
| return true; |
| case ui::VKEY_RIGHT: |
| focus_manager_->AdvanceFocus(false); |
| return true; |
| case ui::VKEY_HOME: |
| focus_manager_->SetFocusedViewWithReason( |
| GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal); |
| return true; |
| case ui::VKEY_END: |
| focus_manager_->SetFocusedViewWithReason( |
| GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void AccessiblePaneView::SetVisible(bool flag) { |
| if (visible() && !flag && pane_has_focus_) { |
| RemovePaneFocus(); |
| focus_manager_->RestoreFocusedView(); |
| } |
| View::SetVisible(flag); |
| } |
| |
| void AccessiblePaneView::GetAccessibleState(ui::AccessibleViewState* state) { |
| state->role = ui::AccessibilityTypes::ROLE_PANE; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // FocusChangeListener overrides: |
| |
| void AccessiblePaneView::OnWillChangeFocus(views::View* focused_before, |
| views::View* focused_now) { |
| // Act when focus has changed. |
| } |
| |
| void AccessiblePaneView::OnDidChangeFocus(views::View* focused_before, |
| views::View* focused_now) { |
| if (!focused_now) |
| return; |
| |
| views::FocusManager::FocusChangeReason reason = |
| focus_manager_->focus_change_reason(); |
| |
| if (!Contains(focused_now) || |
| reason == views::FocusManager::kReasonDirectFocusChange) { |
| // We should remove pane focus (i.e. make most of the controls |
| // not focusable again) because the focus has left 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). |
| 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; |
| } |
| |
| } // namespace views |