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.h"
7 #include "ui/base/resource/resource_bundle.h"
8 #include "ui/gfx/animation/slide_animation.h"
9 #include "ui/gfx/animation/throb_animation.h"
10 #include "ui/gfx/canvas.h"
16 // The duration of the crossfade animation when swapping the button's images.
17 const int kSwapImagesAnimationDurationMs
= 200;
19 // The duration of the fade out animation of the old icon during a crossfade
20 // animation as a ratio of |kSwapImagesAnimationDurationMs|.
21 const float kFadeOutRatio
= 0.5f
;
26 const char FrameCaptionButton::kViewClassName
[] = "FrameCaptionButton";
28 FrameCaptionButton::FrameCaptionButton(views::ButtonListener
* listener
,
29 CaptionButtonIcon icon
)
30 : CustomButton(listener
),
32 paint_as_active_(false),
35 inactive_icon_image_id_(-1),
36 hovered_background_image_id_(-1),
37 pressed_background_image_id_(-1),
38 swap_images_animation_(new gfx::SlideAnimation(this)) {
39 swap_images_animation_
->Reset(1);
41 // Do not flip the gfx::Canvas passed to the OnPaint() method. The snap left
42 // and snap right button icons should not be flipped. The other icons are
43 // horizontally symmetrical.
46 FrameCaptionButton::~FrameCaptionButton() {
49 void FrameCaptionButton::SetImages(CaptionButtonIcon icon
,
52 int inactive_icon_image_id
,
53 int hovered_background_image_id
,
54 int pressed_background_image_id
) {
55 // The early return is dependant on |animate| because callers use SetImages()
56 // with ANIMATE_NO to progress the crossfade animation to the end.
58 (animate
== ANIMATE_YES
|| !swap_images_animation_
->is_animating()) &&
59 icon_image_id
== icon_image_id_
&&
60 inactive_icon_image_id
== inactive_icon_image_id_
&&
61 hovered_background_image_id
== hovered_background_image_id_
&&
62 pressed_background_image_id
== pressed_background_image_id_
) {
66 if (animate
== ANIMATE_YES
)
67 crossfade_icon_image_
= GetIconImageToPaint();
70 icon_image_id_
= icon_image_id
;
71 inactive_icon_image_id_
= inactive_icon_image_id
;
72 hovered_background_image_id_
= hovered_background_image_id
;
73 pressed_background_image_id_
= pressed_background_image_id
;
75 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
76 icon_image_
= *rb
.GetImageSkiaNamed(icon_image_id
);
77 inactive_icon_image_
= *rb
.GetImageSkiaNamed(inactive_icon_image_id
);
78 hovered_background_image_
= *rb
.GetImageSkiaNamed(
79 hovered_background_image_id
);
80 pressed_background_image_
= *rb
.GetImageSkiaNamed(
81 pressed_background_image_id
);
83 if (animate
== ANIMATE_YES
) {
84 swap_images_animation_
->Reset(0);
85 swap_images_animation_
->SetSlideDuration(kSwapImagesAnimationDurationMs
);
86 swap_images_animation_
->Show();
88 swap_images_animation_
->Reset(1);
90 PreferredSizeChanged();
94 bool FrameCaptionButton::IsAnimatingImageSwap() const {
95 return swap_images_animation_
->is_animating();
98 void FrameCaptionButton::SetAlpha(int alpha
) {
99 if (alpha_
!= alpha
) {
105 gfx::Size
FrameCaptionButton::GetPreferredSize() const {
106 return hovered_background_image_
.isNull() ?
107 gfx::Size() : hovered_background_image_
.size();
110 const char* FrameCaptionButton::GetClassName() const {
111 return kViewClassName
;
114 void FrameCaptionButton::OnPaint(gfx::Canvas
* canvas
) {
115 if (hover_animation_
->is_animating() || state() == STATE_HOVERED
) {
116 int hovered_background_alpha
= hover_animation_
->is_animating() ?
117 hover_animation_
->CurrentValueBetween(0, 255) : 255;
119 paint
.setAlpha(hovered_background_alpha
);
120 canvas
->DrawImageInt(hovered_background_image_
, 0, 0, paint
);
121 } else if (state() == STATE_PRESSED
) {
122 canvas
->DrawImageInt(pressed_background_image_
, 0, 0);
125 int icon_alpha
= swap_images_animation_
->CurrentValueBetween(0, 255);
126 int crossfade_icon_alpha
= 0;
127 if (icon_alpha
< static_cast<int>(kFadeOutRatio
* 255))
128 crossfade_icon_alpha
= static_cast<int>(255 - icon_alpha
/ kFadeOutRatio
);
130 gfx::ImageSkia icon_image
= GetIconImageToPaint();
131 if (crossfade_icon_alpha
> 0 && !crossfade_icon_image_
.isNull()) {
132 gfx::Canvas
icon_canvas(icon_image
.size(), canvas
->image_scale(), false);
134 paint
.setAlpha(icon_alpha
);
135 icon_canvas
.DrawImageInt(icon_image
, 0, 0, paint
);
137 paint
.setAlpha(crossfade_icon_alpha
);
138 paint
.setXfermodeMode(SkXfermode::kPlus_Mode
);
139 icon_canvas
.DrawImageInt(crossfade_icon_image_
, 0, 0, paint
);
141 PaintCentered(canvas
, gfx::ImageSkia(icon_canvas
.ExtractImageRep()),
144 if (!swap_images_animation_
->is_animating())
146 PaintCentered(canvas
, icon_image
, icon_alpha
);
150 void FrameCaptionButton::OnGestureEvent(ui::GestureEvent
* event
) {
151 // CustomButton does not become pressed when the user drags off and then back
152 // onto the button. Make FrameCaptionButton pressed in this case because this
153 // behavior is more consistent with AlternateFrameSizeButton.
154 if (event
->type() == ui::ET_GESTURE_SCROLL_BEGIN
||
155 event
->type() == ui::ET_GESTURE_SCROLL_UPDATE
) {
156 if (HitTestPoint(event
->location())) {
157 SetState(STATE_PRESSED
);
159 event
->StopPropagation();
161 SetState(STATE_NORMAL
);
163 } else if (event
->type() == ui::ET_GESTURE_SCROLL_END
) {
164 if (HitTestPoint(event
->location())) {
165 SetState(STATE_HOVERED
);
167 event
->StopPropagation();
170 CustomButton::OnGestureEvent(event
);
173 const gfx::ImageSkia
& FrameCaptionButton::GetIconImageToPaint() const {
174 return paint_as_active_
? icon_image_
: inactive_icon_image_
;
177 void FrameCaptionButton::PaintCentered(gfx::Canvas
* canvas
,
178 const gfx::ImageSkia
& to_center
,
181 paint
.setAlpha(alpha
);
182 canvas
->DrawImageInt(to_center
,
183 (width() - to_center
.width()) / 2,
184 (height() - to_center
.height()) / 2,