1 // Copyright (c) 2012 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 "ui/views/controls/button/image_button.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "ui/base/accessibility/accessible_view_state.h"
9 #include "ui/gfx/animation/throb_animation.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/gfx/image/image_skia_operations.h"
12 #include "ui/gfx/scoped_canvas.h"
13 #include "ui/views/painter.h"
14 #include "ui/views/widget/widget.h"
15
16 namespace views {
17
18 static const int kDefaultWidth = 16; // Default button width if no theme.
19 static const int kDefaultHeight = 14; // Default button height if no theme.
20
21 const char ImageButton::kViewClassName[] = "ImageButton";
22
23 ////////////////////////////////////////////////////////////////////////////////
24 // ImageButton, public:
25
ImageButton(ButtonListener * listener)26 ImageButton::ImageButton(ButtonListener* listener)
27 : CustomButton(listener),
28 h_alignment_(ALIGN_LEFT),
29 v_alignment_(ALIGN_TOP),
30 preferred_size_(kDefaultWidth, kDefaultHeight),
31 draw_image_mirrored_(false),
32 focus_painter_(Painter::CreateDashedFocusPainter()) {
33 // By default, we request that the gfx::Canvas passed to our View::OnPaint()
34 // implementation is flipped horizontally so that the button's images are
35 // mirrored when the UI directionality is right-to-left.
36 EnableCanvasFlippingForRTLUI(true);
37 }
38
~ImageButton()39 ImageButton::~ImageButton() {
40 }
41
GetImage(ButtonState state) const42 const gfx::ImageSkia& ImageButton::GetImage(ButtonState state) const {
43 return images_[state];
44 }
45
SetImage(ButtonState state,const gfx::ImageSkia * image)46 void ImageButton::SetImage(ButtonState state, const gfx::ImageSkia* image) {
47 images_[state] = image ? *image : gfx::ImageSkia();
48 PreferredSizeChanged();
49 }
50
SetBackground(SkColor color,const gfx::ImageSkia * image,const gfx::ImageSkia * mask)51 void ImageButton::SetBackground(SkColor color,
52 const gfx::ImageSkia* image,
53 const gfx::ImageSkia* mask) {
54 if (image == NULL || mask == NULL) {
55 background_image_ = gfx::ImageSkia();
56 return;
57 }
58
59 background_image_ = gfx::ImageSkiaOperations::CreateButtonBackground(color,
60 *image, *mask);
61 }
62
SetOverlayImage(const gfx::ImageSkia * image)63 void ImageButton::SetOverlayImage(const gfx::ImageSkia* image) {
64 if (!image) {
65 overlay_image_ = gfx::ImageSkia();
66 return;
67 }
68 overlay_image_ = *image;
69 }
70
SetImageAlignment(HorizontalAlignment h_align,VerticalAlignment v_align)71 void ImageButton::SetImageAlignment(HorizontalAlignment h_align,
72 VerticalAlignment v_align) {
73 h_alignment_ = h_align;
74 v_alignment_ = v_align;
75 SchedulePaint();
76 }
77
SetFocusPainter(scoped_ptr<Painter> focus_painter)78 void ImageButton::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
79 focus_painter_ = focus_painter.Pass();
80 }
81
82 ////////////////////////////////////////////////////////////////////////////////
83 // ImageButton, View overrides:
84
GetPreferredSize()85 gfx::Size ImageButton::GetPreferredSize() {
86 gfx::Size size = preferred_size_;
87 if (!images_[STATE_NORMAL].isNull()) {
88 size = gfx::Size(images_[STATE_NORMAL].width(),
89 images_[STATE_NORMAL].height());
90 }
91
92 gfx::Insets insets = GetInsets();
93 size.Enlarge(insets.width(), insets.height());
94 return size;
95 }
96
GetClassName() const97 const char* ImageButton::GetClassName() const {
98 return kViewClassName;
99 }
100
OnPaint(gfx::Canvas * canvas)101 void ImageButton::OnPaint(gfx::Canvas* canvas) {
102 // Call the base class first to paint any background/borders.
103 View::OnPaint(canvas);
104
105 gfx::ImageSkia img = GetImageToPaint();
106
107 if (!img.isNull()) {
108 gfx::ScopedCanvas scoped(canvas);
109 if (draw_image_mirrored_) {
110 canvas->Translate(gfx::Vector2d(width(), 0));
111 canvas->Scale(-1, 1);
112 }
113
114 gfx::Point position = ComputeImagePaintPosition(img);
115 if (!background_image_.isNull())
116 canvas->DrawImageInt(background_image_, position.x(), position.y());
117
118 canvas->DrawImageInt(img, position.x(), position.y());
119
120 if (!overlay_image_.isNull())
121 canvas->DrawImageInt(overlay_image_, position.x(), position.y());
122 }
123
124 Painter::PaintFocusPainter(this, canvas, focus_painter());
125 }
126
127 ////////////////////////////////////////////////////////////////////////////////
128 // ImageButton, protected:
129
OnFocus()130 void ImageButton::OnFocus() {
131 View::OnFocus();
132 if (focus_painter_.get())
133 SchedulePaint();
134 }
135
OnBlur()136 void ImageButton::OnBlur() {
137 View::OnBlur();
138 if (focus_painter_.get())
139 SchedulePaint();
140 }
141
GetImageToPaint()142 gfx::ImageSkia ImageButton::GetImageToPaint() {
143 gfx::ImageSkia img;
144
145 if (!images_[STATE_HOVERED].isNull() && hover_animation_->is_animating()) {
146 img = gfx::ImageSkiaOperations::CreateBlendedImage(images_[STATE_NORMAL],
147 images_[STATE_HOVERED], hover_animation_->GetCurrentValue());
148 } else {
149 img = images_[state_];
150 }
151
152 return !img.isNull() ? img : images_[STATE_NORMAL];
153 }
154
155 ////////////////////////////////////////////////////////////////////////////////
156 // ImageButton, private:
157
ComputeImagePaintPosition(const gfx::ImageSkia & image)158 gfx::Point ImageButton::ComputeImagePaintPosition(const gfx::ImageSkia& image) {
159 int x = 0, y = 0;
160 gfx::Rect rect = GetContentsBounds();
161
162 HorizontalAlignment h_alignment = h_alignment_;
163 if (draw_image_mirrored_) {
164 if (h_alignment == ALIGN_RIGHT)
165 h_alignment = ALIGN_LEFT;
166 else if (h_alignment == ALIGN_LEFT)
167 h_alignment = ALIGN_RIGHT;
168 }
169
170 if (h_alignment == ALIGN_CENTER)
171 x = (rect.width() - image.width()) / 2;
172 else if (h_alignment == ALIGN_RIGHT)
173 x = rect.width() - image.width();
174
175 if (v_alignment_ == ALIGN_MIDDLE)
176 y = (rect.height() - image.height()) / 2;
177 else if (v_alignment_ == ALIGN_BOTTOM)
178 y = rect.height() - image.height();
179
180 x += rect.x();
181 y += rect.y();
182
183 return gfx::Point(x, y);
184 }
185
186 ////////////////////////////////////////////////////////////////////////////////
187 // ToggleImageButton, public:
188
ToggleImageButton(ButtonListener * listener)189 ToggleImageButton::ToggleImageButton(ButtonListener* listener)
190 : ImageButton(listener),
191 toggled_(false) {
192 }
193
~ToggleImageButton()194 ToggleImageButton::~ToggleImageButton() {
195 }
196
SetToggled(bool toggled)197 void ToggleImageButton::SetToggled(bool toggled) {
198 if (toggled == toggled_)
199 return;
200
201 for (int i = 0; i < STATE_COUNT; ++i) {
202 gfx::ImageSkia temp = images_[i];
203 images_[i] = alternate_images_[i];
204 alternate_images_[i] = temp;
205 }
206 toggled_ = toggled;
207 SchedulePaint();
208
209 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, true);
210 }
211
SetToggledImage(ButtonState state,const gfx::ImageSkia * image)212 void ToggleImageButton::SetToggledImage(ButtonState state,
213 const gfx::ImageSkia* image) {
214 if (toggled_) {
215 images_[state] = image ? *image : gfx::ImageSkia();
216 if (state_ == state)
217 SchedulePaint();
218 } else {
219 alternate_images_[state] = image ? *image : gfx::ImageSkia();
220 }
221 }
222
SetToggledTooltipText(const string16 & tooltip)223 void ToggleImageButton::SetToggledTooltipText(const string16& tooltip) {
224 toggled_tooltip_text_ = tooltip;
225 }
226
227 ////////////////////////////////////////////////////////////////////////////////
228 // ToggleImageButton, ImageButton overrides:
229
GetImage(ButtonState state) const230 const gfx::ImageSkia& ToggleImageButton::GetImage(ButtonState state) const {
231 if (toggled_)
232 return alternate_images_[state];
233 return images_[state];
234 }
235
SetImage(ButtonState state,const gfx::ImageSkia * image)236 void ToggleImageButton::SetImage(ButtonState state,
237 const gfx::ImageSkia* image) {
238 if (toggled_) {
239 alternate_images_[state] = image ? *image : gfx::ImageSkia();
240 } else {
241 images_[state] = image ? *image : gfx::ImageSkia();
242 if (state_ == state)
243 SchedulePaint();
244 }
245 PreferredSizeChanged();
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
249 // ToggleImageButton, View overrides:
250
GetTooltipText(const gfx::Point & p,string16 * tooltip) const251 bool ToggleImageButton::GetTooltipText(const gfx::Point& p,
252 string16* tooltip) const {
253 if (!toggled_ || toggled_tooltip_text_.empty())
254 return Button::GetTooltipText(p, tooltip);
255
256 *tooltip = toggled_tooltip_text_;
257 return true;
258 }
259
GetAccessibleState(ui::AccessibleViewState * state)260 void ToggleImageButton::GetAccessibleState(ui::AccessibleViewState* state) {
261 ImageButton::GetAccessibleState(state);
262 GetTooltipText(gfx::Point(), &state->name);
263 }
264
265 } // namespace views
266