Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/views/accessible_pane_view.h"
      6 
      7 #include "base/message_loop/message_loop.h"
      8 #include "ui/accessibility/ax_view_state.h"
      9 #include "ui/views/focus/focus_search.h"
     10 #include "ui/views/focus/view_storage.h"
     11 #include "ui/views/widget/widget.h"
     12 
     13 namespace views {
     14 
     15 // Create tiny subclass of FocusSearch that overrides GetParent and Contains,
     16 // delegating these to methods in AccessiblePaneView. This is needed so that
     17 // subclasses of AccessiblePaneView can customize the focus search logic and
     18 // include views that aren't part of the AccessiblePaneView's view
     19 // hierarchy in the focus order.
     20 class AccessiblePaneViewFocusSearch : public FocusSearch {
     21  public:
     22   explicit AccessiblePaneViewFocusSearch(AccessiblePaneView* pane_view)
     23       : FocusSearch(pane_view, true, true),
     24         accessible_pane_view_(pane_view) {}
     25 
     26  protected:
     27   virtual View* GetParent(View* v) OVERRIDE {
     28     return accessible_pane_view_->ContainsForFocusSearch(root(), v) ?
     29         accessible_pane_view_->GetParentForFocusSearch(v) : NULL;
     30   }
     31 
     32   // Returns true if |v| is contained within the hierarchy rooted at |root|.
     33   // Subclasses can override this if they need custom focus search behavior.
     34   virtual bool Contains(View* root, const View* v) OVERRIDE {
     35     return accessible_pane_view_->ContainsForFocusSearch(root, v);
     36   }
     37 
     38  private:
     39   AccessiblePaneView* accessible_pane_view_;
     40   DISALLOW_COPY_AND_ASSIGN(AccessiblePaneViewFocusSearch);
     41 };
     42 
     43 AccessiblePaneView::AccessiblePaneView()
     44     : pane_has_focus_(false),
     45       allow_deactivate_on_esc_(false),
     46       focus_manager_(NULL),
     47       home_key_(ui::VKEY_HOME, ui::EF_NONE),
     48       end_key_(ui::VKEY_END, ui::EF_NONE),
     49       escape_key_(ui::VKEY_ESCAPE, ui::EF_NONE),
     50       left_key_(ui::VKEY_LEFT, ui::EF_NONE),
     51       right_key_(ui::VKEY_RIGHT, ui::EF_NONE),
     52       method_factory_(this) {
     53   focus_search_.reset(new AccessiblePaneViewFocusSearch(this));
     54   last_focused_view_storage_id_ = ViewStorage::GetInstance()->CreateStorageID();
     55 }
     56 
     57 AccessiblePaneView::~AccessiblePaneView() {
     58   if (pane_has_focus_) {
     59     focus_manager_->RemoveFocusChangeListener(this);
     60   }
     61 }
     62 
     63 bool AccessiblePaneView::SetPaneFocus(views::View* initial_focus) {
     64   if (!visible())
     65     return false;
     66 
     67   if (!focus_manager_)
     68     focus_manager_ = GetFocusManager();
     69 
     70   View* focused_view = focus_manager_->GetFocusedView();
     71   if (focused_view && !ContainsForFocusSearch(this, focused_view)) {
     72     ViewStorage* view_storage = ViewStorage::GetInstance();
     73     view_storage->RemoveView(last_focused_view_storage_id_);
     74     view_storage->StoreView(last_focused_view_storage_id_, focused_view);
     75   }
     76 
     77   // Use the provided initial focus if it's visible and enabled, otherwise
     78   // use the first focusable child.
     79   if (!initial_focus ||
     80       !ContainsForFocusSearch(this, initial_focus) ||
     81       !initial_focus->visible() ||
     82       !initial_focus->enabled()) {
     83     initial_focus = GetFirstFocusableChild();
     84   }
     85 
     86   // Return false if there are no focusable children.
     87   if (!initial_focus)
     88     return false;
     89 
     90   focus_manager_->SetFocusedView(initial_focus);
     91 
     92   // If we already have pane focus, we're done.
     93   if (pane_has_focus_)
     94     return true;
     95 
     96   // Otherwise, set accelerators and start listening for focus change events.
     97   pane_has_focus_ = true;
     98   ui::AcceleratorManager::HandlerPriority normal =
     99       ui::AcceleratorManager::kNormalPriority;
    100   focus_manager_->RegisterAccelerator(home_key_, normal, this);
    101   focus_manager_->RegisterAccelerator(end_key_, normal, this);
    102   focus_manager_->RegisterAccelerator(escape_key_, normal, this);
    103   focus_manager_->RegisterAccelerator(left_key_, normal, this);
    104   focus_manager_->RegisterAccelerator(right_key_, normal, this);
    105   focus_manager_->AddFocusChangeListener(this);
    106 
    107   return true;
    108 }
    109 
    110 bool AccessiblePaneView::SetPaneFocusAndFocusDefault() {
    111   return SetPaneFocus(GetDefaultFocusableChild());
    112 }
    113 
    114 views::View* AccessiblePaneView::GetDefaultFocusableChild() {
    115   return NULL;
    116 }
    117 
    118 View* AccessiblePaneView::GetParentForFocusSearch(View* v) {
    119   return v->parent();
    120 }
    121 
    122 bool AccessiblePaneView::ContainsForFocusSearch(View* root, const View* v) {
    123   return root->Contains(v);
    124 }
    125 
    126 void AccessiblePaneView::RemovePaneFocus() {
    127   focus_manager_->RemoveFocusChangeListener(this);
    128   pane_has_focus_ = false;
    129 
    130   focus_manager_->UnregisterAccelerator(home_key_, this);
    131   focus_manager_->UnregisterAccelerator(end_key_, this);
    132   focus_manager_->UnregisterAccelerator(escape_key_, this);
    133   focus_manager_->UnregisterAccelerator(left_key_, this);
    134   focus_manager_->UnregisterAccelerator(right_key_, this);
    135 }
    136 
    137 views::View* AccessiblePaneView::GetFirstFocusableChild() {
    138   FocusTraversable* dummy_focus_traversable;
    139   views::View* dummy_focus_traversable_view;
    140   return focus_search_->FindNextFocusableView(
    141       NULL, false, views::FocusSearch::DOWN, false,
    142       &dummy_focus_traversable, &dummy_focus_traversable_view);
    143 }
    144 
    145 views::View* AccessiblePaneView::GetLastFocusableChild() {
    146   FocusTraversable* dummy_focus_traversable;
    147   views::View* dummy_focus_traversable_view;
    148   return focus_search_->FindNextFocusableView(
    149       this, true, views::FocusSearch::DOWN, false,
    150       &dummy_focus_traversable, &dummy_focus_traversable_view);
    151 }
    152 
    153 ////////////////////////////////////////////////////////////////////////////////
    154 // View overrides:
    155 
    156 views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
    157   if (pane_has_focus_)
    158     return this;
    159   else
    160     return NULL;
    161 }
    162 
    163 bool AccessiblePaneView::AcceleratorPressed(
    164     const ui::Accelerator& accelerator) {
    165 
    166   views::View* focused_view = focus_manager_->GetFocusedView();
    167   if (!ContainsForFocusSearch(this, focused_view))
    168     return false;
    169 
    170   switch (accelerator.key_code()) {
    171     case ui::VKEY_ESCAPE: {
    172       RemovePaneFocus();
    173       View* last_focused_view = ViewStorage::GetInstance()->RetrieveView(
    174           last_focused_view_storage_id_);
    175       if (last_focused_view) {
    176         focus_manager_->SetFocusedViewWithReason(
    177             last_focused_view, FocusManager::kReasonFocusRestore);
    178       } else if (allow_deactivate_on_esc_) {
    179         focused_view->GetWidget()->Deactivate();
    180       }
    181       return true;
    182     }
    183     case ui::VKEY_LEFT:
    184       focus_manager_->AdvanceFocus(true);
    185       return true;
    186     case ui::VKEY_RIGHT:
    187       focus_manager_->AdvanceFocus(false);
    188       return true;
    189     case ui::VKEY_HOME:
    190       focus_manager_->SetFocusedViewWithReason(
    191           GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal);
    192       return true;
    193     case ui::VKEY_END:
    194       focus_manager_->SetFocusedViewWithReason(
    195           GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal);
    196       return true;
    197     default:
    198       return false;
    199   }
    200 }
    201 
    202 void AccessiblePaneView::SetVisible(bool flag) {
    203   if (visible() && !flag && pane_has_focus_) {
    204     RemovePaneFocus();
    205     focus_manager_->RestoreFocusedView();
    206   }
    207   View::SetVisible(flag);
    208 }
    209 
    210 void AccessiblePaneView::GetAccessibleState(ui::AXViewState* state) {
    211   state->role = ui::AX_ROLE_PANE;
    212 }
    213 
    214 void AccessiblePaneView::RequestFocus() {
    215   SetPaneFocusAndFocusDefault();
    216 }
    217 
    218 ////////////////////////////////////////////////////////////////////////////////
    219 // FocusChangeListener overrides:
    220 
    221 void AccessiblePaneView::OnWillChangeFocus(views::View* focused_before,
    222                                            views::View* focused_now) {
    223   //  Act when focus has changed.
    224 }
    225 
    226 void AccessiblePaneView::OnDidChangeFocus(views::View* focused_before,
    227                                           views::View* focused_now) {
    228   if (!focused_now)
    229     return;
    230 
    231   views::FocusManager::FocusChangeReason reason =
    232       focus_manager_->focus_change_reason();
    233 
    234   if (!ContainsForFocusSearch(this, focused_now) ||
    235       reason == views::FocusManager::kReasonDirectFocusChange) {
    236     // We should remove pane focus (i.e. make most of the controls
    237     // not focusable again) because the focus has left the pane,
    238     // or because the focus changed within the pane due to the user
    239     // directly focusing to a specific view (e.g., clicking on it).
    240     RemovePaneFocus();
    241   }
    242 }
    243 
    244 ////////////////////////////////////////////////////////////////////////////////
    245 // FocusTraversable overrides:
    246 
    247 views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
    248   DCHECK(pane_has_focus_);
    249   return focus_search_.get();
    250 }
    251 
    252 views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
    253   DCHECK(pane_has_focus_);
    254   return NULL;
    255 }
    256 
    257 views::View* AccessiblePaneView::GetFocusTraversableParentView() {
    258   DCHECK(pane_has_focus_);
    259   return NULL;
    260 }
    261 
    262 }  // namespace views
    263