summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authorjonross@chromium.org <jonross@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-02 23:43:25 +0000
committerjonross@chromium.org <jonross@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-02 23:43:25 +0000
commit2ff309aa4dce18acce062cf5fc821f10eaa42df4 (patch)
tree28b642fd57a882097871c28f006bffaed4011428 /ash
parent700aac6b15a3433a440fbec046174eda8edee921 (diff)
downloadchromium_src-2ff309aa4dce18acce062cf5fc821f10eaa42df4.zip
chromium_src-2ff309aa4dce18acce062cf5fc821f10eaa42df4.tar.gz
chromium_src-2ff309aa4dce18acce062cf5fc821f10eaa42df4.tar.bz2
Animate window control changes in TouchView
Add animation for the hiding and showing of the resize button. Add an animation for sliding the minimize button into the position of the resize button. Delay changing the size of FrameCaptionButtonContainerView to account for conflicting layout changes caused by the transition to TouchView. TEST=FrameCaptionButtonContainerViewTest TEST=CustomFrameViewAshTest TES=BrowserNonClientFrameViewAshTest BUG=363717 Review URL: https://codereview.chromium.org/271913002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274371 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r--ash/frame/caption_buttons/frame_caption_button_container_view.cc134
-rw-r--r--ash/frame/caption_buttons/frame_caption_button_container_view.h11
-rw-r--r--ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc57
-rw-r--r--ash/frame/custom_frame_view_ash.cc15
-rw-r--r--ash/frame/default_header_painter.cc4
5 files changed, 208 insertions, 13 deletions
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 12bbb71..2ed1048 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -5,6 +5,7 @@
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include <cmath>
+#include <map>
#include "ash/ash_switches.h"
#include "ash/frame/caption_buttons/frame_caption_button.h"
@@ -14,7 +15,10 @@
#include "grit/ui_strings.h" // Accessibility names
#include "ui/base/hit_test.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/animation/tween.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/insets.h"
#include "ui/gfx/point.h"
@@ -25,6 +29,14 @@ namespace ash {
namespace {
+// Visual design parameters for animating the transition to maximize mode.
+// When the size button hides we delay sliding the minimize button into its
+// location. Also used to delay showing the size button so that the minimize
+// button slides out of that position.
+const int kAnimationDelayMs = 100;
+const int kMinimizeSlideDurationMs = 500;
+const int kSizeFadeDurationMs = 250;
+
// Converts |point| from |src| to |dst| and hittests against |dst|.
bool ConvertPointToViewAndHitTest(const views::View* src,
const views::View* dst,
@@ -47,22 +59,31 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
minimize_button_(NULL),
size_button_(NULL),
close_button_(NULL) {
+ SetPaintToLayer(true);
+ SetFillsBoundsOpaquely(false);
+
// Insert the buttons left to right.
minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
minimize_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED);
+ minimize_button_->SetPaintToLayer(true);
+ minimize_button_->SetFillsBoundsOpaquely(false);
AddChildView(minimize_button_);
size_button_ = new FrameSizeButton(this, frame, this);
size_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
+ size_button_->SetPaintToLayer(true);
+ size_button_->SetFillsBoundsOpaquely(false);
UpdateSizeButtonVisibility(false);
AddChildView(size_button_);
close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
close_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
+ close_button_->SetPaintToLayer(true);
+ close_button_->SetFillsBoundsOpaquely(false);
AddChildView(close_button_);
}
@@ -127,8 +148,46 @@ void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility(
// time when ShellObserver::OnMaximizeModeStarted is called. This prevents
// this method from performing that check, and instead relies on the calling
// code to tell it to force being hidden.
- size_button_->SetVisible(
- !force_hidden && frame_->widget_delegate()->CanMaximize());
+
+ bool visible = !force_hidden && frame_->widget_delegate()->CanMaximize();
+
+ // Turning visibility off prevents animations from rendering. Setting the
+ // size button visibility to false will occur after the animation.
+ if (visible) {
+ size_button_->SetVisible(true);
+ // Because we delay calling View::SetVisible(false) until the end of the
+ // animation, if SetVisible(true) is called mid-animation, the View still
+ // believes it is visible and will not update the target layer visibility.
+ size_button_->layer()->SetVisible(true);
+ }
+
+ ui::ScopedLayerAnimationSettings settings(
+ size_button_->layer()->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kSizeFadeDurationMs));
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+
+ if (visible) {
+ settings.SetTweenType(gfx::Tween::EASE_OUT);
+ // Delay fade in so that the minimize button has begun its sliding
+ // animation.
+ size_button_->layer()->GetAnimator()->SchedulePauseForProperties(
+ base::TimeDelta::FromMilliseconds(kAnimationDelayMs),
+ ui::LayerAnimationElement::OPACITY);
+ size_button_->layer()->SetOpacity(1.0f);
+ } else {
+ settings.SetTweenType(gfx::Tween::EASE_IN);
+ // Observer will call size_button_->SetVisible(false) upon completion of
+ // the animation.
+ // TODO(jonross): avoid the delayed SetVisible(false) call by acquring
+ // the size_button's layer before making it invisible. That layer can then
+ // be animated and deleted upon completion of the animation. See
+ // LayerOwner::RecreateLayer
+ settings.AddObserver(this);
+ size_button_->layer()->SetOpacity(0.0f);
+ size_button_->layer()->SetVisible(false);
+ }
}
gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
@@ -142,15 +201,56 @@ gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
}
void FrameCaptionButtonContainerView::Layout() {
- int x = 0;
- for (int i = 0; i < child_count(); ++i) {
+ int x = width();
+ // Offsets the initial position of a child, so that buttons slide into the
+ // place as other buttons are added/removed.
+ int offset_x = 0;
+ for (int i = child_count() - 1; i >= 0; --i) {
views::View* child = child_at(i);
- if (!child->visible())
+ ui::LayerAnimator* child_animator = child->layer()->GetAnimator();
+ bool child_animating = child_animator->is_animating();
+ // The actual property visibility is not being animated, otherwise the
+ // view does not render.
+ bool child_animating_opacity = child_animator->
+ IsAnimatingProperty(ui::LayerAnimationElement::OPACITY);
+ bool child_target_visibility = child->layer()->GetTargetVisibility();
+
+ if (child_animating_opacity) {
+ if (child_target_visibility)
+ offset_x += child->width();
+ else
+ offset_x -= child->width();
+ }
+
+ if (!child->visible() || !child_target_visibility)
continue;
+ scoped_ptr<ui::ScopedLayerAnimationSettings> animation;
gfx::Size size = child->GetPreferredSize();
+ x -= size.width();
+
+ // Animate the button if a previous button is currently animating
+ // its visibility.
+ if (offset_x != 0) {
+ if (!child_animating)
+ child->SetBounds(x + offset_x, 0, size.width(), size.height());
+ if (offset_x < 0) {
+ // Delay sliding to where the previous button was located.
+ child_animator->SchedulePauseForProperties(
+ base::TimeDelta::FromMilliseconds(kAnimationDelayMs),
+ ui::LayerAnimationElement::BOUNDS);
+ }
+
+ ui::ScopedLayerAnimationSettings* settings =
+ new ui::ScopedLayerAnimationSettings(child_animator);
+ settings->SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kMinimizeSlideDurationMs));
+ settings->SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ settings->SetTweenType(gfx::Tween::EASE_OUT);
+ animation.reset(settings);
+ }
child->SetBounds(x, 0, size.width(), size.height());
- x += size.width();
}
}
@@ -202,7 +302,7 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
if (sender == minimize_button_) {
frame_->Minimize();
} else if (sender == size_button_) {
- if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen.
+ if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen.
frame_->SetFullscreen(false);
action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
} else if (frame_->IsMaximized()) {
@@ -212,7 +312,7 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
frame_->Maximize();
action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
}
- } else if(sender == close_button_) {
+ } else if (sender == close_button_) {
frame_->Close();
action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
} else {
@@ -289,6 +389,24 @@ void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
}
}
+void FrameCaptionButtonContainerView::OnImplicitAnimationsCompleted() {
+ // If there is another animation in the queue, the reverse animation was
+ // triggered before the completion of animating to invisible. Do not turn off
+ // the visibility so that the next animation may render.
+ if (!size_button_->layer()->GetAnimator()->is_animating() &&
+ !size_button_->layer()->GetTargetVisibility()) {
+ size_button_->SetVisible(false);
+ }
+ // TODO(jonross): currently we need to delay telling the parent about the
+ // size change from visibility. When the size changes this forces a relayout
+ // and we want to animate both the bounds of FrameCaptionButtonContainerView
+ // along with that of its children. However when the parent is currently
+ // having its bounds changed this leads to strange animations where this view
+ // renders outside of its parents. Create a more specific animation where
+ // height and y are immediately fixed, and where we only animate width and x.
+ PreferredSizeChanged();
+}
+
FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
: icon_image_id(-1),
inactive_icon_image_id(-1),
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 9e67b93..113e0c4 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.h
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.h
@@ -5,8 +5,11 @@
#ifndef ASH_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_
#define ASH_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_
+#include <map>
+
#include "ash/ash_export.h"
#include "ash/frame/caption_buttons/frame_size_button_delegate.h"
+#include "ui/compositor/layer_animation_observer.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
@@ -21,7 +24,8 @@ namespace ash {
class ASH_EXPORT FrameCaptionButtonContainerView
: public views::View,
public views::ButtonListener,
- public FrameSizeButtonDelegate {
+ public FrameSizeButtonDelegate,
+ public ui::ImplicitAnimationObserver {
public:
static const char kViewClassName[];
@@ -136,6 +140,9 @@ class ASH_EXPORT FrameCaptionButtonContainerView
const FrameCaptionButton* to_hover,
const FrameCaptionButton* to_press) OVERRIDE;
+ // ui::ImplicitAnimationObserver:
+ virtual void OnImplicitAnimationsCompleted() OVERRIDE;
+
// The widget that the buttons act on.
views::Widget* frame_;
@@ -152,6 +159,6 @@ class ASH_EXPORT FrameCaptionButtonContainerView
DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView);
};
-} // namesapace ash
+} // namespace ash
#endif // ASH_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_
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..7e9745f 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
@@ -7,6 +7,7 @@
#include "ash/frame/caption_buttons/frame_caption_button.h"
#include "ash/test/ash_test_base.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"
@@ -108,6 +109,7 @@ TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
FrameCaptionButtonContainerView container1(widget_can_maximize.get(),
FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
SetMockImages(&container1);
+ container1.SetBoundsRect(gfx::Rect(container1.GetPreferredSize()));
container1.Layout();
FrameCaptionButtonContainerView::TestApi t1(&container1);
EXPECT_TRUE(t1.minimize_button()->visible());
@@ -123,6 +125,7 @@ TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
FrameCaptionButtonContainerView container2(widget_cannot_maximize.get(),
FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
SetMockImages(&container2);
+ container2.SetBoundsRect(gfx::Rect(container2.GetPreferredSize()));
container2.Layout();
FrameCaptionButtonContainerView::TestApi t2(&container2);
EXPECT_TRUE(t2.minimize_button()->visible());
@@ -136,6 +139,7 @@ TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
FrameCaptionButtonContainerView container3(widget_cannot_maximize.get(),
FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED);
SetMockImages(&container3);
+ container3.SetBoundsRect(gfx::Rect(container3.GetPreferredSize()));
container3.Layout();
FrameCaptionButtonContainerView::TestApi t3(&container3);
EXPECT_FALSE(t3.minimize_button()->visible());
@@ -145,4 +149,57 @@ TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
&container3, *t3.close_button(), *t3.close_button()));
}
+// Tests that the layout animations triggered by button visibilty 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.
+ container.UpdateSizeButtonVisibility(/*force_hidden*/ true);
+ 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, close_button_bounds);
+ EXPECT_LT(container.GetPreferredSize().width(),
+ initial_container_bounds.width());
+
+ // Revealing the size button should cause the minimze button to return to its
+ // original position.
+ container.UpdateSizeButtonVisibility(/*force_hidden*/ false);
+ container.Layout();
+ 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 85722ff..4f424fe 100644
--- a/ash/frame/custom_frame_view_ash.cc
+++ b/ash/frame/custom_frame_view_ash.cc
@@ -4,6 +4,9 @@
#include "ash/frame/custom_frame_view_ash.h"
+#include <algorithm>
+#include <vector>
+
#include "ash/ash_switches.h"
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include "ash/frame/default_header_painter.h"
@@ -150,6 +153,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;
@@ -285,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 it response to the preferred
+ // size changes.
+ if (child != caption_button_container_)
+ return;
+ parent()->Layout();
+}
+
///////////////////////////////////////////////////////////////////////////////
// CustomFrameViewAsh::HeaderView, ShellObserver overrides:
diff --git a/ash/frame/default_header_painter.cc b/ash/frame/default_header_painter.cc
index fd31d77..e8a46ef 100644
--- a/ash/frame/default_header_painter.cc
+++ b/ash/frame/default_header_painter.cc
@@ -200,8 +200,6 @@ void DefaultHeaderPainter::PaintHeader(gfx::Canvas* canvas, Mode mode) {
}
void DefaultHeaderPainter::LayoutHeader() {
- caption_button_container_->Layout();
-
gfx::Size caption_button_container_size =
caption_button_container_->GetPreferredSize();
caption_button_container_->SetBounds(
@@ -209,7 +207,7 @@ void DefaultHeaderPainter::LayoutHeader() {
0,
caption_button_container_size.width(),
caption_button_container_size.height());
-
+ caption_button_container_->Layout();
if (window_icon_) {
// Vertically center the window icon with respect to the caption button
// container.