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_
|