root/ui/views/controls/button/text_button.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Paint
  2. GetInsets
  3. GetMinimumSize
  4. SetInsets
  5. Paint
  6. GetMinimumSize
  7. Paint
  8. focus_painter_
  9. SetIsDefault
  10. SetText
  11. SetFontList
  12. SetEnabledColor
  13. SetDisabledColor
  14. SetHighlightColor
  15. SetHoverColor
  16. ClearMaxTextSize
  17. SetShowMultipleIconStates
  18. SetMultiLine
  19. GetPreferredSize
  20. GetHeightForWidth
  21. OnPaint
  22. OnBoundsChanged
  23. GetAnimation
  24. UpdateColor
  25. UpdateTextSize
  26. CalculateTextSize
  27. ComputeCanvasStringFlags
  28. OnFocus
  29. OnBlur
  30. GetExtraParams
  31. GetContentBounds
  32. GetTextBounds
  33. SetFocusPainter
  34. PaintButton
  35. GetMinimumSize
  36. OnEnabledChanged
  37. GetClassName
  38. OnNativeThemeChanged
  39. GetThemePaintRect
  40. GetThemeState
  41. GetThemeAnimation
  42. GetBackgroundThemeState
  43. GetForegroundThemeState
  44. full_justification_
  45. SetIcon
  46. SetHoverIcon
  47. SetPushedIcon
  48. GetPreferredSize
  49. PaintButton
  50. set_ignore_minimum_size
  51. set_full_justification
  52. GetClassName
  53. GetThemePart
  54. GetExtraParams
  55. GetTextBounds
  56. GetImageToPaint

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/button/text_button.h"

#include <algorithm>

#include "base/logging.h"
#include "grit/ui_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/painter.h"
#include "ui/views/widget/widget.h"

#if defined(OS_WIN)
#include "skia/ext/skia_utils_win.h"
#include "ui/gfx/platform_font_win.h"
#include "ui/native_theme/native_theme_win.h"
#endif

namespace views {

namespace {

// Default space between the icon and text.
const int kDefaultIconTextSpacing = 5;

// Preferred padding between text and edge.
const int kPreferredPaddingHorizontal = 6;
const int kPreferredPaddingVertical = 5;

// Preferred padding between text and edge for NativeTheme border.
const int kPreferredNativeThemePaddingHorizontal = 12;
const int kPreferredNativeThemePaddingVertical = 5;

// By default the focus rect is drawn at the border of the view.  For a button,
// we inset the focus rect by 3 pixels so that it doesn't draw on top of the
// button's border. This roughly matches how the Windows native focus rect for
// buttons looks. A subclass that draws a button with different padding may need
// to provide a different focus painter and do something different.
const int kFocusRectInset = 3;

// How long the hover fade animation should last.
const int kHoverAnimationDurationMs = 170;

#if defined(OS_WIN)
// These sizes are from http://msdn.microsoft.com/en-us/library/aa511279.aspx
const int kMinWidthDLUs = 50;
const int kMinHeightDLUs = 14;
#endif

// The default hot and pushed button image IDs; normal has none by default.
const int kHotImages[] = IMAGE_GRID(IDR_TEXTBUTTON_HOVER);
const int kPushedImages[] = IMAGE_GRID(IDR_TEXTBUTTON_PRESSED);

}  // namespace

// static
const char TextButtonBase::kViewClassName[] = "TextButtonBase";
// static
const char TextButton::kViewClassName[] = "TextButton";


// TextButtonBorder -----------------------------------------------------------

TextButtonBorder::TextButtonBorder() {
}

TextButtonBorder::~TextButtonBorder() {
}

void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) {
}

gfx::Insets TextButtonBorder::GetInsets() const {
  return insets_;
}

gfx::Size TextButtonBorder::GetMinimumSize() const {
  return gfx::Size();
}

void TextButtonBorder::SetInsets(const gfx::Insets& insets) {
  insets_ = insets;
}


// TextButtonDefaultBorder ----------------------------------------------------

TextButtonDefaultBorder::TextButtonDefaultBorder()
    : vertical_padding_(kPreferredPaddingVertical) {
  set_hot_painter(Painter::CreateImageGridPainter(kHotImages));
  set_pushed_painter(Painter::CreateImageGridPainter(kPushedImages));
  SetInsets(gfx::Insets(vertical_padding_, kPreferredPaddingHorizontal,
                        vertical_padding_, kPreferredPaddingHorizontal));
}

TextButtonDefaultBorder::~TextButtonDefaultBorder() {
}

void TextButtonDefaultBorder::Paint(const View& view, gfx::Canvas* canvas) {
  const TextButton* button = static_cast<const TextButton*>(&view);
  int state = button->state();
  bool animating = button->GetAnimation()->is_animating();

  Painter* painter = normal_painter_.get();
  // Use the hot painter when we're hovered. Also use the hot painter when we're
  // STATE_NORMAL and |animating| so that we show throb animations started from
  // CustomButton::StartThrobbing which should start throbbing the button
  // regardless of whether it is hovered.
  if (button->show_multiple_icon_states() &&
      ((state == TextButton::STATE_HOVERED) ||
       (state == TextButton::STATE_PRESSED) ||
       ((state == TextButton::STATE_NORMAL) && animating))) {
    painter = (state == TextButton::STATE_PRESSED) ?
        pushed_painter_.get() : hot_painter_.get();
  }
  if (painter) {
    if (animating) {
      // TODO(pkasting): Really this should crossfade between states so it could
      // handle the case of having a non-NULL |normal_painter_|.
      canvas->SaveLayerAlpha(static_cast<uint8>(
          button->GetAnimation()->CurrentValueBetween(0, 255)));
      painter->Paint(canvas, view.size());
      canvas->Restore();
    } else {
      painter->Paint(canvas, view.size());
    }
  }
}

gfx::Size TextButtonDefaultBorder::GetMinimumSize() const {
  gfx::Size size;
  if (normal_painter_)
    size.SetToMax(normal_painter_->GetMinimumSize());
  if (hot_painter_)
    size.SetToMax(hot_painter_->GetMinimumSize());
  if (pushed_painter_)
    size.SetToMax(pushed_painter_->GetMinimumSize());
  return size;
}


// TextButtonNativeThemeBorder ------------------------------------------------

TextButtonNativeThemeBorder::TextButtonNativeThemeBorder(
    NativeThemeDelegate* delegate)
    : delegate_(delegate) {
  SetInsets(gfx::Insets(kPreferredNativeThemePaddingVertical,
                        kPreferredNativeThemePaddingHorizontal,
                        kPreferredNativeThemePaddingVertical,
                        kPreferredNativeThemePaddingHorizontal));
}

TextButtonNativeThemeBorder::~TextButtonNativeThemeBorder() {
}

void TextButtonNativeThemeBorder::Paint(const View& view, gfx::Canvas* canvas) {
  const ui::NativeTheme* theme = view.GetNativeTheme();
  const TextButtonBase* tb = static_cast<const TextButton*>(&view);
  ui::NativeTheme::Part part = delegate_->GetThemePart();
  gfx::Rect rect(delegate_->GetThemePaintRect());

  if (tb->show_multiple_icon_states() &&
      delegate_->GetThemeAnimation() != NULL &&
      delegate_->GetThemeAnimation()->is_animating()) {
    // Paint background state.
    ui::NativeTheme::ExtraParams prev_extra;
    ui::NativeTheme::State prev_state =
        delegate_->GetBackgroundThemeState(&prev_extra);
    theme->Paint(canvas->sk_canvas(), part, prev_state, rect, prev_extra);

    // Composite foreground state above it.
    ui::NativeTheme::ExtraParams extra;
    ui::NativeTheme::State state = delegate_->GetForegroundThemeState(&extra);
    int alpha = delegate_->GetThemeAnimation()->CurrentValueBetween(0, 255);
    canvas->SaveLayerAlpha(static_cast<uint8>(alpha));
    theme->Paint(canvas->sk_canvas(), part, state, rect, extra);
    canvas->Restore();
  } else {
    ui::NativeTheme::ExtraParams extra;
    ui::NativeTheme::State state = delegate_->GetThemeState(&extra);
    theme->Paint(canvas->sk_canvas(), part, state, rect, extra);
  }
}


// TextButtonBase -------------------------------------------------------------

TextButtonBase::TextButtonBase(ButtonListener* listener,
                               const base::string16& text)
    : CustomButton(listener),
      alignment_(ALIGN_LEFT),
      min_width_(0),
      min_height_(0),
      max_width_(0),
      show_multiple_icon_states_(true),
      is_default_(false),
      multi_line_(false),
      use_enabled_color_from_theme_(true),
      use_disabled_color_from_theme_(true),
      use_highlight_color_from_theme_(true),
      use_hover_color_from_theme_(true),
      focus_painter_(Painter::CreateDashedFocusPainter()) {
  SetText(text);
  // OnNativeThemeChanged sets the color member variables.
  TextButtonBase::OnNativeThemeChanged(GetNativeTheme());
  SetAnimationDuration(kHoverAnimationDurationMs);
}

TextButtonBase::~TextButtonBase() {
}

void TextButtonBase::SetIsDefault(bool is_default) {
  if (is_default == is_default_)
    return;
  is_default_ = is_default;
  if (is_default_)
    AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
  else
    RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
  SchedulePaint();
}

void TextButtonBase::SetText(const base::string16& text) {
  if (text == text_)
    return;
  text_ = text;
  SetAccessibleName(text);
  UpdateTextSize();
}

void TextButtonBase::SetFontList(const gfx::FontList& font_list) {
  font_list_ = font_list;
  UpdateTextSize();
}

void TextButtonBase::SetEnabledColor(SkColor color) {
  color_enabled_ = color;
  use_enabled_color_from_theme_ = false;
  UpdateColor();
}

void TextButtonBase::SetDisabledColor(SkColor color) {
  color_disabled_ = color;
  use_disabled_color_from_theme_ = false;
  UpdateColor();
}

void TextButtonBase::SetHighlightColor(SkColor color) {
  color_highlight_ = color;
  use_highlight_color_from_theme_ = false;
}

void TextButtonBase::SetHoverColor(SkColor color) {
  color_hover_ = color;
  use_hover_color_from_theme_ = false;
}

void TextButtonBase::ClearMaxTextSize() {
  max_text_size_ = text_size_;
}

void TextButtonBase::SetShowMultipleIconStates(bool show_multiple_icon_states) {
  show_multiple_icon_states_ = show_multiple_icon_states;
}

void TextButtonBase::SetMultiLine(bool multi_line) {
  if (multi_line != multi_line_) {
    multi_line_ = multi_line;
    max_text_size_.SetSize(0, 0);
    UpdateTextSize();
    SchedulePaint();
  }
}

gfx::Size TextButtonBase::GetPreferredSize() {
  gfx::Insets insets = GetInsets();

  // Use the max size to set the button boundaries.
  // In multiline mode max size can be undefined while
  // width() is 0, so max it out with current text size.
  gfx::Size prefsize(std::max(max_text_size_.width(),
                              text_size_.width()) + insets.width(),
                     std::max(max_text_size_.height(),
                              text_size_.height()) + insets.height());

  if (max_width_ > 0)
    prefsize.set_width(std::min(max_width_, prefsize.width()));

  prefsize.set_width(std::max(prefsize.width(), min_width_));
  prefsize.set_height(std::max(prefsize.height(), min_height_));

  return prefsize;
}

int TextButtonBase::GetHeightForWidth(int w) {
  if (!multi_line_)
    return View::GetHeightForWidth(w);

  if (max_width_ > 0)
    w = std::min(max_width_, w);

  gfx::Size text_size;
  CalculateTextSize(&text_size, w);
  int height = text_size.height() + GetInsets().height();

  return std::max(height, min_height_);
}

void TextButtonBase::OnPaint(gfx::Canvas* canvas) {
  PaintButton(canvas, PB_NORMAL);
}

void TextButtonBase::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  if (multi_line_)
    UpdateTextSize();
}

const gfx::Animation* TextButtonBase::GetAnimation() const {
  return hover_animation_.get();
}

void TextButtonBase::UpdateColor() {
  color_ = enabled() ? color_enabled_ : color_disabled_;
}

void TextButtonBase::UpdateTextSize() {
  int text_width = width();
  // If width is defined, use GetTextBounds.width() for maximum text width,
  // as it will take size of checkbox/radiobutton into account.
  if (text_width != 0) {
    gfx::Rect text_bounds = GetTextBounds();
    text_width = text_bounds.width();
  }
  CalculateTextSize(&text_size_, text_width);
  // Before layout width() is 0, and multiline text will be treated as one line.
  // Do not store max_text_size in this case. UpdateTextSize will be called
  // again once width() changes.
  if (!multi_line_ || text_width != 0) {
    max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()),
                           std::max(max_text_size_.height(),
                                    text_size_.height()));
    PreferredSizeChanged();
  }
}

void TextButtonBase::CalculateTextSize(gfx::Size* text_size, int max_width) {
  int h = font_list_.GetHeight();
  int w = multi_line_ ? max_width : 0;
  int flags = ComputeCanvasStringFlags();
  if (!multi_line_)
    flags |= gfx::Canvas::NO_ELLIPSIS;

  gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, 0, flags);
  text_size->SetSize(w, h);
}

int TextButtonBase::ComputeCanvasStringFlags() const {
  if (!multi_line_)
    return 0;

  int flags = gfx::Canvas::MULTI_LINE;
  switch (alignment_) {
    case ALIGN_LEFT:
      flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_RIGHT:
      flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
      break;
    case ALIGN_CENTER:
      flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
      break;
  }
  return flags;
}

void TextButtonBase::OnFocus() {
  View::OnFocus();
  if (focus_painter_)
    SchedulePaint();
}

void TextButtonBase::OnBlur() {
  View::OnBlur();
  if (focus_painter_)
    SchedulePaint();
}

void TextButtonBase::GetExtraParams(
    ui::NativeTheme::ExtraParams* params) const {
  params->button.checked = false;
  params->button.indeterminate = false;
  params->button.is_default = false;
  params->button.is_focused = false;
  params->button.has_border = false;
  params->button.classic_state = 0;
  params->button.background_color =
      GetNativeTheme()->GetSystemColor(
          ui::NativeTheme::kColorId_ButtonBackgroundColor);
}

gfx::Rect TextButtonBase::GetContentBounds(int extra_width) const {
  gfx::Insets insets = GetInsets();
  int available_width = width() - insets.width();
  int content_width = text_size_.width() + extra_width;
  int content_x = 0;
  switch(alignment_) {
    case ALIGN_LEFT:
      content_x = insets.left();
      break;
    case ALIGN_RIGHT:
      content_x = width() - insets.right() - content_width;
      if (content_x < insets.left())
        content_x = insets.left();
      break;
    case ALIGN_CENTER:
      content_x = insets.left() + std::max(0,
          (available_width - content_width) / 2);
      break;
  }
  content_width = std::min(content_width,
                           width() - insets.right() - content_x);
  int available_height = height() - insets.height();
  int content_y = (available_height - text_size_.height()) / 2 + insets.top();

  gfx::Rect bounds(content_x, content_y, content_width, text_size_.height());
  return bounds;
}

gfx::Rect TextButtonBase::GetTextBounds() const {
  return GetContentBounds(0);
}

void TextButtonBase::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
  focus_painter_ = focus_painter.Pass();
}

void TextButtonBase::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
  if (mode == PB_NORMAL) {
    OnPaintBackground(canvas);
    OnPaintBorder(canvas);
    Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
  }

  gfx::Rect text_bounds(GetTextBounds());
  if (text_bounds.width() > 0) {
    // Because the text button can (at times) draw multiple elements on the
    // canvas, we can not mirror the button by simply flipping the canvas as
    // doing this will mirror the text itself. Flipping the canvas will also
    // make the icons look wrong because icons are almost always represented as
    // direction-insensitive images and such images should never be flipped
    // horizontally.
    //
    // Due to the above, we must perform the flipping manually for RTL UIs.
    text_bounds.set_x(GetMirroredXForRect(text_bounds));

    SkColor text_color = (show_multiple_icon_states_ &&
        (state() == STATE_HOVERED || state() == STATE_PRESSED)) ?
            color_hover_ : color_;

    int draw_string_flags = gfx::Canvas::DefaultCanvasTextAlignment() |
        ComputeCanvasStringFlags();

    if (mode == PB_FOR_DRAG) {
      // Disable sub-pixel rendering as background is transparent.
      draw_string_flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING;
      canvas->DrawStringRectWithHalo(text_, font_list_,
                                     SK_ColorBLACK, SK_ColorWHITE,
                                     text_bounds, draw_string_flags);
    } else {
      canvas->DrawStringRectWithFlags(text_, font_list_, text_color,
                                      text_bounds, draw_string_flags);
    }
  }
}

gfx::Size TextButtonBase::GetMinimumSize() {
  return max_text_size_;
}

void TextButtonBase::OnEnabledChanged() {
  // We should always call UpdateColor() since the state of the button might be
  // changed by other functions like CustomButton::SetState().
  UpdateColor();
  CustomButton::OnEnabledChanged();
}

const char* TextButtonBase::GetClassName() const {
  return kViewClassName;
}

void TextButtonBase::OnNativeThemeChanged(const ui::NativeTheme* theme) {
  if (use_enabled_color_from_theme_) {
    color_enabled_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonEnabledColor);
  }
  if (use_disabled_color_from_theme_) {
    color_disabled_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonDisabledColor);
  }
  if (use_highlight_color_from_theme_) {
    color_highlight_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonHighlightColor);
  }
  if (use_hover_color_from_theme_) {
    color_hover_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonHoverColor);
  }
  UpdateColor();
}

gfx::Rect TextButtonBase::GetThemePaintRect() const {
  return GetLocalBounds();
}

ui::NativeTheme::State TextButtonBase::GetThemeState(
    ui::NativeTheme::ExtraParams* params) const {
  GetExtraParams(params);
  switch(state()) {
    case STATE_DISABLED:
      return ui::NativeTheme::kDisabled;
    case STATE_NORMAL:
      return ui::NativeTheme::kNormal;
    case STATE_HOVERED:
      return ui::NativeTheme::kHovered;
    case STATE_PRESSED:
      return ui::NativeTheme::kPressed;
    default:
      NOTREACHED() << "Unknown state: " << state();
      return ui::NativeTheme::kNormal;
  }
}

const gfx::Animation* TextButtonBase::GetThemeAnimation() const {
#if defined(OS_WIN)
  if (GetNativeTheme() == ui::NativeThemeWin::instance()) {
    return ui::NativeThemeWin::instance()->IsThemingActive() ?
        hover_animation_.get() : NULL;
  }
#endif
  return hover_animation_.get();
}

ui::NativeTheme::State TextButtonBase::GetBackgroundThemeState(
  ui::NativeTheme::ExtraParams* params) const {
  GetExtraParams(params);
  return ui::NativeTheme::kNormal;
}

ui::NativeTheme::State TextButtonBase::GetForegroundThemeState(
  ui::NativeTheme::ExtraParams* params) const {
  GetExtraParams(params);
  return ui::NativeTheme::kHovered;
}


// TextButton -----------------------------------------------------------------

TextButton::TextButton(ButtonListener* listener, const base::string16& text)
    : TextButtonBase(listener, text),
      icon_placement_(ICON_ON_LEFT),
      has_hover_icon_(false),
      has_pushed_icon_(false),
      icon_text_spacing_(kDefaultIconTextSpacing),
      ignore_minimum_size_(true),
      full_justification_(false) {
  SetBorder(scoped_ptr<Border>(new TextButtonDefaultBorder));
  SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets(
                      gfx::Insets(kFocusRectInset, kFocusRectInset,
                                  kFocusRectInset, kFocusRectInset)));
}

TextButton::~TextButton() {
}

void TextButton::SetIcon(const gfx::ImageSkia& icon) {
  icon_ = icon;
  SchedulePaint();
}

void TextButton::SetHoverIcon(const gfx::ImageSkia& icon) {
  icon_hover_ = icon;
  has_hover_icon_ = true;
  SchedulePaint();
}

void TextButton::SetPushedIcon(const gfx::ImageSkia& icon) {
  icon_pushed_ = icon;
  has_pushed_icon_ = true;
  SchedulePaint();
}

gfx::Size TextButton::GetPreferredSize() {
  gfx::Size prefsize(TextButtonBase::GetPreferredSize());
  prefsize.Enlarge(icon_.width(), 0);
  prefsize.set_height(std::max(prefsize.height(), icon_.height()));

  // Use the max size to set the button boundaries.
  if (icon_.width() > 0 && !text_.empty())
    prefsize.Enlarge(icon_text_spacing_, 0);

  if (max_width_ > 0)
    prefsize.set_width(std::min(max_width_, prefsize.width()));

#if defined(OS_WIN)
  // Clamp the size returned to at least the minimum size.
  if (!ignore_minimum_size_) {
    gfx::PlatformFontWin* platform_font = static_cast<gfx::PlatformFontWin*>(
        font_list_.GetPrimaryFont().platform_font());
    prefsize.set_width(std::max(
        prefsize.width(),
        platform_font->horizontal_dlus_to_pixels(kMinWidthDLUs)));
    prefsize.set_height(std::max(
        prefsize.height(),
        platform_font->vertical_dlus_to_pixels(kMinHeightDLUs)));
  }
#endif

  prefsize.set_width(std::max(prefsize.width(), min_width_));
  prefsize.set_height(std::max(prefsize.height(), min_height_));

  return prefsize;
}

void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
  if (full_justification_ && icon_placement_ == ICON_ON_LEFT)
      set_alignment(ALIGN_RIGHT);

  TextButtonBase::PaintButton(canvas, mode);

  const gfx::ImageSkia& icon = GetImageToPaint();

  if (icon.width() > 0) {
    gfx::Rect text_bounds = GetTextBounds();
    int icon_x = 0;
    int spacing = text_.empty() ? 0 : icon_text_spacing_;
    gfx::Insets insets = GetInsets();
    switch (icon_placement_) {
      case ICON_ON_LEFT:
        icon_x = full_justification_ ? insets.left()
                                     : text_bounds.x() - icon.width() - spacing;
        break;
      case ICON_ON_RIGHT:
        icon_x = full_justification_ ? width() - insets.right() - icon.width()
                                     : text_bounds.right() + spacing;
        break;
      case ICON_CENTERED:
        DCHECK(text_.empty());
        icon_x = (width() - insets.width() - icon.width()) / 2 + insets.left();
        break;
      default:
        NOTREACHED();
        break;
    }

    int available_height = height() - insets.height();
    int icon_y = (available_height - icon.height()) / 2 + insets.top();

    // Mirroring the icon position if necessary.
    gfx::Rect icon_bounds(icon_x, icon_y, icon.width(), icon.height());
    icon_bounds.set_x(GetMirroredXForRect(icon_bounds));
    canvas->DrawImageInt(icon, icon_bounds.x(), icon_bounds.y());
  }
}

void TextButton::set_ignore_minimum_size(bool ignore_minimum_size) {
  ignore_minimum_size_ = ignore_minimum_size;
}

void TextButton::set_full_justification(bool full_justification) {
  full_justification_ = full_justification;
}

const char* TextButton::GetClassName() const {
  return kViewClassName;
}

ui::NativeTheme::Part TextButton::GetThemePart() const {
  return ui::NativeTheme::kPushButton;
}

void TextButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const {
  TextButtonBase::GetExtraParams(params);
  params->button.is_default = is_default_;
}

gfx::Rect TextButton::GetTextBounds() const {
  int extra_width = 0;

  const gfx::ImageSkia& icon = GetImageToPaint();
  if (icon.width() > 0)
    extra_width = icon.width() + (text_.empty() ? 0 : icon_text_spacing_);

  gfx::Rect bounds(GetContentBounds(extra_width));

  if (extra_width > 0) {
    // Make sure the icon is always fully visible.
    if (icon_placement_ == ICON_ON_LEFT) {
      bounds.Inset(extra_width, 0, 0, 0);
    } else if (icon_placement_ == ICON_ON_RIGHT) {
      bounds.Inset(0, 0, extra_width, 0);
    }
  }

  return bounds;
}

const gfx::ImageSkia& TextButton::GetImageToPaint() const {
  if (show_multiple_icon_states_) {
    if (has_hover_icon_ && (state() == STATE_HOVERED))
      return icon_hover_;
    if (has_pushed_icon_ && (state() == STATE_PRESSED))
      return icon_pushed_;
  }
  return icon_;
}

}  // namespace views

/* [<][>][^][v][top][bottom][index][help] */