Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / ui / views / controls / button / text_button.cc
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