OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "mash/wm/frame/caption_buttons/frame_caption_button_container_view.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <cmath> | |
10 #include <map> | |
11 | |
12 #include "base/macros.h" | |
13 #include "mash/wm/frame/caption_buttons/frame_caption_button.h" | |
14 #include "ui/base/hit_test.h" | |
15 #include "ui/base/l10n/l10n_util.h" | |
16 #include "ui/gfx/animation/slide_animation.h" | |
17 #include "ui/gfx/animation/tween.h" | |
18 #include "ui/gfx/canvas.h" | |
19 #include "ui/gfx/geometry/insets.h" | |
20 #include "ui/gfx/geometry/point.h" | |
21 #include "ui/strings/grit/ui_strings.h" // Accessibility names | |
22 #include "ui/views/widget/widget.h" | |
23 #include "ui/views/widget/widget_delegate.h" | |
24 | |
25 namespace mash { | |
26 namespace wm { | |
27 | |
28 namespace { | |
29 | |
30 // Duration of the animation of the position of |minimize_button_|. | |
31 const int kPositionAnimationDurationMs = 500; | |
32 | |
33 // Duration of the animation of the alpha of |size_button_|. | |
34 const int kAlphaAnimationDurationMs = 250; | |
35 | |
36 // Delay during |maximize_mode_animation_| hide to wait before beginning to | |
37 // animate the position of |minimize_button_|. | |
38 const int kHidePositionDelayMs = 100; | |
39 | |
40 // Duration of |maximize_mode_animation_| hiding. | |
41 // Hiding size button 250 | |
42 // |------------------------| | |
43 // Delay 100 Slide minimize button 500 | |
44 // |---------|-------------------------------------------------| | |
45 const int kHideAnimationDurationMs = | |
46 kHidePositionDelayMs + kPositionAnimationDurationMs; | |
47 | |
48 // Delay during |maximize_mode_animation_| show to wait before beginning to | |
49 // animate the alpha of |size_button_|. | |
50 const int kShowAnimationAlphaDelayMs = 100; | |
51 | |
52 // Duration of |maximize_mode_animation_| showing. | |
53 // Slide minimize button 500 | |
54 // |-------------------------------------------------| | |
55 // Delay 100 Show size button 250 | |
56 // |---------|-----------------------| | |
57 const int kShowAnimationDurationMs = kPositionAnimationDurationMs; | |
58 | |
59 // Value of |maximize_mode_animation_| showing to begin animating alpha of | |
60 // |size_button_|. | |
61 float SizeButtonShowStartValue() { | |
62 return static_cast<float>(kShowAnimationAlphaDelayMs) / | |
63 kShowAnimationDurationMs; | |
64 } | |
65 | |
66 // Amount of |maximize_mode_animation_| showing to animate the alpha of | |
67 // |size_button_|. | |
68 float SizeButtonShowDuration() { | |
69 return static_cast<float>(kAlphaAnimationDurationMs) / | |
70 kShowAnimationDurationMs; | |
71 } | |
72 | |
73 // Amount of |maximize_mode_animation_| hiding to animate the alpha of | |
74 // |size_button_|. | |
75 float SizeButtonHideDuration() { | |
76 return static_cast<float>(kAlphaAnimationDurationMs) / | |
77 kHideAnimationDurationMs; | |
78 } | |
79 | |
80 // Value of |maximize_mode_animation_| hiding to begin animating the position of | |
81 // |minimize_button_|. | |
82 float HidePositionStartValue() { | |
83 return 1.0f - | |
84 static_cast<float>(kHidePositionDelayMs) / kHideAnimationDurationMs; | |
85 } | |
86 | |
87 // Converts |point| from |src| to |dst| and hittests against |dst|. | |
88 bool ConvertPointToViewAndHitTest(const views::View* src, | |
89 const views::View* dst, | |
90 const gfx::Point& point) { | |
91 gfx::Point converted(point); | |
92 views::View::ConvertPointToTarget(src, dst, &converted); | |
93 return dst->HitTestPoint(converted); | |
94 } | |
95 | |
96 // Bounds animation values to the range 0.0 - 1.0. Allows for mapping of offset | |
97 // animations to the expected range so that gfx::Tween::CalculateValue() can be | |
98 // used. | |
99 double CapAnimationValue(double value) { | |
100 return std::min(1.0, std::max(0.0, value)); | |
101 } | |
102 | |
103 } // namespace | |
104 | |
105 // static | |
106 const char FrameCaptionButtonContainerView::kViewClassName[] = | |
107 "FrameCaptionButtonContainerView"; | |
108 | |
109 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( | |
110 views::Widget* frame) | |
111 : frame_(frame), | |
112 minimize_button_(NULL), | |
113 size_button_(NULL), | |
114 close_button_(NULL) { | |
115 const bool size_button_visibility = ShouldSizeButtonBeVisible(); | |
116 maximize_mode_animation_.reset(new gfx::SlideAnimation(this)); | |
117 maximize_mode_animation_->SetTweenType(gfx::Tween::LINEAR); | |
118 | |
119 // Ensure animation tracks visibility of size button. | |
120 if (size_button_visibility) | |
121 maximize_mode_animation_->Reset(1.0f); | |
122 | |
123 // Insert the buttons left to right. | |
124 minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); | |
125 minimize_button_->SetAccessibleName( | |
126 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); | |
127 minimize_button_->SetVisible(frame_->widget_delegate()->CanMinimize()); | |
128 AddChildView(minimize_button_); | |
129 | |
130 size_button_ = | |
131 new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE); | |
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::SetButtonImages( | |
146 CaptionButtonIcon icon, | |
147 int icon_image_id, | |
148 int hovered_background_image_id, | |
149 int pressed_background_image_id) { | |
150 button_icon_id_map_[icon] = ButtonIconIds( | |
151 icon_image_id, hovered_background_image_id, pressed_background_image_id); | |
152 FrameCaptionButton* buttons[] = {minimize_button_, size_button_, | |
153 close_button_}; | |
154 for (size_t i = 0; i < arraysize(buttons); ++i) { | |
155 if (buttons[i]->icon() == icon) { | |
156 buttons[i]->SetImages(icon, FrameCaptionButton::ANIMATE_NO, icon_image_id, | |
157 hovered_background_image_id, | |
158 pressed_background_image_id); | |
159 } | |
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::ResetWindowControls() { | |
170 SetButtonsToNormal(ANIMATE_NO); | |
171 } | |
172 | |
173 int FrameCaptionButtonContainerView::NonClientHitTest( | |
174 const gfx::Point& point) const { | |
175 if (close_button_->visible() && | |
176 ConvertPointToViewAndHitTest(this, close_button_, point)) { | |
177 return HTCLOSE; | |
178 } else if (size_button_->visible() && | |
179 ConvertPointToViewAndHitTest(this, size_button_, point)) { | |
180 return HTMAXBUTTON; | |
181 } else if (minimize_button_->visible() && | |
182 ConvertPointToViewAndHitTest(this, minimize_button_, point)) { | |
183 return HTMINBUTTON; | |
184 } | |
185 return HTNOWHERE; | |
186 } | |
187 | |
188 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() { | |
189 bool visible = ShouldSizeButtonBeVisible(); | |
190 if (visible) { | |
191 size_button_->SetVisible(true); | |
192 maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs); | |
193 maximize_mode_animation_->Show(); | |
194 } else { | |
195 maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs); | |
196 maximize_mode_animation_->Hide(); | |
197 } | |
198 } | |
199 | |
200 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const { | |
201 int width = 0; | |
202 for (int i = 0; i < child_count(); ++i) { | |
203 const views::View* child = child_at(i); | |
204 if (child->visible()) | |
205 width += child_at(i)->GetPreferredSize().width(); | |
206 } | |
207 return gfx::Size(width, close_button_->GetPreferredSize().height()); | |
208 } | |
209 | |
210 void FrameCaptionButtonContainerView::Layout() { | |
211 int x = 0; | |
212 for (int i = 0; i < child_count(); ++i) { | |
213 views::View* child = child_at(i); | |
214 if (!child->visible()) | |
215 continue; | |
216 | |
217 gfx::Size size = child->GetPreferredSize(); | |
218 child->SetBounds(x, 0, size.width(), size.height()); | |
219 x += size.width(); | |
220 } | |
221 if (maximize_mode_animation_->is_animating()) { | |
222 AnimationProgressed(maximize_mode_animation_.get()); | |
223 } | |
224 } | |
225 | |
226 const char* FrameCaptionButtonContainerView::GetClassName() const { | |
227 return kViewClassName; | |
228 } | |
229 | |
230 void FrameCaptionButtonContainerView::AnimationEnded( | |
231 const gfx::Animation* animation) { | |
232 // Ensure that position is calculated at least once. | |
233 AnimationProgressed(animation); | |
234 | |
235 double current_value = maximize_mode_animation_->GetCurrentValue(); | |
236 if (current_value == 0.0) { | |
237 size_button_->SetVisible(false); | |
238 PreferredSizeChanged(); | |
239 } | |
240 } | |
241 | |
242 void FrameCaptionButtonContainerView::AnimationProgressed( | |
243 const gfx::Animation* animation) { | |
244 double current_value = animation->GetCurrentValue(); | |
245 int size_alpha = 0; | |
246 int minimize_x = 0; | |
247 if (maximize_mode_animation_->IsShowing()) { | |
248 double scaled_value = | |
249 CapAnimationValue((current_value - SizeButtonShowStartValue()) / | |
250 SizeButtonShowDuration()); | |
251 double tweened_value_alpha = | |
252 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value); | |
253 size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255); | |
254 | |
255 double tweened_value_slide = | |
256 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value); | |
257 minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide, | |
258 size_button_->x(), 0); | |
259 } else { | |
260 double scaled_value_alpha = | |
261 CapAnimationValue((1.0f - current_value) / SizeButtonHideDuration()); | |
262 double tweened_value_alpha = | |
263 gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha); | |
264 size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0); | |
265 | |
266 double scaled_value_position = CapAnimationValue( | |
267 (HidePositionStartValue() - current_value) / HidePositionStartValue()); | |
268 double tweened_value_position = | |
269 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position); | |
270 minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0, | |
271 size_button_->x()); | |
272 } | |
273 size_button_->SetAlpha(size_alpha); | |
274 minimize_button_->SetX(minimize_x); | |
275 } | |
276 | |
277 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, | |
278 CaptionButtonIcon icon, | |
279 Animate animate) { | |
280 // The early return is dependant on |animate| because callers use | |
281 // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation | |
282 // to the end. | |
283 if (button->icon() == icon && | |
284 (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) { | |
285 return; | |
286 } | |
287 | |
288 FrameCaptionButton::Animate fcb_animate = | |
289 (animate == ANIMATE_YES) ? FrameCaptionButton::ANIMATE_YES | |
290 : FrameCaptionButton::ANIMATE_NO; | |
291 std::map<CaptionButtonIcon, ButtonIconIds>::const_iterator it = | |
292 button_icon_id_map_.find(icon); | |
293 if (it != button_icon_id_map_.end()) { | |
294 button->SetImages(icon, fcb_animate, it->second.icon_image_id, | |
295 it->second.hovered_background_image_id, | |
296 it->second.pressed_background_image_id); | |
297 } | |
298 } | |
299 | |
300 bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const { | |
301 return frame_->widget_delegate()->CanMaximize(); | |
302 } | |
303 | |
304 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) { | |
305 SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE, | |
306 animate); | |
307 minimize_button_->SetState(views::Button::STATE_NORMAL); | |
308 size_button_->SetState(views::Button::STATE_NORMAL); | |
309 close_button_->SetState(views::Button::STATE_NORMAL); | |
310 } | |
311 | |
312 void FrameCaptionButtonContainerView::SetButtonIcons( | |
313 CaptionButtonIcon minimize_button_icon, | |
314 CaptionButtonIcon close_button_icon, | |
315 Animate animate) { | |
316 SetButtonIcon(minimize_button_, minimize_button_icon, animate); | |
317 SetButtonIcon(close_button_, close_button_icon, animate); | |
318 } | |
319 | |
320 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, | |
321 const ui::Event& event) { | |
322 // Abort any animations of the button icons. | |
323 SetButtonsToNormal(ANIMATE_NO); | |
324 | |
325 if (sender == minimize_button_) { | |
326 frame_->Minimize(); | |
327 } else if (sender == size_button_) { | |
328 if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. | |
329 frame_->Restore(); | |
330 } else if (frame_->IsMaximized()) { | |
331 frame_->Restore(); | |
332 } else { | |
333 frame_->Maximize(); | |
334 } | |
335 } else if (sender == close_button_) { | |
336 frame_->Close(); | |
337 } | |
338 } | |
339 | |
340 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() | |
341 : icon_image_id(-1), | |
342 hovered_background_image_id(-1), | |
343 pressed_background_image_id(-1) {} | |
344 | |
345 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds( | |
346 int icon_id, | |
347 int hovered_background_id, | |
348 int pressed_background_id) | |
349 : icon_image_id(icon_id), | |
350 hovered_background_image_id(hovered_background_id), | |
351 pressed_background_image_id(pressed_background_id) {} | |
352 | |
353 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {} | |
354 | |
355 } // namespace wm | |
356 } // namespace mash | |
OLD | NEW |