From 4cc9db41256928c1c5ce7f9c52df8acefa6c8f30 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 13 Feb 2025 15:30:33 +1300 Subject: [PATCH] Move FlTextInputHandler from FlView (#162131) This moves the final part of the keyboard handling from FlView to the FlEngine and means keyboard should work correctly with multiple views. --- .../ci/licenses_golden/licenses_flutter | 4 - .../src/flutter/shell/platform/linux/BUILD.gn | 2 - .../flutter/shell/platform/linux/fl_engine.cc | 20 ++ .../shell/platform/linux/fl_engine_private.h | 11 ++ .../platform/linux/fl_text_input_handler.cc | 87 ++++----- .../platform/linux/fl_text_input_handler.h | 40 +++- .../linux/fl_text_input_handler_test.cc | 179 +++++++++--------- .../linux/fl_text_input_view_delegate.cc | 24 --- .../linux/fl_text_input_view_delegate.h | 48 ----- .../flutter/shell/platform/linux/fl_view.cc | 57 ++---- .../platform/linux/testing/mock_epoxy.cc | 6 + .../shell/platform/linux/testing/mock_epoxy.h | 1 + .../platform/linux/testing/mock_im_context.cc | 179 ++++++++---------- .../platform/linux/testing/mock_im_context.h | 26 +-- .../platform/linux/testing/mock_keymap.cc | 6 + .../platform/linux/testing/mock_keymap.h | 1 + .../testing/mock_text_input_view_delegate.cc | 74 -------- .../testing/mock_text_input_view_delegate.h | 41 ---- .../platform/linux/testing/mock_window.cc | 6 + .../platform/linux/testing/mock_window.h | 1 + 20 files changed, 313 insertions(+), 500 deletions(-) delete mode 100644 engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.cc delete mode 100644 engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.h delete mode 100644 engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.cc delete mode 100644 engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 8357d60e6e..43dd4bd6b6 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -43908,8 +43908,6 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_channel.h + ../../.. ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler_test.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_view_delegate.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_view_delegate.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_texture.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_texture_gl.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_texture_gl_private.h + ../../../flutter/LICENSE @@ -46910,8 +46908,6 @@ FILE: ../../../flutter/shell/platform/linux/fl_text_input_channel.h FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler.cc FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler.h FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler_test.cc -FILE: ../../../flutter/shell/platform/linux/fl_text_input_view_delegate.cc -FILE: ../../../flutter/shell/platform/linux/fl_text_input_view_delegate.h FILE: ../../../flutter/shell/platform/linux/fl_texture.cc FILE: ../../../flutter/shell/platform/linux/fl_texture_gl.cc FILE: ../../../flutter/shell/platform/linux/fl_texture_gl_private.h diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index 5f6d955ddd..a73bd3282b 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -150,7 +150,6 @@ source_set("flutter_linux_sources") { "fl_task_runner.h", "fl_text_input_channel.cc", "fl_text_input_handler.cc", - "fl_text_input_view_delegate.cc", "fl_texture.cc", "fl_texture_gl.cc", "fl_texture_registrar.cc", @@ -262,7 +261,6 @@ executable("flutter_linux_unittests") { "testing/mock_renderer.cc", "testing/mock_settings.cc", "testing/mock_signal_handler.cc", - "testing/mock_text_input_view_delegate.cc", "testing/mock_texture_registrar.cc", "testing/mock_window.cc", ] diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index 2fd7a6237c..74943adc2d 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -61,6 +61,9 @@ struct _FlEngine { // Process keyboard events. FlKeyboardManager* keyboard_manager; + // Implements the flutter/textinput channel. + FlTextInputHandler* text_input_handler; + // Implements the flutter/keyboard channel. FlKeyboardHandler* keyboard_handler; @@ -391,9 +394,20 @@ static void fl_engine_update_semantics_cb(const FlutterSemanticsUpdate2* update, static void setup_keyboard(FlEngine* self) { g_clear_object(&self->keyboard_manager); self->keyboard_manager = fl_keyboard_manager_new(self); + g_clear_object(&self->keyboard_handler); self->keyboard_handler = fl_keyboard_handler_new(self->binary_messenger, self->keyboard_manager); + + GtkWidget* widget = + self->text_input_handler != nullptr + ? fl_text_input_handler_get_widget(self->text_input_handler) + : nullptr; + g_clear_object(&self->text_input_handler); + self->text_input_handler = fl_text_input_handler_new(self->binary_messenger); + if (widget != nullptr) { + fl_text_input_handler_set_widget(self->text_input_handler, widget); + } } // Called right before the engine is restarted. @@ -474,6 +488,7 @@ static void fl_engine_dispose(GObject* object) { g_clear_object(&self->settings_handler); g_clear_object(&self->platform_handler); g_clear_object(&self->keyboard_manager); + g_clear_object(&self->text_input_handler); g_clear_object(&self->keyboard_handler); g_clear_object(&self->mouse_cursor_handler); g_clear_object(&self->task_runner); @@ -1285,6 +1300,11 @@ FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* self) { return self->keyboard_manager; } +FlTextInputHandler* fl_engine_get_text_input_handler(FlEngine* self) { + g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); + return self->text_input_handler; +} + FlMouseCursorHandler* fl_engine_get_mouse_cursor_handler(FlEngine* self) { g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); return self->mouse_cursor_handler; diff --git a/engine/src/flutter/shell/platform/linux/fl_engine_private.h b/engine/src/flutter/shell/platform/linux/fl_engine_private.h index 069fc9217d..d5c9399bf0 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine_private.h +++ b/engine/src/flutter/shell/platform/linux/fl_engine_private.h @@ -13,6 +13,7 @@ #include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h" #include "flutter/shell/platform/linux/fl_renderer.h" #include "flutter/shell/platform/linux/fl_task_runner.h" +#include "flutter/shell/platform/linux/fl_text_input_handler.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" @@ -585,6 +586,16 @@ void fl_engine_request_app_exit(FlEngine* engine); */ FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* engine); +/** + * fl_engine_get_text_input_handler: + * @engine: an #FlEngine. + * + * Gets the text input handler used by this engine. + * + * Returns: a #FlTextInputHandler. + */ +FlTextInputHandler* fl_engine_get_text_input_handler(FlEngine* engine); + /** * fl_engine_get_mouse_cursor_handler: * @engine: an #FlEngine. diff --git a/engine/src/flutter/shell/platform/linux/fl_text_input_handler.cc b/engine/src/flutter/shell/platform/linux/fl_text_input_handler.cc index c3d1b65513..2598d9b27d 100644 --- a/engine/src/flutter/shell/platform/linux/fl_text_input_handler.cc +++ b/engine/src/flutter/shell/platform/linux/fl_text_input_handler.cc @@ -19,6 +19,9 @@ struct _FlTextInputHandler { FlTextInputChannel* channel; + // The widget with input focus. + GtkWidget* widget; + // Client ID provided by Flutter to report events with. int64_t client_id; @@ -37,8 +40,6 @@ struct _FlTextInputHandler { // Input method. GtkIMContext* im_context; - GWeakRef view_delegate; - flutter::TextInputModel* text_model; // A 4x4 matrix that maps from `EditableText` local coordinates to the @@ -316,12 +317,6 @@ static void clear_client(gpointer user_data) { // after each of these updates. It transforms the composing rect to GDK window // coordinates and notifies GTK of the updated cursor position. static void update_im_cursor_position(FlTextInputHandler* self) { - g_autoptr(FlTextInputViewDelegate) view_delegate = - FL_TEXT_INPUT_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate)); - if (view_delegate == nullptr) { - return; - } - // Skip update if not composing to avoid setting to position 0. if (!self->text_model->composing()) { return; @@ -338,8 +333,9 @@ static void update_im_cursor_position(FlTextInputHandler* self) { // Transform from Flutter view coordinates to GTK window coordinates. GdkRectangle preedit_rect = {}; - fl_text_input_view_delegate_translate_coordinates( - view_delegate, x, y, &preedit_rect.x, &preedit_rect.y); + gtk_widget_translate_coordinates(self->widget, + gtk_widget_get_toplevel(self->widget), x, y, + &preedit_rect.x, &preedit_rect.y); // Set the cursor location in window coordinates so that GTK can position // any system input method windows. @@ -395,7 +391,6 @@ static void fl_text_input_handler_dispose(GObject* object) { delete self->text_model; self->text_model = nullptr; } - g_weak_ref_clear(&self->view_delegate); g_clear_object(&self->cancellable); G_OBJECT_CLASS(fl_text_input_handler_parent_class)->dispose(object); @@ -414,9 +409,26 @@ static void fl_text_input_handler_init(FlTextInputHandler* self) { self->cancellable = g_cancellable_new(); } -static void init_im_context(FlTextInputHandler* self, - GtkIMContext* im_context) { - self->im_context = GTK_IM_CONTEXT(g_object_ref(im_context)); +static FlTextInputChannelVTable text_input_vtable = { + .set_client = set_client, + .hide = hide, + .show = show, + .set_editing_state = set_editing_state, + .clear_client = clear_client, + .set_editable_size_and_transform = set_editable_size_and_transform, + .set_marked_text_rect = set_marked_text_rect, +}; + +FlTextInputHandler* fl_text_input_handler_new(FlBinaryMessenger* messenger) { + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); + + FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER( + g_object_new(fl_text_input_handler_get_type(), nullptr)); + + self->channel = + fl_text_input_channel_new(messenger, &text_input_vtable, self); + + self->im_context = GTK_IM_CONTEXT(gtk_im_multicontext_new()); // On Wayland, this call sets up the input method so it can be enabled // immediately when required. Without it, on-screen keyboard's don't come up @@ -440,39 +452,28 @@ static void init_im_context(FlTextInputHandler* self, g_signal_connect_object(self->im_context, "delete-surrounding", G_CALLBACK(im_delete_surrounding_cb), self, G_CONNECT_SWAPPED); -} - -static FlTextInputChannelVTable text_input_vtable = { - .set_client = set_client, - .hide = hide, - .show = show, - .set_editing_state = set_editing_state, - .clear_client = clear_client, - .set_editable_size_and_transform = set_editable_size_and_transform, - .set_marked_text_rect = set_marked_text_rect, -}; - -FlTextInputHandler* fl_text_input_handler_new( - FlBinaryMessenger* messenger, - GtkIMContext* im_context, - FlTextInputViewDelegate* view_delegate) { - g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); - g_return_val_if_fail(GTK_IS_IM_CONTEXT(im_context), nullptr); - g_return_val_if_fail(FL_IS_TEXT_INPUT_VIEW_DELEGATE(view_delegate), nullptr); - - FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER( - g_object_new(fl_text_input_handler_get_type(), nullptr)); - - self->channel = - fl_text_input_channel_new(messenger, &text_input_vtable, self); - - init_im_context(self, im_context); - - g_weak_ref_init(&self->view_delegate, view_delegate); return self; } +GtkIMContext* fl_text_input_handler_get_im_context(FlTextInputHandler* self) { + g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(self), nullptr); + return self->im_context; +} + +void fl_text_input_handler_set_widget(FlTextInputHandler* self, + GtkWidget* widget) { + g_return_if_fail(FL_IS_TEXT_INPUT_HANDLER(self)); + self->widget = widget; + gtk_im_context_set_client_window(self->im_context, + gtk_widget_get_window(self->widget)); +} + +GtkWidget* fl_text_input_handler_get_widget(FlTextInputHandler* self) { + g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(self), nullptr); + return self->widget; +} + gboolean fl_text_input_handler_filter_keypress(FlTextInputHandler* self, FlKeyEvent* event) { g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(self), FALSE); diff --git a/engine/src/flutter/shell/platform/linux/fl_text_input_handler.h b/engine/src/flutter/shell/platform/linux/fl_text_input_handler.h index f0095b2cde..0eeda97826 100644 --- a/engine/src/flutter/shell/platform/linux/fl_text_input_handler.h +++ b/engine/src/flutter/shell/platform/linux/fl_text_input_handler.h @@ -8,7 +8,6 @@ #include #include "flutter/shell/platform/linux/fl_key_event.h" -#include "flutter/shell/platform/linux/fl_text_input_view_delegate.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" G_BEGIN_DECLS @@ -29,25 +28,50 @@ G_DECLARE_FINAL_TYPE(FlTextInputHandler, /** * fl_text_input_handler_new: * @messenger: an #FlBinaryMessenger. - * @im_context: (allow-none): a #GtkIMContext. - * @view_delegate: an #FlTextInputViewDelegate. * * Creates a new handler that implements SystemChannels.textInput from the * Flutter services library. * * Returns: a new #FlTextInputHandler. */ -FlTextInputHandler* fl_text_input_handler_new( - FlBinaryMessenger* messenger, - GtkIMContext* im_context, - FlTextInputViewDelegate* view_delegate); +FlTextInputHandler* fl_text_input_handler_new(FlBinaryMessenger* messenger); + +/** + * fl_text_input_handler_get_im_context: + * @handler: an #FlTextInputHandler. + * + * Get the IM context that is being used. Provided for testing purposes. + * + * Returns: a #GtkIMContext. + */ +GtkIMContext* fl_text_input_handler_get_im_context(FlTextInputHandler* handler); + +/** + * fl_text_input_handler_set_widget: + * @handler: an #FlTextInputHandler. + * @widget: the widget with keyboard focus. + * + * Set the widget that has input focus. + */ +void fl_text_input_handler_set_widget(FlTextInputHandler* handler, + GtkWidget* widget); + +/** + * fl_text_input_handler_get_widget: + * @handler: an #FlTextInputHandler. + * + * Get the widget that has input focus. + * + * Returns: a #GtkWidget or %NULL if none active. + */ +GtkWidget* fl_text_input_handler_get_widget(FlTextInputHandler* handler); /** * fl_text_input_handler_filter_keypress * @handler: an #FlTextInputHandler. * @event: a #FlKeyEvent * - * Process a Gdk key event. + * Process a key event. * * Returns: %TRUE if the event was used. */ diff --git a/engine/src/flutter/shell/platform/linux/fl_text_input_handler_test.cc b/engine/src/flutter/shell/platform/linux/fl_text_input_handler_test.cc index 2677befdfa..b86509013d 100644 --- a/engine/src/flutter/shell/platform/linux/fl_text_input_handler_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_text_input_handler_test.cc @@ -10,7 +10,6 @@ #include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" #include "flutter/shell/platform/linux/testing/fl_test.h" #include "flutter/shell/platform/linux/testing/mock_im_context.h" -#include "flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h" #include "flutter/testing/testing.h" #include "gmock/gmock.h" @@ -154,10 +153,9 @@ static void send_key_event(FlTextInputHandler* handler, TEST(FlTextInputHandlerTest, MessageHandler) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); EXPECT_TRUE( @@ -169,10 +167,9 @@ TEST(FlTextInputHandlerTest, MessageHandler) { TEST(FlTextInputHandlerTest, SetClient) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, {.client_id = 1}); @@ -183,14 +180,12 @@ TEST(FlTextInputHandlerTest, SetClient) { TEST(FlTextInputHandlerTest, Show) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); - EXPECT_CALL(context, - gtk_im_context_focus_in(::testing::Eq(context))); + EXPECT_CALL(context, gtk_im_context_focus_in); gboolean called = FALSE; fl_mock_binary_messenger_invoke_json_method( @@ -216,14 +211,12 @@ TEST(FlTextInputHandlerTest, Show) { TEST(FlTextInputHandlerTest, Hide) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); - EXPECT_CALL(context, - gtk_im_context_focus_out(::testing::Eq(context))); + EXPECT_CALL(context, gtk_im_context_focus_out); gboolean called = FALSE; fl_mock_binary_messenger_invoke_json_method( @@ -249,10 +242,9 @@ TEST(FlTextInputHandlerTest, Hide) { TEST(FlTextInputHandlerTest, ClearClient) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); gboolean called = FALSE; @@ -279,10 +271,9 @@ TEST(FlTextInputHandlerTest, ClearClient) { TEST(FlTextInputHandlerTest, PerformAction) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, { @@ -340,10 +331,9 @@ TEST(FlTextInputHandlerTest, PerformAction) { TEST(FlTextInputHandlerTest, MultilineWithSendAction) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, { @@ -399,10 +389,9 @@ TEST(FlTextInputHandlerTest, MultilineWithSendAction) { TEST(FlTextInputHandlerTest, MoveCursor) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, {.client_id = 1}); @@ -465,10 +454,9 @@ TEST(FlTextInputHandlerTest, MoveCursor) { TEST(FlTextInputHandlerTest, Select) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, {.client_id = 1}); @@ -531,17 +519,15 @@ TEST(FlTextInputHandlerTest, Select) { TEST(FlTextInputHandlerTest, Composing) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); // update - EXPECT_CALL(context, - gtk_im_context_get_preedit_string( - ::testing::Eq(context), - ::testing::A(), ::testing::_, ::testing::A())) + EXPECT_CALL(context, gtk_im_context_get_preedit_string( + ::testing::_, ::testing::A(), ::testing::_, + ::testing::A())) .WillOnce( ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter")), ::testing::SetArgPointee<3>(0))); @@ -601,10 +587,14 @@ TEST(FlTextInputHandlerTest, Composing) { }, &call_count); - g_signal_emit_by_name(context, "preedit-start", nullptr); - g_signal_emit_by_name(context, "preedit-changed", nullptr); - g_signal_emit_by_name(context, "commit", "engine", nullptr); - g_signal_emit_by_name(context, "preedit-end", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-start", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-changed", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "engine", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-end", nullptr); EXPECT_EQ(call_count, 3); fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); @@ -613,10 +603,9 @@ TEST(FlTextInputHandlerTest, Composing) { TEST(FlTextInputHandlerTest, SurroundingText) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, {.client_id = 1}); @@ -628,11 +617,11 @@ TEST(FlTextInputHandlerTest, SurroundingText) { // retrieve EXPECT_CALL(context, gtk_im_context_set_surrounding( - ::testing::Eq(context), - ::testing::StrEq("Flutter"), 7, 3)); + ::testing::_, ::testing::StrEq("Flutter"), -1, 3)); gboolean retrieved = false; - g_signal_emit_by_name(context, "retrieve-surrounding", &retrieved, nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "retrieve-surrounding", &retrieved, nullptr); EXPECT_TRUE(retrieved); int call_count = 0; @@ -668,7 +657,8 @@ TEST(FlTextInputHandlerTest, SurroundingText) { &call_count); gboolean deleted = false; - g_signal_emit_by_name(context, "delete-surrounding", 1, 2, &deleted, nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "delete-surrounding", 1, 2, &deleted, nullptr); EXPECT_TRUE(deleted); EXPECT_EQ(call_count, 1); @@ -678,13 +668,13 @@ TEST(FlTextInputHandlerTest, SurroundingText) { TEST(FlTextInputHandlerTest, SetMarkedTextRect) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); - g_signal_emit_by_name(context, "preedit-start", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-start", nullptr); // set editable size and transform g_autoptr(FlValue) size_and_transform = build_map({ @@ -729,15 +719,15 @@ TEST(FlTextInputHandlerTest, SetMarkedTextRect) { &called); EXPECT_TRUE(called); - EXPECT_CALL(delegate, fl_text_input_view_delegate_translate_coordinates( - ::testing::Eq(delegate), - ::testing::Eq(27), ::testing::Eq(32), ::testing::_, - ::testing::_)) - .WillOnce(::testing::DoAll(::testing::SetArgPointee<3>(123), - ::testing::SetArgPointee<4>(456))); + EXPECT_CALL(context, gtk_widget_translate_coordinates( + ::testing::_, ::testing::_, ::testing::Eq(27), + ::testing::Eq(32), ::testing::_, ::testing::_)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<4>(123), + ::testing::SetArgPointee<5>(456), + ::testing::Return(true))); EXPECT_CALL(context, gtk_im_context_set_cursor_location( - ::testing::Eq(context), + ::testing::_, ::testing::Pointee(::testing::AllOf( ::testing::Field(&GdkRectangle::x, 123), ::testing::Field(&GdkRectangle::y, 456), @@ -775,10 +765,9 @@ TEST(FlTextInputHandlerTest, SetMarkedTextRect) { TEST(FlTextInputHandlerTest, TextInputTypeNone) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, { @@ -786,11 +775,8 @@ TEST(FlTextInputHandlerTest, TextInputTypeNone) { .input_type = "TextInputType.none", }); - EXPECT_CALL(context, - gtk_im_context_focus_in(::testing::Eq(context))) - .Times(0); - EXPECT_CALL(context, - gtk_im_context_focus_out(::testing::Eq(context))); + EXPECT_CALL(context, gtk_im_context_focus_in).Times(0); + EXPECT_CALL(context, gtk_im_context_focus_out); gboolean called = FALSE; fl_mock_binary_messenger_invoke_json_method( @@ -816,10 +802,9 @@ TEST(FlTextInputHandlerTest, TextInputTypeNone) { TEST(FlTextInputHandlerTest, TextEditingDelta) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); set_client(messenger, { @@ -881,10 +866,9 @@ TEST(FlTextInputHandlerTest, TextEditingDelta) { TEST(FlTextInputHandlerTest, ComposingDelta) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); // set config @@ -893,13 +877,13 @@ TEST(FlTextInputHandlerTest, ComposingDelta) { .enable_delta_model = true, }); - g_signal_emit_by_name(context, "preedit-start", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-start", nullptr); // update - EXPECT_CALL(context, - gtk_im_context_get_preedit_string( - ::testing::Eq(context), - ::testing::A(), ::testing::_, ::testing::A())) + EXPECT_CALL(context, gtk_im_context_get_preedit_string( + ::testing::_, ::testing::A(), ::testing::_, + ::testing::A())) .WillOnce( ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter ")), ::testing::SetArgPointee<3>(8))); @@ -982,9 +966,12 @@ TEST(FlTextInputHandlerTest, ComposingDelta) { }, &call_count); - g_signal_emit_by_name(context, "preedit-changed", nullptr); - g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr); - g_signal_emit_by_name(context, "preedit-end", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-changed", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "Flutter engine", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), + "preedit-end", nullptr); EXPECT_EQ(call_count, 3); fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); @@ -993,10 +980,9 @@ TEST(FlTextInputHandlerTest, ComposingDelta) { TEST(FlTextInputHandlerTest, NonComposingDelta) { g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; - ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( - FL_BINARY_MESSENGER(messenger), context, delegate); + g_autoptr(FlTextInputHandler) handler = + fl_text_input_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); // set config @@ -1173,13 +1159,20 @@ TEST(FlTextInputHandlerTest, NonComposingDelta) { }, &call_count); - g_signal_emit_by_name(context, "commit", "F", nullptr); - g_signal_emit_by_name(context, "commit", "l", nullptr); - g_signal_emit_by_name(context, "commit", "u", nullptr); - g_signal_emit_by_name(context, "commit", "t", nullptr); - g_signal_emit_by_name(context, "commit", "t", nullptr); - g_signal_emit_by_name(context, "commit", "e", nullptr); - g_signal_emit_by_name(context, "commit", "r", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "F", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "l", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "u", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "t", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "t", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "e", nullptr); + g_signal_emit_by_name(fl_text_input_handler_get_im_context(handler), "commit", + "r", nullptr); EXPECT_EQ(call_count, 7); fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); diff --git a/engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.cc b/engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.cc deleted file mode 100644 index 52e94d4391..0000000000 --- a/engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Flutter 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 "flutter/shell/platform/linux/fl_text_input_view_delegate.h" - -G_DEFINE_INTERFACE(FlTextInputViewDelegate, - fl_text_input_view_delegate, - G_TYPE_OBJECT) - -static void fl_text_input_view_delegate_default_init( - FlTextInputViewDelegateInterface* iface) {} - -void fl_text_input_view_delegate_translate_coordinates( - FlTextInputViewDelegate* self, - gint view_x, - gint view_y, - gint* window_x, - gint* window_y) { - g_return_if_fail(FL_IS_TEXT_INPUT_VIEW_DELEGATE(self)); - - FL_TEXT_INPUT_VIEW_DELEGATE_GET_IFACE(self)->translate_coordinates( - self, view_x, view_y, window_x, window_y); -} diff --git a/engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.h b/engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.h deleted file mode 100644 index 8dc3d37c94..0000000000 --- a/engine/src/flutter/shell/platform/linux/fl_text_input_view_delegate.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXT_INPUT_VIEW_DELEGATE_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXT_INPUT_VIEW_DELEGATE_H_ - -#include - -#include "flutter/shell/platform/embedder/embedder.h" - -G_BEGIN_DECLS - -G_DECLARE_INTERFACE(FlTextInputViewDelegate, - fl_text_input_view_delegate, - FL, - TEXT_INPUT_VIEW_DELEGATE, - GObject); - -/** - * FlTextInputViewDelegate: - * - * An interface for a class that provides `FlTextInputHandler` with - * view-related features. - * - * This interface is typically implemented by `FlView`. - */ - -struct _FlTextInputViewDelegateInterface { - GTypeInterface g_iface; - - void (*translate_coordinates)(FlTextInputViewDelegate* delegate, - gint view_x, - gint view_y, - gint* window_x, - gint* window_y); -}; - -void fl_text_input_view_delegate_translate_coordinates( - FlTextInputViewDelegate* delegate, - gint view_x, - gint view_y, - gint* window_x, - gint* window_y); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXT_INPUT_VIEW_DELEGATE_H_ diff --git a/engine/src/flutter/shell/platform/linux/fl_view.cc b/engine/src/flutter/shell/platform/linux/fl_view.cc index 425250184e..995fa4d498 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view.cc +++ b/engine/src/flutter/shell/platform/linux/fl_view.cc @@ -18,8 +18,6 @@ #include "flutter/shell/platform/linux/fl_renderer_gdk.h" #include "flutter/shell/platform/linux/fl_scrolling_manager.h" #include "flutter/shell/platform/linux/fl_socket_accessible.h" -#include "flutter/shell/platform/linux/fl_text_input_handler.h" -#include "flutter/shell/platform/linux/fl_text_input_view_delegate.h" #include "flutter/shell/platform/linux/fl_touch_manager.h" #include "flutter/shell/platform/linux/fl_view_accessible.h" #include "flutter/shell/platform/linux/fl_window_state_monitor.h" @@ -62,9 +60,6 @@ struct _FlView { // Manages touch events. FlTouchManager* touch_manager; - // Flutter system channel handlers. - FlTextInputHandler* text_input_handler; - // Accessible tree from Flutter, exposed as an AtkPlug. FlViewAccessible* view_accessible; @@ -83,18 +78,13 @@ static void fl_renderable_iface_init(FlRenderableInterface* iface); static void fl_view_plugin_registry_iface_init( FlPluginRegistryInterface* iface); -static void fl_view_text_input_delegate_iface_init( - FlTextInputViewDelegateInterface* iface); - G_DEFINE_TYPE_WITH_CODE( FlView, fl_view, GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE(fl_renderable_get_type(), fl_renderable_iface_init) G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(), - fl_view_plugin_registry_iface_init) - G_IMPLEMENT_INTERFACE(fl_text_input_view_delegate_get_type(), - fl_view_text_input_delegate_iface_init)) + fl_view_plugin_registry_iface_init)) // Emit the first frame signal in the main thread. static gboolean first_frame_idle_cb(gpointer user_data) { @@ -112,21 +102,6 @@ static gboolean window_delete_event_cb(FlView* self) { return TRUE; } -// Initialize keyboard. -static void init_keyboard(FlView* self) { - FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine); - - GdkWindow* window = - gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(self))); - g_return_if_fail(GDK_IS_WINDOW(window)); - g_autoptr(GtkIMContext) im_context = gtk_im_multicontext_new(); - gtk_im_context_set_client_window(im_context, window); - - g_clear_object(&self->text_input_handler); - self->text_input_handler = fl_text_input_handler_new( - messenger, im_context, FL_TEXT_INPUT_VIEW_DELEGATE(self)); -} - static void init_scrolling(FlView* self) { g_clear_object(&self->scrolling_manager); self->scrolling_manager = @@ -277,7 +252,6 @@ static void update_semantics_cb(FlEngine* engine, // which usually indicates the user has requested a hot restart (Shift-R in the // Flutter CLI.) static void on_pre_engine_restart_cb(FlView* self) { - init_keyboard(self); init_scrolling(self); init_touch(self); } @@ -323,18 +297,6 @@ static void fl_view_plugin_registry_iface_init( iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin; } -static void fl_view_text_input_delegate_iface_init( - FlTextInputViewDelegateInterface* iface) { - iface->translate_coordinates = [](FlTextInputViewDelegate* delegate, - gint view_x, gint view_y, gint* window_x, - gint* window_y) { - FlView* self = FL_VIEW(delegate); - gtk_widget_translate_coordinates(GTK_WIDGET(self), - gtk_widget_get_toplevel(GTK_WIDGET(self)), - view_x, view_y, window_x, window_y); - }; -} - static void sync_modifier_if_needed(FlView* self, GdkEvent* event) { guint event_time = gdk_event_get_time(event); GdkModifierType event_state = static_cast(0); @@ -534,8 +496,6 @@ static void realize_cb(FlView* self) { g_signal_connect_swapped(toplevel_window, "delete-event", G_CALLBACK(window_delete_event_cb), self); - init_keyboard(self); - fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id, FL_RENDERABLE(self)); @@ -674,8 +634,9 @@ static gboolean handle_key_event(FlView* self, GdkEventKey* key_event) { } if (redispatch_event != nullptr) { - if (!fl_text_input_handler_filter_keypress(self->text_input_handler, - redispatch_event)) { + if (!fl_text_input_handler_filter_keypress( + fl_engine_get_text_input_handler(self->engine), + redispatch_event)) { fl_keyboard_manager_add_redispatched_event( fl_engine_get_keyboard_manager(self->engine), redispatch_event); gdk_event_put(fl_key_event_get_origin(redispatch_event)); @@ -687,6 +648,15 @@ static gboolean handle_key_event(FlView* self, GdkEventKey* key_event) { return TRUE; } +// Implements GtkWidget::key_press_event. +static gboolean fl_view_focus_in_event(GtkWidget* widget, + GdkEventFocus* event) { + FlView* self = FL_VIEW(widget); + fl_text_input_handler_set_widget( + fl_engine_get_text_input_handler(self->engine), widget); + return FALSE; +} + // Implements GtkWidget::key_press_event. static gboolean fl_view_key_press_event(GtkWidget* widget, GdkEventKey* key_event) { @@ -708,6 +678,7 @@ static void fl_view_class_init(FlViewClass* klass) { GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); widget_class->realize = fl_view_realize; + widget_class->focus_in_event = fl_view_focus_in_event; widget_class->key_press_event = fl_view_key_press_event; widget_class->key_release_event = fl_view_key_release_event; diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc b/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc index b03207079c..d453b983fd 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc +++ b/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc @@ -60,6 +60,12 @@ MockEpoxy::MockEpoxy() { mock = this; } +MockEpoxy::~MockEpoxy() { + if (mock == this) { + mock = nullptr; + } +} + static bool check_display(EGLDisplay dpy) { if (dpy == nullptr) { mock_error = EGL_BAD_DISPLAY; diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h b/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h index 846d43420b..80bc05bb36 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h +++ b/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h @@ -16,6 +16,7 @@ namespace testing { class MockEpoxy { public: MockEpoxy(); + ~MockEpoxy(); MOCK_METHOD(bool, epoxy_has_gl_extension, (const char* extension)); MOCK_METHOD(bool, epoxy_is_desktop_gl, ()); diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_im_context.cc b/engine/src/flutter/shell/platform/linux/testing/mock_im_context.cc index facf66fa39..4984452fb1 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_im_context.cc +++ b/engine/src/flutter/shell/platform/linux/testing/mock_im_context.cc @@ -6,116 +6,87 @@ using namespace flutter::testing; -G_DECLARE_FINAL_TYPE(FlMockIMContext, - fl_mock_im_context, - FL, - MOCK_IM_CONTEXT, - GtkIMContext) +static MockIMContext* mock = nullptr; -struct _FlMockIMContext { - GtkIMContext parent_instance; - MockIMContext* mock; -}; - -G_DEFINE_TYPE(FlMockIMContext, fl_mock_im_context, GTK_TYPE_IM_CONTEXT) - -static void fl_mock_im_context_set_client_window(GtkIMContext* context, - GdkWindow* window) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_set_client_window(context, window); -} - -static void fl_mock_im_context_get_preedit_string(GtkIMContext* context, - gchar** str, - PangoAttrList** attrs, - gint* cursor_pos) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_get_preedit_string(context, str, attrs, - cursor_pos); -} - -static gboolean fl_mock_im_context_filter_keypress(GtkIMContext* context, - GdkEventKey* event) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - return self->mock->gtk_im_context_filter_keypress(context, event); -} - -static void fl_mock_im_context_focus_in(GtkIMContext* context) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_focus_in(context); -} - -static void fl_mock_im_context_focus_out(GtkIMContext* context) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_focus_out(context); -} - -static void fl_mock_im_context_reset(GtkIMContext* context) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_reset(context); -} - -static void fl_mock_im_context_set_cursor_location(GtkIMContext* context, - GdkRectangle* area) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_set_cursor_location(context, area); -} - -static void fl_mock_im_context_set_use_preedit(GtkIMContext* context, - gboolean use_preedit) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_set_use_preedit(context, use_preedit); -} - -static void fl_mock_im_context_set_surrounding(GtkIMContext* context, - const gchar* text, - gint len, - gint cursor_index) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - self->mock->gtk_im_context_set_surrounding(context, text, len, cursor_index); -} - -static gboolean fl_mock_im_context_get_surrounding(GtkIMContext* context, - gchar** text, - gint* cursor_index) { - FlMockIMContext* self = FL_MOCK_IM_CONTEXT(context); - return self->mock->gtk_im_context_get_surrounding(context, text, - cursor_index); -} - -static void fl_mock_im_context_class_init(FlMockIMContextClass* klass) { - GtkIMContextClass* im_context_class = GTK_IM_CONTEXT_CLASS(klass); - im_context_class->set_client_window = fl_mock_im_context_set_client_window; - im_context_class->get_preedit_string = fl_mock_im_context_get_preedit_string; - im_context_class->filter_keypress = fl_mock_im_context_filter_keypress; - im_context_class->focus_in = fl_mock_im_context_focus_in; - im_context_class->focus_out = fl_mock_im_context_focus_out; - im_context_class->reset = fl_mock_im_context_reset; - im_context_class->set_cursor_location = - fl_mock_im_context_set_cursor_location; - im_context_class->set_use_preedit = fl_mock_im_context_set_use_preedit; - im_context_class->set_surrounding = fl_mock_im_context_set_surrounding; - im_context_class->get_surrounding = fl_mock_im_context_get_surrounding; -} - -static void fl_mock_im_context_init(FlMockIMContext* self) {} - -static GtkIMContext* fl_mock_im_context_new(MockIMContext* mock) { - FlMockIMContext* self = - FL_MOCK_IM_CONTEXT(g_object_new(fl_mock_im_context_get_type(), nullptr)); - self->mock = mock; - return GTK_IM_CONTEXT(self); +MockIMContext::MockIMContext() { + mock = this; } MockIMContext::~MockIMContext() { - if (FL_IS_MOCK_IM_CONTEXT(instance_)) { - g_clear_object(&instance_); + if (mock == this) { + mock = nullptr; } } -MockIMContext::operator GtkIMContext*() { - if (instance_ == nullptr) { - instance_ = fl_mock_im_context_new(this); +void gtk_im_context_set_client_window(GtkIMContext* context, + GdkWindow* window) { + if (mock != nullptr) { + mock->gtk_im_context_set_client_window(context, window); } - return instance_; +} + +void gtk_im_context_get_preedit_string(GtkIMContext* context, + gchar** str, + PangoAttrList** attrs, + gint* cursor_pos) { + if (mock != nullptr) { + mock->gtk_im_context_get_preedit_string(context, str, attrs, cursor_pos); + } +} + +gboolean gtk_im_context_filter_keypress(GtkIMContext* context, + GdkEventKey* event) { + if (mock == nullptr) { + return TRUE; + } + + return mock->gtk_im_context_filter_keypress(context, event); +} + +void gtk_im_context_focus_in(GtkIMContext* context) { + if (mock != nullptr) { + mock->gtk_im_context_focus_in(context); + } +} + +void gtk_im_context_focus_out(GtkIMContext* context) { + if (mock != nullptr) { + mock->gtk_im_context_focus_out(context); + } +} + +void gtk_im_context_set_cursor_location(GtkIMContext* context, + const GdkRectangle* area) { + if (mock != nullptr) { + mock->gtk_im_context_set_cursor_location(context, area); + } +} + +void gtk_im_context_set_surrounding(GtkIMContext* context, + const gchar* text, + gint len, + gint cursor_index) { + if (mock != nullptr) { + mock->gtk_im_context_set_surrounding(context, text, len, cursor_index); + } +} + +gboolean gtk_widget_translate_coordinates(GtkWidget* src_widget, + GtkWidget* dest_widget, + gint src_x, + gint src_y, + gint* dest_x, + gint* dest_y) { + if (mock == nullptr) { + *dest_x = src_x; + *dest_y = src_y; + return TRUE; + } + + return mock->gtk_widget_translate_coordinates(src_widget, dest_widget, src_x, + src_y, dest_x, dest_y); +} + +GtkWidget* gtk_widget_get_toplevel(GtkWidget* widget) { + return widget; } diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_im_context.h b/engine/src/flutter/shell/platform/linux/testing/mock_im_context.h index 243030cc8a..3e1d784950 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_im_context.h +++ b/engine/src/flutter/shell/platform/linux/testing/mock_im_context.h @@ -14,14 +14,9 @@ namespace testing { class MockIMContext { public: + MockIMContext(); ~MockIMContext(); - // This was an existing use of operator overloading. It's against our style - // guide but enabling clang tidy on header files is a higher priority than - // fixing this. - // NOLINTNEXTLINE(google-explicit-constructor) - operator GtkIMContext*(); - MOCK_METHOD(void, gtk_im_context_set_client_window, (GtkIMContext * context, GdkWindow* window)); @@ -36,23 +31,22 @@ class MockIMContext { (GtkIMContext * context, GdkEventKey* event)); MOCK_METHOD(gboolean, gtk_im_context_focus_in, (GtkIMContext * context)); MOCK_METHOD(void, gtk_im_context_focus_out, (GtkIMContext * context)); - MOCK_METHOD(void, gtk_im_context_reset, (GtkIMContext * context)); MOCK_METHOD(void, gtk_im_context_set_cursor_location, - (GtkIMContext * context, GdkRectangle* area)); - MOCK_METHOD(void, - gtk_im_context_set_use_preedit, - (GtkIMContext * context, gboolean use_preedit)); + (GtkIMContext * context, const GdkRectangle* area)); MOCK_METHOD( void, gtk_im_context_set_surrounding, (GtkIMContext * context, const gchar* text, gint len, gint cursor_index)); MOCK_METHOD(gboolean, - gtk_im_context_get_surrounding, - (GtkIMContext * context, gchar** text, gint* cursor_index)); - - private: - GtkIMContext* instance_ = nullptr; + gtk_widget_translate_coordinates, + (GtkWidget * src_widget, + GtkWidget* dest_widget, + gint src_x, + gint src_y, + gint* dest_x, + gint* dest_y)); + MOCK_METHOD(GtkWidget*, gtk_widget_get_toplevel, (GtkWidget * widget)); }; } // namespace testing diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_keymap.cc b/engine/src/flutter/shell/platform/linux/testing/mock_keymap.cc index 3858244dd3..c510eda802 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_keymap.cc +++ b/engine/src/flutter/shell/platform/linux/testing/mock_keymap.cc @@ -28,6 +28,12 @@ MockKeymap::MockKeymap() { mock = this; } +MockKeymap::~MockKeymap() { + if (mock == this) { + mock = nullptr; + } +} + GdkKeymap* gdk_keymap_get_for_display(GdkDisplay* display) { FlMockKeymap* keymap = FL_MOCK_KEYMAP(g_object_new(fl_mock_keymap_get_type(), nullptr)); diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_keymap.h b/engine/src/flutter/shell/platform/linux/testing/mock_keymap.h index 4593785ddd..b47304b050 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_keymap.h +++ b/engine/src/flutter/shell/platform/linux/testing/mock_keymap.h @@ -15,6 +15,7 @@ namespace testing { class MockKeymap { public: MockKeymap(); + ~MockKeymap(); MOCK_METHOD(GdkKeymap*, gdk_keymap_get_for_display, (GdkDisplay * display)); MOCK_METHOD(guint, diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.cc b/engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.cc deleted file mode 100644 index fa8259cb26..0000000000 --- a/engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Flutter 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 "flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h" - -using namespace flutter::testing; - -G_DECLARE_FINAL_TYPE(FlMockTextInputViewDelegate, - fl_mock_text_input_view_delegate, - FL, - MOCK_TEXT_INPUT_VIEW_DELEGATE, - GObject) - -struct _FlMockTextInputViewDelegate { - GObject parent_instance; - MockTextInputViewDelegate* mock; -}; - -static FlTextInputViewDelegate* fl_mock_text_input_view_delegate_new( - MockTextInputViewDelegate* mock) { - FlMockTextInputViewDelegate* self = FL_MOCK_TEXT_INPUT_VIEW_DELEGATE( - g_object_new(fl_mock_text_input_view_delegate_get_type(), nullptr)); - self->mock = mock; - return FL_TEXT_INPUT_VIEW_DELEGATE(self); -} - -MockTextInputViewDelegate::MockTextInputViewDelegate() - : instance_(fl_mock_text_input_view_delegate_new(this)) {} - -MockTextInputViewDelegate::~MockTextInputViewDelegate() { - if (FL_IS_TEXT_INPUT_VIEW_DELEGATE(instance_)) { - g_clear_object(&instance_); - } -} - -MockTextInputViewDelegate::operator FlTextInputViewDelegate*() { - return instance_; -} - -static void fl_mock_text_input_view_delegate_iface_init( - FlTextInputViewDelegateInterface* iface); - -G_DEFINE_TYPE_WITH_CODE( - FlMockTextInputViewDelegate, - fl_mock_text_input_view_delegate, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(fl_text_input_view_delegate_get_type(), - fl_mock_text_input_view_delegate_iface_init)) - -static void fl_mock_text_input_view_delegate_class_init( - FlMockTextInputViewDelegateClass* klass) {} - -static void fl_mock_text_input_view_delegate_translate_coordinates( - FlTextInputViewDelegate* view_delegate, - gint view_x, - gint view_y, - gint* window_x, - gint* window_y) { - g_return_if_fail(FL_IS_MOCK_TEXT_INPUT_VIEW_DELEGATE(view_delegate)); - FlMockTextInputViewDelegate* self = - FL_MOCK_TEXT_INPUT_VIEW_DELEGATE(view_delegate); - self->mock->fl_text_input_view_delegate_translate_coordinates( - view_delegate, view_x, view_y, window_x, window_y); -} - -static void fl_mock_text_input_view_delegate_iface_init( - FlTextInputViewDelegateInterface* iface) { - iface->translate_coordinates = - fl_mock_text_input_view_delegate_translate_coordinates; -} - -static void fl_mock_text_input_view_delegate_init( - FlMockTextInputViewDelegate* self) {} diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h b/engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h deleted file mode 100644 index 0f5b2811a2..0000000000 --- a/engine/src/flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_TEXT_INPUT_VIEW_DELEGATE_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_TEXT_INPUT_VIEW_DELEGATE_H_ - -#include - -#include "flutter/shell/platform/linux/fl_text_input_view_delegate.h" - -#include "gmock/gmock.h" - -namespace flutter { -namespace testing { - -// Mock for FlTextInputVuewDelegate. -class MockTextInputViewDelegate { - public: - MockTextInputViewDelegate(); - ~MockTextInputViewDelegate(); - - // NOLINTNEXTLINE(google-explicit-constructor) - operator FlTextInputViewDelegate*(); - - MOCK_METHOD(void, - fl_text_input_view_delegate_translate_coordinates, - (FlTextInputViewDelegate * delegate, - gint view_x, - gint view_y, - gint* window_x, - gint* window_y)); - - private: - FlTextInputViewDelegate* instance_ = nullptr; -}; - -} // namespace testing -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_TEXT_INPUT_VIEW_DELEGATE_H_ diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_window.cc b/engine/src/flutter/shell/platform/linux/testing/mock_window.cc index ab7e3839c0..415a14c521 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_window.cc +++ b/engine/src/flutter/shell/platform/linux/testing/mock_window.cc @@ -12,6 +12,12 @@ MockWindow::MockWindow() { mock = this; } +MockWindow::~MockWindow() { + if (mock == this) { + mock = nullptr; + } +} + GdkDisplay* gdk_display_get_default() { return GDK_DISPLAY(g_object_new(gdk_display_get_type(), nullptr)); } diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_window.h b/engine/src/flutter/shell/platform/linux/testing/mock_window.h index a5dffa4127..bee5e0cd8e 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_window.h +++ b/engine/src/flutter/shell/platform/linux/testing/mock_window.h @@ -15,6 +15,7 @@ namespace testing { class MockWindow { public: MockWindow(); + ~MockWindow(); MOCK_METHOD(GdkWindowState, gdk_window_get_state, (GdkWindow * window)); };