Home | History | Annotate | Download | only in ui
      1 // Copyright 2013 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 "chrome/browser/chromeos/ui/focus_ring_controller.h"
      6 
      7 #include "ash/system/tray/actionable_view.h"
      8 #include "ash/system/tray/tray_background_view.h"
      9 #include "ash/system/tray/tray_popup_header_button.h"
     10 #include "ash/wm/window_util.h"
     11 #include "chrome/browser/chromeos/ui/focus_ring_layer.h"
     12 #include "ui/aura/window.h"
     13 #include "ui/views/controls/button/label_button.h"
     14 #include "ui/views/view.h"
     15 #include "ui/views/widget/widget.h"
     16 
     17 namespace chromeos {
     18 
     19 FocusRingController::FocusRingController()
     20     : visible_(false),
     21       widget_(NULL) {
     22 }
     23 
     24 FocusRingController::~FocusRingController() {
     25   SetVisible(false);
     26 }
     27 
     28 void FocusRingController::SetVisible(bool visible) {
     29   if (visible_ == visible)
     30     return;
     31 
     32   visible_ = visible;
     33 
     34   if (visible_) {
     35     views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
     36     aura::Window* active_window = ash::wm::GetActiveWindow();
     37     if (active_window)
     38       SetWidget(views::Widget::GetWidgetForNativeWindow(active_window));
     39   } else {
     40     views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
     41     SetWidget(NULL);
     42   }
     43 }
     44 
     45 void FocusRingController::UpdateFocusRing() {
     46   views::View* view = NULL;
     47   if (widget_ && widget_->GetFocusManager())
     48     view = widget_->GetFocusManager()->GetFocusedView();
     49 
     50   // No focus ring if no focused view or the focused view covers the whole
     51   // widget content area (such as RenderWidgetHostWidgetAura).
     52   if (!view ||
     53       view->ConvertRectToWidget(view->bounds()) ==
     54           widget_->GetContentsView()->bounds()) {
     55     focus_ring_layer_.reset();
     56     return;
     57   }
     58 
     59   gfx::Rect view_bounds = view->GetContentsBounds();
     60 
     61   // Workarounds that attempts to pick a better bounds.
     62   if (view->GetClassName() == views::LabelButton::kViewClassName) {
     63     view_bounds = view->GetLocalBounds();
     64     view_bounds.Inset(2, 2, 2, 2);
     65   }
     66 
     67   // Workarounds for system tray items that have customized focus borders.  The
     68   // insets here must be consistent with the ones used by those classes.
     69   if (view->GetClassName() == ash::ActionableView::kViewClassName) {
     70     view_bounds = view->GetLocalBounds();
     71     view_bounds.Inset(1, 1, 3, 3);
     72   } else if (view->GetClassName() == ash::TrayBackgroundView::kViewClassName) {
     73     view_bounds.Inset(1, 1, 3, 3);
     74   } else if (view->GetClassName() ==
     75              ash::TrayPopupHeaderButton::kViewClassName) {
     76     view_bounds = view->GetLocalBounds();
     77     view_bounds.Inset(2, 1, 2, 2);
     78   }
     79 
     80   // Convert view bounds to widget/window coordinates.
     81   view_bounds = view->ConvertRectToWidget(view_bounds);
     82 
     83   // Translate window coordinates to root window coordinates.
     84   DCHECK(view->GetWidget());
     85   aura::Window* window = view->GetWidget()->GetNativeWindow();
     86   aura::Window* root_window = window->GetRootWindow();
     87   gfx::Point origin = view_bounds.origin();
     88   aura::Window::ConvertPointToTarget(window, root_window, &origin);
     89   view_bounds.set_origin(origin);
     90 
     91   // Update the focus ring layer.
     92   if (!focus_ring_layer_)
     93     focus_ring_layer_.reset(new FocusRingLayer(this));
     94   focus_ring_layer_->Set(root_window, view_bounds);
     95 }
     96 
     97 void FocusRingController::OnDeviceScaleFactorChanged() {
     98   UpdateFocusRing();
     99 }
    100 
    101 void FocusRingController::SetWidget(views::Widget* widget) {
    102   if (widget_) {
    103     widget_->RemoveObserver(this);
    104     if (widget_->GetFocusManager())
    105       widget_->GetFocusManager()->RemoveFocusChangeListener(this);
    106   }
    107 
    108   widget_ = widget;
    109 
    110   if (widget_) {
    111     widget_->AddObserver(this);
    112     if (widget_->GetFocusManager())
    113       widget_->GetFocusManager()->AddFocusChangeListener(this);
    114   }
    115 
    116   UpdateFocusRing();
    117 }
    118 
    119 void FocusRingController::OnWidgetDestroying(views::Widget* widget) {
    120   DCHECK_EQ(widget_, widget);
    121   SetWidget(NULL);
    122 }
    123 
    124 void FocusRingController::OnWidgetBoundsChanged(views::Widget* widget,
    125                                                 const gfx::Rect& new_bounds) {
    126   DCHECK_EQ(widget_, widget);
    127   UpdateFocusRing();
    128 }
    129 
    130 void FocusRingController::OnNativeFocusChange(gfx::NativeView focused_before,
    131                                               gfx::NativeView focused_now) {
    132   views::Widget* widget =
    133       focused_now ? views::Widget::GetWidgetForNativeWindow(focused_now) : NULL;
    134   SetWidget(widget);
    135 }
    136 
    137 void FocusRingController::OnWillChangeFocus(views::View* focused_before,
    138                                             views::View* focused_now) {
    139 }
    140 
    141 void FocusRingController::OnDidChangeFocus(views::View* focused_before,
    142                                            views::View* focused_now) {
    143   DCHECK_EQ(focused_now, widget_->GetFocusManager()->GetFocusedView());
    144   UpdateFocusRing();
    145 }
    146 
    147 }  // namespace chromeos
    148