• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
6 
7 #include <cmath>
8 #include <map>
9 
10 #include "ash/ash_switches.h"
11 #include "ash/frame/caption_buttons/frame_caption_button.h"
12 #include "ash/frame/caption_buttons/frame_size_button.h"
13 #include "ash/metrics/user_metrics_recorder.h"
14 #include "ash/shell.h"
15 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
16 #include "ui/base/hit_test.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
19 #include "ui/gfx/animation/slide_animation.h"
20 #include "ui/gfx/animation/tween.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/insets.h"
23 #include "ui/gfx/point.h"
24 #include "ui/strings/grit/ui_strings.h"  // Accessibility names
25 #include "ui/views/widget/widget.h"
26 #include "ui/views/widget/widget_delegate.h"
27 
28 namespace ash {
29 
30 namespace {
31 
32 // Duration of the animation of the position of |minimize_button_|.
33 const int kPositionAnimationDurationMs = 500;
34 
35 // Duration of the animation of the alpha of |size_button_|.
36 const int kAlphaAnimationDurationMs = 250;
37 
38 // Delay during |maximize_mode_animation_| hide to wait before beginning to
39 // animate the position of |minimize_button_|.
40 const int kHidePositionDelayMs = 100;
41 
42 // Duration of |maximize_mode_animation_| hiding.
43 // Hiding size button 250
44 // |------------------------|
45 // Delay 100      Slide minimize button 500
46 // |---------|-------------------------------------------------|
47 const int kHideAnimationDurationMs =
48     kHidePositionDelayMs + kPositionAnimationDurationMs;
49 
50 // Delay during |maximize_mode_animation_| show to wait before beginning to
51 // animate the alpha of |size_button_|.
52 const int kShowAnimationAlphaDelayMs = 100;
53 
54 // Duration of |maximize_mode_animation_| showing.
55 // Slide minimize button 500
56 // |-------------------------------------------------|
57 // Delay 100   Show size button 250
58 // |---------|-----------------------|
59 const int kShowAnimationDurationMs = kPositionAnimationDurationMs;
60 
61 // Value of |maximize_mode_animation_| showing to begin animating alpha of
62 // |size_button_|.
SizeButtonShowStartValue()63 float SizeButtonShowStartValue() {
64   return static_cast<float>(kShowAnimationAlphaDelayMs)
65       / kShowAnimationDurationMs;
66 }
67 
68 // Amount of |maximize_mode_animation_| showing to animate the alpha of
69 // |size_button_|.
SizeButtonShowDuration()70 float SizeButtonShowDuration() {
71   return static_cast<float>(kAlphaAnimationDurationMs)
72       / kShowAnimationDurationMs;
73 }
74 
75 // Amount of |maximize_mode_animation_| hiding to animate the alpha of
76 // |size_button_|.
SizeButtonHideDuration()77 float SizeButtonHideDuration() {
78   return static_cast<float>(kAlphaAnimationDurationMs)
79       / kHideAnimationDurationMs;
80 }
81 
82 // Value of |maximize_mode_animation_| hiding to begin animating the position of
83 // |minimize_button_|.
HidePositionStartValue()84 float HidePositionStartValue() {
85   return 1.0f - static_cast<float>(kHidePositionDelayMs)
86       / kHideAnimationDurationMs;
87 }
88 
89 // Converts |point| from |src| to |dst| and hittests against |dst|.
ConvertPointToViewAndHitTest(const views::View * src,const views::View * dst,const gfx::Point & point)90 bool ConvertPointToViewAndHitTest(const views::View* src,
91                                   const views::View* dst,
92                                   const gfx::Point& point) {
93   gfx::Point converted(point);
94   views::View::ConvertPointToTarget(src, dst, &converted);
95   return dst->HitTestPoint(converted);
96 }
97 
98 // Bounds animation values to the range 0.0 - 1.0. Allows for mapping of offset
99 // animations to the expected range so that gfx::Tween::CalculateValue() can be
100 // used.
CapAnimationValue(double value)101 double CapAnimationValue(double value) {
102   return std::min(1.0, std::max(0.0, value));
103 }
104 
105 }  // namespace
106 
107 // static
108 const char FrameCaptionButtonContainerView::kViewClassName[] =
109     "FrameCaptionButtonContainerView";
110 
FrameCaptionButtonContainerView(views::Widget * frame,MinimizeAllowed minimize_allowed)111 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
112     views::Widget* frame,
113     MinimizeAllowed minimize_allowed)
114     : frame_(frame),
115       minimize_button_(NULL),
116       size_button_(NULL),
117       close_button_(NULL) {
118   bool size_button_visibility = ShouldSizeButtonBeVisible();
119   maximize_mode_animation_.reset(new gfx::SlideAnimation(this));
120   maximize_mode_animation_->SetTweenType(gfx::Tween::LINEAR);
121 
122   // Ensure animation tracks visibility of size button.
123   if (size_button_visibility)
124     maximize_mode_animation_->Reset(1.0f);
125 
126   // Insert the buttons left to right.
127   minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
128   minimize_button_->SetAccessibleName(
129       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
130   minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED);
131   AddChildView(minimize_button_);
132 
133   size_button_ = new FrameSizeButton(this, frame, this);
134   size_button_->SetAccessibleName(
135       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
136   size_button_->SetVisible(size_button_visibility);
137   AddChildView(size_button_);
138 
139   close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
140   close_button_->SetAccessibleName(
141       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
142   AddChildView(close_button_);
143 }
144 
~FrameCaptionButtonContainerView()145 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {
146 }
147 
EndAnimations()148 void FrameCaptionButtonContainerView::TestApi::EndAnimations() {
149   container_view_->maximize_mode_animation_->End();
150 }
151 
SetButtonImages(CaptionButtonIcon icon,int icon_image_id,int inactive_icon_image_id,int hovered_background_image_id,int pressed_background_image_id)152 void FrameCaptionButtonContainerView::SetButtonImages(
153     CaptionButtonIcon icon,
154     int icon_image_id,
155     int inactive_icon_image_id,
156     int hovered_background_image_id,
157     int pressed_background_image_id) {
158   button_icon_id_map_[icon] = ButtonIconIds(icon_image_id,
159                                             inactive_icon_image_id,
160                                             hovered_background_image_id,
161                                             pressed_background_image_id);
162   FrameCaptionButton* buttons[] = {
163     minimize_button_, size_button_, close_button_
164   };
165   for (size_t i = 0; i < arraysize(buttons); ++i) {
166     if (buttons[i]->icon() == icon) {
167       buttons[i]->SetImages(icon,
168                             FrameCaptionButton::ANIMATE_NO,
169                             icon_image_id,
170                             inactive_icon_image_id,
171                             hovered_background_image_id,
172                             pressed_background_image_id);
173     }
174   }
175 }
176 
SetPaintAsActive(bool paint_as_active)177 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
178   minimize_button_->set_paint_as_active(paint_as_active);
179   size_button_->set_paint_as_active(paint_as_active);
180   close_button_->set_paint_as_active(paint_as_active);
181 }
182 
ResetWindowControls()183 void FrameCaptionButtonContainerView::ResetWindowControls() {
184   SetButtonsToNormal(ANIMATE_NO);
185 }
186 
NonClientHitTest(const gfx::Point & point) const187 int FrameCaptionButtonContainerView::NonClientHitTest(
188     const gfx::Point& point) const {
189   if (close_button_->visible() &&
190       ConvertPointToViewAndHitTest(this, close_button_, point)) {
191     return HTCLOSE;
192   } else if (size_button_->visible() &&
193              ConvertPointToViewAndHitTest(this, size_button_, point)) {
194     return HTMAXBUTTON;
195   } else if (minimize_button_->visible() &&
196              ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
197     return HTMINBUTTON;
198   }
199   return HTNOWHERE;
200 }
201 
UpdateSizeButtonVisibility()202 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() {
203   bool visible = ShouldSizeButtonBeVisible();
204   if (visible) {
205     size_button_->SetVisible(true);
206     maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs);
207     maximize_mode_animation_->Show();
208   } else {
209     maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs);
210     maximize_mode_animation_->Hide();
211   }
212 }
213 
GetPreferredSize() const214 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
215   int width = 0;
216   for (int i = 0; i < child_count(); ++i) {
217     const views::View* child = child_at(i);
218     if (child->visible())
219       width += child_at(i)->GetPreferredSize().width();
220   }
221   return gfx::Size(width, close_button_->GetPreferredSize().height());
222 }
223 
Layout()224 void FrameCaptionButtonContainerView::Layout() {
225   int x = 0;
226   for (int i = 0; i < child_count(); ++i) {
227     views::View* child = child_at(i);
228     if (!child->visible())
229       continue;
230 
231     gfx::Size size = child->GetPreferredSize();
232     child->SetBounds(x, 0, size.width(), size.height());
233     x += size.width();
234   }
235   if (maximize_mode_animation_->is_animating()) {
236     AnimationProgressed(maximize_mode_animation_.get());
237   }
238 }
239 
GetClassName() const240 const char* FrameCaptionButtonContainerView::GetClassName() const {
241   return kViewClassName;
242 }
243 
AnimationEnded(const gfx::Animation * animation)244 void FrameCaptionButtonContainerView::AnimationEnded(
245     const gfx::Animation* animation) {
246   // Ensure that position is calculated at least once.
247   AnimationProgressed(animation);
248 
249   double current_value = maximize_mode_animation_->GetCurrentValue();
250   if (current_value == 0.0) {
251     size_button_->SetVisible(false);
252     PreferredSizeChanged();
253   }
254 }
255 
AnimationProgressed(const gfx::Animation * animation)256 void FrameCaptionButtonContainerView::AnimationProgressed(
257     const gfx::Animation* animation) {
258   double current_value = animation->GetCurrentValue();
259   int size_alpha = 0;
260   int minimize_x = 0;
261   if (maximize_mode_animation_->IsShowing()) {
262     double scaled_value = CapAnimationValue(
263         (current_value - SizeButtonShowStartValue())
264             / SizeButtonShowDuration());
265     double tweened_value_alpha =
266         gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,scaled_value);
267     size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255);
268 
269     double tweened_value_slide =
270         gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value);
271     minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide,
272                                                    size_button_->x(), 0);
273   } else {
274     double scaled_value_alpha = CapAnimationValue(
275         (1.0f - current_value) / SizeButtonHideDuration());
276     double tweened_value_alpha =
277         gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha);
278     size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0);
279 
280     double scaled_value_position = CapAnimationValue(
281         (HidePositionStartValue() - current_value)
282             / HidePositionStartValue());
283     double tweened_value_position =
284         gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position);
285     minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0,
286                                                    size_button_->x());
287   }
288   size_button_->SetAlpha(size_alpha);
289   minimize_button_->SetX(minimize_x);
290 }
291 
SetButtonIcon(FrameCaptionButton * button,CaptionButtonIcon icon,Animate animate)292 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button,
293                                                     CaptionButtonIcon icon,
294                                                     Animate animate) {
295   // The early return is dependant on |animate| because callers use
296   // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation
297   // to the end.
298   if (button->icon() == icon &&
299       (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) {
300     return;
301   }
302 
303   FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
304       FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
305   std::map<CaptionButtonIcon, ButtonIconIds>::const_iterator it =
306       button_icon_id_map_.find(icon);
307   if (it != button_icon_id_map_.end()) {
308     button->SetImages(icon,
309                       fcb_animate,
310                       it->second.icon_image_id,
311                       it->second.inactive_icon_image_id,
312                       it->second.hovered_background_image_id,
313                       it->second.pressed_background_image_id);
314   }
315 }
316 
ShouldSizeButtonBeVisible() const317 bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const {
318   return !Shell::GetInstance()->maximize_mode_controller()->
319       IsMaximizeModeWindowManagerEnabled() &&
320       frame_->widget_delegate()->CanMaximize();
321 }
322 
ButtonPressed(views::Button * sender,const ui::Event & event)323 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
324                                                     const ui::Event& event) {
325   // Abort any animations of the button icons.
326   SetButtonsToNormal(ANIMATE_NO);
327 
328   ash::UserMetricsAction action =
329       ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
330   if (sender == minimize_button_) {
331     frame_->Minimize();
332   } else if (sender == size_button_) {
333     if (frame_->IsFullscreen()) {  // Can be clicked in immersive fullscreen.
334       frame_->Restore();
335       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
336     } else if (frame_->IsMaximized()) {
337       frame_->Restore();
338       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
339     } else {
340       frame_->Maximize();
341       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
342     }
343   } else if (sender == close_button_) {
344     frame_->Close();
345     action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
346   } else {
347     return;
348   }
349   ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action);
350 }
351 
IsMinimizeButtonVisible() const352 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
353   return minimize_button_->visible();
354 }
355 
SetButtonsToNormal(Animate animate)356 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
357   SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
358       animate);
359   minimize_button_->SetState(views::Button::STATE_NORMAL);
360   size_button_->SetState(views::Button::STATE_NORMAL);
361   close_button_->SetState(views::Button::STATE_NORMAL);
362 }
363 
SetButtonIcons(CaptionButtonIcon minimize_button_icon,CaptionButtonIcon close_button_icon,Animate animate)364 void FrameCaptionButtonContainerView::SetButtonIcons(
365     CaptionButtonIcon minimize_button_icon,
366     CaptionButtonIcon close_button_icon,
367     Animate animate) {
368   SetButtonIcon(minimize_button_, minimize_button_icon, animate);
369   SetButtonIcon(close_button_, close_button_icon, animate);
370 }
371 
GetButtonClosestTo(const gfx::Point & position_in_screen) const372 const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo(
373     const gfx::Point& position_in_screen) const {
374   // Since the buttons all have the same size, the closest button is the button
375   // with the center point closest to |position_in_screen|.
376   // TODO(pkotwicz): Make the caption buttons not overlap.
377   gfx::Point position(position_in_screen);
378   views::View::ConvertPointFromScreen(this, &position);
379 
380   FrameCaptionButton* buttons[] = {
381     minimize_button_, size_button_, close_button_
382   };
383   int min_squared_distance = INT_MAX;
384   FrameCaptionButton* closest_button = NULL;
385   for (size_t i = 0; i < arraysize(buttons); ++i) {
386     FrameCaptionButton* button = buttons[i];
387     if (!button->visible())
388       continue;
389 
390     gfx::Point center_point = button->GetLocalBounds().CenterPoint();
391     views::View::ConvertPointToTarget(button, this, &center_point);
392     int squared_distance = static_cast<int>(
393         pow(static_cast<double>(position.x() - center_point.x()), 2) +
394         pow(static_cast<double>(position.y() - center_point.y()), 2));
395     if (squared_distance < min_squared_distance) {
396       min_squared_distance = squared_distance;
397       closest_button = button;
398     }
399   }
400   return closest_button;
401 }
402 
SetHoveredAndPressedButtons(const FrameCaptionButton * to_hover,const FrameCaptionButton * to_press)403 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
404     const FrameCaptionButton* to_hover,
405     const FrameCaptionButton* to_press) {
406   FrameCaptionButton* buttons[] = {
407     minimize_button_, size_button_, close_button_
408   };
409   for (size_t i = 0; i < arraysize(buttons); ++i) {
410     FrameCaptionButton* button = buttons[i];
411     views::Button::ButtonState new_state = views::Button::STATE_NORMAL;
412     if (button == to_hover)
413       new_state = views::Button::STATE_HOVERED;
414     else if (button == to_press)
415       new_state = views::Button::STATE_PRESSED;
416     button->SetState(new_state);
417   }
418 }
419 
ButtonIconIds()420 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
421     : icon_image_id(-1),
422       inactive_icon_image_id(-1),
423       hovered_background_image_id(-1),
424       pressed_background_image_id(-1) {
425 }
426 
ButtonIconIds(int icon_id,int inactive_icon_id,int hovered_background_id,int pressed_background_id)427 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds(
428     int icon_id,
429     int inactive_icon_id,
430     int hovered_background_id,
431     int pressed_background_id)
432     : icon_image_id(icon_id),
433       inactive_icon_image_id(inactive_icon_id),
434       hovered_background_image_id(hovered_background_id),
435       pressed_background_image_id(pressed_background_id) {
436 }
437 
~ButtonIconIds()438 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {
439 }
440 
441 }  // namespace ash
442