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