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.
5 #include "chrome/browser/chromeos/ui/focus_ring_layer.h"
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/gfx/image/canvas_image_source.h"
15 #include "ui/gfx/shadow_value.h"
16 #include "ui/gfx/skia_util.h"
17 #include "ui/views/controls/button/label_button.h"
18 #include "ui/views/painter.h"
19 #include "ui/views/view.h"
20 #include "ui/views/widget/widget.h"
26 const int kShadowRadius = 23;
27 const int kCenterBlockSize = 2 * kShadowRadius;
28 const int kFocusRingImageSize = kShadowRadius * 2 + kCenterBlockSize;
29 const SkColor kShadowColor = SkColorSetRGB(77, 144, 254);
31 // FocusRingImageSource generates a base image that could be used by
32 // ImagePainter to paint a focus ring around a rect. The base image is a
33 // transparent square block of kCenterBlockSize pixels with blue halo around.
34 class FocusRingImageSource : public gfx::CanvasImageSource {
36 FocusRingImageSource()
37 : CanvasImageSource(gfx::Size(kFocusRingImageSize, kFocusRingImageSize),
40 gfx::ShadowValue(gfx::Point(), kShadowRadius, kShadowColor));
43 virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
45 paint.setColor(kShadowColor);
47 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(shadows_);
48 paint.setLooper(looper.get());
50 const gfx::Rect rect(kShadowRadius, kShadowRadius,
51 kCenterBlockSize, kCenterBlockSize);
52 canvas->DrawRect(rect, paint);
53 canvas->FillRect(rect, SK_ColorTRANSPARENT, SkXfermode::kSrc_Mode);
57 gfx::ShadowValues shadows_;
59 DISALLOW_COPY_AND_ASSIGN(FocusRingImageSource);
64 FocusRingLayer::FocusRingLayer() {
65 gfx::ImageSkia ring_image(
66 new FocusRingImageSource,
67 gfx::Size(kFocusRingImageSize, kFocusRingImageSize));
68 ring_painter_.reset(views::Painter::CreateImagePainter(
70 gfx::Insets(kShadowRadius, kShadowRadius, kShadowRadius, kShadowRadius)));
73 FocusRingLayer::~FocusRingLayer() {}
75 void FocusRingLayer::SetForView(views::View* view) {
78 !view->GetWidget()->GetNativeWindow() ||
79 !view->GetWidget()->GetNativeWindow()->layer()) {
85 layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
86 layer_->set_name("FocusRing");
87 layer_->set_delegate(this);
88 layer_->SetFillsBoundsOpaquely(false);
91 // Puts the focus ring layer as a sibling layer of the widget layer so that
92 // it does not clip at the widget's boundary.
93 ui::Layer* widget_layer = view->GetWidget()->GetNativeWindow()->layer();
94 widget_layer->parent()->Add(layer_.get());
96 // Workarounds that attempts to pick a better bounds.
97 gfx::Rect view_bounds = view->GetContentsBounds();
98 if (view->GetClassName() == views::LabelButton::kViewClassName) {
99 view_bounds = view->GetLocalBounds();
100 view_bounds.Inset(2, 2, 2, 2);
103 // Workarounds for system tray items that has a customized OnPaintFocusBorder.
104 // The insets here must be consistent with the ones used in OnPaintFocusBorder
106 if (view->GetClassName() ==
107 ash::internal::ActionableView::kViewClassName) {
108 view_bounds = view->GetLocalBounds();
109 view_bounds.Inset(1, 1, 3, 3);
110 } else if (view->GetClassName() ==
111 ash::internal::TrayBackgroundView::kViewClassName) {
112 view_bounds.Inset(1, 1, 3, 3);
113 } else if (view->GetClassName() ==
114 ash::internal::TrayPopupHeaderButton::kViewClassName) {
115 view_bounds = view->GetLocalBounds();
116 view_bounds.Inset(2, 1, 2, 2);
119 // Note the bounds calculation below assumes no transformation and ignores
121 const gfx::Rect widget_bounds = widget_layer->GetTargetBounds();
122 gfx::Rect bounds = view->ConvertRectToWidget(view_bounds);
123 bounds.Offset(widget_bounds.OffsetFromOrigin());
124 bounds.Inset(-kShadowRadius, -kShadowRadius, -kShadowRadius, -kShadowRadius);
125 layer_->SetBounds(bounds);
128 void FocusRingLayer::OnPaintLayer(gfx::Canvas* canvas) {
129 ring_painter_->Paint(canvas, layer_->bounds().size());
132 void FocusRingLayer::OnDeviceScaleFactorChanged(float device_scale_factor) {
135 base::Closure FocusRingLayer::PrepareForLayerBoundsChange() {
136 return base::Bind(&base::DoNothing);
139 } // namespace chromeos