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