1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ 6 #define UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ 7 8 #include <memory> 9 10 #include "base/bind.h" 11 #include "base/macros.h" 12 #include "build/build_config.h" 13 #include "ui/events/event_constants.h" 14 #include "ui/gfx/animation/throb_animation.h" 15 #include "ui/native_theme/native_theme.h" 16 #include "ui/views/animation/animation_delegate_views.h" 17 #include "ui/views/animation/ink_drop_host_view.h" 18 #include "ui/views/animation/ink_drop_state.h" 19 #include "ui/views/controls/button/button_controller_delegate.h" 20 #include "ui/views/controls/focus_ring.h" 21 #include "ui/views/painter.h" 22 #include "ui/views/widget/widget_observer.h" 23 24 namespace views { 25 namespace test { 26 class ButtonTestApi; 27 } 28 29 class Button; 30 class ButtonController; 31 class ButtonObserver; 32 class Event; 33 34 // An interface implemented by an object to let it know that a button was 35 // pressed. 36 class VIEWS_EXPORT ButtonListener { 37 public: 38 virtual void ButtonPressed(Button* sender, const ui::Event& event) = 0; 39 40 protected: 41 virtual ~ButtonListener() = default; 42 }; 43 44 // A View representing a button. A Button is not focusable by default and will 45 // not be part of the focus chain, unless in accessibility mode (see 46 // SetFocusForPlatform()). 47 class VIEWS_EXPORT Button : public InkDropHostView, 48 public AnimationDelegateViews { 49 public: 50 METADATA_HEADER(Button); 51 52 ~Button() override; 53 54 // Button states for various button sub-types. 55 enum ButtonState { 56 STATE_NORMAL = 0, 57 STATE_HOVERED, 58 STATE_PRESSED, 59 STATE_DISABLED, 60 STATE_COUNT, 61 }; 62 63 static constexpr ButtonState kButtonStates[STATE_COUNT] = { 64 ButtonState::STATE_NORMAL, ButtonState::STATE_HOVERED, 65 ButtonState::STATE_PRESSED, ButtonState::STATE_DISABLED}; 66 67 // An enum describing the events on which a button should be clicked for a 68 // given key event. 69 enum class KeyClickAction { 70 kOnKeyPress, 71 kOnKeyRelease, 72 kNone, 73 }; 74 75 // TODO(cyan): Consider having Button implement ButtonControllerDelegate. 76 class VIEWS_EXPORT DefaultButtonControllerDelegate 77 : public ButtonControllerDelegate { 78 public: 79 explicit DefaultButtonControllerDelegate(Button* button); 80 ~DefaultButtonControllerDelegate() override; 81 82 // views::ButtonControllerDelegate: 83 void RequestFocusFromEvent() override; 84 void NotifyClick(const ui::Event& event) override; 85 void OnClickCanceled(const ui::Event& event) override; 86 bool IsTriggerableEvent(const ui::Event& event) override; 87 bool ShouldEnterPushedState(const ui::Event& event) override; 88 bool ShouldEnterHoveredState() override; 89 InkDrop* GetInkDrop() override; 90 int GetDragOperations(const gfx::Point& press_pt) override; 91 bool InDrag() override; 92 93 private: 94 DISALLOW_COPY_AND_ASSIGN(DefaultButtonControllerDelegate); 95 }; 96 97 static const Button* AsButton(const View* view); 98 static Button* AsButton(View* view); 99 100 static ButtonState GetButtonStateFrom(ui::NativeTheme::State state); 101 102 // Make the button focusable as per the platform. 103 void SetFocusForPlatform(); 104 105 void SetTooltipText(const base::string16& tooltip_text); 106 tag()107 int tag() const { return tag_; } set_tag(int tag)108 void set_tag(int tag) { tag_ = tag; } 109 110 void SetAccessibleName(const base::string16& name); 111 const base::string16& GetAccessibleName() const; 112 113 // Get/sets the current display state of the button. state()114 ButtonState state() const { return state_; } 115 // Clients passing in STATE_DISABLED should consider calling 116 // SetEnabled(false) instead because the enabled flag can affect other things 117 // like event dispatching, focus traversals, etc. Calling SetEnabled(false) 118 // will also set the state of |this| to STATE_DISABLED. 119 void SetState(ButtonState state); 120 // Returns the visual appearance state of the button. This takes into account 121 // both the button's display state and the state of the containing widget. 122 ButtonState GetVisualState() const; 123 124 // Starts throbbing. See HoverAnimation for a description of cycles_til_stop. 125 // This method does nothing if |animate_on_state_change_| is false. 126 void StartThrobbing(int cycles_til_stop); 127 128 // Stops throbbing immediately. 129 void StopThrobbing(); 130 131 // Set how long the hover animation will last for. 132 void SetAnimationDuration(base::TimeDelta duration); 133 set_triggerable_event_flags(int triggerable_event_flags)134 void set_triggerable_event_flags(int triggerable_event_flags) { 135 triggerable_event_flags_ = triggerable_event_flags; 136 } triggerable_event_flags()137 int triggerable_event_flags() const { return triggerable_event_flags_; } 138 139 // Sets whether |RequestFocus| should be invoked on a mouse press. The default 140 // is false. set_request_focus_on_press(bool value)141 void set_request_focus_on_press(bool value) { 142 // On Mac, buttons should not request focus on a mouse press. Hence keep the 143 // default value i.e. false. 144 #if !defined(OS_MACOSX) 145 request_focus_on_press_ = value; 146 #endif 147 } 148 request_focus_on_press()149 bool request_focus_on_press() const { return request_focus_on_press_; } 150 151 // See description above field. set_animate_on_state_change(bool value)152 void set_animate_on_state_change(bool value) { 153 animate_on_state_change_ = value; 154 } 155 hide_ink_drop_when_showing_context_menu()156 bool hide_ink_drop_when_showing_context_menu() const { 157 return hide_ink_drop_when_showing_context_menu_; 158 } set_hide_ink_drop_when_showing_context_menu(bool hide_ink_drop_when_showing_context_menu)159 void set_hide_ink_drop_when_showing_context_menu( 160 bool hide_ink_drop_when_showing_context_menu) { 161 hide_ink_drop_when_showing_context_menu_ = 162 hide_ink_drop_when_showing_context_menu; 163 } 164 set_show_ink_drop_when_hot_tracked(bool show_ink_drop_when_hot_tracked)165 void set_show_ink_drop_when_hot_tracked(bool show_ink_drop_when_hot_tracked) { 166 show_ink_drop_when_hot_tracked_ = show_ink_drop_when_hot_tracked; 167 } 168 set_ink_drop_base_color(SkColor color)169 void set_ink_drop_base_color(SkColor color) { ink_drop_base_color_ = color; } set_has_ink_drop_action_on_click(bool has_ink_drop_action_on_click)170 void set_has_ink_drop_action_on_click(bool has_ink_drop_action_on_click) { 171 has_ink_drop_action_on_click_ = has_ink_drop_action_on_click; 172 } 173 void SetInstallFocusRingOnFocus(bool install_focus_ring_on_focus); 174 175 void SetHotTracked(bool is_hot_tracked); 176 bool IsHotTracked() const; 177 178 void SetFocusPainter(std::unique_ptr<Painter> focus_painter); 179 180 // Highlights the ink drop for the button. 181 void SetHighlighted(bool bubble_visible); 182 183 void AddButtonObserver(ButtonObserver* observer); 184 void RemoveButtonObserver(ButtonObserver* observer); 185 186 // Overridden from View: 187 bool OnMousePressed(const ui::MouseEvent& event) override; 188 bool OnMouseDragged(const ui::MouseEvent& event) override; 189 void OnMouseReleased(const ui::MouseEvent& event) override; 190 void OnMouseCaptureLost() override; 191 void OnMouseEntered(const ui::MouseEvent& event) override; 192 void OnMouseExited(const ui::MouseEvent& event) override; 193 void OnMouseMoved(const ui::MouseEvent& event) override; 194 bool OnKeyPressed(const ui::KeyEvent& event) override; 195 bool OnKeyReleased(const ui::KeyEvent& event) override; 196 void OnGestureEvent(ui::GestureEvent* event) override; 197 bool AcceleratorPressed(const ui::Accelerator& accelerator) override; 198 bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override; 199 base::string16 GetTooltipText(const gfx::Point& p) const override; 200 void ShowContextMenu(const gfx::Point& p, 201 ui::MenuSourceType source_type) override; 202 void OnDragDone() override; 203 // Instead of overriding this, subclasses that want custom painting should use 204 // PaintButtonContents. 205 void OnPaint(gfx::Canvas* canvas) final; 206 void GetAccessibleNodeData(ui::AXNodeData* node_data) override; 207 void VisibilityChanged(View* starting_from, bool is_visible) override; 208 void ViewHierarchyChanged( 209 const ViewHierarchyChangedDetails& details) override; 210 void OnFocus() override; 211 void OnBlur() override; 212 void AddedToWidget() override; 213 void RemovedFromWidget() override; 214 215 // Overridden from InkDropHostView: 216 std::unique_ptr<InkDrop> CreateInkDrop() override; 217 SkColor GetInkDropBaseColor() const override; 218 219 // Overridden from views::AnimationDelegateViews: 220 void AnimationProgressed(const gfx::Animation* animation) override; 221 222 // Returns the click action for the given key event. 223 // Subclasses may override this method to support default actions for key 224 // events. 225 // TODO(cyan): Move this into the ButtonController. 226 virtual KeyClickAction GetKeyClickActionForEvent(const ui::KeyEvent& event); 227 button_controller()228 ButtonController* button_controller() const { 229 return button_controller_.get(); 230 } 231 232 void SetButtonController(std::unique_ptr<ButtonController> button_controller); 233 234 gfx::Point GetMenuPosition() const; 235 236 protected: 237 // Construct the Button with a Listener. The listener can be null. This can be 238 // true of buttons that don't have a listener - e.g. menubuttons where there's 239 // no default action and checkboxes. 240 explicit Button(ButtonListener* listener); 241 242 // Called when the button has been clicked or tapped and should request focus 243 // if necessary. 244 virtual void RequestFocusFromEvent(); 245 246 // Cause the button to notify the listener that a click occurred. 247 virtual void NotifyClick(const ui::Event& event); 248 249 // Called when a button gets released without triggering an action. 250 // Note: This is only wired up for mouse button events and not gesture 251 // events. 252 virtual void OnClickCanceled(const ui::Event& event); 253 254 // Called when the tooltip is set. 255 virtual void OnSetTooltipText(const base::string16& tooltip_text); 256 257 // Invoked from SetState() when SetState() is passed a value that differs from 258 // the current node_data. Button's implementation of StateChanged() does 259 // nothing; this method is provided for subclasses that wish to do something 260 // on state changes. 261 virtual void StateChanged(ButtonState old_state); 262 263 // Returns true if the event is one that can trigger notifying the listener. 264 // This implementation returns true if the left mouse button is down. 265 // TODO(cyan): Remove this method and move the implementation into 266 // ButtonController. 267 virtual bool IsTriggerableEvent(const ui::Event& event); 268 269 // Returns true if the ink drop should be updated by Button when 270 // OnClickCanceled() is called. This method is provided for subclasses. 271 // If the method is overriden and returns false, the subclass is responsible 272 // will be responsible for updating the ink drop. 273 virtual bool ShouldUpdateInkDropOnClickCanceled() const; 274 275 // Returns true if the button should become pressed when the user 276 // holds the mouse down over the button. For this implementation, 277 // we simply return IsTriggerableEvent(event). 278 virtual bool ShouldEnterPushedState(const ui::Event& event); 279 280 // Override to paint custom button contents. Any background or border set on 281 // the view will be painted before this is called and |focus_painter_| will be 282 // painted afterwards. 283 virtual void PaintButtonContents(gfx::Canvas* canvas); 284 285 // Returns true if the button should enter hovered state; that is, if the 286 // mouse is over the button, and no other window has capture (which would 287 // prevent the button from receiving MouseExited events and updating its 288 // node_data). This does not take into account enabled node_data. 289 bool ShouldEnterHoveredState(); 290 hover_animation()291 const gfx::ThrobAnimation& hover_animation() const { 292 return hover_animation_; 293 } 294 focus_ring()295 FocusRing* focus_ring() { return focus_ring_.get(); } 296 297 // The button's listener. Notified when clicked. 298 ButtonListener* listener_; 299 300 private: 301 friend class test::ButtonTestApi; 302 FRIEND_TEST_ALL_PREFIXES(BlueButtonTest, Border); 303 304 // Bridge class to allow Button to observe a Widget without being a 305 // WidgetObserver. This is desirable because many Button subclasses are 306 // themselves WidgetObservers, and if Button is a WidgetObserver, any change 307 // to its WidgetObserver overrides requires updating all the subclasses as 308 // well. 309 class WidgetObserverButtonBridge : public WidgetObserver { 310 public: 311 explicit WidgetObserverButtonBridge(Button* owner); 312 ~WidgetObserverButtonBridge() override; 313 314 // WidgetObserver: 315 void OnWidgetPaintAsActiveChanged(Widget* widget, 316 bool paint_as_active) override; 317 void OnWidgetDestroying(Widget* widget) override; 318 319 private: 320 Button* owner_; 321 322 DISALLOW_COPY_AND_ASSIGN(WidgetObserverButtonBridge); 323 }; 324 325 void OnEnabledChanged(); 326 327 void WidgetPaintAsActiveChanged(Widget* widget, bool active); 328 329 // The text shown in a tooltip. 330 base::string16 tooltip_text_; 331 332 // Accessibility data. 333 base::string16 accessible_name_; 334 335 // The id tag associated with this button. Used to disambiguate buttons in 336 // the ButtonListener implementation. 337 int tag_ = -1; 338 339 ButtonState state_ = STATE_NORMAL; 340 341 gfx::ThrobAnimation hover_animation_{this}; 342 343 // Should we animate when the state changes? 344 bool animate_on_state_change_ = false; 345 346 // Is the hover animation running because StartThrob was invoked? 347 bool is_throbbing_ = false; 348 349 // Mouse event flags which can trigger button actions. 350 int triggerable_event_flags_ = ui::EF_LEFT_MOUSE_BUTTON; 351 352 // See description above setter. 353 bool request_focus_on_press_ = false; 354 355 // True when a button click should trigger an animation action on 356 // ink_drop_delegate(). 357 bool has_ink_drop_action_on_click_ = false; 358 359 // When true, the ink drop ripple and hover will be hidden prior to showing 360 // the context menu. 361 bool hide_ink_drop_when_showing_context_menu_ = true; 362 363 // When true, the ink drop ripple will be shown when setting state to hot 364 // tracked with SetHotTracked(). 365 bool show_ink_drop_when_hot_tracked_ = false; 366 367 // The color of the ripple and hover. 368 SkColor ink_drop_base_color_; 369 370 // The focus ring for this Button. 371 std::unique_ptr<FocusRing> focus_ring_; 372 373 std::unique_ptr<Painter> focus_painter_; 374 375 std::unique_ptr<WidgetObserverButtonBridge> widget_observer_; 376 377 // ButtonController is responsible for handling events sent to the Button and 378 // related state changes from the events. 379 // TODO(cyan): Make sure all state changes are handled within 380 // ButtonController. 381 std::unique_ptr<ButtonController> button_controller_; 382 383 PropertyChangedSubscription enabled_changed_subscription_{ 384 AddEnabledChangedCallback(base::BindRepeating(&Button::OnEnabledChanged, 385 base::Unretained(this)))}; 386 387 base::ObserverList<ButtonObserver> button_observers_; 388 389 DISALLOW_COPY_AND_ASSIGN(Button); 390 }; 391 392 } // namespace views 393 394 #endif // UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ 395