summaryrefslogtreecommitdiffstats
path: root/views/controls/button
diff options
context:
space:
mode:
Diffstat (limited to 'views/controls/button')
-rw-r--r--views/controls/button/button.cc79
-rw-r--r--views/controls/button/button.h74
-rw-r--r--views/controls/button/button_dropdown.cc192
-rw-r--r--views/controls/button/button_dropdown.h62
-rw-r--r--views/controls/button/checkbox.cc172
-rw-r--r--views/controls/button/checkbox.h84
-rw-r--r--views/controls/button/custom_button.cc236
-rw-r--r--views/controls/button/custom_button.h105
-rw-r--r--views/controls/button/image_button.cc154
-rw-r--r--views/controls/button/image_button.h101
-rw-r--r--views/controls/button/menu_button.cc253
-rw-r--r--views/controls/button/menu_button.h92
-rw-r--r--views/controls/button/native_button.cc178
-rw-r--r--views/controls/button/native_button.h100
-rw-r--r--views/controls/button/native_button_win.cc234
-rw-r--r--views/controls/button/native_button_win.h105
-rw-r--r--views/controls/button/native_button_wrapper.h62
-rw-r--r--views/controls/button/radio_button.cc107
-rw-r--r--views/controls/button/radio_button.h43
-rw-r--r--views/controls/button/text_button.cc325
-rw-r--r--views/controls/button/text_button.h138
21 files changed, 2896 insertions, 0 deletions
diff --git a/views/controls/button/button.cc b/views/controls/button/button.cc
new file mode 100644
index 0000000..cbb42bf
--- /dev/null
+++ b/views/controls/button/button.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/button/button.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// Button, public:
+
+Button::~Button() {
+}
+
+void Button::SetTooltipText(const std::wstring& tooltip_text) {
+ tooltip_text_ = tooltip_text;
+ TooltipTextChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Button, View overrides:
+
+bool Button::GetTooltipText(int x, int y, std::wstring* tooltip) {
+ if (!tooltip_text_.empty()) {
+ *tooltip = tooltip_text_;
+ return true;
+ }
+ return false;
+}
+
+bool Button::GetAccessibleKeyboardShortcut(std::wstring* shortcut) {
+ if (!accessible_shortcut_.empty()) {
+ *shortcut = accessible_shortcut_;
+ return true;
+ }
+ return false;
+}
+
+bool Button::GetAccessibleName(std::wstring* name) {
+ if (!accessible_name_.empty()) {
+ *name = accessible_name_;
+ return true;
+ }
+ return false;
+}
+
+bool Button::GetAccessibleRole(AccessibilityTypes::Role* role) {
+ *role = AccessibilityTypes::ROLE_PUSHBUTTON;
+ return true;
+}
+
+void Button::SetAccessibleKeyboardShortcut(const std::wstring& shortcut) {
+ accessible_shortcut_.assign(shortcut);
+}
+
+void Button::SetAccessibleName(const std::wstring& name) {
+ accessible_name_.assign(name);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Button, protected:
+
+Button::Button(ButtonListener* listener)
+ : listener_(listener),
+ tag_(-1),
+ mouse_event_flags_(0) {
+}
+
+void Button::NotifyClick(int mouse_event_flags) {
+ mouse_event_flags_ = mouse_event_flags;
+ // We can be called when there is no listener, in cases like double clicks on
+ // menu buttons etc.
+ if (listener_)
+ listener_->ButtonPressed(this);
+ // NOTE: don't attempt to reset mouse_event_flags_ as the listener may have
+ // deleted us.
+}
+
+} // namespace views
diff --git a/views/controls/button/button.h b/views/controls/button/button.h
new file mode 100644
index 0000000..514758f
--- /dev/null
+++ b/views/controls/button/button.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2006-2008 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_CONTROLS_BUTTON_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_BUTTON_H_
+
+#include "views/view.h"
+
+namespace views {
+
+class Button;
+
+// An interface implemented by an object to let it know that a button was
+// pressed.
+class ButtonListener {
+ public:
+ virtual void ButtonPressed(Button* sender) = 0;
+};
+
+// A View representing a button. Depending on the specific type, the button
+// could be implemented by a native control or custom rendered.
+class Button : public View {
+ public:
+ virtual ~Button();
+
+ void SetTooltipText(const std::wstring& tooltip_text);
+
+ int tag() const { return tag_; }
+ void set_tag(int tag) { tag_ = tag; }
+
+ int mouse_event_flags() const { return mouse_event_flags_; }
+
+ // Overridden from View:
+ virtual bool GetTooltipText(int x, int y, std::wstring* tooltip);
+ virtual bool GetAccessibleKeyboardShortcut(std::wstring* shortcut);
+ virtual bool GetAccessibleName(std::wstring* name);
+ virtual bool GetAccessibleRole(AccessibilityTypes::Role* role);
+ virtual void SetAccessibleKeyboardShortcut(const std::wstring& shortcut);
+ virtual void SetAccessibleName(const std::wstring& name);
+
+ protected:
+ // Construct the Button with a Listener. The listener can be NULL. This can be
+ // true of buttons that don't have a listener - e.g. menubuttons where there's
+ // no default action and checkboxes.
+ explicit Button(ButtonListener* listener);
+
+ // Cause the button to notify the listener that a click occurred.
+ virtual void NotifyClick(int mouse_event_flags);
+
+ // The button's listener. Notified when clicked.
+ ButtonListener* listener_;
+
+ private:
+ // The text shown in a tooltip.
+ std::wstring tooltip_text_;
+
+ // Accessibility data.
+ std::wstring accessible_shortcut_;
+ std::wstring accessible_name_;
+
+ // The id tag associated with this button. Used to disambiguate buttons in
+ // the ButtonListener implementation.
+ int tag_;
+
+ // Event flags present when the button was clicked.
+ int mouse_event_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(Button);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_BUTTON_BUTTON_H_
diff --git a/views/controls/button/button_dropdown.cc b/views/controls/button/button_dropdown.cc
new file mode 100644
index 0000000..270894b
--- /dev/null
+++ b/views/controls/button/button_dropdown.cc
@@ -0,0 +1,192 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/button/button_dropdown.h"
+
+#include "app/l10n_util.h"
+#include "base/message_loop.h"
+#include "grit/generated_resources.h"
+#include "views/controls/menu/view_menu_delegate.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+// How long to wait before showing the menu
+static const int kMenuTimerDelay = 500;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - constructors, destructors, initialization, cleanup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+ButtonDropDown::ButtonDropDown(ButtonListener* listener,
+ Menu::Delegate* menu_delegate)
+ : ImageButton(listener),
+ menu_delegate_(menu_delegate),
+ y_position_on_lbuttondown_(0),
+ show_menu_factory_(this) {
+}
+
+ButtonDropDown::~ButtonDropDown() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - Events
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool ButtonDropDown::OnMousePressed(const MouseEvent& e) {
+ if (IsEnabled() && e.IsLeftMouseButton() && HitTest(e.location())) {
+ // Store the y pos of the mouse coordinates so we can use them later to
+ // determine if the user dragged the mouse down (which should pop up the
+ // drag down menu immediately, instead of waiting for the timer)
+ y_position_on_lbuttondown_ = e.y();
+
+ // Schedule a task that will show the menu.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ show_menu_factory_.NewRunnableMethod(&ButtonDropDown::ShowDropDownMenu,
+ GetWidget()->GetNativeView()),
+ kMenuTimerDelay);
+ }
+
+ return ImageButton::OnMousePressed(e);
+}
+
+void ButtonDropDown::OnMouseReleased(const MouseEvent& e, bool canceled) {
+ ImageButton::OnMouseReleased(e, canceled);
+
+ if (canceled)
+ return;
+
+ if (e.IsLeftMouseButton())
+ show_menu_factory_.RevokeAll();
+
+ if (IsEnabled() && e.IsRightMouseButton() && HitTest(e.location())) {
+ show_menu_factory_.RevokeAll();
+ // Make the button look depressed while the menu is open.
+ // NOTE: SetState() schedules a paint, but it won't occur until after the
+ // context menu message loop has terminated, so we PaintNow() to
+ // update the appearance synchronously.
+ SetState(BS_PUSHED);
+ PaintNow();
+ ShowDropDownMenu(GetWidget()->GetNativeView());
+ }
+}
+
+bool ButtonDropDown::OnMouseDragged(const MouseEvent& e) {
+ bool result = ImageButton::OnMouseDragged(e);
+
+ if (!show_menu_factory_.empty()) {
+ // SM_CYDRAG is a pixel value for minimum dragging distance before operation
+ // counts as a drag, and not just as a click and accidental move of a mouse.
+ // See http://msdn2.microsoft.com/en-us/library/ms724385.aspx for details.
+ int dragging_threshold = GetSystemMetrics(SM_CYDRAG);
+
+ // If the mouse is dragged to a y position lower than where it was when
+ // clicked then we should not wait for the menu to appear but show
+ // it immediately.
+ if (e.y() > y_position_on_lbuttondown_ + dragging_threshold) {
+ show_menu_factory_.RevokeAll();
+ ShowDropDownMenu(GetWidget()->GetNativeView());
+ }
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - Menu functions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void ButtonDropDown::ShowContextMenu(int x, int y, bool is_mouse_gesture) {
+ show_menu_factory_.RevokeAll();
+ // Make the button look depressed while the menu is open.
+ // NOTE: SetState() schedules a paint, but it won't occur until after the
+ // context menu message loop has terminated, so we PaintNow() to
+ // update the appearance synchronously.
+ SetState(BS_PUSHED);
+ PaintNow();
+ ShowDropDownMenu(GetWidget()->GetNativeView());
+ SetState(BS_HOT);
+}
+
+void ButtonDropDown::ShowDropDownMenu(HWND window) {
+ if (menu_delegate_) {
+ gfx::Rect lb = GetLocalBounds(true);
+
+ // Both the menu position and the menu anchor type change if the UI layout
+ // is right-to-left.
+ gfx::Point menu_position(lb.origin());
+ menu_position.Offset(0, lb.height() - 1);
+ if (UILayoutIsRightToLeft())
+ menu_position.Offset(lb.width() - 1, 0);
+
+ Menu::AnchorPoint anchor = Menu::TOPLEFT;
+ if (UILayoutIsRightToLeft())
+ anchor = Menu::TOPRIGHT;
+
+ View::ConvertPointToScreen(this, &menu_position);
+
+ int left_bound = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ if (menu_position.x() < left_bound)
+ menu_position.set_x(left_bound);
+
+ Menu menu(menu_delegate_, anchor, window);
+
+ // ID's for AppendMenu is 1-based because RunMenu will ignore the user
+ // selection if id=0 is selected (0 = NO-OP) so we add 1 here and subtract 1
+ // in the handlers above to get the actual index
+ int item_count = menu_delegate_->GetItemCount();
+ for (int i = 0; i < item_count; i++) {
+ if (menu_delegate_->IsItemSeparator(i + 1)) {
+ menu.AppendSeparator();
+ } else {
+ if (menu_delegate_->HasIcon(i + 1))
+ menu.AppendMenuItemWithIcon(i + 1, L"", SkBitmap());
+ else
+ menu.AppendMenuItem(i+1, L"", Menu::NORMAL);
+ }
+ }
+
+ menu.RunMenuAt(menu_position.x(), menu_position.y());
+
+ // Need to explicitly clear mouse handler so that events get sent
+ // properly after the menu finishes running. If we don't do this, then
+ // the first click to other parts of the UI is eaten.
+ SetMouseHandler(NULL);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - Accessibility
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool ButtonDropDown::GetAccessibleDefaultAction(std::wstring* action) {
+ DCHECK(action);
+
+ action->assign(l10n_util::GetString(IDS_ACCACTION_PRESS));
+ return true;
+}
+
+bool ButtonDropDown::GetAccessibleRole(AccessibilityTypes::Role* role) {
+ DCHECK(role);
+
+ *role = AccessibilityTypes::ROLE_BUTTONDROPDOWN;
+ return true;
+}
+
+bool ButtonDropDown::GetAccessibleState(AccessibilityTypes::State* state) {
+ DCHECK(state);
+
+ *state = AccessibilityTypes::STATE_HASPOPUP;
+ return true;
+}
+
+} // namespace views
diff --git a/views/controls/button/button_dropdown.h b/views/controls/button/button_dropdown.h
new file mode 100644
index 0000000..feb5b5c
--- /dev/null
+++ b/views/controls/button/button_dropdown.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2006-2008 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_CONTROLS_BUTTON_BUTTON_DROPDOWN_H_
+#define VIEWS_CONTROLS_BUTTON_BUTTON_DROPDOWN_H_
+
+#include "base/task.h"
+#include "views/controls/button/image_button.h"
+#include "views/controls/menu/menu.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown
+//
+// A button class that when pressed (and held) or pressed (and drag down) will
+// display a menu
+//
+////////////////////////////////////////////////////////////////////////////////
+class ButtonDropDown : public ImageButton {
+ public:
+ ButtonDropDown(ButtonListener* listener, Menu::Delegate* menu_delegate);
+ virtual ~ButtonDropDown();
+
+ // Accessibility accessors, overridden from View.
+ virtual bool GetAccessibleDefaultAction(std::wstring* action);
+ virtual bool GetAccessibleRole(AccessibilityTypes::Role* role);
+ virtual bool GetAccessibleState(AccessibilityTypes::State* state);
+
+ private:
+ // Overridden from Button
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual bool OnMouseDragged(const MouseEvent& e);
+
+ // Overridden from View. Used to display the right-click menu, as triggered
+ // by the keyboard, for instance. Using the member function ShowDropDownMenu
+ // for the actual display.
+ virtual void ShowContextMenu(int x,
+ int y,
+ bool is_mouse_gesture);
+
+ // Internal function to show the dropdown menu
+ void ShowDropDownMenu(HWND window);
+
+ // Specifies who to delegate populating the menu
+ Menu::Delegate* menu_delegate_;
+
+ // Y position of mouse when left mouse button is pressed
+ int y_position_on_lbuttondown_;
+
+ // A factory for tasks that show the dropdown context menu for the button.
+ ScopedRunnableMethodFactory<ButtonDropDown> show_menu_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ButtonDropDown);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_BUTTON_BUTTON_DROPDOWN_H_
diff --git a/views/controls/button/checkbox.cc b/views/controls/button/checkbox.cc
new file mode 100644
index 0000000..8783bcf
--- /dev/null
+++ b/views/controls/button/checkbox.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "views/controls/button/checkbox.h"
+
+#include "app/gfx/chrome_canvas.h"
+#include "views/controls/label.h"
+
+namespace views {
+
+// static
+const char Checkbox::kViewClassName[] = "views/Checkbox";
+
+static const int kCheckboxLabelSpacing = 4;
+static const int kLabelFocusPaddingHorizontal = 2;
+static const int kLabelFocusPaddingVertical = 1;
+
+////////////////////////////////////////////////////////////////////////////////
+// Checkbox, public:
+
+Checkbox::Checkbox() : NativeButton(NULL), checked_(false) {
+ Init(std::wstring());
+}
+
+Checkbox::Checkbox(const std::wstring& label)
+ : NativeButton(NULL, label),
+ checked_(false) {
+ Init(label);
+}
+
+Checkbox::~Checkbox() {
+}
+
+void Checkbox::SetMultiLine(bool multiline) {
+ label_->SetMultiLine(multiline);
+}
+
+void Checkbox::SetChecked(bool checked) {
+ if (checked_ == checked)
+ return;
+ checked_ = checked;
+ if (native_wrapper_)
+ native_wrapper_->UpdateChecked();
+}
+
+// static
+int Checkbox::GetTextIndent() {
+ return NativeButtonWrapper::GetFixedWidth() + kCheckboxLabelSpacing;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Checkbox, View overrides:
+
+gfx::Size Checkbox::GetPreferredSize() {
+ gfx::Size prefsize = native_wrapper_->GetView()->GetPreferredSize();
+ prefsize.set_width(
+ prefsize.width() + kCheckboxLabelSpacing +
+ kLabelFocusPaddingHorizontal * 2);
+ gfx::Size label_prefsize = label_->GetPreferredSize();
+ prefsize.set_width(prefsize.width() + label_prefsize.width());
+ prefsize.set_height(
+ std::max(prefsize.height(),
+ label_prefsize.height() + kLabelFocusPaddingVertical * 2));
+ return prefsize;
+}
+
+void Checkbox::Layout() {
+ if (!native_wrapper_)
+ return;
+
+ gfx::Size checkmark_prefsize = native_wrapper_->GetView()->GetPreferredSize();
+ int label_x = checkmark_prefsize.width() + kCheckboxLabelSpacing +
+ kLabelFocusPaddingHorizontal;
+ label_->SetBounds(
+ label_x, 0, std::max(0, width() - label_x - kLabelFocusPaddingHorizontal),
+ height());
+ int first_line_height = label_->GetFont().height();
+ native_wrapper_->GetView()->SetBounds(
+ 0, ((first_line_height - checkmark_prefsize.height()) / 2),
+ checkmark_prefsize.width(), checkmark_prefsize.height());
+ native_wrapper_->GetView()->Layout();
+}
+
+void Checkbox::PaintFocusBorder(ChromeCanvas* canvas) {
+ // Our focus border is rendered by the label, so we don't do anything here.
+}
+
+View* Checkbox::GetViewForPoint(const gfx::Point& point) {
+ return GetViewForPoint(point, false);
+}
+
+View* Checkbox::GetViewForPoint(const gfx::Point& point,
+ bool can_create_floating) {
+ return GetLocalBounds(true).Contains(point) ? this : NULL;
+}
+
+void Checkbox::OnMouseEntered(const MouseEvent& e) {
+ native_wrapper_->SetPushed(HitTestLabel(e));
+}
+
+void Checkbox::OnMouseMoved(const MouseEvent& e) {
+ native_wrapper_->SetPushed(HitTestLabel(e));
+}
+
+void Checkbox::OnMouseExited(const MouseEvent& e) {
+ native_wrapper_->SetPushed(false);
+}
+
+bool Checkbox::OnMousePressed(const MouseEvent& e) {
+ native_wrapper_->SetPushed(HitTestLabel(e));
+ return true;
+}
+
+void Checkbox::OnMouseReleased(const MouseEvent& e, bool canceled) {
+ native_wrapper_->SetPushed(false);
+ if (!canceled && HitTestLabel(e)) {
+ SetChecked(!checked());
+ ButtonPressed();
+ }
+}
+
+bool Checkbox::OnMouseDragged(const MouseEvent& e) {
+ return false;
+}
+
+void Checkbox::WillGainFocus() {
+ label_->set_paint_as_focused(true);
+}
+
+void Checkbox::WillLoseFocus() {
+ label_->set_paint_as_focused(false);
+}
+
+std::string Checkbox::GetClassName() const {
+ return kViewClassName;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Checkbox, NativeButton overrides:
+
+void Checkbox::CreateWrapper() {
+ native_wrapper_ = NativeButtonWrapper::CreateCheckboxWrapper(this);
+ native_wrapper_->UpdateLabel();
+ native_wrapper_->UpdateChecked();
+}
+
+void Checkbox::InitBorder() {
+ // No border, so we do nothing.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Checkbox, protected:
+
+bool Checkbox::HitTestLabel(const MouseEvent& e) {
+ gfx::Point tmp(e.location());
+ ConvertPointToView(this, label_, &tmp);
+ return label_->HitTest(tmp);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Checkbox, private:
+
+void Checkbox::Init(const std::wstring& label_text) {
+ set_minimum_size(gfx::Size(0, 0));
+ label_ = new Label(label_text);
+ label_->set_has_focus_border(true);
+ label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ AddChildView(label_);
+}
+
+} // namespace views
diff --git a/views/controls/button/checkbox.h b/views/controls/button/checkbox.h
new file mode 100644
index 0000000..db6706b
--- /dev/null
+++ b/views/controls/button/checkbox.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2009 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_CONTROLS_BUTTON_CHECKBOX_H_
+#define VIEWS_CONTROLS_BUTTON_CHECKBOX_H_
+
+#include "views/controls/button/native_button.h"
+
+namespace views {
+
+class Label;
+
+// A NativeButton subclass representing a checkbox.
+class Checkbox : public NativeButton {
+ public:
+ // The button's class name.
+ static const char kViewClassName[];
+
+ Checkbox();
+ Checkbox(const std::wstring& label);
+ virtual ~Checkbox();
+
+ // 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 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.
+ void SetMultiLine(bool multiline);
+
+ // Sets/Gets whether or not the checkbox is checked.
+ virtual void SetChecked(bool checked);
+ bool checked() const { return checked_; }
+
+ // Returns the indentation of the text from the left edge of the view.
+ static int GetTextIndent();
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void PaintFocusBorder(ChromeCanvas* canvas);
+ virtual View* GetViewForPoint(const gfx::Point& point);
+ virtual View* GetViewForPoint(const gfx::Point& point,
+ bool can_create_floating);
+ virtual void OnMouseEntered(const MouseEvent& e);
+ virtual void OnMouseMoved(const MouseEvent& e);
+ virtual void OnMouseExited(const MouseEvent& e);
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual bool OnMouseDragged(const MouseEvent& e);
+ virtual void WillGainFocus();
+ virtual void WillLoseFocus();
+
+ protected:
+ virtual std::string GetClassName() const;
+
+ // Overridden from NativeButton2:
+ virtual void CreateWrapper();
+ virtual void InitBorder();
+
+ // Returns true if the event (in Checkbox coordinates) is within the bounds of
+ // the label.
+ bool HitTestLabel(const MouseEvent& e);
+
+ private:
+ // Called from the constructor to create and configure the checkbox label.
+ void Init(const std::wstring& label_text);
+
+ // The checkbox's label. We don't use the OS version because of transparency
+ // and sizing issues.
+ Label* label_;
+
+ // True if the checkbox is checked.
+ bool checked_;
+
+ DISALLOW_COPY_AND_ASSIGN(Checkbox);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_BUTTON_CHECKBOX_H_
diff --git a/views/controls/button/custom_button.cc b/views/controls/button/custom_button.cc
new file mode 100644
index 0000000..2b5f43d
--- /dev/null
+++ b/views/controls/button/custom_button.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/button/custom_button.h"
+
+#include "app/throb_animation.h"
+#include "base/keyboard_codes.h"
+
+namespace views {
+
+// How long the hover animation takes if uninterrupted.
+static const int kHoverFadeDurationMs = 150;
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, public:
+
+CustomButton::~CustomButton() {
+}
+
+void CustomButton::SetState(ButtonState state) {
+ if (state != state_) {
+ if (animate_on_state_change_ || !hover_animation_->IsAnimating()) {
+ animate_on_state_change_ = true;
+ if (state_ == BS_NORMAL && state == BS_HOT) {
+ // Button is hovered from a normal state, start hover animation.
+ hover_animation_->Show();
+ } else if (state_ == BS_HOT && state == BS_NORMAL) {
+ // Button is returning to a normal state from hover, start hover
+ // fade animation.
+ hover_animation_->Hide();
+ } else {
+ hover_animation_->Stop();
+ }
+ }
+
+ state_ = state;
+ SchedulePaint();
+ }
+}
+
+void CustomButton::StartThrobbing(int cycles_til_stop) {
+ animate_on_state_change_ = false;
+ hover_animation_->StartThrobbing(cycles_til_stop);
+}
+
+void CustomButton::SetAnimationDuration(int duration) {
+ hover_animation_->SetSlideDuration(duration);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, View overrides:
+
+void CustomButton::SetEnabled(bool enabled) {
+ if (enabled && state_ == BS_DISABLED) {
+ SetState(BS_NORMAL);
+ } else if (!enabled && state_ != BS_DISABLED) {
+ SetState(BS_DISABLED);
+ }
+}
+
+bool CustomButton::IsEnabled() const {
+ return state_ != BS_DISABLED;
+}
+
+bool CustomButton::IsFocusable() const {
+ return (state_ != BS_DISABLED) && View::IsFocusable();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, protected:
+
+CustomButton::CustomButton(ButtonListener* listener)
+ : Button(listener),
+ state_(BS_NORMAL),
+ animate_on_state_change_(true),
+ triggerable_event_flags_(MouseEvent::EF_LEFT_BUTTON_DOWN) {
+ hover_animation_.reset(new ThrobAnimation(this));
+ hover_animation_->SetSlideDuration(kHoverFadeDurationMs);
+}
+
+bool CustomButton::IsTriggerableEvent(const MouseEvent& e) {
+ return (triggerable_event_flags_ & e.GetFlags()) != 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, View overrides (protected):
+
+bool CustomButton::AcceleratorPressed(const Accelerator& accelerator) {
+ if (enabled_) {
+ SetState(BS_NORMAL);
+ NotifyClick(0);
+ return true;
+ }
+ return false;
+}
+
+bool CustomButton::OnMousePressed(const MouseEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (IsTriggerableEvent(e) && HitTest(e.location()))
+ SetState(BS_PUSHED);
+ RequestFocus();
+ }
+ return true;
+}
+
+bool CustomButton::OnMouseDragged(const MouseEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (!HitTest(e.location()))
+ SetState(BS_NORMAL);
+ else if (IsTriggerableEvent(e))
+ SetState(BS_PUSHED);
+ else
+ SetState(BS_HOT);
+ }
+ return true;
+}
+
+void CustomButton::OnMouseReleased(const MouseEvent& e, bool canceled) {
+ if (InDrag()) {
+ // Starting a drag results in a MouseReleased, we need to ignore it.
+ return;
+ }
+
+ if (state_ != BS_DISABLED) {
+ if (canceled || !HitTest(e.location())) {
+ SetState(BS_NORMAL);
+ } else {
+ SetState(BS_HOT);
+ if (IsTriggerableEvent(e)) {
+ NotifyClick(e.GetFlags());
+ // We may be deleted at this point (by the listener's notification
+ // handler) so no more doing anything, just return.
+ return;
+ }
+ }
+ }
+}
+
+void CustomButton::OnMouseEntered(const MouseEvent& e) {
+ if (state_ != BS_DISABLED)
+ SetState(BS_HOT);
+}
+
+void CustomButton::OnMouseMoved(const MouseEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (HitTest(e.location())) {
+ SetState(BS_HOT);
+ } else {
+ SetState(BS_NORMAL);
+ }
+ }
+}
+
+void CustomButton::OnMouseExited(const MouseEvent& e) {
+ // Starting a drag results in a MouseExited, we need to ignore it.
+ if (state_ != BS_DISABLED && !InDrag())
+ SetState(BS_NORMAL);
+}
+
+bool CustomButton::OnKeyPressed(const KeyEvent& e) {
+ if (state_ != BS_DISABLED) {
+ // Space sets button state to pushed. Enter clicks the button. This matches
+ // the Windows native behavior of buttons, where Space clicks the button
+ // on KeyRelease and Enter clicks the button on KeyPressed.
+ if (e.GetCharacter() == base::VKEY_SPACE) {
+ SetState(BS_PUSHED);
+ return true;
+ } else if (e.GetCharacter() == base::VKEY_RETURN) {
+ SetState(BS_NORMAL);
+ NotifyClick(0);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CustomButton::OnKeyReleased(const KeyEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (e.GetCharacter() == base::VKEY_SPACE) {
+ SetState(BS_NORMAL);
+ NotifyClick(0);
+ return true;
+ }
+ }
+ return false;
+}
+
+void CustomButton::OnDragDone() {
+ SetState(BS_NORMAL);
+}
+
+void CustomButton::ShowContextMenu(int x, int y, bool is_mouse_gesture) {
+ if (GetContextMenuController()) {
+ // We're about to show the context menu. Showing the context menu likely
+ // means we won't get a mouse exited and reset state. Reset it now to be
+ // sure.
+ if (state_ != BS_DISABLED)
+ SetState(BS_NORMAL);
+ View::ShowContextMenu(x, y, is_mouse_gesture);
+ }
+}
+
+void CustomButton::ViewHierarchyChanged(bool is_add, View *parent,
+ View *child) {
+ if (!is_add && state_ != BS_DISABLED)
+ SetState(BS_NORMAL);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, AnimationDelegate implementation:
+
+void CustomButton::AnimationProgressed(const Animation* animation) {
+ SchedulePaint();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, private:
+
+void CustomButton::SetHighlighted(bool highlighted) {
+ if (highlighted && state_ != BS_DISABLED) {
+ SetState(BS_HOT);
+ } else if (!highlighted && state_ != BS_DISABLED) {
+ SetState(BS_NORMAL);
+ }
+}
+
+bool CustomButton::IsHighlighted() const {
+ return state_ == BS_HOT;
+}
+
+bool CustomButton::IsPushed() const {
+ return state_ == BS_PUSHED;
+}
+
+} // namespace views
diff --git a/views/controls/button/custom_button.h b/views/controls/button/custom_button.h
new file mode 100644
index 0000000..32ff76b
--- /dev/null
+++ b/views/controls/button/custom_button.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2006-2008 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_CONTROLS_BUTTON_CUSTOM_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_
+
+#include "app/animation.h"
+#include "views/controls/button/button.h"
+
+class ThrobAnimation;
+
+namespace views {
+
+// A button with custom rendering. The common base class of IconButton and
+// TextButton.
+class CustomButton : public Button,
+ public AnimationDelegate {
+ public:
+ virtual ~CustomButton();
+
+ // Possible states
+ enum ButtonState {
+ BS_NORMAL = 0,
+ BS_HOT,
+ BS_PUSHED,
+ BS_DISABLED,
+ BS_COUNT
+ };
+
+ // Get/sets the current display state of the button.
+ ButtonState state() const { return state_; }
+ void SetState(ButtonState state);
+
+ // Starts throbbing. See HoverAnimation for a description of cycles_til_stop.
+ void StartThrobbing(int cycles_til_stop);
+
+ // Set how long the hover animation will last for.
+ void SetAnimationDuration(int duration);
+
+ // Overridden from View:
+ virtual void SetEnabled(bool enabled);
+ virtual bool IsEnabled() const;
+ virtual bool IsFocusable() const;
+
+ void set_triggerable_event_flags(int triggerable_event_flags) {
+ triggerable_event_flags_ = triggerable_event_flags;
+ }
+
+ int triggerable_event_flags() const {
+ return triggerable_event_flags_;
+ }
+
+ protected:
+ // Construct the Button with a Listener. See comment for Button's ctor.
+ explicit CustomButton(ButtonListener* listener);
+
+ // Returns true if the event is one that can trigger notifying the listener.
+ // This implementation returns true if the left mouse button is down.
+ virtual bool IsTriggerableEvent(const MouseEvent& e);
+
+ // Overridden from View:
+ virtual bool AcceleratorPressed(const Accelerator& accelerator);
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual bool OnMouseDragged(const MouseEvent& e);
+ virtual void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual void OnMouseEntered(const MouseEvent& e);
+ virtual void OnMouseMoved(const MouseEvent& e);
+ virtual void OnMouseExited(const MouseEvent& e);
+ virtual bool OnKeyPressed(const KeyEvent& e);
+ virtual bool OnKeyReleased(const KeyEvent& e);
+ virtual void OnDragDone();
+ virtual void ShowContextMenu(int x, int y, bool is_mouse_gesture);
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation);
+
+ // The button state (defined in implementation)
+ ButtonState state_;
+
+ // Hover animation.
+ scoped_ptr<ThrobAnimation> hover_animation_;
+
+ private:
+ // Set / test whether the button is highlighted (in the hover state).
+ void SetHighlighted(bool highlighted);
+ bool IsHighlighted() const;
+
+ // Returns whether the button is pushed.
+ bool IsPushed() const;
+
+ // Should we animate when the state changes? Defaults to true, but false while
+ // throbbing.
+ bool animate_on_state_change_;
+
+ // Mouse event flags which can trigger button actions.
+ int triggerable_event_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomButton);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_
diff --git a/views/controls/button/image_button.cc b/views/controls/button/image_button.cc
new file mode 100644
index 0000000..3c4c7fe
--- /dev/null
+++ b/views/controls/button/image_button.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/button/image_button.h"
+
+#include "app/gfx/chrome_canvas.h"
+#include "app/throb_animation.h"
+#include "skia/ext/image_operations.h"
+
+namespace views {
+
+static const int kDefaultWidth = 16; // Default button width if no theme.
+static const int kDefaultHeight = 14; // Default button height if no theme.
+
+////////////////////////////////////////////////////////////////////////////////
+// ImageButton, public:
+
+ImageButton::ImageButton(ButtonListener* listener)
+ : CustomButton(listener),
+ h_alignment_(ALIGN_LEFT),
+ v_alignment_(ALIGN_TOP) {
+ // By default, we request that the ChromeCanvas passed to our View::Paint()
+ // implementation is flipped horizontally so that the button's bitmaps are
+ // mirrored when the UI directionality is right-to-left.
+ EnableCanvasFlippingForRTLUI(true);
+}
+
+ImageButton::~ImageButton() {
+}
+
+void ImageButton::SetImage(ButtonState aState, SkBitmap* anImage) {
+ images_[aState] = anImage ? *anImage : SkBitmap();
+}
+
+void ImageButton::SetImageAlignment(HorizontalAlignment h_align,
+ VerticalAlignment v_align) {
+ h_alignment_ = h_align;
+ v_alignment_ = v_align;
+ SchedulePaint();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImageButton, View overrides:
+
+gfx::Size ImageButton::GetPreferredSize() {
+ if (!images_[BS_NORMAL].isNull())
+ return gfx::Size(images_[BS_NORMAL].width(), images_[BS_NORMAL].height());
+ return gfx::Size(kDefaultWidth, kDefaultHeight);
+}
+
+void ImageButton::Paint(ChromeCanvas* canvas) {
+ // Call the base class first to paint any background/borders.
+ View::Paint(canvas);
+
+ SkBitmap img = GetImageToPaint();
+
+ if (!img.isNull()) {
+ int x = 0, y = 0;
+
+ if (h_alignment_ == ALIGN_CENTER)
+ x = (width() - img.width()) / 2;
+ else if (h_alignment_ == ALIGN_RIGHT)
+ x = width() - img.width();
+
+ if (v_alignment_ == ALIGN_MIDDLE)
+ y = (height() - img.height()) / 2;
+ else if (v_alignment_ == ALIGN_BOTTOM)
+ y = height() - img.height();
+
+ canvas->DrawBitmapInt(img, x, y);
+ }
+ PaintFocusBorder(canvas);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImageButton, protected:
+
+SkBitmap ImageButton::GetImageToPaint() {
+ SkBitmap img;
+
+ if (!images_[BS_HOT].isNull() && hover_animation_->IsAnimating()) {
+ img = skia::ImageOperations::CreateBlendedBitmap(images_[BS_NORMAL],
+ images_[BS_HOT], hover_animation_->GetCurrentValue());
+ } else {
+ img = images_[state_];
+ }
+
+ return !img.isNull() ? img : images_[BS_NORMAL];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToggleImageButton, public:
+
+ToggleImageButton::ToggleImageButton(ButtonListener* listener)
+ : ImageButton(listener),
+ toggled_(false) {
+}
+
+ToggleImageButton::~ToggleImageButton() {
+}
+
+void ToggleImageButton::SetToggled(bool toggled) {
+ if (toggled == toggled_)
+ return;
+
+ for (int i = 0; i < BS_COUNT; ++i) {
+ SkBitmap temp = images_[i];
+ images_[i] = alternate_images_[i];
+ alternate_images_[i] = temp;
+ }
+ toggled_ = toggled;
+ SchedulePaint();
+}
+
+void ToggleImageButton::SetToggledImage(ButtonState state, SkBitmap* image) {
+ if (toggled_) {
+ images_[state] = image ? *image : SkBitmap();
+ if (state_ == state)
+ SchedulePaint();
+ } else {
+ alternate_images_[state] = image ? *image : SkBitmap();
+ }
+}
+
+void ToggleImageButton::SetToggledTooltipText(const std::wstring& tooltip) {
+ toggled_tooltip_text_.assign(tooltip);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToggleImageButton, ImageButton overrides:
+
+void ToggleImageButton::SetImage(ButtonState state, SkBitmap* image) {
+ if (toggled_) {
+ alternate_images_[state] = image ? *image : SkBitmap();
+ } else {
+ images_[state] = image ? *image : SkBitmap();
+ if (state_ == state)
+ SchedulePaint();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToggleImageButton, View overrides:
+
+bool ToggleImageButton::GetTooltipText(int x, int y, std::wstring* tooltip) {
+ if (!toggled_ || toggled_tooltip_text_.empty())
+ return Button::GetTooltipText(x, y, tooltip);
+
+ *tooltip = toggled_tooltip_text_;
+ return true;
+}
+
+} // namespace views
diff --git a/views/controls/button/image_button.h b/views/controls/button/image_button.h
new file mode 100644
index 0000000..c687561
--- /dev/null
+++ b/views/controls/button/image_button.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2006-2008 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_CONTROLS_BUTTON_IMAGE_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_IMAGE_BUTTON_H_
+
+#include "skia/include/SkBitmap.h"
+#include "views/controls/button/custom_button.h"
+
+namespace views {
+
+// An image button.
+class ImageButton : public CustomButton {
+ public:
+ explicit ImageButton(ButtonListener* listener);
+ virtual ~ImageButton();
+
+ // Set the image the button should use for the provided state.
+ virtual void SetImage(ButtonState aState, SkBitmap* anImage);
+
+ enum HorizontalAlignment { ALIGN_LEFT = 0,
+ ALIGN_CENTER,
+ ALIGN_RIGHT, };
+
+ enum VerticalAlignment { ALIGN_TOP = 0,
+ ALIGN_MIDDLE,
+ ALIGN_BOTTOM };
+
+ // Sets how the image is laid out within the button's bounds.
+ void SetImageAlignment(HorizontalAlignment h_align,
+ VerticalAlignment v_align);
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Paint(ChromeCanvas* canvas);
+
+ protected:
+ // Returns the image to paint. This is invoked from paint and returns a value
+ // from images.
+ virtual SkBitmap GetImageToPaint();
+
+ // The images used to render the different states of this button.
+ SkBitmap images_[BS_COUNT];
+
+ private:
+ // Image alignment.
+ HorizontalAlignment h_alignment_;
+ VerticalAlignment v_alignment_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageButton);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ToggleImageButton
+//
+// A toggle-able ImageButton. It swaps out its graphics when toggled.
+//
+////////////////////////////////////////////////////////////////////////////////
+class ToggleImageButton : public ImageButton {
+ public:
+ ToggleImageButton(ButtonListener* listener);
+ virtual ~ToggleImageButton();
+
+ // Change the toggled state.
+ void SetToggled(bool toggled);
+
+ // Like Button::SetImage(), but to set the graphics used for the
+ // "has been toggled" state. Must be called for each button state
+ // before the button is toggled.
+ void SetToggledImage(ButtonState state, SkBitmap* image);
+
+ // Set the tooltip text displayed when the button is toggled.
+ void SetToggledTooltipText(const std::wstring& tooltip);
+
+ // Overridden from ImageButton:
+ virtual void SetImage(ButtonState aState, SkBitmap* anImage);
+
+ // Overridden from View:
+ virtual bool GetTooltipText(int x, int y, std::wstring* tooltip);
+
+ private:
+ // The parent class's images_ member is used for the current images,
+ // and this array is used to hold the alternative images.
+ // We swap between the two when toggling.
+ SkBitmap alternate_images_[BS_COUNT];
+
+ // True if the button is currently toggled.
+ bool toggled_;
+
+ // The parent class's tooltip_text_ is displayed when not toggled, and
+ // this one is shown when toggled.
+ std::wstring toggled_tooltip_text_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ToggleImageButton);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_BUTTON_IMAGE_BUTTON_H_
diff --git a/views/controls/button/menu_button.cc b/views/controls/button/menu_button.cc
new file mode 100644
index 0000000..888137b
--- /dev/null
+++ b/views/controls/button/menu_button.cc
@@ -0,0 +1,253 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/button/menu_button.h"
+
+#include "app/drag_drop_types.h"
+#include "app/gfx/chrome_canvas.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "views/controls/button/button.h"
+#include "views/controls/menu/view_menu_delegate.h"
+#include "views/event.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace views {
+
+// The amount of time, in milliseconds, we wait before allowing another mouse
+// pressed event to show the menu.
+static const int64 kMinimumTimeBetweenButtonClicks = 100;
+
+// The down arrow used to differentiate the menu button from normal
+// text buttons.
+static const SkBitmap* kMenuMarker = NULL;
+
+// How much padding to put on the left and right of the menu marker.
+static const int kMenuMarkerPaddingLeft = 3;
+static const int kMenuMarkerPaddingRight = -1;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - constructors, destructors, initialization
+//
+////////////////////////////////////////////////////////////////////////////////
+
+MenuButton::MenuButton(ButtonListener* listener,
+ const std::wstring& text,
+ ViewMenuDelegate* menu_delegate,
+ bool show_menu_marker)
+ : TextButton(listener, text),
+ menu_visible_(false),
+ menu_closed_time_(),
+ menu_delegate_(menu_delegate),
+ show_menu_marker_(show_menu_marker) {
+ if (kMenuMarker == NULL) {
+ kMenuMarker = ResourceBundle::GetSharedInstance()
+ .GetBitmapNamed(IDR_MENU_DROPARROW);
+ }
+ set_alignment(TextButton::ALIGN_LEFT);
+}
+
+MenuButton::~MenuButton() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - Public APIs
+//
+////////////////////////////////////////////////////////////////////////////////
+
+gfx::Size MenuButton::GetPreferredSize() {
+ gfx::Size prefsize = TextButton::GetPreferredSize();
+ if (show_menu_marker_) {
+ prefsize.Enlarge(kMenuMarker->width() + kMenuMarkerPaddingLeft +
+ kMenuMarkerPaddingRight,
+ 0);
+ }
+ return prefsize;
+}
+
+void MenuButton::Paint(ChromeCanvas* canvas, bool for_drag) {
+ TextButton::Paint(canvas, for_drag);
+
+ if (show_menu_marker_) {
+ gfx::Insets insets = GetInsets();
+
+ // We can not use the views' mirroring infrastructure for mirroring a
+ // MenuButton control (see TextButton::Paint() for a detailed explanation
+ // regarding why we can not flip the canvas). Therefore, we need to
+ // manually mirror the position of the down arrow.
+ gfx::Rect arrow_bounds(width() - insets.right() -
+ kMenuMarker->width() - kMenuMarkerPaddingRight,
+ height() / 2 - kMenuMarker->height() / 2,
+ kMenuMarker->width(),
+ kMenuMarker->height());
+ arrow_bounds.set_x(MirroredLeftPointForRect(arrow_bounds));
+ canvas->DrawBitmapInt(*kMenuMarker, arrow_bounds.x(), arrow_bounds.y());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - Events
+//
+////////////////////////////////////////////////////////////////////////////////
+
+int MenuButton::GetMaximumScreenXCoordinate() {
+ Widget* widget = GetWidget();
+
+ if (!widget) {
+ NOTREACHED();
+ return 0;
+ }
+
+ HWND hwnd = widget->GetNativeView();
+ CRect t;
+ ::GetWindowRect(hwnd, &t);
+
+ gfx::Rect r(t);
+ gfx::Rect monitor_rect = win_util::GetMonitorBoundsForRect(r);
+ return monitor_rect.x() + monitor_rect.width() - 1;
+}
+
+bool MenuButton::Activate() {
+ SetState(BS_PUSHED);
+ // We need to synchronously paint here because subsequently we enter a
+ // menu modal loop which will stop this window from updating and
+ // receiving the paint message that should be spawned by SetState until
+ // after the menu closes.
+ PaintNow();
+ if (menu_delegate_) {
+ gfx::Rect lb = GetLocalBounds(true);
+
+ // The position of the menu depends on whether or not the locale is
+ // right-to-left.
+ gfx::Point menu_position(lb.right(), lb.bottom());
+ if (UILayoutIsRightToLeft())
+ menu_position.set_x(lb.x());
+
+ View::ConvertPointToScreen(this, &menu_position);
+ if (UILayoutIsRightToLeft())
+ menu_position.Offset(2, -4);
+ else
+ menu_position.Offset(-2, -4);
+
+ int max_x_coordinate = GetMaximumScreenXCoordinate();
+ if (max_x_coordinate && max_x_coordinate <= menu_position.x())
+ menu_position.set_x(max_x_coordinate - 1);
+
+ // We're about to show the menu from a mouse press. By showing from the
+ // mouse press event we block RootView in mouse dispatching. This also
+ // appears to cause RootView to get a mouse pressed BEFORE the mouse
+ // release is seen, which means RootView sends us another mouse press no
+ // matter where the user pressed. To force RootView to recalculate the
+ // mouse target during the mouse press we explicitly set the mouse handler
+ // to NULL.
+ GetRootView()->SetMouseHandler(NULL);
+
+ menu_visible_ = true;
+ menu_delegate_->RunMenu(this, menu_position.ToPOINT(),
+ GetWidget()->GetNativeView());
+ menu_visible_ = false;
+ menu_closed_time_ = Time::Now();
+
+ // Now that the menu has closed, we need to manually reset state to
+ // "normal" since the menu modal loop will have prevented normal
+ // mouse move messages from getting to this View. We set "normal"
+ // and not "hot" because the likelihood is that the mouse is now
+ // somewhere else (user clicked elsewhere on screen to close the menu
+ // or selected an item) and we will inevitably refresh the hot state
+ // in the event the mouse _is_ over the view.
+ SetState(BS_NORMAL);
+
+ // We must return false here so that the RootView does not get stuck
+ // sending all mouse pressed events to us instead of the appropriate
+ // target.
+ return false;
+ }
+ return true;
+}
+
+bool MenuButton::OnMousePressed(const MouseEvent& e) {
+ RequestFocus();
+ if (state() != BS_DISABLED) {
+ // If we're draggable (GetDragOperations returns a non-zero value), then
+ // don't pop on press, instead wait for release.
+ if (e.IsOnlyLeftMouseButton() && HitTest(e.location()) &&
+ GetDragOperations(e.x(), e.y()) == DragDropTypes::DRAG_NONE) {
+ TimeDelta delta = Time::Now() - menu_closed_time_;
+ int64 delta_in_milliseconds = delta.InMilliseconds();
+ if (delta_in_milliseconds > kMinimumTimeBetweenButtonClicks) {
+ return Activate();
+ }
+ }
+ }
+ return true;
+}
+
+void MenuButton::OnMouseReleased(const MouseEvent& e,
+ bool canceled) {
+ if (GetDragOperations(e.x(), e.y()) != DragDropTypes::DRAG_NONE &&
+ state() != BS_DISABLED && !canceled && !InDrag() &&
+ e.IsOnlyLeftMouseButton() && HitTest(e.location())) {
+ Activate();
+ } else {
+ TextButton::OnMouseReleased(e, canceled);
+ }
+}
+
+// When the space bar or the enter key is pressed we need to show the menu.
+bool MenuButton::OnKeyReleased(const KeyEvent& e) {
+ if ((e.GetCharacter() == VK_SPACE) || (e.GetCharacter() == VK_RETURN)) {
+ return Activate();
+ }
+ return true;
+}
+
+// The reason we override View::OnMouseExited is because we get this event when
+// we display the menu. If we don't override this method then
+// BaseButton::OnMouseExited will get the event and will set the button's state
+// to BS_NORMAL instead of keeping the state BM_PUSHED. This, in turn, will
+// cause the button to appear depressed while the menu is displayed.
+void MenuButton::OnMouseExited(const MouseEvent& event) {
+ if ((state_ != BS_DISABLED) && (!menu_visible_) && (!InDrag())) {
+ SetState(BS_NORMAL);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - accessibility
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool MenuButton::GetAccessibleDefaultAction(std::wstring* action) {
+ DCHECK(action);
+
+ action->assign(l10n_util::GetString(IDS_ACCACTION_PRESS));
+ return true;
+}
+
+bool MenuButton::GetAccessibleRole(AccessibilityTypes::Role* role) {
+ DCHECK(role);
+
+ *role = AccessibilityTypes::ROLE_BUTTONDROPDOWN;
+ return true;
+}
+
+bool MenuButton::GetAccessibleState(AccessibilityTypes::State* state) {
+ DCHECK(state);
+
+ *state = AccessibilityTypes::STATE_HASPOPUP;
+ return true;
+}
+
+} // namespace views
diff --git a/views/controls/button/menu_button.h b/views/controls/button/menu_button.h
new file mode 100644
index 0000000..cbe9ab8
--- /dev/null
+++ b/views/controls/button/menu_button.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2006-2008 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_CONTROLS_BUTTON_MENU_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_MENU_BUTTON_H_
+
+#include <windows.h>
+
+#include "app/gfx/chrome_font.h"
+#include "base/time.h"
+#include "views/background.h"
+#include "views/controls/button/text_button.h"
+
+namespace views {
+
+class MouseEvent;
+class ViewMenuDelegate;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton
+//
+// A button that shows a menu when the left mouse button is pushed
+//
+////////////////////////////////////////////////////////////////////////////////
+class MenuButton : public TextButton {
+ public:
+ //
+ // Create a Button
+ MenuButton(ButtonListener* listener,
+ const std::wstring& text,
+ ViewMenuDelegate* menu_delegate,
+ bool show_menu_marker);
+ virtual ~MenuButton();
+
+ void set_menu_delegate(ViewMenuDelegate* delegate) {
+ menu_delegate_ = delegate;
+ }
+
+ // Activate the button (called when the button is pressed).
+ virtual bool Activate();
+
+ // Overridden to take into account the potential use of a drop marker.
+ virtual gfx::Size GetPreferredSize();
+ virtual void Paint(ChromeCanvas* canvas, bool for_drag);
+
+ // These methods are overriden to implement a simple push button
+ // behavior
+ virtual bool OnMousePressed(const MouseEvent& e);
+ void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual bool OnKeyReleased(const KeyEvent& e);
+ virtual void OnMouseExited(const MouseEvent& event);
+
+ // Accessibility accessors, overridden from View.
+ virtual bool GetAccessibleDefaultAction(std::wstring* action);
+ virtual bool GetAccessibleRole(AccessibilityTypes::Role* role);
+ virtual bool GetAccessibleState(AccessibilityTypes::State* state);
+
+ protected:
+ // true if the menu is currently visible.
+ bool menu_visible_;
+
+ private:
+
+ // Compute the maximum X coordinate for the current screen. MenuButtons
+ // use this to make sure a menu is never shown off screen.
+ int GetMaximumScreenXCoordinate();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MenuButton);
+
+ // We use a time object in order to keep track of when the menu was closed.
+ // The time is used for simulating menu behavior for the menu button; that
+ // is, if the menu is shown and the button is pressed, we need to close the
+ // menu. There is no clean way to get the second click event because the
+ // menu is displayed using a modal loop and, unlike regular menus in Windows,
+ // the button is not part of the displayed menu.
+ base::Time menu_closed_time_;
+
+ // The associated menu's resource identifier.
+ ViewMenuDelegate* menu_delegate_;
+
+ // Whether or not we're showing a drop marker.
+ bool show_menu_marker_;
+
+ friend class TextButtonBackground;
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_BUTTON_MENU_BUTTON_H_
diff --git a/views/controls/button/native_button.cc b/views/controls/button/native_button.cc
new file mode 100644
index 0000000..0af6f88
--- /dev/null
+++ b/views/controls/button/native_button.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "views/controls/button/native_button.h"
+
+#include "app/l10n_util.h"
+#include "base/logging.h"
+
+namespace views {
+
+static int kButtonBorderHWidth = 8;
+
+// static
+const char NativeButton::kViewClassName[] = "views/NativeButton";
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButton, public:
+
+NativeButton::NativeButton(ButtonListener* listener)
+ : Button(listener),
+ native_wrapper_(NULL),
+ is_default_(false),
+ ignore_minimum_size_(false),
+ minimum_size_(50, 14) {
+ // The min size in DLUs comes from
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp
+ InitBorder();
+ SetFocusable(true);
+}
+
+NativeButton::NativeButton(ButtonListener* listener, const std::wstring& label)
+ : Button(listener),
+ native_wrapper_(NULL),
+ is_default_(false),
+ ignore_minimum_size_(false),
+ minimum_size_(50, 14) {
+ SetLabel(label); // SetLabel takes care of label layout in RTL UI.
+ // The min size in DLUs comes from
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp
+ InitBorder();
+ SetFocusable(true);
+}
+
+NativeButton::~NativeButton() {
+}
+
+void NativeButton::SetLabel(const std::wstring& label) {
+ label_ = label;
+
+ // Even though we create a flipped HWND for a native button when the locale
+ // is right-to-left, Windows does not render text for the button using a
+ // right-to-left context (perhaps because the parent HWND is not flipped).
+ // The result is that RTL strings containing punctuation marks are not
+ // displayed properly. For example, the string "...ABC" (where A, B and C are
+ // Hebrew characters) is displayed as "ABC..." which is incorrect.
+ //
+ // In order to overcome this problem, we mark the localized Hebrew strings as
+ // RTL strings explicitly (using the appropriate Unicode formatting) so that
+ // Windows displays the text correctly regardless of the HWND hierarchy.
+ std::wstring localized_label;
+ if (l10n_util::AdjustStringForLocaleDirection(label_, &localized_label))
+ label_ = localized_label;
+
+ if (native_wrapper_)
+ native_wrapper_->UpdateLabel();
+}
+
+void NativeButton::SetIsDefault(bool is_default) {
+ if (is_default == is_default_)
+ return;
+ if (is_default)
+ AddAccelerator(Accelerator(VK_RETURN, false, false, false));
+ else
+ RemoveAccelerator(Accelerator(VK_RETURN, false, false, false));
+ SetAppearsAsDefault(is_default);
+}
+
+void NativeButton::SetAppearsAsDefault(bool appears_as_default) {
+ is_default_ = appears_as_default;
+ if (native_wrapper_)
+ native_wrapper_->UpdateDefault();
+}
+
+void NativeButton::ButtonPressed() {
+ RequestFocus();
+
+ // TODO(beng): obtain mouse event flags for native buttons someday.
+ NotifyClick(mouse_event_flags());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButton, View overrides:
+
+gfx::Size NativeButton::GetPreferredSize() {
+ if (!native_wrapper_)
+ return gfx::Size();
+
+ gfx::Size sz = native_wrapper_->GetView()->GetPreferredSize();
+
+ // Add in the border size. (Do this before clamping the minimum size in case
+ // that clamping causes an increase in size that would include the borders.
+ gfx::Insets border = GetInsets();
+ sz.set_width(sz.width() + border.left() + border.right());
+ sz.set_height(sz.height() + border.top() + border.bottom());
+
+ // Clamp the size returned to at least the minimum size.
+ if (!ignore_minimum_size_) {
+ if (minimum_size_.width()) {
+ int min_width = font_.horizontal_dlus_to_pixels(minimum_size_.width());
+ sz.set_width(std::max(static_cast<int>(sz.width()), min_width));
+ }
+ if (minimum_size_.height()) {
+ int min_height = font_.vertical_dlus_to_pixels(minimum_size_.height());
+ sz.set_height(std::max(static_cast<int>(sz.height()), min_height));
+ }
+ }
+
+ return sz;
+}
+
+void NativeButton::Layout() {
+ if (native_wrapper_) {
+ native_wrapper_->GetView()->SetBounds(0, 0, width(), height());
+ native_wrapper_->GetView()->Layout();
+ }
+}
+
+void NativeButton::SetEnabled(bool flag) {
+ Button::SetEnabled(flag);
+ if (native_wrapper_)
+ native_wrapper_->UpdateEnabled();
+}
+
+void NativeButton::ViewHierarchyChanged(bool is_add, View* parent,
+ View* child) {
+ if (is_add && !native_wrapper_ && GetWidget()) {
+ CreateWrapper();
+ AddChildView(native_wrapper_->GetView());
+ }
+}
+
+std::string NativeButton::GetClassName() const {
+ return kViewClassName;
+}
+
+bool NativeButton::AcceleratorPressed(const Accelerator& accelerator) {
+ if (IsEnabled()) {
+ NotifyClick(mouse_event_flags());
+ return true;
+ }
+ return false;
+}
+
+void NativeButton::Focus() {
+ // Forward the focus to the wrapper.
+ if (native_wrapper_)
+ native_wrapper_->SetFocus();
+ else
+ Button::Focus(); // Will focus the RootView window (so we still get
+ // keyboard messages).
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButton, protected:
+
+void NativeButton::CreateWrapper() {
+ native_wrapper_ = NativeButtonWrapper::CreateNativeButtonWrapper(this);
+ native_wrapper_->UpdateLabel();
+ native_wrapper_->UpdateEnabled();
+}
+
+void NativeButton::InitBorder() {
+ set_border(Border::CreateEmptyBorder(0, kButtonBorderHWidth, 0,
+ kButtonBorderHWidth));
+}
+
+} // namespace views
diff --git a/views/controls/button/native_button.h b/views/controls/button/native_button.h
new file mode 100644
index 0000000..52946d7
--- /dev/null
+++ b/views/controls/button/native_button.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2009 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_CONTROLS_BUTTON_NATIVE_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_H_
+
+#include "app/gfx/chrome_font.h"
+#include "views/controls/button/button.h"
+#include "views/controls/button/native_button_wrapper.h"
+
+class ChromeFont;
+
+namespace views {
+
+class NativeButton : public Button {
+ public:
+ // The button's class name.
+ static const char kViewClassName[];
+
+ explicit NativeButton(ButtonListener* listener);
+ NativeButton(ButtonListener* listener, const std::wstring& label);
+ virtual ~NativeButton();
+
+ // Sets/Gets the text to be used as the button's label.
+ void SetLabel(const std::wstring& label);
+ std::wstring label() const { return label_; }
+
+ // Sets the font to be used when displaying the button's label.
+ void set_font(const ChromeFont& font) { font_ = font; }
+ const ChromeFont& font() const { return font_; }
+
+ // Sets/Gets whether or not the button appears and behaves as the default
+ // button in its current context.
+ void SetIsDefault(bool default_button);
+ bool is_default() const { return is_default_; }
+
+ // Sets whether or not the button appears as the default button. This does
+ // not make it behave as the default (i.e. no enter key accelerator is
+ // registered, use SetIsDefault for that).
+ void SetAppearsAsDefault(bool default_button);
+
+ void set_minimum_size(const gfx::Size& minimum_size) {
+ minimum_size_ = minimum_size;
+ }
+ void set_ignore_minimum_size(bool ignore_minimum_size) {
+ ignore_minimum_size_ = ignore_minimum_size;
+ }
+
+ // Called by the wrapper when the actual wrapped native button was pressed.
+ void ButtonPressed();
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void SetEnabled(bool flag);
+ virtual void Focus();
+
+ protected:
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+ virtual std::string GetClassName() const;
+ virtual bool AcceleratorPressed(const Accelerator& accelerator);
+
+ // Create the button wrapper. Can be overridden by subclass to create a
+ // wrapper of a particular type. See NativeButtonWrapper interface for types.
+ virtual void CreateWrapper();
+
+ // Sets a border to the button. Override to set a different border or to not
+ // set one (the default is 0,8,0,8 for push buttons).
+ virtual void InitBorder();
+
+ // The object that actually implements the native button.
+ NativeButtonWrapper* native_wrapper_;
+
+ private:
+ // The button label.
+ std::wstring label_;
+
+ // True if the button is the default button in its context.
+ bool is_default_;
+
+ // The font used to render the button label.
+ ChromeFont font_;
+
+ // True if the button should ignore the minimum size for the platform. Default
+ // is false. Set to true to create narrower buttons.
+ bool ignore_minimum_size_;
+
+ // The minimum size of the button from the specified size in native dialog
+ // units. The definition of this unit may vary from platform to platform. If
+ // the width/height is non-zero, the preferred size of the button will not be
+ // less than this value when the dialog units are converted to pixels.
+ gfx::Size minimum_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeButton);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_H_
diff --git a/views/controls/button/native_button_win.cc b/views/controls/button/native_button_win.cc
new file mode 100644
index 0000000..6748241
--- /dev/null
+++ b/views/controls/button/native_button_win.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "views/controls/button/native_button_win.h"
+
+#include "base/logging.h"
+#include "views/controls/button/checkbox.h"
+#include "views/controls/button/native_button.h"
+#include "views/controls/button/radio_button.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButtonWin, public:
+
+NativeButtonWin::NativeButtonWin(NativeButton* native_button)
+ : NativeControlWin(),
+ native_button_(native_button) {
+ // Associates the actual HWND with the native_button so the native_button is
+ // the one considered as having the focus (not the wrapper) when the HWND is
+ // focused directly (with a click for example).
+ SetAssociatedFocusView(native_button);
+}
+
+NativeButtonWin::~NativeButtonWin() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButtonWin, NativeButtonWrapper implementation:
+
+void NativeButtonWin::UpdateLabel() {
+ SetWindowText(GetHWND(), native_button_->label().c_str());
+}
+
+void NativeButtonWin::UpdateFont() {
+ SendMessage(GetHWND(), WM_SETFONT,
+ reinterpret_cast<WPARAM>(native_button_->font().hfont()),
+ FALSE);
+}
+
+void NativeButtonWin::UpdateEnabled() {
+ SetEnabled(native_button_->IsEnabled());
+}
+
+void NativeButtonWin::UpdateDefault() {
+ if (!IsCheckbox()) {
+ SendMessage(GetHWND(), BM_SETSTYLE,
+ native_button_->is_default() ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON,
+ true);
+ }
+}
+
+View* NativeButtonWin::GetView() {
+ return this;
+}
+
+void NativeButtonWin::SetFocus() {
+ // Focus the associated HWND.
+ Focus();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButtonWin, View overrides:
+
+gfx::Size NativeButtonWin::GetPreferredSize() {
+ SIZE sz = {0};
+ SendMessage(GetHWND(), BCM_GETIDEALSIZE, 0, reinterpret_cast<LPARAM>(&sz));
+
+ return gfx::Size(sz.cx, sz.cy);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButtonWin, NativeControlWin overrides:
+
+bool NativeButtonWin::ProcessMessage(UINT message, WPARAM w_param,
+ LPARAM l_param, LRESULT* result) {
+ if (message == WM_COMMAND && HIWORD(w_param) == BN_CLICKED) {
+ native_button_->ButtonPressed();
+ *result = 0;
+ return true;
+ }
+ return NativeControlWin::ProcessMessage(message, w_param, l_param, result);
+}
+
+bool NativeButtonWin::OnKeyDown(int vkey) {
+ bool enter_pressed = vkey == VK_RETURN;
+ if (enter_pressed)
+ native_button_->ButtonPressed();
+ return enter_pressed;
+}
+
+bool NativeButtonWin::NotifyOnKeyDown() const {
+ return true;
+}
+
+void NativeButtonWin::CreateNativeControl() {
+ DWORD flags = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_PUSHBUTTON;
+ if (native_button_->is_default())
+ flags |= BS_DEFPUSHBUTTON;
+ HWND control_hwnd = CreateWindowEx(GetAdditionalExStyle(), L"BUTTON", L"",
+ flags, 0, 0, width(), height(),
+ GetWidget()->GetNativeView(), NULL, NULL,
+ NULL);
+ NativeControlCreated(control_hwnd);
+}
+
+void NativeButtonWin::NativeControlCreated(HWND control_hwnd) {
+ NativeControlWin::NativeControlCreated(control_hwnd);
+
+ UpdateFont();
+ UpdateLabel();
+ UpdateDefault();
+}
+
+// We could obtain this from the theme, but that only works if themes are
+// active.
+static const int kCheckboxSize = 13; // pixels
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeCheckboxWin, public:
+
+NativeCheckboxWin::NativeCheckboxWin(Checkbox* checkbox)
+ : NativeButtonWin(checkbox),
+ checkbox_(checkbox) {
+}
+
+NativeCheckboxWin::~NativeCheckboxWin() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeCheckboxWin, View overrides:
+
+gfx::Size NativeCheckboxWin::GetPreferredSize() {
+ return gfx::Size(kCheckboxSize, kCheckboxSize);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeCheckboxWin, NativeButtonWrapper implementation:
+
+void NativeCheckboxWin::UpdateChecked() {
+ SendMessage(GetHWND(), BM_SETCHECK,
+ checkbox_->checked() ? BST_CHECKED : BST_UNCHECKED, 0);
+}
+
+void NativeCheckboxWin::SetPushed(bool pushed) {
+ SendMessage(GetHWND(), BM_SETSTATE, pushed, 0);
+}
+
+bool NativeCheckboxWin::OnKeyDown(int vkey) {
+ // Override the NativeButtonWin behavior which triggers the button on enter
+ // key presses when focused.
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeCheckboxWin, NativeButtonWin overrides:
+
+bool NativeCheckboxWin::ProcessMessage(UINT message, WPARAM w_param,
+ LPARAM l_param, LRESULT* result) {
+ if (message == WM_COMMAND && HIWORD(w_param) == BN_CLICKED) {
+ if (!IsRadioButton() || !checkbox_->checked())
+ checkbox_->SetChecked(!checkbox_->checked());
+ // Fall through to the NativeButtonWin's handler, which will send the
+ // clicked notification to the listener...
+ }
+ return NativeButtonWin::ProcessMessage(message, w_param, l_param, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeCheckboxWin, protected:
+
+void NativeCheckboxWin::CreateNativeControl() {
+ HWND control_hwnd = CreateWindowEx(
+ WS_EX_TRANSPARENT | GetAdditionalExStyle(), L"BUTTON", L"",
+ WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_CHECKBOX,
+ 0, 0, width(), height(), GetWidget()->GetNativeView(), NULL, NULL, NULL);
+ NativeControlCreated(control_hwnd);
+}
+
+void NativeCheckboxWin::NativeControlCreated(HWND control_hwnd) {
+ NativeButtonWin::NativeControlCreated(control_hwnd);
+ UpdateChecked();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeRadioButtonWin, public:
+
+NativeRadioButtonWin::NativeRadioButtonWin(RadioButton* radio_button)
+ : NativeCheckboxWin(radio_button) {
+}
+
+NativeRadioButtonWin::~NativeRadioButtonWin() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeRadioButtonWin, NativeCheckboxWin overrides:
+
+void NativeRadioButtonWin::CreateNativeControl() {
+ HWND control_hwnd = CreateWindowEx(
+ GetAdditionalExStyle(), L"BUTTON",
+ L"", WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_RADIOBUTTON,
+ 0, 0, width(), height(), GetWidget()->GetNativeView(), NULL, NULL, NULL);
+ NativeControlCreated(control_hwnd);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButtonWrapper, public:
+
+// static
+int NativeButtonWrapper::GetFixedWidth() {
+ return kCheckboxSize;
+}
+
+// static
+NativeButtonWrapper* NativeButtonWrapper::CreateNativeButtonWrapper(
+ NativeButton* native_button) {
+ return new NativeButtonWin(native_button);
+}
+
+// static
+NativeButtonWrapper* NativeButtonWrapper::CreateCheckboxWrapper(
+ Checkbox* checkbox) {
+ return new NativeCheckboxWin(checkbox);
+}
+
+// static
+NativeButtonWrapper* NativeButtonWrapper::CreateRadioButtonWrapper(
+ RadioButton* radio_button) {
+ return new NativeRadioButtonWin(radio_button);
+}
+
+} // namespace views
diff --git a/views/controls/button/native_button_win.h b/views/controls/button/native_button_win.h
new file mode 100644
index 0000000..3f5141a
--- /dev/null
+++ b/views/controls/button/native_button_win.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 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_CONTROLS_BUTTON_NATIVE_BUTTON_WIN_H_
+#define VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WIN_H_
+
+#include "views/controls/native_control_win.h"
+#include "views/controls/button/native_button_wrapper.h"
+
+namespace views {
+
+// A View that hosts a native Windows button.
+class NativeButtonWin : public NativeControlWin,
+ public NativeButtonWrapper {
+ public:
+ explicit NativeButtonWin(NativeButton* native_button);
+ virtual ~NativeButtonWin();
+
+ // Overridden from NativeButtonWrapper:
+ virtual void UpdateLabel();
+ virtual void UpdateFont();
+ virtual void UpdateEnabled();
+ virtual void UpdateDefault();
+ virtual View* GetView();
+ virtual void SetFocus();
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+
+ // Overridden from NativeControlWin:
+ virtual bool ProcessMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* result);
+ virtual bool OnKeyDown(int vkey);
+
+ protected:
+ virtual bool NotifyOnKeyDown() const;
+ virtual void CreateNativeControl();
+ virtual void NativeControlCreated(HWND control_hwnd);
+
+ // Returns true if this button is actually a checkbox or radio button.
+ virtual bool IsCheckbox() const { return false; }
+
+ private:
+ // The NativeButton we are bound to.
+ NativeButton* native_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeButtonWin);
+};
+
+// A View that hosts a native Windows checkbox.
+class NativeCheckboxWin : public NativeButtonWin {
+ public:
+ explicit NativeCheckboxWin(Checkbox* native_button);
+ virtual ~NativeCheckboxWin();
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+
+ // Overridden from NativeButtonWrapper:
+ virtual void UpdateChecked();
+ virtual void SetPushed(bool pushed);
+ virtual bool OnKeyDown(int vkey);
+
+ // Overridden from NativeControlWin:
+ virtual bool ProcessMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* result);
+
+ protected:
+ virtual void CreateNativeControl();
+ virtual void NativeControlCreated(HWND control_hwnd);
+ virtual bool IsCheckbox() const { return true; }
+
+ // Returns true if this button is actually a radio button.
+ virtual bool IsRadioButton() const { return false; }
+
+ private:
+ // The Checkbox we are bound to.
+ Checkbox* checkbox_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeCheckboxWin);
+};
+
+// A View that hosts a native Windows radio button.
+class NativeRadioButtonWin : public NativeCheckboxWin {
+ public:
+ explicit NativeRadioButtonWin(RadioButton* radio_button);
+ virtual ~NativeRadioButtonWin();
+
+ protected:
+ // Overridden from NativeCheckboxWin:
+ virtual void CreateNativeControl();
+ virtual bool IsRadioButton() const { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeRadioButtonWin);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WIN_H_
diff --git a/views/controls/button/native_button_wrapper.h b/views/controls/button/native_button_wrapper.h
new file mode 100644
index 0000000..5535ed4
--- /dev/null
+++ b/views/controls/button/native_button_wrapper.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2009 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_CONTROLS_BUTTON_NATIVE_BUTTON_WRAPPER_H_
+#define VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WRAPPER_H_
+
+class ChromeFont;
+
+namespace views {
+
+class Checkbox;
+class NativeButton;
+class RadioButton;
+
+// A specialization of NativeControlWrapper that hosts a platform-native button.
+class NativeButtonWrapper {
+ public:
+ // Updates the native button's label from the state stored in its associated
+ // NativeButton.
+ virtual void UpdateLabel() = 0;
+
+ // Updates the native button's label font from the state stored in its
+ // associated NativeButton.
+ virtual void UpdateFont() = 0;
+
+ // Updates the native button's enabled state from the state stored in its
+ // associated NativeButton.
+ virtual void UpdateEnabled() = 0;
+
+ // Updates the native button's default state from the state stored in its
+ // associated NativeButton.
+ virtual void UpdateDefault() = 0;
+
+ // Updates the native button's checked state from the state stored in its
+ // associated NativeCheckbox. Valid only for checkboxes and radio buttons.
+ virtual void UpdateChecked() {}
+
+ // Shows the pushed state for the button if |pushed| is true.
+ virtual void SetPushed(bool pushed) {};
+
+ // Retrieves the views::View that hosts the native control.
+ virtual View* GetView() = 0;
+
+ // Sets the focus to the button.
+ virtual void SetFocus() = 0;
+
+ // Return the width of the button. Used for fixed size buttons (checkboxes and
+ // radio buttons) only.
+ static int GetFixedWidth();
+
+ // Creates an appropriate NativeButtonWrapper for the platform.
+ static NativeButtonWrapper* CreateNativeButtonWrapper(NativeButton* button);
+ static NativeButtonWrapper* CreateCheckboxWrapper(Checkbox* checkbox);
+ static NativeButtonWrapper* CreateRadioButtonWrapper(
+ RadioButton* radio_button);
+
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WRAPPER_H_
diff --git a/views/controls/button/radio_button.cc b/views/controls/button/radio_button.cc
new file mode 100644
index 0000000..3f4820d
--- /dev/null
+++ b/views/controls/button/radio_button.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "views/controls/button/radio_button.h"
+
+#include "views/widget/root_view.h"
+
+namespace views {
+
+// static
+const char RadioButton::kViewClassName[] = "views/RadioButton";
+
+////////////////////////////////////////////////////////////////////////////////
+// RadioButton, public:
+
+RadioButton::RadioButton() : Checkbox() {
+}
+
+RadioButton::RadioButton(const std::wstring& label) : Checkbox(label) {
+}
+
+RadioButton::RadioButton(const std::wstring& label, int group_id)
+ : Checkbox(label) {
+ SetGroup(group_id);
+}
+
+RadioButton::~RadioButton() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RadioButton, Checkbox overrides:
+
+void RadioButton::SetChecked(bool checked) {
+ if (checked == RadioButton::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 = GetParent();
+ while (container && container->GetParent())
+ container = container->GetParent();
+ 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) {
+ RadioButton* peer = static_cast<RadioButton*>(*i);
+ peer->SetChecked(false);
+ }
+ }
+ }
+ }
+ Checkbox::SetChecked(checked);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RadioButton, View overrides:
+
+View* RadioButton::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) {
+ RadioButton* radio_button = static_cast<RadioButton*>(*iter);
+ if (radio_button->checked())
+ return radio_button;
+ }
+ return NULL;
+}
+
+bool RadioButton::IsGroupFocusTraversable() const {
+ // When focusing a radio button with tab/shift+tab, only the selected button
+ // from the group should be focused.
+ return false;
+}
+
+void RadioButton::OnMouseReleased(const MouseEvent& event, bool canceled) {
+ native_wrapper_->SetPushed(false);
+ // 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() && !canceled && HitTestLabel(event))
+ SetChecked(true);
+
+ ButtonPressed();
+}
+
+std::string RadioButton::GetClassName() const {
+ return kViewClassName;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RadioButton, NativeButton overrides:
+
+void RadioButton::CreateWrapper() {
+ native_wrapper_ = NativeButtonWrapper::CreateRadioButtonWrapper(this);
+ native_wrapper_->UpdateLabel();
+ native_wrapper_->UpdateChecked();
+}
+
+} // namespace views
diff --git a/views/controls/button/radio_button.h b/views/controls/button/radio_button.h
new file mode 100644
index 0000000..ab7fbe2
--- /dev/null
+++ b/views/controls/button/radio_button.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 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_CONTROLS_BUTTON_RADIO_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_RADIO_BUTTON_H_
+
+#include "views/controls/button/checkbox.h"
+
+namespace views {
+
+// A Checkbox subclass representing a radio button.
+class RadioButton : public Checkbox {
+ public:
+ // The button's class name.
+ static const char kViewClassName[];
+
+ RadioButton();
+ RadioButton(const std::wstring& label);
+ RadioButton(const std::wstring& label, int group_id);
+ virtual ~RadioButton();
+
+ // Overridden from Checkbox:
+ virtual void SetChecked(bool checked);
+
+ // Overridden from View:
+ virtual View* GetSelectedViewForGroup(int group_id);
+ virtual bool IsGroupFocusTraversable() const;
+ virtual void OnMouseReleased(const MouseEvent& event, bool canceled);
+
+ protected:
+ virtual std::string GetClassName() const;
+
+ // Overridden from NativeButton:
+ virtual void CreateWrapper();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RadioButton);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_BUTTON_RADIO_BUTTON_H_
diff --git a/views/controls/button/text_button.cc b/views/controls/button/text_button.cc
new file mode 100644
index 0000000..4f68b90
--- /dev/null
+++ b/views/controls/button/text_button.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/controls/button/text_button.h"
+
+#include "app/gfx/chrome_canvas.h"
+#include "app/l10n_util.h"
+#include "app/throb_animation.h"
+#include "app/resource_bundle.h"
+#include "views/controls/button/button.h"
+#include "views/event.h"
+#include "grit/theme_resources.h"
+
+namespace views {
+
+// Padding between the icon and text.
+static const int kIconTextPadding = 5;
+
+// Preferred padding between text and edge
+static const int kPreferredPaddingHorizontal = 6;
+static const int kPreferredPaddingVertical = 5;
+
+static const SkColor kEnabledColor = SkColorSetRGB(6, 45, 117);
+static const SkColor kHighlightColor = SkColorSetARGB(200, 255, 255, 255);
+static const SkColor kDisabledColor = SkColorSetRGB(161, 161, 146);
+
+// How long the hover fade animation should last.
+static const int kHoverAnimationDurationMs = 170;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButtonBorder - constructors, destructors, initialization
+//
+////////////////////////////////////////////////////////////////////////////////
+
+TextButtonBorder::TextButtonBorder() {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ hot_set_.top_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_H);
+ hot_set_.top = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_H);
+ hot_set_.top_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_H);
+ hot_set_.left = rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_H);
+ hot_set_.center = rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_H);
+ hot_set_.right = rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_H);
+ hot_set_.bottom_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_H);
+ hot_set_.bottom = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_H);
+ hot_set_.bottom_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_H);
+
+ pushed_set_.top_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_P);
+ pushed_set_.top = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_P);
+ pushed_set_.top_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_P);
+ pushed_set_.left = rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_P);
+ pushed_set_.center = rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_P);
+ pushed_set_.right = rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_P);
+ pushed_set_.bottom_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_P);
+ pushed_set_.bottom = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_P);
+ pushed_set_.bottom_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_P);
+}
+
+TextButtonBorder::~TextButtonBorder() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButtonBackground - painting
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void TextButtonBorder::Paint(const View& view, ChromeCanvas* canvas) const {
+ const TextButton* mb = static_cast<const TextButton*>(&view);
+ int state = mb->state();
+
+ // TextButton takes care of deciding when to call Paint.
+ const MBBImageSet* set = &hot_set_;
+ if (state == TextButton::BS_PUSHED)
+ set = &pushed_set_;
+
+ if (set) {
+ gfx::Rect bounds = view.bounds();
+
+ // Draw the top left image
+ canvas->DrawBitmapInt(*set->top_left, 0, 0);
+
+ // Tile the top image
+ canvas->TileImageInt(
+ *set->top,
+ set->top_left->width(),
+ 0,
+ bounds.width() - set->top_right->width() - set->top_left->width(),
+ set->top->height());
+
+ // Draw the top right image
+ canvas->DrawBitmapInt(*set->top_right,
+ bounds.width() - set->top_right->width(), 0);
+
+ // Tile the left image
+ canvas->TileImageInt(
+ *set->left,
+ 0,
+ set->top_left->height(),
+ set->top_left->width(),
+ bounds.height() - set->top->height() - set->bottom_left->height());
+
+ // Tile the center image
+ canvas->TileImageInt(
+ *set->center,
+ set->left->width(),
+ set->top->height(),
+ bounds.width() - set->right->width() - set->left->width(),
+ bounds.height() - set->bottom->height() - set->top->height());
+
+ // Tile the right image
+ canvas->TileImageInt(
+ *set->right,
+ bounds.width() - set->right->width(),
+ set->top_right->height(),
+ bounds.width(),
+ bounds.height() - set->bottom_right->height() -
+ set->top_right->height());
+
+ // Draw the bottom left image
+ canvas->DrawBitmapInt(*set->bottom_left,
+ 0,
+ bounds.height() - set->bottom_left->height());
+
+ // Tile the bottom image
+ canvas->TileImageInt(
+ *set->bottom,
+ set->bottom_left->width(),
+ bounds.height() - set->bottom->height(),
+ bounds.width() - set->bottom_right->width() -
+ set->bottom_left->width(),
+ set->bottom->height());
+
+ // Draw the bottom right image
+ canvas->DrawBitmapInt(*set->bottom_right,
+ bounds.width() - set->bottom_right->width(),
+ bounds.height() - set->bottom_right->height());
+ } else {
+ // Do nothing
+ }
+}
+
+void TextButtonBorder::GetInsets(gfx::Insets* insets) const {
+ insets->Set(kPreferredPaddingVertical, kPreferredPaddingHorizontal,
+ kPreferredPaddingVertical, kPreferredPaddingHorizontal);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextButton, public:
+
+TextButton::TextButton(ButtonListener* listener, const std::wstring& text)
+ : CustomButton(listener),
+ alignment_(ALIGN_LEFT),
+ font_(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont)),
+ color_(kEnabledColor),
+ max_width_(0) {
+ SetText(text);
+ set_border(new TextButtonBorder);
+ SetAnimationDuration(kHoverAnimationDurationMs);
+}
+
+TextButton::~TextButton() {
+}
+
+void TextButton::SetText(const std::wstring& text) {
+ text_ = text;
+ // Update our new current and max text size
+ text_size_.SetSize(font_.GetStringWidth(text_), font_.height());
+ max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()),
+ std::max(max_text_size_.height(),
+ text_size_.height()));
+}
+
+void TextButton::SetIcon(const SkBitmap& icon) {
+ icon_ = icon;
+}
+
+void TextButton::ClearMaxTextSize() {
+ max_text_size_ = text_size_;
+}
+
+void TextButton::Paint(ChromeCanvas* canvas, bool for_drag) {
+ if (!for_drag) {
+ PaintBackground(canvas);
+
+ if (hover_animation_->IsAnimating()) {
+ // Draw the hover bitmap into an offscreen buffer, then blend it
+ // back into the current canvas.
+ canvas->saveLayerAlpha(NULL,
+ static_cast<int>(hover_animation_->GetCurrentValue() * 255),
+ SkCanvas::kARGB_NoClipLayer_SaveFlag);
+ canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
+ PaintBorder(canvas);
+ canvas->restore();
+ } else if (state_ == BS_HOT || state_ == BS_PUSHED) {
+ PaintBorder(canvas);
+ }
+
+ PaintFocusBorder(canvas);
+ }
+
+ 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 += kIconTextPadding;
+ }
+ // 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() + kIconTextPadding;
+ const int text_width = std::min(text_size_.width(),
+ width() - insets.right() - text_x);
+ int text_y = (available_height - text_size_.height()) / 2 + insets.top();
+
+ if (text_width > 0) {
+ // Because the text button can (at times) draw multiple elements on the
+ // canvas, we can not mirror the button by simply flipping the canvas as
+ // doing this will mirror the text itself. Flipping the canvas will also
+ // make the icons look wrong because icons are almost always represented as
+ // direction insentisive bitmaps and such bitmaps should never be flipped
+ // 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(MirroredLeftPointForRect(text_bounds));
+
+ if (for_drag) {
+#if defined(OS_WIN)
+ // TODO(erg): Either port DrawStringWithHalo to linux or find an
+ // alternative here.
+ canvas->DrawStringWithHalo(text_, font_, color_, kHighlightColor,
+ text_bounds.x(),
+ text_bounds.y(),
+ text_bounds.width(),
+ text_bounds.height(),
+ l10n_util::DefaultCanvasTextAlignment());
+#endif
+ } else {
+ // Draw bevel highlight
+ canvas->DrawStringInt(text_,
+ font_,
+ kHighlightColor,
+ text_bounds.x() + 1,
+ text_bounds.y() + 1,
+ text_bounds.width(),
+ text_bounds.height());
+
+ canvas->DrawStringInt(text_,
+ font_,
+ color_,
+ text_bounds.x(),
+ text_bounds.y(),
+ text_bounds.width(),
+ text_bounds.height());
+ }
+ }
+
+ if (icon_.width() > 0) {
+ 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(MirroredLeftPointForRect(icon_bounds));
+ canvas->DrawBitmapInt(icon_, icon_bounds.x(), icon_bounds.y());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextButton, View overrides:
+
+gfx::Size TextButton::GetPreferredSize() {
+ gfx::Insets insets = GetInsets();
+
+ // 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(kIconTextPadding, 0);
+
+ if (max_width_ > 0)
+ prefsize.set_width(std::min(max_width_, prefsize.width()));
+
+ return prefsize;
+}
+
+gfx::Size TextButton::GetMinimumSize() {
+ return max_text_size_;
+}
+
+void TextButton::SetEnabled(bool enabled) {
+ if (enabled == IsEnabled())
+ return;
+ CustomButton::SetEnabled(enabled);
+ color_ = enabled ? kEnabledColor : kDisabledColor;
+ SchedulePaint();
+}
+
+bool TextButton::OnMousePressed(const MouseEvent& e) {
+ return true;
+}
+
+void TextButton::Paint(ChromeCanvas* canvas) {
+ Paint(canvas, false);
+}
+
+} // namespace views
diff --git a/views/controls/button/text_button.h b/views/controls/button/text_button.h
new file mode 100644
index 0000000..16bac57
--- /dev/null
+++ b/views/controls/button/text_button.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2006-2008 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_CONTROLS_BUTTON_TEXT_BUTTON_H_
+#define VIEWS_CONTROLS_BUTTON_TEXT_BUTTON_H_
+
+#include "app/gfx/chrome_font.h"
+#include "skia/include/SkBitmap.h"
+#include "views/border.h"
+#include "views/controls/button/custom_button.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButtonBorder
+//
+// A Border subclass that paints a TextButton's background layer -
+// basically the button frame in the hot/pushed states.
+//
+////////////////////////////////////////////////////////////////////////////////
+class TextButtonBorder : public Border {
+ public:
+ TextButtonBorder();
+ virtual ~TextButtonBorder();
+
+ // Render the background for the provided view
+ virtual void Paint(const View& view, ChromeCanvas* canvas) const;
+
+ // Returns the insets for the border.
+ virtual void GetInsets(gfx::Insets* insets) const;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TextButtonBorder);
+
+ // Images
+ struct MBBImageSet {
+ SkBitmap* top_left;
+ SkBitmap* top;
+ SkBitmap* top_right;
+ SkBitmap* left;
+ SkBitmap* center;
+ SkBitmap* right;
+ SkBitmap* bottom_left;
+ SkBitmap* bottom;
+ SkBitmap* bottom_right;
+ };
+ MBBImageSet hot_set_;
+ MBBImageSet pushed_set_;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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 CustomButton {
+ public:
+ TextButton(ButtonListener* listener, const std::wstring& text);
+ virtual ~TextButton();
+
+ // 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
+ // resizing the button when the text changes.
+ virtual void SetText(const std::wstring& text);
+ std::wstring text() const { return text_; }
+
+ enum TextAlignment {
+ ALIGN_LEFT,
+ ALIGN_CENTER,
+ ALIGN_RIGHT
+ };
+
+ void set_alignment(TextAlignment alignment) { alignment_ = alignment; }
+
+ // Sets the icon.
+ void SetIcon(const SkBitmap& icon);
+ SkBitmap icon() const { return icon_; }
+
+ // TextButton remembers the maximum display size of the text passed to
+ // SetText. This method resets the cached maximum display size to the
+ // current size.
+ void ClearMaxTextSize();
+
+ void set_max_width(int max_width) { max_width_ = max_width; }
+
+ // Paint the button into the specified canvas. If |for_drag| is true, the
+ // function paints a drag image representation into the canvas.
+ virtual void Paint(ChromeCanvas* canvas, bool for_drag);
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual gfx::Size GetMinimumSize();
+ virtual void SetEnabled(bool enabled);
+
+ protected:
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual void Paint(ChromeCanvas* canvas);
+
+ private:
+ // The text string that is displayed in the button.
+ std::wstring text_;
+
+ // The size of the text string.
+ gfx::Size text_size_;
+
+ // Track the size of the largest text string seen so far, so that
+ // changing text_ will not resize the button boundary.
+ gfx::Size max_text_size_;
+
+ // The alignment of the text string within the button.
+ TextAlignment alignment_;
+
+ // The font used to paint the text.
+ ChromeFont font_;
+
+ // Text color.
+ SkColor color_;
+
+ // An icon displayed with the text.
+ SkBitmap 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_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TextButton);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_BUTTON_TEXT_BUTTON_H_