#include "ash/events/select_to_speak_event_handler.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/accessibility/accessibility_event_handler_manager.h"
#include "ash/public/cpp/select_to_speak_event_handler_delegate.h"
#include "ash/shell.h"
#include "base/containers/contains.h"
#include "ui/events/types/event_type.h"
namespace ash {
const ui::KeyboardCode kSpeakSelectionKey = ui::VKEY_S;
SelectToSpeakEventHandler::SelectToSpeakEventHandler(
SelectToSpeakEventHandlerDelegate* delegate)
: delegate_(delegate) {
DCHECK(delegate_);
Shell::Get()->AddAccessibilityEventHandler(
this, AccessibilityEventHandlerManager::HandlerType::kSelectToSpeak);
}
SelectToSpeakEventHandler::~SelectToSpeakEventHandler() {
Shell::Get()->RemoveAccessibilityEventHandler(this);
}
bool SelectToSpeakEventHandler::IsSelectToSpeakEnabled() {
return Shell::Get()->accessibility_controller()->select_to_speak().enabled();
}
void SelectToSpeakEventHandler::SetSelectToSpeakStateSelecting(
bool is_selecting) {
if (is_selecting && state_ == INACTIVE) {
state_ = SELECTION_REQUESTED;
} else if (!is_selecting) {
if (state_ != MOUSE_RELEASED)
state_ = INACTIVE;
touch_id_ = ui::kPointerIdUnknown;
touch_type_ = ui::EventPointerType::kUnknown;
}
}
bool SelectToSpeakEventHandler::IsKeyDownForTesting(
ui::KeyboardCode code) const {
return keys_currently_down_.contains(code);
}
void SelectToSpeakEventHandler::OnKeyEvent(ui::KeyEvent* event) {
DCHECK(IsSelectToSpeakEnabled());
DCHECK(event);
bool pressed = event->type() == ui::EventType::kKeyPressed;
bool released = event->type() == ui::EventType::kKeyReleased;
if (!(pressed || released)) {
return;
}
ui::KeyboardCode key_code = event->key_code();
if (key_code != kSpeakSelectionKey && key_code != ui::VKEY_LWIN &&
key_code != ui::VKEY_RWIN && key_code != ui::VKEY_CONTROL) {
if (state_ == SEARCH_DOWN) {
state_ = INACTIVE;
}
return;
}
if (pressed) {
keys_currently_down_.insert(key_code);
} else {
DCHECK(base::Contains(keys_currently_down_, key_code));
keys_currently_down_.erase(key_code);
}
bool cancel_event = false;
if (key_code == ui::VKEY_LWIN || key_code == ui::VKEY_RWIN) {
if (pressed && state_ == INACTIVE) {
state_ = SEARCH_DOWN;
} else if (event->type() == ui::EventType::kKeyReleased) {
if (state_ == CAPTURING_MOUSE) {
cancel_event = true;
state_ = WAIT_FOR_MOUSE_RELEASE;
} else if (state_ == MOUSE_RELEASED) {
cancel_event = true;
state_ = INACTIVE;
} else if (state_ == CAPTURING_SPEAK_SELECTION_KEY) {
cancel_event = true;
state_ = WAIT_FOR_SPEAK_SELECTION_KEY_RELEASE;
} else if (state_ == SPEAK_SELECTION_KEY_RELEASED) {
cancel_event = true;
state_ = INACTIVE;
} else if (state_ == SEARCH_DOWN) {
state_ = INACTIVE;
}
}
} else if (key_code == kSpeakSelectionKey) {
if (pressed &&
(state_ == SEARCH_DOWN || state_ == SPEAK_SELECTION_KEY_RELEASED)) {
cancel_event = true;
state_ = CAPTURING_SPEAK_SELECTION_KEY;
} else if (event->type() == ui::EventType::kKeyReleased) {
if (state_ == CAPTURING_SPEAK_SELECTION_KEY) {
cancel_event = true;
state_ = SPEAK_SELECTION_KEY_RELEASED;
} else if (state_ == WAIT_FOR_SPEAK_SELECTION_KEY_RELEASE) {
cancel_event = true;
state_ = INACTIVE;
}
}
} else if (state_ == SEARCH_DOWN) {
state_ = INACTIVE;
}
delegate_->DispatchKeysCurrentlyDown(keys_currently_down_);
if (cancel_event)
CancelEvent(event);
}
void SelectToSpeakEventHandler::OnMouseEvent(ui::MouseEvent* event) {
DCHECK(IsSelectToSpeakEnabled());
DCHECK(event);
if (state_ == INACTIVE)
return;
if (event->type() == ui::EventType::kMousePressed) {
if (state_ == SEARCH_DOWN || state_ == MOUSE_RELEASED)
state_ = CAPTURING_MOUSE;
else if (state_ == SELECTION_REQUESTED)
state_ = CAPTURING_MOUSE_ONLY;
}
if (state_ == SELECTION_REQUESTED || state_ == SEARCH_DOWN) {
CancelEvent(event);
return;
}
if (state_ == WAIT_FOR_MOUSE_RELEASE &&
event->type() == ui::EventType::kMouseReleased) {
state_ = INACTIVE;
return;
}
if (state_ != CAPTURING_MOUSE && state_ != CAPTURING_MOUSE_ONLY)
return;
if (event->type() == ui::EventType::kMouseReleased) {
if (state_ == CAPTURING_MOUSE)
state_ = MOUSE_RELEASED;
else if (state_ == CAPTURING_MOUSE_ONLY)
state_ = INACTIVE;
}
delegate_->DispatchMouseEvent(*event);
if (event->type() == ui::EventType::kMousePressed ||
event->type() == ui::EventType::kMouseReleased ||
event->type() == ui::EventType::kMouseDragged) {
CancelEvent(event);
}
}
void SelectToSpeakEventHandler::OnTouchEvent(ui::TouchEvent* event) {
DCHECK(IsSelectToSpeakEnabled());
DCHECK(event);
if (state_ != SELECTION_REQUESTED && state_ != CAPTURING_TOUCH_ONLY)
return;
if (event->type() == ui::EventType::kTouchPressed &&
state_ == SELECTION_REQUESTED && touch_id_ == ui::kPointerIdUnknown) {
state_ = CAPTURING_TOUCH_ONLY;
touch_id_ = event->pointer_details().id;
touch_type_ = event->pointer_details().pointer_type;
}
if (touch_id_ != event->pointer_details().id ||
touch_type_ != event->pointer_details().pointer_type) {
CancelEvent(event);
return;
}
if (event->type() == ui::EventType::kTouchReleased &&
state_ == CAPTURING_TOUCH_ONLY) {
state_ = INACTIVE;
touch_id_ = ui::kPointerIdUnknown;
touch_type_ = ui::EventPointerType::kUnknown;
}
ui::EventType type;
switch (event->type()) {
case ui::EventType::kTouchPressed:
type = ui::EventType::kMousePressed;
break;
case ui::EventType::kTouchReleased:
case ui::EventType::kTouchCancelled:
type = ui::EventType::kMouseReleased;
break;
case ui::EventType::kTouchMoved:
type = ui::EventType::kMouseDragged;
break;
default:
return;
}
int flags = ui::EF_LEFT_MOUSE_BUTTON;
gfx::Point root_location = event->target()
? event->target()->GetScreenLocation(*event)
: event->root_location();
ui::MouseEvent event_to_send(type, event->location(), root_location,
event->time_stamp(), flags, flags);
delegate_->DispatchMouseEvent(event_to_send);
if (event->type() != ui::EventType::kTouchMoved) {
CancelEvent(event);
}
}
void SelectToSpeakEventHandler::CancelEvent(ui::Event* event) {
DCHECK(event);
if (event->cancelable()) {
event->SetHandled();
event->StopPropagation();
}
}
std::string_view SelectToSpeakEventHandler::GetLogContext() const {
return "SelectToSpeakEventHandler";
}
}