| // 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 |