- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / ui / focus_ring_layer.cc
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/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"
21
22 namespace chromeos {
23
24 namespace {
25
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);
30
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 {
35  public:
36   FocusRingImageSource()
37       : CanvasImageSource(gfx::Size(kFocusRingImageSize, kFocusRingImageSize),
38                           false) {
39     shadows_.push_back(
40         gfx::ShadowValue(gfx::Point(), kShadowRadius, kShadowColor));
41   }
42
43   virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
44     SkPaint paint;
45     paint.setColor(kShadowColor);
46
47     skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(shadows_);
48     paint.setLooper(looper.get());
49
50     const gfx::Rect rect(kShadowRadius, kShadowRadius,
51                          kCenterBlockSize, kCenterBlockSize);
52     canvas->DrawRect(rect, paint);
53     canvas->FillRect(rect, SK_ColorTRANSPARENT, SkXfermode::kSrc_Mode);
54   }
55
56  private:
57   gfx::ShadowValues shadows_;
58
59   DISALLOW_COPY_AND_ASSIGN(FocusRingImageSource);
60 };
61
62 }  // namespace
63
64 FocusRingLayer::FocusRingLayer() {
65   gfx::ImageSkia ring_image(
66       new FocusRingImageSource,
67       gfx::Size(kFocusRingImageSize, kFocusRingImageSize));
68   ring_painter_.reset(views::Painter::CreateImagePainter(
69       ring_image,
70       gfx::Insets(kShadowRadius, kShadowRadius, kShadowRadius, kShadowRadius)));
71 }
72
73 FocusRingLayer::~FocusRingLayer() {}
74
75 void FocusRingLayer::SetForView(views::View* view) {
76   if (!view ||
77       !view->GetWidget() ||
78       !view->GetWidget()->GetNativeWindow() ||
79       !view->GetWidget()->GetNativeWindow()->layer()) {
80     layer_.reset();
81     return;
82   }
83
84   if (!layer_) {
85     layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
86     layer_->set_name("FocusRing");
87     layer_->set_delegate(this);
88     layer_->SetFillsBoundsOpaquely(false);
89   }
90
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());
95
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);
101   }
102
103   // Workarounds for system tray items that has a customized OnPaintFocusBorder.
104   // The insets here must be consistent with the ones used in OnPaintFocusBorder
105   // and DrawBorder.
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);
117   }
118
119   // Note the bounds calculation below assumes no transformation and ignores
120   // animations.
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);
126 }
127
128 void FocusRingLayer::OnPaintLayer(gfx::Canvas* canvas) {
129   ring_painter_->Paint(canvas, layer_->bounds().size());
130 }
131
132 void FocusRingLayer::OnDeviceScaleFactorChanged(float device_scale_factor) {
133 }
134
135 base::Closure FocusRingLayer::PrepareForLayerBoundsChange() {
136   return base::Bind(&base::DoNothing);
137 }
138
139 }  // namespace chromeos