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