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

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

Issue 1822823002: Implement Chrome OS accessibility features to highlight focus, caret & cursor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed feedback, added missing files Created 4 years, 9 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h" 5 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <algorithm>
xiyuan 2016/03/22 04:34:27 nit: alpha sort the headers?
dmazzoni 2016/03/22 20:23:02 Actually I think this is right - Chrome's style is
8 9
9 #include "ash/display/window_tree_host_manager.h" 10 #include "ash/display/window_tree_host_manager.h"
10 #include "ash/shell.h" 11 #include "ash/shell.h"
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "chrome/browser/chromeos/ui/focus_ring_layer.h" 13 #include "chrome/browser/chromeos/ui/focus_ring_layer.h"
13 #include "ui/gfx/screen.h" 14 #include "ui/gfx/screen.h"
14 15
15 namespace chromeos { 16 namespace chromeos {
16 17
17 namespace { 18 namespace {
18 19
19 // The number of pixels the focus ring is outset from the object it outlines, 20 // The number of pixels the focus ring is outset from the object it outlines,
20 // which also determines the border radius of the rounded corners. 21 // which also determines the border radius of the rounded corners.
21 // TODO(dmazzoni): take display resolution into account. 22 // TODO(dmazzoni): take display resolution into account.
22 const int kAccessibilityFocusRingMargin = 7; 23 const int kAccessibilityFocusRingMargin = 7;
23 24
24 // Time to transition between one location and the next. 25 // Time to transition between one location and the next.
25 const int kTransitionTimeMilliseconds = 300; 26 const int kTransitionTimeMilliseconds = 300;
26 27
28 const int kCursorFadeInTimeMilliseconds = 400;
29 const int kCursorFadeOutTimeMilliseconds = 1200;
30
31 // The color of the cursor ring.
32 const int kCursorRingColorRed = 255;
33 const int kCursorRingColorGreen = 51;
34 const int kCursorRingColorBlue = 51;
35
36 // The color of the caret ring.
37 const int kCaretRingColorRed = 51;
38 const int kCaretRingColorGreen = 51;
39 const int kCaretRingColorBlue = 255;
40
27 // A Region is an unordered collection of Rects that maintains its 41 // A Region is an unordered collection of Rects that maintains its
28 // bounding box. Used in the middle of an algorithm that groups 42 // bounding box. Used in the middle of an algorithm that groups
29 // adjacent and overlapping rects. 43 // adjacent and overlapping rects.
30 struct Region { 44 struct Region {
31 explicit Region(gfx::Rect initial_rect) { 45 explicit Region(gfx::Rect initial_rect) {
32 bounds = initial_rect; 46 bounds = initial_rect;
33 rects.push_back(initial_rect); 47 rects.push_back(initial_rect);
34 } 48 }
35 gfx::Rect bounds; 49 gfx::Rect bounds;
36 std::vector<gfx::Rect> rects; 50 std::vector<gfx::Rect> rects;
37 }; 51 };
38 52
39 } // namespace 53 } // namespace
40 54
41 // static 55 // static
42 AccessibilityFocusRingController* 56 AccessibilityFocusRingController*
43 AccessibilityFocusRingController::GetInstance() { 57 AccessibilityFocusRingController::GetInstance() {
44 return base::Singleton<AccessibilityFocusRingController>::get(); 58 return base::Singleton<AccessibilityFocusRingController>::get();
45 } 59 }
46 60
47 AccessibilityFocusRingController::AccessibilityFocusRingController() 61 AccessibilityFocusRingController::AccessibilityFocusRingController()
48 : compositor_(nullptr) { 62 : compositor_(nullptr), cursor_opacity_(0), cursor_compositor_(nullptr) {}
49 }
50 63
51 AccessibilityFocusRingController::~AccessibilityFocusRingController() { 64 AccessibilityFocusRingController::~AccessibilityFocusRingController() {
52 } 65 }
53 66
54 void AccessibilityFocusRingController::SetFocusRing( 67 void AccessibilityFocusRingController::SetFocusRing(
55 const std::vector<gfx::Rect>& rects) { 68 const std::vector<gfx::Rect>& rects) {
56 rects_ = rects; 69 rects_ = rects;
57 Update(); 70 Update();
58 } 71 }
59 72
60 void AccessibilityFocusRingController::Update() { 73 void AccessibilityFocusRingController::Update() {
61 previous_rings_.swap(rings_); 74 previous_rings_.swap(rings_);
62 rings_.clear(); 75 rings_.clear();
63 RectsToRings(rects_, &rings_); 76 RectsToRings(rects_, &rings_);
64 layers_.resize(rings_.size()); 77 layers_.resize(rings_.size());
78 if (rings_.empty())
79 return;
80
65 for (size_t i = 0; i < rings_.size(); ++i) { 81 for (size_t i = 0; i < rings_.size(); ++i) {
66 if (!layers_[i]) 82 if (!layers_[i])
67 layers_[i] = new AccessibilityFocusRingLayer(this); 83 layers_[i] = new AccessibilityFocusRingLayer(this);
68 84
69 if (i > 0) { 85 if (i > 0) {
70 // Focus rings other than the first one don't animate. 86 // Focus rings other than the first one don't animate.
71 layers_[i]->Set(rings_[i]); 87 layers_[i]->Set(rings_[i]);
72 continue;
73 }
74
75 gfx::Rect bounds = rings_[0].GetBounds();
76 gfx::Display display = gfx::Screen::GetScreen()->GetDisplayMatching(bounds);
77 aura::Window* root_window = ash::Shell::GetInstance()
78 ->window_tree_host_manager()
79 ->GetRootWindowForDisplayId(display.id());
80 ui::Compositor* compositor = root_window->layer()->GetCompositor();
81 if (!compositor || root_window != layers_[0]->root_window()) {
82 layers_[0]->Set(rings_[0]);
83 if (compositor_ && compositor_->HasAnimationObserver(this)) {
84 compositor_->RemoveAnimationObserver(this);
85 compositor_ = nullptr;
86 }
87 continue;
88 }
89
90 focus_change_time_ = base::TimeTicks::Now();
91 if (!compositor->HasAnimationObserver(this)) {
92 compositor_ = compositor;
93 compositor_->AddAnimationObserver(this);
94 } 88 }
95 } 89 }
90
91 ui::Compositor* compositor = CompositorForBounds(rings_[0].GetBounds());
92 if (compositor != compositor_) {
93 RemoveAnimationObservers();
94 compositor_ = compositor;
95 AddAnimationObservers();
96 }
97
98 if (compositor_ && compositor_->HasAnimationObserver(this)) {
99 focus_change_time_ = base::TimeTicks::Now();
100 } else {
101 // If we can't animate, set the location of the first ring.
102 layers_[0]->Set(rings_[0]);
103 }
104 }
105
106 ui::Compositor* AccessibilityFocusRingController::CompositorForBounds(
107 const gfx::Rect& bounds) {
108 gfx::Display display = gfx::Screen::GetScreen()->GetDisplayMatching(bounds);
109 aura::Window* root_window = ash::Shell::GetInstance()
110 ->window_tree_host_manager()
111 ->GetRootWindowForDisplayId(display.id());
112 return root_window->layer()->GetCompositor();
113 }
114
115 void AccessibilityFocusRingController::RemoveAnimationObservers() {
116 if (compositor_ && compositor_->HasAnimationObserver(this))
117 compositor_->RemoveAnimationObserver(this);
118 if (cursor_compositor_ && cursor_compositor_->HasAnimationObserver(this))
119 cursor_compositor_->RemoveAnimationObserver(this);
120 }
121
122 void AccessibilityFocusRingController::AddAnimationObservers() {
123 if (compositor_ && !compositor_->HasAnimationObserver(this))
124 compositor_->AddAnimationObserver(this);
125 if (cursor_compositor_ && !cursor_compositor_->HasAnimationObserver(this))
126 cursor_compositor_->AddAnimationObserver(this);
127 }
128
129 void AccessibilityFocusRingController::SetCursorRing(
130 const gfx::Point& location) {
131 cursor_location_ = location;
132 cursor_change_time_ = base::TimeTicks::Now();
133 if (cursor_opacity_ == 0)
134 cursor_start_time_ = cursor_change_time_;
135
136 if (!cursor_layer_) {
137 cursor_layer_.reset(new AccessibilityCursorRingLayer(
138 this, kCursorRingColorRed, kCursorRingColorGreen,
139 kCursorRingColorBlue));
140 }
141 cursor_layer_->Set(location);
142
143 ui::Compositor* compositor =
144 CompositorForBounds(gfx::Rect(location.x(), location.y(), 0, 0));
145 if (compositor != cursor_compositor_) {
146 RemoveAnimationObservers();
147 cursor_compositor_ = compositor;
148 AddAnimationObservers();
149 }
150 }
151
152 void AccessibilityFocusRingController::SetCaretRing(
153 const gfx::Point& location) {
154 caret_location_ = location;
155
156 if (!caret_layer_) {
157 caret_layer_.reset(new AccessibilityCursorRingLayer(
158 this, kCaretRingColorRed, kCaretRingColorGreen, kCaretRingColorBlue));
159 }
160
161 caret_layer_->Set(location);
96 } 162 }
97 163
98 void AccessibilityFocusRingController::RectsToRings( 164 void AccessibilityFocusRingController::RectsToRings(
99 const std::vector<gfx::Rect>& src_rects, 165 const std::vector<gfx::Rect>& src_rects,
100 std::vector<AccessibilityFocusRing>* rings) const { 166 std::vector<AccessibilityFocusRing>* rings) const {
101 if (src_rects.empty()) 167 if (src_rects.empty())
102 return; 168 return;
103 169
104 // Give all of the rects a margin. 170 // Give all of the rects a margin.
105 std::vector<gfx::Rect> rects; 171 std::vector<gfx::Rect> rects;
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 r2.y() <= r1.bottom() + slop && 350 r2.y() <= r1.bottom() + slop &&
285 r2.bottom() >= r1.y() - slop); 351 r2.bottom() >= r1.y() - slop);
286 } 352 }
287 353
288 void AccessibilityFocusRingController::OnDeviceScaleFactorChanged() { 354 void AccessibilityFocusRingController::OnDeviceScaleFactorChanged() {
289 Update(); 355 Update();
290 } 356 }
291 357
292 void AccessibilityFocusRingController::OnAnimationStep( 358 void AccessibilityFocusRingController::OnAnimationStep(
293 base::TimeTicks timestamp) { 359 base::TimeTicks timestamp) {
294 if (rings_.empty()) 360 if (!rings_.empty() && compositor_)
295 return; 361 AnimateFocusRings(timestamp);
296 362
297 CHECK(compositor_); 363 if (cursor_layer_ && cursor_compositor_)
364 AnimateCursorRing(timestamp);
365 }
366
367 void AccessibilityFocusRingController::AnimateFocusRings(
368 base::TimeTicks timestamp) {
298 CHECK(!rings_.empty()); 369 CHECK(!rings_.empty());
299 CHECK(!layers_.empty()); 370 CHECK(!layers_.empty());
300 CHECK(layers_[0]); 371 CHECK(layers_[0]);
301 372
302 // It's quite possible for the first 1 or 2 animation frames to be 373 // It's quite possible for the first 1 or 2 animation frames to be
303 // for a timestamp that's earlier than the time we received the 374 // for a timestamp that's earlier than the time we received the
304 // focus change, so we just treat those as a delta of zero. 375 // focus change, so we just treat those as a delta of zero.
305 if (timestamp < focus_change_time_) 376 if (timestamp < focus_change_time_)
306 timestamp = focus_change_time_; 377 timestamp = focus_change_time_;
307 378
308 base::TimeDelta delta = timestamp - focus_change_time_; 379 base::TimeDelta delta = timestamp - focus_change_time_;
309 base::TimeDelta transition_time = 380 base::TimeDelta transition_time =
310 base::TimeDelta::FromMilliseconds(kTransitionTimeMilliseconds); 381 base::TimeDelta::FromMilliseconds(kTransitionTimeMilliseconds);
311 if (delta >= transition_time) { 382 if (delta >= transition_time) {
312 layers_[0]->Set(rings_[0]); 383 layers_[0]->Set(rings_[0]);
313 compositor_->RemoveAnimationObserver(this); 384
385 RemoveAnimationObservers();
314 compositor_ = nullptr; 386 compositor_ = nullptr;
387 AddAnimationObservers();
315 return; 388 return;
316 } 389 }
317 390
318 double fraction = delta.InSecondsF() / transition_time.InSecondsF(); 391 double fraction = delta.InSecondsF() / transition_time.InSecondsF();
319 392
320 // Ease-in effect. 393 // Ease-in effect.
321 fraction = pow(fraction, 0.3); 394 fraction = pow(fraction, 0.3);
322 395
396 // Handle corner case where we're animating but we don't have previous
397 // rings.
398 if (previous_rings_.empty())
399 previous_rings_ = rings_;
400
323 layers_[0]->Set(AccessibilityFocusRing::Interpolate( 401 layers_[0]->Set(AccessibilityFocusRing::Interpolate(
324 previous_rings_[0], rings_[0], fraction)); 402 previous_rings_[0], rings_[0], fraction));
325 } 403 }
326 404
405 void AccessibilityFocusRingController::AnimateCursorRing(
406 base::TimeTicks timestamp) {
407 CHECK(cursor_layer_);
408
409 // It's quite possible for the first 1 or 2 animation frames to be
410 // for a timestamp that's earlier than the time we received the
411 // mouse movement, so we just treat those as a delta of zero.
412 if (timestamp < cursor_start_time_)
413 timestamp = cursor_start_time_;
414
415 base::TimeDelta start_delta = timestamp - cursor_start_time_;
416 base::TimeDelta change_delta = timestamp - cursor_change_time_;
417 base::TimeDelta fade_in_time =
418 base::TimeDelta::FromMilliseconds(kCursorFadeInTimeMilliseconds);
419 base::TimeDelta fade_out_time =
420 base::TimeDelta::FromMilliseconds(kCursorFadeOutTimeMilliseconds);
421
422 if (change_delta > fade_in_time + fade_out_time) {
423 cursor_opacity_ = 0.0;
424 cursor_layer_.reset();
425 return;
426 }
427
428 if (start_delta < fade_in_time) {
429 cursor_opacity_ = start_delta.InSecondsF() / fade_in_time.InSecondsF();
430 if (cursor_opacity_ > 1.0)
431 cursor_opacity_ = 1.0;
432 } else {
433 cursor_opacity_ = 1.0 - (change_delta.InSecondsF() /
434 (fade_in_time + fade_out_time).InSecondsF());
435 if (cursor_opacity_ < 0.0)
436 cursor_opacity_ = 0.0;
437 }
438 cursor_layer_->SetOpacity(cursor_opacity_);
439 }
440
327 void AccessibilityFocusRingController::OnCompositingShuttingDown( 441 void AccessibilityFocusRingController::OnCompositingShuttingDown(
328 ui::Compositor* compositor) { 442 ui::Compositor* compositor) {
329 DCHECK_EQ(compositor_, compositor); 443 if (compositor == compositor_ || compositor == cursor_compositor_)
330 compositor_->RemoveAnimationObserver(this); 444 compositor->RemoveAnimationObserver(this);
331 compositor_ = nullptr; 445
446 if (compositor == compositor_)
447 compositor_ = nullptr;
448 if (compositor == cursor_compositor_)
449 cursor_compositor_ = nullptr;
332 } 450 }
333 451
334 } // namespace chromeos 452 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698