123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/capture_mode/user_nudge_controller.h"
- #include "ash/capture_mode/capture_mode_controller.h"
- #include "ash/capture_mode/capture_mode_session.h"
- #include "ash/capture_mode/capture_mode_util.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/style/dark_light_mode_controller_impl.h"
- #include "base/bind.h"
- #include "base/check.h"
- #include "base/time/time.h"
- #include "ui/aura/window.h"
- #include "ui/gfx/geometry/transform.h"
- #include "ui/gfx/geometry/transform_util.h"
- #include "ui/views/animation/animation_builder.h"
- #include "ui/views/animation/animation_sequence_block.h"
- namespace ash {
- namespace {
- constexpr float kBaseRingOpacity = 0.21f;
- constexpr float kRippleRingOpacity = 0.5f;
- constexpr float kBaseRingScaleUpFactor = 1.1f;
- constexpr float kRippleRingScaleUpFactor = 3.0f;
- constexpr float kHighlightedViewScaleUpFactor = 1.2f;
- constexpr base::TimeDelta kVisibilityChangeDuration = base::Milliseconds(200);
- constexpr base::TimeDelta kScaleUpDuration = base::Milliseconds(500);
- constexpr base::TimeDelta kScaleDownDelay = base::Milliseconds(650);
- constexpr base::TimeDelta kScaleDownOffset = kScaleUpDuration + kScaleDownDelay;
- constexpr base::TimeDelta kScaleDownDuration = base::Milliseconds(1350);
- constexpr base::TimeDelta kRippleAnimationDuration = base::Milliseconds(2000);
- constexpr base::TimeDelta kDelayToShowNudge = base::Milliseconds(1000);
- constexpr base::TimeDelta kDelayToRepeatNudge = base::Milliseconds(2500);
- // Returns the given `view`'s layer bounds in root coordinates ignoring any
- // transforms it or any of its ancestors may have.
- gfx::Rect GetViewLayerBoundsInRootNoTransform(views::View* view) {
- auto* layer = view->layer();
- DCHECK(layer);
- gfx::Point origin;
- while (layer) {
- const auto layer_origin = layer->bounds().origin();
- origin.Offset(layer_origin.x(), layer_origin.y());
- layer = layer->parent();
- }
- return gfx::Rect(origin, view->layer()->size());
- }
- } // namespace
- UserNudgeController::UserNudgeController(CaptureModeSession* session,
- views::View* view_to_be_highlighted)
- : capture_session_(session),
- view_to_be_highlighted_(view_to_be_highlighted) {
- view_to_be_highlighted_->SetPaintToLayer();
- view_to_be_highlighted_->layer()->SetFillsBoundsOpaquely(false);
- // Rings are created initially with 0 opacity. Calling SetVisible() will
- // animate them towards their correct state.
- const SkColor ring_color =
- DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() ? SK_ColorWHITE
- : SK_ColorBLACK;
- base_ring_.SetColor(ring_color);
- base_ring_.SetFillsBoundsOpaquely(false);
- base_ring_.SetOpacity(0);
- ripple_ring_.SetColor(ring_color);
- ripple_ring_.SetFillsBoundsOpaquely(false);
- ripple_ring_.SetOpacity(0);
- Reposition();
- }
- UserNudgeController::~UserNudgeController() {
- if (should_dismiss_nudge_forever_)
- CaptureModeController::Get()->DisableUserNudgeForever();
- capture_session_->capture_toast_controller()->MaybeDismissCaptureToast(
- CaptureToastType::kUserNudge,
- /*animate=*/false);
- }
- void UserNudgeController::Reposition() {
- auto* parent_window = GetParentWindow();
- auto* parent_layer = parent_window->layer();
- if (parent_layer != base_ring_.parent()) {
- parent_layer->Add(&base_ring_);
- parent_layer->Add(&ripple_ring_);
- }
- const auto view_bounds_in_root =
- GetViewLayerBoundsInRootNoTransform(view_to_be_highlighted_);
- base_ring_.SetBounds(view_bounds_in_root);
- base_ring_.SetRoundedCornerRadius(
- gfx::RoundedCornersF(view_bounds_in_root.width() / 2.f));
- ripple_ring_.SetBounds(view_bounds_in_root);
- ripple_ring_.SetRoundedCornerRadius(
- gfx::RoundedCornersF(view_bounds_in_root.width() / 2.f));
- }
- void UserNudgeController::SetVisible(bool visible) {
- if (is_visible_ == visible)
- return;
- is_visible_ = visible;
- auto* capture_toast_controller = capture_session_->capture_toast_controller();
- views::AnimationBuilder builder;
- builder.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- if (!is_visible_) {
- // We should no longer repeat the nudge animation.
- timer_.Stop();
- // We should also stop any ongoing animation on the `base_ring_` in
- // particular since we observe this animation ending to schedule a repeat.
- // See OnBaseRingAnimationEnded().
- base_ring_.GetAnimator()->AbortAllAnimations();
- // Animate all animation layers and the toast widget to 0 opacity.
- builder.Once()
- .SetDuration(kVisibilityChangeDuration)
- .SetOpacity(&base_ring_, 0, gfx::Tween::FAST_OUT_SLOW_IN)
- .SetOpacity(&ripple_ring_, 0, gfx::Tween::FAST_OUT_SLOW_IN);
- capture_toast_controller->MaybeDismissCaptureToast(
- CaptureToastType::kUserNudge);
- return;
- }
- // Animate the `base_ring_` and the `toast_widget_` to their default shown
- // opacity. Note that we don't need to show the `ripple_ring_` since it only
- // shows as the nudge animation is being performed.
- // Once those elements reach their default shown opacity, we perform the nudge
- // animation.
- builder
- .OnEnded(base::BindOnce(&UserNudgeController::PerformNudgeAnimations,
- weak_ptr_factory_.GetWeakPtr()))
- .Once()
- .SetDuration(kDelayToShowNudge)
- .SetOpacity(&base_ring_, kBaseRingOpacity, gfx::Tween::FAST_OUT_SLOW_IN);
- capture_toast_controller->ShowCaptureToast(CaptureToastType::kUserNudge);
- }
- void UserNudgeController::PerformNudgeAnimations() {
- PerformBaseRingAnimation();
- PerformRippleRingAnimation();
- PerformViewScaleAnimation();
- }
- void UserNudgeController::PerformBaseRingAnimation() {
- // The `base_ring_` should scale up around the center of the
- // `view_to_be_highlighted_` to grab the user's attention, and then scales
- // back down to its original size.
- const gfx::Transform scale_up_transform =
- capture_mode_util::GetScaleTransformAboutCenter(&base_ring_,
- kBaseRingScaleUpFactor);
- views::AnimationBuilder()
- .SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
- .OnEnded(base::BindOnce(&UserNudgeController::OnBaseRingAnimationEnded,
- base::Unretained(this)))
- .Once()
- .SetDuration(kScaleUpDuration)
- .SetTransform(&base_ring_, scale_up_transform,
- gfx::Tween::ACCEL_40_DECEL_20)
- .Offset(kScaleDownOffset)
- .SetDuration(kScaleDownDuration)
- .SetTransform(&base_ring_, gfx::Transform(),
- gfx::Tween::FAST_OUT_SLOW_IN_3);
- }
- void UserNudgeController::PerformRippleRingAnimation() {
- // The ripple scales up to 3x the size of the `view_to_be_highlighted_` and
- // around its center while fading out.
- ripple_ring_.SetOpacity(kRippleRingOpacity);
- ripple_ring_.SetTransform(gfx::Transform());
- const gfx::Transform scale_up_transform =
- capture_mode_util::GetScaleTransformAboutCenter(&ripple_ring_,
- kRippleRingScaleUpFactor);
- views::AnimationBuilder()
- .SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
- .Once()
- .SetDuration(kRippleAnimationDuration)
- .SetOpacity(&ripple_ring_, 0, gfx::Tween::ACCEL_0_80_DECEL_80)
- .SetTransform(&ripple_ring_, scale_up_transform,
- gfx::Tween::ACCEL_0_40_DECEL_100);
- }
- void UserNudgeController::PerformViewScaleAnimation() {
- // The `view_to_be_highlighted_` scales up and down around its own center in
- // a similar fashion to that of the `base_ring_`.
- auto* view_layer = view_to_be_highlighted_->layer();
- const gfx::Transform scale_up_transform =
- capture_mode_util::GetScaleTransformAboutCenter(
- view_layer, kHighlightedViewScaleUpFactor);
- views::AnimationBuilder()
- .SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
- .Once()
- .SetDuration(kScaleUpDuration)
- .SetTransform(view_layer, scale_up_transform,
- gfx::Tween::ACCEL_40_DECEL_20)
- .Offset(kScaleDownOffset)
- .SetDuration(kScaleDownDuration)
- .SetTransform(view_layer, gfx::Transform(),
- gfx::Tween::FAST_OUT_SLOW_IN_3);
- }
- void UserNudgeController::OnBaseRingAnimationEnded() {
- timer_.Start(FROM_HERE, kDelayToRepeatNudge,
- base::BindOnce(&UserNudgeController::PerformNudgeAnimations,
- weak_ptr_factory_.GetWeakPtr()));
- }
- aura::Window* UserNudgeController::GetParentWindow() const {
- auto* root_window =
- view_to_be_highlighted_->GetWidget()->GetNativeWindow()->GetRootWindow();
- DCHECK(root_window);
- return root_window->GetChildById(kShellWindowId_OverlayContainer);
- }
- } // namespace ash
|