[ozone/wayland] Text input refactors part 3: Single instance
Make ZwpTextInputV1 and ZwpTextInputV3 single instances per WaylandConnection, shared by multiple per-window WaylandInputMethodContext instances. Introduce new text-input focus derived from text-input-v3 enter/leave for: - Ensuring the correct WaylandInputMethodContext is set as the v3 client. - Determining window focus if keyboard focus isn't available. Remove `WaylandWindowObserver::OnKeyboardFocusedWindowChanged()` and add `WaylandWindow::FocusClient` instead for notifying keyboard and text input focus changes specifically for that window. This makes it possible for the WaylandWindow to keep a reference to the associated WaylandInputMethodContext and directly notify focus changes to it. Bug: 414284073 Change-Id: Id8ced1a690c759107983a5c1cb71195f9556a1d7 Fixed: 368299543, 369574355, 372650991 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6496390 Reviewed-by: Kramer Ge <fangzhoug@chromium.org> Reviewed-by: Thomas Anderson <thomasanderson@chromium.org> Commit-Queue: Orko Garai <orko@igalia.com> Cr-Commit-Position: refs/heads/main@{#1455466}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
169446238b
commit
d6678747e4
ui
base
ime
ozone
platform
wayland
host
wayland_connection.ccwayland_connection.hwayland_input_method_context.ccwayland_input_method_context.hwayland_input_method_context_unittest.ccwayland_toplevel_window.ccwayland_window.ccwayland_window.hwayland_window_manager.ccwayland_window_manager.hwayland_window_observer.ccwayland_window_observer.hzwp_text_input_v3.cczwp_text_input_v3_unittest.cc
ozone_platform_wayland.ccx11
@ -43,8 +43,11 @@ bool IsSameKeyEvent(const ui::KeyEvent& lhs, const ui::KeyEvent& rhs) {
|
||||
namespace ui {
|
||||
|
||||
InputMethodAuraLinux::InputMethodAuraLinux(
|
||||
ImeKeyEventDispatcher* ime_key_event_dispatcher)
|
||||
|
||||
ImeKeyEventDispatcher* ime_key_event_dispatcher,
|
||||
gfx::AcceleratedWidget widget)
|
||||
: InputMethodBase(ime_key_event_dispatcher),
|
||||
widget_(widget),
|
||||
text_input_type_(TEXT_INPUT_TYPE_NONE),
|
||||
is_sync_mode_(false),
|
||||
composition_changed_(false) {
|
||||
@ -457,6 +460,10 @@ InputMethodAuraLinux::GetVirtualKeyboardController() {
|
||||
|
||||
// Overriden from ui::LinuxInputMethodContextDelegate
|
||||
|
||||
gfx::AcceleratedWidget InputMethodAuraLinux::GetClientWindowKey() const {
|
||||
return widget_;
|
||||
}
|
||||
|
||||
void InputMethodAuraLinux::OnCommit(const std::u16string& text) {
|
||||
if (IgnoringNonKeyInput() || !GetTextInputClient())
|
||||
return;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "ui/base/ime/composition_text.h"
|
||||
#include "ui/base/ime/input_method_base.h"
|
||||
#include "ui/base/ime/linux/linux_input_method_context.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -22,15 +23,15 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) InputMethodAuraLinux
|
||||
: public InputMethodBase,
|
||||
public LinuxInputMethodContextDelegate {
|
||||
public:
|
||||
explicit InputMethodAuraLinux(
|
||||
ImeKeyEventDispatcher* ime_key_event_dispatcher);
|
||||
explicit InputMethodAuraLinux(ImeKeyEventDispatcher* ime_key_event_dispatcher,
|
||||
gfx::AcceleratedWidget widget);
|
||||
InputMethodAuraLinux(const InputMethodAuraLinux&) = delete;
|
||||
InputMethodAuraLinux& operator=(const InputMethodAuraLinux&) = delete;
|
||||
~InputMethodAuraLinux() override;
|
||||
|
||||
LinuxInputMethodContext* GetContextForTesting();
|
||||
|
||||
// Overriden from InputMethod.
|
||||
// Overridden from InputMethod.
|
||||
ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override;
|
||||
void OnTextInputTypeChanged(TextInputClient* client) override;
|
||||
void OnCaretBoundsChanged(const TextInputClient* client) override;
|
||||
@ -38,7 +39,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) InputMethodAuraLinux
|
||||
bool IsCandidatePopupOpen() const override;
|
||||
VirtualKeyboardController* GetVirtualKeyboardController() override;
|
||||
|
||||
// Overriden from ui::LinuxInputMethodContextDelegate
|
||||
// Overridden from ui::LinuxInputMethodContextDelegate
|
||||
gfx::AcceleratedWidget GetClientWindowKey() const override;
|
||||
void OnCommit(const std::u16string& text) override;
|
||||
void OnConfirmCompositionText(bool keep_selection) override;
|
||||
void OnDeleteSurroundingText(size_t before, size_t after) override;
|
||||
@ -87,6 +89,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) InputMethodAuraLinux
|
||||
void ResetContext();
|
||||
bool IgnoringNonKeyInput() const;
|
||||
|
||||
const gfx::AcceleratedWidget widget_;
|
||||
|
||||
std::unique_ptr<LinuxInputMethodContext> context_;
|
||||
|
||||
// The last key event that IME is probably in process in
|
||||
|
@ -350,8 +350,8 @@ class InputMethodAuraLinuxTest : public testing::Test {
|
||||
|
||||
void SetUp() override {
|
||||
delegate_ = std::make_unique<InputMethodDelegateForTesting>();
|
||||
input_method_auralinux_ =
|
||||
std::make_unique<InputMethodAuraLinux>(delegate_.get());
|
||||
input_method_auralinux_ = std::make_unique<InputMethodAuraLinux>(
|
||||
delegate_.get(), gfx::kNullAcceleratedWidget);
|
||||
input_method_auralinux_->OnFocus();
|
||||
context_ = static_cast<LinuxInputMethodContextForTesting*>(
|
||||
input_method_auralinux_->GetContextForTesting());
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "ui/base/ime/text_input_mode.h"
|
||||
#include "ui/base/ime/text_input_type.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
#include "ui/gfx/range/range.h"
|
||||
|
||||
namespace gfx {
|
||||
@ -86,6 +87,9 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) LinuxInputMethodContextDelegate {
|
||||
public:
|
||||
virtual ~LinuxInputMethodContextDelegate() {}
|
||||
|
||||
// Returns the key for the window containing the input method.
|
||||
virtual gfx::AcceleratedWidget GetClientWindowKey() const = 0;
|
||||
|
||||
// Commits the |text| to the text input client.
|
||||
virtual void OnCommit(const std::u16string& text) = 0;
|
||||
|
||||
|
@ -64,6 +64,8 @@
|
||||
#include "ui/ozone/platform/wayland/host/xdg_session_manager.h"
|
||||
#include "ui/ozone/platform/wayland/host/zwp_idle_inhibit_manager.h"
|
||||
#include "ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h"
|
||||
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
|
||||
#include "ui/ozone/platform/wayland/host/zwp_text_input_v3.h"
|
||||
#include "ui/platform_window/common/platform_window_defaults.h"
|
||||
|
||||
namespace ui {
|
||||
@ -366,6 +368,32 @@ std::vector<KeyboardDevice> WaylandConnection::CreateKeyboardDevices() const {
|
||||
return devices;
|
||||
}
|
||||
|
||||
ZwpTextInputV1* WaylandConnection::EnsureTextInputV1() {
|
||||
if (text_input_v1_) {
|
||||
return text_input_v1_.get();
|
||||
}
|
||||
if (text_input_manager_v1_) {
|
||||
text_input_v1_ = std::make_unique<ZwpTextInputV1Impl>(
|
||||
this, text_input_manager_v1_.get());
|
||||
} else {
|
||||
LOG(WARNING) << "text-input-v1 not available.";
|
||||
}
|
||||
return text_input_v1_.get();
|
||||
}
|
||||
|
||||
ZwpTextInputV3* WaylandConnection::EnsureTextInputV3() {
|
||||
if (text_input_v3_) {
|
||||
return text_input_v3_.get();
|
||||
}
|
||||
if (text_input_manager_v3_) {
|
||||
text_input_v3_ = std::make_unique<ZwpTextInputV3Impl>(
|
||||
this, text_input_manager_v3_.get());
|
||||
} else {
|
||||
LOG(WARNING) << "text-input-v3 not available.";
|
||||
}
|
||||
return text_input_v3_.get();
|
||||
}
|
||||
|
||||
std::vector<TouchscreenDevice> WaylandConnection::CreateTouchscreenDevices()
|
||||
const {
|
||||
std::vector<TouchscreenDevice> devices;
|
||||
|
@ -67,6 +67,8 @@ class XdgForeignWrapper;
|
||||
class XdgSessionManager;
|
||||
class ZwpIdleInhibitManager;
|
||||
class ZwpPrimarySelectionDeviceManager;
|
||||
class ZwpTextInputV1;
|
||||
class ZwpTextInputV3;
|
||||
|
||||
class WaylandConnection {
|
||||
public:
|
||||
@ -112,9 +114,6 @@ class WaylandConnection {
|
||||
zwp_text_input_manager_v1* text_input_manager_v1() const {
|
||||
return text_input_manager_v1_.get();
|
||||
}
|
||||
zwp_text_input_manager_v3* text_input_manager_v3() const {
|
||||
return text_input_manager_v3_.get();
|
||||
}
|
||||
zwp_linux_explicit_synchronization_v1* linux_explicit_synchronization_v1()
|
||||
const {
|
||||
return linux_explicit_synchronization_.get();
|
||||
@ -207,6 +206,10 @@ class WaylandConnection {
|
||||
return zwp_primary_selection_device_manager_.get();
|
||||
}
|
||||
|
||||
ZwpTextInputV1* EnsureTextInputV1();
|
||||
ZwpTextInputV3* EnsureTextInputV3();
|
||||
bool SupportsTextInputFocus() const { return !!text_input_v3_; }
|
||||
|
||||
WaylandDataDragController* data_drag_controller() const {
|
||||
return data_drag_controller_.get();
|
||||
}
|
||||
@ -463,6 +466,8 @@ class WaylandConnection {
|
||||
gtk_primary_selection_device_manager_;
|
||||
std::unique_ptr<ZwpPrimarySelectionDeviceManager>
|
||||
zwp_primary_selection_device_manager_;
|
||||
std::unique_ptr<ZwpTextInputV1> text_input_v1_;
|
||||
std::unique_ptr<ZwpTextInputV3> text_input_v3_;
|
||||
std::unique_ptr<WaylandClipboard> clipboard_;
|
||||
|
||||
// Objects specific to KDE Plasma desktop environment.
|
||||
|
@ -246,9 +246,12 @@ WaylandInputMethodContext::WaylandInputMethodContext(
|
||||
: connection_(connection),
|
||||
key_delegate_(key_delegate),
|
||||
ime_delegate_(ime_delegate),
|
||||
window_(connection_->window_manager()
|
||||
->GetWindow(ime_delegate_->GetClientWindowKey())
|
||||
->AsWeakPtr()),
|
||||
text_input_v1_(nullptr),
|
||||
character_composer_(kPreeditStringMode) {
|
||||
connection_->window_manager()->AddObserver(this);
|
||||
window_->set_focus_client(this);
|
||||
Init();
|
||||
}
|
||||
|
||||
@ -259,7 +262,9 @@ WaylandInputMethodContext::~WaylandInputMethodContext() {
|
||||
DismissVirtualKeyboard();
|
||||
text_input_v1_->OnClientDestroyed(text_input_v1_client_.get());
|
||||
}
|
||||
connection_->window_manager()->RemoveObserver(this);
|
||||
if (window_) {
|
||||
window_->set_focus_client(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandInputMethodContext::CreateTextInput() {
|
||||
@ -277,12 +282,7 @@ void WaylandInputMethodContext::CreateTextInput() {
|
||||
|
||||
if (enable_using_cmd_line_version &&
|
||||
version_from_cmd_line == kWaylandTextInputVersion1) {
|
||||
if (!connection_->text_input_manager_v1()) {
|
||||
LOG(WARNING) << "text-input-v1 is not supported by the compositor.";
|
||||
return;
|
||||
}
|
||||
text_input_v1_ = std::make_unique<ZwpTextInputV1Impl>(
|
||||
connection_, connection_->text_input_manager_v1());
|
||||
text_input_v1_ = connection_->EnsureTextInputV1();
|
||||
text_input_v1_client_ =
|
||||
std::make_unique<WaylandInputMethodContextV1Client>(this);
|
||||
} else if (base::FeatureList::IsEnabled(features::kWaylandTextInputV3) ||
|
||||
@ -294,12 +294,7 @@ void WaylandInputMethodContext::CreateTextInput() {
|
||||
"--enable-wayland-ime should be present. Defaulting to "
|
||||
"text-input-v3.";
|
||||
}
|
||||
if (!connection_->text_input_manager_v3()) {
|
||||
LOG(WARNING) << "text-input-v3 is not supported by the compositor.";
|
||||
return;
|
||||
}
|
||||
text_input_v3_ = std::make_unique<ZwpTextInputV3Impl>(
|
||||
connection_, connection_->text_input_manager_v3());
|
||||
text_input_v3_ = connection_->EnsureTextInputV3();
|
||||
text_input_v3_client_ =
|
||||
std::make_unique<WaylandInputmethodContextV3Client>(this);
|
||||
}
|
||||
@ -320,7 +315,7 @@ void WaylandInputMethodContext::Init() {
|
||||
}
|
||||
|
||||
void WaylandInputMethodContext::SetTextInputV1ForTesting(
|
||||
std::unique_ptr<ZwpTextInputV1> text_input_v1) {
|
||||
ZwpTextInputV1* text_input_v1) {
|
||||
text_input_v1_ = std::move(text_input_v1);
|
||||
if (!text_input_v1_client_) {
|
||||
text_input_v1_client_ =
|
||||
@ -331,7 +326,7 @@ void WaylandInputMethodContext::SetTextInputV1ForTesting(
|
||||
}
|
||||
|
||||
void WaylandInputMethodContext::SetTextInputV3ForTesting(
|
||||
std::unique_ptr<ZwpTextInputV3> text_input_v3) {
|
||||
ZwpTextInputV3* text_input_v3) {
|
||||
text_input_v3_ = std::move(text_input_v3);
|
||||
if (!text_input_v3_client_) {
|
||||
text_input_v3_client_ =
|
||||
@ -438,7 +433,9 @@ void WaylandInputMethodContext::SetCursorLocation(const gfx::Rect& rect) {
|
||||
return;
|
||||
}
|
||||
WaylandWindow* focused_window =
|
||||
connection_->window_manager()->GetCurrentKeyboardFocusedWindow();
|
||||
text_input_v3_
|
||||
? connection_->window_manager()->GetCurrentTextInputFocusedWindow()
|
||||
: connection_->window_manager()->GetCurrentKeyboardFocusedWindow();
|
||||
if (!focused_window) {
|
||||
return;
|
||||
}
|
||||
@ -818,32 +815,46 @@ void WaylandInputMethodContext::OnModifiersMap(
|
||||
modifiers_map_ = std::move(modifiers_map);
|
||||
}
|
||||
|
||||
void WaylandInputMethodContext::OnKeyboardFocusedWindowChanged() {
|
||||
void WaylandInputMethodContext::OnTextInputFocusChanged(bool focused) {
|
||||
CHECK(text_input_v3_);
|
||||
window_focused_ = focused;
|
||||
MaybeUpdateActivated(false);
|
||||
}
|
||||
|
||||
void WaylandInputMethodContext::OnKeyboardFocusChanged(bool focused) {
|
||||
if (text_input_v3_) {
|
||||
// For text-input-v3, zwp_text_input_l3::{enter,leave} is used instead.
|
||||
return;
|
||||
}
|
||||
window_focused_ = focused;
|
||||
MaybeUpdateActivated(false);
|
||||
}
|
||||
|
||||
bool WaylandInputMethodContext::WindowIsActiveForTextInputV1() const {
|
||||
if (!text_input_v1_ || !window_) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
// The associated window has keyboard focus
|
||||
window_focused_ ||
|
||||
// If no keyboard is connected, the toplevel window active state is used
|
||||
// to deduce if this window is active.
|
||||
(!connection_->seat()->keyboard() &&
|
||||
window_->GetRootParentWindow()->IsActive());
|
||||
}
|
||||
|
||||
void WaylandInputMethodContext::MaybeUpdateActivated(
|
||||
bool skip_virtual_keyboard_update) {
|
||||
if (!text_input_v3_ && !text_input_v1_) {
|
||||
return;
|
||||
}
|
||||
|
||||
WaylandWindow* focused_window =
|
||||
connection_->window_manager()->GetCurrentKeyboardFocusedWindow();
|
||||
if (!focused_window && !connection_->seat()->keyboard()) {
|
||||
// If no keyboard is connected, the current active window is used.
|
||||
focused_window = connection_->window_manager()->GetCurrentActiveWindow();
|
||||
}
|
||||
// Activate Wayland IME only if the following conditions are met:
|
||||
// 1) InputMethod has some TextInputClient connected.
|
||||
// 2) There is a focused Chromium window.
|
||||
// 3) The focused window matches the window containing this
|
||||
// WaylandInputMethodContext.
|
||||
bool activated = focused_ && focused_window;
|
||||
// 2) The associated window for this context is focused, or there is an
|
||||
// active window for text-input-v1.
|
||||
bool activated =
|
||||
focused_ && (window_focused_ || WindowIsActiveForTextInputV1());
|
||||
if (activated_ == activated)
|
||||
return;
|
||||
|
||||
@ -856,7 +867,7 @@ void WaylandInputMethodContext::MaybeUpdateActivated(
|
||||
attributes_.should_do_learning);
|
||||
} else {
|
||||
text_input_v1_->SetClient(text_input_v1_client_.get());
|
||||
text_input_v1_->Activate(focused_window);
|
||||
text_input_v1_->Activate(window_.get());
|
||||
text_input_v1_->SetContentType(attributes_.input_type, attributes_.flags,
|
||||
attributes_.should_do_learning);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "ui/base/ime/virtual_keyboard_controller.h"
|
||||
#include "ui/gfx/range/range.h"
|
||||
#include "ui/ozone/platform/wayland/host/wayland_keyboard.h"
|
||||
#include "ui/ozone/platform/wayland/host/wayland_window_observer.h"
|
||||
#include "ui/ozone/platform/wayland/host/wayland_window.h"
|
||||
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
|
||||
#include "ui/ozone/platform/wayland/host/zwp_text_input_v3.h"
|
||||
|
||||
@ -30,7 +30,7 @@ class WaylandConnection;
|
||||
|
||||
class WaylandInputMethodContext : public LinuxInputMethodContext,
|
||||
public VirtualKeyboardController,
|
||||
public WaylandWindowObserver {
|
||||
public WaylandWindow::FocusClient {
|
||||
public:
|
||||
class Delegate;
|
||||
|
||||
@ -70,8 +70,9 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
|
||||
void RemoveObserver(VirtualKeyboardControllerObserver* observer) override;
|
||||
bool IsKeyboardVisible() override;
|
||||
|
||||
// WaylandWindowObserver overrides:
|
||||
void OnKeyboardFocusedWindowChanged() override;
|
||||
// WaylandWindow::FocusClient overrides:
|
||||
void OnKeyboardFocusChanged(bool focused) override;
|
||||
void OnTextInputFocusChanged(bool focused) override;
|
||||
|
||||
// Callbacks from the v1 and v3 clients.
|
||||
void OnPreeditString(std::string_view text,
|
||||
@ -92,9 +93,9 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
|
||||
return surrounding_text_tracker_.predicted_state();
|
||||
}
|
||||
|
||||
void SetTextInputV1ForTesting(std::unique_ptr<ZwpTextInputV1> text_input_v1);
|
||||
void SetTextInputV1ForTesting(ZwpTextInputV1* text_input_v1);
|
||||
|
||||
void SetTextInputV3ForTesting(std::unique_ptr<ZwpTextInputV3> text_input_v3);
|
||||
void SetTextInputV3ForTesting(ZwpTextInputV3* text_input_v3);
|
||||
|
||||
void SetDesktopEnvironmentForTesting(
|
||||
base::nix::DesktopEnvironment desktop_environment) {
|
||||
@ -106,6 +107,7 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
|
||||
void Focus(bool skip_virtual_keyboard_update);
|
||||
void Blur(bool skip_virtual_keyboard_update);
|
||||
void UpdatePreeditText(const std::u16string& preedit_text);
|
||||
bool WindowIsActiveForTextInputV1() const;
|
||||
// If |skip_virtual_keyboard_update| is true, no virtual keyboard show/hide
|
||||
// requests will be sent. This is used to prevent flickering the virtual
|
||||
// keyboard when it would be immediately reshown anyway, e.g. when changing
|
||||
@ -121,14 +123,21 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
|
||||
// Delegate IME-specific events to be handled by //ui code.
|
||||
const raw_ptr<LinuxInputMethodContextDelegate> ime_delegate_;
|
||||
|
||||
// Window obtained from IME delegate's widget. This WaylandInputMethodContext
|
||||
// will be associated exclusively with this window and so this object will not
|
||||
// change for the lifetime of the window.
|
||||
base::WeakPtr<WaylandWindow> window_;
|
||||
std::unique_ptr<ZwpTextInputV1Client> text_input_v1_client_;
|
||||
std::unique_ptr<ZwpTextInputV3Client> text_input_v3_client_;
|
||||
std::unique_ptr<ZwpTextInputV1> text_input_v1_;
|
||||
std::unique_ptr<ZwpTextInputV3> text_input_v3_;
|
||||
raw_ptr<ZwpTextInputV1> text_input_v1_;
|
||||
raw_ptr<ZwpTextInputV3> text_input_v3_;
|
||||
|
||||
// Tracks whether InputMethod in Chrome has some focus.
|
||||
bool focused_ = false;
|
||||
|
||||
// Tracks whether the window associated with the input method is focused.
|
||||
bool window_focused_ = false;
|
||||
|
||||
// Tracks whether a request to activate InputMethod is sent to wayland
|
||||
// compositor.
|
||||
bool activated_ = false;
|
||||
|
@ -183,7 +183,8 @@ class MockZwpTextInputV3 : public ZwpTextInputV3 {
|
||||
|
||||
class TestInputMethodContextDelegate : public LinuxInputMethodContextDelegate {
|
||||
public:
|
||||
TestInputMethodContextDelegate() = default;
|
||||
explicit TestInputMethodContextDelegate(gfx::AcceleratedWidget window_key)
|
||||
: client_window_key_(window_key) {}
|
||||
TestInputMethodContextDelegate(const TestInputMethodContextDelegate&) =
|
||||
delete;
|
||||
TestInputMethodContextDelegate& operator=(
|
||||
@ -220,6 +221,10 @@ class TestInputMethodContextDelegate : public LinuxInputMethodContextDelegate {
|
||||
virtual_keyboard_bounds_ = screen_bounds;
|
||||
}
|
||||
|
||||
gfx::AcceleratedWidget GetClientWindowKey() const override {
|
||||
return client_window_key_;
|
||||
}
|
||||
|
||||
bool was_on_commit_called() const { return was_on_commit_called_; }
|
||||
|
||||
const std::optional<ui::CompositionText>& last_preedit() {
|
||||
@ -256,6 +261,7 @@ class TestInputMethodContextDelegate : public LinuxInputMethodContextDelegate {
|
||||
}
|
||||
|
||||
private:
|
||||
gfx::AcceleratedWidget client_window_key_;
|
||||
bool was_on_commit_called_ = false;
|
||||
std::optional<std::u16string> last_commit_text_;
|
||||
std::optional<bool> last_on_confirm_composition_arg_;
|
||||
@ -323,15 +329,13 @@ class WaylandInputMethodContextTest : public WaylandTest {
|
||||
protected:
|
||||
void SetUpInternal() {
|
||||
input_method_context_delegate_ =
|
||||
std::make_unique<TestInputMethodContextDelegate>();
|
||||
std::make_unique<TestInputMethodContextDelegate>(window_->GetWidget());
|
||||
keyboard_delegate_ = std::make_unique<TestKeyboardDelegate>();
|
||||
input_method_context_ = std::make_unique<WaylandInputMethodContext>(
|
||||
connection_.get(), keyboard_delegate_.get(),
|
||||
input_method_context_delegate_.get());
|
||||
auto text_input_v1 = std::make_unique<ZwpTextInputV1Impl>(
|
||||
connection_.get(), connection_->text_input_manager_v1());
|
||||
text_input_v1_ = text_input_v1.get();
|
||||
input_method_context_->SetTextInputV1ForTesting(std::move(text_input_v1));
|
||||
text_input_v1_ = connection_->EnsureTextInputV1();
|
||||
input_method_context_->SetTextInputV1ForTesting(text_input_v1_.get());
|
||||
input_method_context_->SetDesktopEnvironmentForTesting(
|
||||
// Ensure by default it doesn't pick the current desktop from the system
|
||||
// the tests are running on.
|
||||
@ -1266,14 +1270,13 @@ class WaylandInputMethodContextWithMockV3Test : public WaylandTestSimple {
|
||||
void SetUp() override {
|
||||
WaylandTestSimple::SetUp();
|
||||
input_method_context_delegate_ =
|
||||
std::make_unique<TestInputMethodContextDelegate>();
|
||||
std::make_unique<TestInputMethodContextDelegate>(window_->GetWidget());
|
||||
keyboard_delegate_ = std::make_unique<TestKeyboardDelegate>();
|
||||
mock_wrapper_ = std::make_unique<MockZwpTextInputV3>();
|
||||
input_method_context_ = std::make_unique<WaylandInputMethodContext>(
|
||||
connection_.get(), keyboard_delegate_.get(),
|
||||
input_method_context_delegate_.get());
|
||||
auto mock_wrapper = std::make_unique<MockZwpTextInputV3>();
|
||||
mock_wrapper_ = mock_wrapper.get();
|
||||
input_method_context_->SetTextInputV3ForTesting(std::move(mock_wrapper));
|
||||
input_method_context_->SetTextInputV3ForTesting(mock_wrapper_.get());
|
||||
input_method_context_->SetDesktopEnvironmentForTesting(
|
||||
// Ensure by default it doesn't pick the current desktop from the system
|
||||
// the tests are running on.
|
||||
@ -1284,8 +1287,8 @@ class WaylandInputMethodContextWithMockV3Test : public WaylandTestSimple {
|
||||
std::unique_ptr<TestInputMethodContextDelegate>
|
||||
input_method_context_delegate_;
|
||||
std::unique_ptr<TestKeyboardDelegate> keyboard_delegate_;
|
||||
std::unique_ptr<MockZwpTextInputV3> mock_wrapper_;
|
||||
std::unique_ptr<WaylandInputMethodContext> input_method_context_;
|
||||
raw_ptr<MockZwpTextInputV3> mock_wrapper_;
|
||||
};
|
||||
|
||||
TEST_F(WaylandInputMethodContextWithMockV3Test,
|
||||
|
@ -420,15 +420,27 @@ void WaylandToplevelWindow::UpdateActivationState() {
|
||||
bool prev_is_active = is_active_;
|
||||
|
||||
// Determine active state from keyboard focus. If keyboard is unavailable,
|
||||
// determine it from xdg-shell "activated" state as that's the only hint the
|
||||
// compositor provides us on whether our window is considered active.
|
||||
// TODO(crbug.com/369574355): utilize zwp_text_input_v3::{enter,leave}
|
||||
// eventually
|
||||
// determine it from zwp_text_input_v3::{enter,leave}.
|
||||
// If neither of those are available, use xdg-shell "activated" state as
|
||||
// that's the only other hint the compositor provides us on whether our window
|
||||
// is considered active.
|
||||
if (connection()->IsKeyboardAvailable()) {
|
||||
auto* keyboard_focused_window =
|
||||
connection()->window_manager()->GetCurrentKeyboardFocusedWindow();
|
||||
is_active_ = keyboard_focused_window &&
|
||||
keyboard_focused_window->GetRootParentWindow() == this;
|
||||
} else if (connection()->SupportsTextInputFocus()) {
|
||||
// Note: Some compositors (sway, niri, cosmic etc.) may not send
|
||||
// text-input-v3 enter/leave events if an IM framework is not
|
||||
// installed/running. So text input focus cannot be used always instead of
|
||||
// keyboard focus above. However, if there is no physical keyboard, there
|
||||
// should be an IM framework to facilitate inputting text in some way, e.g.
|
||||
// using a virtual keyboard, and so it should be okay to expect focus to be
|
||||
// received from text-input in that case if text-input-v3 is available.
|
||||
auto* text_input_focused_window =
|
||||
connection()->window_manager()->GetCurrentTextInputFocusedWindow();
|
||||
is_active_ = text_input_focused_window &&
|
||||
text_input_focused_window->GetRootParentWindow() == this;
|
||||
} else {
|
||||
is_active_ = is_xdg_active_;
|
||||
}
|
||||
|
@ -135,6 +135,13 @@ WaylandWindow::~WaylandWindow() {
|
||||
for (auto bubble : child_bubbles_) {
|
||||
bubble->set_parent_window(nullptr);
|
||||
}
|
||||
|
||||
if (focus_client_) {
|
||||
focus_client_->OnKeyboardFocusChanged(false);
|
||||
if (connection_->SupportsTextInputFocus()) {
|
||||
focus_client_->OnTextInputFocusChanged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandWindow::OnWindowLostCapture() {
|
||||
@ -319,6 +326,18 @@ void WaylandWindow::OnPointerFocusChanged(bool focused) {
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandWindow::OnKeyboardFocusChanged(bool focused) {
|
||||
if (focus_client_) {
|
||||
focus_client_->OnKeyboardFocusChanged(focused);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandWindow::OnTextInputFocusChanged(bool focused) {
|
||||
if (focus_client_) {
|
||||
focus_client_->OnTextInputFocusChanged(focused);
|
||||
}
|
||||
}
|
||||
|
||||
bool WaylandWindow::HasPointerFocus() const {
|
||||
return this ==
|
||||
connection_->window_manager()->GetCurrentPointerFocusedWindow();
|
||||
|
@ -68,6 +68,13 @@ class WaylandWindow : public PlatformWindow,
|
||||
public WaylandExtension,
|
||||
public EventTarget {
|
||||
public:
|
||||
// An interface to receive window focus change events.
|
||||
class FocusClient {
|
||||
public:
|
||||
virtual void OnKeyboardFocusChanged(bool focused) = 0;
|
||||
virtual void OnTextInputFocusChanged(bool focused) = 0;
|
||||
};
|
||||
|
||||
WaylandWindow(const WaylandWindow&) = delete;
|
||||
WaylandWindow& operator=(const WaylandWindow&) = delete;
|
||||
|
||||
@ -127,9 +134,19 @@ class WaylandWindow : public PlatformWindow,
|
||||
const gfx::FrameData& data,
|
||||
std::vector<wl::WaylandOverlayConfig>& overlays);
|
||||
|
||||
// Called when the focus changed on this window.
|
||||
void set_focus_client(FocusClient* focus_client) {
|
||||
focus_client_ = focus_client;
|
||||
}
|
||||
|
||||
// Called when the pointer focus changed on this window.
|
||||
void OnPointerFocusChanged(bool focused);
|
||||
|
||||
// Called when the keyboard focus changed on this window.
|
||||
void OnKeyboardFocusChanged(bool focused);
|
||||
|
||||
// Called when the text input focus changed on this window.
|
||||
void OnTextInputFocusChanged(bool focused);
|
||||
|
||||
// Returns the focus status of this window.
|
||||
bool HasPointerFocus() const;
|
||||
bool HasKeyboardFocus() const;
|
||||
@ -619,6 +636,10 @@ class WaylandWindow : public PlatformWindow,
|
||||
// dip.
|
||||
gfx::Rect restored_bounds_dip_;
|
||||
|
||||
// A focus client that, once set, is expected to live at least as long as this
|
||||
// window.
|
||||
raw_ptr<FocusClient> focus_client_ = nullptr;
|
||||
|
||||
// This holds the currently applied state. When in doubt, use this as the
|
||||
// source of truth for this window's state. Whenever applied_state_ is
|
||||
// changed, that change should be applied and a new in-flight request and
|
||||
|
@ -17,6 +17,38 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
namespace {
|
||||
|
||||
void UpdateToplevelActivation(WaylandWindow* old_focused_window,
|
||||
WaylandWindow* new_focused_window) {
|
||||
auto* old_focused_toplevel_window =
|
||||
old_focused_window
|
||||
? old_focused_window->GetRootParentWindow()->AsWaylandToplevelWindow()
|
||||
: nullptr;
|
||||
auto* focused_toplevel_window =
|
||||
new_focused_window
|
||||
? new_focused_window->GetRootParentWindow()->AsWaylandToplevelWindow()
|
||||
: nullptr;
|
||||
if (focused_toplevel_window != old_focused_toplevel_window) {
|
||||
if (old_focused_toplevel_window) {
|
||||
old_focused_toplevel_window->UpdateActivationState();
|
||||
}
|
||||
if (focused_toplevel_window) {
|
||||
focused_toplevel_window->UpdateActivationState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateParentToplevelAcivationOnRemoval(WaylandWindow* window) {
|
||||
auto* toplevel_window =
|
||||
window->GetRootParentWindow()->AsWaylandToplevelWindow();
|
||||
if (toplevel_window && toplevel_window != window) {
|
||||
toplevel_window->UpdateActivationState();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WaylandWindowManager::WaylandWindowManager(WaylandConnection* connection)
|
||||
: connection_(connection) {}
|
||||
|
||||
@ -166,6 +198,10 @@ WaylandWindow* WaylandWindowManager::GetCurrentKeyboardFocusedWindow() const {
|
||||
return keyboard_focused_window_;
|
||||
}
|
||||
|
||||
WaylandWindow* WaylandWindowManager::GetCurrentTextInputFocusedWindow() const {
|
||||
return text_input_focused_window_;
|
||||
}
|
||||
|
||||
void WaylandWindowManager::SetPointerFocusedWindow(WaylandWindow* window) {
|
||||
auto* old_focused_window = GetCurrentPointerFocusedWindow();
|
||||
if (window == old_focused_window)
|
||||
@ -192,22 +228,28 @@ void WaylandWindowManager::SetKeyboardFocusedWindow(WaylandWindow* window) {
|
||||
if (window == old_focused_window)
|
||||
return;
|
||||
keyboard_focused_window_ = window;
|
||||
observers_.Notify(&WaylandWindowObserver::OnKeyboardFocusedWindowChanged);
|
||||
auto* old_focused_toplevel_window =
|
||||
old_focused_window
|
||||
? old_focused_window->GetRootParentWindow()->AsWaylandToplevelWindow()
|
||||
: nullptr;
|
||||
auto* focused_toplevel_window =
|
||||
window ? window->GetRootParentWindow()->AsWaylandToplevelWindow()
|
||||
: nullptr;
|
||||
if (focused_toplevel_window != old_focused_toplevel_window) {
|
||||
if (old_focused_toplevel_window) {
|
||||
old_focused_toplevel_window->UpdateActivationState();
|
||||
}
|
||||
if (focused_toplevel_window) {
|
||||
focused_toplevel_window->UpdateActivationState();
|
||||
}
|
||||
if (old_focused_window) {
|
||||
old_focused_window->OnKeyboardFocusChanged(false);
|
||||
}
|
||||
if (window) {
|
||||
window->OnKeyboardFocusChanged(true);
|
||||
}
|
||||
UpdateToplevelActivation(old_focused_window, window);
|
||||
}
|
||||
|
||||
void WaylandWindowManager::SetTextInputFocusedWindow(WaylandWindow* window) {
|
||||
auto* old_focused_window = GetCurrentTextInputFocusedWindow();
|
||||
if (window == old_focused_window) {
|
||||
return;
|
||||
}
|
||||
text_input_focused_window_ = window;
|
||||
if (old_focused_window) {
|
||||
old_focused_window->OnTextInputFocusChanged(false);
|
||||
}
|
||||
if (window) {
|
||||
window->OnTextInputFocusChanged(true);
|
||||
}
|
||||
UpdateToplevelActivation(old_focused_window, window);
|
||||
}
|
||||
|
||||
void WaylandWindowManager::AddWindow(gfx::AcceleratedWidget widget,
|
||||
@ -223,24 +265,23 @@ void WaylandWindowManager::RemoveWindow(gfx::AcceleratedWidget widget) {
|
||||
|
||||
window_map_.erase(widget);
|
||||
|
||||
// Reset `pointer_focused_window_` and `keyboard_focused_window_` before
|
||||
// notifying any observers to make sure GetCurrentPointerFocusedWindow() and
|
||||
// GetCurrentKeyboardFocusedWindow() behave correctly. Especially the former
|
||||
// can be problematic if notifying WaylandWindowDragController that a window
|
||||
// has been removed before resetting `pointer_focused_window_`, because that
|
||||
// leads to WaylandEventSource::OnPointerButtonEvent() being called, which
|
||||
// then calls GetCurrentPointerFocusedWindow().
|
||||
// Reset `*_focused_window_` before notifying any observers to make sure
|
||||
// GetCurrent*FocusedWindow() behave correctly.
|
||||
// The pointer case in particular can be problematic if notifying
|
||||
// WaylandWindowDragController that a window has been removed before resetting
|
||||
// `pointer_focused_window_`, because that leads to
|
||||
// WaylandEventSource::OnPointerButtonEvent() being called, which then calls
|
||||
// GetCurrentPointerFocusedWindow().
|
||||
if (window == pointer_focused_window_) {
|
||||
pointer_focused_window_ = nullptr;
|
||||
}
|
||||
if (window == keyboard_focused_window_) {
|
||||
keyboard_focused_window_ = nullptr;
|
||||
observers_.Notify(&WaylandWindowObserver::OnKeyboardFocusedWindowChanged);
|
||||
auto* toplevel_window =
|
||||
window->GetRootParentWindow()->AsWaylandToplevelWindow();
|
||||
if (toplevel_window && toplevel_window != window) {
|
||||
toplevel_window->UpdateActivationState();
|
||||
}
|
||||
UpdateParentToplevelAcivationOnRemoval(window);
|
||||
}
|
||||
if (window == text_input_focused_window_) {
|
||||
text_input_focused_window_ = nullptr;
|
||||
UpdateParentToplevelAcivationOnRemoval(window);
|
||||
}
|
||||
|
||||
observers_.Notify(&WaylandWindowObserver::OnWindowRemoved, window);
|
||||
|
@ -80,6 +80,9 @@ class WaylandWindowManager {
|
||||
// Returns a current focused window by keyboard.
|
||||
WaylandWindow* GetCurrentKeyboardFocusedWindow() const;
|
||||
|
||||
// Returns a current focused window by text input.
|
||||
WaylandWindow* GetCurrentTextInputFocusedWindow() const;
|
||||
|
||||
// Sets the given window as the pointer focused window.
|
||||
// If there already is another, the old one will be unset.
|
||||
// If nullptr is passed to |window|, it means pointer focus is unset from
|
||||
@ -101,6 +104,14 @@ class WaylandWindowManager {
|
||||
// The given |window| must be managed by this manager.
|
||||
void SetKeyboardFocusedWindow(WaylandWindow* window);
|
||||
|
||||
// Sets the given window as the text input focused window.
|
||||
// If there already is another, the old one will be unset.
|
||||
// If nullptr is passed to |window|, it means text-input focus is unset from
|
||||
// any window.
|
||||
// The given |window| must be managed by this manager.
|
||||
// Text input focus usually follows keyboard focus.
|
||||
void SetTextInputFocusedWindow(WaylandWindow* window);
|
||||
|
||||
// Returns all stored windows.
|
||||
std::vector<WaylandWindow*> GetAllWindows() const;
|
||||
|
||||
@ -135,6 +146,7 @@ class WaylandWindowManager {
|
||||
private:
|
||||
raw_ptr<WaylandWindow> pointer_focused_window_ = nullptr;
|
||||
raw_ptr<WaylandWindow> keyboard_focused_window_ = nullptr;
|
||||
raw_ptr<WaylandWindow> text_input_focused_window_ = nullptr;
|
||||
|
||||
const raw_ptr<WaylandConnection> connection_;
|
||||
|
||||
|
@ -23,8 +23,6 @@ void WaylandWindowObserver::OnSubsurfaceRemoved(WaylandWindow* window,
|
||||
WaylandSubsurface* subsurface) {
|
||||
}
|
||||
|
||||
void WaylandWindowObserver::OnKeyboardFocusedWindowChanged() {}
|
||||
|
||||
void WaylandWindowObserver::OnWindowRemovedFromSession(WaylandWindow* window) {}
|
||||
|
||||
} // namespace ui
|
||||
|
@ -35,11 +35,6 @@ class WaylandWindowObserver : public base::CheckedObserver {
|
||||
virtual void OnSubsurfaceRemoved(WaylandWindow* window,
|
||||
WaylandSubsurface* subsurface);
|
||||
|
||||
// Called when the keyboard focused window is changed.
|
||||
// The latest keyboard focused window can be obtain via
|
||||
// WaylandWindowManager::GetCurrentKeyboardFocusedWindow().
|
||||
virtual void OnKeyboardFocusedWindowChanged();
|
||||
|
||||
// Called when a window is being permanently removed from the
|
||||
// session it did belong to.
|
||||
virtual void OnWindowRemovedFromSession(WaylandWindow* window);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "ui/base/wayland/wayland_client_input_types.h"
|
||||
#include "ui/gfx/range/range.h"
|
||||
#include "ui/ozone/platform/wayland/common/wayland_util.h"
|
||||
#include "ui/ozone/platform/wayland/host/span_style.h"
|
||||
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
|
||||
#include "ui/ozone/platform/wayland/host/wayland_seat.h"
|
||||
@ -333,19 +334,17 @@ void ZwpTextInputV3Impl::Commit() {
|
||||
void ZwpTextInputV3Impl::OnEnter(void* data,
|
||||
struct zwp_text_input_v3* text_input,
|
||||
struct wl_surface* surface) {
|
||||
// Same as text-input-v1, we don't use this for text-input focus changes and
|
||||
// instead use wayland keyboard enter/leave events to activate or deactivate
|
||||
// text-input.
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
auto* self = static_cast<ZwpTextInputV3Impl*>(data);
|
||||
if (auto* window = wl::RootWindowFromWlSurface(surface)) {
|
||||
self->connection_->window_manager()->SetTextInputFocusedWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
void ZwpTextInputV3Impl::OnLeave(void* data,
|
||||
struct zwp_text_input_v3* text_input,
|
||||
struct wl_surface* surface) {
|
||||
// Same as text-input-v1, we don't use this for text-input focus changes and
|
||||
// instead use wayland keyboard enter/leave events to activate or deactivate
|
||||
// text-input.
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
auto* self = static_cast<ZwpTextInputV3Impl*>(data);
|
||||
self->connection_->window_manager()->SetTextInputFocusedWindow(nullptr);
|
||||
}
|
||||
|
||||
void ZwpTextInputV3Impl::OnPreeditString(void* data,
|
||||
|
@ -35,8 +35,7 @@ class ZwpTextInputV3Test : public WaylandTestSimple {
|
||||
void SetUp() override {
|
||||
WaylandTestSimple::SetUp();
|
||||
|
||||
text_input_v3_ = std::make_unique<ZwpTextInputV3Impl>(
|
||||
connection_.get(), connection_->text_input_manager_v3());
|
||||
text_input_v3_ = connection_->EnsureTextInputV3();
|
||||
text_input_v3_->SetClient(&test_client_);
|
||||
}
|
||||
|
||||
@ -46,7 +45,7 @@ class ZwpTextInputV3Test : public WaylandTestSimple {
|
||||
}
|
||||
|
||||
MockZwpTextInputV3Client test_client_;
|
||||
std::unique_ptr<ZwpTextInputV3> text_input_v3_;
|
||||
raw_ptr<ZwpTextInputV3> text_input_v3_;
|
||||
};
|
||||
|
||||
TEST_F(ZwpTextInputV3Test, Enable) {
|
||||
|
@ -176,7 +176,8 @@ class OzonePlatformWayland : public OzonePlatform,
|
||||
std::unique_ptr<InputMethod> CreateInputMethod(
|
||||
ImeKeyEventDispatcher* ime_key_event_dispatcher,
|
||||
gfx::AcceleratedWidget widget) override {
|
||||
return std::make_unique<InputMethodAuraLinux>(ime_key_event_dispatcher);
|
||||
return std::make_unique<InputMethodAuraLinux>(ime_key_event_dispatcher,
|
||||
widget);
|
||||
}
|
||||
|
||||
PlatformMenuUtils* GetPlatformMenuUtils() override {
|
||||
|
@ -133,11 +133,12 @@ class OzonePlatformX11 : public OzonePlatform,
|
||||
|
||||
std::unique_ptr<InputMethod> CreateInputMethod(
|
||||
ImeKeyEventDispatcher* ime_key_event_dispatcher,
|
||||
gfx::AcceleratedWidget) override {
|
||||
gfx::AcceleratedWidget widget) override {
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
return std::make_unique<ash::InputMethodAsh>(ime_key_event_dispatcher);
|
||||
#else
|
||||
return std::make_unique<InputMethodAuraLinux>(ime_key_event_dispatcher);
|
||||
return std::make_unique<InputMethodAuraLinux>(ime_key_event_dispatcher,
|
||||
widget);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user