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