Flutter Engine
 
Loading...
Searching...
No Matches
fl_text_input_channel.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
9
10static constexpr char kChannelName[] = "flutter/textinput";
11
12static constexpr char kBadArgumentsError[] = "Bad Arguments";
13
14static constexpr char kSetClientMethod[] = "TextInput.setClient";
15static constexpr char kShowMethod[] = "TextInput.show";
16static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState";
17static constexpr char kClearClientMethod[] = "TextInput.clearClient";
18static constexpr char kHideMethod[] = "TextInput.hide";
19static constexpr char kUpdateEditingStateMethod[] =
20 "TextInputClient.updateEditingState";
21static constexpr char kUpdateEditingStateWithDeltasMethod[] =
22 "TextInputClient.updateEditingStateWithDeltas";
23static constexpr char kPerformActionMethod[] = "TextInputClient.performAction";
24static constexpr char kSetEditableSizeAndTransform[] =
25 "TextInput.setEditableSizeAndTransform";
26static constexpr char kSetMarkedTextRect[] = "TextInput.setMarkedTextRect";
27
28static constexpr char kInputActionKey[] = "inputAction";
29static constexpr char kTextInputTypeKey[] = "inputType";
30static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
31static constexpr char kTextInputTypeNameKey[] = "name";
32static constexpr char kTextKey[] = "text";
33static constexpr char kSelectionBaseKey[] = "selectionBase";
34static constexpr char kSelectionExtentKey[] = "selectionExtent";
35static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
36static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
37static constexpr char kComposingBaseKey[] = "composingBase";
38static constexpr char kComposingExtentKey[] = "composingExtent";
39
40static constexpr char kTransform[] = "transform";
41
42static constexpr char kMultilineInputType[] = "TextInputType.multiline";
43static constexpr char kNoneInputType[] = "TextInputType.none";
44
45static constexpr char kTextAffinityUpstream[] = "TextAffinity.upstream";
46static constexpr char kTextAffinityDownstream[] = "TextAffinity.downstream";
47
50
51 FlMethodChannel* channel;
52
54
55 gpointer user_data;
56};
57
58G_DEFINE_TYPE(FlTextInputChannel, fl_text_input_channel, G_TYPE_OBJECT)
59
60static const gchar* text_affinity_to_string(FlTextAffinity affinity) {
61 switch (affinity) {
66 default:
67 g_assert_not_reached();
68 }
69}
70
71// Called when the input method client is set up.
72static FlMethodResponse* set_client(FlTextInputChannel* self, FlValue* args) {
75 return FL_METHOD_RESPONSE(fl_method_error_response_new(
76 kBadArgumentsError, "Expected 2-element list", nullptr));
77 }
78
79 int64_t client_id = fl_value_get_int(fl_value_get_list_value(args, 0));
80 FlValue* config_value = fl_value_get_list_value(args, 1);
81 const gchar* input_action = nullptr;
82 FlValue* input_action_value =
84 if (fl_value_get_type(input_action_value) == FL_VALUE_TYPE_STRING) {
85 input_action = fl_value_get_string(input_action_value);
86 }
87
88 FlValue* enable_delta_model_value =
90 gboolean enable_delta_model = fl_value_get_bool(enable_delta_model_value);
91
92 // Reset the input type, then set only if appropriate.
94 FlValue* input_type_value =
96 if (fl_value_get_type(input_type_value) == FL_VALUE_TYPE_MAP) {
97 FlValue* input_type_name_value =
99 if (fl_value_get_type(input_type_name_value) == FL_VALUE_TYPE_STRING) {
100 const gchar* input_type_name = fl_value_get_string(input_type_name_value);
101 if (g_strcmp0(input_type_name, kMultilineInputType) == 0) {
102 input_type = FL_TEXT_INPUT_TYPE_MULTILINE;
103 } else if (g_strcmp0(input_type_name, kNoneInputType) == 0) {
104 input_type = FL_TEXT_INPUT_TYPE_NONE;
105 }
106 }
107 }
108
109 self->vtable->set_client(client_id, input_action, enable_delta_model,
110 input_type, self->user_data);
111
112 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
113}
114
115// Hides the input method.
116static FlMethodResponse* hide(FlTextInputChannel* self) {
117 self->vtable->hide(self->user_data);
118 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
119}
120
121// Shows the input method.
122static FlMethodResponse* show(FlTextInputChannel* self) {
123 self->vtable->show(self->user_data);
124 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
125}
126
127// Updates the editing state from Flutter.
128static FlMethodResponse* set_editing_state(FlTextInputChannel* self,
129 FlValue* args) {
130 const gchar* text =
132 int64_t selection_base =
134 int64_t selection_extent =
136 int64_t composing_base =
138 int64_t composing_extent =
140
141 self->vtable->set_editing_state(text, selection_base, selection_extent,
142 composing_base, composing_extent,
143 self->user_data);
144
145 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
146}
147
148// Called when the input method client is complete.
149static FlMethodResponse* clear_client(FlTextInputChannel* self) {
150 self->vtable->clear_client(self->user_data);
151 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
152}
153
154// Handles updates to the EditableText size and position from the framework.
155//
156// On changes to the size or position of the RenderObject underlying the
157// EditableText, this update may be triggered. It provides an updated size and
158// transform from the local coordinate system of the EditableText to root
159// Flutter coordinate system.
160static FlMethodResponse* set_editable_size_and_transform(
161 FlTextInputChannel* self,
162 FlValue* args) {
163 FlValue* transform_value = fl_value_lookup_string(args, kTransform);
164 if (fl_value_get_length(transform_value) != 16) {
165 return FL_METHOD_RESPONSE(fl_method_error_response_new(
166 kBadArgumentsError, "Invalid transform", nullptr));
167 }
168
169 double transform[16];
170 for (size_t i = 0; i < 16; i++) {
171 transform[i] =
173 }
174 self->vtable->set_editable_size_and_transform(transform, self->user_data);
175
176 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
177}
178
179// Handles updates to the composing rect from the framework.
180//
181// On changes to the state of the EditableText in the framework, this update
182// may be triggered. It provides an updated rect for the composing region in
183// local coordinates of the EditableText. In the case where there is no
184// composing region, the cursor rect is sent.
185static FlMethodResponse* set_marked_text_rect(FlTextInputChannel* self,
186 FlValue* args) {
191
192 self->vtable->set_marked_text_rect(x, y, width, height, self->user_data);
193
194 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
195}
196
197// Called when a method call is received from Flutter.
198static void method_call_cb(FlMethodChannel* channel,
199 FlMethodCall* method_call,
200 gpointer user_data) {
201 FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(user_data);
202
203 const gchar* method = fl_method_call_get_name(method_call);
205
206 g_autoptr(FlMethodResponse) response = nullptr;
207 if (strcmp(method, kSetClientMethod) == 0) {
208 response = set_client(self, args);
209 } else if (strcmp(method, kShowMethod) == 0) {
210 response = show(self);
211 } else if (strcmp(method, kSetEditingStateMethod) == 0) {
212 response = set_editing_state(self, args);
213 } else if (strcmp(method, kClearClientMethod) == 0) {
214 response = clear_client(self);
215 } else if (strcmp(method, kHideMethod) == 0) {
216 response = hide(self);
217 } else if (strcmp(method, kSetEditableSizeAndTransform) == 0) {
219 } else if (strcmp(method, kSetMarkedTextRect) == 0) {
220 response = set_marked_text_rect(self, args);
221 } else {
222 response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
223 }
224
225 g_autoptr(GError) error = nullptr;
226 if (!fl_method_call_respond(method_call, response, &error)) {
227 g_warning("Failed to send method call response: %s", error->message);
228 }
229}
230
231static void fl_text_input_channel_dispose(GObject* object) {
232 FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(object);
233
234 g_clear_object(&self->channel);
235
236 G_OBJECT_CLASS(fl_text_input_channel_parent_class)->dispose(object);
237}
238
239static void fl_text_input_channel_class_init(FlTextInputChannelClass* klass) {
240 G_OBJECT_CLASS(klass)->dispose = fl_text_input_channel_dispose;
241}
242
243static void fl_text_input_channel_init(FlTextInputChannel* self) {}
244
245FlTextInputChannel* fl_text_input_channel_new(FlBinaryMessenger* messenger,
247 gpointer user_data) {
248 g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
249 g_return_val_if_fail(vtable != nullptr, nullptr);
250
251 FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(
252 g_object_new(fl_text_input_channel_get_type(), nullptr));
253
254 self->vtable = vtable;
255 self->user_data = user_data;
256
257 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
258 self->channel =
259 fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
261 nullptr);
262
263 return self;
264}
265
267 FlTextInputChannel* self,
268 int64_t client_id,
269 const gchar* text,
270 int64_t selection_base,
271 int64_t selection_extent,
272 FlTextAffinity selection_affinity,
273 gboolean selection_is_directional,
274 int64_t composing_base,
275 int64_t composing_extent,
276 GCancellable* cancellable,
277 GAsyncReadyCallback callback,
278 gpointer user_data) {
279 g_return_if_fail(FL_IS_TEXT_INPUT_CHANNEL(self));
280
284
287 fl_value_new_int(selection_base));
289 fl_value_new_int(selection_extent));
292 fl_value_new_string(text_affinity_to_string(selection_affinity)));
294 fl_value_new_bool(selection_is_directional));
296 fl_value_new_int(composing_base));
298 fl_value_new_int(composing_extent));
299
301
303 args, cancellable, callback, user_data);
304}
305
307 GAsyncResult* result,
308 GError** error) {
309 g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
310 FL_METHOD_CHANNEL(object), result, error);
311 if (response == nullptr) {
312 return FALSE;
313 }
314 return fl_method_response_get_result(response, error) != nullptr;
315}
316
318 FlTextInputChannel* self,
319 int64_t client_id,
320 const gchar* old_text,
321 const gchar* delta_text,
322 int64_t delta_start,
323 int64_t delta_end,
324 int64_t selection_base,
325 int64_t selection_extent,
326 FlTextAffinity selection_affinity,
327 gboolean selection_is_directional,
328 int64_t composing_base,
329 int64_t composing_extent,
330 GCancellable* cancellable,
331 GAsyncReadyCallback callback,
332 gpointer user_data) {
333 g_return_if_fail(FL_IS_TEXT_INPUT_CHANNEL(self));
334
337
338 g_autoptr(FlValue) deltaValue = fl_value_new_map();
339 fl_value_set_string_take(deltaValue, "oldText",
340 fl_value_new_string(old_text));
341 fl_value_set_string_take(deltaValue, "deltaText",
342 fl_value_new_string(delta_text));
343 fl_value_set_string_take(deltaValue, "deltaStart",
344 fl_value_new_int(delta_start));
345 fl_value_set_string_take(deltaValue, "deltaEnd", fl_value_new_int(delta_end));
346 fl_value_set_string_take(deltaValue, "selectionBase",
347 fl_value_new_int(selection_base));
348 fl_value_set_string_take(deltaValue, "selectionExtent",
349 fl_value_new_int(selection_extent));
351 deltaValue, "selectionAffinity",
352 fl_value_new_string(text_affinity_to_string(selection_affinity)));
353 fl_value_set_string_take(deltaValue, "selectionIsDirectional",
354 fl_value_new_bool(selection_is_directional));
355 fl_value_set_string_take(deltaValue, "composingBase",
356 fl_value_new_int(composing_base));
357 fl_value_set_string_take(deltaValue, "composingExtent",
358 fl_value_new_int(composing_extent));
359
361 fl_value_append(deltas, deltaValue);
363 fl_value_set_string(value, "deltas", deltas);
364
366
369 cancellable, callback, user_data);
370}
371
373 GObject* object,
374 GAsyncResult* result,
375 GError** error) {
376 g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
377 FL_METHOD_CHANNEL(object), result, error);
378 if (response == nullptr) {
379 return FALSE;
380 }
381 return fl_method_response_get_result(response, error) != nullptr;
382}
383
385 int64_t client_id,
386 const gchar* input_action,
387 GCancellable* cancellable,
388 GAsyncReadyCallback callback,
389 gpointer user_data) {
390 g_return_if_fail(FL_IS_TEXT_INPUT_CHANNEL(self));
391
395
397 cancellable, callback, user_data);
398}
399
401 GAsyncResult* result,
402 GError** error) {
403 g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
404 FL_METHOD_CHANNEL(object), result, error);
405 if (response == nullptr) {
406 return FALSE;
407 }
408 return fl_method_response_get_result(response, error) != nullptr;
409}
int32_t value
int32_t x
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_autoptr(GMutexLocker) locker
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
G_MODULE_EXPORT FlJsonMethodCodec * fl_json_method_codec_new()
G_MODULE_EXPORT const gchar * fl_method_call_get_name(FlMethodCall *self)
G_MODULE_EXPORT gboolean fl_method_call_respond(FlMethodCall *self, FlMethodResponse *response, GError **error)
G_MODULE_EXPORT FlValue * fl_method_call_get_args(FlMethodCall *self)
G_MODULE_EXPORT FlMethodResponse * fl_method_channel_invoke_method_finish(FlMethodChannel *self, GAsyncResult *result, GError **error)
G_MODULE_EXPORT FlMethodChannel * fl_method_channel_new(FlBinaryMessenger *messenger, const gchar *name, FlMethodCodec *codec)
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 void fl_method_channel_set_method_call_handler(FlMethodChannel *self, FlMethodChannelMethodCallHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
G_BEGIN_DECLS G_MODULE_EXPORT FlMethodCall * method_call
G_MODULE_EXPORT FlValue * fl_method_response_get_result(FlMethodResponse *self, GError **error)
G_MODULE_EXPORT FlMethodErrorResponse * fl_method_error_response_new(const gchar *code, const gchar *message, FlValue *details)
G_MODULE_EXPORT FlMethodSuccessResponse * fl_method_success_response_new(FlValue *result)
G_MODULE_EXPORT FlMethodNotImplementedResponse * fl_method_not_implemented_response_new()
const gchar * channel
const uint8_t uint32_t uint32_t GError ** error
static constexpr char kClearClientMethod[]
static constexpr char kSetEditableSizeAndTransform[]
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)
static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)
static constexpr char kTextAffinityUpstream[]
static constexpr char kSetMarkedTextRect[]
static constexpr char kEnableDeltaModel[]
static constexpr char kNoneInputType[]
static void fl_text_input_channel_dispose(GObject *object)
static constexpr char kTransform[]
static constexpr char kSelectionIsDirectionalKey[]
FlTextInputChannel * fl_text_input_channel_new(FlBinaryMessenger *messenger, FlTextInputChannelVTable *vtable, gpointer user_data)
static constexpr char kHideMethod[]
static constexpr char kMultilineInputType[]
static FlMethodResponse * set_client(FlTextInputChannel *self, FlValue *args)
static FlMethodResponse * set_marked_text_rect(FlTextInputChannel *self, FlValue *args)
static constexpr char kSetEditingStateMethod[]
static constexpr char kSelectionExtentKey[]
static constexpr char kTextInputTypeNameKey[]
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)
static FlMethodResponse * set_editing_state(FlTextInputChannel *self, FlValue *args)
static FlMethodResponse * set_editable_size_and_transform(FlTextInputChannel *self, FlValue *args)
static constexpr char kChannelName[]
void fl_text_input_channel_perform_action(FlTextInputChannel *self, int64_t client_id, const gchar *input_action, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
static constexpr char kSetClientMethod[]
static constexpr char kUpdateEditingStateMethod[]
static constexpr char kTextInputTypeKey[]
static constexpr char kBadArgumentsError[]
static constexpr char kPerformActionMethod[]
static constexpr char kShowMethod[]
static FlMethodResponse * clear_client(FlTextInputChannel *self)
static FlMethodResponse * show(FlTextInputChannel *self)
static constexpr char kComposingExtentKey[]
static constexpr char kTextAffinityDownstream[]
static constexpr char kInputActionKey[]
gboolean fl_text_input_channel_perform_action_finish(GObject *object, GAsyncResult *result, GError **error)
static constexpr char kComposingBaseKey[]
static constexpr char kUpdateEditingStateWithDeltasMethod[]
static const gchar * text_affinity_to_string(FlTextAffinity affinity)
static void fl_text_input_channel_class_init(FlTextInputChannelClass *klass)
static constexpr char kSelectionAffinityKey[]
gboolean fl_text_input_channel_update_editing_state_with_deltas_finish(GObject *object, GAsyncResult *result, GError **error)
static FlMethodResponse * hide(FlTextInputChannel *self)
static constexpr char kTextKey[]
static constexpr char kSelectionBaseKey[]
static void fl_text_input_channel_init(FlTextInputChannel *self)
@ FL_TEXT_AFFINITY_DOWNSTREAM
@ FL_TEXT_AFFINITY_UPSTREAM
@ FL_TEXT_INPUT_TYPE_MULTILINE
@ FL_TEXT_INPUT_TYPE_TEXT
@ FL_TEXT_INPUT_TYPE_NONE
G_MODULE_EXPORT void fl_value_set_string(FlValue *self, const gchar *key, FlValue *value)
Definition fl_value.cc:639
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition fl_value.cc:366
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition fl_value.cc:650
G_MODULE_EXPORT FlValue * fl_value_lookup_string(FlValue *self, const gchar *key)
Definition fl_value.cc:811
G_MODULE_EXPORT int64_t fl_value_get_int(FlValue *self)
Definition fl_value.cc:668
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue *self)
Definition fl_value.cc:466
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
Definition fl_value.cc:276
G_MODULE_EXPORT void fl_value_append(FlValue *self, FlValue *value)
Definition fl_value.cc:592
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition fl_value.cc:255
G_MODULE_EXPORT const gchar * fl_value_get_string(FlValue *self)
Definition fl_value.cc:682
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition fl_value.cc:262
G_MODULE_EXPORT void fl_value_append_take(FlValue *self, FlValue *value)
Definition fl_value.cc:600
G_MODULE_EXPORT FlValue * fl_value_get_list_value(FlValue *self, size_t index)
Definition fl_value.cc:776
G_MODULE_EXPORT bool fl_value_get_bool(FlValue *self)
Definition fl_value.cc:661
G_MODULE_EXPORT FlValue * fl_value_new_list()
Definition fl_value.cc:349
G_MODULE_EXPORT double fl_value_get_float(FlValue *self)
Definition fl_value.cc:675
G_MODULE_EXPORT size_t fl_value_get_length(FlValue *self)
Definition fl_value.cc:724
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition fl_value.h:42
@ FL_VALUE_TYPE_STRING
Definition fl_value.h:68
@ FL_VALUE_TYPE_LIST
Definition fl_value.h:73
@ FL_VALUE_TYPE_MAP
Definition fl_value.h:74
FlutterDesktopBinaryReply callback
std::u16string text
double y
int32_t height
int32_t width
FlTextInputChannelVTable * vtable