diff options
author | jonross@chromium.org <jonross@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-01 14:23:18 +0000 |
---|---|---|
committer | jonross@chromium.org <jonross@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-01 14:23:18 +0000 |
commit | 8d1ef7fddec8a8886a20895c369cdcb4a011a693 (patch) | |
tree | 852e89f23056a42b10c4ea71928efebc1324798d /ash | |
parent | 6e7abcb1aea81a9b7d2d9f021039c6a8641c0e4b (diff) | |
download | chromium_src-8d1ef7fddec8a8886a20895c369cdcb4a011a693.zip chromium_src-8d1ef7fddec8a8886a20895c369cdcb4a011a693.tar.gz chromium_src-8d1ef7fddec8a8886a20895c369cdcb4a011a693.tar.bz2 |
Animate Window Controls for Maximize Mode
Reimplement the animation of window controls when maximize mode is toggled.
Instead of layer animations SlideAnimation is used. This allows them to render in the header area when Immersive mode is active.
TEST=FrameCaptionButtonContainerViewTest.TestUpdateSizeButtonVisibilityAnimation
BUG=363717
Review URL: https://codereview.chromium.org/397013002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286987 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
7 files changed, 270 insertions, 9 deletions
diff --git a/ash/frame/caption_buttons/frame_caption_button.cc b/ash/frame/caption_buttons/frame_caption_button.cc index c8d71c3..2eb30a3 100644 --- a/ash/frame/caption_buttons/frame_caption_button.cc +++ b/ash/frame/caption_buttons/frame_caption_button.cc @@ -30,6 +30,7 @@ FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener, : CustomButton(listener), icon_(icon), paint_as_active_(false), + alpha_(255), icon_image_id_(-1), inactive_icon_image_id_(-1), hovered_background_image_id_(-1), @@ -94,6 +95,13 @@ bool FrameCaptionButton::IsAnimatingImageSwap() const { return swap_images_animation_->is_animating(); } +void FrameCaptionButton::SetAlpha(int alpha) { + if (alpha_ != alpha) { + alpha_ = alpha; + SchedulePaint(); + } +} + gfx::Size FrameCaptionButton::GetPreferredSize() const { return hovered_background_image_.isNull() ? gfx::Size() : hovered_background_image_.size(); @@ -130,8 +138,11 @@ void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) { paint.setXfermodeMode(SkXfermode::kPlus_Mode); icon_canvas.DrawImageInt(crossfade_icon_image_, 0, 0, paint); - PaintCentered(canvas, gfx::ImageSkia(icon_canvas.ExtractImageRep()), 255); + PaintCentered(canvas, gfx::ImageSkia(icon_canvas.ExtractImageRep()), + alpha_); } else { + if (!swap_images_animation_->is_animating()) + icon_alpha = alpha_; PaintCentered(canvas, icon_image, icon_alpha); } } diff --git a/ash/frame/caption_buttons/frame_caption_button.h b/ash/frame/caption_buttons/frame_caption_button.h index 6fd9831..a348e30 100644 --- a/ash/frame/caption_buttons/frame_caption_button.h +++ b/ash/frame/caption_buttons/frame_caption_button.h @@ -46,6 +46,9 @@ class ASH_EXPORT FrameCaptionButton : public views::CustomButton { // SetImages(). bool IsAnimatingImageSwap() const; + // Sets the alpha to use for painting. Used to animate visibility changes. + void SetAlpha(int alpha); + // views::View overrides: virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; @@ -78,6 +81,9 @@ class ASH_EXPORT FrameCaptionButton : public views::CustomButton { // Whether the button should be painted as active. bool paint_as_active_; + // Current alpha to use for painting. + int alpha_; + // The images and image ids used to paint the button. int icon_image_id_; int inactive_icon_image_id_; diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.cc b/ash/frame/caption_buttons/frame_caption_button_container_view.cc index 5cb0529..ce3cd4d 100644 --- a/ash/frame/caption_buttons/frame_caption_button_container_view.cc +++ b/ash/frame/caption_buttons/frame_caption_button_container_view.cc @@ -17,6 +17,8 @@ #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/animation/tween.h" #include "ui/gfx/canvas.h" #include "ui/gfx/insets.h" #include "ui/gfx/point.h" @@ -27,6 +29,63 @@ namespace ash { namespace { +// Duration of the animation of the position of |minimize_button_|. +const int kPositionAnimationDurationMs = 500; + +// Duration of the animation of the alpha of |size_button_|. +const int kAlphaAnimationDurationMs = 250; + +// Delay during |maximize_mode_animation_| hide to wait before beginning to +// animate the position of |minimize_button_|. +const int kHidePositionDelayMs = 100; + +// Duration of |maximize_mode_animation_| hiding. +// Hiding size button 250 +// |------------------------| +// Delay 100 Slide minimize button 500 +// |---------|-------------------------------------------------| +const int kHideAnimationDurationMs = + kHidePositionDelayMs + kPositionAnimationDurationMs; + +// Delay during |maximize_mode_animation_| show to wait before beginning to +// animate the alpha of |size_button_|. +const int kShowAnimationAlphaDelayMs = 100; + +// Duration of |maximize_mode_animation_| showing. +// Slide minimize button 500 +// |-------------------------------------------------| +// Delay 100 Show size button 250 +// |---------|-----------------------| +const int kShowAnimationDurationMs = kPositionAnimationDurationMs; + +// Value of |maximize_mode_animation_| showing to begin animating alpha of +// |size_button_|. +float SizeButtonShowStartValue() { + return static_cast<float>(kShowAnimationAlphaDelayMs) + / kShowAnimationDurationMs; +} + +// Amount of |maximize_mode_animation_| showing to animate the alpha of +// |size_button_|. +float SizeButtonShowDuration() { + return static_cast<float>(kAlphaAnimationDurationMs) + / kShowAnimationDurationMs; +} + +// Amount of |maximize_mode_animation_| hiding to animate the alpha of +// |size_button_|. +float SizeButtonHideDuration() { + return static_cast<float>(kAlphaAnimationDurationMs) + / kHideAnimationDurationMs; +} + +// Value of |maximize_mode_animation_| hiding to begin animating the position of +// |minimize_button_|. +float HidePositionStartValue() { + return 1.0f - static_cast<float>(kHidePositionDelayMs) + / kHideAnimationDurationMs; +} + // Converts |point| from |src| to |dst| and hittests against |dst|. bool ConvertPointToViewAndHitTest(const views::View* src, const views::View* dst, @@ -36,6 +95,13 @@ bool ConvertPointToViewAndHitTest(const views::View* src, return dst->HitTestPoint(converted); } +// Bounds animation values to the range 0.0 - 1.0. Allows for mapping of offset +// animations to the expected range so that gfx::Tween::CalculateValue() can be +// used. +double CapAnimationValue(double value) { + return std::min(1.0, std::max(0.0, value)); +} + } // namespace // static @@ -49,6 +115,14 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( minimize_button_(NULL), size_button_(NULL), close_button_(NULL) { + bool size_button_visibility = ShouldSizeButtonBeVisible(); + maximize_mode_animation_.reset(new gfx::SlideAnimation(this)); + maximize_mode_animation_->SetTweenType(gfx::Tween::LINEAR); + + // Ensure animation tracks visibility of size button. + if (size_button_visibility) + maximize_mode_animation_->Reset(1.0f); + // Insert the buttons left to right. minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); minimize_button_->SetAccessibleName( @@ -59,7 +133,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( size_button_ = new FrameSizeButton(this, frame, this); size_button_->SetAccessibleName( l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); - UpdateSizeButtonVisibility(); + size_button_->SetVisible(size_button_visibility); AddChildView(size_button_); close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); @@ -71,6 +145,10 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() { } +void FrameCaptionButtonContainerView::TestApi::EndAnimations() { + container_view_->maximize_mode_animation_->End(); +} + void FrameCaptionButtonContainerView::SetButtonImages( CaptionButtonIcon icon, int icon_image_id, @@ -122,10 +200,15 @@ int FrameCaptionButtonContainerView::NonClientHitTest( } void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() { - size_button_->SetVisible( - !Shell::GetInstance()->maximize_mode_controller()-> - IsMaximizeModeWindowManagerEnabled() && - frame_->widget_delegate()->CanMaximize()); + bool visible = ShouldSizeButtonBeVisible(); + if (visible) { + size_button_->SetVisible(true); + maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs); + maximize_mode_animation_->Show(); + } else { + maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs); + maximize_mode_animation_->Hide(); + } } gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const { @@ -149,12 +232,63 @@ void FrameCaptionButtonContainerView::Layout() { child->SetBounds(x, 0, size.width(), size.height()); x += size.width(); } + if (maximize_mode_animation_->is_animating()) { + AnimationProgressed(maximize_mode_animation_.get()); + } } const char* FrameCaptionButtonContainerView::GetClassName() const { return kViewClassName; } +void FrameCaptionButtonContainerView::AnimationEnded( + const gfx::Animation* animation) { + // Ensure that position is calculated at least once. + AnimationProgressed(animation); + + double current_value = maximize_mode_animation_->GetCurrentValue(); + if (current_value == 0.0) { + size_button_->SetVisible(false); + PreferredSizeChanged(); + } +} + +void FrameCaptionButtonContainerView::AnimationProgressed( + const gfx::Animation* animation) { + double current_value = animation->GetCurrentValue(); + int size_alpha = 0; + int minimize_x = 0; + if (maximize_mode_animation_->IsShowing()) { + double scaled_value = CapAnimationValue( + (current_value - SizeButtonShowStartValue()) + / SizeButtonShowDuration()); + double tweened_value_alpha = + gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,scaled_value); + size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255); + + double tweened_value_slide = + gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value); + minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide, + size_button_->x(), 0); + } else { + double scaled_value_alpha = CapAnimationValue( + (1.0f - current_value) / SizeButtonHideDuration()); + double tweened_value_alpha = + gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha); + size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0); + + double scaled_value_position = CapAnimationValue( + (HidePositionStartValue() - current_value) + / HidePositionStartValue()); + double tweened_value_position = + gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position); + minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0, + size_button_->x()); + } + size_button_->SetAlpha(size_alpha); + minimize_button_->SetX(minimize_x); +} + void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, CaptionButtonIcon icon, Animate animate) { @@ -180,6 +314,12 @@ void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, } } +bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const { + return !Shell::GetInstance()->maximize_mode_controller()-> + IsMaximizeModeWindowManagerEnabled() && + frame_->widget_delegate()->CanMaximize(); +} + void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, const ui::Event& event) { // When shift-clicking, slow down animations for visual debugging. diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.h b/ash/frame/caption_buttons/frame_caption_button_container_view.h index 579d5cd..658df05 100644 --- a/ash/frame/caption_buttons/frame_caption_button_container_view.h +++ b/ash/frame/caption_buttons/frame_caption_button_container_view.h @@ -9,9 +9,14 @@ #include "ash/ash_export.h" #include "ash/frame/caption_buttons/frame_size_button_delegate.h" +#include "ui/gfx/animation/animation_delegate.h" #include "ui/views/controls/button/button.h" #include "ui/views/view.h" +namespace gfx { +class SlideAnimation; +} + namespace views { class Widget; } @@ -23,7 +28,8 @@ namespace ash { class ASH_EXPORT FrameCaptionButtonContainerView : public views::View, public views::ButtonListener, - public FrameSizeButtonDelegate { + public FrameSizeButtonDelegate, + public gfx::AnimationDelegate { public: static const char kViewClassName[]; @@ -42,12 +48,14 @@ class ASH_EXPORT FrameCaptionButtonContainerView virtual ~FrameCaptionButtonContainerView(); // For testing. - class TestApi { + class ASH_EXPORT TestApi { public: explicit TestApi(FrameCaptionButtonContainerView* container_view) : container_view_(container_view) { } + void EndAnimations(); + FrameCaptionButton* minimize_button() const { return container_view_->minimize_button_; } @@ -97,6 +105,10 @@ class ASH_EXPORT FrameCaptionButtonContainerView virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; + // Overridden from gfx::AnimationDelegate: + virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE; + virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; + private: friend class FrameCaptionButtonContainerViewTest; @@ -122,6 +134,10 @@ class ASH_EXPORT FrameCaptionButtonContainerView CaptionButtonIcon icon, Animate animate); + // Returns true if maximize mode is not enabled, and |frame_| widget delegate + // can be maximized. + bool ShouldSizeButtonBeVisible() const; + // views::ButtonListener: virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; @@ -151,6 +167,10 @@ class ASH_EXPORT FrameCaptionButtonContainerView // CaptionButtonIcon. std::map<CaptionButtonIcon, ButtonIconIds> button_icon_id_map_; + // Animation that affects the position of |minimize_button_| and the + // visibility of |size_button_|. + scoped_ptr<gfx::SlideAnimation> maximize_mode_animation_; + DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView); }; diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc index acd72c8..a487005 100644 --- a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc +++ b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc @@ -5,8 +5,11 @@ #include "ash/frame/caption_buttons/frame_caption_button_container_view.h" #include "ash/frame/caption_buttons/frame_caption_button.h" +#include "ash/shell.h" #include "ash/test/ash_test_base.h" +#include "ash/wm/maximize_mode/maximize_mode_controller.h" #include "grit/ash_resources.h" +#include "ui/gfx/geometry/rect.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -16,7 +19,7 @@ namespace { class TestWidgetDelegate : public views::WidgetDelegateView { public: - TestWidgetDelegate(bool can_maximize) : can_maximize_(can_maximize) { + explicit TestWidgetDelegate(bool can_maximize) : can_maximize_(can_maximize) { } virtual ~TestWidgetDelegate() { } @@ -145,4 +148,65 @@ TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) { &container3, *t3.close_button(), *t3.close_button())); } +// Tests that the layout animations trigered by button visibility result in the +// correct placement of the buttons. +TEST_F(FrameCaptionButtonContainerViewTest, + TestUpdateSizeButtonVisibilityAnimation) { + scoped_ptr<views::Widget> widget_can_maximize( + CreateTestWidget(MAXIMIZE_ALLOWED)); + FrameCaptionButtonContainerView container(widget_can_maximize.get(), + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + SetMockImages(&container); + container.SetBoundsRect(gfx::Rect(container.GetPreferredSize())); + container.Layout(); + + FrameCaptionButtonContainerView::TestApi test(&container); + gfx::Rect initial_minimize_button_bounds = test.minimize_button()->bounds(); + gfx::Rect initial_size_button_bounds = test.size_button()->bounds(); + gfx::Rect initial_close_button_bounds = test.close_button()->bounds(); + gfx::Rect initial_container_bounds = container.bounds(); + + ASSERT_EQ(initial_size_button_bounds.x(), + initial_minimize_button_bounds.right()); + ASSERT_EQ(initial_close_button_bounds.x(), + initial_size_button_bounds.right()); + + // Hidden size button should result in minimize button animating to the + // right. The size button should not be visible, but should not have moved. + Shell::GetInstance()->maximize_mode_controller()-> + EnableMaximizeModeWindowManager(true); + container.UpdateSizeButtonVisibility(); + test.EndAnimations(); + // Parent needs to layout in response to size change. + container.Layout(); + + EXPECT_TRUE(test.minimize_button()->visible()); + EXPECT_FALSE(test.size_button()->visible()); + EXPECT_TRUE(test.close_button()->visible()); + gfx::Rect minimize_button_bounds = test.minimize_button()->bounds(); + gfx::Rect close_button_bounds = test.close_button()->bounds(); + EXPECT_EQ(close_button_bounds.x(), minimize_button_bounds.right()); + EXPECT_EQ(initial_size_button_bounds, test.size_button()->bounds()); + EXPECT_EQ(initial_close_button_bounds.size(), close_button_bounds.size()); + EXPECT_LT(container.GetPreferredSize().width(), + initial_container_bounds.width()); + + // Revealing the size button should cause the minimize button to return to its + // original position. + Shell::GetInstance()->maximize_mode_controller()-> + EnableMaximizeModeWindowManager(false); + container.UpdateSizeButtonVisibility(); + // Calling code needs to layout in response to size change. + container.Layout(); + test.EndAnimations(); + EXPECT_TRUE(test.minimize_button()->visible()); + EXPECT_TRUE(test.size_button()->visible()); + EXPECT_TRUE(test.close_button()->visible()); + EXPECT_EQ(initial_minimize_button_bounds, test.minimize_button()->bounds()); + EXPECT_EQ(initial_size_button_bounds, test.size_button()->bounds()); + EXPECT_EQ(initial_close_button_bounds, test.close_button()->bounds()); + EXPECT_EQ(container.GetPreferredSize().width(), + initial_container_bounds.width()); +} + } // namespace ash diff --git a/ash/frame/custom_frame_view_ash.cc b/ash/frame/custom_frame_view_ash.cc index 60e1a02..eb20914 100644 --- a/ash/frame/custom_frame_view_ash.cc +++ b/ash/frame/custom_frame_view_ash.cc @@ -154,6 +154,7 @@ class CustomFrameViewAsh::HeaderView // views::View: virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE; // ShellObserver: virtual void OnMaximizeModeStarted() OVERRIDE; @@ -288,6 +289,17 @@ void CustomFrameViewAsh::HeaderView::OnPaint(gfx::Canvas* canvas) { header_painter_->PaintHeader(canvas, header_mode); } +void CustomFrameViewAsh::HeaderView:: + ChildPreferredSizeChanged(views::View* child) { + // FrameCaptionButtonContainerView animates the visibility changes in + // UpdateSizeButtonVisibility(false). Due to this a new size is not available + // until the completion of the animation. Layout in response to the preferred + // size changes. + if (child != caption_button_container_) + return; + parent()->Layout(); +} + /////////////////////////////////////////////////////////////////////////////// // CustomFrameViewAsh::HeaderView, ShellObserver overrides: diff --git a/ash/frame/custom_frame_view_ash_unittest.cc b/ash/frame/custom_frame_view_ash_unittest.cc index 5e20247..3bcf912 100644 --- a/ash/frame/custom_frame_view_ash_unittest.cc +++ b/ash/frame/custom_frame_view_ash_unittest.cc @@ -81,6 +81,12 @@ class TestWidgetConstraintsDelegate : public TestWidgetDelegate { bounds(); } + void EndFrameCaptionButtonContainerViewAnimations() { + FrameCaptionButtonContainerView::TestApi test(custom_frame_view()-> + GetFrameCaptionButtonContainerViewForTest()); + test.EndAnimations(); + } + int GetTitleBarHeight() const { return custom_frame_view()->NonClientTopBorderHeight(); } @@ -212,11 +218,13 @@ TEST_F(CustomFrameViewAshTest, HeaderViewNotifiedOfChildSizeChange) { GetFrameCaptionButtonContainerViewBounds(); Shell::GetInstance()->maximize_mode_controller()-> EnableMaximizeModeWindowManager(true); + delegate->EndFrameCaptionButtonContainerViewAnimations(); const gfx::Rect maximize_mode_bounds = delegate-> GetFrameCaptionButtonContainerViewBounds(); EXPECT_GT(initial.width(), maximize_mode_bounds.width()); Shell::GetInstance()->maximize_mode_controller()-> EnableMaximizeModeWindowManager(false); + delegate->EndFrameCaptionButtonContainerViewAnimations(); const gfx::Rect after_restore = delegate-> GetFrameCaptionButtonContainerViewBounds(); EXPECT_EQ(initial, after_restore); |