1// Copyright 2014 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/accessibility_focus_ring_layer.h"
6
7#include "ash/display/display_controller.h"
8#include "ash/shell.h"
9#include "base/bind.h"
10#include "ui/aura/window.h"
11#include "ui/compositor/layer.h"
12#include "ui/gfx/canvas.h"
13
14namespace chromeos {
15
16namespace {
17
18// The number of pixels in the color gradient that fades to transparent.
19const int kGradientWidth = 6;
20
21// The color of the focus ring. In the future this might be a parameter.
22const int kFocusRingColorRed = 247;
23const int kFocusRingColorGreen = 152;
24const int kFocusRingColorBlue = 58;
25
26int sign(int x) {
27  return ((x > 0) ? 1 : (x == 0) ? 0 : -1);
28}
29
30SkPath MakePath(const AccessibilityFocusRing& input_ring,
31                int outset,
32                const gfx::Vector2d& offset) {
33  AccessibilityFocusRing ring = input_ring;
34
35  for (int i = 0; i < 36; i++) {
36    gfx::Point p = input_ring.points[i];
37    gfx::Point prev;
38    gfx::Point next;
39
40    int prev_index = i;
41    do {
42      prev_index = (prev_index + 35) % 36;
43      prev = input_ring.points[prev_index];
44    } while (prev.x() == p.x() && prev.y() == p.y() && prev_index != i);
45
46    int next_index = i;
47    do {
48      next_index = (next_index + 1) % 36;
49      next = input_ring.points[next_index];
50    } while (next.x() == p.x() && next.y() == p.y() && next_index != i);
51
52    gfx::Point delta0 = gfx::Point(sign(p.x() - prev.x()),
53                                   sign(p.y() - prev.y()));
54    gfx::Point delta1 = gfx::Point(sign(next.x() - p.x()),
55                                   sign(next.y() - p.y()));
56
57    if (delta0.x() == delta1.x() && delta0.y() == delta1.y()) {
58      ring.points[i] = gfx::Point(
59          input_ring.points[i].x() + outset * delta0.y(),
60          input_ring.points[i].y() - outset * delta0.x());
61    } else {
62      ring.points[i] = gfx::Point(
63          input_ring.points[i].x() + ((i + 13) % 36 >= 18 ? outset : -outset),
64          input_ring.points[i].y() + ((i + 4) % 36 >= 18 ? outset : -outset));
65    }
66  }
67
68  SkPath path;
69  gfx::Point p0 = ring.points[0] - offset;
70  path.moveTo(SkIntToScalar(p0.x()), SkIntToScalar(p0.y()));
71  for (int i = 0; i < 12; i++) {
72    int index0 = ((3 * i) + 1) % 36;
73    int index1 = ((3 * i) + 2) % 36;
74    int index2 = ((3 * i) + 3) % 36;
75    gfx::Point p0 = ring.points[index0] - offset;
76    gfx::Point p1 = ring.points[index1] - offset;
77    gfx::Point p2 = ring.points[index2] - offset;
78    path.lineTo(SkIntToScalar(p0.x()), SkIntToScalar(p0.y()));
79    path.quadTo(SkIntToScalar(p1.x()), SkIntToScalar(p1.y()),
80                SkIntToScalar(p2.x()), SkIntToScalar(p2.y()));
81  }
82
83  return path;
84}
85
86}  // namespace
87
88AccessibilityFocusRingLayer::AccessibilityFocusRingLayer(
89    FocusRingLayerDelegate* delegate)
90    : FocusRingLayer(delegate) {
91}
92
93AccessibilityFocusRingLayer::~AccessibilityFocusRingLayer() {}
94
95void AccessibilityFocusRingLayer::Set(const AccessibilityFocusRing& ring) {
96  ring_ = ring;
97
98  gfx::Rect bounds = ring.GetBounds();
99  int inset = kGradientWidth;
100  bounds.Inset(-inset, -inset, -inset, -inset);
101
102  gfx::Display display =
103      gfx::Screen::GetNativeScreen()->GetDisplayMatching(bounds);
104  aura::Window* root_window = ash::Shell::GetInstance()->display_controller()
105      ->GetRootWindowForDisplayId(display.id());
106  CreateOrUpdateLayer(root_window, "AccessibilityFocusRing");
107
108  // Update the layer bounds.
109  layer()->SetBounds(bounds);
110}
111
112void AccessibilityFocusRingLayer::OnPaintLayer(gfx::Canvas* canvas) {
113  gfx::Vector2d offset = layer()->bounds().OffsetFromOrigin();
114
115  SkPaint paint;
116  paint.setFlags(SkPaint::kAntiAlias_Flag);
117  paint.setStyle(SkPaint::kStroke_Style);
118  paint.setStrokeWidth(2);
119
120  SkPath path;
121  const int w = kGradientWidth;
122  for (int i = 0; i < w; ++i) {
123    paint.setColor(
124        SkColorSetARGBMacro(
125            255 * (w - i) * (w - i) / (w * w),
126            kFocusRingColorRed, kFocusRingColorGreen, kFocusRingColorBlue));
127    path = MakePath(ring_, i, offset);
128    canvas->DrawPath(path, paint);
129  }
130}
131
132}  // namespace chromeos
133