Chromium Code Reviews| 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 #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 Loading... | |
| 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 |
| OLD | NEW |