Home | History | Annotate | Download | only in button
      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