diff options
-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', |