Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ash / frame / caption_buttons / frame_caption_button_container_view.cc
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_|.
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_|.
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_|.
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_|.
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|.
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.
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
111 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
112     views::Widget* frame)
113     : frame_(frame),
114       minimize_button_(NULL),
115       size_button_(NULL),
116       close_button_(NULL) {
117   bool size_button_visibility = ShouldSizeButtonBeVisible();
118   maximize_mode_animation_.reset(new gfx::SlideAnimation(this));
119   maximize_mode_animation_->SetTweenType(gfx::Tween::LINEAR);
120
121   // Ensure animation tracks visibility of size button.
122   if (size_button_visibility)
123     maximize_mode_animation_->Reset(1.0f);
124
125   // Insert the buttons left to right.
126   minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
127   minimize_button_->SetAccessibleName(
128       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
129   minimize_button_->SetVisible(frame_->widget_delegate()->CanMinimize());
130   AddChildView(minimize_button_);
131
132   size_button_ = new FrameSizeButton(this, frame, this);
133   size_button_->SetAccessibleName(
134       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
135   size_button_->SetVisible(size_button_visibility);
136   AddChildView(size_button_);
137
138   close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
139   close_button_->SetAccessibleName(
140       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
141   AddChildView(close_button_);
142 }
143
144 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {
145 }
146
147 void FrameCaptionButtonContainerView::TestApi::EndAnimations() {
148   container_view_->maximize_mode_animation_->End();
149 }
150
151 void FrameCaptionButtonContainerView::SetButtonImages(
152     CaptionButtonIcon icon,
153     int icon_image_id,
154     int inactive_icon_image_id,
155     int hovered_background_image_id,
156     int pressed_background_image_id) {
157   button_icon_id_map_[icon] = ButtonIconIds(icon_image_id,
158                                             inactive_icon_image_id,
159                                             hovered_background_image_id,
160                                             pressed_background_image_id);
161   FrameCaptionButton* buttons[] = {
162     minimize_button_, size_button_, close_button_
163   };
164   for (size_t i = 0; i < arraysize(buttons); ++i) {
165     if (buttons[i]->icon() == icon) {
166       buttons[i]->SetImages(icon,
167                             FrameCaptionButton::ANIMATE_NO,
168                             icon_image_id,
169                             inactive_icon_image_id,
170                             hovered_background_image_id,
171                             pressed_background_image_id);
172     }
173   }
174 }
175
176 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
177   minimize_button_->set_paint_as_active(paint_as_active);
178   size_button_->set_paint_as_active(paint_as_active);
179   close_button_->set_paint_as_active(paint_as_active);
180 }
181
182 void FrameCaptionButtonContainerView::ResetWindowControls() {
183   SetButtonsToNormal(ANIMATE_NO);
184 }
185
186 int FrameCaptionButtonContainerView::NonClientHitTest(
187     const gfx::Point& point) const {
188   if (close_button_->visible() &&
189       ConvertPointToViewAndHitTest(this, close_button_, point)) {
190     return HTCLOSE;
191   } else if (size_button_->visible() &&
192              ConvertPointToViewAndHitTest(this, size_button_, point)) {
193     return HTMAXBUTTON;
194   } else if (minimize_button_->visible() &&
195              ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
196     return HTMINBUTTON;
197   }
198   return HTNOWHERE;
199 }
200
201 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() {
202   bool visible = ShouldSizeButtonBeVisible();
203   if (visible) {
204     size_button_->SetVisible(true);
205     maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs);
206     maximize_mode_animation_->Show();
207   } else {
208     maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs);
209     maximize_mode_animation_->Hide();
210   }
211 }
212
213 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
214   int width = 0;
215   for (int i = 0; i < child_count(); ++i) {
216     const views::View* child = child_at(i);
217     if (child->visible())
218       width += child_at(i)->GetPreferredSize().width();
219   }
220   return gfx::Size(width, close_button_->GetPreferredSize().height());
221 }
222
223 void FrameCaptionButtonContainerView::Layout() {
224   int x = 0;
225   for (int i = 0; i < child_count(); ++i) {
226     views::View* child = child_at(i);
227     if (!child->visible())
228       continue;
229
230     gfx::Size size = child->GetPreferredSize();
231     child->SetBounds(x, 0, size.width(), size.height());
232     x += size.width();
233   }
234   if (maximize_mode_animation_->is_animating()) {
235     AnimationProgressed(maximize_mode_animation_.get());
236   }
237 }
238
239 const char* FrameCaptionButtonContainerView::GetClassName() const {
240   return kViewClassName;
241 }
242
243 void FrameCaptionButtonContainerView::AnimationEnded(
244     const gfx::Animation* animation) {
245   // Ensure that position is calculated at least once.
246   AnimationProgressed(animation);
247
248   double current_value = maximize_mode_animation_->GetCurrentValue();
249   if (current_value == 0.0) {
250     size_button_->SetVisible(false);
251     PreferredSizeChanged();
252   }
253 }
254
255 void FrameCaptionButtonContainerView::AnimationProgressed(
256     const gfx::Animation* animation) {
257   double current_value = animation->GetCurrentValue();
258   int size_alpha = 0;
259   int minimize_x = 0;
260   if (maximize_mode_animation_->IsShowing()) {
261     double scaled_value = CapAnimationValue(
262         (current_value - SizeButtonShowStartValue())
263             / SizeButtonShowDuration());
264     double tweened_value_alpha =
265         gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,scaled_value);
266     size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255);
267
268     double tweened_value_slide =
269         gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value);
270     minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide,
271                                                    size_button_->x(), 0);
272   } else {
273     double scaled_value_alpha = CapAnimationValue(
274         (1.0f - current_value) / SizeButtonHideDuration());
275     double tweened_value_alpha =
276         gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha);
277     size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0);
278
279     double scaled_value_position = CapAnimationValue(
280         (HidePositionStartValue() - current_value)
281             / HidePositionStartValue());
282     double tweened_value_position =
283         gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position);
284     minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0,
285                                                    size_button_->x());
286   }
287   size_button_->SetAlpha(size_alpha);
288   minimize_button_->SetX(minimize_x);
289 }
290
291 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button,
292                                                     CaptionButtonIcon icon,
293                                                     Animate animate) {
294   // The early return is dependant on |animate| because callers use
295   // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation
296   // to the end.
297   if (button->icon() == icon &&
298       (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) {
299     return;
300   }
301
302   FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
303       FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
304   std::map<CaptionButtonIcon, ButtonIconIds>::const_iterator it =
305       button_icon_id_map_.find(icon);
306   if (it != button_icon_id_map_.end()) {
307     button->SetImages(icon,
308                       fcb_animate,
309                       it->second.icon_image_id,
310                       it->second.inactive_icon_image_id,
311                       it->second.hovered_background_image_id,
312                       it->second.pressed_background_image_id);
313   }
314 }
315
316 bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const {
317   return !Shell::GetInstance()->maximize_mode_controller()->
318       IsMaximizeModeWindowManagerEnabled() &&
319       frame_->widget_delegate()->CanMaximize();
320 }
321
322 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
323                                                     const ui::Event& event) {
324   // Abort any animations of the button icons.
325   SetButtonsToNormal(ANIMATE_NO);
326
327   ash::UserMetricsAction action =
328       ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
329   if (sender == minimize_button_) {
330     frame_->Minimize();
331   } else if (sender == size_button_) {
332     if (frame_->IsFullscreen()) {  // Can be clicked in immersive fullscreen.
333       frame_->Restore();
334       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
335     } else if (frame_->IsMaximized()) {
336       frame_->Restore();
337       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
338     } else {
339       frame_->Maximize();
340       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
341     }
342   } else if (sender == close_button_) {
343     frame_->Close();
344     action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
345   } else {
346     return;
347   }
348   ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action);
349 }
350
351 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
352   return minimize_button_->visible();
353 }
354
355 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
356   SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
357       animate);
358   minimize_button_->SetState(views::Button::STATE_NORMAL);
359   size_button_->SetState(views::Button::STATE_NORMAL);
360   close_button_->SetState(views::Button::STATE_NORMAL);
361 }
362
363 void FrameCaptionButtonContainerView::SetButtonIcons(
364     CaptionButtonIcon minimize_button_icon,
365     CaptionButtonIcon close_button_icon,
366     Animate animate) {
367   SetButtonIcon(minimize_button_, minimize_button_icon, animate);
368   SetButtonIcon(close_button_, close_button_icon, animate);
369 }
370
371 const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo(
372     const gfx::Point& position_in_screen) const {
373   // Since the buttons all have the same size, the closest button is the button
374   // with the center point closest to |position_in_screen|.
375   // TODO(pkotwicz): Make the caption buttons not overlap.
376   gfx::Point position(position_in_screen);
377   views::View::ConvertPointFromScreen(this, &position);
378
379   FrameCaptionButton* buttons[] = {
380     minimize_button_, size_button_, close_button_
381   };
382   int min_squared_distance = INT_MAX;
383   FrameCaptionButton* closest_button = NULL;
384   for (size_t i = 0; i < arraysize(buttons); ++i) {
385     FrameCaptionButton* button = buttons[i];
386     if (!button->visible())
387       continue;
388
389     gfx::Point center_point = button->GetLocalBounds().CenterPoint();
390     views::View::ConvertPointToTarget(button, this, &center_point);
391     int squared_distance = static_cast<int>(
392         pow(static_cast<double>(position.x() - center_point.x()), 2) +
393         pow(static_cast<double>(position.y() - center_point.y()), 2));
394     if (squared_distance < min_squared_distance) {
395       min_squared_distance = squared_distance;
396       closest_button = button;
397     }
398   }
399   return closest_button;
400 }
401
402 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
403     const FrameCaptionButton* to_hover,
404     const FrameCaptionButton* to_press) {
405   FrameCaptionButton* buttons[] = {
406     minimize_button_, size_button_, close_button_
407   };
408   for (size_t i = 0; i < arraysize(buttons); ++i) {
409     FrameCaptionButton* button = buttons[i];
410     views::Button::ButtonState new_state = views::Button::STATE_NORMAL;
411     if (button == to_hover)
412       new_state = views::Button::STATE_HOVERED;
413     else if (button == to_press)
414       new_state = views::Button::STATE_PRESSED;
415     button->SetState(new_state);
416   }
417 }
418
419 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
420     : icon_image_id(-1),
421       inactive_icon_image_id(-1),
422       hovered_background_image_id(-1),
423       pressed_background_image_id(-1) {
424 }
425
426 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds(
427     int icon_id,
428     int inactive_icon_id,
429     int hovered_background_id,
430     int pressed_background_id)
431     : icon_image_id(icon_id),
432       inactive_icon_image_id(inactive_icon_id),
433       hovered_background_image_id(hovered_background_id),
434       pressed_background_image_id(pressed_background_id) {
435 }
436
437 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {
438 }
439
440 }  // namespace ash