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