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