| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/app_list/apps_grid_row_change_animator.h" |
| |
| #include <utility> |
| |
| #include "ash/app_list/views/apps_grid_view.h" |
| #include "base/auto_reset.h" |
| #include "base/i18n/rtl.h" |
| #include "base/time/time.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/geometry/transform_util.h" |
| |
| namespace ash { |
| |
| AppsGridRowChangeAnimator::AppsGridRowChangeAnimator( |
| AppsGridView* apps_grid_view) |
| : apps_grid_view_(apps_grid_view) {} |
| |
| AppsGridRowChangeAnimator::~AppsGridRowChangeAnimator() = default; |
| |
| void AppsGridRowChangeAnimator::AnimateBetweenRows( |
| AppListItemView* view, |
| const gfx::Rect& current, |
| const gfx::Rect& target, |
| views::AnimationSequenceBlock* animation_sequence) { |
| base::AutoReset<bool> auto_reset(&setting_up_animation_, true); |
| |
| // The direction used to calculate the offset for animating the item view into |
| // and the layer copy out of the grid. Reversed for RTL. |
| int dir = current.y() < target.y() ? 1 : -1; |
| if (base::i18n::IsRTL()) |
| dir *= -1; |
| |
| int offset = |
| apps_grid_view_->GetTotalTileSize(apps_grid_view_->GetSelectedPage()) |
| .width(); |
| |
| // Calculate where offscreen the layer copy will animate to. |
| gfx::Rect current_out = current; |
| current_out.Offset(dir * offset, 0); |
| |
| // The transform for moving the layer copy out and off screen. |
| gfx::Transform layer_copy_transform; |
| |
| // The bounds where `view` will begin animating to `target`. |
| gfx::RectF target_in; |
| |
| // The layer copy of the view which is animating between rows. |
| std::unique_ptr<ui::Layer> row_change_layer; |
| |
| if (row_change_layers_.count(view)) { |
| row_change_layer = std::move(row_change_layers_[view]); |
| row_change_layers_.erase(view); |
| |
| // Reverse existing row change animation, by swapping the positions of the |
| // mid-animation layer copy and `view`. Then animate each to the correct |
| // target. |
| |
| // Calculate 'target_in' so that 'view' can start its animation in the place |
| // of the layer copy. |
| target_in = row_change_layer->transform().MapRect( |
| gfx::RectF(row_change_layer->bounds())); |
| |
| // Calculate the current bounds of `view` including its layer transform. |
| gfx::RectF current_bounds_in_animation = |
| view->layer()->transform().MapRect(gfx::RectF(current)); |
| |
| // Set the bounds of the layer copy to the current bounds of'view'. |
| row_change_layer->SetBounds( |
| gfx::ToRoundedRect(current_bounds_in_animation)); |
| row_change_layer->SetTransform(gfx::Transform()); |
| |
| layer_copy_transform = gfx::TransformBetweenRects( |
| current_bounds_in_animation, gfx::RectF(current_out)); |
| |
| // Swap the opacity of the layer copy and the item view. |
| const float layer_copy_opacity = row_change_layer->opacity(); |
| row_change_layer->SetOpacity(view->layer()->opacity()); |
| view->layer()->SetOpacity(layer_copy_opacity); |
| } else { |
| // Create the row change animation for this view. `view` will animate from |
| // offscreen into the 'target' location, while a layer copy of 'view' will |
| // animate from the original `view` bounds to offscreen. |
| view->EnsureLayer(); |
| row_change_layer = view->RecreateLayer(); |
| |
| layer_copy_transform = gfx::TransformBetweenRects(gfx::RectF(current), |
| gfx::RectF(current_out)); |
| |
| view->layer()->SetOpacity(0.0f); |
| |
| // Calculate offscreen position to begin animating `view` from. |
| target_in = gfx::RectF(target); |
| const int target_in_direction = current.y() < target.y() ? -1 : 1; |
| target_in.Offset(target_in_direction * offset, 0); |
| target_in = |
| gfx::RectF(apps_grid_view_->GetMirroredRect(ToRoundedRect(target_in))); |
| } |
| |
| // Set the transform for the item view before animating it into the target |
| // grid position. |
| view->layer()->SetTransform(gfx::TransformBetweenRects( |
| gfx::RectF(apps_grid_view_->GetMirroredRect(target)), target_in)); |
| view->SetBoundsRect(target); |
| |
| // Fade out and animate out the copied layer. Fade in the real item view. |
| animation_sequence |
| ->SetTransform(row_change_layer.get(), layer_copy_transform, |
| gfx::Tween::ACCEL_40_DECEL_100_3) |
| .SetOpacity(row_change_layer.get(), 0.0f, |
| gfx::Tween::ACCEL_40_DECEL_100_3) |
| .SetOpacity(view->layer(), 1.0f, gfx::Tween::ACCEL_40_DECEL_100_3) |
| .SetTransform(view->layer(), gfx::Transform(), |
| gfx::Tween::ACCEL_40_DECEL_100_3); |
| |
| row_change_layers_[view] = std::move(row_change_layer); |
| } |
| |
| void AppsGridRowChangeAnimator::CancelAnimation(views::View* view) { |
| row_change_layers_.erase(view); |
| } |
| |
| bool AppsGridRowChangeAnimator::IsAnimating() const { |
| return !row_change_layers_.empty() || setting_up_animation_; |
| } |
| |
| } // namespace ash |