Flutter Engine
fl_text_input_plugin.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 
5 #include "flutter/shell/platform/linux/fl_text_input_plugin.h"
6 
7 #include <gtk/gtk.h>
8 
9 #include "flutter/shell/platform/common/text_input_model.h"
10 #include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
11 #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
12 
13 static constexpr char kChannelName[] = "flutter/textinput";
14 
15 static constexpr char kBadArgumentsError[] = "Bad Arguments";
16 
17 static constexpr char kSetClientMethod[] = "TextInput.setClient";
18 static constexpr char kShowMethod[] = "TextInput.show";
19 static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState";
20 static constexpr char kClearClientMethod[] = "TextInput.clearClient";
21 static constexpr char kHideMethod[] = "TextInput.hide";
22 static constexpr char kUpdateEditingStateMethod[] =
23  "TextInputClient.updateEditingState";
24 static constexpr char kPerformActionMethod[] = "TextInputClient.performAction";
25 static constexpr char kSetEditableSizeAndTransform[] =
26  "TextInput.setEditableSizeAndTransform";
27 static constexpr char kSetMarkedTextRect[] = "TextInput.setMarkedTextRect";
28 
29 static constexpr char kInputActionKey[] = "inputAction";
30 static constexpr char kTextInputTypeKey[] = "inputType";
31 static constexpr char kTextInputTypeNameKey[] = "name";
32 static constexpr char kTextKey[] = "text";
33 static constexpr char kSelectionBaseKey[] = "selectionBase";
34 static constexpr char kSelectionExtentKey[] = "selectionExtent";
35 static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
36 static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
37 static constexpr char kComposingBaseKey[] = "composingBase";
38 static constexpr char kComposingExtentKey[] = "composingExtent";
39 
40 static constexpr char kTransform[] = "transform";
41 
42 static constexpr char kTextAffinityDownstream[] = "TextAffinity.downstream";
43 static constexpr char kMultilineInputType[] = "TextInputType.multiline";
44 static constexpr char kNoneInputType[] = "TextInputType.none";
45 
46 static constexpr int64_t kClientIdUnset = -1;
47 
48 typedef enum {
50  // Send newline when multi-line and enter is pressed.
52  // The input method is not shown at all.
55 
57  GObject parent_instance;
58 
59  FlMethodChannel* channel;
60 
61  // Client ID provided by Flutter to report events with.
62  int64_t client_id;
63 
64  // Input action to perform when enter pressed.
65  gchar* input_action;
66 
67  // The type of the input method.
69 
70  // Input method.
71  GtkIMContext* im_context;
72 
73  // IM filter.
75 
77 
78  // The owning Flutter view.
79  FlView* view;
80 
81  // A 4x4 matrix that maps from `EditableText` local coordinates to the
82  // coordinate system of `PipelineOwner.rootNode`.
83  double editabletext_transform[4][4];
84 
85  // The smallest rect, in local coordinates, of the text in the composing
86  // range, or of the caret in the case where there is no current composing
87  // range. This value is updated via `TextInput.setMarkedTextRect` messages
88  // over the text input channel.
89  GdkRectangle composing_rect;
90 };
91 
92 G_DEFINE_TYPE_WITH_PRIVATE(FlTextInputPlugin,
93  fl_text_input_plugin,
94  G_TYPE_OBJECT)
95 
96 // Completes method call and returns TRUE if the call was successful.
97 static gboolean finish_method(GObject* object,
98  GAsyncResult* result,
99  GError** error) {
100  g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
101  FL_METHOD_CHANNEL(object), result, error);
102  if (response == nullptr) {
103  return FALSE;
104  }
105  return fl_method_response_get_result(response, error) != nullptr;
106 }
107 
108 // Called when a response is received from TextInputClient.updateEditingState()
109 static void update_editing_state_response_cb(GObject* object,
110  GAsyncResult* result,
111  gpointer user_data) {
112  g_autoptr(GError) error = nullptr;
113  if (!finish_method(object, result, &error)) {
114  g_warning("Failed to call %s: %s", kUpdateEditingStateMethod,
115  error->message);
116  }
117 }
118 
119 // Informs Flutter of text input changes.
120 static void update_editing_state(FlTextInputPlugin* self) {
122  fl_text_input_plugin_get_instance_private(self));
123 
124  g_autoptr(FlValue) args = fl_value_new_list();
126  g_autoptr(FlValue) value = fl_value_new_map();
127 
128  flutter::TextRange selection = priv->text_model->selection();
130  value, kTextKey,
131  fl_value_new_string(priv->text_model->GetText().c_str()));
133  fl_value_new_int(selection.base()));
135  fl_value_new_int(selection.extent()));
136 
137  int composing_base = priv->text_model->composing()
138  ? priv->text_model->composing_range().base()
139  : -1;
140  int composing_extent = priv->text_model->composing()
141  ? priv->text_model->composing_range().extent()
142  : -1;
144  fl_value_new_int(composing_base));
146  fl_value_new_int(composing_extent));
147 
148  // The following keys are not implemented and set to default values.
153 
155 
157  args, nullptr,
159 }
160 
161 // Called when a response is received from TextInputClient.performAction()
162 static void perform_action_response_cb(GObject* object,
163  GAsyncResult* result,
164  gpointer user_data) {
165  g_autoptr(GError) error = nullptr;
166  if (!finish_method(object, result, &error)) {
167  g_warning("Failed to call %s: %s", kPerformActionMethod, error->message);
168  }
169 }
170 
171 // Inform Flutter that the input has been activated.
172 static void perform_action(FlTextInputPlugin* self) {
174  fl_text_input_plugin_get_instance_private(self));
175 
176  g_return_if_fail(FL_IS_TEXT_INPUT_PLUGIN(self));
177  g_return_if_fail(priv->client_id != 0);
178  g_return_if_fail(priv->input_action != nullptr);
179 
180  g_autoptr(FlValue) args = fl_value_new_list();
183 
185  nullptr, perform_action_response_cb, self);
186 }
187 
188 // Signal handler for GtkIMContext::preedit-start
189 static void im_preedit_start_cb(FlTextInputPlugin* self) {
191  fl_text_input_plugin_get_instance_private(self));
192  priv->text_model->BeginComposing();
193 
194  // Set the top-level window used for system input method windows.
195  GdkWindow* window =
196  gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(priv->view)));
197  gtk_im_context_set_client_window(priv->im_context, window);
198 }
199 
200 // Signal handler for GtkIMContext::preedit-changed
201 static void im_preedit_changed_cb(FlTextInputPlugin* self) {
203  fl_text_input_plugin_get_instance_private(self));
204  g_autofree gchar* buf = nullptr;
205  gint cursor_offset = 0;
206  gtk_im_context_get_preedit_string(priv->im_context, &buf, nullptr,
207  &cursor_offset);
208  cursor_offset += priv->text_model->composing_range().base();
209  priv->text_model->UpdateComposingText(buf);
210  priv->text_model->SetSelection(
211  flutter::TextRange(cursor_offset, cursor_offset));
212 
213  update_editing_state(self);
214 }
215 
216 // Signal handler for GtkIMContext::commit
217 static void im_commit_cb(FlTextInputPlugin* self, const gchar* text) {
219  fl_text_input_plugin_get_instance_private(self));
220  priv->text_model->AddText(text);
221  if (priv->text_model->composing()) {
222  priv->text_model->CommitComposing();
223  }
224  update_editing_state(self);
225 }
226 
227 // Signal handler for GtkIMContext::preedit-end
228 static void im_preedit_end_cb(FlTextInputPlugin* self) {
230  fl_text_input_plugin_get_instance_private(self));
231  priv->text_model->EndComposing();
232  update_editing_state(self);
233 }
234 
235 // Signal handler for GtkIMContext::retrieve-surrounding
236 static gboolean im_retrieve_surrounding_cb(FlTextInputPlugin* self) {
238  fl_text_input_plugin_get_instance_private(self));
239  auto text = priv->text_model->GetText();
240  size_t cursor_offset = priv->text_model->GetCursorOffset();
241  gtk_im_context_set_surrounding(priv->im_context, text.c_str(), -1,
242  cursor_offset);
243  return TRUE;
244 }
245 
246 // Signal handler for GtkIMContext::delete-surrounding
247 static gboolean im_delete_surrounding_cb(FlTextInputPlugin* self,
248  gint offset,
249  gint n_chars) {
251  fl_text_input_plugin_get_instance_private(self));
252  if (priv->text_model->DeleteSurrounding(offset, n_chars)) {
253  update_editing_state(self);
254  }
255  return TRUE;
256 }
257 
258 // Called when the input method client is set up.
259 static FlMethodResponse* set_client(FlTextInputPlugin* self, FlValue* args) {
260  if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST ||
261  fl_value_get_length(args) < 2) {
262  return FL_METHOD_RESPONSE(fl_method_error_response_new(
263  kBadArgumentsError, "Expected 2-element list", nullptr));
264  }
266  fl_text_input_plugin_get_instance_private(self));
267 
269  FlValue* config_value = fl_value_get_list_value(args, 1);
270  g_free(priv->input_action);
271  FlValue* input_action_value =
273  if (fl_value_get_type(input_action_value) == FL_VALUE_TYPE_STRING) {
274  priv->input_action = g_strdup(fl_value_get_string(input_action_value));
275  }
276 
277  // Reset the input type, then set only if appropriate.
279  FlValue* input_type_value =
281  if (fl_value_get_type(input_type_value) == FL_VALUE_TYPE_MAP) {
282  FlValue* input_type_name =
284  if (fl_value_get_type(input_type_name) == FL_VALUE_TYPE_STRING) {
285  const gchar* input_type = fl_value_get_string(input_type_name);
286  if (g_strcmp0(input_type, kMultilineInputType) == 0) {
288  } else if (g_strcmp0(input_type, kNoneInputType) == 0) {
290  }
291  }
292  }
293 
294  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
295 }
296 
297 // Hides the input method.
298 static FlMethodResponse* hide(FlTextInputPlugin* self) {
300  fl_text_input_plugin_get_instance_private(self));
301  gtk_im_context_focus_out(priv->im_context);
302 
303  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
304 }
305 
306 // Shows the input method.
307 static FlMethodResponse* show(FlTextInputPlugin* self) {
309  fl_text_input_plugin_get_instance_private(self));
310  if (priv->input_type == FL_TEXT_INPUT_TYPE_NONE) {
311  return hide(self);
312  }
313 
314  // Set the top-level window used for system input method windows.
315  GdkWindow* window =
316  gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(priv->view)));
317  gtk_im_context_set_client_window(priv->im_context, window);
318 
319  gtk_im_context_focus_in(priv->im_context);
320 
321  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
322 }
323 
324 // Updates the editing state from Flutter.
325 static FlMethodResponse* set_editing_state(FlTextInputPlugin* self,
326  FlValue* args) {
328  fl_text_input_plugin_get_instance_private(self));
329  const gchar* text =
331  priv->text_model->SetText(text);
332 
333  int64_t selection_base =
335  int64_t selection_extent =
337  // Flutter uses -1/-1 for invalid; translate that to 0/0 for the model.
338  if (selection_base == -1 && selection_extent == -1) {
339  selection_base = selection_extent = 0;
340  }
341 
342  priv->text_model->SetText(text);
343  priv->text_model->SetSelection(
344  flutter::TextRange(selection_base, selection_extent));
345 
346  int64_t composing_base =
348  int64_t composing_extent =
350  if (composing_base == -1 && composing_extent == -1) {
351  priv->text_model->EndComposing();
352  } else {
353  size_t composing_start = std::min(composing_base, composing_extent);
354  size_t cursor_offset = selection_base - composing_start;
356  flutter::TextRange(composing_base, composing_extent), cursor_offset);
357  }
358 
359  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
360 }
361 
362 // Called when the input method client is complete.
363 static FlMethodResponse* clear_client(FlTextInputPlugin* self) {
365  fl_text_input_plugin_get_instance_private(self));
366  priv->client_id = kClientIdUnset;
367 
368  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
369 }
370 
371 // Update the IM cursor position.
372 //
373 // As text is input by the user, the framework sends two streams of updates
374 // over the text input channel: updates to the composing rect (cursor rect when
375 // not in IME composing mode) and updates to the matrix transform from local
376 // coordinates to Flutter root coordinates. This function is called after each
377 // of these updates. It transforms the composing rect to GTK window coordinates
378 // and notifies GTK of the updated cursor position.
379 static void update_im_cursor_position(FlTextInputPlugin* self) {
381  fl_text_input_plugin_get_instance_private(self));
382 
383  // Skip update if not composing to avoid setting to position 0.
384  if (!priv->text_model->composing()) {
385  return;
386  }
387 
388  // Transform the x, y positions of the cursor from local coordinates to
389  // Flutter view coordinates.
390  gint x = priv->composing_rect.x * priv->editabletext_transform[0][0] +
391  priv->composing_rect.y * priv->editabletext_transform[1][0] +
392  priv->editabletext_transform[3][0] + priv->composing_rect.width;
393  gint y = priv->composing_rect.x * priv->editabletext_transform[0][1] +
394  priv->composing_rect.y * priv->editabletext_transform[1][1] +
395  priv->editabletext_transform[3][1] + priv->composing_rect.height;
396 
397  // Transform from Flutter view coordinates to GTK window coordinates.
398  GdkRectangle preedit_rect;
399  gtk_widget_translate_coordinates(
400  GTK_WIDGET(priv->view), gtk_widget_get_toplevel(GTK_WIDGET(priv->view)),
401  x, y, &preedit_rect.x, &preedit_rect.y);
402 
403  // Set the cursor location in window coordinates so that GTK can position any
404  // system input method windows.
405  gtk_im_context_set_cursor_location(priv->im_context, &preedit_rect);
406 }
407 
408 // Handles updates to the EditableText size and position from the framework.
409 //
410 // On changes to the size or position of the RenderObject underlying the
411 // EditableText, this update may be triggered. It provides an updated size and
412 // transform from the local coordinate system of the EditableText to root
413 // Flutter coordinate system.
414 static FlMethodResponse* set_editable_size_and_transform(
415  FlTextInputPlugin* self,
416  FlValue* args) {
418  size_t transform_len = fl_value_get_length(transform);
419  g_warn_if_fail(transform_len == 16);
420 
421  for (size_t i = 0; i < transform_len; ++i) {
422  double val = fl_value_get_float(fl_value_get_list_value(transform, i));
424  fl_text_input_plugin_get_instance_private(self));
425  priv->editabletext_transform[i / 4][i % 4] = val;
426  }
428 
429  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
430 }
431 
432 // Handles updates to the composing rect from the framework.
433 //
434 // On changes to the state of the EditableText in the framework, this update
435 // may be triggered. It provides an updated rect for the composing region in
436 // local coordinates of the EditableText. In the case where there is no
437 // composing region, the cursor rect is sent.
438 static FlMethodResponse* set_marked_text_rect(FlTextInputPlugin* self,
439  FlValue* args) {
441  fl_text_input_plugin_get_instance_private(self));
442  priv->composing_rect.x =
444  priv->composing_rect.y =
446  priv->composing_rect.width =
448  priv->composing_rect.height =
451 
452  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
453 }
454 
455 // Called when a method call is received from Flutter.
456 static void method_call_cb(FlMethodChannel* channel,
457  FlMethodCall* method_call,
458  gpointer user_data) {
459  FlTextInputPlugin* self = FL_TEXT_INPUT_PLUGIN(user_data);
460 
461  const gchar* method = fl_method_call_get_name(method_call);
462  FlValue* args = fl_method_call_get_args(method_call);
463 
464  g_autoptr(FlMethodResponse) response = nullptr;
465  if (strcmp(method, kSetClientMethod) == 0) {
466  response = set_client(self, args);
467  } else if (strcmp(method, kShowMethod) == 0) {
468  response = show(self);
469  } else if (strcmp(method, kSetEditingStateMethod) == 0) {
470  response = set_editing_state(self, args);
471  } else if (strcmp(method, kClearClientMethod) == 0) {
472  response = clear_client(self);
473  } else if (strcmp(method, kHideMethod) == 0) {
474  response = hide(self);
475  } else if (strcmp(method, kSetEditableSizeAndTransform) == 0) {
476  response = set_editable_size_and_transform(self, args);
477  } else if (strcmp(method, kSetMarkedTextRect) == 0) {
478  response = set_marked_text_rect(self, args);
479  } else {
480  response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
481  }
482 
483  g_autoptr(GError) error = nullptr;
484  if (!fl_method_call_respond(method_call, response, &error)) {
485  g_warning("Failed to send method call response: %s", error->message);
486  }
487 }
488 
489 // Disposes of an FlTextInputPlugin.
490 static void fl_text_input_plugin_dispose(GObject* object) {
491  FlTextInputPlugin* self = FL_TEXT_INPUT_PLUGIN(object);
493  fl_text_input_plugin_get_instance_private(self));
494 
495  g_clear_object(&priv->channel);
496  g_clear_pointer(&priv->input_action, g_free);
497  g_clear_object(&priv->im_context);
498  if (priv->text_model != nullptr) {
499  delete priv->text_model;
500  priv->text_model = nullptr;
501  }
502  priv->view = nullptr;
503 
504  G_OBJECT_CLASS(fl_text_input_plugin_parent_class)->dispose(object);
505 }
506 
507 // Implements FlTextInputPlugin::filter_keypress.
509  FlTextInputPlugin* self,
510  FlKeyEvent* event) {
511  g_return_val_if_fail(FL_IS_TEXT_INPUT_PLUGIN(self), false);
512 
514  fl_text_input_plugin_get_instance_private(self));
515 
516  if (priv->client_id == kClientIdUnset) {
517  return FALSE;
518  }
519 
520  if (priv->im_filter(priv->im_context, event->origin)) {
521  return TRUE;
522  }
523 
524  // Handle the enter/return key.
525  gboolean do_action = FALSE;
526  // Handle navigation keys.
527  gboolean changed = FALSE;
528  if (event->is_press) {
529  switch (event->keyval) {
530  case GDK_KEY_End:
531  case GDK_KEY_KP_End:
532  if (event->state & GDK_SHIFT_MASK) {
533  changed = priv->text_model->SelectToEnd();
534  } else {
535  changed = priv->text_model->MoveCursorToEnd();
536  }
537  break;
538  case GDK_KEY_Return:
539  case GDK_KEY_KP_Enter:
540  case GDK_KEY_ISO_Enter:
542  priv->text_model->AddCodePoint('\n');
543  changed = TRUE;
544  }
545  do_action = TRUE;
546  break;
547  case GDK_KEY_Home:
548  case GDK_KEY_KP_Home:
549  if (event->state & GDK_SHIFT_MASK) {
550  changed = priv->text_model->SelectToBeginning();
551  } else {
552  changed = priv->text_model->MoveCursorToBeginning();
553  }
554  break;
555  case GDK_KEY_BackSpace:
556  case GDK_KEY_Delete:
557  case GDK_KEY_KP_Delete:
558  case GDK_KEY_Left:
559  case GDK_KEY_KP_Left:
560  case GDK_KEY_Right:
561  case GDK_KEY_KP_Right:
562  // Already handled inside the framework in RenderEditable.
563  break;
564  }
565  }
566 
567  if (changed) {
568  update_editing_state(self);
569  }
570  if (do_action) {
571  perform_action(self);
572  }
573 
574  return changed;
575 }
576 
577 // Initializes the FlTextInputPlugin class.
578 static void fl_text_input_plugin_class_init(FlTextInputPluginClass* klass) {
579  G_OBJECT_CLASS(klass)->dispose = fl_text_input_plugin_dispose;
580  FL_TEXT_INPUT_PLUGIN_CLASS(klass)->filter_keypress =
582 }
583 
584 // Initializes an instance of the FlTextInputPlugin class.
585 static void fl_text_input_plugin_init(FlTextInputPlugin* self) {
587  fl_text_input_plugin_get_instance_private(self));
588 
589  priv->client_id = kClientIdUnset;
590  priv->im_context = gtk_im_multicontext_new();
592  g_signal_connect_object(priv->im_context, "preedit-start",
593  G_CALLBACK(im_preedit_start_cb), self,
594  G_CONNECT_SWAPPED);
595  g_signal_connect_object(priv->im_context, "preedit-end",
596  G_CALLBACK(im_preedit_end_cb), self,
597  G_CONNECT_SWAPPED);
598  g_signal_connect_object(priv->im_context, "preedit-changed",
599  G_CALLBACK(im_preedit_changed_cb), self,
600  G_CONNECT_SWAPPED);
601  g_signal_connect_object(priv->im_context, "commit", G_CALLBACK(im_commit_cb),
602  self, G_CONNECT_SWAPPED);
603  g_signal_connect_object(priv->im_context, "retrieve-surrounding",
604  G_CALLBACK(im_retrieve_surrounding_cb), self,
605  G_CONNECT_SWAPPED);
606  g_signal_connect_object(priv->im_context, "delete-surrounding",
607  G_CALLBACK(im_delete_surrounding_cb), self,
608  G_CONNECT_SWAPPED);
609  priv->text_model = new flutter::TextInputModel();
610 }
611 
612 FlTextInputPlugin* fl_text_input_plugin_new(
613  FlBinaryMessenger* messenger,
614  FlView* view,
616  g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
617  g_return_val_if_fail(im_filter != nullptr, nullptr);
618 
619  FlTextInputPlugin* self = FL_TEXT_INPUT_PLUGIN(
620  g_object_new(fl_text_input_plugin_get_type(), nullptr));
621 
622  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
624  fl_text_input_plugin_get_instance_private(self));
625  priv->channel =
626  fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
628  nullptr);
629  priv->view = view;
630  priv->im_filter = im_filter;
631 
632  return self;
633 }
634 
635 // Filters the a keypress given to the plugin through the plugin's
636 // filter_keypress callback.
637 gboolean fl_text_input_plugin_filter_keypress(FlTextInputPlugin* self,
638  FlKeyEvent* event) {
639  g_return_val_if_fail(FL_IS_TEXT_INPUT_PLUGIN(self), FALSE);
640  if (FL_TEXT_INPUT_PLUGIN_GET_CLASS(self)->filter_keypress) {
641  return FL_TEXT_INPUT_PLUGIN_GET_CLASS(self)->filter_keypress(self, event);
642  }
643  return FALSE;
644 }
G_MODULE_EXPORT FlValue * fl_value_new_list()
Definition: fl_value.cc:338
G_BEGIN_DECLS FlValue * args
G_MODULE_EXPORT FlMethodSuccessResponse * fl_method_success_response_new(FlValue *result)
static constexpr char kSetMarkedTextRect[]
G_BEGIN_DECLS FlMethodCall * method_call
return fl_method_response_get_result(response, error) !
G_MODULE_EXPORT FlMethodResponse * fl_method_channel_invoke_method_finish(FlMethodChannel *self, GAsyncResult *result, GError **error)
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue *self)
Definition: fl_value.cc:428
static constexpr char kSetClientMethod[]
static gboolean im_delete_surrounding_cb(FlTextInputPlugin *self, gint offset, gint n_chars)
size_t extent() const
Definition: text_range.h:36
FlTextInputPluginImFilter im_filter
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:40
G_MODULE_EXPORT FlJsonMethodCodec * fl_json_method_codec_new()
void SetText(const std::string &text)
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition: fl_value.cc:355
static void im_preedit_end_cb(FlTextInputPlugin *self)
static constexpr char kShowMethod[]
void * user_data
std::string GetText() const
G_MODULE_EXPORT int64_t fl_value_get_int(FlValue *self)
Definition: fl_value.cc:628
static gboolean im_retrieve_surrounding_cb(FlTextInputPlugin *self)
GAsyncResult * result
guint keyval
Definition: fl_key_event.h:39
static constexpr char kTextAffinityDownstream[]
void AddCodePoint(char32_t c)
static FlMethodResponse * set_editing_state(FlTextInputPlugin *self, FlValue *args)
bool SetComposingRange(const TextRange &range, size_t cursor_offset)
static constexpr char kSetEditableSizeAndTransform[]
GAsyncResult GError ** error
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition: fl_value.cc:244
static constexpr char kPerformActionMethod[]
G_MODULE_EXPORT FlValue * fl_method_call_get_args(FlMethodCall *self)
FlKeyEvent * event
static constexpr char kComposingBaseKey[]
G_MODULE_EXPORT void fl_value_append(FlValue *self, FlValue *value)
Definition: fl_value.cc:552
static void fl_text_input_plugin_dispose(GObject *object)
void AddText(const std::u16string &text)
static constexpr char kSelectionIsDirectionalKey[]
static constexpr char kMultilineInputType[]
static constexpr char kComposingExtentKey[]
G_MODULE_EXPORT double fl_value_get_float(FlValue *self)
Definition: fl_value.cc:635
G_MODULE_EXPORT void fl_value_append_take(FlValue *self, FlValue *value)
Definition: fl_value.cc:560
static void im_preedit_start_cb(FlTextInputPlugin *self)
uint8_t value
G_DEFINE_TYPE_WITH_PRIVATE(FlTextInputPlugin, fl_text_input_plugin, G_TYPE_OBJECT) static gboolean finish_method(GObject *object
G_MODULE_EXPORT FlValue * fl_value_lookup_string(FlValue *self, const gchar *key)
Definition: fl_value.cc:770
G_MODULE_EXPORT gboolean fl_method_call_respond(FlMethodCall *self, FlMethodResponse *response, GError **error)
static constexpr char kInputActionKey[]
static void update_im_cursor_position(FlTextInputPlugin *self)
static void update_editing_state(FlTextInputPlugin *self)
static FlMethodResponse * set_editable_size_and_transform(FlTextInputPlugin *self, FlValue *args)
gboolean fl_text_input_plugin_filter_keypress(FlTextInputPlugin *self, FlKeyEvent *event)
static FlMethodResponse * hide(FlTextInputPlugin *self)
static void im_commit_cb(FlTextInputPlugin *self, const gchar *text)
G_MODULE_EXPORT void fl_method_channel_set_method_call_handler(FlMethodChannel *self, FlMethodChannelMethodCallHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
static constexpr char kSelectionAffinityKey[]
G_MODULE_EXPORT const gchar * fl_method_call_get_name(FlMethodCall *self)
G_MODULE_EXPORT FlMethodChannel * fl_method_channel_new(FlBinaryMessenger *messenger, const gchar *name, FlMethodCodec *codec)
static void perform_action(FlTextInputPlugin *self)
TextRange composing_range() const
static void fl_text_input_plugin_class_init(FlTextInputPluginClass *klass)
static constexpr char kTextKey[]
static constexpr char kSetEditingStateMethod[]
static constexpr char kSelectionExtentKey[]
static constexpr int64_t kClientIdUnset
static FlMethodResponse * set_marked_text_rect(FlTextInputPlugin *self, FlValue *args)
G_MODULE_EXPORT FlValue * fl_value_get_list_value(FlValue *self, size_t index)
Definition: fl_value.cc:735
gpointer origin
Definition: fl_key_event.h:50
static void update_editing_state_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
static constexpr char kClearClientMethod[]
static FlMethodResponse * clear_client(FlTextInputPlugin *self)
static constexpr char kSelectionBaseKey[]
gboolean(* FlTextInputPluginImFilter)(GtkIMContext *im_context, gpointer gdk_event)
size_t base() const
Definition: text_range.h:30
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition: fl_value.cc:251
static constexpr char kNoneInputType[]
TextRange selection() const
static constexpr char kChannelName[]
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
Definition: fl_value.cc:265
G_MODULE_EXPORT void fl_method_channel_invoke_method(FlMethodChannel *self, const gchar *method, FlValue *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
G_MODULE_EXPORT const gchar * fl_value_get_string(FlValue *self)
Definition: fl_value.cc:642
static void perform_action_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
std::u16string text
static void fl_text_input_plugin_init(FlTextInputPlugin *self)
static constexpr char kHideMethod[]
G_MODULE_EXPORT FlMethodNotImplementedResponse * fl_method_not_implemented_response_new()
FlTextInputPlugin * fl_text_input_plugin_new(FlBinaryMessenger *messenger, FlView *view, FlTextInputPluginImFilter im_filter)
flutter::TextInputModel * text_model
static void im_preedit_changed_cb(FlTextInputPlugin *self)
static gboolean fl_text_input_plugin_filter_keypress_default(FlTextInputPlugin *self, FlKeyEvent *event)
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition: fl_value.cc:610
static constexpr char kUpdateEditingStateMethod[]
static constexpr char kBadArgumentsError[]
return FALSE
static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)
static constexpr char kTransform[]
G_MODULE_EXPORT size_t fl_value_get_length(FlValue *self)
Definition: fl_value.cc:684
static FlMethodResponse * set_client(FlTextInputPlugin *self, FlValue *args)
static constexpr char kTextInputTypeNameKey[]
static FlMethodResponse * show(FlTextInputPlugin *self)
bool SetSelection(const TextRange &range)
static constexpr char kTextInputTypeKey[]
G_MODULE_EXPORT FlMethodErrorResponse * fl_method_error_response_new(const gchar *code, const gchar *message, FlValue *details)
bool DeleteSurrounding(int offset_from_cursor, int count)
void UpdateComposingText(const std::u16string &text)