blob: ce690d4828bf26f78b59e7208d234cc657b0d048 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/quick_insert/quick_insert_caps_lock_bubble_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/quick_insert/views/quick_insert_caps_lock_state_view.h"
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/i18n/rtl.h"
#include "base/location.h"
#include "base/time/time.h"
#include "ui/aura/window.h"
#include "ui/base/ime/ash/ime_bridge.h"
#include "ui/base/ime/ash/ime_keyboard.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_ui_types.h"
namespace ash {
namespace {
constexpr base::TimeDelta kBubbleViewDisplayTime = base::Seconds(2);
// Starting at the time the bubble is shown, events that would normally close
// the bubble are ignored for this grace period, to prevent the bubble from
// being closed immediately.
constexpr base::TimeDelta kIgnoreCloseBubbleEventsDuration =
base::Milliseconds(100);
gfx::NativeView GetParentView() {
aura::Window* active_window = window_util::GetActiveWindow();
return Shell::GetContainer(active_window
? active_window->GetRootWindow()
: Shell::GetRootWindowForNewWindows(),
kShellWindowId_SettingBubbleContainer);
}
ui::TextInputClient* GetFocusedTextInputClient() {
const ui::InputMethod* input_method =
IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
if (!input_method || !input_method->GetTextInputClient()) {
return nullptr;
}
return input_method->GetTextInputClient();
}
// Gets the current caret bounds in universal screen coordinates in DIP. Returns
// an empty rect if there is no active caret or the caret bounds can't be
// determined (e.g. no focused input field).
gfx::Rect GetCaretBounds() {
if (ui::TextInputClient* client = GetFocusedTextInputClient()) {
return client->GetCaretBounds();
}
return gfx::Rect();
}
bool ShouldCloseBubbleOnEvent(ui::Event* event) {
switch (event->type()) {
case ui::EventType::kMousePressed:
case ui::EventType::kTouchPressed:
case ui::EventType::kKeyPressed:
return true;
default:
return false;
}
}
// Gets the text direction for the currently focused input field.
base::i18n::TextDirection GetTextDirection() {
if (ui::TextInputClient* client = GetFocusedTextInputClient()) {
return client->GetTextDirection();
}
return base::i18n::TextDirection::UNKNOWN_DIRECTION;
}
} // namespace
QuickInsertCapsLockBubbleController::QuickInsertCapsLockBubbleController(
input_method::ImeKeyboard* keyboard) {
ime_keyboard_observation_.Observe(keyboard);
Shell::Get()->AddPreTargetHandler(this);
}
QuickInsertCapsLockBubbleController::~QuickInsertCapsLockBubbleController() {
Shell::Get()->RemovePreTargetHandler(this);
// Close the bubble if it's open to avoid a dangling pointer.
CloseBubble();
}
void QuickInsertCapsLockBubbleController::OnMouseEvent(ui::MouseEvent* event) {
MaybeCloseBubbleByEvent(event);
}
void QuickInsertCapsLockBubbleController::OnTouchEvent(ui::TouchEvent* event) {
MaybeCloseBubbleByEvent(event);
}
void QuickInsertCapsLockBubbleController::OnKeyEvent(ui::KeyEvent* event) {
MaybeCloseBubbleByEvent(event);
}
void QuickInsertCapsLockBubbleController::CloseBubble() {
bubble_close_timer_.Stop();
if (bubble_view_ != nullptr) {
bubble_view_->Close();
bubble_view_ = nullptr;
}
}
void QuickInsertCapsLockBubbleController::OnCapsLockChanged(bool enabled) {
CloseBubble();
if (GetFocusedTextInputClient() == nullptr) {
return;
}
bubble_view_ = new QuickInsertCapsLockStateView(
GetParentView(), enabled, GetCaretBounds(), GetTextDirection());
bubble_view_->Show();
bubble_close_timer_.Start(
FROM_HERE, kBubbleViewDisplayTime,
base::BindOnce(&QuickInsertCapsLockBubbleController::CloseBubble,
weak_ptr_factory_.GetWeakPtr()));
last_shown_ = base::TimeTicks::Now();
}
void QuickInsertCapsLockBubbleController::OnLayoutChanging(
const std::string& layout_name) {}
void QuickInsertCapsLockBubbleController::MaybeCloseBubbleByEvent(
ui::Event* event) {
if (ShouldCloseBubbleOnEvent(event) &&
base::TimeTicks::Now() - last_shown_ > kIgnoreCloseBubbleEventsDuration) {
CloseBubble();
}
}
} // namespace ash