Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(136)

Side by Side Diff: chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc

Issue 557393003: Introduce AccessibilityFocusRing. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@accessibility_private_histograms
Patch Set: Address last feedback Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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_controller.h"
6
7 #include "chrome/browser/chromeos/ui/focus_ring_layer.h"
8
9 namespace chromeos {
10
11 namespace {
12
13 // The number of pixels the focus ring is outset from the object it outlines,
14 // which also determines the border radius of the rounded corners.
15 // TODO(dmazzoni): take display resolution into account.
16 const int kAccessibilityFocusRingMargin = 16;
17
18 // A Region is an unordered collection of Rects that maintains its
19 // bounding box. Used in the middle of an algorithm that groups
20 // adjacent and overlapping rects.
21 struct Region {
22 explicit Region(gfx::Rect initial_rect) {
23 bounds = initial_rect;
24 rects.push_back(initial_rect);
25 }
26 gfx::Rect bounds;
27 std::vector<gfx::Rect> rects;
28 };
29
30 } // namespace
31
32 // static
33 AccessibilityFocusRingController*
34 AccessibilityFocusRingController::GetInstance() {
35 return Singleton<AccessibilityFocusRingController>::get();
36 }
37
38 AccessibilityFocusRingController::AccessibilityFocusRingController() {
39 }
40
41 AccessibilityFocusRingController::~AccessibilityFocusRingController() {
42 }
43
44 void AccessibilityFocusRingController::SetFocusRing(
45 const std::vector<gfx::Rect>& rects) {
46 rects_ = rects;
47 Update();
48 }
49
50 void AccessibilityFocusRingController::Update() {
51 rings_.clear();
52 RectsToRings(rects_, &rings_);
53
54 if (!main_focus_ring_layer_)
55 main_focus_ring_layer_.reset(new AccessibilityFocusRingLayer(this));
56
57 if (!rings_.empty())
58 main_focus_ring_layer_->Set(rings_[0]);
59 }
60
61 void AccessibilityFocusRingController::RectsToRings(
62 const std::vector<gfx::Rect>& rects,
63 std::vector<AccessibilityFocusRing>* rings) const {
64 if (rects.empty())
65 return;
66
67 // Split the rects into contiguous regions.
68 std::vector<Region> regions;
69 regions.push_back(Region(rects[0]));
70 for (size_t i = 1; i < rects.size(); ++i) {
71 bool found = false;
72 for (size_t j = 0; j < regions.size(); ++j) {
73 if (Intersects(rects[i], regions[j].bounds)) {
74 regions[j].rects.push_back(rects[i]);
75 regions[j].bounds.Union(rects[i]);
76 found = true;
77 }
78 }
79 if (!found) {
80 regions.push_back(Region(rects[i]));
81 }
82 }
83
84 // Keep merging regions that intersect.
85 // TODO(dmazzoni): reduce the worst-case complexity! This appears like
86 // it could be O(n^3), make sure it's not in practice.
87 bool merged;
88 do {
89 merged = false;
90 for (size_t i = 0; i < regions.size() - 1 && !merged; ++i) {
91 for (size_t j = i + 1; j < regions.size() && !merged; ++j) {
92 if (Intersects(regions[i].bounds, regions[j].bounds)) {
93 regions[i].rects.insert(regions[i].rects.end(),
94 regions[j].rects.begin(),
95 regions[j].rects.end());
96 regions[i].bounds.Union(regions[j].bounds);
97 regions.erase(regions.begin() + j);
98 merged = true;
99 }
100 }
101 }
102 } while (merged);
103
104 for (size_t i = 0; i < regions.size(); ++i) {
105 std::sort(regions[i].rects.begin(), regions[i].rects.end());
106 rings->push_back(RingFromSortedRects(regions[i].rects));
107 }
108 }
109
110 int AccessibilityFocusRingController::GetMargin() const {
111 return kAccessibilityFocusRingMargin;
112 }
113
114 // Given a vector of rects that all overlap, already sorted from top to bottom
115 // and left to right, split them into three shapes covering the top, middle,
116 // and bottom of a "paragraph shape".
117 //
118 // Input:
119 //
120 // +---+---+
121 // | 1 | 2 |
122 // +---------------------+---+---+
123 // | 3 |
124 // +--------+---------------+----+
125 // | 4 | 5 |
126 // +--------+---------------+--+
127 // | 6 |
128 // +---------+-----------------+
129 // | 7 |
130 // +---------+
131 //
132 // Output:
133 //
134 // +-------+
135 // | Top |
136 // +---------------------+-------+
137 // | |
138 // | |
139 // | Middle |
140 // | |
141 // | |
142 // +---------+-------------------+
143 // | Bottom |
144 // +---------+
145 //
146 // When there's no clear "top" or "bottom" segment, split the overall rect
147 // evenly so that some of the area still fits into the "top" and "bottom"
148 // segments.
149 void AccessibilityFocusRingController::SplitIntoParagraphShape(
150 const std::vector<gfx::Rect>& rects,
151 gfx::Rect* top,
152 gfx::Rect* middle,
153 gfx::Rect* bottom) const {
154 size_t n = rects.size();
155
156 // Figure out how many rects belong in the top portion.
157 gfx::Rect top_rect = rects[0];
158 int top_middle = (top_rect.y() + top_rect.bottom()) / 2;
159 size_t top_count = 1;
160 while (top_count < n && rects[top_count].y() < top_middle) {
161 top_rect.Union(rects[top_count]);
162 top_middle = (top_rect.y() + top_rect.bottom()) / 2;
163 top_count++;
164 }
165
166 // Figure out how many rects belong in the bottom portion.
167 gfx::Rect bottom_rect = rects[n - 1];
168 int bottom_middle = (bottom_rect.y() + bottom_rect.bottom()) / 2;
169 size_t bottom_count = std::min(static_cast<size_t>(1), n - top_count);
170 while (bottom_count + top_count < n &&
171 rects[n - bottom_count - 1].bottom() > bottom_middle) {
172 bottom_rect.Union(rects[n - bottom_count - 1]);
173 bottom_middle = (bottom_rect.y() + bottom_rect.bottom()) / 2;
174 bottom_count++;
175 }
176
177 // Whatever's left goes to the middle rect, but if there's no middle or
178 // bottom rect, split the existing rects evenly to make one.
179 gfx::Rect middle_rect;
180 if (top_count + bottom_count < n) {
181 middle_rect = rects[top_count];
182 for (size_t i = top_count + 1; i < n - bottom_count; i++)
183 middle_rect.Union(rects[i]);
184 } else if (bottom_count > 0) {
185 gfx::Rect enclosing_rect = top_rect;
186 enclosing_rect.Union(bottom_rect);
187 int middle_top = (top_rect.y() + top_rect.bottom() * 2) / 3;
188 int middle_bottom = (bottom_rect.y() * 2 + bottom_rect.bottom()) / 3;
189 top_rect.set_height(middle_top - top_rect.y());
190 bottom_rect.set_height(bottom_rect.bottom() - middle_bottom);
191 bottom_rect.set_y(middle_bottom);
192 middle_rect = gfx::Rect(enclosing_rect.x(), middle_top,
193 enclosing_rect.width(), middle_bottom - middle_top);
194 } else {
195 int middle_top = (top_rect.y() * 2 + top_rect.bottom()) / 3;
196 int middle_bottom = (top_rect.y() + top_rect.bottom() * 2) / 3;
197 middle_rect = gfx::Rect(top_rect.x(), middle_top,
198 top_rect.width(), middle_bottom - middle_top);
199 bottom_rect = gfx::Rect(
200 top_rect.x(), middle_bottom,
201 top_rect.width(), top_rect.bottom() - middle_bottom);
202 top_rect.set_height(middle_top - top_rect.y());
203 }
204
205 if (middle_rect.y() > top_rect.bottom()) {
206 middle_rect.set_height(
207 middle_rect.height() + middle_rect.y() - top_rect.bottom());
208 middle_rect.set_y(top_rect.bottom());
209 }
210
211 if (middle_rect.bottom() < bottom_rect.y()) {
212 middle_rect.set_height(bottom_rect.y() - middle_rect.y());
213 }
214
215 *top = top_rect;
216 *middle = middle_rect;
217 *bottom = bottom_rect;
218 }
219
220 AccessibilityFocusRing AccessibilityFocusRingController::RingFromSortedRects(
221 const std::vector<gfx::Rect>& rects) const {
222 if (rects.size() == 1)
223 return AccessibilityFocusRing::CreateWithRect(rects[0], GetMargin());
224
225 gfx::Rect top;
226 gfx::Rect middle;
227 gfx::Rect bottom;
228 SplitIntoParagraphShape(rects, &top, &middle, &bottom);
229
230 return AccessibilityFocusRing::CreateWithParagraphShape(
231 top, middle, bottom, GetMargin());
232 }
233
234 bool AccessibilityFocusRingController::Intersects(
235 const gfx::Rect& r1, const gfx::Rect& r2) const {
236 int slop = GetMargin();
237 return (r2.x() <= r1.right() + slop &&
238 r2.right() >= r1.x() - slop &&
239 r2.y() <= r1.bottom() + slop &&
240 r2.bottom() >= r1.y() - slop);
241 }
242
243 void AccessibilityFocusRingController::OnDeviceScaleFactorChanged() {
244 Update();
245 }
246
247 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698