58 G_DEFINE_TYPE(FlTextInputHandler, fl_text_input_handler, G_TYPE_OBJECT)
64 g_autoptr(GError)
error =
nullptr;
67 if (!g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
68 g_warning(
"Failed to update editing state: %s",
error->message);
78 g_autoptr(GError)
error =
nullptr;
80 object, result, &
error)) {
81 if (!g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
82 g_warning(
"Failed to update editing state with deltas: %s",
90 int composing_base = -1;
91 int composing_extent = -1;
92 if (!self->text_model->composing_range().collapsed()) {
93 composing_base =
self->text_model->composing_range().base();
94 composing_extent =
self->text_model->composing_range().extent();
98 self->channel, self->client_id, self->text_model->GetText().c_str(),
100 composing_base, composing_extent, self->cancellable,
108 int composing_base = -1;
109 int composing_extent = -1;
110 if (!self->text_model->composing_range().collapsed()) {
111 composing_base =
self->text_model->composing_range().
base();
112 composing_extent =
self->text_model->composing_range().extent();
115 self->channel, self->client_id, delta->
old_text().c_str(),
118 composing_base, composing_extent, self->cancellable,
124 GAsyncResult* result,
126 g_autoptr(GError)
error =
nullptr;
128 if (!g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
129 g_warning(
"Failed to perform action: %s",
error->message);
136 g_return_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self));
137 g_return_if_fail(self->client_id != 0);
138 g_return_if_fail(self->input_action !=
nullptr);
141 self->input_action, self->cancellable,
147 self->text_model->BeginComposing();
152 std::string text_before_change =
self->text_model->GetText();
154 self->text_model->composing_range();
155 g_autofree gchar* buf =
nullptr;
156 gint cursor_offset = 0;
157 gtk_im_context_get_preedit_string(self->im_context, &buf,
nullptr,
159 if (self->text_model->composing()) {
160 cursor_offset +=
self->text_model->composing_range().start();
162 cursor_offset +=
self->text_model->selection().start();
164 self->text_model->UpdateComposingText(buf);
167 if (self->enable_delta_model) {
168 std::string text(buf);
170 text_before_change, composing_before_change, text);
178 static void im_commit_cb(FlTextInputHandler*
self,
const gchar* text) {
179 std::string text_before_change =
self->text_model->GetText();
181 self->text_model->composing_range();
183 gboolean was_composing =
self->text_model->composing();
185 self->text_model->AddText(text);
186 if (self->text_model->composing()) {
187 self->text_model->CommitComposing();
190 if (self->enable_delta_model) {
192 was_composing ? composing_before_change : selection_before_change;
193 std::unique_ptr<flutter::TextEditingDelta> delta =
194 std::make_unique<flutter::TextEditingDelta>(text_before_change,
195 replace_range, text);
204 self->text_model->EndComposing();
205 if (self->enable_delta_model) {
216 auto text =
self->text_model->GetText();
217 size_t cursor_offset =
self->text_model->GetCursorOffset();
218 gtk_im_context_set_surrounding(self->im_context, text.c_str(), -1,
227 std::string text_before_change =
self->text_model->GetText();
228 if (self->text_model->DeleteSurrounding(offset, n_chars)) {
229 if (self->enable_delta_model) {
231 text_before_change, self->text_model->composing_range(),
232 self->text_model->GetText());
243 const gchar* input_action,
244 gboolean enable_delta_model,
247 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
249 self->client_id = client_id;
250 g_free(self->input_action);
251 self->input_action = g_strdup(input_action);
252 self->enable_delta_model = enable_delta_model;
253 self->input_type = input_type;
258 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
260 gtk_im_context_focus_out(self->im_context);
265 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
272 gtk_im_context_focus_in(self->im_context);
277 int64_t selection_base,
278 int64_t selection_extent,
279 int64_t composing_base,
280 int64_t composing_extent,
282 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
284 self->text_model->SetText(text);
287 if (selection_base == -1 && selection_extent == -1) {
288 selection_base = selection_extent = 0;
291 self->text_model->SetText(text);
292 self->text_model->SetSelection(
295 if (composing_base == -1 && composing_extent == -1) {
296 self->text_model->EndComposing();
298 size_t composing_start = std::min(composing_base, composing_extent);
299 size_t cursor_offset = selection_base - composing_start;
300 self->text_model->SetComposingRange(
307 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
321 if (!self->text_model->composing()) {
327 gint x =
self->composing_rect.x *
self->editabletext_transform[0][0] +
328 self->composing_rect.y *
self->editabletext_transform[1][0] +
329 self->editabletext_transform[3][0] +
self->composing_rect.width;
330 gint y =
self->composing_rect.x *
self->editabletext_transform[0][1] +
331 self->composing_rect.y *
self->editabletext_transform[1][1] +
332 self->editabletext_transform[3][1] +
self->composing_rect.height;
335 GdkRectangle preedit_rect = {};
336 gtk_widget_translate_coordinates(self->widget,
337 gtk_widget_get_toplevel(self->widget), x, y,
338 &preedit_rect.x, &preedit_rect.y);
342 gtk_im_context_set_cursor_location(self->im_context, &preedit_rect);
353 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
355 for (
size_t i = 0;
i < 16;
i++) {
356 self->editabletext_transform[
i / 4][
i % 4] = transform[
i];
372 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
user_data);
374 self->composing_rect.x = x;
375 self->composing_rect.y = y;
376 self->composing_rect.width =
width;
377 self->composing_rect.height =
height;
383 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
object);
385 g_cancellable_cancel(self->cancellable);
387 g_clear_object(&self->channel);
388 g_clear_pointer(&self->input_action, g_free);
389 g_clear_object(&self->im_context);
390 if (self->text_model !=
nullptr) {
391 delete self->text_model;
392 self->text_model =
nullptr;
394 g_clear_object(&self->cancellable);
396 G_OBJECT_CLASS(fl_text_input_handler_parent_class)->dispose(
object);
409 self->cancellable = g_cancellable_new();
423 g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger),
nullptr);
425 FlTextInputHandler*
self = FL_TEXT_INPUT_HANDLER(
426 g_object_new(fl_text_input_handler_get_type(),
nullptr));
431 self->im_context = GTK_IM_CONTEXT(gtk_im_multicontext_new());
436 gtk_im_context_focus_out(self->im_context);
438 g_signal_connect_object(self->im_context,
"preedit-start",
441 g_signal_connect_object(self->im_context,
"preedit-end",
444 g_signal_connect_object(self->im_context,
"preedit-changed",
447 g_signal_connect_object(self->im_context,
"commit", G_CALLBACK(
im_commit_cb),
448 self, G_CONNECT_SWAPPED);
449 g_signal_connect_object(self->im_context,
"retrieve-surrounding",
452 g_signal_connect_object(self->im_context,
"delete-surrounding",
460 g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self),
nullptr);
461 return self->im_context;
466 g_return_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self));
467 self->widget = widget;
468 gtk_im_context_set_client_window(self->im_context,
469 gtk_widget_get_window(self->widget));
473 g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self),
nullptr);
479 g_return_val_if_fail(FL_IS_TEXT_INPUT_HANDLER(
self), FALSE);
485 if (gtk_im_context_filter_keypress(
491 std::string text_before_change =
self->text_model->GetText();
493 std::string text =
self->text_model->GetText();
496 gboolean do_action = FALSE;
498 gboolean changed = FALSE;
504 changed =
self->text_model->SelectToEnd();
506 changed =
self->text_model->MoveCursorToEnd();
510 case GDK_KEY_KP_Enter:
511 case GDK_KEY_ISO_Enter:
514 self->text_model->AddCodePoint(
'\n');
521 case GDK_KEY_KP_Home:
523 changed =
self->text_model->SelectToBeginning();
525 changed =
self->text_model->MoveCursorToBeginning();
528 case GDK_KEY_BackSpace:
530 case GDK_KEY_KP_Delete:
532 case GDK_KEY_KP_Left:
534 case GDK_KEY_KP_Right:
541 if (self->enable_delta_model) {
543 text_before_change, selection_before_change, text);
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
GdkEvent * fl_key_event_get_origin(FlKeyEvent *self)
gboolean fl_key_event_get_is_press(FlKeyEvent *self)
GdkModifierType fl_key_event_get_state(FlKeyEvent *self)
guint fl_key_event_get_keyval(FlKeyEvent *self)
const uint8_t uint32_t uint32_t * height
const uint8_t uint32_t * width
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 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 update_editing_state_with_delta(FlTextInputHandler *self, flutter::TextEditingDelta *delta)
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 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 set_client(int64_t client_id, const gchar *input_action, gboolean enable_delta_model, FlTextInputType input_type, gpointer user_data)
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)
gboolean enable_delta_model
FlTextInputChannel * channel
flutter::TextInputModel * text_model
GtkIMContext * im_context
double editabletext_transform[4][4]
GdkRectangle composing_rect
FlTextInputType input_type
GCancellable * cancellable
void(* set_client)(int64_t client_id, const gchar *input_action, gboolean enable_delta_model, FlTextInputType input_type, 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