OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h" | 5 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 #include <map> | 8 #include <map> |
9 | 9 |
10 #include "ash/ash_switches.h" | 10 #include "ash/ash_switches.h" |
11 #include "ash/frame/caption_buttons/frame_caption_button.h" | 11 #include "ash/frame/caption_buttons/frame_caption_button.h" |
12 #include "ash/frame/caption_buttons/frame_size_button.h" | 12 #include "ash/frame/caption_buttons/frame_size_button.h" |
13 #include "ash/metrics/user_metrics_recorder.h" | 13 #include "ash/metrics/user_metrics_recorder.h" |
14 #include "ash/shell.h" | 14 #include "ash/shell.h" |
15 #include "ash/wm/maximize_mode/maximize_mode_controller.h" | 15 #include "ash/wm/maximize_mode/maximize_mode_controller.h" |
16 #include "grit/ui_strings.h" // Accessibility names | 16 #include "grit/ui_strings.h" // Accessibility names |
17 #include "ui/base/hit_test.h" | 17 #include "ui/base/hit_test.h" |
18 #include "ui/base/l10n/l10n_util.h" | 18 #include "ui/base/l10n/l10n_util.h" |
| 19 #include "ui/compositor/layer.h" |
19 #include "ui/compositor/scoped_animation_duration_scale_mode.h" | 20 #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| 21 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 22 #include "ui/gfx/animation/tween.h" |
20 #include "ui/gfx/canvas.h" | 23 #include "ui/gfx/canvas.h" |
21 #include "ui/gfx/insets.h" | 24 #include "ui/gfx/insets.h" |
22 #include "ui/gfx/point.h" | 25 #include "ui/gfx/point.h" |
23 #include "ui/views/widget/widget.h" | 26 #include "ui/views/widget/widget.h" |
24 #include "ui/views/widget/widget_delegate.h" | 27 #include "ui/views/widget/widget_delegate.h" |
25 | 28 |
26 namespace ash { | 29 namespace ash { |
27 | 30 |
28 namespace { | 31 namespace { |
29 | 32 |
| 33 // Visual design parameters for animating the transition to maximize mode. |
| 34 // When the size button hides we delay sliding the minimize button into its |
| 35 // location. Also used to delay showing the size button so that the minimize |
| 36 // button slides out of that position. |
| 37 const int kAnimationDelayMs = 100; |
| 38 const int kMinimizeSlideDurationMs = 500; |
| 39 const int kSizeFadeDurationMs = 250; |
| 40 |
30 // Converts |point| from |src| to |dst| and hittests against |dst|. | 41 // Converts |point| from |src| to |dst| and hittests against |dst|. |
31 bool ConvertPointToViewAndHitTest(const views::View* src, | 42 bool ConvertPointToViewAndHitTest(const views::View* src, |
32 const views::View* dst, | 43 const views::View* dst, |
33 const gfx::Point& point) { | 44 const gfx::Point& point) { |
34 gfx::Point converted(point); | 45 gfx::Point converted(point); |
35 views::View::ConvertPointToTarget(src, dst, &converted); | 46 views::View::ConvertPointToTarget(src, dst, &converted); |
36 return dst->HitTestPoint(converted); | 47 return dst->HitTestPoint(converted); |
37 } | 48 } |
38 | 49 |
39 } // namespace | 50 } // namespace |
40 | 51 |
41 // static | 52 // static |
42 const char FrameCaptionButtonContainerView::kViewClassName[] = | 53 const char FrameCaptionButtonContainerView::kViewClassName[] = |
43 "FrameCaptionButtonContainerView"; | 54 "FrameCaptionButtonContainerView"; |
44 | 55 |
45 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( | 56 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( |
46 views::Widget* frame, | 57 views::Widget* frame, |
47 MinimizeAllowed minimize_allowed) | 58 MinimizeAllowed minimize_allowed) |
48 : frame_(frame), | 59 : frame_(frame), |
49 minimize_button_(NULL), | 60 minimize_button_(NULL), |
50 size_button_(NULL), | 61 size_button_(NULL), |
51 close_button_(NULL) { | 62 close_button_(NULL) { |
| 63 SetPaintToLayer(true); |
| 64 SetFillsBoundsOpaquely(false); |
| 65 set_layer_owner_delegate(this); |
| 66 |
52 // Insert the buttons left to right. | 67 // Insert the buttons left to right. |
53 minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); | 68 minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); |
54 minimize_button_->SetAccessibleName( | 69 minimize_button_->SetAccessibleName( |
55 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); | 70 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); |
56 minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED); | 71 minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED); |
57 AddChildView(minimize_button_); | 72 AddChildView(minimize_button_); |
58 | 73 |
59 size_button_ = new FrameSizeButton(this, frame, this); | 74 size_button_ = new FrameSizeButton(this, frame, this); |
60 size_button_->SetAccessibleName( | 75 size_button_->SetAccessibleName( |
61 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); | 76 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 ConvertPointToViewAndHitTest(this, size_button_, point)) { | 130 ConvertPointToViewAndHitTest(this, size_button_, point)) { |
116 return HTMAXBUTTON; | 131 return HTMAXBUTTON; |
117 } else if (minimize_button_->visible() && | 132 } else if (minimize_button_->visible() && |
118 ConvertPointToViewAndHitTest(this, minimize_button_, point)) { | 133 ConvertPointToViewAndHitTest(this, minimize_button_, point)) { |
119 return HTMINBUTTON; | 134 return HTMINBUTTON; |
120 } | 135 } |
121 return HTNOWHERE; | 136 return HTNOWHERE; |
122 } | 137 } |
123 | 138 |
124 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() { | 139 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() { |
125 size_button_->SetVisible( | 140 bool visible = !Shell::GetInstance()->maximize_mode_controller()-> |
126 !Shell::GetInstance()->maximize_mode_controller()-> | |
127 IsMaximizeModeWindowManagerEnabled() && | 141 IsMaximizeModeWindowManagerEnabled() && |
128 frame_->widget_delegate()->CanMaximize()); | 142 frame_->widget_delegate()->CanMaximize(); |
| 143 |
| 144 // Turning visibility off prevents animations from rendering. Setting the |
| 145 // size button visibility to false will occur after the animation. |
| 146 if (visible) { |
| 147 size_button_->SetVisible(true); |
| 148 // Because we delay calling View::SetVisible(false) until the end of the |
| 149 // animation, if SetVisible(true) is called mid-animation, the View still |
| 150 // believes it is visible and will not update the target layer visibility. |
| 151 size_button_->layer()->SetVisible(true); |
| 152 } |
| 153 |
| 154 ui::ScopedLayerAnimationSettings settings( |
| 155 size_button_->layer()->GetAnimator()); |
| 156 settings.SetTransitionDuration( |
| 157 base::TimeDelta::FromMilliseconds(kSizeFadeDurationMs)); |
| 158 settings.SetPreemptionStrategy( |
| 159 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 160 |
| 161 if (visible) { |
| 162 settings.SetTweenType(gfx::Tween::EASE_OUT); |
| 163 // Delay fade in so that the minimize button has begun its sliding |
| 164 // animation. |
| 165 size_button_->layer()->GetAnimator()->SchedulePauseForProperties( |
| 166 base::TimeDelta::FromMilliseconds(kAnimationDelayMs), |
| 167 ui::LayerAnimationElement::OPACITY); |
| 168 size_button_->layer()->SetOpacity(1.0f); |
| 169 } else { |
| 170 settings.SetTweenType(gfx::Tween::EASE_IN); |
| 171 // Observer will call size_button_->SetVisible(false) upon completion of |
| 172 // the animation. |
| 173 // TODO(jonross): avoid the delayed SetVisible(false) call by acquring |
| 174 // the size_button's layer before making it invisible. That layer can then |
| 175 // be animated and deleted upon completion of the animation. See |
| 176 // LayerOwner::RecreateLayer |
| 177 settings.AddObserver(this); |
| 178 size_button_->layer()->SetOpacity(0.0f); |
| 179 size_button_->layer()->SetVisible(false); |
| 180 } |
129 } | 181 } |
130 | 182 |
131 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const { | 183 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const { |
132 int width = 0; | 184 int width = 0; |
133 for (int i = 0; i < child_count(); ++i) { | 185 for (int i = 0; i < child_count(); ++i) { |
134 const views::View* child = child_at(i); | 186 const views::View* child = child_at(i); |
135 if (child->visible()) | 187 if (child->visible()) |
136 width += child_at(i)->GetPreferredSize().width(); | 188 width += child_at(i)->GetPreferredSize().width(); |
137 } | 189 } |
138 return gfx::Size(width, close_button_->GetPreferredSize().height()); | 190 return gfx::Size(width, close_button_->GetPreferredSize().height()); |
139 } | 191 } |
140 | 192 |
141 void FrameCaptionButtonContainerView::Layout() { | 193 void FrameCaptionButtonContainerView::Layout() { |
142 int x = 0; | 194 int x = width(); |
143 for (int i = 0; i < child_count(); ++i) { | 195 // Offsets the initial position of a child, so that buttons slide into the |
| 196 // place as other buttons are added/removed. |
| 197 int offset_x = 0; |
| 198 for (int i = child_count() - 1; i >= 0; --i) { |
144 views::View* child = child_at(i); | 199 views::View* child = child_at(i); |
145 if (!child->visible()) | 200 ui::LayerAnimator* child_animator = child->layer()->GetAnimator(); |
| 201 bool child_animating = child_animator->is_animating(); |
| 202 // The actual property visibility is not being animated, otherwise the |
| 203 // view does not render. |
| 204 bool child_animating_opacity = child_animator-> |
| 205 IsAnimatingProperty(ui::LayerAnimationElement::OPACITY); |
| 206 bool child_target_visibility = child->layer()->GetTargetVisibility(); |
| 207 |
| 208 if (child_animating_opacity) { |
| 209 if (child_target_visibility) |
| 210 offset_x += child->width(); |
| 211 else |
| 212 offset_x -= child->width(); |
| 213 } |
| 214 |
| 215 if (!child->visible() || !child_target_visibility) |
146 continue; | 216 continue; |
147 | 217 |
| 218 scoped_ptr<ui::ScopedLayerAnimationSettings> animation; |
148 gfx::Size size = child->GetPreferredSize(); | 219 gfx::Size size = child->GetPreferredSize(); |
| 220 x -= size.width(); |
| 221 |
| 222 // Animate the button if a previous button is currently animating |
| 223 // its visibility. |
| 224 if (offset_x != 0) { |
| 225 if (!child_animating) |
| 226 child->SetBounds(x + offset_x, 0, size.width(), size.height()); |
| 227 if (offset_x < 0) { |
| 228 // Delay sliding to where the previous button was located. |
| 229 child_animator->SchedulePauseForProperties( |
| 230 base::TimeDelta::FromMilliseconds(kAnimationDelayMs), |
| 231 ui::LayerAnimationElement::BOUNDS); |
| 232 } |
| 233 |
| 234 ui::ScopedLayerAnimationSettings* settings = |
| 235 new ui::ScopedLayerAnimationSettings(child_animator); |
| 236 settings->SetTransitionDuration( |
| 237 base::TimeDelta::FromMilliseconds(kMinimizeSlideDurationMs)); |
| 238 settings->SetPreemptionStrategy( |
| 239 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 240 settings->SetTweenType(gfx::Tween::EASE_OUT); |
| 241 animation.reset(settings); |
| 242 } |
149 child->SetBounds(x, 0, size.width(), size.height()); | 243 child->SetBounds(x, 0, size.width(), size.height()); |
150 x += size.width(); | |
151 } | 244 } |
152 } | 245 } |
153 | 246 |
154 const char* FrameCaptionButtonContainerView::GetClassName() const { | 247 const char* FrameCaptionButtonContainerView::GetClassName() const { |
155 return kViewClassName; | 248 return kViewClassName; |
156 } | 249 } |
157 | 250 |
158 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, | 251 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, |
159 CaptionButtonIcon icon, | 252 CaptionButtonIcon icon, |
160 Animate animate) { | 253 Animate animate) { |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 FrameCaptionButton* button = buttons[i]; | 372 FrameCaptionButton* button = buttons[i]; |
280 views::Button::ButtonState new_state = views::Button::STATE_NORMAL; | 373 views::Button::ButtonState new_state = views::Button::STATE_NORMAL; |
281 if (button == to_hover) | 374 if (button == to_hover) |
282 new_state = views::Button::STATE_HOVERED; | 375 new_state = views::Button::STATE_HOVERED; |
283 else if (button == to_press) | 376 else if (button == to_press) |
284 new_state = views::Button::STATE_PRESSED; | 377 new_state = views::Button::STATE_PRESSED; |
285 button->SetState(new_state); | 378 button->SetState(new_state); |
286 } | 379 } |
287 } | 380 } |
288 | 381 |
| 382 void FrameCaptionButtonContainerView::OnImplicitAnimationsCompleted() { |
| 383 // If there is another animation in the queue, the reverse animation was |
| 384 // triggered before the completion of animating to invisible. Do not turn off |
| 385 // the visibility so that the next animation may render. |
| 386 if (!size_button_->layer()->GetAnimator()->is_animating() && |
| 387 !size_button_->layer()->GetTargetVisibility()) { |
| 388 size_button_->SetVisible(false); |
| 389 } |
| 390 // TODO(jonross): currently we need to delay telling the parent about the |
| 391 // size change from visibility. When the size changes this forces a relayout |
| 392 // and we want to animate both the bounds of FrameCaptionButtonContainerView |
| 393 // along with that of its children. However when the parent is currently |
| 394 // having its bounds changed this leads to strange animations where this view |
| 395 // renders outside of its parents. Create a more specific animation where |
| 396 // height and y are immediately fixed, and where we only animate width and x. |
| 397 PreferredSizeChanged(); |
| 398 } |
| 399 |
| 400 void FrameCaptionButtonContainerView::OnLayerRecreated(ui::Layer* old_layer, |
| 401 ui::Layer* new_layer) { |
| 402 GetWidget()->UpdateRootLayers(); |
| 403 } |
| 404 |
289 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() | 405 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() |
290 : icon_image_id(-1), | 406 : icon_image_id(-1), |
291 inactive_icon_image_id(-1), | 407 inactive_icon_image_id(-1), |
292 hovered_background_image_id(-1), | 408 hovered_background_image_id(-1), |
293 pressed_background_image_id(-1) { | 409 pressed_background_image_id(-1) { |
294 } | 410 } |
295 | 411 |
296 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds( | 412 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds( |
297 int icon_id, | 413 int icon_id, |
298 int inactive_icon_id, | 414 int inactive_icon_id, |
299 int hovered_background_id, | 415 int hovered_background_id, |
300 int pressed_background_id) | 416 int pressed_background_id) |
301 : icon_image_id(icon_id), | 417 : icon_image_id(icon_id), |
302 inactive_icon_image_id(inactive_icon_id), | 418 inactive_icon_image_id(inactive_icon_id), |
303 hovered_background_image_id(hovered_background_id), | 419 hovered_background_image_id(hovered_background_id), |
304 pressed_background_image_id(pressed_background_id) { | 420 pressed_background_image_id(pressed_background_id) { |
305 } | 421 } |
306 | 422 |
307 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() { | 423 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() { |
308 } | 424 } |
309 | 425 |
310 } // namespace ash | 426 } // namespace ash |
OLD | NEW |