File: button.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (456 lines) | stat: -rw-r--r-- 17,161 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_
#define UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_

#include <memory>
#include <optional>
#include <utility>
#include <variant>

#include "base/callback_list.h"
#include "base/functional/bind.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "ui/actions/actions.h"
#include "ui/base/metadata/metadata_types.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
#include "ui/events/event_constants.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/animation/animation_delegate_views.h"
#include "ui/views/animation/ink_drop_host.h"
#include "ui/views/animation/ink_drop_state.h"
#include "ui/views/controls/button/button_controller_delegate.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/painter.h"
#include "ui/views/view.h"

namespace ui {
class Event;
}  // namespace ui

namespace views {

namespace test {
class ButtonTestApi;
}

class Button;
class ButtonController;

// A View representing a button. A Button is focusable by default and will
// be part of the focus chain.
class VIEWS_EXPORT Button : public View, public AnimationDelegateViews {
  METADATA_HEADER(Button, View)

 public:
  // Button states for various button sub-types.
  enum ButtonState {
    STATE_NORMAL = 0,
    STATE_HOVERED,
    STATE_PRESSED,
    STATE_DISABLED,
    STATE_COUNT,
  };

  // An enum describing the events on which a button should be clicked for a
  // given key event.
  enum class KeyClickAction {
    kOnKeyPress,
    kOnKeyRelease,
    kNone,
  };

  // TODO(cyan): Consider having Button implement ButtonControllerDelegate.
  class VIEWS_EXPORT DefaultButtonControllerDelegate
      : public ButtonControllerDelegate {
   public:
    explicit DefaultButtonControllerDelegate(Button* button);

    DefaultButtonControllerDelegate(const DefaultButtonControllerDelegate&) =
        delete;
    DefaultButtonControllerDelegate& operator=(
        const DefaultButtonControllerDelegate&) = delete;

    ~DefaultButtonControllerDelegate() override;

    // views::ButtonControllerDelegate:
    void RequestFocusFromEvent() override;
    void NotifyClick(const ui::Event& event) override;
    void OnClickCanceled(const ui::Event& event) override;
    bool IsTriggerableEvent(const ui::Event& event) override;
    bool ShouldEnterPushedState(const ui::Event& event) override;
    bool ShouldEnterHoveredState() override;
    InkDrop* GetInkDrop() override;
    int GetDragOperations(const gfx::Point& press_pt) override;
    bool InDrag() override;
  };

  // PressedCallback wraps a one-arg callback type with multiple constructors to
  // allow callers to specify a OnceClosure or RepeatingClosure if they don't
  // care about the callback arg.
  //
  // TODO(crbug.com/41348855): Re-evaluate if this class can/should be converted
  // to a type alias + various helpers or overloads to support the
  // RepeatingClosure case.
  class VIEWS_EXPORT PressedCallback {
   public:
    using Callback = base::RepeatingCallback<void(const ui::Event& event)>;

    // Allow providing callbacks that expect either zero or one args, since many
    // callers don't care about the argument and can avoid adapter functions
    // this way.
    PressedCallback(base::OnceClosure closure);       // NOLINT
    PressedCallback(Callback callback = Callback());  // NOLINT
    PressedCallback(base::RepeatingClosure closure);  // NOLINT
    PressedCallback(PressedCallback&&);
    PressedCallback& operator=(PressedCallback&&);
    ~PressedCallback();

    // Returns true if `callback_` holds a non-null callback, regardless if the
    // callback is once or repeating.
    explicit operator bool() const;

    // Precondition:
    // `operator bool()` must be true (i.e. callback_ must be a non-null
    // callback).
    //
    // Postcondition:
    // If `callback_` holds a `base::OnceClosure`, `operator bool()` will be
    // false.
    void Run(const ui::Event& event);

   private:
    std::variant<base::OnceClosure, base::RepeatingClosure, Callback> callback_;
  };

  // This is used to ensure that multiple overlapping elements anchored on this
  // button correctly handle highlighting.
  class VIEWS_EXPORT ScopedAnchorHighlight {
   public:
    explicit ScopedAnchorHighlight(base::WeakPtr<Button> button);
    ~ScopedAnchorHighlight();

    ScopedAnchorHighlight(ScopedAnchorHighlight&&);
    ScopedAnchorHighlight& operator=(ScopedAnchorHighlight&&);

   private:
    base::WeakPtr<Button> button_;
  };

  static constexpr ButtonState kButtonStates[STATE_COUNT] = {
      ButtonState::STATE_NORMAL, ButtonState::STATE_HOVERED,
      ButtonState::STATE_PRESSED, ButtonState::STATE_DISABLED};

  Button(const Button&) = delete;
  Button& operator=(const Button&) = delete;

  ~Button() override;

  static const Button* AsButton(const View* view);
  static Button* AsButton(View* view);

  static ButtonState GetButtonStateFrom(ui::NativeTheme::State state);

  // Tag is now a property. These accessors are deprecated. Use GetTag() and
  // SetTag() below or even better, use SetID()/GetID() from the ancestor.
  int tag() const { return tag_; }
  void set_tag(int tag) { tag_ = tag; }

  virtual void SetCallback(PressedCallback callback);

  void AdjustAccessibleName(std::u16string& new_name,
                            ax::mojom::NameFrom& name_from) override;

  // Button uses the tooltip text in `AdjustAccessibleName` to provide an
  // alternative accessible name if there is no existing accessible name.
  // However, some button subclasses have a custom locally cached tooltip text
  // that should be used instead. Views that follow this pattern should override
  // this method to provide an alternative accessible name if they cache a
  // custom tooltip text that is different from the one cached in View.
  virtual std::u16string GetAlternativeAccessibleName() const;

  // Get/sets the current display state of the button.
  ButtonState GetState() const;
  // Clients passing in STATE_DISABLED should consider calling
  // SetEnabled(false) instead because the enabled flag can affect other things
  // like event dispatching, focus traversals, etc. Calling SetEnabled(false)
  // will also set the state of |this| to STATE_DISABLED.
  void SetState(ButtonState state);

  // Set how long the hover animation will last for.
  void SetAnimationDuration(base::TimeDelta duration);

  void SetTriggerableEventFlags(int triggerable_event_flags);
  int GetTriggerableEventFlags() const;

  // Sets whether |RequestFocus| should be invoked on a mouse press. The default
  // is false.
  void SetRequestFocusOnPress(bool value);
  bool GetRequestFocusOnPress() const;

  // See description above field.
  void SetAnimateOnStateChange(bool value);
  bool GetAnimateOnStateChange() const;

  void SetHideInkDropWhenShowingContextMenu(bool value);
  bool GetHideInkDropWhenShowingContextMenu() const;

  void SetShowInkDropWhenHotTracked(bool value);
  bool GetShowInkDropWhenHotTracked() const;

  void SetHasInkDropActionOnClick(bool value);
  bool GetHasInkDropActionOnClick() const;

  void SetInstallFocusRingOnFocus(bool install_focus_ring_on_focus);
  bool GetInstallFocusRingOnFocus() const;

  void SetHotTracked(bool is_hot_tracked);
  bool IsHotTracked() const;

  // TODO(crbug.com/40801855): These property accessors and tag_ field should be
  // removed and use SetID()/GetID from the ancestor View class.
  void SetTag(int value);
  int GetTag() const;

  void SetFocusPainter(std::unique_ptr<Painter> focus_painter);

  // Highlights the ink drop for the button.
  void SetHighlighted(bool highlighted);

  // Menus, bubbles, and IPH should call this when they anchor. This ensures
  // that highlighting is handled correctly with multiple anchored elements.
  // TODO(crbug.com/40262104): Migrate callers of SetHighlighted to this
  // function, where appropriate.
  ScopedAnchorHighlight AddAnchorHighlight();

  base::CallbackListSubscription AddStateChangedCallback(
      PropertyChangedCallback callback);
  base::CallbackListSubscription AddAnchorCountChangedCallback(
      base::RepeatingCallback<void(size_t)> callback);

  // Overridden from View:
  bool OnMousePressed(const ui::MouseEvent& event) override;
  bool OnMouseDragged(const ui::MouseEvent& event) override;
  void OnMouseReleased(const ui::MouseEvent& event) override;
  void OnMouseCaptureLost() override;
  void OnMouseEntered(const ui::MouseEvent& event) override;
  void OnMouseExited(const ui::MouseEvent& event) override;
  void OnMouseMoved(const ui::MouseEvent& event) override;
  bool OnKeyPressed(const ui::KeyEvent& event) override;
  bool OnKeyReleased(const ui::KeyEvent& event) override;
  void OnGestureEvent(ui::GestureEvent* event) override;
  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
  bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override;
  void ShowContextMenu(const gfx::Point& p,
                       ui::mojom::MenuSourceType source_type) override;
  void OnDragDone() override;
  // Instead of overriding this, subclasses that want custom painting should use
  // PaintButtonContents.
  void OnPaint(gfx::Canvas* canvas) final;
  void VisibilityChanged(View* starting_from, bool is_visible) override;
  void ViewHierarchyChanged(
      const ViewHierarchyChangedDetails& details) override;
  void OnFocus() override;
  void OnBlur() override;
  std::unique_ptr<ActionViewInterface> GetActionViewInterface() override;

  // Overridden from views::AnimationDelegateViews:
  void AnimationProgressed(const gfx::Animation* animation) override;

  // Returns the click action for the given key event.
  // Subclasses may override this method to support default actions for key
  // events.
  // TODO(cyan): Move this into the ButtonController.
  virtual KeyClickAction GetKeyClickActionForEvent(const ui::KeyEvent& event);

  ButtonController* button_controller() const {
    return button_controller_.get();
  }

  void SetButtonController(std::unique_ptr<ButtonController> button_controller);

  gfx::Point GetMenuPosition() const;

  View* ink_drop_view() const { return ink_drop_view_; }
  void SetInkDropView(View* view);

 protected:
  explicit Button(PressedCallback callback = PressedCallback());

  // Called when the button has been clicked or tapped and should request focus
  // if necessary.
  virtual void RequestFocusFromEvent();

  // Cause the button to notify the listener that a click occurred.
  virtual void NotifyClick(const ui::Event& event);

  // Called when a button gets released without triggering an action.
  // Note: This is only wired up for mouse button events and not gesture
  // events.
  virtual void OnClickCanceled(const ui::Event& event);

  // Called when the tooltip is set.
  virtual void OnSetTooltipText(const std::u16string& tooltip_text);

  void OnTooltipTextChanged(const std::u16string& old_tooltip_text) override;

  // Invoked from SetState() when SetState() is passed a value that differs from
  // the current node_data. Button's implementation of StateChanged() does
  // nothing; this method is provided for subclasses that wish to do something
  // on state changes.
  virtual void StateChanged(ButtonState old_state);

  // Returns true if the event is one that can trigger notifying the listener.
  // This implementation returns true if the left mouse button is down.
  // TODO(cyan): Remove this method and move the implementation into
  // ButtonController.
  virtual bool IsTriggerableEvent(const ui::Event& event);

  // Returns true if the ink drop should be updated by Button when
  // OnClickCanceled() is called. This method is provided for subclasses.
  // If the method is overriden and returns false, the subclass is responsible
  // will be responsible for updating the ink drop.
  virtual bool ShouldUpdateInkDropOnClickCanceled() const;

  // Returns true if the button should become pressed when the user
  // holds the mouse down over the button. For this implementation,
  // we simply return IsTriggerableEvent(event).
  virtual bool ShouldEnterPushedState(const ui::Event& event);

  // Override to paint custom button contents. Any background or border set on
  // the view will be painted before this is called and |focus_painter_| will be
  // painted afterwards.
  virtual void PaintButtonContents(gfx::Canvas* canvas);

  // Returns true if the button should enter hovered state; that is, if the
  // mouse is over the button, and no other window has capture (which would
  // prevent the button from receiving MouseExited events and updating its
  // node_data). This does not take into account enabled node_data.
  bool ShouldEnterHoveredState();

  const gfx::ThrobAnimation& hover_animation() const {
    return hover_animation_;
  }

  // Getter used by metadata only.
  const PressedCallback& GetCallback() const { return callback_; }

  base::WeakPtr<Button> GetWeakPtr();

  virtual void OnEnabledChanged();

  virtual void UpdateAccessibleCheckedState();

  // Sets the |default_action_verb_| for accessibility. Subclasses may
  // call this method to set their specific default action verb.
  void SetDefaultActionVerb(ax::mojom::DefaultActionVerb verb);
  // Called whenever the state impacting default action verb changes.
  void UpdateAccessibleDefaultActionVerb();

 private:
  friend class test::ButtonTestApi;
  friend class ScopedAnchorHighlight;
  FRIEND_TEST_ALL_PREFIXES(BlueButtonTest, Border);

  void ReleaseAnchorHighlight();

  // The button's listener. Notified when clicked.
  PressedCallback callback_;

  // Callbacks called when the anchor count changes.
  base::RepeatingCallbackList<void(size_t)> anchor_count_changed_callbacks_;

  // The id tag associated with this button. Used to disambiguate buttons.
  // TODO(pbos): See if this can be removed, e.g. by replacing with SetID().
  int tag_ = -1;

  ButtonState state_ = STATE_NORMAL;

  gfx::ThrobAnimation hover_animation_{this};

  // Should we animate when the state changes?
  bool animate_on_state_change_ = false;

  // Mouse event flags which can trigger button actions.
  int triggerable_event_flags_ = ui::EF_LEFT_MOUSE_BUTTON;

  // See description above setter.
  bool request_focus_on_press_ = false;

  // True when a button click should trigger an animation action on
  // ink_drop_delegate().
  bool has_ink_drop_action_on_click_ = false;

  // When true, the ink drop ripple and hover will be hidden prior to showing
  // the context menu.
  bool hide_ink_drop_when_showing_context_menu_ = true;

  // When true, the ink drop ripple will be shown when setting state to hot
  // tracked with SetHotTracked().
  bool show_ink_drop_when_hot_tracked_ = false;

  // |ink_drop_view_| is generally the button, but can be overridden for special
  // cases (e.g. Checkbox) where the InkDrop may be more appropriately installed
  // on a child view of the button.
  raw_ptr<View> ink_drop_view_ = this;

  std::unique_ptr<Painter> focus_painter_;

  // ButtonController is responsible for handling events sent to the Button and
  // related state changes from the events.
  // TODO(cyan): Make sure all state changes are handled within
  // ButtonController.
  std::unique_ptr<ButtonController> button_controller_;

  base::CallbackListSubscription enabled_changed_subscription_{
      AddEnabledChangedCallback(base::BindRepeating(&Button::OnEnabledChanged,
                                                    base::Unretained(this)))};

  size_t anchor_count_ = 0;

  ax::mojom::DefaultActionVerb default_action_verb_ =
      ax::mojom::DefaultActionVerb::kPress;

  base::WeakPtrFactory<Button> weak_ptr_factory_{this};
};

class VIEWS_EXPORT ButtonActionViewInterface : public BaseActionViewInterface {
 public:
  explicit ButtonActionViewInterface(Button* action_view);
  ~ButtonActionViewInterface() override = default;

  // BaseActionViewInterface:
  void ActionItemChangedImpl(actions::ActionItem* action_item) override;
  void LinkActionInvocationToView(
      base::RepeatingClosure invoke_action_callback) override;

 private:
  raw_ptr<Button> action_view_;
};

BEGIN_VIEW_BUILDER(VIEWS_EXPORT, Button, View)
VIEW_BUILDER_PROPERTY(Button::PressedCallback, Callback)
VIEW_BUILDER_PROPERTY(base::TimeDelta, AnimationDuration)
VIEW_BUILDER_PROPERTY(bool, AnimateOnStateChange)
VIEW_BUILDER_PROPERTY(bool, HasInkDropActionOnClick)
VIEW_BUILDER_PROPERTY(bool, HideInkDropWhenShowingContextMenu)
VIEW_BUILDER_PROPERTY(bool, InstallFocusRingOnFocus)
VIEW_BUILDER_PROPERTY(bool, RequestFocusOnPress)
VIEW_BUILDER_PROPERTY(Button::ButtonState, State)
VIEW_BUILDER_PROPERTY(int, Tag)
VIEW_BUILDER_PROPERTY(std::u16string, TooltipText)
VIEW_BUILDER_PROPERTY(int, TriggerableEventFlags)
END_VIEW_BUILDER

}  // namespace views

DEFINE_VIEW_BUILDER(VIEWS_EXPORT, Button)

#endif  // UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_