57 G_DEFINE_TYPE(FlTextInputHandler, fl_text_input_handler, G_TYPE_OBJECT)
63 g_autoptr(GError)
error =
nullptr;
66 if (!g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
67 g_warning(
"Failed to update editing state: %s",
error->message);
77 g_autoptr(GError)
error =
nullptr;
79 object, result, &
error)) {
80 if (!g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
81 g_warning(
"Failed to update editing state with deltas: %s",
89 int composing_base = -1;
90 int composing_extent = -1;
91 if (!self->text_model->composing_range().collapsed()) {
92 composing_base =
self->text_model->composing_range().base();
93 composing_extent =
self->text_model->composing_range().extent();
97 self->channel, self->client_id, self->text_model->GetText().c_str(),
99 composing_base, composing_extent, self->cancellable,
107 int composing_base = -1;
108 int composing_extent = -1;
109 if (!self->text_model->composing_range().collapsed()) {
110 composing_base =
self->text_model->composing_range().
base();
111 composing_extent =
self->text_model->composing_range().extent();
114 self->channel, self->client_id, delta->
old_text().c_str(),
117 composing_base, composing_extent, self->cancellable,
123 GAsyncResult* result,
125 g_autoptr(GError)
error =
nullptr;
127 if (!g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
128 g_warning(
"Failed to perform action: %s",
error->message);
135 g_return_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self));
136 g_return_if_fail(self->client_id != 0);
137 g_return_if_fail(self->input_action !=
nullptr);
140 self->input_action, self->cancellable,
146 self->text_model->BeginComposing();
151 std::string text_before_change =
self->text_model->GetText();
153 self->text_model->composing_range();
154 g_autofree gchar* buf =
nullptr;
155 gint cursor_offset = 0;
156 gtk_im_context_get_preedit_string(self->im_context, &buf,
nullptr,
158 if (self->text_model->composing()) {
159 cursor_offset +=
self->text_model->composing_range().start();
161 cursor_offset +=
self->text_model->selection().start();
163 self->text_model->UpdateComposingText(buf);
166 if (self->enable_delta_model) {
167 std::string text(buf);
169 text_before_change, composing_before_change, text);
177 static void im_commit_cb(FlTextInputHandler*
self,
const gchar* text) {
178 std::string text_before_change =
self->text_model->GetText();
180 self->text_model->composing_range();
182 gboolean was_composing =
self->text_model->composing();
184 self->text_model->AddText(text);
185 if (self->text_model->composing()) {
186 self->text_model->CommitComposing();
189 if (self->enable_delta_model) {
191 was_composing ? composing_before_change : selection_before_change;
192 std::unique_ptr<flutter::TextEditingDelta> delta =
193 std::make_unique<flutter::TextEditingDelta>(text_before_change,
194 replace_range, text);
203 self->text_model->EndComposing();
204 if (self->enable_delta_model) {
215 auto text =
self->text_model->GetText();
216 size_t cursor_offset =
self->text_model->GetCursorOffset();
217 gtk_im_context_set_surrounding(self->im_context, text.c_str(), -1,
226 std::string text_before_change =
self->text_model->GetText();
227 if (self->text_model->DeleteSurrounding(offset, n_chars)) {
228 if (self->enable_delta_model) {
230 text_before_change, self->text_model->composing_range(),
231 self->text_model->GetText());
242 const gchar* input_action,
243 gboolean enable_delta_model,
246 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
248 self->client_id = client_id;
249 g_free(self->input_action);
250 self->input_action = g_strdup(input_action);
251 self->enable_delta_model = enable_delta_model;
252 self->input_type = input_type;
257 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
259 gtk_im_context_focus_out(self->im_context);
264 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
271 gtk_im_context_focus_in(self->im_context);
276 int64_t selection_base,
277 int64_t selection_extent,
278 int64_t composing_base,
279 int64_t composing_extent,
281 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
283 self->text_model->SetText(text);
286 if (selection_base == -1 && selection_extent == -1) {
287 selection_base = selection_extent = 0;
290 self->text_model->SetText(text);
291 self->text_model->SetSelection(
294 if (composing_base == -1 && composing_extent == -1) {
295 self->text_model->EndComposing();
297 size_t composing_start = std::min(composing_base, composing_extent);
298 size_t cursor_offset = selection_base - composing_start;
299 self->text_model->SetComposingRange(
306 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
319 g_autoptr(FlTextInputViewDelegate) view_delegate =
320 FL_TEXT_INPUT_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
321 if (view_delegate ==
nullptr) {
326 if (!self->text_model->composing()) {
332 gint x =
self->composing_rect.x *
self->editabletext_transform[0][0] +
333 self->composing_rect.y *
self->editabletext_transform[1][0] +
334 self->editabletext_transform[3][0] +
self->composing_rect.width;
335 gint y =
self->composing_rect.x *
self->editabletext_transform[0][1] +
336 self->composing_rect.y *
self->editabletext_transform[1][1] +
337 self->editabletext_transform[3][1] +
self->composing_rect.height;
340 GdkRectangle preedit_rect = {};
342 view_delegate, x, y, &preedit_rect.x, &preedit_rect.y);
346 gtk_im_context_set_cursor_location(self->im_context, &preedit_rect);
357 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
359 for (
size_t i = 0;
i < 16;
i++) {
360 self->editabletext_transform[
i / 4][
i % 4] = transform[
i];
376 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
378 self->composing_rect.x = x;
379 self->composing_rect.y = y;
380 self->composing_rect.width =
width;
381 self->composing_rect.height =
height;
387 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
object);
389 g_cancellable_cancel(self->cancellable);
391 g_clear_object(&self->channel);
392 g_clear_pointer(&self->input_action, g_free);
393 g_clear_object(&self->im_context);
394 if (self->text_model !=
nullptr) {
395 delete self->text_model;
396 self->text_model =
nullptr;
398 g_weak_ref_clear(&self->view_delegate);
399 g_clear_object(&self->cancellable);
401 G_OBJECT_CLASS(fl_text_input_handler_parent_class)->dispose(
object);
414 self->cancellable = g_cancellable_new();
418 GtkIMContext* im_context) {
419 self->im_context = GTK_IM_CONTEXT(g_object_ref(im_context));
424 gtk_im_context_focus_out(self->im_context);
426 g_signal_connect_object(self->im_context,
"preedit-start",
429 g_signal_connect_object(self->im_context,
"preedit-end",
432 g_signal_connect_object(self->im_context,
"preedit-changed",
435 g_signal_connect_object(self->im_context,
"commit", G_CALLBACK(
im_commit_cb),
436 self, G_CONNECT_SWAPPED);
437 g_signal_connect_object(self->im_context,
"retrieve-surrounding",
440 g_signal_connect_object(self->im_context,
"delete-surrounding",
456 FlBinaryMessenger* messenger,
457 GtkIMContext* im_context,
458 FlTextInputViewDelegate* view_delegate) {
459 g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger),
nullptr);
460 g_return_val_if_fail(GTK_IS_IM_CONTEXT(im_context),
nullptr);
461 g_return_val_if_fail(FL_IS_TEXT_INPUT_VIEW_DELEGATE(view_delegate),
nullptr);
463 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
464 g_object_new(fl_text_input_handler_get_type(),
nullptr));
471 g_weak_ref_init(&self->view_delegate, view_delegate);
478 g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self), FALSE);
484 if (gtk_im_context_filter_keypress(
490 std::string text_before_change =
self->text_model->GetText();
492 std::string text =
self->text_model->GetText();
495 gboolean do_action = FALSE;
497 gboolean changed = FALSE;
503 changed =
self->text_model->SelectToEnd();
505 changed =
self->text_model->MoveCursorToEnd();
509 case GDK_KEY_KP_Enter:
510 case GDK_KEY_ISO_Enter:
513 self->text_model->AddCodePoint(
'\n');
520 case GDK_KEY_KP_Home:
522 changed =
self->text_model->SelectToBeginning();
524 changed =
self->text_model->MoveCursorToBeginning();
527 case GDK_KEY_BackSpace:
529 case GDK_KEY_KP_Delete:
531 case GDK_KEY_KP_Left:
533 case GDK_KEY_KP_Right:
540 if (self->enable_delta_model) {
542 text_before_change, selection_before_change, text);