diff --git a/shell/platform/linux/fl_text_input_plugin.cc b/shell/platform/linux/fl_text_input_plugin.cc index 951e21e249d81..bb32331d27a01 100644 --- a/shell/platform/linux/fl_text_input_plugin.cc +++ b/shell/platform/linux/fl_text_input_plugin.cc @@ -263,6 +263,8 @@ static void im_preedit_changed_cb(FlTextInputPlugin* self) { FlTextInputPluginPrivate* priv = static_cast( fl_text_input_plugin_get_instance_private(self)); std::string text_before_change = priv->text_model->GetText(); + flutter::TextRange composing_before_change = + priv->text_model->composing_range(); g_autofree gchar* buf = nullptr; gint cursor_offset = 0; gtk_im_context_get_preedit_string(priv->im_context, &buf, nullptr, @@ -278,7 +280,7 @@ static void im_preedit_changed_cb(FlTextInputPlugin* self) { if (priv->enable_delta_model) { std::string text(buf); flutter::TextEditingDelta delta = flutter::TextEditingDelta( - text_before_change, priv->text_model->composing_range(), text); + text_before_change, composing_before_change, text); update_editing_state_with_delta(self, &delta); } else { update_editing_state(self); @@ -290,7 +292,10 @@ static void im_commit_cb(FlTextInputPlugin* self, const gchar* text) { FlTextInputPluginPrivate* priv = static_cast( fl_text_input_plugin_get_instance_private(self)); std::string text_before_change = priv->text_model->GetText(); + flutter::TextRange composing_before_change = + priv->text_model->composing_range(); flutter::TextRange selection_before_change = priv->text_model->selection(); + gboolean was_composing = priv->text_model->composing(); priv->text_model->AddText(text); if (priv->text_model->composing()) { @@ -298,9 +303,12 @@ static void im_commit_cb(FlTextInputPlugin* self, const gchar* text) { } if (priv->enable_delta_model) { - flutter::TextEditingDelta delta = flutter::TextEditingDelta( - text_before_change, selection_before_change, text); - update_editing_state_with_delta(self, &delta); + flutter::TextRange replace_range = + was_composing ? composing_before_change : selection_before_change; + std::unique_ptr delta = + std::make_unique(text_before_change, + replace_range, text); + update_editing_state_with_delta(self, delta.get()); } else { update_editing_state(self); } @@ -312,8 +320,8 @@ static void im_preedit_end_cb(FlTextInputPlugin* self) { fl_text_input_plugin_get_instance_private(self)); priv->text_model->EndComposing(); if (priv->enable_delta_model) { - flutter::TextEditingDelta delta = flutter::TextEditingDelta( - "", flutter::TextRange(-1, -1), priv->text_model->GetText()); + flutter::TextEditingDelta delta = + flutter::TextEditingDelta(priv->text_model->GetText()); update_editing_state_with_delta(self, &delta); } else { update_editing_state(self); diff --git a/shell/platform/linux/fl_text_input_plugin_test.cc b/shell/platform/linux/fl_text_input_plugin_test.cc index 182e5311ba9bb..d01fa7642f8ba 100644 --- a/shell/platform/linux/fl_text_input_plugin_test.cc +++ b/shell/platform/linux/fl_text_input_plugin_test.cc @@ -958,6 +958,8 @@ TEST(FlTextInputPluginTest, ComposingDelta) { messenger.ReceiveMessage("flutter/textinput", set_client); + g_signal_emit_by_name(context, "preedit-start", nullptr); + // update EXPECT_CALL(context, gtk_im_context_get_preedit_string( @@ -976,7 +978,7 @@ TEST(FlTextInputPluginTest, ComposingDelta) { .old_text = "", .delta_text = "Flutter ", .delta_start = 0, - .delta_end = 8, + .delta_end = 0, .selection_base = 8, .selection_extent = 8, .composing_base = 0, @@ -1004,13 +1006,13 @@ TEST(FlTextInputPluginTest, ComposingDelta) { build_list({ build_editing_delta({ .old_text = "Flutter ", - .delta_text = "engine", - .delta_start = 8, + .delta_text = "Flutter engine", + .delta_start = 0, .delta_end = 8, .selection_base = 14, .selection_extent = 14, - .composing_base = 0, - .composing_extent = 8, + .composing_base = -1, + .composing_extent = -1, }), }), }}), @@ -1024,7 +1026,7 @@ TEST(FlTextInputPluginTest, ComposingDelta) { FlValueEq(commit)), ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "engine", nullptr); + g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr); // end g_autoptr(FlValue) end = build_list({ @@ -1033,7 +1035,7 @@ TEST(FlTextInputPluginTest, ComposingDelta) { "deltas", build_list({ build_editing_delta({ - .delta_text = "Flutter engine", + .old_text = "Flutter engine", .selection_base = 14, .selection_extent = 14, }), @@ -1051,3 +1053,241 @@ TEST(FlTextInputPluginTest, ComposingDelta) { g_signal_emit_by_name(context, "preedit-end", nullptr); } + +TEST(FlTextInputPluginTest, NonComposingDelta) { + ::testing::NiceMock messenger; + ::testing::NiceMock context; + ::testing::NiceMock delegate; + + g_autoptr(FlTextInputPlugin) plugin = + fl_text_input_plugin_new(messenger, context, delegate); + EXPECT_NE(plugin, nullptr); + + // set config + g_autoptr(FlValue) args = build_input_config({ + .client_id = 1, + .enable_delta_model = true, + }); + g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); + g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( + FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr); + + g_autoptr(FlValue) null = fl_value_new_null(); + EXPECT_CALL(messenger, fl_binary_messenger_send_response( + ::testing::Eq(messenger), + ::testing::A(), + SuccessResponse(null), ::testing::A())) + .WillOnce(::testing::Return(true)); + + messenger.ReceiveMessage("flutter/textinput", set_client); + + // commit F + g_autoptr(FlValue) commit = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "", + .delta_text = "F", + .delta_start = 0, + .delta_end = 0, + .selection_base = 1, + .selection_extent = 1, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commit)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "F", nullptr); + + // commit l + g_autoptr(FlValue) commitL = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "F", + .delta_text = "l", + .delta_start = 1, + .delta_end = 1, + .selection_base = 2, + .selection_extent = 2, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commitL)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "l", nullptr); + + // commit u + g_autoptr(FlValue) commitU = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Fl", + .delta_text = "u", + .delta_start = 2, + .delta_end = 2, + .selection_base = 3, + .selection_extent = 3, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commitU)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "u", nullptr); + + // commit t + g_autoptr(FlValue) commitTa = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flu", + .delta_text = "t", + .delta_start = 3, + .delta_end = 3, + .selection_base = 4, + .selection_extent = 4, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commitTa)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "t", nullptr); + + // commit t again + g_autoptr(FlValue) commitTb = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flut", + .delta_text = "t", + .delta_start = 4, + .delta_end = 4, + .selection_base = 5, + .selection_extent = 5, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commitTb)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "t", nullptr); + + // commit e + g_autoptr(FlValue) commitE = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutt", + .delta_text = "e", + .delta_start = 5, + .delta_end = 5, + .selection_base = 6, + .selection_extent = 6, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commitE)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "e", nullptr); + + // commit r + g_autoptr(FlValue) commitR = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutte", + .delta_text = "r", + .delta_start = 6, + .delta_end = 6, + .selection_base = 7, + .selection_extent = 7, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + + EXPECT_CALL(messenger, + fl_binary_messenger_send_on_channel( + ::testing::Eq(messenger), + ::testing::StrEq("flutter/textinput"), + MethodCall("TextInputClient.updateEditingStateWithDeltas", + FlValueEq(commitR)), + ::testing::_, ::testing::_, ::testing::_)); + + g_signal_emit_by_name(context, "commit", "r", nullptr); +}