910e62b5创建于 1月15日历史提交
// 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