// 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 #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; // 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 // override OnPaintFocusBorder and do something different. static const int kFocusRectInset = 3; // 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(&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( 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(&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(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(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; } void TextButton::OnPaintFocusBorder(gfx::Canvas* canvas) { if ((IsFocusable() || IsAccessibilityFocusableInRootView()) && HasFocus()) { gfx::Rect rect(GetLocalBounds()); rect.Inset(kFocusRectInset, kFocusRectInset); canvas->DrawFocusRect(rect); } } 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) if ((IsFocusable() || IsAccessibilityFocusableInRootView()) && HasFocus()) { gfx::Rect rect(GetLocalBounds()); rect.Inset(kFocusRectInset, kFocusRectInset); 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