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