blob: b9c7bcceb10bb22f390ae39a623ecec85fb1da15 [file] [log] [blame]
// Copyright (c) 2011 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 "views/controls/button/text_button.h"
#include <algorithm>
#include "base/logging.h"
#include "grit/ui_resources.h"
#include "ui/base/animation/throb_animation.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas_skia.h"
#include "views/controls/button/button.h"
#include "views/events/event.h"
#include "views/widget/widget.h"
#if defined(OS_WIN)
#include "skia/ext/skia_utils_win.h"
#include "ui/gfx/native_theme_win.h"
#include "ui/gfx/platform_font_win.h"
#endif
namespace views {
#if defined(OS_WIN)
// The min size in DLUs comes from
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp
static const int kMinWidthDLUs = 50;
static const int kMinHeightDLUs = 14;
#endif
// Default space between the icon and text.
static const int kDefaultIconTextSpacing = 5;
// Preferred padding between text and edge
static const int kPreferredPaddingHorizontal = 6;
static const int kPreferredPaddingVertical = 5;
// Preferred padding between text and edge for NativeTheme border
static const int kPreferredNativeThemePaddingHorizontal = 12;
static const int kPreferredNativeThemePaddingVertical = 5;
// Default background color for buttons.
const SkColor kBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
#if defined(USE_AURA)
// static
const SkColor TextButtonBase::kEnabledColor = SkColorSetRGB(0x44, 0x44, 0x44);
// static
const SkColor TextButtonBase::kHighlightColor = SkColorSetRGB(0, 0, 0);
// static
const SkColor TextButtonBase::kDisabledColor = SkColorSetRGB(0x99, 0x99, 0x99);
// static
const SkColor TextButtonBase::kHoverColor = TextButton::kEnabledColor;
#else // !defined(USE_AURA)
// static
const SkColor TextButtonBase::kEnabledColor = SkColorSetRGB(6, 45, 117);
// static
const SkColor TextButtonBase::kHighlightColor =
SkColorSetARGB(200, 255, 255, 255);
// static
const SkColor TextButtonBase::kDisabledColor = SkColorSetRGB(161, 161, 146);
// static
const SkColor TextButtonBase::kHoverColor = TextButton::kEnabledColor;
#endif // defined(USE_AURA)
// How long the hover fade animation should last.
static const int kHoverAnimationDurationMs = 170;
// static
const char TextButtonBase::kViewClassName[] = "views/TextButtonBase";
// static
const char TextButton::kViewClassName[] = "views/TextButton";
// static
const char NativeTextButton::kViewClassName[] = "views/NativeTextButton";
static int PrefixTypeToCanvasType(TextButton::PrefixType type) {
switch (type) {
case TextButton::PREFIX_HIDE:
return gfx::Canvas::HIDE_PREFIX;
case TextButton::PREFIX_SHOW:
return gfx::Canvas::SHOW_PREFIX;
case TextButton::PREFIX_NONE:
return 0;
default:
NOTREACHED();
return 0;
}
}
////////////////////////////////////////////////////////////////////////////////
//
// TextButtonBorder - constructors, destructors, initialization
//
////////////////////////////////////////////////////////////////////////////////
TextButtonBorder::TextButtonBorder()
: vertical_padding_(kPreferredPaddingVertical) {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
BorderImageSet normal_set = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
set_normal_set(normal_set);
BorderImageSet hot_set = {
rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_H),
rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_H),
};
set_hot_set(hot_set);
BorderImageSet pushed_set = {
rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_P),
rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_P),
};
set_pushed_set(pushed_set);
}
TextButtonBorder::~TextButtonBorder() {
}
////////////////////////////////////////////////////////////////////////////////
//
// TextButtonBorder - painting
//
////////////////////////////////////////////////////////////////////////////////
void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) const {
const TextButton* button = static_cast<const TextButton*>(&view);
int state = button->state();
const BorderImageSet* set = &normal_set_;
if (button->show_multiple_icon_states() &&
((state == TextButton::BS_HOT) || (state == TextButton::BS_PUSHED)))
set = (state == TextButton::BS_HOT) ? &hot_set_ : &pushed_set_;
if (set->top_left) {
if (button->GetAnimation()->is_animating()) {
// TODO(pkasting): Really this should crossfade between states so it could
// handle the case of having a non-NULL |normal_set_|.
canvas->SaveLayerAlpha(static_cast<uint8>(
button->GetAnimation()->CurrentValueBetween(0, 255)));
canvas->GetSkCanvas()->drawARGB(0, 255, 255, 255,
SkXfermode::kClear_Mode);
Paint(view, canvas, *set);
canvas->Restore();
} else {
Paint(view, canvas, *set);
}
}
}
void TextButtonBorder::Paint(const View& view,
gfx::Canvas* canvas,
const BorderImageSet& set) const {
DCHECK(set.top_left);
int width = view.bounds().width();
int height = view.bounds().height();
int tl_width = set.top_left->width();
int tl_height = set.top_left->height();
int t_height = set.top->height();
int tr_height = set.top_right->height();
int l_width = set.left->width();
int r_width = set.right->width();
int bl_width = set.bottom_left->width();
int bl_height = set.bottom_left->height();
int b_height = set.bottom->height();
int br_width = set.bottom_right->width();
int br_height = set.bottom_right->height();
canvas->DrawBitmapInt(*set.top_left, 0, 0);
canvas->DrawBitmapInt(*set.top, 0, 0, set.top->width(), t_height, tl_width, 0,
width - tl_width - set.top_right->width(), t_height, false);
canvas->DrawBitmapInt(*set.top_right, width - set.top_right->width(), 0);
canvas->DrawBitmapInt(*set.left, 0, 0, l_width, set.left->height(), 0,
tl_height, tl_width, height - tl_height - bl_height, false);
canvas->DrawBitmapInt(*set.center, 0, 0, set.center->width(),
set.center->height(), l_width, t_height, width - l_width - r_width,
height - t_height - b_height, false);
canvas->DrawBitmapInt(*set.right, 0, 0, r_width, set.right->height(),
width - r_width, tr_height, r_width,
height - tr_height - br_height, false);
canvas->DrawBitmapInt(*set.bottom_left, 0, height - bl_height);
canvas->DrawBitmapInt(*set.bottom, 0, 0, set.bottom->width(), b_height,
bl_width, height - b_height,
width - bl_width - br_width, b_height, false);
canvas->DrawBitmapInt(*set.bottom_right, width - br_width,
height - br_height);
}
void TextButtonBorder::GetInsets(gfx::Insets* insets) const {
insets->Set(vertical_padding_, kPreferredPaddingHorizontal,
vertical_padding_, kPreferredPaddingHorizontal);
}
////////////////////////////////////////////////////////////////////////////////
//
// TextButtonNativeThemeBorder
//
////////////////////////////////////////////////////////////////////////////////
TextButtonNativeThemeBorder::TextButtonNativeThemeBorder(
NativeThemeDelegate* delegate)
: delegate_(delegate) {
}
TextButtonNativeThemeBorder::~TextButtonNativeThemeBorder() {
}
void TextButtonNativeThemeBorder::Paint(const View& view,
gfx::Canvas* canvas) const {
const TextButtonBase* tb = static_cast<const TextButton*>(&view);
const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
gfx::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.
gfx::NativeTheme::ExtraParams prev_extra;
gfx::NativeTheme::State prev_state =
delegate_->GetBackgroundThemeState(&prev_extra);
native_theme->Paint(
canvas->GetSkCanvas(), part, prev_state, rect, prev_extra);
// Composite foreground state above it.
gfx::NativeTheme::ExtraParams extra;
gfx::NativeTheme::State state = delegate_->GetForegroundThemeState(&extra);
int alpha = delegate_->GetThemeAnimation()->CurrentValueBetween(0, 255);
canvas->SaveLayerAlpha(static_cast<uint8>(alpha));
native_theme->Paint(canvas->GetSkCanvas(), part, state, rect, extra);
canvas->Restore();
} else {
gfx::NativeTheme::ExtraParams extra;
gfx::NativeTheme::State state = delegate_->GetThemeState(&extra);
native_theme->Paint(canvas->GetSkCanvas(), part, state, rect, extra);
}
}
void TextButtonNativeThemeBorder::GetInsets(gfx::Insets* insets) const {
insets->Set(kPreferredNativeThemePaddingVertical,
kPreferredNativeThemePaddingHorizontal,
kPreferredNativeThemePaddingVertical,
kPreferredNativeThemePaddingHorizontal);
}
////////////////////////////////////////////////////////////////////////////////
// TextButtonBase, public:
TextButtonBase::TextButtonBase(ButtonListener* listener, const string16& text)
: CustomButton(listener),
alignment_(ALIGN_LEFT),
font_(ResourceBundle::GetSharedInstance().GetFont(
ResourceBundle::BaseFont)),
color_(kEnabledColor),
color_enabled_(kEnabledColor),
color_disabled_(kDisabledColor),
color_highlight_(kHighlightColor),
color_hover_(kHoverColor),
text_halo_color_(0),
has_text_halo_(false),
active_text_shadow_color_(0),
inactive_text_shadow_color_(0),
has_shadow_(false),
shadow_offset_(gfx::Point(1, 1)),
max_width_(0),
show_multiple_icon_states_(true),
is_default_(false),
multi_line_(false),
prefix_type_(PREFIX_NONE) {
SetText(text);
SetAnimationDuration(kHoverAnimationDurationMs);
}
TextButtonBase::~TextButtonBase() {
}
void TextButtonBase::SetIsDefault(bool is_default) {
if (is_default == is_default_)
return;
is_default_ = is_default;
if (is_default_)
AddAccelerator(Accelerator(ui::VKEY_RETURN, false, false, false));
else
RemoveAccelerator(Accelerator(ui::VKEY_RETURN, false, false, false));
SchedulePaint();
}
void TextButtonBase::SetText(const string16& text) {
text_ = text;
SetAccessibleName(text);
UpdateTextSize();
}
void TextButtonBase::SetFont(const gfx::Font& font) {
font_ = font;
UpdateTextSize();
}
void TextButtonBase::SetEnabledColor(SkColor color) {
color_enabled_ = color;
UpdateColor();
}
void TextButtonBase::SetDisabledColor(SkColor color) {
color_disabled_ = color;
UpdateColor();
}
void TextButtonBase::SetHighlightColor(SkColor color) {
color_highlight_ = color;
}
void TextButtonBase::SetHoverColor(SkColor color) {
color_hover_ = color;
}
void TextButtonBase::SetTextHaloColor(SkColor color) {
text_halo_color_ = color;
has_text_halo_ = true;
}
void TextButtonBase::SetTextShadowColors(SkColor active_color,
SkColor inactive_color) {
active_text_shadow_color_ = active_color;
inactive_text_shadow_color_ = inactive_color;
has_shadow_ = true;
}
void TextButtonBase::SetTextShadowOffset(int x, int y) {
shadow_offset_.SetPoint(x, y);
}
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::ClearEmbellishing() {
has_shadow_ = false;
has_text_halo_ = false;
}
void TextButtonBase::SetMultiLine(bool multi_line) {
if (multi_line != multi_line_) {
multi_line_ = multi_line;
UpdateTextSize();
PreferredSizeChanged();
SchedulePaint();
}
}
gfx::Size TextButtonBase::GetPreferredSize() {
gfx::Insets insets = GetInsets();
// Use the max size to set the button boundaries.
gfx::Size prefsize(max_text_size_.width() + insets.width(),
max_text_size_.height() + insets.height());
if (max_width_ > 0)
prefsize.set_width(std::min(max_width_, prefsize.width()));
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);
return text_size.height() + GetInsets().height();
}
void TextButtonBase::OnPaint(gfx::Canvas* canvas) {
PaintButton(canvas, PB_NORMAL);
}
const ui::Animation* TextButtonBase::GetAnimation() const {
return hover_animation_.get();
}
void TextButtonBase::UpdateColor() {
color_ = View::IsEnabled() ? color_enabled_ : color_disabled_;
}
void TextButtonBase::UpdateTextSize() {
CalculateTextSize(&text_size_, width());
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_.GetHeight();
int w = multi_line_ ? max_width : 0;
int flags = ComputeCanvasStringFlags();
if (!multi_line_)
flags |= gfx::Canvas::NO_ELLIPSIS;
gfx::CanvasSkia::SizeStringInt(text_, font_, &w, &h, flags);
// Add 2 extra pixels to width and height when text halo is used.
if (has_text_halo_) {
w += 2;
h += 2;
}
text_size->SetSize(w, h);
}
int TextButtonBase::ComputeCanvasStringFlags() const {
int flags = PrefixTypeToCanvasType(prefix_type_);
if (multi_line_) {
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::GetExtraParams(
gfx::NativeTheme::ExtraParams* params) const {
params->button.checked = false;
params->button.indeterminate = false;
params->button.is_default = false;
params->button.has_border = false;
params->button.classic_state = 0;
params->button.background_color = kBackgroundColor;
}
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::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
if (mode == PB_NORMAL) {
OnPaintBackground(canvas);
OnPaintBorder(canvas);
OnPaintFocusBorder(canvas);
}
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 bitmaps and such bitmaps 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() == BS_HOT || state() == BS_PUSHED)) ? color_hover_ : color_;
int draw_string_flags = gfx::CanvasSkia::DefaultCanvasTextAlignment() |
ComputeCanvasStringFlags();
if (mode == PB_FOR_DRAG) {
#if defined(OS_WIN)
// TODO(erg): Either port DrawStringWithHalo to linux or find an
// alternative here.
canvas->AsCanvasSkia()->DrawStringWithHalo(
text_, font_, text_color, color_highlight_, text_bounds.x(),
text_bounds.y(), text_bounds.width(), text_bounds.height(),
draw_string_flags);
#else
canvas->DrawStringInt(text_,
font_,
text_color,
text_bounds.x(),
text_bounds.y(),
text_bounds.width(),
text_bounds.height(),
draw_string_flags);
#endif
} else if (has_text_halo_) {
canvas->AsCanvasSkia()->DrawStringWithHalo(
text_, font_, text_color, text_halo_color_,
text_bounds.x(), text_bounds.y(), text_bounds.width(),
text_bounds.height(), draw_string_flags);
} else if (has_shadow_) {
SkColor shadow_color =
GetWidget()->IsActive() ? active_text_shadow_color_ :
inactive_text_shadow_color_;
canvas->DrawStringInt(text_,
font_,
shadow_color,
text_bounds.x() + shadow_offset_.x(),
text_bounds.y() + shadow_offset_.y(),
text_bounds.width(),
text_bounds.height(),
draw_string_flags);
canvas->DrawStringInt(text_,
font_,
text_color,
text_bounds.x(),
text_bounds.y(),
text_bounds.width(),
text_bounds.height(),
draw_string_flags);
} else {
canvas->DrawStringInt(text_,
font_,
text_color,
text_bounds.x(),
text_bounds.y(),
text_bounds.width(),
text_bounds.height(),
draw_string_flags);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// TextButtonBase, View overrides:
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();
}
std::string TextButtonBase::GetClassName() const {
return kViewClassName;
}
////////////////////////////////////////////////////////////////////////////////
// TextButtonBase, NativeThemeDelegate overrides:
gfx::Rect TextButtonBase::GetThemePaintRect() const {
return GetLocalBounds();
}
gfx::NativeTheme::State TextButtonBase::GetThemeState(
gfx::NativeTheme::ExtraParams* params) const {
GetExtraParams(params);
switch(state()) {
case BS_DISABLED:
return gfx::NativeTheme::kDisabled;
case BS_NORMAL:
return gfx::NativeTheme::kNormal;
case BS_HOT:
return gfx::NativeTheme::kHovered;
case BS_PUSHED:
return gfx::NativeTheme::kPressed;
default:
NOTREACHED() << "Unknown state: " << state();
return gfx::NativeTheme::kNormal;
}
}
const ui::Animation* TextButtonBase::GetThemeAnimation() const {
#if defined(USE_AURA)
return hover_animation_.get();
#elif defined(OS_WIN)
return gfx::NativeThemeWin::instance()->IsThemingActive()
? hover_animation_.get() : NULL;
#else
return hover_animation_.get();
#endif
}
gfx::NativeTheme::State TextButtonBase::GetBackgroundThemeState(
gfx::NativeTheme::ExtraParams* params) const {
GetExtraParams(params);
return gfx::NativeTheme::kNormal;
}
gfx::NativeTheme::State TextButtonBase::GetForegroundThemeState(
gfx::NativeTheme::ExtraParams* params) const {
GetExtraParams(params);
return gfx::NativeTheme::kHovered;
}
////////////////////////////////////////////////////////////////////////////////
//
// TextButton
//
////////////////////////////////////////////////////////////////////////////////
TextButton::TextButton(ButtonListener* listener, const 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) {
set_border(new TextButtonBorder);
}
TextButton::~TextButton() {
}
void TextButton::SetIcon(const SkBitmap& icon) {
icon_ = icon;
SchedulePaint();
}
void TextButton::SetHoverIcon(const SkBitmap& icon) {
icon_hover_ = icon;
has_hover_icon_ = true;
SchedulePaint();
}
void TextButton::SetPushedIcon(const SkBitmap& 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_.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)));
}
// GTK returns a meaningful preferred size so that we don't need to adjust
// the preferred size as we do on windows.
#endif
return prefsize;
}
void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
TextButtonBase::PaintButton(canvas, mode);
const SkBitmap& icon = GetImageToPaint();
if (icon.width() > 0) {
gfx::Rect text_bounds = GetTextBounds();
int icon_x;
int spacing = text_.empty() ? 0 : icon_text_spacing_;
if (icon_placement_ == ICON_ON_LEFT) {
icon_x = text_bounds.x() - icon.width() - spacing;
} else {
icon_x = text_bounds.right() + spacing;
}
gfx::Insets insets = GetInsets();
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->DrawBitmapInt(icon, icon_bounds.x(), icon_bounds.y());
}
}
void TextButton::set_ignore_minimum_size(bool ignore_minimum_size) {
ignore_minimum_size_ = ignore_minimum_size;
}
std::string TextButton::GetClassName() const {
return kViewClassName;
}
gfx::NativeTheme::Part TextButton::GetThemePart() const {
return gfx::NativeTheme::kPushButton;
}
void TextButton::GetExtraParams(gfx::NativeTheme::ExtraParams* params) const {
TextButtonBase::GetExtraParams(params);
params->button.is_default = is_default_;
}
gfx::Rect TextButton::GetTextBounds() const {
int extra_width = 0;
const SkBitmap& 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 {
bounds.Inset(0, 0, extra_width, 0);
}
}
return bounds;
}
const SkBitmap& TextButton::GetImageToPaint() const {
if (show_multiple_icon_states_) {
if (has_hover_icon_ && (state() == BS_HOT))
return icon_hover_;
if (has_pushed_icon_ && (state() == BS_PUSHED))
return icon_pushed_;
}
return icon_;
}
////////////////////////////////////////////////////////////////////////////////
//
// NativeTextButton
//
////////////////////////////////////////////////////////////////////////////////
NativeTextButton::NativeTextButton(ButtonListener* listener)
: TextButton(listener, string16()) {
Init();
}
NativeTextButton::NativeTextButton(ButtonListener* listener,
const string16& text)
: TextButton(listener, text) {
Init();
}
void NativeTextButton::Init() {
#if defined(OS_WIN)
// Windows will like to show its own colors.
// Halos and such are ignored as they are always set by specific calls.
color_enabled_ = skia::COLORREFToSkColor(GetSysColor(COLOR_BTNTEXT));
color_disabled_ = skia::COLORREFToSkColor(GetSysColor(COLOR_GRAYTEXT));
color_hover_ = color_ = color_enabled_;
#endif
set_border(new TextButtonNativeThemeBorder(this));
set_ignore_minimum_size(false);
set_alignment(ALIGN_CENTER);
set_focusable(true);
}
gfx::Size NativeTextButton::GetMinimumSize() {
return GetPreferredSize();
}
std::string NativeTextButton::GetClassName() const {
return kViewClassName;
}
void NativeTextButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
#if defined(OS_WIN)
// On windows, make sure the focus border is inset wrt the entire button so
// that the native text button appears more like a windows button.
if ((IsFocusable() || IsAccessibilityFocusableInRootView()) && HasFocus()) {
gfx::Rect rect(GetLocalBounds());
rect.Inset(3, 3);
canvas->DrawFocusRect(rect);
}
#else
TextButton::OnPaintFocusBorder(canvas);
#endif
}
void NativeTextButton::GetExtraParams(
gfx::NativeTheme::ExtraParams* params) const {
TextButton::GetExtraParams(params);
params->button.has_border = true;
}
} // namespace views