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 | 9 |
9 #include "ash/ash_switches.h" | 10 #include "ash/ash_switches.h" |
10 #include "ash/frame/caption_buttons/frame_caption_button.h" | 11 #include "ash/frame/caption_buttons/frame_caption_button.h" |
11 #include "ash/frame/caption_buttons/frame_size_button.h" | 12 #include "ash/frame/caption_buttons/frame_size_button.h" |
12 #include "ash/metrics/user_metrics_recorder.h" | 13 #include "ash/metrics/user_metrics_recorder.h" |
13 #include "ash/shell.h" | 14 #include "ash/shell.h" |
14 #include "grit/ui_strings.h" // Accessibility names | 15 #include "grit/ui_strings.h" // Accessibility names |
15 #include "ui/base/hit_test.h" | 16 #include "ui/base/hit_test.h" |
16 #include "ui/base/l10n/l10n_util.h" | 17 #include "ui/base/l10n/l10n_util.h" |
18 #include "ui/compositor/layer.h" | |
17 #include "ui/compositor/scoped_animation_duration_scale_mode.h" | 19 #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
20 #include "ui/compositor/scoped_layer_animation_settings.h" | |
21 #include "ui/gfx/animation/tween.h" | |
18 #include "ui/gfx/canvas.h" | 22 #include "ui/gfx/canvas.h" |
19 #include "ui/gfx/insets.h" | 23 #include "ui/gfx/insets.h" |
20 #include "ui/gfx/point.h" | 24 #include "ui/gfx/point.h" |
21 #include "ui/views/widget/widget.h" | 25 #include "ui/views/widget/widget.h" |
22 #include "ui/views/widget/widget_delegate.h" | 26 #include "ui/views/widget/widget_delegate.h" |
23 | 27 |
24 namespace ash { | 28 namespace ash { |
25 | 29 |
26 namespace { | 30 namespace { |
27 | 31 |
32 // Visual design parameters for animating the transition to maximize mode. | |
33 // When the size button hides we delay sliding the minimize button into its | |
34 // location. Also used to delay showing the size button so that the minimize | |
35 // button slides out of that position. | |
36 const int kAnimationDelayMs = 100; | |
37 const int kMinimizeSlideDurationMs = 500; | |
38 const int kSizeFadeDurationMs = 250; | |
39 | |
28 // Converts |point| from |src| to |dst| and hittests against |dst|. | 40 // Converts |point| from |src| to |dst| and hittests against |dst|. |
29 bool ConvertPointToViewAndHitTest(const views::View* src, | 41 bool ConvertPointToViewAndHitTest(const views::View* src, |
30 const views::View* dst, | 42 const views::View* dst, |
31 const gfx::Point& point) { | 43 const gfx::Point& point) { |
32 gfx::Point converted(point); | 44 gfx::Point converted(point); |
33 views::View::ConvertPointToTarget(src, dst, &converted); | 45 views::View::ConvertPointToTarget(src, dst, &converted); |
34 return dst->HitTestPoint(converted); | 46 return dst->HitTestPoint(converted); |
35 } | 47 } |
36 | 48 |
37 } // namespace | 49 } // namespace |
38 | 50 |
39 // static | 51 // static |
40 const char FrameCaptionButtonContainerView::kViewClassName[] = | 52 const char FrameCaptionButtonContainerView::kViewClassName[] = |
41 "FrameCaptionButtonContainerView"; | 53 "FrameCaptionButtonContainerView"; |
42 | 54 |
43 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( | 55 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( |
44 views::Widget* frame, | 56 views::Widget* frame, |
45 MinimizeAllowed minimize_allowed) | 57 MinimizeAllowed minimize_allowed) |
46 : frame_(frame), | 58 : frame_(frame), |
47 minimize_button_(NULL), | 59 minimize_button_(NULL), |
48 size_button_(NULL), | 60 size_button_(NULL), |
49 close_button_(NULL) { | 61 close_button_(NULL) { |
62 SetPaintToLayer(true); | |
63 SetFillsBoundsOpaquely(false); | |
64 | |
50 // Insert the buttons left to right. | 65 // Insert the buttons left to right. |
51 minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); | 66 minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); |
52 minimize_button_->SetAccessibleName( | 67 minimize_button_->SetAccessibleName( |
53 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); | 68 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); |
54 minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED); | 69 minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED); |
70 minimize_button_->SetPaintToLayer(true); | |
71 minimize_button_->SetFillsBoundsOpaquely(false); | |
72 | |
55 AddChildView(minimize_button_); | 73 AddChildView(minimize_button_); |
56 | 74 |
57 size_button_ = new FrameSizeButton(this, frame, this); | 75 size_button_ = new FrameSizeButton(this, frame, this); |
58 size_button_->SetAccessibleName( | 76 size_button_->SetAccessibleName( |
59 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); | 77 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); |
78 size_button_->SetPaintToLayer(true); | |
79 size_button_->SetFillsBoundsOpaquely(false); | |
60 UpdateSizeButtonVisibility(false); | 80 UpdateSizeButtonVisibility(false); |
61 AddChildView(size_button_); | 81 AddChildView(size_button_); |
62 | 82 |
63 close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); | 83 close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); |
64 close_button_->SetAccessibleName( | 84 close_button_->SetAccessibleName( |
65 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); | 85 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); |
86 close_button_->SetPaintToLayer(true); | |
87 close_button_->SetFillsBoundsOpaquely(false); | |
66 AddChildView(close_button_); | 88 AddChildView(close_button_); |
67 } | 89 } |
68 | 90 |
69 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() { | 91 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() { |
70 } | 92 } |
71 | 93 |
72 void FrameCaptionButtonContainerView::SetButtonImages( | 94 void FrameCaptionButtonContainerView::SetButtonImages( |
73 CaptionButtonIcon icon, | 95 CaptionButtonIcon icon, |
74 int icon_image_id, | 96 int icon_image_id, |
75 int inactive_icon_image_id, | 97 int inactive_icon_image_id, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
120 } | 142 } |
121 | 143 |
122 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility( | 144 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility( |
123 bool force_hidden) { | 145 bool force_hidden) { |
124 // TODO(flackr): Refactor the Maximize Mode notifications. Currently | 146 // TODO(flackr): Refactor the Maximize Mode notifications. Currently |
125 // UpdateSizeButtonVisibilty requires a force_hidden parameter. This is | 147 // UpdateSizeButtonVisibilty requires a force_hidden parameter. This is |
126 // because Shell::IsMaximizeWindowManagerEnabled is still false at the | 148 // because Shell::IsMaximizeWindowManagerEnabled is still false at the |
127 // time when ShellObserver::OnMaximizeModeStarted is called. This prevents | 149 // time when ShellObserver::OnMaximizeModeStarted is called. This prevents |
128 // this method from performing that check, and instead relies on the calling | 150 // this method from performing that check, and instead relies on the calling |
129 // code to tell it to force being hidden. | 151 // code to tell it to force being hidden. |
130 size_button_->SetVisible( | 152 |
131 !force_hidden && frame_->widget_delegate()->CanMaximize()); | 153 bool visible = !force_hidden && frame_->widget_delegate()->CanMaximize(); |
154 | |
155 // Turning visibility off prevents animations from rendering. Setting the | |
156 // size button visibility to false will occur after the animation. | |
157 if (visible) { | |
158 size_button_->SetVisible(visible); | |
159 // If SetVisible(true) is called while animating to not visible, then | |
160 // views::View::SetVisible(true) is a no-op. When the previous animation | |
161 // ends layer->SetVisible(false) is called. To prevent this | |
162 // layer->SetVisible(true) immediately interrupts the animation of this | |
163 // property, and keeps the layer visible. | |
flackr
2014/05/23 15:17:26
I would just say,
Because we delay calling View::S
jonross
2014/05/23 17:08:57
Done.
| |
164 size_button_->layer()->SetVisible(true); | |
165 } | |
166 | |
167 ui::ScopedLayerAnimationSettings settings( | |
168 size_button_->layer()->GetAnimator()); | |
169 settings.SetTransitionDuration( | |
170 base::TimeDelta::FromMilliseconds(kSizeFadeDurationMs)); | |
171 settings.SetPreemptionStrategy( | |
172 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
173 | |
174 if (visible) { | |
175 settings.SetTweenType(gfx::Tween::EASE_OUT); | |
176 // Delay fade in so that the minimize button has begun its sliding | |
177 // animation. | |
178 size_button_->layer()->GetAnimator()->SchedulePauseForProperties( | |
179 base::TimeDelta::FromMilliseconds(kAnimationDelayMs), | |
180 ui::LayerAnimationElement::OPACITY); | |
181 size_button_->layer()->SetOpacity(1.0f); | |
182 } else { | |
183 settings.SetTweenType(gfx::Tween::EASE_IN); | |
184 // Observer will call size_button_->SetVisible(false) upon completion of | |
185 // the animation. | |
186 // TODO(jonross): avoid the delayed SetVisible(false) call by acquring | |
187 // the size_button's layer before making it invisible. That layer can then | |
188 // be animated and deleted upon completion of the animation. See | |
189 // LayerOwner::RecreateLayer | |
190 settings.AddObserver(this); | |
191 size_button_->layer()->SetOpacity(0.0f); | |
192 size_button_->layer()->SetVisible(false); | |
193 } | |
132 } | 194 } |
133 | 195 |
134 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() { | 196 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() { |
135 int width = 0; | 197 int width = 0; |
136 for (int i = 0; i < child_count(); ++i) { | 198 for (int i = 0; i < child_count(); ++i) { |
137 views::View* child = child_at(i); | 199 views::View* child = child_at(i); |
138 if (child->visible()) | 200 if (child->visible()) |
139 width += child_at(i)->GetPreferredSize().width(); | 201 width += child_at(i)->GetPreferredSize().width(); |
140 } | 202 } |
141 return gfx::Size(width, close_button_->GetPreferredSize().height()); | 203 return gfx::Size(width, close_button_->GetPreferredSize().height()); |
142 } | 204 } |
143 | 205 |
144 void FrameCaptionButtonContainerView::Layout() { | 206 void FrameCaptionButtonContainerView::Layout() { |
145 int x = 0; | 207 int x = width(); |
146 for (int i = 0; i < child_count(); ++i) { | 208 // Offsets the initial position of a child, so that buttons slide into the |
209 // place as other buttons are added/removed. | |
210 int offset_x = 0; | |
211 for (int i = child_count() - 1; i >= 0; --i) { | |
147 views::View* child = child_at(i); | 212 views::View* child = child_at(i); |
148 if (!child->visible()) | 213 ui::LayerAnimator* child_animator = child->layer()->GetAnimator(); |
214 bool child_animating = child_animator->is_animating(); | |
215 // The actual property visibility is not being animated, otherwise the | |
216 // view does not render. | |
217 bool child_animating_opacity = child_animator-> | |
218 IsAnimatingProperty(ui::LayerAnimationElement::OPACITY); | |
219 bool child_target_visibility = child->layer()->GetTargetVisibility(); | |
220 | |
221 if (child_animating_opacity) { | |
222 if (child_target_visibility) | |
223 offset_x += child->width(); | |
224 else | |
225 offset_x -= child->width(); | |
226 } | |
227 | |
228 if (!child->visible() || !child_target_visibility) | |
149 continue; | 229 continue; |
150 | 230 |
231 scoped_ptr<ui::ScopedLayerAnimationSettings> animation; | |
151 gfx::Size size = child->GetPreferredSize(); | 232 gfx::Size size = child->GetPreferredSize(); |
233 x -= size.width(); | |
234 | |
235 // Animate the button if a previous button is currently animating | |
236 // its visibility. | |
237 if (offset_x != 0) { | |
238 if (!child_animating) | |
239 child->SetBounds(x + offset_x, 0, size.width(), size.height()); | |
240 if (offset_x < 0) { | |
241 // Delay sliding to where the previous button was located. | |
242 child_animator->SchedulePauseForProperties( | |
243 base::TimeDelta::FromMilliseconds(kAnimationDelayMs), | |
244 ui::LayerAnimationElement::BOUNDS); | |
245 } | |
246 | |
247 ui::ScopedLayerAnimationSettings* settings = | |
248 new ui::ScopedLayerAnimationSettings(child_animator); | |
249 settings->SetTransitionDuration( | |
250 base::TimeDelta::FromMilliseconds(kMinimizeSlideDurationMs)); | |
251 settings->SetPreemptionStrategy( | |
252 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
253 settings->SetTweenType(gfx::Tween::EASE_OUT); | |
254 animation.reset(settings); | |
255 } | |
152 child->SetBounds(x, 0, size.width(), size.height()); | 256 child->SetBounds(x, 0, size.width(), size.height()); |
153 x += size.width(); | |
154 } | 257 } |
155 } | 258 } |
156 | 259 |
157 const char* FrameCaptionButtonContainerView::GetClassName() const { | 260 const char* FrameCaptionButtonContainerView::GetClassName() const { |
158 return kViewClassName; | 261 return kViewClassName; |
159 } | 262 } |
160 | 263 |
161 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, | 264 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, |
162 CaptionButtonIcon icon, | 265 CaptionButtonIcon icon, |
163 Animate animate) { | 266 Animate animate) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 } | 298 } |
196 | 299 |
197 // Abort any animations of the button icons. | 300 // Abort any animations of the button icons. |
198 SetButtonsToNormal(ANIMATE_NO); | 301 SetButtonsToNormal(ANIMATE_NO); |
199 | 302 |
200 ash::UserMetricsAction action = | 303 ash::UserMetricsAction action = |
201 ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; | 304 ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; |
202 if (sender == minimize_button_) { | 305 if (sender == minimize_button_) { |
203 frame_->Minimize(); | 306 frame_->Minimize(); |
204 } else if (sender == size_button_) { | 307 } else if (sender == size_button_) { |
205 if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. | 308 if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. |
206 frame_->SetFullscreen(false); | 309 frame_->SetFullscreen(false); |
207 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; | 310 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; |
208 } else if (frame_->IsMaximized()) { | 311 } else if (frame_->IsMaximized()) { |
209 frame_->Restore(); | 312 frame_->Restore(); |
210 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; | 313 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; |
211 } else { | 314 } else { |
212 frame_->Maximize(); | 315 frame_->Maximize(); |
213 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; | 316 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; |
214 } | 317 } |
215 } else if(sender == close_button_) { | 318 } else if (sender == close_button_) { |
216 frame_->Close(); | 319 frame_->Close(); |
217 action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; | 320 action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; |
218 } else { | 321 } else { |
219 return; | 322 return; |
220 } | 323 } |
221 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action); | 324 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action); |
222 } | 325 } |
223 | 326 |
224 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const { | 327 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const { |
225 return minimize_button_->visible(); | 328 return minimize_button_->visible(); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
282 FrameCaptionButton* button = buttons[i]; | 385 FrameCaptionButton* button = buttons[i]; |
283 views::Button::ButtonState new_state = views::Button::STATE_NORMAL; | 386 views::Button::ButtonState new_state = views::Button::STATE_NORMAL; |
284 if (button == to_hover) | 387 if (button == to_hover) |
285 new_state = views::Button::STATE_HOVERED; | 388 new_state = views::Button::STATE_HOVERED; |
286 else if (button == to_press) | 389 else if (button == to_press) |
287 new_state = views::Button::STATE_PRESSED; | 390 new_state = views::Button::STATE_PRESSED; |
288 button->SetState(new_state); | 391 button->SetState(new_state); |
289 } | 392 } |
290 } | 393 } |
291 | 394 |
395 void FrameCaptionButtonContainerView::OnImplicitAnimationsCompleted() { | |
396 // If there is another animation in the queue, the reverse animation was | |
397 // triggered before the completion of animating to invisible. Do not turn off | |
398 // the visibility so that the next animation may render. | |
399 if (!size_button_->layer()->GetAnimator()->is_animating() && | |
400 !size_button_->layer()->GetTargetVisibility()) | |
flackr
2014/05/23 15:17:26
nit: multiline if condition needs braces { }.
jonross
2014/05/23 17:08:57
Done.
| |
401 size_button_->SetVisible(false); | |
402 // TODO(jonross): currently we need to delay telling the parent about the | |
403 // size change from visibility. When the size changes this forces a relayout | |
404 // and we want to animate both the bounds of FrameCaptionButtonContainerView | |
405 // along with that of its children. However when the parent is currently | |
406 // having its bounds changed this leads to strange animations where this view | |
407 // renders outside of its parents. Create a more specific animation where | |
408 // height and y are immediately fixed, and where we only animate width and x. | |
409 PreferredSizeChanged(); | |
410 } | |
411 | |
292 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() | 412 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() |
293 : icon_image_id(-1), | 413 : icon_image_id(-1), |
294 inactive_icon_image_id(-1), | 414 inactive_icon_image_id(-1), |
295 hovered_background_image_id(-1), | 415 hovered_background_image_id(-1), |
296 pressed_background_image_id(-1) { | 416 pressed_background_image_id(-1) { |
297 } | 417 } |
298 | 418 |
299 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds( | 419 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds( |
300 int icon_id, | 420 int icon_id, |
301 int inactive_icon_id, | 421 int inactive_icon_id, |
302 int hovered_background_id, | 422 int hovered_background_id, |
303 int pressed_background_id) | 423 int pressed_background_id) |
304 : icon_image_id(icon_id), | 424 : icon_image_id(icon_id), |
305 inactive_icon_image_id(inactive_icon_id), | 425 inactive_icon_image_id(inactive_icon_id), |
306 hovered_background_image_id(hovered_background_id), | 426 hovered_background_image_id(hovered_background_id), |
307 pressed_background_image_id(pressed_background_id) { | 427 pressed_background_image_id(pressed_background_id) { |
308 } | 428 } |
309 | 429 |
310 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() { | 430 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() { |
311 } | 431 } |
312 | 432 |
313 } // namespace ash | 433 } // namespace ash |
OLD | NEW |