OLD | NEW |
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 Loading... |
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 |
OLD | NEW |