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