diff options
author | rogerta@chromium.org <rogerta@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-28 19:05:46 +0000 |
---|---|---|
committer | rogerta@chromium.org <rogerta@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-28 19:05:46 +0000 |
commit | 184167dc22992b074d64839878259b4eee00ae4b (patch) | |
tree | b3539992b6e9056b1bf48dfaebb7bcd0651d0f30 /views | |
parent | b2384caf56ea8da6d6cbd44c5d22270a18296fa2 (diff) | |
download | chromium_src-184167dc22992b074d64839878259b4eee00ae4b.zip chromium_src-184167dc22992b074d64839878259b4eee00ae4b.tar.gz chromium_src-184167dc22992b074d64839878259b4eee00ae4b.tar.bz2 |
Resubmitting change to fix a build break in the arm builder. The only change
from the original code is in text_button.cc, line 402, where text_x is
initialized to 0.
Add classes for native themed push buttons, radio buttons, and checkboxes.
These controls expose the same public interface and the existing controls
of the same type to make it easier to change between the implementations.
BUG=None
TEST=The new controls should look and feel like native platform controls
R=ben@chromium.org
Review URL: http://codereview.chromium.org/6853015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83373 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/controls/button/checkbox.cc | 100 | ||||
-rw-r--r-- | views/controls/button/checkbox.h | 55 | ||||
-rw-r--r-- | views/controls/button/radio_button.cc | 90 | ||||
-rw-r--r-- | views/controls/button/radio_button.h | 31 | ||||
-rw-r--r-- | views/controls/button/text_button.cc | 505 | ||||
-rw-r--r-- | views/controls/button/text_button.h | 191 | ||||
-rw-r--r-- | views/examples/button_example.cc | 62 | ||||
-rw-r--r-- | views/examples/button_example.h | 5 | ||||
-rw-r--r-- | views/examples/examples_main.cc | 5 | ||||
-rw-r--r-- | views/examples/native_theme_button_example.cc | 6 | ||||
-rw-r--r-- | views/examples/native_theme_button_example.h | 6 | ||||
-rw-r--r-- | views/examples/native_theme_checkbox_example.cc | 39 | ||||
-rw-r--r-- | views/examples/native_theme_checkbox_example.h | 46 | ||||
-rw-r--r-- | views/examples/radio_button_example.cc | 33 | ||||
-rw-r--r-- | views/examples/radio_button_example.h | 6 | ||||
-rw-r--r-- | views/native_theme_delegate.h | 52 | ||||
-rw-r--r-- | views/native_theme_painter.cc | 3 | ||||
-rw-r--r-- | views/native_theme_painter.h | 38 | ||||
-rw-r--r-- | views/views.gyp | 4 |
19 files changed, 1033 insertions, 244 deletions
diff --git a/views/controls/button/checkbox.cc b/views/controls/button/checkbox.cc index 3425c98..45d0501 100644 --- a/views/controls/button/checkbox.cc +++ b/views/controls/button/checkbox.cc @@ -14,6 +14,9 @@ namespace views { // static const char Checkbox::kViewClassName[] = "views/Checkbox"; +// static +const char CheckboxNt::kViewClassName[] = "views/CheckboxNt"; + static const int kCheckboxLabelSpacing = 4; static const int kLabelFocusPaddingHorizontal = 2; static const int kLabelFocusPaddingVertical = 1; @@ -218,4 +221,101 @@ void Checkbox::Init(const std::wstring& label_text) { AddChildView(label_); } +//////////////////////////////////////////////////////////////////////////////// +// CheckboxNt, public: + +CheckboxNt::CheckboxNt(const std::wstring& label) + : TextButtonBase(NULL, label), + checked_(false) { + set_border(new TextButtonNativeThemeBorder(this)); +} + +CheckboxNt::~CheckboxNt() { +} + +void CheckboxNt::SetChecked(bool checked) { + checked_ = checked; + SchedulePaint(); +} + +void CheckboxNt::SetMultiLine(bool multiline) { + NOTREACHED() << "Not yet implemented"; +} + +gfx::Size CheckboxNt::GetPreferredSize() { + gfx::Size prefsize(TextButtonBase::GetPreferredSize()); + gfx::NativeTheme::ExtraParams extra; + gfx::NativeTheme::State state = GetThemeState(&extra); + gfx::Size size = gfx::NativeTheme::instance()->GetPartSize(GetThemePart(), + state, + extra); + prefsize.Enlarge(size.width(), 0); + prefsize.set_height(std::max(prefsize.height(), size.height())); + + if (max_width_ > 0) + prefsize.set_width(std::min(max_width_, prefsize.width())); + + return prefsize; +} + +std::string CheckboxNt::GetClassName() const { + return kViewClassName; +} + +void CheckboxNt::GetAccessibleState(ui::AccessibleViewState* state) { + TextButtonBase::GetAccessibleState(state); + state->role = ui::AccessibilityTypes::ROLE_CHECKBUTTON; + state->state = checked() ? ui::AccessibilityTypes::STATE_CHECKED : 0; +} + +void CheckboxNt::OnPaintFocusBorder(gfx::Canvas* canvas) { + if (HasFocus() && (IsFocusable() || IsAccessibilityFocusableInRootView())) { + gfx::Rect bounds(GetTextBounds()); + // Increate the bounding box by one on each side so that that focus border + // does not draw on top of the letters. + bounds.Inset(-1, -1, -1, -1); + canvas->DrawFocusRect(bounds.x(), bounds.y(), bounds.width(), + bounds.height()); + } +} + +void CheckboxNt::NotifyClick(const views::Event& event) { + SetChecked(!checked()); + RequestFocus(); + TextButtonBase::NotifyClick(event); +} + +gfx::NativeTheme::Part CheckboxNt::GetThemePart() const { + return gfx::NativeTheme::kCheckbox; +} + +gfx::Rect CheckboxNt::GetThemePaintRect() const { + gfx::NativeTheme::ExtraParams extra; + gfx::NativeTheme::State state = GetThemeState(&extra); + gfx::Size size(gfx::NativeTheme::instance()->GetPartSize(GetThemePart(), + state, + extra)); + gfx::Insets insets = GetInsets(); + gfx::Rect rect(insets.left(), 0, size.width(), height()); + rect.set_x(GetMirroredXForRect(rect)); + return rect; +} + +void CheckboxNt::GetExtraParams(gfx::NativeTheme::ExtraParams* params) const { + TextButtonBase::GetExtraParams(params); + params->button.is_default = false; + params->button.checked = checked_; +} + +gfx::Rect CheckboxNt::GetTextBounds() const { + gfx::Rect bounds(TextButtonBase::GetTextBounds()); + gfx::NativeTheme::ExtraParams extra; + gfx::NativeTheme::State state = GetThemeState(&extra); + gfx::Size size(gfx::NativeTheme::instance()->GetPartSize(GetThemePart(), + state, + extra)); + bounds.Offset(size.width() + kCheckboxLabelSpacing, 0); + return bounds; +} + } // namespace views diff --git a/views/controls/button/checkbox.h b/views/controls/button/checkbox.h index e251406..8d30a33 100644 --- a/views/controls/button/checkbox.h +++ b/views/controls/button/checkbox.h @@ -9,6 +9,7 @@ #include <string> #include "views/controls/button/native_button.h" +#include "views/controls/button/text_button.h" namespace views { @@ -87,6 +88,60 @@ class Checkbox : public NativeButtonBase { DISALLOW_COPY_AND_ASSIGN(Checkbox); }; +// A native themed class representing a checkbox. This class does not use +// platform specific objects to replicate the native platforms looks and feel. +// +// This class will eventually be renamed to Checkbox to replace the class +// above. +// +// TODO: A Checkbox feature not support by CheckboxNt is multi-line. +class CheckboxNt : public TextButtonBase { + public: + explicit CheckboxNt(const std::wstring& label); + virtual ~CheckboxNt(); + + // Sets a listener for this checkbox. Checkboxes aren't required to have them + // since their state can be read independently of them being toggled. + void set_listener(ButtonListener* listener) { listener_ = listener; } + + // Sets/Gets whether or not the checkbox is checked. + virtual void SetChecked(bool checked); + bool checked() const { return checked_; } + + // Sets whether or not the checkbox label should wrap multiple lines of text. + // If true, long lines are wrapped, and this is reflected in the preferred + // size returned by GetPreferredSize. If false, text that will not fit within + // the available bounds for the label will be cropped. + // TODO: not yet implemented. + void SetMultiLine(bool multiline); + + protected: + // Overridden from View: + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual std::string GetClassName() const OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; + + private: + // Overridden from Button: + virtual void NotifyClick(const views::Event& event) OVERRIDE; + + // Overridden from TextButtonBase: + virtual gfx::NativeTheme::Part GetThemePart() const OVERRIDE; + virtual gfx::Rect GetThemePaintRect() const OVERRIDE; + virtual void GetExtraParams( + gfx::NativeTheme::ExtraParams* params) const OVERRIDE; + virtual gfx::Rect GetTextBounds() const OVERRIDE; + + // True if the checkbox is checked. + bool checked_; + + // The button's class name. + static const char kViewClassName[]; + + DISALLOW_COPY_AND_ASSIGN(CheckboxNt); +}; + } // namespace views #endif // VIEWS_CONTROLS_BUTTON_CHECKBOX_H_ diff --git a/views/controls/button/radio_button.cc b/views/controls/button/radio_button.cc index a6e3f19..a5292eb 100644 --- a/views/controls/button/radio_button.cc +++ b/views/controls/button/radio_button.cc @@ -13,6 +13,9 @@ namespace views { // static const char RadioButton::kViewClassName[] = "views/RadioButton"; +// static +const char RadioButtonNt::kViewClassName[] = "views/RadioButtonNt"; + //////////////////////////////////////////////////////////////////////////////// // RadioButton, public: @@ -112,4 +115,91 @@ NativeButtonWrapper* RadioButton::CreateWrapper() { return NativeButtonWrapper::CreateRadioButtonWrapper(this); } +//////////////////////////////////////////////////////////////////////////////// +// +// RadioButtonNt +// +//////////////////////////////////////////////////////////////////////////////// + +RadioButtonNt::RadioButtonNt(const std::wstring& label, int group_id) + : CheckboxNt(label) { + SetGroup(group_id); +} + +RadioButtonNt::~RadioButtonNt() { +} + +void RadioButtonNt::SetChecked(bool checked) { + if (checked == RadioButtonNt::checked()) + return; + if (checked) { + // We can't just get the root view here because sometimes the radio + // button isn't attached to a root view (e.g., if it's part of a tab page + // that is currently not active). + View* container = parent(); + while (container && container->parent()) + container = container->parent(); + if (container) { + std::vector<View*> other; + container->GetViewsWithGroup(GetGroup(), &other); + std::vector<View*>::iterator i; + for (i = other.begin(); i != other.end(); ++i) { + if (*i != this) { + if ((*i)->GetClassName() != kViewClassName) { + NOTREACHED() << "radio-button-nt has same group as other non " + "radio-button-nt views."; + continue; + } + RadioButtonNt* peer = static_cast<RadioButtonNt*>(*i); + peer->SetChecked(false); + } + } + } + } + CheckboxNt::SetChecked(checked); +} + +std::string RadioButtonNt::GetClassName() const { + return kViewClassName; +} + +void RadioButtonNt::GetAccessibleState(ui::AccessibleViewState* state) { + CheckboxNt::GetAccessibleState(state); + state->role = ui::AccessibilityTypes::ROLE_RADIOBUTTON; +} + +View* RadioButtonNt::GetSelectedViewForGroup(int group_id) { + std::vector<View*> views; + GetRootView()->GetViewsWithGroup(group_id, &views); + if (views.empty()) + return NULL; + + for (std::vector<View*>::const_iterator iter = views.begin(); + iter != views.end(); ++iter) { + // REVIEW: why don't we check the runtime type like is done above? + RadioButtonNt* radio_button = static_cast<RadioButtonNt*>(*iter); + if (radio_button->checked()) + return radio_button; + } + return NULL; +} + +bool RadioButtonNt::IsGroupFocusTraversable() const { + // When focusing a radio button with tab/shift+tab, only the selected button + // from the group should be focused. + return false; +} + +void RadioButtonNt::NotifyClick(const views::Event& event) { + // Set the checked state to true only if we are unchecked, since we can't + // be toggled on and off like a checkbox. + if (!checked()) + SetChecked(true); + RequestFocus(); +} + +gfx::NativeTheme::Part RadioButtonNt::GetThemePart() const { + return gfx::NativeTheme::kRadio; +} + } // namespace views diff --git a/views/controls/button/radio_button.h b/views/controls/button/radio_button.h index 7df8387..c922558 100644 --- a/views/controls/button/radio_button.h +++ b/views/controls/button/radio_button.h @@ -46,6 +46,37 @@ class RadioButton : public Checkbox { DISALLOW_COPY_AND_ASSIGN(RadioButton); }; +// A native themed class representing a radio button. This class does not use +// platform specific objects to replicate the native platforms looks and feel. +// +// This class will eventually be renamed to RadioButton to replace the class +// above. +class RadioButtonNt : public CheckboxNt { + public: + // The button's class name. + static const char kViewClassName[]; + + RadioButtonNt(const std::wstring& label, int group_id); + virtual ~RadioButtonNt(); + + // Overridden from View: + virtual std::string GetClassName() const OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual View* GetSelectedViewForGroup(int group_id) OVERRIDE; + virtual bool IsGroupFocusTraversable() const OVERRIDE; + + // Overridden from Button: + virtual void NotifyClick(const views::Event& event) OVERRIDE; + + // Overridden from TextButtonBase: + virtual gfx::NativeTheme::Part GetThemePart() const OVERRIDE; + + // Overridden from CheckboxNt: + virtual void SetChecked(bool checked) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(RadioButtonNt); +}; + } // namespace views #endif // VIEWS_CONTROLS_BUTTON_RADIO_BUTTON_H_ diff --git a/views/controls/button/text_button.cc b/views/controls/button/text_button.cc index b8e8d72..c5fbdcb 100644 --- a/views/controls/button/text_button.cc +++ b/views/controls/button/text_button.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -8,13 +8,13 @@ #include "base/logging.h" #include "base/utf_string_conversions.h" +#include "grit/app_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" -#include "grit/app_resources.h" namespace views { @@ -26,18 +26,21 @@ static const int kPreferredPaddingHorizontal = 6; static const int kPreferredPaddingVertical = 5; // static -const SkColor TextButton::kEnabledColor = SkColorSetRGB(6, 45, 117); +const SkColor TextButtonBase::kEnabledColor = SkColorSetRGB(6, 45, 117); // static -const SkColor TextButton::kHighlightColor = SkColorSetARGB(200, 255, 255, 255); +const SkColor TextButtonBase::kHighlightColor = + SkColorSetARGB(200, 255, 255, 255); // static -const SkColor TextButton::kDisabledColor = SkColorSetRGB(161, 161, 146); +const SkColor TextButtonBase::kDisabledColor = SkColorSetRGB(161, 161, 146); // static -const SkColor TextButton::kHoverColor = TextButton::kEnabledColor; +const SkColor TextButtonBase::kHoverColor = TextButton::kEnabledColor; // 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 int PrefixTypeToCanvasType(TextButton::PrefixType type) { @@ -93,9 +96,8 @@ TextButtonBorder::~TextButtonBorder() { // TextButtonBorder - painting // //////////////////////////////////////////////////////////////////////////////// - void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) const { - const TextButton* mb = static_cast<const TextButton*>(&view); + const TextButtonBase* mb = static_cast<const TextButton*>(&view); int state = mb->state(); // TextButton takes care of deciding when to call Paint. @@ -103,10 +105,24 @@ void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) const { if (state == TextButton::BS_PUSHED) set = &pushed_set_; - if (set) { + bool is_animating = mb->GetAnimation()->is_animating(); + bool show_mult_icons = mb->show_multiple_icon_states(); + bool do_paint = (show_mult_icons && is_animating) || + (show_mult_icons && (state == TextButton::BS_HOT || + state == TextButton::BS_PUSHED)) || + (state == TextButton::BS_NORMAL && mb->normal_has_border()); + if (set && do_paint) { + if (is_animating) { + canvas->SaveLayerAlpha( + static_cast<uint8>(mb->GetAnimation()->CurrentValueBetween(0, 255))); + canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, + SkXfermode::kClear_Mode); + } + Paint(view, canvas, *set); - } else { - // Do nothing + + if (is_animating) + canvas->Restore(); } } @@ -180,12 +196,63 @@ void TextButtonBorder::GetInsets(gfx::Insets* insets) const { } //////////////////////////////////////////////////////////////////////////////// -// TextButton, public: +// +// 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::CanvasSkia* skia_canvas = canvas->AsCanvasSkia(); + 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(skia_canvas, 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); + skia_canvas->SaveLayerAlpha(static_cast<uint8>(alpha)); + native_theme->Paint(skia_canvas, part, state, rect, extra); + skia_canvas->Restore(); + } else { + gfx::NativeTheme::ExtraParams extra; + gfx::NativeTheme::State state = delegate_->GetThemeState(&extra); + native_theme->Paint(skia_canvas, part, state, rect, extra); + } +} + +void TextButtonNativeThemeBorder::GetInsets(gfx::Insets* insets) const { + insets->Set(kPreferredPaddingVertical, kPreferredPaddingHorizontal, + kPreferredPaddingVertical, kPreferredPaddingHorizontal); +} + + +//////////////////////////////////////////////////////////////////////////////// +// TextButtonBase, public: -TextButton::TextButton(ButtonListener* listener, const std::wstring& text) +TextButtonBase::TextButtonBase(ButtonListener* listener, + const std::wstring& text) : CustomButton(listener), alignment_(ALIGN_LEFT), - icon_placement_(ICON_ON_LEFT), font_(ResourceBundle::GetSharedInstance().GetFont( ResourceBundle::BaseFont)), color_(kEnabledColor), @@ -198,153 +265,171 @@ TextButton::TextButton(ButtonListener* listener, const std::wstring& text) active_text_shadow_color_(0), inactive_text_shadow_color_(0), has_shadow_(false), - has_hover_icon_(false), - has_pushed_icon_(false), max_width_(0), normal_has_border_(false), show_multiple_icon_states_(true), - prefix_type_(PREFIX_NONE), - icon_text_spacing_(kDefaultIconTextSpacing) { + is_default_(false), + prefix_type_(PREFIX_NONE) { SetText(text); - set_border(new TextButtonBorder); SetAnimationDuration(kHoverAnimationDurationMs); } -TextButton::~TextButton() { +TextButtonBase::~TextButtonBase() { } -void TextButton::SetText(const std::wstring& text) { +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 std::wstring& text) { text_ = WideToUTF16Hack(text); SetAccessibleName(WideToUTF16Hack(text)); UpdateTextSize(); } -void TextButton::SetIcon(const SkBitmap& icon) { - icon_ = icon; -} - -void TextButton::SetHoverIcon(const SkBitmap& icon) { - icon_hover_ = icon; - has_hover_icon_ = true; -} - -void TextButton::SetPushedIcon(const SkBitmap& icon) { - icon_pushed_ = icon; - has_pushed_icon_ = true; -} - -void TextButton::SetFont(const gfx::Font& font) { +void TextButtonBase::SetFont(const gfx::Font& font) { font_ = font; UpdateTextSize(); } -void TextButton::SetEnabledColor(SkColor color) { +void TextButtonBase::SetEnabledColor(SkColor color) { color_enabled_ = color; UpdateColor(); } -void TextButton::SetDisabledColor(SkColor color) { +void TextButtonBase::SetDisabledColor(SkColor color) { color_disabled_ = color; UpdateColor(); } -void TextButton::SetHighlightColor(SkColor color) { +void TextButtonBase::SetHighlightColor(SkColor color) { color_highlight_ = color; } -void TextButton::SetHoverColor(SkColor color) { +void TextButtonBase::SetHoverColor(SkColor color) { color_hover_ = color; } -void TextButton::SetTextHaloColor(SkColor color) { +void TextButtonBase::SetTextHaloColor(SkColor color) { text_halo_color_ = color; has_text_halo_ = true; } -void TextButton::SetTextShadowColors(SkColor active_color, - SkColor inactive_color) { +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 TextButton::ClearMaxTextSize() { +void TextButtonBase::ClearMaxTextSize() { max_text_size_ = text_size_; } -void TextButton::SetNormalHasBorder(bool normal_has_border) { +void TextButtonBase::SetNormalHasBorder(bool normal_has_border) { normal_has_border_ = normal_has_border; } -void TextButton::SetShowMultipleIconStates(bool show_multiple_icon_states) { +void TextButtonBase::SetShowMultipleIconStates(bool show_multiple_icon_states) { show_multiple_icon_states_ = show_multiple_icon_states; } -void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { - if (mode == PB_NORMAL) { - OnPaintBackground(canvas); +gfx::Size TextButtonBase::GetPreferredSize() { + gfx::Insets insets = GetInsets(); - if (show_multiple_icon_states_ && hover_animation_->is_animating()) { - // Draw the hover bitmap into an offscreen buffer, then blend it - // back into the current canvas. - canvas->SaveLayerAlpha( - static_cast<int>(hover_animation_->GetCurrentValue() * 255)); - canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, - SkXfermode::kClear_Mode); - OnPaintBorder(canvas); - canvas->Restore(); - } else if ((show_multiple_icon_states_ && - (state_ == BS_HOT || state_ == BS_PUSHED)) || - (state_ == BS_NORMAL && normal_has_border_)) { - OnPaintBorder(canvas); - } + // Use the max size to set the button boundaries. + gfx::Size prefsize(max_text_size_.width() + insets.width(), + max_text_size_.height() + insets.height()); - OnPaintFocusBorder(canvas); - } + if (max_width_ > 0) + prefsize.set_width(std::min(max_width_, prefsize.width())); - SkBitmap icon = icon_; - if (show_multiple_icon_states_) { - if (has_hover_icon_ && (state() == BS_HOT)) - icon = icon_hover_; - else if (has_pushed_icon_ && (state() == BS_PUSHED)) - icon = icon_pushed_; + return prefsize; +} + +void TextButtonBase::OnPaint(gfx::Canvas* canvas) { + PaintButton(canvas, PB_NORMAL); +} + +const ui::Animation* TextButtonBase::GetAnimation() const { + return hover_animation_.get(); +} + +void TextButtonBase::UpdateColor() { + color_ = IsEnabled() ? color_enabled_ : color_disabled_; +} + +void TextButtonBase::UpdateTextSize() { + int width = 0, height = 0; + gfx::CanvasSkia::SizeStringInt( + text_, font_, &width, &height, + gfx::Canvas::NO_ELLIPSIS | PrefixTypeToCanvasType(prefix_type_)); + + // Add 2 extra pixels to width and height when text halo is used. + if (has_text_halo_) { + width += 2; + height += 2; } + text_size_.SetSize(width, font_.GetHeight()); + 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::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 = kEnabledColor; +} + +gfx::Rect TextButtonBase::GetTextBounds() const { gfx::Insets insets = GetInsets(); int available_width = width() - insets.width(); - int available_height = height() - insets.height(); - // Use the actual text (not max) size to properly center the text. int content_width = text_size_.width(); - if (icon.width() > 0) { - content_width += icon.width(); - if (!text_.empty()) - content_width += icon_text_spacing_; + int text_x = 0; + switch(alignment_) { + case ALIGN_LEFT: + text_x = insets.left(); + break; + case ALIGN_RIGHT: + text_x = width() - insets.right() - content_width; + break; + case ALIGN_CENTER: + text_x = insets.left() + std::max(0, + (available_width - content_width) / 2); + break; } - // Place the icon along the left edge. - int icon_x; - if (alignment_ == ALIGN_LEFT) { - icon_x = insets.left(); - } else if (alignment_ == ALIGN_RIGHT) { - icon_x = available_width - content_width; - } else { - icon_x = - std::max(0, (available_width - content_width) / 2) + insets.left(); - } - int text_x = icon_x; - if (icon.width() > 0) - text_x += icon.width() + icon_text_spacing_; const int text_width = std::min(text_size_.width(), width() - insets.right() - text_x); + int available_height = height() - insets.height(); int text_y = (available_height - text_size_.height()) / 2 + insets.top(); - // If the icon should go on the other side, swap the elements. - if (icon_placement_ == ICON_ON_RIGHT) { - int new_text_x = icon_x; - icon_x = new_text_x + text_width + icon_text_spacing_; - text_x = new_text_x; + gfx::Rect bounds(text_x, text_y, text_width, text_size_.height()); + return bounds; +} + +void TextButtonBase::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { + if (mode == PB_NORMAL) { + OnPaintBackground(canvas); + OnPaintBorder(canvas); + OnPaintFocusBorder(canvas); } - if (text_width > 0) { + 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 @@ -353,7 +438,6 @@ void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { // horizontally. // // Due to the above, we must perform the flipping manually for RTL UIs. - gfx::Rect text_bounds(text_x, text_y, text_width, text_size_.height()); text_bounds.set_x(GetMirroredXForRect(text_bounds)); SkColor text_color = (show_multiple_icon_states_ && @@ -416,51 +500,109 @@ void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { draw_string_flags); } } +} - if (icon.width() > 0) { - int icon_y = (available_height - icon.height()) / 2 + insets.top(); +//////////////////////////////////////////////////////////////////////////////// +// TextButtonBase, View overrides: - // 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()); +gfx::Size TextButtonBase::GetMinimumSize() { + return max_text_size_; +} + +void TextButtonBase::SetEnabled(bool enabled) { + if (enabled != IsEnabled()) { + CustomButton::SetEnabled(enabled); } + // We should always call UpdateColor() since the state of the button might be + // changed by other functions like CustomButton::SetState(). + UpdateColor(); + SchedulePaint(); } -void TextButton::UpdateColor() { - color_ = IsEnabled() ? color_enabled_ : color_disabled_; +std::string TextButtonBase::GetClassName() const { + return kViewClassName; } -void TextButton::UpdateTextSize() { - int width = 0, height = 0; - gfx::CanvasSkia::SizeStringInt( - text_, font_, &width, &height, - gfx::Canvas::NO_ELLIPSIS | PrefixTypeToCanvasType(prefix_type_)); +//////////////////////////////////////////////////////////////////////////////// +// TextButtonBase, NativeThemeDelegate overrides: - // Add 2 extra pixels to width and height when text halo is used. - if (has_text_halo_) { - width += 2; - height += 2; +gfx::Rect TextButtonBase::GetThemePaintRect() const { + return bounds(); +} + +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; } +} - text_size_.SetSize(width, font_.GetHeight()); - max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()), - std::max(max_text_size_.height(), - text_size_.height())); - PreferredSizeChanged(); +const ui::Animation* TextButtonBase::GetThemeAnimation() const { + return hover_animation_.get(); +} + +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, View overrides: + +TextButton::TextButton(ButtonListener* listener, + const std::wstring& text) + : TextButtonBase(listener, text), + icon_placement_(ICON_ON_LEFT), + has_hover_icon_(false), + has_pushed_icon_(false), + icon_text_spacing_(kDefaultIconTextSpacing) { + set_border(new TextButtonBorder); +} + +TextButton::~TextButton() { +} + +void TextButton::SetIcon(const SkBitmap& icon) { + icon_ = icon; +} + +void TextButton::SetHoverIcon(const SkBitmap& icon) { + icon_hover_ = icon; + has_hover_icon_ = true; +} + +void TextButton::SetPushedIcon(const SkBitmap& icon) { + icon_pushed_ = icon; + has_pushed_icon_ = true; +} gfx::Size TextButton::GetPreferredSize() { - gfx::Insets insets = GetInsets(); + 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. - gfx::Size prefsize(max_text_size_.width() + icon_.width() + insets.width(), - std::max(max_text_size_.height(), icon_.height()) + - insets.height()); - if (icon_.width() > 0 && !text_.empty()) prefsize.Enlarge(icon_text_spacing_, 0); @@ -470,26 +612,115 @@ gfx::Size TextButton::GetPreferredSize() { return prefsize; } -gfx::Size TextButton::GetMinimumSize() { - return max_text_size_; -} +void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { + TextButtonBase::PaintButton(canvas, mode); -void TextButton::SetEnabled(bool enabled) { - if (enabled != IsEnabled()) { - CustomButton::SetEnabled(enabled); + SkBitmap icon = icon_; + if (show_multiple_icon_states_) { + if (has_hover_icon_ && (state() == BS_HOT)) + icon = icon_hover_; + else if (has_pushed_icon_ && (state() == BS_PUSHED)) + icon = icon_pushed_; + } + + 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()); } - // We should always call UpdateColor() since the state of the button might be - // changed by other functions like CustomButton::SetState(). - UpdateColor(); - SchedulePaint(); } std::string TextButton::GetClassName() const { return kViewClassName; } -void TextButton::OnPaint(gfx::Canvas* canvas) { - PaintButton(canvas, PB_NORMAL); +gfx::NativeTheme::Part TextButton::GetThemePart() const { + return gfx::NativeTheme::kPushButton; +} + +void TextButton::GetExtraParams(gfx::NativeTheme::ExtraParams* params) const { + params->button.checked = false; + params->button.indeterminate = false; + params->button.is_default = is_default_; + params->button.has_border = false; + params->button.classic_state = 0; + params->button.background_color = kEnabledColor; +} + +gfx::Rect TextButton::GetTextBounds() const { + gfx::Rect bounds(TextButtonBase::GetTextBounds()); + + SkBitmap icon = icon_; + if (show_multiple_icon_states_) { + if (has_hover_icon_ && (state() == BS_HOT)) + icon = icon_hover_; + else if (has_pushed_icon_ && (state() == BS_PUSHED)) + icon = icon_pushed_; + } + + if (icon.width() > 0) { + gfx::Insets insets = GetInsets(); + int available_width = width() - insets.width(); + int icon_width = icon.width() + (text_.empty() ? 0 : icon_text_spacing_); + int new_content_width = bounds.width() + icon_width; + + if (new_content_width < available_width) { + switch(alignment_) { + case ALIGN_LEFT: + if (icon_placement_ == ICON_ON_LEFT) + bounds.Offset(icon_width, 0); + break; + case ALIGN_RIGHT: + if (icon_placement_ == ICON_ON_RIGHT) + bounds.Offset(-icon_width, 0); + break; + case ALIGN_CENTER: + if (icon_placement_ == ICON_ON_LEFT) { + bounds.Offset(icon_width / 2, 0); + } else { + bounds.Offset(-icon_width / 2, 0); + } + break; + } + } else { + // Make sure the icon is always fully visible. + switch(alignment_) { + case ALIGN_LEFT: + case ALIGN_CENTER: + if (icon_placement_ == ICON_ON_LEFT) { + bounds.Offset(icon_width, 0); + } else { + bounds.set_width(available_width - icon_width); + } + break; + case ALIGN_RIGHT: + if (icon_placement_ == ICON_ON_RIGHT) { + bounds.Offset(-icon_width, 0); + } else { + bounds.set_x(icon_width + insets.left()); + } + break; + } + } + } + + return bounds; } } // namespace views + + diff --git a/views/controls/button/text_button.h b/views/controls/button/text_button.h index f8ab377..8b1c502 100644 --- a/views/controls/button/text_button.h +++ b/views/controls/button/text_button.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -15,6 +15,7 @@ #include "ui/gfx/font.h" #include "views/border.h" #include "views/controls/button/custom_button.h" +#include "views/native_theme_delegate.h" namespace views { @@ -35,10 +36,8 @@ class TextButtonBorder : public Border { TextButtonBorder(); virtual ~TextButtonBorder(); - // Render the background for the provided view + // Implementation of Border: virtual void Paint(const View& view, gfx::Canvas* canvas) const; - - // Returns the insets for the border. virtual void GetInsets(gfx::Insets* insets) const; protected: @@ -67,14 +66,41 @@ class TextButtonBorder : public Border { //////////////////////////////////////////////////////////////////////////////// // -// TextButton +// TextButtonNativeThemeBorder // -// A button which displays text and/or and icon that can be changed in -// response to actions. TextButton reserves space for the largest string +// A Border subclass that paints a TextButton's background layer using the +// platform's native theme look. This handles normal/disabled/hot/pressed +// states, with possible animation between states. +// +//////////////////////////////////////////////////////////////////////////////// +class TextButtonNativeThemeBorder : public Border { + public: + TextButtonNativeThemeBorder(NativeThemeDelegate* delegate); + virtual ~TextButtonNativeThemeBorder(); + + // Implementation of Border: + virtual void Paint(const View& view, gfx::Canvas* canvas) const; + virtual void GetInsets(gfx::Insets* insets) const; + + private: + // The delegate the controls the appearance of this border. + NativeThemeDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(TextButtonNativeThemeBorder); +}; + + +//////////////////////////////////////////////////////////////////////////////// +// +// TextButtonBase +// +// A base ckass for different types of buttons, like push buttons, radio +// buttons, and checkboxes, that do not depende on native components for +// look and feel. TextButton reserves space for the largest string // passed to SetText. To reset the cached max size invoke ClearMaxTextSize. // //////////////////////////////////////////////////////////////////////////////// -class TextButton : public CustomButton { +class TextButtonBase : public CustomButton, public NativeThemeDelegate { public: // The menu button's class name. static const char kViewClassName[]; @@ -92,8 +118,7 @@ class TextButton : public CustomButton { PREFIX_SHOW }; - TextButton(ButtonListener* listener, const std::wstring& text); - virtual ~TextButton(); + virtual ~TextButtonBase(); // Call SetText once per string in your set of possible values at button // creation time, so that it can contain the largest of them and avoid @@ -111,27 +136,10 @@ class TextButton : public CustomButton { void set_prefix_type(PrefixType type) { prefix_type_ = type; } - void set_icon_text_spacing(int icon_text_spacing) { - icon_text_spacing_ = icon_text_spacing; - } - - // Sets the icon. - void SetIcon(const SkBitmap& icon); - void SetHoverIcon(const SkBitmap& icon); - void SetPushedIcon(const SkBitmap& icon); - - bool HasIcon() const { return !icon_.empty(); } - - // Meanings are reversed for right-to-left layouts. - enum IconPlacement { - ICON_ON_LEFT, - ICON_ON_RIGHT - }; + const ui::Animation* GetAnimation() const; - IconPlacement icon_placement() { return icon_placement_; } - void set_icon_placement(IconPlacement icon_placement) { - icon_placement_ = icon_placement; - } + void SetIsDefault(bool is_default); + bool is_default() const { return is_default_; } // TextButton remembers the maximum display size of the text passed to // SetText. This method resets the cached maximum display size to the @@ -152,9 +160,13 @@ class TextButton : public CustomButton { // inactive. Both possible colors are set in this method, and the // appropriate one is chosen during Paint. void SetTextShadowColors(SkColor active_color, SkColor inactive_color); + + bool normal_has_border() const { return normal_has_border_; } void SetNormalHasBorder(bool normal_has_border); + // Sets whether or not to show the hot and pushed states for the button icon // (if present) in addition to the normal state. Defaults to true. + bool show_multiple_icon_states() const { return show_multiple_icon_states_; } void SetShowMultipleIconStates(bool show_multiple_icon_states); // Paint the button into the specified canvas. If |mode| is |PB_FOR_DRAG|, the @@ -163,9 +175,10 @@ class TextButton : public CustomButton { virtual void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode); // Overridden from View: - virtual gfx::Size GetPreferredSize(); - virtual gfx::Size GetMinimumSize(); - virtual void SetEnabled(bool enabled); + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual void SetEnabled(bool enabled) OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; // Text colors. static const SkColor kEnabledColor; @@ -177,9 +190,7 @@ class TextButton : public CustomButton { virtual std::string GetClassName() const; protected: - SkBitmap icon() const { return icon_; } - - virtual void OnPaint(gfx::Canvas* canvas); + TextButtonBase(ButtonListener* listener, const std::wstring& text); // Called when enabled or disabled state changes, or the colors for those // states change. @@ -189,10 +200,23 @@ class TextButton : public CustomButton { // invoked when the font or text changes. void UpdateTextSize(); + // Overridden from NativeThemeDelegate: + virtual gfx::Rect GetThemePaintRect() const OVERRIDE; + virtual gfx::NativeTheme::State GetThemeState( + gfx::NativeTheme::ExtraParams* params) const OVERRIDE; + virtual const ui::Animation* GetThemeAnimation() const OVERRIDE; + virtual gfx::NativeTheme::State GetBackgroundThemeState( + gfx::NativeTheme::ExtraParams* params) const OVERRIDE; + virtual gfx::NativeTheme::State GetForegroundThemeState( + gfx::NativeTheme::ExtraParams* params) const OVERRIDE; + + virtual void GetExtraParams(gfx::NativeTheme::ExtraParams* params) const; + + virtual gfx::Rect GetTextBounds() const; + // The text string that is displayed in the button. string16 text_; - private: // The size of the text string. gfx::Size text_size_; @@ -203,9 +227,6 @@ class TextButton : public CustomButton { // The alignment of the text string within the button. TextAlignment alignment_; - // The position of the icon. - IconPlacement icon_placement_; - // The font used to paint the text. gfx::Font font_; @@ -227,17 +248,6 @@ class TextButton : public CustomButton { SkColor inactive_text_shadow_color_; bool has_shadow_; - // An icon displayed with the text. - SkBitmap icon_; - - // An optional different version of the icon for hover state. - SkBitmap icon_hover_; - bool has_hover_icon_; - - // An optional different version of the icon for pushed state. - SkBitmap icon_pushed_; - bool has_pushed_icon_; - // The width of the button will never be larger than this value. A value <= 0 // indicates the width is not constrained. int max_width_; @@ -248,8 +258,87 @@ class TextButton : public CustomButton { // Whether or not to show the hot and pushed icon states. bool show_multiple_icon_states_; + // Whether or not the button appears and behaves as the default button in its + // current context. + bool is_default_; + PrefixType prefix_type_; + DISALLOW_COPY_AND_ASSIGN(TextButtonBase); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// TextButton +// +// A button which displays text and/or and icon that can be changed in +// response to actions. TextButton reserves space for the largest string +// passed to SetText. To reset the cached max size invoke ClearMaxTextSize. +// +//////////////////////////////////////////////////////////////////////////////// +class TextButton : public TextButtonBase { + public: + // The button's class name. + static const char kViewClassName[]; + + TextButton(ButtonListener* listener, const std::wstring& text); + virtual ~TextButton(); + + void set_icon_text_spacing(int icon_text_spacing) { + icon_text_spacing_ = icon_text_spacing; + } + + // Sets the icon. + void SetIcon(const SkBitmap& icon); + void SetHoverIcon(const SkBitmap& icon); + void SetPushedIcon(const SkBitmap& icon); + + bool HasIcon() const { return !icon_.empty(); } + + // Meanings are reversed for right-to-left layouts. + enum IconPlacement { + ICON_ON_LEFT, + ICON_ON_RIGHT + }; + + IconPlacement icon_placement() { return icon_placement_; } + void set_icon_placement(IconPlacement icon_placement) { + icon_placement_ = icon_placement; + } + + // Overridden from View: + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual std::string GetClassName() const; + + // Overridden from TextButtonBase: + virtual void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) OVERRIDE; + + protected: + SkBitmap icon() const { return icon_; } + + // Overridden from NativeThemeDelegate: + virtual gfx::NativeTheme::Part GetThemePart() const OVERRIDE; + + // Overridden from TextButtonBase: + virtual void GetExtraParams( + gfx::NativeTheme::ExtraParams* params) const OVERRIDE; + virtual gfx::Rect GetTextBounds() const OVERRIDE; + + private: + // The position of the icon. + IconPlacement icon_placement_; + + // An icon displayed with the text. + SkBitmap icon_; + + // An optional different version of the icon for hover state. + SkBitmap icon_hover_; + bool has_hover_icon_; + + // An optional different version of the icon for pushed state. + SkBitmap icon_pushed_; + bool has_pushed_icon_; + // Space between icon and text. int icon_text_spacing_; diff --git a/views/examples/button_example.cc b/views/examples/button_example.cc index d27a5d2..4ab7f80 100644 --- a/views/examples/button_example.cc +++ b/views/examples/button_example.cc @@ -4,6 +4,9 @@ #include "views/examples/button_example.h" +#include "grit/app_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "views/controls/button/checkbox.h" #include "views/layout/fill_layout.h" #include "views/view.h" @@ -11,7 +14,12 @@ namespace examples { ButtonExample::ButtonExample(ExamplesMain* main) : ExampleBase(main), + alignment_(views::TextButton::ALIGN_LEFT), + use_native_theme_border_(false), + icon_(NULL), count_(0) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + icon_ = rb.GetBitmapNamed(IDR_CLOSE_SA_H); } ButtonExample::~ButtonExample() { @@ -22,7 +30,8 @@ std::wstring ButtonExample::GetExampleTitle() { } void ButtonExample::CreateExampleView(views::View* container) { - button_ = new views::TextButton(this, L"Button"); + views::TextButton* tb = new views::TextButton(this, L"Button"); + button_ = tb; container->SetLayoutManager(new views::FillLayout); container->AddChildView(button_); } @@ -30,6 +39,57 @@ void ButtonExample::CreateExampleView(views::View* container) { void ButtonExample::ButtonPressed(views::Button* sender, const views::Event& event) { PrintStatus(L"Pressed! count:%d", ++count_); + + if (event.IsControlDown()) { + if (event.IsShiftDown()) { + switch(button_->icon_placement()) { + case views::TextButton::ICON_ON_LEFT: + button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT); + break; + case views::TextButton::ICON_ON_RIGHT: + button_->set_icon_placement(views::TextButton::ICON_ON_LEFT); + break; + } + } else if (event.IsAltDown()) { + if (button_->HasIcon()) + button_->SetIcon(SkBitmap()); + else + button_->SetIcon(*icon_); + } else { + switch(alignment_) { + case views::TextButton::ALIGN_LEFT: + alignment_ = views::TextButton::ALIGN_CENTER; + break; + case views::TextButton::ALIGN_CENTER: + alignment_ = views::TextButton::ALIGN_RIGHT; + break; + case views::TextButton::ALIGN_RIGHT: + alignment_ = views::TextButton::ALIGN_LEFT; + break; + } + button_->set_alignment(alignment_); + } + } else if (event.IsShiftDown()) { + if (event.IsAltDown()) { + if (button_->text().length() < 10) { + button_->SetText(L"Startof" + L"ReallyReallyReallyReallyReallyReallyReally" + L"ReallyReallyReallyReallyReallyReallyReally" + L"ReallyReallyReallyReallyReallyReallyReally" + L"LongButtonText"); + } else { + button_->SetText(L"Button"); + } + } else { + use_native_theme_border_ = !use_native_theme_border_; + if (use_native_theme_border_) + button_->set_border(new views::TextButtonNativeThemeBorder(button_)); + else + button_->set_border(new views::TextButtonBorder()); + } + } else if (event.IsAltDown()) { + button_->SetIsDefault(!button_->is_default()); + } } } // namespace examples diff --git a/views/examples/button_example.h b/views/examples/button_example.h index fe8567b..aad1ff9 100644 --- a/views/examples/button_example.h +++ b/views/examples/button_example.h @@ -35,6 +35,11 @@ class ButtonExample : public ExampleBase, public views::ButtonListener { // The only control in this test. views::TextButton* button_; + // Values used to modify the look and feel of the button. + views::TextButton::TextAlignment alignment_; + bool use_native_theme_border_; + SkBitmap* icon_; + // The number of times the button is pressed. int count_; diff --git a/views/examples/examples_main.cc b/views/examples/examples_main.cc index 7f12733..8b2b647 100644 --- a/views/examples/examples_main.cc +++ b/views/examples/examples_main.cc @@ -20,6 +20,7 @@ #include "views/examples/menu_example.h" #include "views/examples/message_box_example.h" #include "views/examples/native_theme_button_example.h" +#include "views/examples/native_theme_checkbox_example.h" #include "views/examples/radio_button_example.h" #include "views/examples/scroll_view_example.h" #include "views/examples/single_split_view_example.h" @@ -99,6 +100,10 @@ void ExamplesMain::Run() { views::Window* window = views::Window::CreateChromeWindow(NULL, gfx::Rect(0, 0, 850, 300), this); + examples::NativeThemeCheckboxExample native_theme_checkbox_example(this); + tabbed_pane->AddTab(native_theme_checkbox_example.GetExampleTitle(), + native_theme_checkbox_example.GetExampleView()); + examples::NativeThemeButtonExample native_theme_button_example(this); tabbed_pane->AddTab(native_theme_button_example.GetExampleTitle(), native_theme_button_example.GetExampleView()); diff --git a/views/examples/native_theme_button_example.cc b/views/examples/native_theme_button_example.cc index 7398a270..e7630e3 100644 --- a/views/examples/native_theme_button_example.cc +++ b/views/examples/native_theme_button_example.cc @@ -136,6 +136,10 @@ gfx::NativeTheme::Part ExampleNativeThemeButton::GetThemePart() const { return gfx::NativeTheme::kPushButton; } +gfx::Rect ExampleNativeThemeButton::GetThemePaintRect() const { + return bounds(); +} + gfx::NativeTheme::State ExampleNativeThemeButton::GetThemeState( gfx::NativeTheme::ExtraParams* params) const { GetExtraParams(params); @@ -182,7 +186,7 @@ void ExampleNativeThemeButton::GetExtraParams( params->button.background_color = SkColorSetARGB(0, 0, 0, 0); } -ui::Animation* ExampleNativeThemeButton::GetThemeAnimation() const { +const ui::Animation* ExampleNativeThemeButton::GetThemeAnimation() const { int selected = cb_state_->selected_item(); return selected <= 3 ? NULL : hover_animation_.get(); } diff --git a/views/examples/native_theme_button_example.h b/views/examples/native_theme_button_example.h index 9ca7c33..91f02a6 100644 --- a/views/examples/native_theme_button_example.h +++ b/views/examples/native_theme_button_example.h @@ -11,6 +11,7 @@ #include "views/controls/button/custom_button.h" #include "views/controls/combobox/combobox.h" #include "views/examples/example_base.h" +#include "views/native_theme_delegate.h" #include "views/native_theme_painter.h" namespace views { @@ -22,7 +23,7 @@ namespace examples { // A subclass of button to test native theme rendering. class ExampleNativeThemeButton : public views::CustomButton, - public views::NativeThemePainter::Delegate, + public views::NativeThemeDelegate, public views::Combobox::Listener { public: ExampleNativeThemeButton(views::ButtonListener* listener, @@ -44,9 +45,10 @@ class ExampleNativeThemeButton : public views::CustomButton, // Overridden from views::NativeThemePainter::Delegate: virtual gfx::NativeTheme::Part GetThemePart() const OVERRIDE; + virtual gfx::Rect GetThemePaintRect() const OVERRIDE; virtual gfx::NativeTheme::State GetThemeState( gfx::NativeTheme::ExtraParams* params) const OVERRIDE; - virtual ui::Animation* GetThemeAnimation() const OVERRIDE; + virtual const ui::Animation* GetThemeAnimation() const OVERRIDE; virtual gfx::NativeTheme::State GetBackgroundThemeState( gfx::NativeTheme::ExtraParams* params) const OVERRIDE; virtual gfx::NativeTheme::State GetForegroundThemeState( diff --git a/views/examples/native_theme_checkbox_example.cc b/views/examples/native_theme_checkbox_example.cc new file mode 100644 index 0000000..f7c6626 --- /dev/null +++ b/views/examples/native_theme_checkbox_example.cc @@ -0,0 +1,39 @@ +// 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/examples/native_theme_checkbox_example.h" + +#include "base/stringprintf.h" +#include "views/controls/button/checkbox.h" +#include "views/controls/button/radio_button.h" +#include "views/layout/fill_layout.h" + +namespace examples { + +NativeThemeCheckboxExample::NativeThemeCheckboxExample(ExamplesMain* main) + : ExampleBase(main), + count_(0) { +} + +NativeThemeCheckboxExample::~NativeThemeCheckboxExample() { +} + +std::wstring NativeThemeCheckboxExample::GetExampleTitle() { + return L"CheckboxNt"; +} + +void NativeThemeCheckboxExample::CreateExampleView(views::View* container) { + //button_ = new views::RadioButtonNt(L"RadioButtonNt", 3); + button_ = new views::CheckboxNt(L"CheckboxNt"); + button_->set_listener(this); + container->SetLayoutManager(new views::FillLayout); + container->AddChildView(button_); +} + +void NativeThemeCheckboxExample::ButtonPressed(views::Button* sender, + const views::Event& event) { + PrintStatus(base::StringPrintf(L"Pressed! count:%d", ++count_).c_str()); +} + +} // namespace examples diff --git a/views/examples/native_theme_checkbox_example.h b/views/examples/native_theme_checkbox_example.h new file mode 100644 index 0000000..4bd83c7 --- /dev/null +++ b/views/examples/native_theme_checkbox_example.h @@ -0,0 +1,46 @@ +// 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. + +#ifndef VIEWS_EXAMPLES_NATIVE_THEME_CHECKBOX_EXAMPLE_H_ +#define VIEWS_EXAMPLES_NATIVE_THEME_CHECKBOX_EXAMPLE_H_ +#pragma once + +#include "base/basictypes.h" +#include "ui/gfx/native_theme.h" +#include "views/controls/button/button.h" +#include "views/examples/example_base.h" + +namespace views { +class CheckboxNt; +} + +namespace examples { + +// NativeThemeCheckboxExample exercises a CheckboxNt control. +class NativeThemeCheckboxExample : public ExampleBase, + public views::ButtonListener { + public: + explicit NativeThemeCheckboxExample(ExamplesMain* main); + virtual ~NativeThemeCheckboxExample(); + + // Overridden from ExampleBase: + virtual std::wstring GetExampleTitle() OVERRIDE; + virtual void CreateExampleView(views::View* container) OVERRIDE; + + private: + // Overridden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const views::Event& event) OVERRIDE; + + // The only control in this test. + views::CheckboxNt* button_; + + int count_; + + DISALLOW_COPY_AND_ASSIGN(NativeThemeCheckboxExample); +}; + +} // namespace examples + +#endif // VIEWS_EXAMPLES_NATIVE_THEME_CHECKBOX_EXAMPLE_H_ diff --git a/views/examples/radio_button_example.cc b/views/examples/radio_button_example.cc index 27d8fa1..546a0ee 100644 --- a/views/examples/radio_button_example.cc +++ b/views/examples/radio_button_example.cc @@ -25,16 +25,17 @@ void RadioButtonExample::CreateExampleView(views::View* container) { select_ = new views::TextButton(this, L"Select"); status_ = new views::TextButton(this, L"Show Status"); - int all = arraysize(radio_buttons_); - - // divide buttons into 2 groups - int group_count = all / 2; - for (int i = 0; i < all; i++) { - int group = i / group_count; + int group = 1; + for (int i = 0; i < arraysize(radio_buttons_); ++i) { radio_buttons_[i] = new views::RadioButton( - base::StringPrintf( - L"Radio %d in group %d", (i % group_count + 1), group), - group); + base::StringPrintf( L"Radio %d in group %d", i + 1, group), group); + } + + ++group; + for (int i = 0; i < arraysize(radio_buttons_nt_); ++i) { + radio_buttons_nt_[i] = new views::RadioButtonNt( + base::StringPrintf( L"Radio %d in group %d", i + 1, group), group); + radio_buttons_nt_[i]->SetFocusable(true); } views::GridLayout* layout = new views::GridLayout(container); @@ -43,10 +44,14 @@ void RadioButtonExample::CreateExampleView(views::View* container) { views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1.0f, views::GridLayout::USE_PREF, 0, 0); - for (int i = 0; i < all; i++) { + for (int i = 0; i < arraysize(radio_buttons_); i++) { layout->StartRow(0, 0); layout->AddView(radio_buttons_[i]); } + for (int i = 0; i < arraysize(radio_buttons_nt_); i++) { + layout->StartRow(0, 0); + layout->AddView(radio_buttons_nt_[i]); + } layout->StartRow(0, 0); layout->AddView(select_); layout->StartRow(0, 0); @@ -57,16 +62,16 @@ void RadioButtonExample::ButtonPressed(views::Button* sender, const views::Event& event) { if (sender == select_) { radio_buttons_[0]->SetChecked(true); - radio_buttons_[5]->SetChecked(true); + radio_buttons_nt_[2]->SetChecked(true); } else if (sender == status_) { // Show the state of radio buttons. PrintStatus(L"Group1: 1:%ls, 2:%ls, 3:%ls Group2: 1:%ls, 2:%ls, 3:%ls", IntToOnOff(radio_buttons_[0]->checked()), IntToOnOff(radio_buttons_[1]->checked()), IntToOnOff(radio_buttons_[2]->checked()), - IntToOnOff(radio_buttons_[3]->checked()), - IntToOnOff(radio_buttons_[4]->checked()), - IntToOnOff(radio_buttons_[5]->checked())); + IntToOnOff(radio_buttons_nt_[0]->checked()), + IntToOnOff(radio_buttons_nt_[1]->checked()), + IntToOnOff(radio_buttons_nt_[2]->checked())); } } diff --git a/views/examples/radio_button_example.h b/views/examples/radio_button_example.h index 92cf431..72217e2 100644 --- a/views/examples/radio_button_example.h +++ b/views/examples/radio_button_example.h @@ -31,9 +31,9 @@ class RadioButtonExample : public ExampleBase, virtual void ButtonPressed(views::Button* sender, const views::Event& event) OVERRIDE; - // 6 radio buttons, 0-2 consists 1st group, and 3-5 consists - // 2nd group. - views::RadioButton* radio_buttons_[6]; + // Two groups of 3 radio buttons. + views::RadioButton* radio_buttons_[3]; + views::RadioButtonNt* radio_buttons_nt_[3]; // Control button to select radio buttons, and show the status of buttons. views::TextButton* select_; diff --git a/views/native_theme_delegate.h b/views/native_theme_delegate.h new file mode 100644 index 0000000..d94a514 --- /dev/null +++ b/views/native_theme_delegate.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef VIEWS_NATIVE_THEME_DELEGATE_H_ +#define VIEWS_NATIVE_THEME_DELEGATE_H_ +#pragma once + +#include "ui/gfx/native_theme.h" +#include "ui/gfx/rect.h" + +namespace views { + +// A delagate that supports animating transtions between different native +// theme states. This delegate can be used to control a native theme Border +// or Painter object. +// +// If animation is onging, the native theme border or painter will +// composite the foreground state over the backgroud state using an alpha +// between 0 and 255 based on the current value of the animation. +class NativeThemeDelegate { + public: + virtual ~NativeThemeDelegate() {} + + // Get the native theme part that should be drawn. + virtual gfx::NativeTheme::Part GetThemePart() const = 0; + + // Get the rectangle that should be painted. + virtual gfx::Rect GetThemePaintRect() const = 0; + + // Get the state of the part, along with any extra data needed for drawing. + virtual gfx::NativeTheme::State GetThemeState( + gfx::NativeTheme::ExtraParams* params) const = 0; + + // If the native theme drawign should be animated, return the Animation object + // that controlls it. If no animation is ongoing, NULL may be returned. + virtual const ui::Animation* GetThemeAnimation() const = 0; + + // If animation is onging, this returns the background native theme state. + virtual gfx::NativeTheme::State GetBackgroundThemeState( + gfx::NativeTheme::ExtraParams* params) const = 0; + + // If animation is onging, this returns the foreground native theme state. + // This state will be composited over the background using an alpha value + // based on the current value of the animation. + virtual gfx::NativeTheme::State GetForegroundThemeState( + gfx::NativeTheme::ExtraParams* params) const = 0; +}; + +} // namespace views + +#endif // VIEWS_NATIVE_THEME_DELEGATE_H_ diff --git a/views/native_theme_painter.cc b/views/native_theme_painter.cc index a99edea..71bb71b 100644 --- a/views/native_theme_painter.cc +++ b/views/native_theme_painter.cc @@ -9,10 +9,11 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/canvas_skia.h" #include "ui/gfx/rect.h" +#include "views/native_theme_delegate.h" namespace views { -NativeThemePainter::NativeThemePainter(Delegate* delegate) +NativeThemePainter::NativeThemePainter(NativeThemeDelegate* delegate) : delegate_(delegate) { DCHECK(delegate_); } diff --git a/views/native_theme_painter.h b/views/native_theme_painter.h index 55dad89..a2c45bc 100644 --- a/views/native_theme_painter.h +++ b/views/native_theme_painter.h @@ -7,7 +7,6 @@ #pragma once #include "base/compiler_specific.h" -#include "ui/gfx/native_theme.h" #include "views/painter.h" namespace gfx { @@ -21,43 +20,14 @@ class Animation; namespace views { +class NativeThemeDelegate; + // A Painter that uses NativeTheme to implement painting and sizing. A // theme delegate must be given at construction time so that the appropriate // painting and sizing can be done. class NativeThemePainter : public Painter { public: - // A delagate that supports animating transtions between different native - // theme states. If animation is onging, the native theme painter will - // composite the foreground state over the backgroud state using an alpha - // between 0 and 255 based on the current value of the animation. - class Delegate { - public: - virtual ~Delegate() {} - - // Get the part that this native theme painter should draw. - virtual gfx::NativeTheme::Part GetThemePart() const = 0; - - // Get the state of the part, along with any extra data needed for painting. - virtual gfx::NativeTheme::State GetThemeState( - gfx::NativeTheme::ExtraParams* params) const = 0; - - // If the native theme painter is animated, return the Animation object - // that is controlling it. If no animation is ongoing, NULL may be - // returned. - virtual ui::Animation* GetThemeAnimation() const = 0; - - // If animation is onging, this returns the background native theme state. - virtual gfx::NativeTheme::State GetBackgroundThemeState( - gfx::NativeTheme::ExtraParams* params) const = 0; - - // If animation is onging, this returns the foreground native theme state. - // This state will be composited over the background using an alpha value - // based on the current value of the animation. - virtual gfx::NativeTheme::State GetForegroundThemeState( - gfx::NativeTheme::ExtraParams* params) const = 0; - }; - - explicit NativeThemePainter(Delegate* delegate); + explicit NativeThemePainter(NativeThemeDelegate* delegate); virtual ~NativeThemePainter() {} @@ -66,7 +36,7 @@ class NativeThemePainter : public Painter { private: // The delegate the controls the appearance of this painter. - Delegate* delegate_; + NativeThemeDelegate* delegate_; // Overridden from Painter: virtual void Paint(int w, int h, gfx::Canvas* canvas) OVERRIDE; diff --git a/views/views.gyp b/views/views.gyp index 0eb07f1..dfa848d 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -303,6 +303,7 @@ 'metrics_win.cc', 'mouse_watcher.cc', 'mouse_watcher.h', + 'native_theme_delegate.h', 'native_theme_painter.cc', 'native_theme_painter.h', 'painter.cc', @@ -550,6 +551,7 @@ 'target_name': 'views_examples', 'type': 'executable', 'dependencies': [ + '../app/app.gyp:app_resources', '../base/base.gyp:base', '../skia/skia.gyp:skia', '../third_party/icu/icu.gyp:icui18n', @@ -576,6 +578,8 @@ 'examples/menu_example.h', 'examples/native_theme_button_example.cc', 'examples/native_theme_button_example.h', + 'examples/native_theme_checkbox_example.cc', + 'examples/native_theme_checkbox_example.h', 'examples/radio_button_example.cc', 'examples/radio_button_example.h', 'examples/scroll_view_example.cc', |