Chromium Code Reviews| Index: ash/frame/caption_buttons/frame_caption_button_container_view.cc |
| 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 12bbb71a35cadd9e0fa774f31add9541abfec4ba..1aeb49265fcba72da20291bd023aa5eff22598b9 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,35 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( |
| minimize_button_(NULL), |
| size_button_(NULL), |
| close_button_(NULL) { |
| + SetPaintToLayer(true); |
| + SetFillsBoundsOpaquely(false); |
| + set_layer_owner_delegate(this); |
| + |
| // 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); |
| + minimize_button_->set_layer_owner_delegate(this); |
| 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); |
| + size_button_->set_layer_owner_delegate(this); |
| 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); |
| + close_button_->set_layer_owner_delegate(this); |
| AddChildView(close_button_); |
| } |
| @@ -127,8 +152,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 +205,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 +306,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 +316,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 +393,37 @@ 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(); |
| +} |
| + |
| +void FrameCaptionButtonContainerView::OnLayerRecreated(ui::Layer* old_layer, |
| + ui::Layer* new_layer) { |
| + if (new_layer == layer()) |
| + GetWidget()->UpdateRootLayers(); |
| + if (new_layer == minimize_button_->layer()); |
| + minimize_button_->GetWidget()->UpdateRootLayers(); |
| + if (new_layer == size_button_->layer()) |
| + size_button_->GetWidget()->UpdateRootLayers(); |
| + if (new_layer == close_button_->layer()) |
| + close_button_->GetWidget()->UpdateRootLayers(); |
|
flackr
2014/06/04 22:41:44
I think it'd make more sense for FrameCaptionButto
jonross
2014/06/05 15:16:39
Done.
|
| + GetWidget()->UpdateRootLayers(); |
| +} |
| + |
| FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() |
| : icon_image_id(-1), |
| inactive_icon_image_id(-1), |