Flutter Linux Embedder
fl_text_input_handler.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <gtk/gtk.h>
8 
12 
13 static constexpr char kNewlineInputAction[] = "TextInputAction.newline";
14 static constexpr char kInputPurposeImProperty[] = "input-purpose";
15 static constexpr char kInputHintsImProperty[] = "input-hints";
16 
17 static constexpr int64_t kClientIdUnset = -1;
18 
20  GObject parent_instance;
21 
22  FlTextInputChannel* channel;
23 
24  // The widget with input focus.
25  GtkWidget* widget;
26 
27  // Client ID provided by Flutter to report events with.
28  int64_t client_id;
29 
30  // Input action to perform when enter pressed.
31  gchar* input_action;
32 
33  // The type of the input method.
35 
36  // Whether to enable that the engine sends text input updates to the framework
37  // as TextEditingDeltas or as one TextEditingValue.
38  // For more information on the delta model, see:
39  // https://master-api.flutter.dev/flutter/services/TextInputConfiguration/enableDeltaModel.html
41 
42  // Input method.
43  GtkIMContext* im_context;
44 
46 
47  // A 4x4 matrix that maps from `EditableText` local coordinates to the
48  // coordinate system of `PipelineOwner.rootNode`.
49  double editabletext_transform[4][4];
50 
51  // The smallest rect, in local coordinates, of the text in the composing
52  // range, or of the caret in the case where there is no current composing
53  // range. This value is updated via `TextInput.setMarkedTextRect` messages
54  // over the text input channel.
55  GdkRectangle composing_rect;
56 
57  GCancellable* cancellable;
58 };
59 
60 G_DEFINE_TYPE(FlTextInputHandler, fl_text_input_handler, G_TYPE_OBJECT)
61 
62 // Called when a response is received from TextInputClient.updateEditingState()
63 static void update_editing_state_response_cb(GObject* object,
64  GAsyncResult* result,
65  gpointer user_data) {
66  g_autoptr(GError) error = nullptr;
68  &error)) {
69  if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
70  g_warning("Failed to update editing state: %s", error->message);
71  }
72  }
73 }
74 
75 // Called when a response is received from
76 // TextInputClient.updateEditingStateWithDeltas()
78  GAsyncResult* result,
79  gpointer user_data) {
80  g_autoptr(GError) error = nullptr;
82  object, result, &error)) {
83  if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
84  g_warning("Failed to update editing state with deltas: %s",
85  error->message);
86  }
87  }
88 }
89 
90 // Informs Flutter of text input changes.
91 static void update_editing_state(FlTextInputHandler* self) {
92  int composing_base = -1;
93  int composing_extent = -1;
94  if (!self->text_model->composing_range().collapsed()) {
95  composing_base = self->text_model->composing_range().base();
96  composing_extent = self->text_model->composing_range().extent();
97  }
98  flutter::TextRange selection = self->text_model->selection();
100  self->channel, self->client_id, self->text_model->GetText().c_str(),
101  selection.base(), selection.extent(), FL_TEXT_AFFINITY_DOWNSTREAM, FALSE,
102  composing_base, composing_extent, self->cancellable,
104 }
105 
106 // Informs Flutter of text input changes by passing just the delta.
107 static void update_editing_state_with_delta(FlTextInputHandler* self,
108  flutter::TextEditingDelta* delta) {
109  flutter::TextRange selection = self->text_model->selection();
110  int composing_base = -1;
111  int composing_extent = -1;
112  if (!self->text_model->composing_range().collapsed()) {
113  composing_base = self->text_model->composing_range().base();
114  composing_extent = self->text_model->composing_range().extent();
115  }
117  self->channel, self->client_id, delta->old_text().c_str(),
118  delta->delta_text().c_str(), delta->delta_start(), delta->delta_end(),
119  selection.base(), selection.extent(), FL_TEXT_AFFINITY_DOWNSTREAM, FALSE,
120  composing_base, composing_extent, self->cancellable,
122 }
123 
124 // Called when a response is received from TextInputClient.performAction()
125 static void perform_action_response_cb(GObject* object,
126  GAsyncResult* result,
127  gpointer user_data) {
128  g_autoptr(GError) error = nullptr;
129  if (!fl_text_input_channel_perform_action_finish(object, result, &error)) {
130  if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
131  g_warning("Failed to perform action: %s", error->message);
132  }
133  }
134 }
135 
136 // Inform Flutter that the input has been activated.
137 static void perform_action(FlTextInputHandler* self) {
138  g_return_if_fail(FL_IS_TEXT_INPUT_HANDLER(self));
139  g_return_if_fail(self->client_id != 0);
140  g_return_if_fail(self->input_action != nullptr);
141 
142  fl_text_input_channel_perform_action(self->channel, self->client_id,
143  self->input_action, self->cancellable,
145 }
146 
147 // Signal handler for GtkIMContext::preedit-start
148 static void im_preedit_start_cb(FlTextInputHandler* self) {
149  self->text_model->BeginComposing();
150 }
151 
152 // Signal handler for GtkIMContext::preedit-changed
153 static void im_preedit_changed_cb(FlTextInputHandler* self) {
154  std::string text_before_change = self->text_model->GetText();
155  flutter::TextRange composing_before_change =
156  self->text_model->composing_range();
157  g_autofree gchar* buf = nullptr;
158  gint cursor_offset = 0;
159  gtk_im_context_get_preedit_string(self->im_context, &buf, nullptr,
160  &cursor_offset);
161  if (self->text_model->composing()) {
162  cursor_offset += self->text_model->composing_range().start();
163  } else {
164  cursor_offset += self->text_model->selection().start();
165  }
166  self->text_model->UpdateComposingText(buf);
167  self->text_model->SetSelection(flutter::TextRange(cursor_offset));
168 
169  if (self->enable_delta_model) {
170  std::string text(buf);
172  text_before_change, composing_before_change, text);
173  update_editing_state_with_delta(self, &delta);
174  } else {
175  update_editing_state(self);
176  }
177 }
178 
179 // Signal handler for GtkIMContext::commit
180 static void im_commit_cb(FlTextInputHandler* self, const gchar* text) {
181  std::string text_before_change = self->text_model->GetText();
182  flutter::TextRange composing_before_change =
183  self->text_model->composing_range();
184  flutter::TextRange selection_before_change = self->text_model->selection();
185  gboolean was_composing = self->text_model->composing();
186 
187  self->text_model->AddText(text);
188  if (self->text_model->composing()) {
189  self->text_model->CommitComposing();
190  }
191 
192  if (self->enable_delta_model) {
193  flutter::TextRange replace_range =
194  was_composing ? composing_before_change : selection_before_change;
195  std::unique_ptr<flutter::TextEditingDelta> delta =
196  std::make_unique<flutter::TextEditingDelta>(text_before_change,
197  replace_range, text);
198  update_editing_state_with_delta(self, delta.get());
199  } else {
200  update_editing_state(self);
201  }
202 }
203 
204 // Signal handler for GtkIMContext::preedit-end
205 static void im_preedit_end_cb(FlTextInputHandler* self) {
206  self->text_model->EndComposing();
207  if (self->enable_delta_model) {
209  flutter::TextEditingDelta(self->text_model->GetText());
210  update_editing_state_with_delta(self, &delta);
211  } else {
212  update_editing_state(self);
213  }
214 }
215 
216 // Signal handler for GtkIMContext::retrieve-surrounding
217 static gboolean im_retrieve_surrounding_cb(FlTextInputHandler* self) {
218  auto text = self->text_model->GetText();
219  size_t cursor_offset = self->text_model->GetCursorOffset();
220  gtk_im_context_set_surrounding(self->im_context, text.c_str(), -1,
221  cursor_offset);
222  return TRUE;
223 }
224 
225 // Signal handler for GtkIMContext::delete-surrounding
226 static gboolean im_delete_surrounding_cb(FlTextInputHandler* self,
227  gint offset,
228  gint n_chars) {
229  std::string text_before_change = self->text_model->GetText();
230  if (self->text_model->DeleteSurrounding(offset, n_chars)) {
231  if (self->enable_delta_model) {
233  text_before_change, self->text_model->composing_range(),
234  self->text_model->GetText());
235  update_editing_state_with_delta(self, &delta);
236  } else {
237  update_editing_state(self);
238  }
239  }
240  return TRUE;
241 }
242 
243 // Called when the input method client is set up.
244 static void set_client(int64_t client_id, gpointer user_data) {
245  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
246 
247  self->client_id = client_id;
248 }
249 
250 // Called when the input method configuration is changed.
251 static void configure(const gchar* input_action,
252  gboolean enable_delta_model,
253  FlTextInputType input_type,
254  GtkInputPurpose im_purpose,
255  GtkInputHints im_hints,
256  gpointer user_data) {
257  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
258 
259  g_free(self->input_action);
260  self->input_action = g_strdup(input_action);
261  self->enable_delta_model = enable_delta_model;
262  self->input_type = input_type;
263 
264  g_object_set(G_OBJECT(self->im_context), kInputPurposeImProperty, im_purpose,
265  nullptr);
266  g_object_set(G_OBJECT(self->im_context), kInputHintsImProperty, im_hints,
267  nullptr);
268 }
269 
270 // Hides the input method.
271 static void hide(gpointer user_data) {
272  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
273 
274  gtk_im_context_focus_out(self->im_context);
275 }
276 
277 // Shows the input method.
278 static void show(gpointer user_data) {
279  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
280 
281  if (self->input_type == FL_TEXT_INPUT_TYPE_NONE) {
282  hide(user_data);
283  return;
284  }
285 
286  gtk_im_context_focus_in(self->im_context);
287 }
288 
289 // Updates the editing state from Flutter.
290 static void set_editing_state(const gchar* text,
291  int64_t selection_base,
292  int64_t selection_extent,
293  int64_t composing_base,
294  int64_t composing_extent,
295  gpointer user_data) {
296  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
297 
298  self->text_model->SetText(text);
299 
300  // Flutter uses -1/-1 for invalid; translate that to 0/0 for the model.
301  if (selection_base == -1 && selection_extent == -1) {
302  selection_base = selection_extent = 0;
303  }
304 
305  self->text_model->SetText(text);
306  self->text_model->SetSelection(
307  flutter::TextRange(selection_base, selection_extent));
308 
309  if (composing_base == -1 && composing_extent == -1) {
310  self->text_model->EndComposing();
311  } else {
312  size_t composing_start = std::min(composing_base, composing_extent);
313  size_t cursor_offset = selection_base - composing_start;
314  self->text_model->SetComposingRange(
315  flutter::TextRange(composing_base, composing_extent), cursor_offset);
316  }
317 }
318 
319 // Called when the input method client is complete.
320 static void clear_client(gpointer user_data) {
321  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
322  self->client_id = kClientIdUnset;
323 }
324 
325 // Update the IM cursor position.
326 //
327 // As text is input by the user, the framework sends two streams of updates
328 // over the text input channel: updates to the composing rect (cursor rect
329 // when not in IME composing mode) and updates to the matrix transform from
330 // local coordinates to Flutter root coordinates. This function is called
331 // after each of these updates. It transforms the composing rect to GDK window
332 // coordinates and notifies GTK of the updated cursor position.
333 static void update_im_cursor_position(FlTextInputHandler* self) {
334  // Skip update if not composing to avoid setting to position 0.
335  if (!self->text_model->composing()) {
336  return;
337  }
338 
339  // Cannot compute a position without a widget (e.g. after the view is
340  // disposed).
341  if (self->widget == nullptr) {
342  return;
343  }
344 
345  // Transform the x, y positions of the cursor from local coordinates to
346  // Flutter view coordinates.
347  gint x = self->composing_rect.x * self->editabletext_transform[0][0] +
348  self->composing_rect.y * self->editabletext_transform[1][0] +
349  self->editabletext_transform[3][0] + self->composing_rect.width;
350  gint y = self->composing_rect.x * self->editabletext_transform[0][1] +
351  self->composing_rect.y * self->editabletext_transform[1][1] +
352  self->editabletext_transform[3][1] + self->composing_rect.height;
353 
354  // Transform from Flutter view coordinates to GTK window coordinates.
355  GdkRectangle preedit_rect = {};
356  gtk_widget_translate_coordinates(self->widget,
357  gtk_widget_get_toplevel(self->widget), x, y,
358  &preedit_rect.x, &preedit_rect.y);
359 
360  // Set the cursor location in window coordinates so that GTK can position
361  // any system input method windows.
362  gtk_im_context_set_cursor_location(self->im_context, &preedit_rect);
363 }
364 
365 // Handles updates to the EditableText size and position from the framework.
366 //
367 // On changes to the size or position of the RenderObject underlying the
368 // EditableText, this update may be triggered. It provides an updated size and
369 // transform from the local coordinate system of the EditableText to root
370 // Flutter coordinate system.
371 static void set_editable_size_and_transform(double* transform,
372  gpointer user_data) {
373  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
374 
375  for (size_t i = 0; i < 16; i++) {
376  self->editabletext_transform[i / 4][i % 4] = transform[i];
377  }
379 }
380 
381 // Handles updates to the composing rect from the framework.
382 //
383 // On changes to the state of the EditableText in the framework, this update
384 // may be triggered. It provides an updated rect for the composing region in
385 // local coordinates of the EditableText. In the case where there is no
386 // composing region, the cursor rect is sent.
387 static void set_marked_text_rect(double x,
388  double y,
389  double width,
390  double height,
391  gpointer user_data) {
392  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
393 
394  self->composing_rect.x = x;
395  self->composing_rect.y = y;
396  self->composing_rect.width = width;
397  self->composing_rect.height = height;
399 }
400 
401 // Disposes of an FlTextInputHandler.
402 static void fl_text_input_handler_dispose(GObject* object) {
403  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(object);
404 
405  g_cancellable_cancel(self->cancellable);
406 
407  g_clear_object(&self->channel);
408  g_clear_pointer(&self->input_action, g_free);
409  g_clear_object(&self->im_context);
410  if (self->text_model != nullptr) {
411  delete self->text_model;
412  self->text_model = nullptr;
413  }
414  g_clear_object(&self->cancellable);
415 
416  G_OBJECT_CLASS(fl_text_input_handler_parent_class)->dispose(object);
417 }
418 
419 // Initializes the FlTextInputHandler class.
420 static void fl_text_input_handler_class_init(FlTextInputHandlerClass* klass) {
421  G_OBJECT_CLASS(klass)->dispose = fl_text_input_handler_dispose;
422 }
423 
424 // Initializes an instance of the FlTextInputHandler class.
425 static void fl_text_input_handler_init(FlTextInputHandler* self) {
426  self->client_id = kClientIdUnset;
427  self->input_type = FL_TEXT_INPUT_TYPE_TEXT;
428  self->text_model = new flutter::TextInputModel();
429  self->cancellable = g_cancellable_new();
430 }
431 
434  .configure = configure,
435  .hide = hide,
436  .show = show,
437  .set_editing_state = set_editing_state,
438  .clear_client = clear_client,
439  .set_editable_size_and_transform = set_editable_size_and_transform,
440  .set_marked_text_rect = set_marked_text_rect,
441 };
442 
443 FlTextInputHandler* fl_text_input_handler_new(FlBinaryMessenger* messenger) {
444  g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
445 
446  FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(
447  g_object_new(fl_text_input_handler_get_type(), nullptr));
448 
449  self->channel =
450  fl_text_input_channel_new(messenger, &text_input_vtable, self);
451 
452  self->im_context = GTK_IM_CONTEXT(gtk_im_multicontext_new());
453 
454  // On Wayland, this call sets up the input method so it can be enabled
455  // immediately when required. Without it, on-screen keyboard's don't come up
456  // the first time a text field is focused.
457  gtk_im_context_focus_out(self->im_context);
458 
459  g_signal_connect_object(self->im_context, "preedit-start",
460  G_CALLBACK(im_preedit_start_cb), self,
461  G_CONNECT_SWAPPED);
462  g_signal_connect_object(self->im_context, "preedit-end",
463  G_CALLBACK(im_preedit_end_cb), self,
464  G_CONNECT_SWAPPED);
465  g_signal_connect_object(self->im_context, "preedit-changed",
466  G_CALLBACK(im_preedit_changed_cb), self,
467  G_CONNECT_SWAPPED);
468  g_signal_connect_object(self->im_context, "commit", G_CALLBACK(im_commit_cb),
469  self, G_CONNECT_SWAPPED);
470  g_signal_connect_object(self->im_context, "retrieve-surrounding",
471  G_CALLBACK(im_retrieve_surrounding_cb), self,
472  G_CONNECT_SWAPPED);
473  g_signal_connect_object(self->im_context, "delete-surrounding",
474  G_CALLBACK(im_delete_surrounding_cb), self,
475  G_CONNECT_SWAPPED);
476 
477  return self;
478 }
479 
480 GtkIMContext* fl_text_input_handler_get_im_context(FlTextInputHandler* self) {
481  g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(self), nullptr);
482  return self->im_context;
483 }
484 
485 void fl_text_input_handler_set_widget(FlTextInputHandler* self,
486  GtkWidget* widget) {
487  g_return_if_fail(FL_IS_TEXT_INPUT_HANDLER(self));
488  self->widget = widget;
489  gtk_im_context_set_client_window(
490  self->im_context,
491  widget != nullptr ? gtk_widget_get_window(widget) : nullptr);
492 }
493 
494 GtkWidget* fl_text_input_handler_get_widget(FlTextInputHandler* self) {
495  g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(self), nullptr);
496  return self->widget;
497 }
498 
499 gboolean fl_text_input_handler_filter_keypress(FlTextInputHandler* self,
500  FlKeyEvent* event) {
501  g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(self), FALSE);
502 
503  if (self->client_id == kClientIdUnset) {
504  return FALSE;
505  }
506 
507  if (gtk_im_context_filter_keypress(
508  self->im_context,
509  reinterpret_cast<GdkEventKey*>(fl_key_event_get_origin(event)))) {
510  return TRUE;
511  }
512 
513  std::string text_before_change = self->text_model->GetText();
514  flutter::TextRange selection_before_change = self->text_model->selection();
515  std::string text = self->text_model->GetText();
516 
517  // Handle the enter/return key.
518  gboolean do_action = FALSE;
519  // Handle navigation keys.
520  gboolean changed = FALSE;
521  if (fl_key_event_get_is_press(event)) {
522  switch (fl_key_event_get_keyval(event)) {
523  case GDK_KEY_End:
524  case GDK_KEY_KP_End:
525  if (fl_key_event_get_state(event) & GDK_SHIFT_MASK) {
526  changed = self->text_model->SelectToEnd();
527  } else {
528  changed = self->text_model->MoveCursorToEnd();
529  }
530  break;
531  case GDK_KEY_Return:
532  case GDK_KEY_KP_Enter:
533  case GDK_KEY_ISO_Enter:
534  if (self->input_type == FL_TEXT_INPUT_TYPE_MULTILINE &&
535  strcmp(self->input_action, kNewlineInputAction) == 0) {
536  self->text_model->AddCodePoint('\n');
537  text = "\n";
538  changed = TRUE;
539  }
540  do_action = TRUE;
541  break;
542  case GDK_KEY_Home:
543  case GDK_KEY_KP_Home:
544  if (fl_key_event_get_state(event) & GDK_SHIFT_MASK) {
545  changed = self->text_model->SelectToBeginning();
546  } else {
547  changed = self->text_model->MoveCursorToBeginning();
548  }
549  break;
550  case GDK_KEY_BackSpace:
551  case GDK_KEY_Delete:
552  case GDK_KEY_KP_Delete:
553  case GDK_KEY_Left:
554  case GDK_KEY_KP_Left:
555  case GDK_KEY_Right:
556  case GDK_KEY_KP_Right:
557  // Already handled inside the framework in RenderEditable.
558  break;
559  }
560  }
561 
562  if (changed) {
563  if (self->enable_delta_model) {
565  text_before_change, selection_before_change, text);
566  update_editing_state_with_delta(self, &delta);
567  } else {
568  update_editing_state(self);
569  }
570  }
571  if (do_action) {
572  perform_action(self);
573  }
574 
575  return changed;
576 }
size_t base() const
Definition: text_range.h:30
size_t extent() const
Definition: text_range.h:36
g_autoptr(FlEngine) engine
const char FlTextDirection FlAssertiveness gpointer user_data
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
FlFramebuffer double double int int height
FlFramebuffer double x
FlFramebuffer double double int width
FlFramebuffer double double y
return TRUE
GdkEvent * fl_key_event_get_origin(FlKeyEvent *self)
gboolean fl_key_event_get_is_press(FlKeyEvent *self)
Definition: fl_key_event.cc:84
GdkModifierType fl_key_event_get_state(FlKeyEvent *self)
Definition: fl_key_event.cc:99
guint fl_key_event_get_keyval(FlKeyEvent *self)
Definition: fl_key_event.cc:94
const uint8_t uint32_t uint32_t GError ** error
void fl_text_input_channel_update_editing_state_with_deltas(FlTextInputChannel *self, int64_t client_id, const gchar *old_text, const gchar *delta_text, int64_t delta_start, int64_t delta_end, int64_t selection_base, int64_t selection_extent, FlTextAffinity selection_affinity, gboolean selection_is_directional, int64_t composing_base, int64_t composing_extent, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
FlTextInputChannel * fl_text_input_channel_new(FlBinaryMessenger *messenger, FlTextInputChannelVTable *vtable, gpointer user_data)
void fl_text_input_channel_update_editing_state(FlTextInputChannel *self, int64_t client_id, const gchar *text, int64_t selection_base, int64_t selection_extent, FlTextAffinity selection_affinity, gboolean selection_is_directional, int64_t composing_base, int64_t composing_extent, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
gboolean fl_text_input_channel_update_editing_state_finish(GObject *object, GAsyncResult *result, GError **error)
void fl_text_input_channel_perform_action(FlTextInputChannel *self, int64_t client_id, const gchar *input_action, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
gboolean fl_text_input_channel_perform_action_finish(GObject *object, GAsyncResult *result, GError **error)
gboolean fl_text_input_channel_update_editing_state_with_deltas_finish(GObject *object, GAsyncResult *result, GError **error)
@ FL_TEXT_AFFINITY_DOWNSTREAM
@ FL_TEXT_INPUT_TYPE_MULTILINE
@ FL_TEXT_INPUT_TYPE_TEXT
@ FL_TEXT_INPUT_TYPE_NONE
static gboolean im_delete_surrounding_cb(FlTextInputHandler *self, gint offset, gint n_chars)
static void show(gpointer user_data)
static void im_preedit_end_cb(FlTextInputHandler *self)
static void update_im_cursor_position(FlTextInputHandler *self)
static void set_marked_text_rect(double x, double y, double width, double height, gpointer user_data)
static gboolean im_retrieve_surrounding_cb(FlTextInputHandler *self)
static void update_editing_state(FlTextInputHandler *self)
static constexpr char kInputHintsImProperty[]
static void clear_client(gpointer user_data)
static void im_preedit_start_cb(FlTextInputHandler *self)
static void hide(gpointer user_data)
static void update_editing_state_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
static void perform_action_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
FlTextInputHandler * fl_text_input_handler_new(FlBinaryMessenger *messenger)
static void set_editable_size_and_transform(double *transform, gpointer user_data)
static void set_client(int64_t client_id, gpointer user_data)
static void update_editing_state_with_delta(FlTextInputHandler *self, flutter::TextEditingDelta *delta)
static constexpr char kInputPurposeImProperty[]
static void set_editing_state(const gchar *text, int64_t selection_base, int64_t selection_extent, int64_t composing_base, int64_t composing_extent, gpointer user_data)
static constexpr char kNewlineInputAction[]
static void update_editing_state_with_deltas_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
static void configure(const gchar *input_action, gboolean enable_delta_model, FlTextInputType input_type, GtkInputPurpose im_purpose, GtkInputHints im_hints, gpointer user_data)
static FlTextInputChannelVTable text_input_vtable
static void fl_text_input_handler_init(FlTextInputHandler *self)
gboolean fl_text_input_handler_filter_keypress(FlTextInputHandler *self, FlKeyEvent *event)
static void im_commit_cb(FlTextInputHandler *self, const gchar *text)
GtkIMContext * fl_text_input_handler_get_im_context(FlTextInputHandler *self)
static void fl_text_input_handler_class_init(FlTextInputHandlerClass *klass)
static void im_preedit_changed_cb(FlTextInputHandler *self)
static constexpr int64_t kClientIdUnset
void fl_text_input_handler_set_widget(FlTextInputHandler *self, GtkWidget *widget)
static void perform_action(FlTextInputHandler *self)
GtkWidget * fl_text_input_handler_get_widget(FlTextInputHandler *self)
static void fl_text_input_handler_dispose(GObject *object)
FlTextInputChannel * channel
flutter::TextInputModel * text_model
double editabletext_transform[4][4]
void(* set_client)(int64_t client_id, gpointer user_data)
A change in the state of an input field.
std::string delta_text() const
int delta_start() const
Get the delta_start_ value.
int delta_end() const
Get the delta_end_ value.
std::string old_text() const