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
16namespace views {
17
18static const int kDefaultWidth = 16;   // Default button width if no theme.
19static const int kDefaultHeight = 14;  // Default button height if no theme.
20
21const char ImageButton::kViewClassName[] = "ImageButton";
22
23////////////////////////////////////////////////////////////////////////////////
24// ImageButton, public:
25
26ImageButton::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
39ImageButton::~ImageButton() {
40}
41
42const gfx::ImageSkia& ImageButton::GetImage(ButtonState state) const {
43  return images_[state];
44}
45
46void ImageButton::SetImage(ButtonState state, const gfx::ImageSkia* image) {
47  images_[state] = image ? *image : gfx::ImageSkia();
48  PreferredSizeChanged();
49}
50
51void 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
63void ImageButton::SetImageAlignment(HorizontalAlignment h_align,
64                                    VerticalAlignment v_align) {
65  h_alignment_ = h_align;
66  v_alignment_ = v_align;
67  SchedulePaint();
68}
69
70void ImageButton::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
71  focus_painter_ = focus_painter.Pass();
72}
73
74////////////////////////////////////////////////////////////////////////////////
75// ImageButton, View overrides:
76
77gfx::Size ImageButton::GetPreferredSize() const {
78  gfx::Size size = preferred_size_;
79  if (!images_[STATE_NORMAL].isNull()) {
80    size = gfx::Size(images_[STATE_NORMAL].width(),
81                     images_[STATE_NORMAL].height());
82  }
83
84  gfx::Insets insets = GetInsets();
85  size.Enlarge(insets.width(), insets.height());
86  return size;
87}
88
89const char* ImageButton::GetClassName() const {
90  return kViewClassName;
91}
92
93void ImageButton::OnPaint(gfx::Canvas* canvas) {
94  // Call the base class first to paint any background/borders.
95  View::OnPaint(canvas);
96
97  gfx::ImageSkia img = GetImageToPaint();
98
99  if (!img.isNull()) {
100    gfx::ScopedCanvas scoped(canvas);
101    if (draw_image_mirrored_) {
102      canvas->Translate(gfx::Vector2d(width(), 0));
103      canvas->Scale(-1, 1);
104    }
105
106    gfx::Point position = ComputeImagePaintPosition(img);
107    if (!background_image_.isNull())
108      canvas->DrawImageInt(background_image_, position.x(), position.y());
109
110    canvas->DrawImageInt(img, position.x(), position.y());
111  }
112
113  Painter::PaintFocusPainter(this, canvas, focus_painter());
114}
115
116////////////////////////////////////////////////////////////////////////////////
117// ImageButton, protected:
118
119void ImageButton::OnFocus() {
120  View::OnFocus();
121  if (focus_painter_.get())
122    SchedulePaint();
123}
124
125void ImageButton::OnBlur() {
126  View::OnBlur();
127  if (focus_painter_.get())
128    SchedulePaint();
129}
130
131gfx::ImageSkia ImageButton::GetImageToPaint() {
132  gfx::ImageSkia img;
133
134  if (!images_[STATE_HOVERED].isNull() && hover_animation_->is_animating()) {
135    img = gfx::ImageSkiaOperations::CreateBlendedImage(images_[STATE_NORMAL],
136        images_[STATE_HOVERED], hover_animation_->GetCurrentValue());
137  } else {
138    img = images_[state_];
139  }
140
141  return !img.isNull() ? img : images_[STATE_NORMAL];
142}
143
144////////////////////////////////////////////////////////////////////////////////
145// ImageButton, private:
146
147gfx::Point ImageButton::ComputeImagePaintPosition(const gfx::ImageSkia& image) {
148  int x = 0, y = 0;
149  gfx::Rect rect = GetContentsBounds();
150
151  HorizontalAlignment h_alignment = h_alignment_;
152  if (draw_image_mirrored_) {
153    if (h_alignment == ALIGN_RIGHT)
154      h_alignment = ALIGN_LEFT;
155    else if (h_alignment == ALIGN_LEFT)
156      h_alignment = ALIGN_RIGHT;
157  }
158
159  if (h_alignment == ALIGN_CENTER)
160    x = (rect.width() - image.width()) / 2;
161  else if (h_alignment == ALIGN_RIGHT)
162    x = rect.width() - image.width();
163
164  if (v_alignment_ == ALIGN_MIDDLE)
165    y = (rect.height() - image.height()) / 2;
166  else if (v_alignment_ == ALIGN_BOTTOM)
167    y = rect.height() - image.height();
168
169  x += rect.x();
170  y += rect.y();
171
172  return gfx::Point(x, y);
173}
174
175////////////////////////////////////////////////////////////////////////////////
176// ToggleImageButton, public:
177
178ToggleImageButton::ToggleImageButton(ButtonListener* listener)
179    : ImageButton(listener),
180      toggled_(false) {
181}
182
183ToggleImageButton::~ToggleImageButton() {
184}
185
186void ToggleImageButton::SetToggled(bool toggled) {
187  if (toggled == toggled_)
188    return;
189
190  for (int i = 0; i < STATE_COUNT; ++i) {
191    gfx::ImageSkia temp = images_[i];
192    images_[i] = alternate_images_[i];
193    alternate_images_[i] = temp;
194  }
195  toggled_ = toggled;
196  SchedulePaint();
197
198  NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, true);
199}
200
201void ToggleImageButton::SetToggledImage(ButtonState state,
202                                        const gfx::ImageSkia* image) {
203  if (toggled_) {
204    images_[state] = image ? *image : gfx::ImageSkia();
205    if (state_ == state)
206      SchedulePaint();
207  } else {
208    alternate_images_[state] = image ? *image : gfx::ImageSkia();
209  }
210}
211
212void ToggleImageButton::SetToggledTooltipText(const base::string16& tooltip) {
213  toggled_tooltip_text_ = tooltip;
214}
215
216////////////////////////////////////////////////////////////////////////////////
217// ToggleImageButton, ImageButton overrides:
218
219const gfx::ImageSkia& ToggleImageButton::GetImage(ButtonState state) const {
220  if (toggled_)
221    return alternate_images_[state];
222  return images_[state];
223}
224
225void ToggleImageButton::SetImage(ButtonState state,
226                                 const gfx::ImageSkia* image) {
227  if (toggled_) {
228    alternate_images_[state] = image ? *image : gfx::ImageSkia();
229  } else {
230    images_[state] = image ? *image : gfx::ImageSkia();
231    if (state_ == state)
232      SchedulePaint();
233  }
234  PreferredSizeChanged();
235}
236
237////////////////////////////////////////////////////////////////////////////////
238// ToggleImageButton, View overrides:
239
240bool ToggleImageButton::GetTooltipText(const gfx::Point& p,
241                                       base::string16* tooltip) const {
242  if (!toggled_ || toggled_tooltip_text_.empty())
243    return Button::GetTooltipText(p, tooltip);
244
245  *tooltip = toggled_tooltip_text_;
246  return true;
247}
248
249void ToggleImageButton::GetAccessibleState(ui::AXViewState* state) {
250  ImageButton::GetAccessibleState(state);
251  GetTooltipText(gfx::Point(), &state->name);
252}
253
254}  // namespace views
255