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