Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / ash / frame / caption_buttons / frame_caption_button_container_view.cc
blob78c5a38ba40fad3fde1f51241a0d95e4ccb558bb
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.
5 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
7 #include <cmath>
8 #include <map>
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/touch/touch_uma.h"
16 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
17 #include "ui/base/hit_test.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
20 #include "ui/gfx/animation/slide_animation.h"
21 #include "ui/gfx/animation/tween.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/geometry/insets.h"
24 #include "ui/gfx/geometry/point.h"
25 #include "ui/strings/grit/ui_strings.h" // Accessibility names
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/widget/widget_delegate.h"
29 namespace ash {
31 namespace {
33 // Duration of the animation of the position of |minimize_button_|.
34 const int kPositionAnimationDurationMs = 500;
36 // Duration of the animation of the alpha of |size_button_|.
37 const int kAlphaAnimationDurationMs = 250;
39 // Delay during |maximize_mode_animation_| hide to wait before beginning to
40 // animate the position of |minimize_button_|.
41 const int kHidePositionDelayMs = 100;
43 // Duration of |maximize_mode_animation_| hiding.
44 // Hiding size button 250
45 // |------------------------|
46 // Delay 100 Slide minimize button 500
47 // |---------|-------------------------------------------------|
48 const int kHideAnimationDurationMs =
49 kHidePositionDelayMs + kPositionAnimationDurationMs;
51 // Delay during |maximize_mode_animation_| show to wait before beginning to
52 // animate the alpha of |size_button_|.
53 const int kShowAnimationAlphaDelayMs = 100;
55 // Duration of |maximize_mode_animation_| showing.
56 // Slide minimize button 500
57 // |-------------------------------------------------|
58 // Delay 100 Show size button 250
59 // |---------|-----------------------|
60 const int kShowAnimationDurationMs = kPositionAnimationDurationMs;
62 // Value of |maximize_mode_animation_| showing to begin animating alpha of
63 // |size_button_|.
64 float SizeButtonShowStartValue() {
65 return static_cast<float>(kShowAnimationAlphaDelayMs)
66 / kShowAnimationDurationMs;
69 // Amount of |maximize_mode_animation_| showing to animate the alpha of
70 // |size_button_|.
71 float SizeButtonShowDuration() {
72 return static_cast<float>(kAlphaAnimationDurationMs)
73 / kShowAnimationDurationMs;
76 // Amount of |maximize_mode_animation_| hiding to animate the alpha of
77 // |size_button_|.
78 float SizeButtonHideDuration() {
79 return static_cast<float>(kAlphaAnimationDurationMs)
80 / kHideAnimationDurationMs;
83 // Value of |maximize_mode_animation_| hiding to begin animating the position of
84 // |minimize_button_|.
85 float HidePositionStartValue() {
86 return 1.0f - static_cast<float>(kHidePositionDelayMs)
87 / kHideAnimationDurationMs;
90 // Converts |point| from |src| to |dst| and hittests against |dst|.
91 bool ConvertPointToViewAndHitTest(const views::View* src,
92 const views::View* dst,
93 const gfx::Point& point) {
94 gfx::Point converted(point);
95 views::View::ConvertPointToTarget(src, dst, &converted);
96 return dst->HitTestPoint(converted);
99 // Bounds animation values to the range 0.0 - 1.0. Allows for mapping of offset
100 // animations to the expected range so that gfx::Tween::CalculateValue() can be
101 // used.
102 double CapAnimationValue(double value) {
103 return std::min(1.0, std::max(0.0, value));
106 } // namespace
108 // static
109 const char FrameCaptionButtonContainerView::kViewClassName[] =
110 "FrameCaptionButtonContainerView";
112 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
113 views::Widget* frame)
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);
122 // Ensure animation tracks visibility of size button.
123 if (size_button_visibility)
124 maximize_mode_animation_->Reset(1.0f);
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(frame_->widget_delegate()->CanMinimize());
131 AddChildView(minimize_button_);
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_);
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_);
145 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {
148 void FrameCaptionButtonContainerView::TestApi::EndAnimations() {
149 container_view_->maximize_mode_animation_->End();
152 void FrameCaptionButtonContainerView::SetButtonImages(
153 CaptionButtonIcon icon,
154 int 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 hovered_background_image_id,
159 pressed_background_image_id);
160 FrameCaptionButton* buttons[] = {
161 minimize_button_, size_button_, close_button_
163 for (size_t i = 0; i < arraysize(buttons); ++i) {
164 if (buttons[i]->icon() == icon) {
165 buttons[i]->SetImages(icon,
166 FrameCaptionButton::ANIMATE_NO,
167 icon_image_id,
168 hovered_background_image_id,
169 pressed_background_image_id);
174 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
175 minimize_button_->set_paint_as_active(paint_as_active);
176 size_button_->set_paint_as_active(paint_as_active);
177 close_button_->set_paint_as_active(paint_as_active);
180 void FrameCaptionButtonContainerView::ResetWindowControls() {
181 SetButtonsToNormal(ANIMATE_NO);
184 int FrameCaptionButtonContainerView::NonClientHitTest(
185 const gfx::Point& point) const {
186 if (close_button_->visible() &&
187 ConvertPointToViewAndHitTest(this, close_button_, point)) {
188 return HTCLOSE;
189 } else if (size_button_->visible() &&
190 ConvertPointToViewAndHitTest(this, size_button_, point)) {
191 return HTMAXBUTTON;
192 } else if (minimize_button_->visible() &&
193 ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
194 return HTMINBUTTON;
196 return HTNOWHERE;
199 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() {
200 bool visible = ShouldSizeButtonBeVisible();
201 if (visible) {
202 size_button_->SetVisible(true);
203 maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs);
204 maximize_mode_animation_->Show();
205 } else {
206 maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs);
207 maximize_mode_animation_->Hide();
211 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
212 int width = 0;
213 for (int i = 0; i < child_count(); ++i) {
214 const views::View* child = child_at(i);
215 if (child->visible())
216 width += child_at(i)->GetPreferredSize().width();
218 return gfx::Size(width, close_button_->GetPreferredSize().height());
221 void FrameCaptionButtonContainerView::Layout() {
222 int x = 0;
223 for (int i = 0; i < child_count(); ++i) {
224 views::View* child = child_at(i);
225 if (!child->visible())
226 continue;
228 gfx::Size size = child->GetPreferredSize();
229 child->SetBounds(x, 0, size.width(), size.height());
230 x += size.width();
232 if (maximize_mode_animation_->is_animating()) {
233 AnimationProgressed(maximize_mode_animation_.get());
237 const char* FrameCaptionButtonContainerView::GetClassName() const {
238 return kViewClassName;
241 void FrameCaptionButtonContainerView::AnimationEnded(
242 const gfx::Animation* animation) {
243 // Ensure that position is calculated at least once.
244 AnimationProgressed(animation);
246 double current_value = maximize_mode_animation_->GetCurrentValue();
247 if (current_value == 0.0) {
248 size_button_->SetVisible(false);
249 PreferredSizeChanged();
253 void FrameCaptionButtonContainerView::AnimationProgressed(
254 const gfx::Animation* animation) {
255 double current_value = animation->GetCurrentValue();
256 int size_alpha = 0;
257 int minimize_x = 0;
258 if (maximize_mode_animation_->IsShowing()) {
259 double scaled_value = CapAnimationValue(
260 (current_value - SizeButtonShowStartValue())
261 / SizeButtonShowDuration());
262 double tweened_value_alpha =
263 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,scaled_value);
264 size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255);
266 double tweened_value_slide =
267 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value);
268 minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide,
269 size_button_->x(), 0);
270 } else {
271 double scaled_value_alpha = CapAnimationValue(
272 (1.0f - current_value) / SizeButtonHideDuration());
273 double tweened_value_alpha =
274 gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha);
275 size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0);
277 double scaled_value_position = CapAnimationValue(
278 (HidePositionStartValue() - current_value)
279 / HidePositionStartValue());
280 double tweened_value_position =
281 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position);
282 minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0,
283 size_button_->x());
285 size_button_->SetAlpha(size_alpha);
286 minimize_button_->SetX(minimize_x);
289 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button,
290 CaptionButtonIcon icon,
291 Animate animate) {
292 // The early return is dependant on |animate| because callers use
293 // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation
294 // to the end.
295 if (button->icon() == icon &&
296 (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) {
297 return;
300 FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
301 FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
302 std::map<CaptionButtonIcon, ButtonIconIds>::const_iterator it =
303 button_icon_id_map_.find(icon);
304 if (it != button_icon_id_map_.end()) {
305 button->SetImages(icon,
306 fcb_animate,
307 it->second.icon_image_id,
308 it->second.hovered_background_image_id,
309 it->second.pressed_background_image_id);
313 bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const {
314 return !Shell::GetInstance()->maximize_mode_controller()->
315 IsMaximizeModeWindowManagerEnabled() &&
316 frame_->widget_delegate()->CanMaximize();
319 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
320 const ui::Event& event) {
321 // Abort any animations of the button icons.
322 SetButtonsToNormal(ANIMATE_NO);
324 ash::UserMetricsAction action =
325 ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
326 if (sender == minimize_button_) {
327 frame_->Minimize();
328 } else if (sender == size_button_) {
329 if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen.
330 frame_->Restore();
331 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
332 } else if (frame_->IsMaximized()) {
333 frame_->Restore();
334 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
335 } else {
336 frame_->Maximize();
337 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
340 if (event.IsGestureEvent()) {
341 TouchUMA::GetInstance()->RecordGestureAction(
342 TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
344 } else if (sender == close_button_) {
345 frame_->Close();
346 action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
347 } else {
348 return;
350 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action);
353 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
354 return minimize_button_->visible();
357 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
358 SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
359 animate);
360 minimize_button_->SetState(views::Button::STATE_NORMAL);
361 size_button_->SetState(views::Button::STATE_NORMAL);
362 close_button_->SetState(views::Button::STATE_NORMAL);
365 void FrameCaptionButtonContainerView::SetButtonIcons(
366 CaptionButtonIcon minimize_button_icon,
367 CaptionButtonIcon close_button_icon,
368 Animate animate) {
369 SetButtonIcon(minimize_button_, minimize_button_icon, animate);
370 SetButtonIcon(close_button_, close_button_icon, animate);
373 const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo(
374 const gfx::Point& position_in_screen) const {
375 // Since the buttons all have the same size, the closest button is the button
376 // with the center point closest to |position_in_screen|.
377 // TODO(pkotwicz): Make the caption buttons not overlap.
378 gfx::Point position(position_in_screen);
379 views::View::ConvertPointFromScreen(this, &position);
381 FrameCaptionButton* buttons[] = {
382 minimize_button_, size_button_, close_button_
384 int min_squared_distance = INT_MAX;
385 FrameCaptionButton* closest_button = NULL;
386 for (size_t i = 0; i < arraysize(buttons); ++i) {
387 FrameCaptionButton* button = buttons[i];
388 if (!button->visible())
389 continue;
391 gfx::Point center_point = button->GetLocalBounds().CenterPoint();
392 views::View::ConvertPointToTarget(button, this, &center_point);
393 int squared_distance = static_cast<int>(
394 pow(static_cast<double>(position.x() - center_point.x()), 2) +
395 pow(static_cast<double>(position.y() - center_point.y()), 2));
396 if (squared_distance < min_squared_distance) {
397 min_squared_distance = squared_distance;
398 closest_button = button;
401 return closest_button;
404 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
405 const FrameCaptionButton* to_hover,
406 const FrameCaptionButton* to_press) {
407 FrameCaptionButton* buttons[] = {
408 minimize_button_, size_button_, close_button_
410 for (size_t i = 0; i < arraysize(buttons); ++i) {
411 FrameCaptionButton* button = buttons[i];
412 views::Button::ButtonState new_state = views::Button::STATE_NORMAL;
413 if (button == to_hover)
414 new_state = views::Button::STATE_HOVERED;
415 else if (button == to_press)
416 new_state = views::Button::STATE_PRESSED;
417 button->SetState(new_state);
421 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
422 : icon_image_id(-1),
423 hovered_background_image_id(-1),
424 pressed_background_image_id(-1) {
427 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds(
428 int icon_id,
429 int hovered_background_id,
430 int pressed_background_id)
431 : icon_image_id(icon_id),
432 hovered_background_image_id(hovered_background_id),
433 pressed_background_image_id(pressed_background_id) {
436 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {
439 } // namespace ash