blob: 03323e5019d475ef4ac422006ec45c2a78172e65 [file] [log] [blame]
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/input_method/assistive_window_controller.h"
#include <string>
#include <vector>
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "chrome/browser/chromeos/input_method/assistive_window_controller_delegate.h"
#include "chrome/browser/chromeos/input_method/assistive_window_properties.h"
#include "chrome/browser/chromeos/input_method/ui/suggestion_details.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/ime/chromeos/ime_bridge.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/widget/widget.h"
namespace chromeos {
namespace input_method {
namespace {
gfx::NativeView GetParentView() {
gfx::NativeView parent = nullptr;
aura::Window* active_window = ash::window_util::GetActiveWindow();
// Use VirtualKeyboardContainer so that it works even with a system modal
// dialog.
parent = ash::Shell::GetContainer(
active_window ? active_window->GetRootWindow()
: ash::Shell::GetRootWindowForNewWindows(),
ash::kShellWindowId_VirtualKeyboardContainer);
return parent;
}
// Delay messages when window is shown until after normal ChromeVox
// announcements are done.
constexpr base::TimeDelta kTtsShowDelay =
base::TimeDelta::FromMilliseconds(1200);
} // namespace
AssistiveWindowController::AssistiveWindowController(
AssistiveWindowControllerDelegate* delegate,
Profile* profile,
std::unique_ptr<TtsHandler> tts_handler)
: delegate_(delegate),
tts_handler_(tts_handler ? std::move(tts_handler)
: std::make_unique<TtsHandler>(profile)) {}
AssistiveWindowController::~AssistiveWindowController() {
if (suggestion_window_view_ && suggestion_window_view_->GetWidget())
suggestion_window_view_->GetWidget()->RemoveObserver(this);
if (undo_window_ && undo_window_->GetWidget())
undo_window_->GetWidget()->RemoveObserver(this);
CHECK(!IsInObserverList());
}
void AssistiveWindowController::InitSuggestionWindow() {
if (suggestion_window_view_)
return;
// suggestion_window_view_ is deleted by DialogDelegateView::DeleteDelegate.
suggestion_window_view_ =
ui::ime::SuggestionWindowView::Create(GetParentView(), this);
views::Widget* widget = suggestion_window_view_->GetWidget();
widget->AddObserver(this);
widget->Show();
}
void AssistiveWindowController::InitUndoWindow() {
if (undo_window_)
return;
// undo_window is deleted by DialogDelegateView::DeleteDelegate.
undo_window_ = new ui::ime::UndoWindow(GetParentView(), this);
views::Widget* widget = undo_window_->InitWidget();
widget->AddObserver(this);
widget->Show();
}
void AssistiveWindowController::OnWidgetClosing(views::Widget* widget) {
if (suggestion_window_view_ &&
widget == suggestion_window_view_->GetWidget()) {
widget->RemoveObserver(this);
suggestion_window_view_ = nullptr;
}
if (undo_window_ && widget == undo_window_->GetWidget()) {
widget->RemoveObserver(this);
undo_window_ = nullptr;
}
}
// TODO(crbug/1119570): Update AcceptSuggestion signature (either use
// announce_string, or no string)
void AssistiveWindowController::AcceptSuggestion(
const std::u16string& suggestion) {
if (window_.type == ui::ime::AssistiveWindowType::kEmojiSuggestion) {
tts_handler_->Announce(
l10n_util::GetStringUTF8(IDS_SUGGESTION_EMOJI_SUGGESTED));
} else {
tts_handler_->Announce(l10n_util::GetStringUTF8(IDS_SUGGESTION_INSERTED));
}
HideSuggestion();
}
void AssistiveWindowController::HideSuggestion() {
suggestion_text_ = base::EmptyString16();
confirmed_length_ = 0;
if (suggestion_window_view_)
suggestion_window_view_->GetWidget()->Close();
}
void AssistiveWindowController::SetBounds(const Bounds& bounds) {
bounds_ = bounds;
// Sets suggestion_window_view_'s bounds here for most up-to-date cursor
// position. This is different from UndoWindow because UndoWindow gets cursors
// position before showing.
// TODO(crbug/1112982): Investigate getting bounds to suggester before sending
// show suggestion request.
if (suggestion_window_view_ && confirmed_length_ == 0)
suggestion_window_view_->SetAnchorRect(bounds.caret);
}
void AssistiveWindowController::FocusStateChanged() {
if (suggestion_window_view_)
HideSuggestion();
if (undo_window_)
undo_window_->Hide();
}
void AssistiveWindowController::ShowSuggestion(
const ui::ime::SuggestionDetails& details) {
if (!suggestion_window_view_)
InitSuggestionWindow();
suggestion_text_ = details.text;
confirmed_length_ = details.confirmed_length;
suggestion_window_view_->Show(details);
}
void AssistiveWindowController::SetButtonHighlighted(
const ui::ime::AssistiveWindowButton& button,
bool highlighted) {
switch (button.window_type) {
case ui::ime::AssistiveWindowType::kEmojiSuggestion:
case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion:
if (!suggestion_window_view_)
return;
suggestion_window_view_->SetButtonHighlighted(button, highlighted);
if (highlighted)
tts_handler_->Announce(button.announce_string);
break;
case ui::ime::AssistiveWindowType::kUndoWindow:
if (!undo_window_)
return;
undo_window_->SetButtonHighlighted(button, highlighted);
tts_handler_->Announce(button.announce_string);
break;
case ui::ime::AssistiveWindowType::kNone:
break;
}
}
std::u16string AssistiveWindowController::GetSuggestionText() const {
return suggestion_text_;
}
size_t AssistiveWindowController::GetConfirmedLength() const {
return confirmed_length_;
}
void AssistiveWindowController::SetAssistiveWindowProperties(
const AssistiveWindowProperties& window) {
window_ = window;
switch (window.type) {
case ui::ime::AssistiveWindowType::kUndoWindow:
if (!undo_window_)
InitUndoWindow();
if (window.visible) {
undo_window_->SetAnchorRect(bounds_.autocorrect.IsEmpty()
? bounds_.caret
: bounds_.autocorrect);
undo_window_->Show();
} else {
undo_window_->Hide();
}
break;
case ui::ime::AssistiveWindowType::kEmojiSuggestion:
case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion:
if (!suggestion_window_view_)
InitSuggestionWindow();
if (window_.visible) {
suggestion_window_view_->ShowMultipleCandidates(window);
} else {
HideSuggestion();
}
break;
case ui::ime::AssistiveWindowType::kNone:
break;
}
tts_handler_->Announce(window.announce_string, kTtsShowDelay);
}
void AssistiveWindowController::AssistiveWindowButtonClicked(
const ui::ime::AssistiveWindowButton& button) const {
delegate_->AssistiveWindowButtonClicked(button);
}
ui::ime::SuggestionWindowView*
AssistiveWindowController::GetSuggestionWindowViewForTesting() {
return suggestion_window_view_;
}
ui::ime::UndoWindow* AssistiveWindowController::GetUndoWindowForTesting()
const {
return undo_window_;
}
} // namespace input_method
} // namespace chromeos