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/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