1868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// found in the LICENSE file.
4868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/chromeos/ui/focus_ring_controller.h"
6868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ash/system/tray/actionable_view.h"
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ash/system/tray/tray_background_view.h"
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ash/system/tray/tray_popup_header_button.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "ash/wm/window_util.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/chromeos/ui/focus_ring_layer.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/aura/window.h"
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/views/controls/button/label_button.h"
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/views/view.h"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "ui/views/widget/widget.h"
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)namespace chromeos {
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FocusRingController::FocusRingController()
20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    : visible_(false),
21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      widget_(NULL) {
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
24868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FocusRingController::~FocusRingController() {
25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  SetVisible(false);
26868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::SetVisible(bool visible) {
29868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (visible_ == visible)
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  visible_ = visible;
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (visible_) {
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    aura::Window* active_window = ash::wm::GetActiveWindow();
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (active_window)
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      SetWidget(views::Widget::GetWidgetForNativeWindow(active_window));
39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  } else {
40868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    SetWidget(NULL);
42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
43868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::UpdateFocusRing() {
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  views::View* view = NULL;
47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (widget_ && widget_->GetFocusManager())
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view = widget_->GetFocusManager()->GetFocusedView();
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // No focus ring if no focused view or the focused view covers the whole
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // widget content area (such as RenderWidgetHostWidgetAura).
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!view ||
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      view->ConvertRectToWidget(view->bounds()) ==
54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          widget_->GetContentsView()->bounds()) {
55868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    focus_ring_layer_.reset();
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gfx::Rect view_bounds = view->GetContentsBounds();
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Workarounds that attempts to pick a better bounds.
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (view->GetClassName() == views::LabelButton::kViewClassName) {
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds = view->GetLocalBounds();
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds.Inset(2, 2, 2, 2);
651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Workarounds for system tray items that have customized focus borders.  The
681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // insets here must be consistent with the ones used by those classes.
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (view->GetClassName() == ash::ActionableView::kViewClassName) {
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds = view->GetLocalBounds();
711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds.Inset(1, 1, 3, 3);
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else if (view->GetClassName() == ash::TrayBackgroundView::kViewClassName) {
731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds.Inset(1, 1, 3, 3);
741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else if (view->GetClassName() ==
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             ash::TrayPopupHeaderButton::kViewClassName) {
761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds = view->GetLocalBounds();
771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    view_bounds.Inset(2, 1, 2, 2);
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Convert view bounds to widget/window coordinates.
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  view_bounds = view->ConvertRectToWidget(view_bounds);
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Translate window coordinates to root window coordinates.
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(view->GetWidget());
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  aura::Window* window = view->GetWidget()->GetNativeWindow();
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  aura::Window* root_window = window->GetRootWindow();
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gfx::Point origin = view_bounds.origin();
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  aura::Window::ConvertPointToTarget(window, root_window, &origin);
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  view_bounds.set_origin(origin);
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Update the focus ring layer.
92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!focus_ring_layer_)
931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    focus_ring_layer_.reset(new FocusRingLayer(this));
941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  focus_ring_layer_->Set(root_window, view_bounds);
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
96868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FocusRingController::OnDeviceScaleFactorChanged() {
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  UpdateFocusRing();
99868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::SetWidget(views::Widget* widget) {
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (widget_) {
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    widget_->RemoveObserver(this);
104868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (widget_->GetFocusManager())
105868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      widget_->GetFocusManager()->RemoveFocusChangeListener(this);
106868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
107868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
108868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  widget_ = widget;
109868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
110868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (widget_) {
111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    widget_->AddObserver(this);
112868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (widget_->GetFocusManager())
113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      widget_->GetFocusManager()->AddFocusChangeListener(this);
114868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
115868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
116868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  UpdateFocusRing();
117868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
118868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::OnWidgetDestroying(views::Widget* widget) {
120868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK_EQ(widget_, widget);
121868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  SetWidget(NULL);
122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
123868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
124868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::OnWidgetBoundsChanged(views::Widget* widget,
125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                                const gfx::Rect& new_bounds) {
126868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK_EQ(widget_, widget);
127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  UpdateFocusRing();
128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::OnNativeFocusChange(gfx::NativeView focused_before,
131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                              gfx::NativeView focused_now) {
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  views::Widget* widget =
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      focused_now ? views::Widget::GetWidgetForNativeWindow(focused_now) : NULL;
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  SetWidget(widget);
135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::OnWillChangeFocus(views::View* focused_before,
138868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                            views::View* focused_now) {
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
140868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void FocusRingController::OnDidChangeFocus(views::View* focused_before,
142868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                           views::View* focused_now) {
143868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK_EQ(focused_now, widget_->GetFocusManager()->GetFocusedView());
144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  UpdateFocusRing();
145868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}  // namespace chromeos
148