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