blob: 0acec7283d734bcb8e7503ebb0455f6bcdbb5df4 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/button/button_controller.h"
#include <memory>
#include <utility>
#include "ui/accessibility/ax_node_data.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/controls/button/button_controller_delegate.h"
namespace views {
ButtonController::ButtonController(
Button* button,
std::unique_ptr<ButtonControllerDelegate> delegate)
: button_(button), button_controller_delegate_(std::move(delegate)) {}
ButtonController::~ButtonController() = default;
bool ButtonController::OnMousePressed(const ui::MouseEvent& event) {
if (button_->GetState() == Button::STATE_DISABLED) {
return true;
}
if (button_->GetState() != Button::STATE_PRESSED &&
button_controller_delegate_->ShouldEnterPushedState(event) &&
button_->HitTestPoint(event.location())) {
button_->SetState(Button::STATE_PRESSED);
InkDrop::Get(button()->ink_drop_view())
->AnimateToState(views::InkDropState::ACTION_PENDING, &event);
}
button_controller_delegate_->RequestFocusFromEvent();
if (button_controller_delegate_->IsTriggerableEvent(event) &&
notify_action_ == ButtonController::NotifyAction::kOnPress) {
button_controller_delegate_->NotifyClick(event);
// NOTE: We may be deleted at this point (by the listener's notification
// handler).
}
return true;
}
void ButtonController::OnMouseReleased(const ui::MouseEvent& event) {
if (button_->GetState() != Button::STATE_DISABLED) {
if (!button_->HitTestPoint(event.location())) {
button_->SetState(Button::STATE_NORMAL);
} else {
button_->SetState(Button::STATE_HOVERED);
if (button_controller_delegate_->IsTriggerableEvent(event) &&
notify_action_ == ButtonController::NotifyAction::kOnRelease) {
button_controller_delegate_->NotifyClick(event);
// NOTE: We may be deleted at this point (by the listener's notification
// handler).
return;
}
}
}
if (notify_action_ == ButtonController::NotifyAction::kOnRelease) {
button_controller_delegate_->OnClickCanceled(event);
}
}
void ButtonController::OnMouseMoved(const ui::MouseEvent& event) {
if (button_->GetState() != Button::STATE_DISABLED) {
button_->SetState(button_->HitTestPoint(event.location())
? Button::STATE_HOVERED
: Button::STATE_NORMAL);
}
}
void ButtonController::OnMouseEntered(const ui::MouseEvent& event) {
if (button_->GetState() != Button::STATE_DISABLED) {
button_->SetState(Button::STATE_HOVERED);
}
}
void ButtonController::OnMouseExited(const ui::MouseEvent& event) {
// Starting a drag results in a MouseExited, we need to ignore it.
if (button_->GetState() != Button::STATE_DISABLED &&
!button_controller_delegate_->InDrag()) {
button_->SetState(Button::STATE_NORMAL);
}
}
bool ButtonController::OnKeyPressed(const ui::KeyEvent& event) {
if (button_->GetState() == Button::STATE_DISABLED) {
return false;
}
switch (button_->GetKeyClickActionForEvent(event)) {
case Button::KeyClickAction::kOnKeyRelease:
button_->SetState(Button::STATE_PRESSED);
if (button_controller_delegate_->GetInkDrop()->GetTargetInkDropState() !=
InkDropState::ACTION_PENDING) {
InkDrop::Get(button()->ink_drop_view())
->AnimateToState(InkDropState::ACTION_PENDING, nullptr /* event */);
}
return true;
case Button::KeyClickAction::kOnKeyPress:
button_->SetState(Button::STATE_NORMAL);
button_controller_delegate_->NotifyClick(event);
return true;
case Button::KeyClickAction::kNone:
return false;
}
NOTREACHED();
}
bool ButtonController::OnKeyReleased(const ui::KeyEvent& event) {
const bool click_button = button_->GetState() == Button::STATE_PRESSED &&
button_->GetKeyClickActionForEvent(event) ==
Button::KeyClickAction::kOnKeyRelease;
if (!click_button) {
return false;
}
button_->SetState(Button::STATE_NORMAL);
button_controller_delegate_->NotifyClick(event);
return true;
}
void ButtonController::OnGestureEvent(ui::GestureEvent* event) {
if (button_->GetState() == Button::STATE_DISABLED) {
return;
}
if (event->type() == ui::EventType::kGestureTap &&
button_controller_delegate_->IsTriggerableEvent(*event)) {
// A GESTURE_END event is issued immediately after EventType::kGestureTap
// and will set the state to STATE_NORMAL beginning the fade out animation.
button_->SetState(Button::STATE_HOVERED);
button_controller_delegate_->NotifyClick(*event);
event->SetHandled();
} else if (event->type() == ui::EventType::kGestureTapDown &&
button_controller_delegate_->ShouldEnterPushedState(*event)) {
button_->SetState(Button::STATE_PRESSED);
button_controller_delegate_->RequestFocusFromEvent();
event->SetHandled();
} else if (event->type() == ui::EventType::kGestureTapCancel ||
event->type() == ui::EventType::kGestureEnd) {
button_->SetState(Button::STATE_NORMAL);
}
}
void ButtonController::NotifyClick() {
ui::KeyEvent fake_event(ui::EventType::kKeyPressed, ui::VKEY_SPACE,
ui::EF_IS_SYNTHESIZED);
button_controller_delegate_->NotifyClick(fake_event);
}
void ButtonController::UpdateButtonAccessibleDefaultActionVerb() {}
bool ButtonController::IsTriggerableEvent(const ui::Event& event) {
return event.type() == ui::EventType::kGestureTapDown ||
event.type() == ui::EventType::kGestureTap ||
(event.IsMouseEvent() &&
(button_->GetTriggerableEventFlags() & event.flags()) != 0);
}
} // namespace views