Commit e4a5a0a2 authored by Andrew Xu's avatar Andrew Xu Committed by Commit Bot

Polish hotseat animation during overview transition

When hotseat enters extended mode from home launcher mode, hotseat's
bounds undergo the following changes:
(1) The origin of the hotseat bounds moves to left.
(2) The hotseat bounds become wider.

Meanwhile, the hotseat background's origin is related to hotseat bounds.
Denote the background origin by |background_origin|; denote the hotseat
bounds by |bounds_rect|. Then  background_origin|.x is calculated
roughly by the equation below:
(1) |padding_inset| := (|bounds_rect|.width - space_taken_by_icons) / 2
(2) |background_origin|.x := padding_inset + |bounds_rect|.x

|bounds_rect|.x becomes smaller while |padding_inset| becomes bigger. It
is the reason why the hotseat bounds move back and forth.

In this CL, the custom layer element is created for the transition
between home launcher mode and extended mode. It ensures that the
hotseat background varies linearly in screen coordinates.

Bug: 1042911
Change-Id: Id91ce3ecf2bbee8509a3f794c6e943c6bf1d6ef3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2225590
Commit-Queue: Andrew Xu <andrewxu@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#776552}
parent 64be8de6
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "ui/aura/scoped_window_targeter.h" #include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window_targeter.h" #include "ui/aura/window_targeter.h"
#include "ui/compositor/animation_metrics_reporter.h" #include "ui/compositor/animation_metrics_reporter.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/color_analysis.h" #include "ui/gfx/color_analysis.h"
#include "ui/gfx/color_palette.h" #include "ui/gfx/color_palette.h"
...@@ -85,6 +86,133 @@ HotseatWidget::StateTransition CalculateHotseatStateTransition( ...@@ -85,6 +86,133 @@ HotseatWidget::StateTransition CalculateHotseatStateTransition(
return HotseatWidget::StateTransition::kOther; return HotseatWidget::StateTransition::kOther;
} }
// Animation implemented specifically for the transition between the home
// launcher state and the extended state.
class HomeAndExtendedTransitionAnimation : public ui::LayerAnimationElement {
public:
HomeAndExtendedTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
double target_opacity,
ui::Layer* hotseat_layer,
HotseatWidget* hotseat_widget)
: ui::LayerAnimationElement(
LayerAnimationElement::BOUNDS | LayerAnimationElement::OPACITY,
hotseat_layer->GetAnimator()->GetTransitionDuration()),
target_widget_bounds_(target_bounds_in_screen),
target_opacity_(target_opacity),
tween_type_(hotseat_layer->GetAnimator()->tween_type()),
hotseat_widget_(hotseat_widget) {}
~HomeAndExtendedTransitionAnimation() override = default;
HomeAndExtendedTransitionAnimation(
const HomeAndExtendedTransitionAnimation& rhs) = delete;
HomeAndExtendedTransitionAnimation& operator=(
const HomeAndExtendedTransitionAnimation& rhs) = delete;
private:
void OnStart(ui::LayerAnimationDelegate* delegate) override {
DCHECK(hotseat_widget_->GetShelfView()->shelf()->IsHorizontalAlignment());
ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
scrollable_shelf_view->set_is_padding_configured_externally(
/*is_padding_configured_externally=*/true);
// Save initial and target padding insets.
initial_padding_insets_ = scrollable_shelf_view->GetTotalEdgePadding();
target_padding_insets_ =
scrollable_shelf_view->CalculateTotalEdgePaddingInTargetBounds();
// Save initial opacity.
start_opacity_ = hotseat_widget_->GetNativeView()->layer()->opacity();
// Save initial hotseat background bounds.
initial_hotseat_background_in_screen_ =
hotseat_widget_->GetWindowBoundsInScreen();
initial_hotseat_background_in_screen_.Inset(initial_padding_insets_);
// Save target hotseat background bounds.
target_hotseat_background_in_screen_ = target_widget_bounds_;
target_hotseat_background_in_screen_.Inset(target_padding_insets_);
}
bool OnProgress(double current,
ui::LayerAnimationDelegate* delegate) override {
const double tweened = gfx::Tween::CalculateValue(tween_type_, current);
// Set scrollable shelf view's padding insets.
gfx::Insets insets_in_animation_progress;
insets_in_animation_progress.set_left(gfx::Tween::LinearIntValueBetween(
tweened, initial_padding_insets_.left(),
target_padding_insets_.left()));
insets_in_animation_progress.set_right(gfx::Tween::LinearIntValueBetween(
tweened, initial_padding_insets_.right(),
target_padding_insets_.right()));
ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
scrollable_shelf_view->SetTotalPaddingInsets(insets_in_animation_progress);
// Update hotseat widget opacity.
delegate->SetOpacityFromAnimation(
gfx::Tween::DoubleValueBetween(tweened, start_opacity_,
target_opacity_),
ui::PropertyChangeReason::FROM_ANIMATION);
// Calculate the hotseat widget's bounds.
const gfx::Rect hotseat_background_in_progress =
gfx::Tween::RectValueBetween(tweened,
initial_hotseat_background_in_screen_,
target_hotseat_background_in_screen_);
gfx::Rect widget_bounds_in_progress = hotseat_background_in_progress;
widget_bounds_in_progress.Inset(
-scrollable_shelf_view->GetTotalEdgePadding());
// Update hotseat widget bounds.
delegate->SetBoundsFromAnimation(widget_bounds_in_progress,
ui::PropertyChangeReason::FROM_ANIMATION);
// Do recovering when the animation ends.
if (current == 1.f) {
scrollable_shelf_view->set_is_padding_configured_externally(
/*is_padding_configured_externally=*/false);
}
return true;
}
void OnGetTarget(TargetValue* target) const override {}
void OnAbort(ui::LayerAnimationDelegate* delegate) override {
GetScrollableShelfView()->set_is_padding_configured_externally(
/*is_padding_configured_externally=*/false);
}
ScrollableShelfView* GetScrollableShelfView() {
return hotseat_widget_->scrollable_shelf_view();
}
// Scrollable shelf's initial padding insets.
gfx::Insets initial_padding_insets_;
// Scrollable shelf's target padding insets.
gfx::Insets target_padding_insets_;
// Hotseat background's initial bounds in screen.
gfx::Rect initial_hotseat_background_in_screen_;
// Hotseat background's target bounds in screen.
gfx::Rect target_hotseat_background_in_screen_;
// Hotseat widget's target bounds in screen.
gfx::Rect target_widget_bounds_;
// Hotseat widget's initial opacity.
double start_opacity_ = 0.f;
// Hotseat widget's target opacity.
double target_opacity_ = 0.f;
gfx::Tween::Type tween_type_ = gfx::Tween::LINEAR;
HotseatWidget* hotseat_widget_ = nullptr;
};
// Custom window targeter for the hotseat. Used so the hotseat only processes // Custom window targeter for the hotseat. Used so the hotseat only processes
// events that land on the visible portion of the hotseat, and only while the // events that land on the visible portion of the hotseat, and only while the
// hotseat is not animating. // hotseat is not animating.
...@@ -382,6 +510,11 @@ HotseatWidget::HotseatWidget() : delegate_view_(new DelegateView()) { ...@@ -382,6 +510,11 @@ HotseatWidget::HotseatWidget() : delegate_view_(new DelegateView()) {
} }
HotseatWidget::~HotseatWidget() { HotseatWidget::~HotseatWidget() {
ui::LayerAnimator* hotseat_layer_animator =
GetNativeView()->layer()->GetAnimator();
if (hotseat_layer_animator->is_animating())
hotseat_layer_animator->AbortAllAnimations();
ShelfConfig::Get()->RemoveObserver(this); ShelfConfig::Get()->RemoveObserver(this);
shelf_->shelf_widget()->hotseat_transition_animator()->RemoveObserver( shelf_->shelf_widget()->hotseat_transition_animator()->RemoveObserver(
delegate_view_); delegate_view_);
...@@ -781,7 +914,43 @@ void HotseatWidget::LayoutHotseatByAnimation(double target_opacity, ...@@ -781,7 +914,43 @@ void HotseatWidget::LayoutHotseatByAnimation(double target_opacity,
animation_setter.SetAnimationMetricsReporter( animation_setter.SetAnimationMetricsReporter(
shelf_->GetHotseatTransitionMetricsReporter(state_)); shelf_->GetHotseatTransitionMetricsReporter(state_));
hotseat_layer->SetOpacity(target_opacity); if (!state_transition_in_progress_.has_value()) {
// Hotseat animation is not triggered by the update in |state_|. So apply
// the normal bounds animation.
StartNormalBoundsAnimation(target_opacity, target_bounds);
return;
}
switch (*state_transition_in_progress_) {
case StateTransition::kHomeLauncherAndExtended:
// Start the hotseat animation specifically for the transition between
// the home launcher mode and the extended mode.
StartHomeLauncherExtendedTransitionAnimation(target_opacity,
target_bounds);
break;
case StateTransition::kHomeLauncherAndHidden:
case StateTransition::kHiddenAndExtended:
case StateTransition::kOther:
StartNormalBoundsAnimation(target_opacity, target_bounds);
}
}
void HotseatWidget::StartHomeLauncherExtendedTransitionAnimation(
double target_opacity,
const gfx::Rect& target_bounds) {
ui::Layer* hotseat_layer = GetNativeView()->layer();
auto animation_elements =
std::make_unique<HomeAndExtendedTransitionAnimation>(
target_bounds, target_opacity, hotseat_layer,
/*hotseat_widget=*/this);
auto* sequence =
new ui::LayerAnimationSequence(std::move(animation_elements));
hotseat_layer->GetAnimator()->StartAnimation(sequence);
}
void HotseatWidget::StartNormalBoundsAnimation(double target_opacity,
const gfx::Rect& target_bounds) {
GetNativeView()->layer()->SetOpacity(target_opacity);
SetBounds(target_bounds); SetBounds(target_bounds);
} }
......
...@@ -199,6 +199,16 @@ class ASH_EXPORT HotseatWidget : public ShelfComponent, ...@@ -199,6 +199,16 @@ class ASH_EXPORT HotseatWidget : public ShelfComponent,
void LayoutHotseatByAnimation(double target_opacity, void LayoutHotseatByAnimation(double target_opacity,
const gfx::Rect& target_bounds); const gfx::Rect& target_bounds);
// Animates the hotseat for the transition between the home launcher state
// and the extended state.
void StartHomeLauncherExtendedTransitionAnimation(
double target_opacity,
const gfx::Rect& target_bounds);
// Starts the default bounds/opacity animation.
void StartNormalBoundsAnimation(double target_opacity,
const gfx::Rect& target_bounds);
// The set of inputs that impact this widget's layout. The assumption is that // The set of inputs that impact this widget's layout. The assumption is that
// this widget needs a relayout if, and only if, one or more of these has // this widget needs a relayout if, and only if, one or more of these has
// changed. // changed.
......
...@@ -713,6 +713,22 @@ bool ScrollableShelfView::RequiresScrollingForItemSize( ...@@ -713,6 +713,22 @@ bool ScrollableShelfView::RequiresScrollingForItemSize(
return !CanFitAllAppsWithoutScrolling(target_size, icons_preferred_size); return !CanFitAllAppsWithoutScrolling(target_size, icons_preferred_size);
} }
gfx::Insets ScrollableShelfView::GetTotalEdgePadding() const {
return extra_padding_insets_ + base_padding_insets_;
}
gfx::Insets ScrollableShelfView::CalculateTotalEdgePaddingInTargetBounds()
const {
return CalculateExtraEdgePadding(/*use_target_bounds=*/true) +
base_padding_insets_;
}
void ScrollableShelfView::SetTotalPaddingInsets(
const gfx::Insets& padding_insets) {
extra_padding_insets_ = padding_insets - base_padding_insets_;
shelf_view_->LayoutIfAppIconsOffsetUpdates();
}
views::View* ScrollableShelfView::GetShelfContainerViewForTest() { views::View* ScrollableShelfView::GetShelfContainerViewForTest() {
return shelf_container_view_; return shelf_container_view_;
} }
...@@ -1431,10 +1447,6 @@ gfx::Insets ScrollableShelfView::CalculateExtraEdgePadding( ...@@ -1431,10 +1447,6 @@ gfx::Insets ScrollableShelfView::CalculateExtraEdgePadding(
return padding_insets; return padding_insets;
} }
gfx::Insets ScrollableShelfView::GetTotalEdgePadding() const {
return extra_padding_insets_ + base_padding_insets_;
}
int ScrollableShelfView::GetStatusWidgetSizeOnPrimaryAxis( int ScrollableShelfView::GetStatusWidgetSizeOnPrimaryAxis(
bool use_target_bounds) const { bool use_target_bounds) const {
const gfx::Size status_widget_size = const gfx::Size status_widget_size =
...@@ -2088,8 +2100,10 @@ int ScrollableShelfView::CalculateScrollDistanceAfterAdjustment( ...@@ -2088,8 +2100,10 @@ int ScrollableShelfView::CalculateScrollDistanceAfterAdjustment(
} }
void ScrollableShelfView::UpdateAvailableSpace() { void ScrollableShelfView::UpdateAvailableSpace() {
extra_padding_insets_ = if (!is_padding_configured_externally_) {
CalculateExtraEdgePadding(/*use_target_bounds=*/false); extra_padding_insets_ =
CalculateExtraEdgePadding(/*use_target_bounds=*/false);
}
base_padding_insets_ = CalculateBaseEdgePadding(); base_padding_insets_ = CalculateBaseEdgePadding();
available_space_ = GetLocalBounds(); available_space_ = GetLocalBounds();
......
...@@ -101,6 +101,16 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView, ...@@ -101,6 +101,16 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView,
bool RequiresScrollingForItemSize(const gfx::Size& target_size, bool RequiresScrollingForItemSize(const gfx::Size& target_size,
int button_size) const; int button_size) const;
// Returns the sum of |base_padding_insets_| and |extra_padding_insets_|.
gfx::Insets GetTotalEdgePadding() const;
// Calculates the sum of the base padding and the extra padding in view's
// target bounds.
gfx::Insets CalculateTotalEdgePaddingInTargetBounds() const;
// Sets padding insets.
void SetTotalPaddingInsets(const gfx::Insets& padding_insets);
views::View* GetShelfContainerViewForTest(); views::View* GetShelfContainerViewForTest();
bool ShouldAdjustForTest() const; bool ShouldAdjustForTest() const;
...@@ -141,6 +151,11 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView, ...@@ -141,6 +151,11 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView,
return extra_padding_insets_; return extra_padding_insets_;
} }
void set_is_padding_configured_externally(
bool is_padding_configured_externally) {
is_padding_configured_externally_ = is_padding_configured_externally;
}
// Size of the arrow button. // Size of the arrow button.
static int GetArrowButtonSize(); static int GetArrowButtonSize();
...@@ -284,16 +299,10 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView, ...@@ -284,16 +299,10 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView,
// scrollable shelf and other components (like status area widget). // scrollable shelf and other components (like status area widget).
gfx::Insets CalculateBaseEdgePadding() const; gfx::Insets CalculateBaseEdgePadding() const;
// Returns the extra padding inset which is influenced by the padding // Returns the extra padding insets based on the scrollable shelf view's
// strategy. There are three strategies: (1) display centering alignment (2) // target bounds or the current bounds, indicated by |use_target_bounds|.
// scrollable shelf centering alignment (3) overflow mode
// |use_target_bounds| indicates which view bounds are used for calculation:
// actual view bounds or target view bounds.
gfx::Insets CalculateExtraEdgePadding(bool use_target_bounds) const; gfx::Insets CalculateExtraEdgePadding(bool use_target_bounds) const;
// Returns the sum of the base padding and the extra padding.
gfx::Insets GetTotalEdgePadding() const;
int GetStatusWidgetSizeOnPrimaryAxis(bool use_target_bounds) const; int GetStatusWidgetSizeOnPrimaryAxis(bool use_target_bounds) const;
// Returns the local bounds depending on which view bounds are used: actual // Returns the local bounds depending on which view bounds are used: actual
...@@ -490,16 +499,25 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView, ...@@ -490,16 +499,25 @@ class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView,
ShelfView* shelf_view_ = nullptr; ShelfView* shelf_view_ = nullptr;
// Padding insets based on |base_padding_| and shelf alignment. // Defines the empty space between hotseat widget and other shelf components,
// such as status area widget. Its value is based on |base_padding_| which is
// a constant and shelf alignment.
gfx::Insets base_padding_insets_; gfx::Insets base_padding_insets_;
// Extra insets decided by the current padding strategy. // Defines the padding space inside the scrollable shelf. It is decided by the
// current padding strategy.
gfx::Insets extra_padding_insets_; gfx::Insets extra_padding_insets_;
// Minimum gap between scrollable shelf and other components (like status area // Minimum gap between scrollable shelf and other components (like status area
// widget) in DIPs. // widget) in DIPs.
const int base_padding_; const int base_padding_;
// Indicates whether |extra_padding_insets_| is configured externally.
// Usually |extra_padding_insets_| is calculated by ScrollableShelfView's
// member function. However, in some animations, |extra_padding_insets_|
// is set by animation progress to ensure the smooth bounds transition.
bool is_padding_configured_externally_ = false;
// Visible space of |shelf_container_view| in ScrollableShelfView's local // Visible space of |shelf_container_view| in ScrollableShelfView's local
// coordinates. Different from |available_space_|, |visible_space_| only // coordinates. Different from |available_space_|, |visible_space_| only
// contains app icons and is mirrored for horizontal shelf under RTL. // contains app icons and is mirrored for horizontal shelf under RTL.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment