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 #include "ui/views/controls/button/button.h"
6
7 #include <utility>
8
9 #include "base/strings/utf_string_conversions.h"
10 #include "ui/accessibility/ax_enums.mojom.h"
11 #include "ui/accessibility/ax_node_data.h"
12 #include "ui/base/class_property.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_utils.h"
15 #include "ui/events/keycodes/keyboard_codes.h"
16 #include "ui/gfx/animation/throb_animation.h"
17 #include "ui/gfx/color_palette.h"
18 #include "ui/native_theme/native_theme.h"
19 #include "ui/views/animation/ink_drop_highlight.h"
20 #include "ui/views/animation/ink_drop_impl.h"
21 #include "ui/views/controls/button/button_controller.h"
22 #include "ui/views/controls/button/button_controller_delegate.h"
23 #include "ui/views/controls/button/button_observer.h"
24 #include "ui/views/controls/button/checkbox.h"
25 #include "ui/views/controls/button/image_button.h"
26 #include "ui/views/controls/button/label_button.h"
27 #include "ui/views/controls/button/menu_button.h"
28 #include "ui/views/controls/button/radio_button.h"
29 #include "ui/views/controls/button/toggle_button.h"
30 #include "ui/views/controls/focus_ring.h"
31 #include "ui/views/painter.h"
32 #include "ui/views/style/platform_style.h"
33 #include "ui/views/widget/widget.h"
34
35 #if defined(USE_AURA)
36 #include "ui/aura/client/capture_client.h"
37 #include "ui/aura/window.h"
38 #endif
39
40 namespace views {
41
42 namespace {
43
44 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsButtonProperty, false)
45
46 } // namespace
47
48 ////////////////////////////////////////////////////////////////////////////////
49 // WidgetObserverButtonBridge:
WidgetObserverButtonBridge(Button * button)50 Button::WidgetObserverButtonBridge::WidgetObserverButtonBridge(Button* button)
51 : owner_(button) {
52 DCHECK(button->GetWidget());
53 button->GetWidget()->AddObserver(this);
54 }
55
~WidgetObserverButtonBridge()56 Button::WidgetObserverButtonBridge::~WidgetObserverButtonBridge() {
57 if (owner_)
58 owner_->GetWidget()->RemoveObserver(this);
59 }
60
OnWidgetPaintAsActiveChanged(Widget * widget,bool paint_as_active)61 void Button::WidgetObserverButtonBridge::OnWidgetPaintAsActiveChanged(
62 Widget* widget,
63 bool paint_as_active) {
64 owner_->WidgetPaintAsActiveChanged(widget, paint_as_active);
65 }
66
OnWidgetDestroying(Widget * widget)67 void Button::WidgetObserverButtonBridge::OnWidgetDestroying(Widget* widget) {
68 widget->RemoveObserver(this);
69 owner_ = nullptr;
70 }
71
72 ////////////////////////////////////////////////////////////////////////////////
73 // ButtonControllerDelegate:
DefaultButtonControllerDelegate(Button * button)74 Button::DefaultButtonControllerDelegate::DefaultButtonControllerDelegate(
75 Button* button)
76 : ButtonControllerDelegate(button) {}
77
78 Button::DefaultButtonControllerDelegate::~DefaultButtonControllerDelegate() =
79 default;
80
RequestFocusFromEvent()81 void Button::DefaultButtonControllerDelegate::RequestFocusFromEvent() {
82 button()->RequestFocusFromEvent();
83 }
84
NotifyClick(const ui::Event & event)85 void Button::DefaultButtonControllerDelegate::NotifyClick(
86 const ui::Event& event) {
87 button()->NotifyClick(event);
88 }
89
OnClickCanceled(const ui::Event & event)90 void Button::DefaultButtonControllerDelegate::OnClickCanceled(
91 const ui::Event& event) {
92 button()->OnClickCanceled(event);
93 }
94
IsTriggerableEvent(const ui::Event & event)95 bool Button::DefaultButtonControllerDelegate::IsTriggerableEvent(
96 const ui::Event& event) {
97 return button()->IsTriggerableEvent(event);
98 }
99
ShouldEnterPushedState(const ui::Event & event)100 bool Button::DefaultButtonControllerDelegate::ShouldEnterPushedState(
101 const ui::Event& event) {
102 return button()->ShouldEnterPushedState(event);
103 }
104
ShouldEnterHoveredState()105 bool Button::DefaultButtonControllerDelegate::ShouldEnterHoveredState() {
106 return button()->ShouldEnterHoveredState();
107 }
108
GetInkDrop()109 InkDrop* Button::DefaultButtonControllerDelegate::GetInkDrop() {
110 return button()->GetInkDrop();
111 }
112
GetDragOperations(const gfx::Point & press_pt)113 int Button::DefaultButtonControllerDelegate::GetDragOperations(
114 const gfx::Point& press_pt) {
115 return button()->GetDragOperations(press_pt);
116 }
117
InDrag()118 bool Button::DefaultButtonControllerDelegate::InDrag() {
119 return button()->InDrag();
120 }
121
122 ////////////////////////////////////////////////////////////////////////////////
123
124 // static
125 constexpr Button::ButtonState Button::kButtonStates[STATE_COUNT];
126
127 // static
AsButton(const views::View * view)128 const Button* Button::AsButton(const views::View* view) {
129 return AsButton(const_cast<View*>(view));
130 }
131
132 // static
AsButton(views::View * view)133 Button* Button::AsButton(views::View* view) {
134 if (view && view->GetProperty(kIsButtonProperty))
135 return static_cast<Button*>(view);
136 return nullptr;
137 }
138
139 // static
GetButtonStateFrom(ui::NativeTheme::State state)140 Button::ButtonState Button::GetButtonStateFrom(ui::NativeTheme::State state) {
141 switch (state) {
142 case ui::NativeTheme::kDisabled:
143 return Button::STATE_DISABLED;
144 case ui::NativeTheme::kHovered:
145 return Button::STATE_HOVERED;
146 case ui::NativeTheme::kNormal:
147 return Button::STATE_NORMAL;
148 case ui::NativeTheme::kPressed:
149 return Button::STATE_PRESSED;
150 case ui::NativeTheme::kNumStates:
151 NOTREACHED();
152 }
153 return Button::STATE_NORMAL;
154 }
155
156 ////////////////////////////////////////////////////////////////////////////////
157 // Button, public:
158
159 Button::~Button() = default;
160
SetFocusForPlatform()161 void Button::SetFocusForPlatform() {
162 #if defined(OS_MACOSX)
163 // On Mac, buttons are focusable only in full keyboard access mode.
164 SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
165 #else
166 SetFocusBehavior(FocusBehavior::ALWAYS);
167 #endif
168 }
169
SetTooltipText(const base::string16 & tooltip_text)170 void Button::SetTooltipText(const base::string16& tooltip_text) {
171 if (tooltip_text == tooltip_text_)
172 return;
173 tooltip_text_ = tooltip_text;
174 OnSetTooltipText(tooltip_text);
175 TooltipTextChanged();
176 }
177
SetAccessibleName(const base::string16 & name)178 void Button::SetAccessibleName(const base::string16& name) {
179 accessible_name_ = name;
180 NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
181 }
182
GetAccessibleName() const183 const base::string16& Button::GetAccessibleName() const {
184 return accessible_name_.empty() ? tooltip_text_ : accessible_name_;
185 }
186
SetState(ButtonState state)187 void Button::SetState(ButtonState state) {
188 if (state == state_)
189 return;
190
191 if (animate_on_state_change_ &&
192 (!is_throbbing_ || !hover_animation_.is_animating())) {
193 is_throbbing_ = false;
194 if ((state_ == STATE_HOVERED) && (state == STATE_NORMAL)) {
195 // For HOVERED -> NORMAL, animate from hovered (1) to not hovered (0).
196 hover_animation_.Hide();
197 } else if (state != STATE_HOVERED) {
198 // For HOVERED -> PRESSED/DISABLED, or any transition not involving
199 // HOVERED at all, simply set the state to not hovered (0).
200 hover_animation_.Reset();
201 } else if (state_ == STATE_NORMAL) {
202 // For NORMAL -> HOVERED, animate from not hovered (0) to hovered (1).
203 hover_animation_.Show();
204 } else {
205 // For PRESSED/DISABLED -> HOVERED, simply set the state to hovered (1).
206 hover_animation_.Reset(1);
207 }
208 }
209
210 ButtonState old_state = state_;
211 state_ = state;
212 StateChanged(old_state);
213 SchedulePaint();
214 }
215
GetVisualState() const216 Button::ButtonState Button::GetVisualState() const {
217 if (PlatformStyle::kInactiveWidgetControlsAppearDisabled && GetWidget() &&
218 !GetWidget()->ShouldPaintAsActive()) {
219 return STATE_DISABLED;
220 }
221 return state();
222 }
223
StartThrobbing(int cycles_til_stop)224 void Button::StartThrobbing(int cycles_til_stop) {
225 if (!animate_on_state_change_)
226 return;
227 is_throbbing_ = true;
228 hover_animation_.StartThrobbing(cycles_til_stop);
229 }
230
StopThrobbing()231 void Button::StopThrobbing() {
232 if (hover_animation_.is_animating()) {
233 hover_animation_.Stop();
234 SchedulePaint();
235 }
236 }
237
SetAnimationDuration(base::TimeDelta duration)238 void Button::SetAnimationDuration(base::TimeDelta duration) {
239 hover_animation_.SetSlideDuration(duration);
240 }
241
SetInstallFocusRingOnFocus(bool install)242 void Button::SetInstallFocusRingOnFocus(bool install) {
243 if (install)
244 focus_ring_ = FocusRing::Install(this);
245 else
246 focus_ring_.reset();
247 }
248
SetHotTracked(bool is_hot_tracked)249 void Button::SetHotTracked(bool is_hot_tracked) {
250 if (state_ != STATE_DISABLED) {
251 SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL);
252 if (show_ink_drop_when_hot_tracked_) {
253 AnimateInkDrop(is_hot_tracked ? views::InkDropState::ACTIVATED
254 : views::InkDropState::HIDDEN,
255 nullptr);
256 }
257 }
258
259 if (is_hot_tracked)
260 NotifyAccessibilityEvent(ax::mojom::Event::kHover, true);
261 }
262
IsHotTracked() const263 bool Button::IsHotTracked() const {
264 return state_ == STATE_HOVERED;
265 }
266
SetFocusPainter(std::unique_ptr<Painter> focus_painter)267 void Button::SetFocusPainter(std::unique_ptr<Painter> focus_painter) {
268 focus_painter_ = std::move(focus_painter);
269 }
270
SetHighlighted(bool bubble_visible)271 void Button::SetHighlighted(bool bubble_visible) {
272 AnimateInkDrop(bubble_visible ? views::InkDropState::ACTIVATED
273 : views::InkDropState::DEACTIVATED,
274 nullptr);
275 for (ButtonObserver& observer : button_observers_)
276 observer.OnHighlightChanged(this, bubble_visible);
277 }
278
AddButtonObserver(ButtonObserver * observer)279 void Button::AddButtonObserver(ButtonObserver* observer) {
280 button_observers_.AddObserver(observer);
281 }
282
RemoveButtonObserver(ButtonObserver * observer)283 void Button::RemoveButtonObserver(ButtonObserver* observer) {
284 button_observers_.RemoveObserver(observer);
285 }
286
GetKeyClickActionForEvent(const ui::KeyEvent & event)287 Button::KeyClickAction Button::GetKeyClickActionForEvent(
288 const ui::KeyEvent& event) {
289 if (event.key_code() == ui::VKEY_SPACE)
290 return PlatformStyle::kKeyClickActionOnSpace;
291 if (event.key_code() == ui::VKEY_RETURN &&
292 PlatformStyle::kReturnClicksFocusedControl)
293 return KeyClickAction::kOnKeyPress;
294 return KeyClickAction::kNone;
295 }
296
SetButtonController(std::unique_ptr<ButtonController> button_controller)297 void Button::SetButtonController(
298 std::unique_ptr<ButtonController> button_controller) {
299 button_controller_ = std::move(button_controller);
300 }
301
GetMenuPosition() const302 gfx::Point Button::GetMenuPosition() const {
303 gfx::Rect lb = GetLocalBounds();
304
305 // Offset of the associated menu position.
306 constexpr gfx::Vector2d kMenuOffset{-2, -4};
307
308 // The position of the menu depends on whether or not the locale is
309 // right-to-left.
310 gfx::Point menu_position(lb.right(), lb.bottom());
311 if (base::i18n::IsRTL())
312 menu_position.set_x(lb.x());
313
314 View::ConvertPointToScreen(this, &menu_position);
315 if (base::i18n::IsRTL())
316 menu_position.Offset(-kMenuOffset.x(), kMenuOffset.y());
317 else
318 menu_position += kMenuOffset;
319
320 DCHECK(GetWidget());
321 const int max_x_coordinate =
322 GetWidget()->GetWorkAreaBoundsInScreen().right() - 1;
323 if (max_x_coordinate && max_x_coordinate <= menu_position.x())
324 menu_position.set_x(max_x_coordinate - 1);
325 return menu_position;
326 }
327
328 ////////////////////////////////////////////////////////////////////////////////
329 // Button, View overrides:
330
OnMousePressed(const ui::MouseEvent & event)331 bool Button::OnMousePressed(const ui::MouseEvent& event) {
332 return button_controller_->OnMousePressed(event);
333 }
334
OnMouseDragged(const ui::MouseEvent & event)335 bool Button::OnMouseDragged(const ui::MouseEvent& event) {
336 if (state_ != STATE_DISABLED) {
337 const bool should_enter_pushed = ShouldEnterPushedState(event);
338 const bool should_show_pending =
339 should_enter_pushed &&
340 button_controller_->notify_action() ==
341 ButtonController::NotifyAction::kOnRelease &&
342 !InDrag();
343 if (HitTestPoint(event.location())) {
344 SetState(should_enter_pushed ? STATE_PRESSED : STATE_HOVERED);
345 if (should_show_pending && GetInkDrop()->GetTargetInkDropState() ==
346 views::InkDropState::HIDDEN) {
347 AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event);
348 }
349 } else {
350 SetState(STATE_NORMAL);
351 if (should_show_pending && GetInkDrop()->GetTargetInkDropState() ==
352 views::InkDropState::ACTION_PENDING) {
353 AnimateInkDrop(views::InkDropState::HIDDEN, &event);
354 }
355 }
356 }
357 return true;
358 }
359
OnMouseReleased(const ui::MouseEvent & event)360 void Button::OnMouseReleased(const ui::MouseEvent& event) {
361 button_controller_->OnMouseReleased(event);
362 }
363
OnMouseCaptureLost()364 void Button::OnMouseCaptureLost() {
365 // Starting a drag results in a MouseCaptureLost. Reset button state.
366 // TODO(varkha): Reset the state even while in drag. The same logic may
367 // applies everywhere so gather any feedback and update.
368 if (state_ != STATE_DISABLED)
369 SetState(STATE_NORMAL);
370 AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
371 GetInkDrop()->SetHovered(false);
372 InkDropHostView::OnMouseCaptureLost();
373 }
374
OnMouseEntered(const ui::MouseEvent & event)375 void Button::OnMouseEntered(const ui::MouseEvent& event) {
376 button_controller_->OnMouseEntered(event);
377 }
378
OnMouseExited(const ui::MouseEvent & event)379 void Button::OnMouseExited(const ui::MouseEvent& event) {
380 button_controller_->OnMouseExited(event);
381 }
382
OnMouseMoved(const ui::MouseEvent & event)383 void Button::OnMouseMoved(const ui::MouseEvent& event) {
384 button_controller_->OnMouseMoved(event);
385 }
386
OnKeyPressed(const ui::KeyEvent & event)387 bool Button::OnKeyPressed(const ui::KeyEvent& event) {
388 return button_controller_->OnKeyPressed(event);
389 }
390
OnKeyReleased(const ui::KeyEvent & event)391 bool Button::OnKeyReleased(const ui::KeyEvent& event) {
392 return button_controller_->OnKeyReleased(event);
393 }
394
OnGestureEvent(ui::GestureEvent * event)395 void Button::OnGestureEvent(ui::GestureEvent* event) {
396 button_controller_->OnGestureEvent(event);
397 }
398
AcceleratorPressed(const ui::Accelerator & accelerator)399 bool Button::AcceleratorPressed(const ui::Accelerator& accelerator) {
400 SetState(STATE_NORMAL);
401 NotifyClick(accelerator.ToKeyEvent());
402 return true;
403 }
404
SkipDefaultKeyEventProcessing(const ui::KeyEvent & event)405 bool Button::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
406 // If this button is focused and the user presses space or enter, don't let
407 // that be treated as an accelerator if there is a key click action
408 // corresponding to it.
409 return GetKeyClickActionForEvent(event) != KeyClickAction::kNone;
410 }
411
GetTooltipText(const gfx::Point & p) const412 base::string16 Button::GetTooltipText(const gfx::Point& p) const {
413 return tooltip_text_;
414 }
415
ShowContextMenu(const gfx::Point & p,ui::MenuSourceType source_type)416 void Button::ShowContextMenu(const gfx::Point& p,
417 ui::MenuSourceType source_type) {
418 if (!context_menu_controller())
419 return;
420
421 // We're about to show the context menu. Showing the context menu likely means
422 // we won't get a mouse exited and reset state. Reset it now to be sure.
423 if (state_ != STATE_DISABLED)
424 SetState(STATE_NORMAL);
425 if (hide_ink_drop_when_showing_context_menu_) {
426 GetInkDrop()->SetHovered(false);
427 AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */);
428 }
429 InkDropHostView::ShowContextMenu(p, source_type);
430 }
431
OnDragDone()432 void Button::OnDragDone() {
433 // Only reset the state to normal if the button isn't currently disabled
434 // (since disabled buttons may still be able to be dragged).
435 if (state_ != STATE_DISABLED)
436 SetState(STATE_NORMAL);
437 AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */);
438 }
439
OnPaint(gfx::Canvas * canvas)440 void Button::OnPaint(gfx::Canvas* canvas) {
441 InkDropHostView::OnPaint(canvas);
442 PaintButtonContents(canvas);
443 Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
444 }
445
GetAccessibleNodeData(ui::AXNodeData * node_data)446 void Button::GetAccessibleNodeData(ui::AXNodeData* node_data) {
447 node_data->role = ax::mojom::Role::kButton;
448 node_data->SetName(GetAccessibleName());
449 if (!GetEnabled())
450 node_data->SetRestriction(ax::mojom::Restriction::kDisabled);
451
452 switch (state_) {
453 case STATE_HOVERED:
454 node_data->AddState(ax::mojom::State::kHovered);
455 break;
456 case STATE_PRESSED:
457 node_data->SetCheckedState(ax::mojom::CheckedState::kTrue);
458 break;
459 case STATE_DISABLED:
460 node_data->SetRestriction(ax::mojom::Restriction::kDisabled);
461 break;
462 case STATE_NORMAL:
463 case STATE_COUNT:
464 // No additional accessibility node_data set for this button node_data.
465 break;
466 }
467 if (GetEnabled())
468 node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kPress);
469
470 button_controller_->UpdateAccessibleNodeData(node_data);
471 }
472
VisibilityChanged(View * starting_from,bool visible)473 void Button::VisibilityChanged(View* starting_from, bool visible) {
474 InkDropHostView::VisibilityChanged(starting_from, visible);
475 if (state_ == STATE_DISABLED)
476 return;
477 SetState(visible && ShouldEnterHoveredState() ? STATE_HOVERED : STATE_NORMAL);
478 }
479
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)480 void Button::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) {
481 if (!details.is_add && state_ != STATE_DISABLED && details.child == this)
482 SetState(STATE_NORMAL);
483 InkDropHostView::ViewHierarchyChanged(details);
484 }
485
OnFocus()486 void Button::OnFocus() {
487 InkDropHostView::OnFocus();
488 if (focus_painter_)
489 SchedulePaint();
490 }
491
OnBlur()492 void Button::OnBlur() {
493 InkDropHostView::OnBlur();
494 if (IsHotTracked() || state_ == STATE_PRESSED) {
495 SetState(STATE_NORMAL);
496 if (GetInkDrop()->GetTargetInkDropState() != views::InkDropState::HIDDEN)
497 AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
498 // TODO(bruthig) : Fix Buttons to work well when multiple input
499 // methods are interacting with a button. e.g. By animating to HIDDEN here
500 // it is possible for a Mouse Release to trigger an action however there
501 // would be no visual cue to the user that this will occur.
502 }
503 if (focus_painter_)
504 SchedulePaint();
505 }
506
AddedToWidget()507 void Button::AddedToWidget() {
508 if (PlatformStyle::kInactiveWidgetControlsAppearDisabled)
509 widget_observer_ = std::make_unique<WidgetObserverButtonBridge>(this);
510 }
511
RemovedFromWidget()512 void Button::RemovedFromWidget() {
513 widget_observer_.reset();
514 }
515
CreateInkDrop()516 std::unique_ptr<InkDrop> Button::CreateInkDrop() {
517 std::unique_ptr<InkDrop> ink_drop = InkDropHostView::CreateInkDrop();
518 ink_drop->SetShowHighlightOnFocus(!focus_ring_);
519 return ink_drop;
520 }
521
GetInkDropBaseColor() const522 SkColor Button::GetInkDropBaseColor() const {
523 return ink_drop_base_color_;
524 }
525
526 ////////////////////////////////////////////////////////////////////////////////
527 // Button, gfx::AnimationDelegate implementation:
528
AnimationProgressed(const gfx::Animation * animation)529 void Button::AnimationProgressed(const gfx::Animation* animation) {
530 SchedulePaint();
531 }
532
533 ////////////////////////////////////////////////////////////////////////////////
534 // Button, protected:
535
Button(ButtonListener * listener)536 Button::Button(ButtonListener* listener)
537 : AnimationDelegateViews(this),
538 listener_(listener),
539 ink_drop_base_color_(gfx::kPlaceholderColor) {
540 SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
541 SetProperty(kIsButtonProperty, true);
542 hover_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150));
543 SetInstallFocusRingOnFocus(PlatformStyle::kPreferFocusRings);
544 button_controller_ = std::make_unique<ButtonController>(
545 this, std::make_unique<DefaultButtonControllerDelegate>(this));
546 }
547
RequestFocusFromEvent()548 void Button::RequestFocusFromEvent() {
549 if (request_focus_on_press_)
550 RequestFocus();
551 }
552
NotifyClick(const ui::Event & event)553 void Button::NotifyClick(const ui::Event& event) {
554 if (has_ink_drop_action_on_click_) {
555 AnimateInkDrop(InkDropState::ACTION_TRIGGERED,
556 ui::LocatedEvent::FromIfValid(&event));
557 }
558
559 // We can be called when there is no listener, in cases like double clicks on
560 // menu buttons etc.
561 if (listener_)
562 listener_->ButtonPressed(this, event);
563 }
564
OnClickCanceled(const ui::Event & event)565 void Button::OnClickCanceled(const ui::Event& event) {
566 if (ShouldUpdateInkDropOnClickCanceled()) {
567 if (GetInkDrop()->GetTargetInkDropState() ==
568 views::InkDropState::ACTION_PENDING ||
569 GetInkDrop()->GetTargetInkDropState() ==
570 views::InkDropState::ALTERNATE_ACTION_PENDING) {
571 AnimateInkDrop(views::InkDropState::HIDDEN,
572 ui::LocatedEvent::FromIfValid(&event));
573 }
574 }
575 }
576
OnSetTooltipText(const base::string16 & tooltip_text)577 void Button::OnSetTooltipText(const base::string16& tooltip_text) {}
578
StateChanged(ButtonState old_state)579 void Button::StateChanged(ButtonState old_state) {
580 button_controller_->OnStateChanged(old_state);
581 for (ButtonObserver& observer : button_observers_)
582 observer.OnStateChanged(this, old_state);
583 }
584
IsTriggerableEvent(const ui::Event & event)585 bool Button::IsTriggerableEvent(const ui::Event& event) {
586 return button_controller_->IsTriggerableEvent(event);
587 }
588
ShouldUpdateInkDropOnClickCanceled() const589 bool Button::ShouldUpdateInkDropOnClickCanceled() const {
590 return true;
591 }
592
ShouldEnterPushedState(const ui::Event & event)593 bool Button::ShouldEnterPushedState(const ui::Event& event) {
594 return IsTriggerableEvent(event);
595 }
596
PaintButtonContents(gfx::Canvas * canvas)597 void Button::PaintButtonContents(gfx::Canvas* canvas) {}
598
ShouldEnterHoveredState()599 bool Button::ShouldEnterHoveredState() {
600 if (!GetVisible())
601 return false;
602
603 bool check_mouse_position = true;
604 #if defined(USE_AURA)
605 // If another window has capture, we shouldn't check the current mouse
606 // position because the button won't receive any mouse events - so if the
607 // mouse was hovered, the button would be stuck in a hovered state (since it
608 // would never receive OnMouseExited).
609 const Widget* widget = GetWidget();
610 if (widget && widget->GetNativeWindow()) {
611 aura::Window* root_window = widget->GetNativeWindow()->GetRootWindow();
612 aura::client::CaptureClient* capture_client =
613 aura::client::GetCaptureClient(root_window);
614 aura::Window* capture_window =
615 capture_client ? capture_client->GetGlobalCaptureWindow() : nullptr;
616 check_mouse_position = !capture_window || capture_window == root_window;
617 }
618 #endif
619
620 return check_mouse_position && IsMouseHovered();
621 }
622
OnEnabledChanged()623 void Button::OnEnabledChanged() {
624 if (GetEnabled() ? (state_ != STATE_DISABLED) : (state_ == STATE_DISABLED))
625 return;
626
627 if (GetEnabled()) {
628 bool should_enter_hover_state = ShouldEnterHoveredState();
629 SetState(should_enter_hover_state ? STATE_HOVERED : STATE_NORMAL);
630 GetInkDrop()->SetHovered(should_enter_hover_state);
631 } else {
632 SetState(STATE_DISABLED);
633 GetInkDrop()->SetHovered(false);
634 }
635 }
636
WidgetPaintAsActiveChanged(Widget * widget,bool paint_as_active)637 void Button::WidgetPaintAsActiveChanged(Widget* widget, bool paint_as_active) {
638 StateChanged(state());
639 }
640
641 BEGIN_METADATA(Button)
642 METADATA_PARENT_CLASS(InkDropHostView)
643 END_METADATA()
644
645 } // namespace views
646