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/text_button.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "grit/ui_resources.h"
     11 #include "ui/base/resource/resource_bundle.h"
     12 #include "ui/gfx/animation/throb_animation.h"
     13 #include "ui/gfx/canvas.h"
     14 #include "ui/gfx/image/image.h"
     15 #include "ui/views/controls/button/button.h"
     16 #include "ui/views/painter.h"
     17 #include "ui/views/widget/widget.h"
     18 
     19 #if defined(OS_WIN)
     20 #include "skia/ext/skia_utils_win.h"
     21 #include "ui/gfx/platform_font_win.h"
     22 #include "ui/native_theme/native_theme_win.h"
     23 #endif
     24 
     25 namespace views {
     26 
     27 namespace {
     28 
     29 // Default space between the icon and text.
     30 const int kDefaultIconTextSpacing = 5;
     31 
     32 // Preferred padding between text and edge.
     33 const int kPreferredPaddingHorizontal = 6;
     34 const int kPreferredPaddingVertical = 5;
     35 
     36 // Preferred padding between text and edge for NativeTheme border.
     37 const int kPreferredNativeThemePaddingHorizontal = 12;
     38 const int kPreferredNativeThemePaddingVertical = 5;
     39 
     40 // By default the focus rect is drawn at the border of the view.  For a button,
     41 // we inset the focus rect by 3 pixels so that it doesn't draw on top of the
     42 // button's border. This roughly matches how the Windows native focus rect for
     43 // buttons looks. A subclass that draws a button with different padding may need
     44 // to provide a different focus painter and do something different.
     45 const int kFocusRectInset = 3;
     46 
     47 // How long the hover fade animation should last.
     48 const int kHoverAnimationDurationMs = 170;
     49 
     50 #if defined(OS_WIN)
     51 // These sizes are from http://msdn.microsoft.com/en-us/library/aa511279.aspx
     52 const int kMinWidthDLUs = 50;
     53 const int kMinHeightDLUs = 14;
     54 #endif
     55 
     56 // The default hot and pushed button image IDs; normal has none by default.
     57 const int kHotImages[] = IMAGE_GRID(IDR_TEXTBUTTON_HOVER);
     58 const int kPushedImages[] = IMAGE_GRID(IDR_TEXTBUTTON_PRESSED);
     59 
     60 }  // namespace
     61 
     62 // static
     63 const char TextButtonBase::kViewClassName[] = "TextButtonBase";
     64 // static
     65 const char TextButton::kViewClassName[] = "TextButton";
     66 
     67 
     68 // TextButtonBorder -----------------------------------------------------------
     69 
     70 TextButtonBorder::TextButtonBorder() {
     71 }
     72 
     73 TextButtonBorder::~TextButtonBorder() {
     74 }
     75 
     76 void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) {
     77 }
     78 
     79 gfx::Insets TextButtonBorder::GetInsets() const {
     80   return insets_;
     81 }
     82 
     83 gfx::Size TextButtonBorder::GetMinimumSize() const {
     84   return gfx::Size();
     85 }
     86 
     87 void TextButtonBorder::SetInsets(const gfx::Insets& insets) {
     88   insets_ = insets;
     89 }
     90 
     91 
     92 // TextButtonDefaultBorder ----------------------------------------------------
     93 
     94 TextButtonDefaultBorder::TextButtonDefaultBorder()
     95     : vertical_padding_(kPreferredPaddingVertical) {
     96   set_hot_painter(Painter::CreateImageGridPainter(kHotImages));
     97   set_pushed_painter(Painter::CreateImageGridPainter(kPushedImages));
     98   SetInsets(gfx::Insets(vertical_padding_, kPreferredPaddingHorizontal,
     99                         vertical_padding_, kPreferredPaddingHorizontal));
    100 }
    101 
    102 TextButtonDefaultBorder::~TextButtonDefaultBorder() {
    103 }
    104 
    105 void TextButtonDefaultBorder::Paint(const View& view, gfx::Canvas* canvas) {
    106   const TextButton* button = static_cast<const TextButton*>(&view);
    107   int state = button->state();
    108   bool animating = button->GetAnimation()->is_animating();
    109 
    110   Painter* painter = normal_painter_.get();
    111   // Use the hot painter when we're hovered. Also use the hot painter when we're
    112   // STATE_NORMAL and |animating| so that we show throb animations started from
    113   // CustomButton::StartThrobbing which should start throbbing the button
    114   // regardless of whether it is hovered.
    115   if (button->show_multiple_icon_states() &&
    116       ((state == TextButton::STATE_HOVERED) ||
    117        (state == TextButton::STATE_PRESSED) ||
    118        ((state == TextButton::STATE_NORMAL) && animating))) {
    119     painter = (state == TextButton::STATE_PRESSED) ?
    120         pushed_painter_.get() : hot_painter_.get();
    121   }
    122   if (painter) {
    123     if (animating) {
    124       // TODO(pkasting): Really this should crossfade between states so it could
    125       // handle the case of having a non-NULL |normal_painter_|.
    126       canvas->SaveLayerAlpha(static_cast<uint8>(
    127           button->GetAnimation()->CurrentValueBetween(0, 255)));
    128       painter->Paint(canvas, view.size());
    129       canvas->Restore();
    130     } else {
    131       painter->Paint(canvas, view.size());
    132     }
    133   }
    134 }
    135 
    136 gfx::Size TextButtonDefaultBorder::GetMinimumSize() const {
    137   gfx::Size size;
    138   if (normal_painter_)
    139     size.SetToMax(normal_painter_->GetMinimumSize());
    140   if (hot_painter_)
    141     size.SetToMax(hot_painter_->GetMinimumSize());
    142   if (pushed_painter_)
    143     size.SetToMax(pushed_painter_->GetMinimumSize());
    144   return size;
    145 }
    146 
    147 
    148 // TextButtonNativeThemeBorder ------------------------------------------------
    149 
    150 TextButtonNativeThemeBorder::TextButtonNativeThemeBorder(
    151     NativeThemeDelegate* delegate)
    152     : delegate_(delegate) {
    153   SetInsets(gfx::Insets(kPreferredNativeThemePaddingVertical,
    154                         kPreferredNativeThemePaddingHorizontal,
    155                         kPreferredNativeThemePaddingVertical,
    156                         kPreferredNativeThemePaddingHorizontal));
    157 }
    158 
    159 TextButtonNativeThemeBorder::~TextButtonNativeThemeBorder() {
    160 }
    161 
    162 void TextButtonNativeThemeBorder::Paint(const View& view, gfx::Canvas* canvas) {
    163   const ui::NativeTheme* theme = view.GetNativeTheme();
    164   const TextButtonBase* tb = static_cast<const TextButton*>(&view);
    165   ui::NativeTheme::Part part = delegate_->GetThemePart();
    166   gfx::Rect rect(delegate_->GetThemePaintRect());
    167 
    168   if (tb->show_multiple_icon_states() &&
    169       delegate_->GetThemeAnimation() != NULL &&
    170       delegate_->GetThemeAnimation()->is_animating()) {
    171     // Paint background state.
    172     ui::NativeTheme::ExtraParams prev_extra;
    173     ui::NativeTheme::State prev_state =
    174         delegate_->GetBackgroundThemeState(&prev_extra);
    175     theme->Paint(canvas->sk_canvas(), part, prev_state, rect, prev_extra);
    176 
    177     // Composite foreground state above it.
    178     ui::NativeTheme::ExtraParams extra;
    179     ui::NativeTheme::State state = delegate_->GetForegroundThemeState(&extra);
    180     int alpha = delegate_->GetThemeAnimation()->CurrentValueBetween(0, 255);
    181     canvas->SaveLayerAlpha(static_cast<uint8>(alpha));
    182     theme->Paint(canvas->sk_canvas(), part, state, rect, extra);
    183     canvas->Restore();
    184   } else {
    185     ui::NativeTheme::ExtraParams extra;
    186     ui::NativeTheme::State state = delegate_->GetThemeState(&extra);
    187     theme->Paint(canvas->sk_canvas(), part, state, rect, extra);
    188   }
    189 }
    190 
    191 
    192 // TextButtonBase -------------------------------------------------------------
    193 
    194 TextButtonBase::TextButtonBase(ButtonListener* listener,
    195                                const base::string16& text)
    196     : CustomButton(listener),
    197       alignment_(ALIGN_LEFT),
    198       min_width_(0),
    199       min_height_(0),
    200       max_width_(0),
    201       show_multiple_icon_states_(true),
    202       is_default_(false),
    203       multi_line_(false),
    204       use_enabled_color_from_theme_(true),
    205       use_disabled_color_from_theme_(true),
    206       use_highlight_color_from_theme_(true),
    207       use_hover_color_from_theme_(true),
    208       focus_painter_(Painter::CreateDashedFocusPainter()) {
    209   SetText(text);
    210   SetAnimationDuration(kHoverAnimationDurationMs);
    211 }
    212 
    213 TextButtonBase::~TextButtonBase() {
    214 }
    215 
    216 void TextButtonBase::SetIsDefault(bool is_default) {
    217   if (is_default == is_default_)
    218     return;
    219   is_default_ = is_default;
    220   if (is_default_)
    221     AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
    222   else
    223     RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
    224   SchedulePaint();
    225 }
    226 
    227 void TextButtonBase::SetText(const base::string16& text) {
    228   if (text == text_)
    229     return;
    230   text_ = text;
    231   SetAccessibleName(text);
    232   UpdateTextSize();
    233 }
    234 
    235 void TextButtonBase::SetFontList(const gfx::FontList& font_list) {
    236   font_list_ = font_list;
    237   UpdateTextSize();
    238 }
    239 
    240 void TextButtonBase::SetEnabledColor(SkColor color) {
    241   color_enabled_ = color;
    242   use_enabled_color_from_theme_ = false;
    243   UpdateColor();
    244 }
    245 
    246 void TextButtonBase::SetDisabledColor(SkColor color) {
    247   color_disabled_ = color;
    248   use_disabled_color_from_theme_ = false;
    249   UpdateColor();
    250 }
    251 
    252 void TextButtonBase::SetHighlightColor(SkColor color) {
    253   color_highlight_ = color;
    254   use_highlight_color_from_theme_ = false;
    255 }
    256 
    257 void TextButtonBase::SetHoverColor(SkColor color) {
    258   color_hover_ = color;
    259   use_hover_color_from_theme_ = false;
    260 }
    261 
    262 void TextButtonBase::ClearMaxTextSize() {
    263   max_text_size_ = text_size_;
    264 }
    265 
    266 void TextButtonBase::SetShowMultipleIconStates(bool show_multiple_icon_states) {
    267   show_multiple_icon_states_ = show_multiple_icon_states;
    268 }
    269 
    270 void TextButtonBase::SetMultiLine(bool multi_line) {
    271   if (multi_line != multi_line_) {
    272     multi_line_ = multi_line;
    273     max_text_size_.SetSize(0, 0);
    274     UpdateTextSize();
    275     SchedulePaint();
    276   }
    277 }
    278 
    279 gfx::Size TextButtonBase::GetPreferredSize() const {
    280   gfx::Insets insets = GetInsets();
    281 
    282   // Use the max size to set the button boundaries.
    283   // In multiline mode max size can be undefined while
    284   // width() is 0, so max it out with current text size.
    285   gfx::Size prefsize(std::max(max_text_size_.width(),
    286                               text_size_.width()) + insets.width(),
    287                      std::max(max_text_size_.height(),
    288                               text_size_.height()) + insets.height());
    289 
    290   if (max_width_ > 0)
    291     prefsize.set_width(std::min(max_width_, prefsize.width()));
    292 
    293   prefsize.set_width(std::max(prefsize.width(), min_width_));
    294   prefsize.set_height(std::max(prefsize.height(), min_height_));
    295 
    296   return prefsize;
    297 }
    298 
    299 int TextButtonBase::GetHeightForWidth(int w) const {
    300   if (!multi_line_)
    301     return View::GetHeightForWidth(w);
    302 
    303   if (max_width_ > 0)
    304     w = std::min(max_width_, w);
    305 
    306   gfx::Size text_size;
    307   CalculateTextSize(&text_size, w);
    308   int height = text_size.height() + GetInsets().height();
    309 
    310   return std::max(height, min_height_);
    311 }
    312 
    313 void TextButtonBase::OnPaint(gfx::Canvas* canvas) {
    314   PaintButton(canvas, PB_NORMAL);
    315 }
    316 
    317 void TextButtonBase::OnBoundsChanged(const gfx::Rect& previous_bounds) {
    318   if (multi_line_)
    319     UpdateTextSize();
    320 }
    321 
    322 const gfx::Animation* TextButtonBase::GetAnimation() const {
    323   return hover_animation_.get();
    324 }
    325 
    326 void TextButtonBase::UpdateColor() {
    327   color_ = enabled() ? color_enabled_ : color_disabled_;
    328 }
    329 
    330 void TextButtonBase::UpdateTextSize() {
    331   int text_width = width();
    332   // If width is defined, use GetTextBounds.width() for maximum text width,
    333   // as it will take size of checkbox/radiobutton into account.
    334   if (text_width != 0) {
    335     gfx::Rect text_bounds = GetTextBounds();
    336     text_width = text_bounds.width();
    337   }
    338   CalculateTextSize(&text_size_, text_width);
    339   // Before layout width() is 0, and multiline text will be treated as one line.
    340   // Do not store max_text_size in this case. UpdateTextSize will be called
    341   // again once width() changes.
    342   if (!multi_line_ || text_width != 0) {
    343     max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()),
    344                            std::max(max_text_size_.height(),
    345                                     text_size_.height()));
    346     PreferredSizeChanged();
    347   }
    348 }
    349 
    350 void TextButtonBase::CalculateTextSize(gfx::Size* text_size,
    351                                        int max_width) const {
    352   int h = font_list_.GetHeight();
    353   int w = multi_line_ ? max_width : 0;
    354   int flags = ComputeCanvasStringFlags();
    355   if (!multi_line_)
    356     flags |= gfx::Canvas::NO_ELLIPSIS;
    357 
    358   gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, 0, flags);
    359   text_size->SetSize(w, h);
    360 }
    361 
    362 void TextButtonBase::OnPaintText(gfx::Canvas* canvas, PaintButtonMode mode) {
    363   gfx::Rect text_bounds(GetTextBounds());
    364   if (text_bounds.width() > 0) {
    365     // Because the text button can (at times) draw multiple elements on the
    366     // canvas, we can not mirror the button by simply flipping the canvas as
    367     // doing this will mirror the text itself. Flipping the canvas will also
    368     // make the icons look wrong because icons are almost always represented as
    369     // direction-insensitive images and such images should never be flipped
    370     // horizontally.
    371     //
    372     // Due to the above, we must perform the flipping manually for RTL UIs.
    373     text_bounds.set_x(GetMirroredXForRect(text_bounds));
    374 
    375     SkColor text_color = (show_multiple_icon_states_ &&
    376         (state() == STATE_HOVERED || state() == STATE_PRESSED)) ?
    377             color_hover_ : color_;
    378 
    379     int draw_string_flags = gfx::Canvas::DefaultCanvasTextAlignment() |
    380         ComputeCanvasStringFlags();
    381 
    382     if (mode == PB_FOR_DRAG) {
    383       // Disable sub-pixel rendering as background is transparent.
    384       draw_string_flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING;
    385       canvas->DrawStringRectWithHalo(text_, font_list_,
    386                                      SK_ColorBLACK, SK_ColorWHITE,
    387                                      text_bounds, draw_string_flags);
    388     } else {
    389       canvas->DrawStringRectWithFlags(text_, font_list_, text_color,
    390                                       text_bounds, draw_string_flags);
    391     }
    392   }
    393 }
    394 
    395 int TextButtonBase::ComputeCanvasStringFlags() const {
    396   if (!multi_line_)
    397     return 0;
    398 
    399   int flags = gfx::Canvas::MULTI_LINE;
    400   switch (alignment_) {
    401     case ALIGN_LEFT:
    402       flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
    403       break;
    404     case ALIGN_RIGHT:
    405       flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
    406       break;
    407     case ALIGN_CENTER:
    408       flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
    409       break;
    410   }
    411   return flags;
    412 }
    413 
    414 void TextButtonBase::OnFocus() {
    415   View::OnFocus();
    416   if (focus_painter_)
    417     SchedulePaint();
    418 }
    419 
    420 void TextButtonBase::OnBlur() {
    421   View::OnBlur();
    422   if (focus_painter_)
    423     SchedulePaint();
    424 }
    425 
    426 void TextButtonBase::GetExtraParams(
    427     ui::NativeTheme::ExtraParams* params) const {
    428   params->button.checked = false;
    429   params->button.indeterminate = false;
    430   params->button.is_default = false;
    431   params->button.is_focused = false;
    432   params->button.has_border = false;
    433   params->button.classic_state = 0;
    434   params->button.background_color =
    435       GetNativeTheme()->GetSystemColor(
    436           ui::NativeTheme::kColorId_ButtonBackgroundColor);
    437 }
    438 
    439 gfx::Rect TextButtonBase::GetContentBounds(int extra_width) const {
    440   gfx::Insets insets = GetInsets();
    441   int available_width = width() - insets.width();
    442   int content_width = text_size_.width() + extra_width;
    443   int content_x = 0;
    444   switch(alignment_) {
    445     case ALIGN_LEFT:
    446       content_x = insets.left();
    447       break;
    448     case ALIGN_RIGHT:
    449       content_x = width() - insets.right() - content_width;
    450       if (content_x < insets.left())
    451         content_x = insets.left();
    452       break;
    453     case ALIGN_CENTER:
    454       content_x = insets.left() + std::max(0,
    455           (available_width - content_width) / 2);
    456       break;
    457   }
    458   content_width = std::min(content_width,
    459                            width() - insets.right() - content_x);
    460   int available_height = height() - insets.height();
    461   int content_y = (available_height - text_size_.height()) / 2 + insets.top();
    462 
    463   gfx::Rect bounds(content_x, content_y, content_width, text_size_.height());
    464   return bounds;
    465 }
    466 
    467 gfx::Rect TextButtonBase::GetTextBounds() const {
    468   return GetContentBounds(0);
    469 }
    470 
    471 void TextButtonBase::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
    472   focus_painter_ = focus_painter.Pass();
    473 }
    474 
    475 void TextButtonBase::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
    476   if (mode == PB_NORMAL) {
    477     OnPaintBackground(canvas);
    478     OnPaintBorder(canvas);
    479     Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
    480   }
    481 
    482   OnPaintText(canvas, mode);
    483 }
    484 
    485 gfx::Size TextButtonBase::GetMinimumSize() const {
    486   return max_text_size_;
    487 }
    488 
    489 void TextButtonBase::OnEnabledChanged() {
    490   // We should always call UpdateColor() since the state of the button might be
    491   // changed by other functions like CustomButton::SetState().
    492   UpdateColor();
    493   CustomButton::OnEnabledChanged();
    494 }
    495 
    496 const char* TextButtonBase::GetClassName() const {
    497   return kViewClassName;
    498 }
    499 
    500 void TextButtonBase::OnNativeThemeChanged(const ui::NativeTheme* theme) {
    501   if (use_enabled_color_from_theme_) {
    502     color_enabled_ = theme->GetSystemColor(
    503         ui::NativeTheme::kColorId_ButtonEnabledColor);
    504   }
    505   if (use_disabled_color_from_theme_) {
    506     color_disabled_ = theme->GetSystemColor(
    507         ui::NativeTheme::kColorId_ButtonDisabledColor);
    508   }
    509   if (use_highlight_color_from_theme_) {
    510     color_highlight_ = theme->GetSystemColor(
    511         ui::NativeTheme::kColorId_ButtonHighlightColor);
    512   }
    513   if (use_hover_color_from_theme_) {
    514     color_hover_ = theme->GetSystemColor(
    515         ui::NativeTheme::kColorId_ButtonHoverColor);
    516   }
    517   UpdateColor();
    518 }
    519 
    520 gfx::Rect TextButtonBase::GetThemePaintRect() const {
    521   return GetLocalBounds();
    522 }
    523 
    524 ui::NativeTheme::State TextButtonBase::GetThemeState(
    525     ui::NativeTheme::ExtraParams* params) const {
    526   GetExtraParams(params);
    527   switch(state()) {
    528     case STATE_DISABLED:
    529       return ui::NativeTheme::kDisabled;
    530     case STATE_NORMAL:
    531       return ui::NativeTheme::kNormal;
    532     case STATE_HOVERED:
    533       return ui::NativeTheme::kHovered;
    534     case STATE_PRESSED:
    535       return ui::NativeTheme::kPressed;
    536     default:
    537       NOTREACHED() << "Unknown state: " << state();
    538       return ui::NativeTheme::kNormal;
    539   }
    540 }
    541 
    542 const gfx::Animation* TextButtonBase::GetThemeAnimation() const {
    543 #if defined(OS_WIN)
    544   if (GetNativeTheme() == ui::NativeThemeWin::instance()) {
    545     return ui::NativeThemeWin::instance()->IsThemingActive() ?
    546         hover_animation_.get() : NULL;
    547   }
    548 #endif
    549   return hover_animation_.get();
    550 }
    551 
    552 ui::NativeTheme::State TextButtonBase::GetBackgroundThemeState(
    553   ui::NativeTheme::ExtraParams* params) const {
    554   GetExtraParams(params);
    555   return ui::NativeTheme::kNormal;
    556 }
    557 
    558 ui::NativeTheme::State TextButtonBase::GetForegroundThemeState(
    559   ui::NativeTheme::ExtraParams* params) const {
    560   GetExtraParams(params);
    561   return ui::NativeTheme::kHovered;
    562 }
    563 
    564 
    565 // TextButton -----------------------------------------------------------------
    566 
    567 TextButton::TextButton(ButtonListener* listener, const base::string16& text)
    568     : TextButtonBase(listener, text),
    569       icon_placement_(ICON_ON_LEFT),
    570       has_hover_icon_(false),
    571       has_pushed_icon_(false),
    572       icon_text_spacing_(kDefaultIconTextSpacing),
    573       ignore_minimum_size_(true),
    574       full_justification_(false) {
    575   SetBorder(scoped_ptr<Border>(new TextButtonDefaultBorder));
    576   SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets(
    577                       gfx::Insets(kFocusRectInset, kFocusRectInset,
    578                                   kFocusRectInset, kFocusRectInset)));
    579 }
    580 
    581 TextButton::~TextButton() {
    582 }
    583 
    584 void TextButton::SetIcon(const gfx::ImageSkia& icon) {
    585   icon_ = icon;
    586   SchedulePaint();
    587 }
    588 
    589 void TextButton::SetHoverIcon(const gfx::ImageSkia& icon) {
    590   icon_hover_ = icon;
    591   has_hover_icon_ = true;
    592   SchedulePaint();
    593 }
    594 
    595 void TextButton::SetPushedIcon(const gfx::ImageSkia& icon) {
    596   icon_pushed_ = icon;
    597   has_pushed_icon_ = true;
    598   SchedulePaint();
    599 }
    600 
    601 gfx::Size TextButton::GetPreferredSize() const {
    602   gfx::Size prefsize(TextButtonBase::GetPreferredSize());
    603   prefsize.Enlarge(icon_.width(), 0);
    604   prefsize.set_height(std::max(prefsize.height(), icon_.height()));
    605 
    606   // Use the max size to set the button boundaries.
    607   if (icon_.width() > 0 && !text_.empty())
    608     prefsize.Enlarge(icon_text_spacing_, 0);
    609 
    610   if (max_width_ > 0)
    611     prefsize.set_width(std::min(max_width_, prefsize.width()));
    612 
    613 #if defined(OS_WIN)
    614   // Clamp the size returned to at least the minimum size.
    615   if (!ignore_minimum_size_) {
    616     gfx::PlatformFontWin* platform_font = static_cast<gfx::PlatformFontWin*>(
    617         font_list_.GetPrimaryFont().platform_font());
    618     prefsize.set_width(std::max(
    619         prefsize.width(),
    620         platform_font->horizontal_dlus_to_pixels(kMinWidthDLUs)));
    621     prefsize.set_height(std::max(
    622         prefsize.height(),
    623         platform_font->vertical_dlus_to_pixels(kMinHeightDLUs)));
    624   }
    625 #endif
    626 
    627   prefsize.set_width(std::max(prefsize.width(), min_width_));
    628   prefsize.set_height(std::max(prefsize.height(), min_height_));
    629 
    630   return prefsize;
    631 }
    632 
    633 void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
    634   if (full_justification_ && icon_placement_ == ICON_ON_LEFT)
    635       set_alignment(ALIGN_RIGHT);
    636 
    637   TextButtonBase::PaintButton(canvas, mode);
    638   OnPaintIcon(canvas, mode);
    639 }
    640 
    641 void TextButton::OnPaintIcon(gfx::Canvas* canvas, PaintButtonMode mode) {
    642   const gfx::ImageSkia& icon = GetImageToPaint();
    643 
    644   if (icon.width() > 0) {
    645     gfx::Rect text_bounds = GetTextBounds();
    646     int icon_x = 0;
    647     int spacing = text_.empty() ? 0 : icon_text_spacing_;
    648     gfx::Insets insets = GetInsets();
    649     switch (icon_placement_) {
    650       case ICON_ON_LEFT:
    651         icon_x = full_justification_ ? insets.left()
    652                                      : text_bounds.x() - icon.width() - spacing;
    653         break;
    654       case ICON_ON_RIGHT:
    655         icon_x = full_justification_ ? width() - insets.right() - icon.width()
    656                                      : text_bounds.right() + spacing;
    657         break;
    658       case ICON_CENTERED:
    659         DCHECK(text_.empty());
    660         icon_x = (width() - insets.width() - icon.width()) / 2 + insets.left();
    661         break;
    662       default:
    663         NOTREACHED();
    664         break;
    665     }
    666 
    667     int available_height = height() - insets.height();
    668     int icon_y = (available_height - icon.height()) / 2 + insets.top();
    669 
    670     // Mirroring the icon position if necessary.
    671     gfx::Rect icon_bounds(icon_x, icon_y, icon.width(), icon.height());
    672     icon_bounds.set_x(GetMirroredXForRect(icon_bounds));
    673     canvas->DrawImageInt(icon, icon_bounds.x(), icon_bounds.y());
    674   }
    675 }
    676 
    677 void TextButton::set_ignore_minimum_size(bool ignore_minimum_size) {
    678   ignore_minimum_size_ = ignore_minimum_size;
    679 }
    680 
    681 void TextButton::set_full_justification(bool full_justification) {
    682   full_justification_ = full_justification;
    683 }
    684 
    685 const char* TextButton::GetClassName() const {
    686   return kViewClassName;
    687 }
    688 
    689 ui::NativeTheme::Part TextButton::GetThemePart() const {
    690   return ui::NativeTheme::kPushButton;
    691 }
    692 
    693 void TextButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const {
    694   TextButtonBase::GetExtraParams(params);
    695   params->button.is_default = is_default_;
    696 }
    697 
    698 gfx::Rect TextButton::GetTextBounds() const {
    699   int extra_width = 0;
    700 
    701   const gfx::ImageSkia& icon = GetImageToPaint();
    702   if (icon.width() > 0)
    703     extra_width = icon.width() + (text_.empty() ? 0 : icon_text_spacing_);
    704 
    705   gfx::Rect bounds(GetContentBounds(extra_width));
    706 
    707   if (extra_width > 0) {
    708     // Make sure the icon is always fully visible.
    709     if (icon_placement_ == ICON_ON_LEFT) {
    710       bounds.Inset(extra_width, 0, 0, 0);
    711     } else if (icon_placement_ == ICON_ON_RIGHT) {
    712       bounds.Inset(0, 0, extra_width, 0);
    713     }
    714   }
    715 
    716   return bounds;
    717 }
    718 
    719 const gfx::ImageSkia& TextButton::GetImageToPaint() const {
    720   if (show_multiple_icon_states_) {
    721     if (has_hover_icon_ && (state() == STATE_HOVERED))
    722       return icon_hover_;
    723     if (has_pushed_icon_ && (state() == STATE_PRESSED))
    724       return icon_pushed_;
    725   }
    726   return icon_;
    727 }
    728 
    729 }  // namespace views
    730