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
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).
21static const int kDefaultWidth = 16;
22static const int kDefaultHeight = 14;
23
24const char ImageButton::kViewClassName[] = "ImageButton";
25
26////////////////////////////////////////////////////////////////////////////////
27// ImageButton, public:
28
29ImageButton::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
41ImageButton::~ImageButton() {
42}
43
44const gfx::ImageSkia& ImageButton::GetImage(ButtonState state) const {
45  return images_[state];
46}
47
48void ImageButton::SetImage(ButtonState state, const gfx::ImageSkia* image) {
49  images_[state] = image ? *image : gfx::ImageSkia();
50  PreferredSizeChanged();
51}
52
53void 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
65void ImageButton::SetImageAlignment(HorizontalAlignment h_align,
66                                    VerticalAlignment v_align) {
67  h_alignment_ = h_align;
68  v_alignment_ = v_align;
69  SchedulePaint();
70}
71
72void ImageButton::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
73  focus_painter_ = focus_painter.Pass();
74}
75
76void 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
87gfx::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
101const char* ImageButton::GetClassName() const {
102  return kViewClassName;
103}
104
105void 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
131void ImageButton::OnFocus() {
132  View::OnFocus();
133  if (focus_painter_.get())
134    SchedulePaint();
135}
136
137void ImageButton::OnBlur() {
138  View::OnBlur();
139  if (focus_painter_.get())
140    SchedulePaint();
141}
142
143gfx::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
159gfx::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
190ToggleImageButton::ToggleImageButton(ButtonListener* listener)
191    : ImageButton(listener),
192      toggled_(false) {
193}
194
195ToggleImageButton::~ToggleImageButton() {
196}
197
198void 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
213void 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
224void ToggleImageButton::SetToggledTooltipText(const base::string16& tooltip) {
225  toggled_tooltip_text_ = tooltip;
226}
227
228////////////////////////////////////////////////////////////////////////////////
229// ToggleImageButton, ImageButton overrides:
230
231const gfx::ImageSkia& ToggleImageButton::GetImage(ButtonState state) const {
232  if (toggled_)
233    return alternate_images_[state];
234  return images_[state];
235}
236
237void 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
252bool 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
261void ToggleImageButton::GetAccessibleState(ui::AXViewState* state) {
262  ImageButton::GetAccessibleState(state);
263  GetTooltipText(gfx::Point(), &state->name);
264}
265
266}  // namespace views
267