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 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 | 10 |
| 11 #include "ash/shell.h" | 11 #include "ash/shell.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "chrome/browser/chromeos/ui/focus_ring_layer.h" | 13 #include "chrome/browser/chromeos/ui/focus_ring_layer.h" |
| 14 | 14 |
| 15 namespace chromeos { | 15 namespace chromeos { |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 | 18 |
| 19 // The number of pixels the focus ring is outset from the object it outlines, | 19 // 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. | 20 // which also determines the border radius of the rounded corners. |
| 21 // TODO(dmazzoni): take display resolution into account. | 21 // TODO(dmazzoni): take display resolution into account. |
| 22 const int kAccessibilityFocusRingMargin = 7; | 22 const int kAccessibilityFocusRingMargin = 7; |
| 23 | 23 |
| 24 // Time to transition between one location and the next. | 24 // Time to transition between one location and the next. |
| 25 const int kTransitionTimeMilliseconds = 300; | 25 const int kTransitionTimeMilliseconds = 300; |
| 26 | 26 |
| 27 // Focus constants. | |
| 28 const int kFocusFadeInTimeMilliseconds = 100; | |
| 29 const int kFocusFadeOutTimeMilliseconds = 1600; | |
| 30 | |
| 31 // Cursor constants. | |
| 27 const int kCursorFadeInTimeMilliseconds = 400; | 32 const int kCursorFadeInTimeMilliseconds = 400; |
| 28 const int kCursorFadeOutTimeMilliseconds = 1200; | 33 const int kCursorFadeOutTimeMilliseconds = 1200; |
| 29 | |
| 30 // The color of the cursor ring. | |
| 31 const int kCursorRingColorRed = 255; | 34 const int kCursorRingColorRed = 255; |
| 32 const int kCursorRingColorGreen = 51; | 35 const int kCursorRingColorGreen = 51; |
| 33 const int kCursorRingColorBlue = 51; | 36 const int kCursorRingColorBlue = 51; |
| 34 | 37 |
| 35 // The color of the caret ring. | 38 // Caret constants. |
| 39 const int kCaretFadeInTimeMilliseconds = 100; | |
| 40 const int kCaretFadeOutTimeMilliseconds = 1600; | |
| 36 const int kCaretRingColorRed = 51; | 41 const int kCaretRingColorRed = 51; |
| 37 const int kCaretRingColorGreen = 51; | 42 const int kCaretRingColorGreen = 51; |
| 38 const int kCaretRingColorBlue = 255; | 43 const int kCaretRingColorBlue = 255; |
| 39 | 44 |
| 40 // A Region is an unordered collection of Rects that maintains its | 45 // A Region is an unordered collection of Rects that maintains its |
| 41 // bounding box. Used in the middle of an algorithm that groups | 46 // bounding box. Used in the middle of an algorithm that groups |
| 42 // adjacent and overlapping rects. | 47 // adjacent and overlapping rects. |
| 43 struct Region { | 48 struct Region { |
| 44 explicit Region(gfx::Rect initial_rect) { | 49 explicit Region(gfx::Rect initial_rect) { |
| 45 bounds = initial_rect; | 50 bounds = initial_rect; |
| 46 rects.push_back(initial_rect); | 51 rects.push_back(initial_rect); |
| 47 } | 52 } |
| 48 gfx::Rect bounds; | 53 gfx::Rect bounds; |
| 49 std::vector<gfx::Rect> rects; | 54 std::vector<gfx::Rect> rects; |
| 50 }; | 55 }; |
| 51 | 56 |
| 52 } // namespace | 57 } // namespace |
| 53 | 58 |
| 54 // static | 59 // static |
| 55 AccessibilityFocusRingController* | 60 AccessibilityFocusRingController* |
| 56 AccessibilityFocusRingController::GetInstance() { | 61 AccessibilityFocusRingController::GetInstance() { |
| 57 return base::Singleton<AccessibilityFocusRingController>::get(); | 62 return base::Singleton<AccessibilityFocusRingController>::get(); |
| 58 } | 63 } |
| 59 | 64 |
| 60 AccessibilityFocusRingController::AccessibilityFocusRingController() | 65 AccessibilityFocusRingController::AccessibilityFocusRingController() |
| 61 : cursor_opacity_(0) {} | 66 : focus_ring_behavior_( |
| 67 AccessibilityFocusRingController::FADE_OUT_FOCUS_RING) { | |
|
xiyuan
2016/06/08 18:17:52
nit: initialize in header?
dmazzoni
2016/06/08 20:01:23
Done.
| |
| 68 focus_animation_info_.fade_in_time = | |
| 69 base::TimeDelta::FromMilliseconds(kFocusFadeInTimeMilliseconds); | |
| 70 focus_animation_info_.fade_out_time = | |
| 71 base::TimeDelta::FromMilliseconds(kFocusFadeOutTimeMilliseconds); | |
| 72 cursor_animation_info_.fade_in_time = | |
| 73 base::TimeDelta::FromMilliseconds(kCursorFadeInTimeMilliseconds); | |
| 74 cursor_animation_info_.fade_out_time = | |
| 75 base::TimeDelta::FromMilliseconds(kCursorFadeOutTimeMilliseconds); | |
| 76 caret_animation_info_.fade_in_time = | |
| 77 base::TimeDelta::FromMilliseconds(kCaretFadeInTimeMilliseconds); | |
| 78 caret_animation_info_.fade_out_time = | |
| 79 base::TimeDelta::FromMilliseconds(kCaretFadeOutTimeMilliseconds); | |
| 80 } | |
| 62 | 81 |
| 63 AccessibilityFocusRingController::~AccessibilityFocusRingController() { | 82 AccessibilityFocusRingController::~AccessibilityFocusRingController() { |
| 64 } | 83 } |
| 65 | 84 |
| 66 void AccessibilityFocusRingController::SetFocusRing( | 85 void AccessibilityFocusRingController::SetFocusRing( |
| 67 const std::vector<gfx::Rect>& rects) { | 86 const std::vector<gfx::Rect>& rects, |
| 68 rects_ = rects; | 87 AccessibilityFocusRingController::FocusRingBehavior focus_ring_behavior) { |
| 69 Update(); | 88 focus_ring_behavior_ = focus_ring_behavior; |
| 89 OnLayerChange(&focus_animation_info_); | |
| 90 focus_rects_ = rects; | |
| 91 UpdateFocusRingsFromFocusRects(); | |
| 70 } | 92 } |
| 71 | 93 |
| 72 void AccessibilityFocusRingController::Update() { | 94 void AccessibilityFocusRingController::UpdateFocusRingsFromFocusRects() { |
| 73 previous_rings_.swap(rings_); | 95 previous_focus_rings_.swap(focus_rings_); |
| 74 rings_.clear(); | 96 focus_rings_.clear(); |
| 75 RectsToRings(rects_, &rings_); | 97 RectsToRings(focus_rects_, &focus_rings_); |
| 76 layers_.resize(rings_.size()); | 98 focus_layers_.resize(focus_rings_.size()); |
| 77 if (rings_.empty()) | 99 if (focus_rings_.empty()) |
| 78 return; | 100 return; |
| 79 | 101 |
| 80 for (size_t i = 0; i < rings_.size(); ++i) { | 102 for (size_t i = 0; i < focus_rings_.size(); ++i) { |
| 81 if (!layers_[i]) | 103 if (!focus_layers_[i]) |
| 82 layers_[i] = new AccessibilityFocusRingLayer(this); | 104 focus_layers_[i] = new AccessibilityFocusRingLayer(this); |
| 83 | |
| 84 if (i > 0) { | |
| 85 // Focus rings other than the first one don't animate. | |
| 86 layers_[i]->Set(rings_[i]); | |
| 87 } | |
| 88 } | 105 } |
| 89 | 106 |
| 90 if (layers_[0]->CanAnimate()) { | 107 if (focus_ring_behavior_ == PERSIST_FOCUS_RING && |
| 91 focus_change_time_ = base::TimeTicks::Now(); | 108 focus_layers_[0]->CanAnimate()) { |
| 109 // In PERSIST mode, animate the first ring to its destination | |
| 110 // location, then set the rest of the rings directly. | |
| 111 for (size_t i = 1; i < focus_rings_.size(); ++i) | |
| 112 focus_layers_[i]->Set(focus_rings_[i]); | |
| 92 } else { | 113 } else { |
| 93 // If we can't animate, set the location of the first ring. | 114 // In FADE mode, set all focus rings to their destination location. |
| 94 layers_[0]->Set(rings_[0]); | 115 for (size_t i = 0; i < focus_rings_.size(); ++i) |
| 116 focus_layers_[i]->Set(focus_rings_[i]); | |
| 95 } | 117 } |
| 96 } | 118 } |
| 97 | 119 |
| 120 void AccessibilityFocusRingController::OnLayerChange( | |
| 121 AccessibilityFocusRingController::LayerAnimationInfo* animation_info) { | |
| 122 animation_info->change_time = base::TimeTicks::Now(); | |
| 123 if (animation_info->opacity == 0) | |
| 124 animation_info->start_time = animation_info->change_time; | |
| 125 } | |
| 126 | |
| 98 void AccessibilityFocusRingController::SetCursorRing( | 127 void AccessibilityFocusRingController::SetCursorRing( |
| 99 const gfx::Point& location) { | 128 const gfx::Point& location) { |
| 100 cursor_location_ = location; | 129 cursor_location_ = location; |
| 101 cursor_change_time_ = base::TimeTicks::Now(); | |
| 102 if (cursor_opacity_ == 0) | |
| 103 cursor_start_time_ = cursor_change_time_; | |
| 104 | |
| 105 if (!cursor_layer_) { | 130 if (!cursor_layer_) { |
| 106 cursor_layer_.reset(new AccessibilityCursorRingLayer( | 131 cursor_layer_.reset(new AccessibilityCursorRingLayer( |
| 107 this, kCursorRingColorRed, kCursorRingColorGreen, | 132 this, kCursorRingColorRed, kCursorRingColorGreen, |
| 108 kCursorRingColorBlue)); | 133 kCursorRingColorBlue)); |
| 109 } | 134 } |
| 110 cursor_layer_->Set(location); | 135 cursor_layer_->Set(location); |
| 136 OnLayerChange(&cursor_animation_info_); | |
| 111 } | 137 } |
| 112 | 138 |
| 113 void AccessibilityFocusRingController::SetCaretRing( | 139 void AccessibilityFocusRingController::SetCaretRing( |
| 114 const gfx::Point& location) { | 140 const gfx::Point& location) { |
| 115 caret_location_ = location; | 141 caret_location_ = location; |
| 116 | 142 |
| 117 if (!caret_layer_) { | 143 if (!caret_layer_) { |
| 118 caret_layer_.reset(new AccessibilityCursorRingLayer( | 144 caret_layer_.reset(new AccessibilityCursorRingLayer( |
| 119 this, kCaretRingColorRed, kCaretRingColorGreen, kCaretRingColorBlue)); | 145 this, kCaretRingColorRed, kCaretRingColorGreen, kCaretRingColorBlue)); |
| 120 } | 146 } |
| 121 | 147 |
| 122 caret_layer_->Set(location); | 148 caret_layer_->Set(location); |
| 149 OnLayerChange(&caret_animation_info_); | |
| 123 } | 150 } |
| 124 | 151 |
| 125 void AccessibilityFocusRingController::RectsToRings( | 152 void AccessibilityFocusRingController::RectsToRings( |
| 126 const std::vector<gfx::Rect>& src_rects, | 153 const std::vector<gfx::Rect>& src_rects, |
| 127 std::vector<AccessibilityFocusRing>* rings) const { | 154 std::vector<AccessibilityFocusRing>* rings) const { |
| 128 if (src_rects.empty()) | 155 if (src_rects.empty()) |
| 129 return; | 156 return; |
| 130 | 157 |
| 131 // Give all of the rects a margin. | 158 // Give all of the rects a margin. |
| 132 std::vector<gfx::Rect> rects; | 159 std::vector<gfx::Rect> rects; |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 306 bool AccessibilityFocusRingController::Intersects( | 333 bool AccessibilityFocusRingController::Intersects( |
| 307 const gfx::Rect& r1, const gfx::Rect& r2) const { | 334 const gfx::Rect& r1, const gfx::Rect& r2) const { |
| 308 int slop = GetMargin(); | 335 int slop = GetMargin(); |
| 309 return (r2.x() <= r1.right() + slop && | 336 return (r2.x() <= r1.right() + slop && |
| 310 r2.right() >= r1.x() - slop && | 337 r2.right() >= r1.x() - slop && |
| 311 r2.y() <= r1.bottom() + slop && | 338 r2.y() <= r1.bottom() + slop && |
| 312 r2.bottom() >= r1.y() - slop); | 339 r2.bottom() >= r1.y() - slop); |
| 313 } | 340 } |
| 314 | 341 |
| 315 void AccessibilityFocusRingController::OnDeviceScaleFactorChanged() { | 342 void AccessibilityFocusRingController::OnDeviceScaleFactorChanged() { |
| 316 Update(); | 343 UpdateFocusRingsFromFocusRects(); |
| 317 } | 344 } |
| 318 | 345 |
| 319 void AccessibilityFocusRingController::OnAnimationStep( | 346 void AccessibilityFocusRingController::OnAnimationStep( |
| 320 base::TimeTicks timestamp) { | 347 base::TimeTicks timestamp) { |
| 321 if (!rings_.empty() && layers_[0]->CanAnimate()) | 348 if (!focus_rings_.empty() && focus_layers_[0]->CanAnimate()) |
| 322 AnimateFocusRings(timestamp); | 349 AnimateFocusRings(timestamp); |
| 323 | 350 |
| 324 if (cursor_layer_ && cursor_layer_->CanAnimate()) | 351 if (cursor_layer_ && cursor_layer_->CanAnimate()) |
| 325 AnimateCursorRing(timestamp); | 352 AnimateCursorRing(timestamp); |
| 353 | |
| 354 if (caret_layer_ && caret_layer_->CanAnimate()) | |
| 355 AnimateCaretRing(timestamp); | |
| 326 } | 356 } |
| 327 | 357 |
| 328 void AccessibilityFocusRingController::AnimateFocusRings( | 358 void AccessibilityFocusRingController::AnimateFocusRings( |
| 329 base::TimeTicks timestamp) { | 359 base::TimeTicks timestamp) { |
| 330 CHECK(!rings_.empty()); | 360 CHECK(!focus_rings_.empty()); |
| 331 CHECK(!layers_.empty()); | 361 CHECK(!focus_layers_.empty()); |
| 332 CHECK(layers_[0]); | 362 CHECK(focus_layers_[0]); |
| 333 | 363 |
| 334 // It's quite possible for the first 1 or 2 animation frames to be | 364 // It's quite possible for the first 1 or 2 animation frames to be |
| 335 // for a timestamp that's earlier than the time we received the | 365 // for a timestamp that's earlier than the time we received the |
| 336 // focus change, so we just treat those as a delta of zero. | 366 // focus change, so we just treat those as a delta of zero. |
| 337 if (timestamp < focus_change_time_) | 367 if (timestamp < focus_animation_info_.change_time) |
| 338 timestamp = focus_change_time_; | 368 timestamp = focus_animation_info_.change_time; |
| 339 | 369 |
| 340 base::TimeDelta delta = timestamp - focus_change_time_; | 370 if (focus_ring_behavior_ == PERSIST_FOCUS_RING) { |
| 341 base::TimeDelta transition_time = | 371 base::TimeDelta delta = timestamp - focus_animation_info_.change_time; |
| 342 base::TimeDelta::FromMilliseconds(kTransitionTimeMilliseconds); | 372 base::TimeDelta transition_time = |
| 343 if (delta >= transition_time) { | 373 base::TimeDelta::FromMilliseconds(kTransitionTimeMilliseconds); |
| 344 layers_[0]->Set(rings_[0]); | 374 if (delta >= transition_time) { |
| 375 focus_layers_[0]->Set(focus_rings_[0]); | |
| 376 return; | |
| 377 } | |
| 378 | |
| 379 double fraction = delta.InSecondsF() / transition_time.InSecondsF(); | |
| 380 | |
| 381 // Ease-in effect. | |
| 382 fraction = pow(fraction, 0.3); | |
| 383 | |
| 384 // Handle corner case where we're animating but we don't have previous | |
| 385 // rings. | |
| 386 if (previous_focus_rings_.empty()) | |
| 387 previous_focus_rings_ = focus_rings_; | |
| 388 | |
| 389 focus_layers_[0]->Set(AccessibilityFocusRing::Interpolate( | |
| 390 previous_focus_rings_[0], focus_rings_[0], fraction)); | |
| 391 } else { | |
| 392 ComputeOpacity(&focus_animation_info_, timestamp); | |
| 393 for (size_t i = 0; i < focus_layers_.size(); ++i) | |
| 394 focus_layers_[i]->SetOpacity(focus_animation_info_.opacity); | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 void AccessibilityFocusRingController::ComputeOpacity( | |
| 399 AccessibilityFocusRingController::LayerAnimationInfo* animation_info, | |
| 400 base::TimeTicks timestamp) { | |
| 401 // It's quite possible for the first 1 or 2 animation frames to be | |
| 402 // for a timestamp that's earlier than the time we received the | |
| 403 // mouse movement, so we just treat those as a delta of zero. | |
| 404 if (timestamp < animation_info->start_time) | |
| 405 timestamp = animation_info->start_time; | |
| 406 | |
| 407 base::TimeDelta start_delta = timestamp - animation_info->start_time; | |
| 408 base::TimeDelta change_delta = timestamp - animation_info->change_time; | |
| 409 base::TimeDelta fade_in_time = animation_info->fade_in_time; | |
| 410 base::TimeDelta fade_out_time = animation_info->fade_out_time; | |
| 411 | |
| 412 if (change_delta > fade_in_time + fade_out_time) { | |
| 413 animation_info->opacity = 0.0; | |
| 345 return; | 414 return; |
| 346 } | 415 } |
| 347 | 416 |
| 348 double fraction = delta.InSecondsF() / transition_time.InSecondsF(); | 417 float opacity; |
| 418 if (start_delta < fade_in_time) { | |
| 419 opacity = start_delta.InSecondsF() / fade_in_time.InSecondsF(); | |
| 420 if (opacity > 1.0) | |
| 421 opacity = 1.0; | |
| 422 } else { | |
| 423 opacity = 1.0 - (change_delta.InSecondsF() / | |
| 424 (fade_in_time + fade_out_time).InSecondsF()); | |
| 425 if (opacity < 0.0) | |
| 426 opacity = 0.0; | |
| 427 } | |
| 349 | 428 |
| 350 // Ease-in effect. | 429 animation_info->opacity = opacity; |
| 351 fraction = pow(fraction, 0.3); | |
| 352 | |
| 353 // Handle corner case where we're animating but we don't have previous | |
| 354 // rings. | |
| 355 if (previous_rings_.empty()) | |
| 356 previous_rings_ = rings_; | |
| 357 | |
| 358 layers_[0]->Set(AccessibilityFocusRing::Interpolate( | |
| 359 previous_rings_[0], rings_[0], fraction)); | |
| 360 } | 430 } |
| 361 | 431 |
| 362 void AccessibilityFocusRingController::AnimateCursorRing( | 432 void AccessibilityFocusRingController::AnimateCursorRing( |
| 363 base::TimeTicks timestamp) { | 433 base::TimeTicks timestamp) { |
| 364 CHECK(cursor_layer_); | 434 CHECK(cursor_layer_); |
| 365 | 435 |
| 366 // It's quite possible for the first 1 or 2 animation frames to be | 436 ComputeOpacity(&cursor_animation_info_, timestamp); |
| 367 // for a timestamp that's earlier than the time we received the | 437 if (cursor_animation_info_.opacity == 0.0) { |
| 368 // mouse movement, so we just treat those as a delta of zero. | |
| 369 if (timestamp < cursor_start_time_) | |
| 370 timestamp = cursor_start_time_; | |
| 371 | |
| 372 base::TimeDelta start_delta = timestamp - cursor_start_time_; | |
| 373 base::TimeDelta change_delta = timestamp - cursor_change_time_; | |
| 374 base::TimeDelta fade_in_time = | |
| 375 base::TimeDelta::FromMilliseconds(kCursorFadeInTimeMilliseconds); | |
| 376 base::TimeDelta fade_out_time = | |
| 377 base::TimeDelta::FromMilliseconds(kCursorFadeOutTimeMilliseconds); | |
| 378 | |
| 379 if (change_delta > fade_in_time + fade_out_time) { | |
| 380 cursor_opacity_ = 0.0; | |
| 381 cursor_layer_.reset(); | 438 cursor_layer_.reset(); |
| 382 return; | 439 return; |
| 383 } | 440 } |
| 441 cursor_layer_->SetOpacity(cursor_animation_info_.opacity); | |
| 442 } | |
| 384 | 443 |
| 385 if (start_delta < fade_in_time) { | 444 void AccessibilityFocusRingController::AnimateCaretRing( |
| 386 cursor_opacity_ = start_delta.InSecondsF() / fade_in_time.InSecondsF(); | 445 base::TimeTicks timestamp) { |
| 387 if (cursor_opacity_ > 1.0) | 446 CHECK(caret_layer_); |
| 388 cursor_opacity_ = 1.0; | 447 |
| 389 } else { | 448 ComputeOpacity(&caret_animation_info_, timestamp); |
| 390 cursor_opacity_ = 1.0 - (change_delta.InSecondsF() / | 449 if (caret_animation_info_.opacity == 0.0) { |
| 391 (fade_in_time + fade_out_time).InSecondsF()); | 450 caret_layer_.reset(); |
| 392 if (cursor_opacity_ < 0.0) | 451 return; |
| 393 cursor_opacity_ = 0.0; | |
| 394 } | 452 } |
| 395 cursor_layer_->SetOpacity(cursor_opacity_); | 453 caret_layer_->SetOpacity(caret_animation_info_.opacity); |
| 396 } | 454 } |
| 397 | 455 |
| 398 } // namespace chromeos | 456 } // namespace chromeos |
| OLD | NEW |