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

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

Powered by Google App Engine
This is Rietveld 408576698