Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2011 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 "base/logging.h"
      6 #include "chrome/browser/ui/view_ids.h"
      7 #include "chrome/browser/ui/views/accessible_pane_view.h"
      8 #include "chrome/browser/ui/views/frame/browser_view.h"
      9 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
     10 #include "ui/base/accessibility/accessible_view_state.h"
     11 #include "views/controls/button/menu_button.h"
     12 #include "views/controls/native/native_view_host.h"
     13 #include "views/focus/focus_search.h"
     14 #include "views/focus/view_storage.h"
     15 #include "views/widget/tooltip_manager.h"
     16 #include "views/widget/widget.h"
     17 
     18 AccessiblePaneView::AccessiblePaneView()
     19     : pane_has_focus_(false),
     20       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
     21       focus_manager_(NULL),
     22       home_key_(ui::VKEY_HOME, false, false, false),
     23       end_key_(ui::VKEY_END, false, false, false),
     24       escape_key_(ui::VKEY_ESCAPE, false, false, false),
     25       left_key_(ui::VKEY_LEFT, false, false, false),
     26       right_key_(ui::VKEY_RIGHT, false, false, false),
     27       last_focused_view_storage_id_(-1) {
     28   focus_search_.reset(new views::FocusSearch(this, true, true));
     29 }
     30 
     31 AccessiblePaneView::~AccessiblePaneView() {
     32   if (pane_has_focus_) {
     33     focus_manager_->RemoveFocusChangeListener(this);
     34   }
     35 }
     36 
     37 bool AccessiblePaneView::SetPaneFocus(int view_storage_id,
     38                                       views::View* initial_focus) {
     39   if (!IsVisible())
     40     return false;
     41 
     42   // Save the storage id to the last focused view. This would be used to request
     43   // focus to the view when the traversal is ended.
     44   last_focused_view_storage_id_ = view_storage_id;
     45 
     46   if (!focus_manager_)
     47     focus_manager_ = GetFocusManager();
     48 
     49   // Use the provided initial focus if it's visible and enabled, otherwise
     50   // use the first focusable child.
     51   if (!initial_focus ||
     52       !Contains(initial_focus) ||
     53       !initial_focus->IsVisible() ||
     54       !initial_focus->IsEnabled()) {
     55     initial_focus = GetFirstFocusableChild();
     56   }
     57 
     58   // Return false if there are no focusable children.
     59   if (!initial_focus)
     60     return false;
     61 
     62   // Set focus to the initial view. If it's a location bar, use a special
     63   // method that tells it to select all, also.
     64   if (initial_focus->GetClassName() == LocationBarView::kViewClassName) {
     65     static_cast<LocationBarView*>(initial_focus)->FocusLocation(true);
     66   } else {
     67     focus_manager_->SetFocusedView(initial_focus);
     68   }
     69 
     70   // If we already have pane focus, we're done.
     71   if (pane_has_focus_)
     72     return true;
     73 
     74   // Otherwise, set accelerators and start listening for focus change events.
     75   pane_has_focus_ = true;
     76   focus_manager_->RegisterAccelerator(home_key_, this);
     77   focus_manager_->RegisterAccelerator(end_key_, this);
     78   focus_manager_->RegisterAccelerator(escape_key_, this);
     79   focus_manager_->RegisterAccelerator(left_key_, this);
     80   focus_manager_->RegisterAccelerator(right_key_, this);
     81   focus_manager_->AddFocusChangeListener(this);
     82 
     83   return true;
     84 }
     85 
     86 bool AccessiblePaneView::SetPaneFocusAndFocusDefault(
     87     int view_storage_id) {
     88   return SetPaneFocus(view_storage_id, GetDefaultFocusableChild());
     89 }
     90 
     91 views::View* AccessiblePaneView::GetDefaultFocusableChild() {
     92   return NULL;
     93 }
     94 
     95 void AccessiblePaneView::RemovePaneFocus() {
     96   focus_manager_->RemoveFocusChangeListener(this);
     97   pane_has_focus_ = false;
     98 
     99   focus_manager_->UnregisterAccelerator(home_key_, this);
    100   focus_manager_->UnregisterAccelerator(end_key_, this);
    101   focus_manager_->UnregisterAccelerator(escape_key_, this);
    102   focus_manager_->UnregisterAccelerator(left_key_, this);
    103   focus_manager_->UnregisterAccelerator(right_key_, this);
    104 }
    105 
    106 void AccessiblePaneView::LocationBarSelectAll() {
    107   views::View* focused_view = GetFocusManager()->GetFocusedView();
    108   if (focused_view &&
    109       focused_view->GetClassName() == LocationBarView::kViewClassName) {
    110     static_cast<LocationBarView*>(focused_view)->SelectAll();
    111   }
    112 }
    113 
    114 void AccessiblePaneView::RestoreLastFocusedView() {
    115   views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
    116   views::View* last_focused_view =
    117       view_storage->RetrieveView(last_focused_view_storage_id_);
    118   if (last_focused_view) {
    119     focus_manager_->SetFocusedViewWithReason(
    120         last_focused_view, views::FocusManager::kReasonFocusRestore);
    121   } else {
    122     // Focus the location bar
    123     views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName);
    124     if (view) {
    125       BrowserView* browser_view = static_cast<BrowserView*>(view);
    126       browser_view->SetFocusToLocationBar(false);
    127     }
    128   }
    129 }
    130 
    131 views::View* AccessiblePaneView::GetFirstFocusableChild() {
    132   FocusTraversable* dummy_focus_traversable;
    133   views::View* dummy_focus_traversable_view;
    134   return focus_search_->FindNextFocusableView(
    135       NULL, false, views::FocusSearch::DOWN, false,
    136       &dummy_focus_traversable, &dummy_focus_traversable_view);
    137 }
    138 
    139 views::View* AccessiblePaneView::GetLastFocusableChild() {
    140   FocusTraversable* dummy_focus_traversable;
    141   views::View* dummy_focus_traversable_view;
    142   return focus_search_->FindNextFocusableView(
    143       this, true, views::FocusSearch::DOWN, false,
    144       &dummy_focus_traversable, &dummy_focus_traversable_view);
    145 }
    146 
    147 ////////////////////////////////////////////////////////////////////////////////
    148 // View overrides:
    149 
    150 views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
    151   if (pane_has_focus_)
    152     return this;
    153   else
    154     return NULL;
    155 }
    156 
    157 bool AccessiblePaneView::AcceleratorPressed(
    158     const views::Accelerator& accelerator) {
    159   // Special case: don't handle any accelerators for the location bar,
    160   // so that it behaves exactly the same whether you focus it with Ctrl+L
    161   // or F6 or Alt+D or Alt+Shift+T.
    162   views::View* focused_view = focus_manager_->GetFocusedView();
    163   if ((focused_view->GetClassName() == LocationBarView::kViewClassName ||
    164        focused_view->GetClassName() == views::NativeViewHost::kViewClassName)) {
    165     return false;
    166   }
    167 
    168   switch (accelerator.GetKeyCode()) {
    169     case ui::VKEY_ESCAPE:
    170       RemovePaneFocus();
    171       RestoreLastFocusedView();
    172       return true;
    173     case ui::VKEY_LEFT:
    174       focus_manager_->AdvanceFocus(true);
    175       return true;
    176     case ui::VKEY_RIGHT:
    177       focus_manager_->AdvanceFocus(false);
    178       return true;
    179     case ui::VKEY_HOME:
    180       focus_manager_->SetFocusedViewWithReason(
    181           GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal);
    182       return true;
    183     case ui::VKEY_END:
    184       focus_manager_->SetFocusedViewWithReason(
    185           GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal);
    186       return true;
    187     default:
    188       return false;
    189   }
    190 }
    191 
    192 void AccessiblePaneView::SetVisible(bool flag) {
    193   if (IsVisible() && !flag && pane_has_focus_) {
    194     RemovePaneFocus();
    195     RestoreLastFocusedView();
    196   }
    197   View::SetVisible(flag);
    198 }
    199 
    200 void AccessiblePaneView::GetAccessibleState(ui::AccessibleViewState* state) {
    201   state->role = ui::AccessibilityTypes::ROLE_PANE;
    202 }
    203 
    204 ////////////////////////////////////////////////////////////////////////////////
    205 // FocusChangeListener overrides:
    206 
    207 void AccessiblePaneView::FocusWillChange(views::View* focused_before,
    208                                          views::View* focused_now) {
    209   if (!focused_now)
    210     return;
    211 
    212   views::FocusManager::FocusChangeReason reason =
    213       focus_manager_->focus_change_reason();
    214 
    215   if (focused_now->GetClassName() == LocationBarView::kViewClassName &&
    216       reason == views::FocusManager::kReasonFocusTraversal) {
    217     // Tabbing to the location bar should select all. Defer so that it happens
    218     // after the focus.
    219     MessageLoop::current()->PostTask(
    220         FROM_HERE, method_factory_.NewRunnableMethod(
    221             &AccessiblePaneView::LocationBarSelectAll));
    222   }
    223 
    224   if (!Contains(focused_now) ||
    225       reason == views::FocusManager::kReasonDirectFocusChange) {
    226     // We should remove pane focus (i.e. make most of the controls
    227     // not focusable again) either because the focus is leaving the pane,
    228     // or because the focus changed within the pane due to the user
    229     // directly focusing to a specific view (e.g., clicking on it).
    230     //
    231     // Defer rather than calling RemovePaneFocus right away, because we can't
    232     // remove |this| as a focus change listener while FocusManager is in the
    233     // middle of iterating over the list of listeners.
    234     MessageLoop::current()->PostTask(
    235         FROM_HERE, method_factory_.NewRunnableMethod(
    236             &AccessiblePaneView::RemovePaneFocus));
    237   }
    238 }
    239 
    240 ////////////////////////////////////////////////////////////////////////////////
    241 // FocusTraversable overrides:
    242 
    243 views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
    244   DCHECK(pane_has_focus_);
    245   return focus_search_.get();
    246 }
    247 
    248 views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
    249   DCHECK(pane_has_focus_);
    250   return NULL;
    251 }
    252 
    253 views::View* AccessiblePaneView::GetFocusTraversableParentView() {
    254   DCHECK(pane_has_focus_);
    255   return NULL;
    256 }
    257