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_layer.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 "base/bind.h"
     11 #include "ui/aura/window.h"
     12 #include "ui/compositor/layer.h"
     13 #include "ui/gfx/canvas.h"
     14 #include "ui/views/controls/button/label_button.h"
     15 #include "ui/views/view.h"
     16 #include "ui/views/widget/widget.h"
     17 
     18 namespace chromeos {
     19 
     20 namespace {
     21 
     22 const int kShadowRadius = 10;
     23 const int kShadowAlpha = 90;
     24 const SkColor kShadowColor = SkColorSetRGB(77, 144, 254);
     25 
     26 }  // namespace
     27 
     28 FocusRingLayer::FocusRingLayer()
     29     : window_(NULL),
     30       root_window_(NULL) {
     31 }
     32 
     33 FocusRingLayer::~FocusRingLayer() {}
     34 
     35 void FocusRingLayer::Update() {
     36   if (!window_)
     37     return;
     38 
     39   aura::Window* root_window = window_->GetRootWindow();
     40   if (!layer_ || root_window != root_window_) {
     41     root_window_ = root_window;
     42     ui::Layer* root_layer = root_window->layer();
     43     layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
     44     layer_->set_name("FocusRing");
     45     layer_->set_delegate(this);
     46     layer_->SetFillsBoundsOpaquely(false);
     47     root_layer->Add(layer_.get());
     48   }
     49 
     50   // Keep moving it to the top in case new layers have been added
     51   // since we created this layer.
     52   layer_->parent()->StackAtTop(layer_.get());
     53 
     54   // Translate native window coordinates to root window coordinates.
     55   gfx::Point origin = focus_ring_.origin();
     56   aura::Window::ConvertPointToTarget(window_, root_window_, &origin);
     57   gfx::Rect layer_bounds = focus_ring_;
     58   layer_bounds.set_origin(origin);
     59   int inset = -(kShadowRadius + 2);
     60   layer_bounds.Inset(inset, inset, inset, inset);
     61   layer_->SetBounds(layer_bounds);
     62 }
     63 
     64 void FocusRingLayer::SetForView(views::View* view) {
     65   if (!view) {
     66     if (layer_ && !focus_ring_.IsEmpty())
     67       layer_->SchedulePaint(focus_ring_);
     68     focus_ring_ = gfx::Rect();
     69     return;
     70   }
     71 
     72   DCHECK(view->GetWidget());
     73   window_ = view->GetWidget()->GetNativeWindow();
     74 
     75   gfx::Rect view_bounds = view->GetContentsBounds();
     76 
     77   // Workarounds that attempts to pick a better bounds.
     78   if (view->GetClassName() == views::LabelButton::kViewClassName) {
     79     view_bounds = view->GetLocalBounds();
     80     view_bounds.Inset(2, 2, 2, 2);
     81   }
     82 
     83   // Workarounds for system tray items that have customized focus borders.  The
     84   // insets here must be consistent with the ones used by those classes.
     85   if (view->GetClassName() == ash::ActionableView::kViewClassName) {
     86     view_bounds = view->GetLocalBounds();
     87     view_bounds.Inset(1, 1, 3, 3);
     88   } else if (view->GetClassName() == ash::TrayBackgroundView::kViewClassName) {
     89     view_bounds.Inset(1, 1, 3, 3);
     90   } else if (view->GetClassName() ==
     91              ash::TrayPopupHeaderButton::kViewClassName) {
     92     view_bounds = view->GetLocalBounds();
     93     view_bounds.Inset(2, 1, 2, 2);
     94   }
     95 
     96   focus_ring_ = view->ConvertRectToWidget(view_bounds);
     97   Update();
     98 }
     99 
    100 void FocusRingLayer::OnPaintLayer(gfx::Canvas* canvas) {
    101   if (focus_ring_.IsEmpty())
    102     return;
    103 
    104   // Convert the focus ring from native-window-relative coordinates to
    105   // layer-relative coordinates.
    106   gfx::Point origin = focus_ring_.origin();
    107   aura::Window::ConvertPointToTarget(window_, root_window_, &origin);
    108   origin -= layer_->bounds().OffsetFromOrigin();
    109   gfx::Rect bounds = focus_ring_;
    110   bounds.set_origin(origin);
    111 
    112   SkPaint paint;
    113   paint.setColor(kShadowColor);
    114   paint.setFlags(SkPaint::kAntiAlias_Flag);
    115   paint.setStyle(SkPaint::kStroke_Style);
    116   paint.setStrokeWidth(2);
    117   int r = kShadowRadius;
    118   for (int i = 0; i < r; i++) {
    119     // Fade out alpha quadratically.
    120     paint.setAlpha((kShadowAlpha * (r - i) * (r - i)) / (r * r));
    121     gfx::Rect outsetRect = bounds;
    122     outsetRect.Inset(-i, -i, -i, -i);
    123     canvas->DrawRect(outsetRect, paint);
    124   }
    125 }
    126 
    127 void FocusRingLayer::OnDeviceScaleFactorChanged(float device_scale_factor) {
    128   Update();
    129 }
    130 
    131 base::Closure FocusRingLayer::PrepareForLayerBoundsChange() {
    132   return base::Bind(&base::DoNothing);
    133 }
    134 
    135 }  // namespace chromeos
    136