|
| 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_channel.h" |
| 6 | +#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h" |
| 7 | + |
| 8 | +struct _FlTextInputChannel { |
| 9 | + FlMethodChannel parent_instance; |
| 10 | + |
| 11 | + FlTextInputChannelSetClientHandler set_client_handler; |
| 12 | + FlTextInputChannelShowHandler show_handler; |
| 13 | + FlTextInputChannelSetEditingStateHandler set_editing_state_handler; |
| 14 | + FlTextInputChannelClearClientHandler clear_client_handler; |
| 15 | + FlTextInputChannelHideHandler hide_handler; |
| 16 | + gpointer handler_data; |
| 17 | +}; |
| 18 | + |
| 19 | +G_DEFINE_TYPE(FlTextInputChannel, |
| 20 | + fl_text_input_channel, |
| 21 | + fl_method_channel_get_type()) |
| 22 | + |
| 23 | +static void method_call_cb(FlMethodChannel* channel, |
| 24 | + const gchar* method, |
| 25 | + FlValue* args, |
| 26 | + FlMethodChannelResponseHandle* response_handle, |
| 27 | + gpointer user_data) { |
| 28 | + FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(user_data); |
| 29 | + |
| 30 | + if (strcmp(method, "TextInput.setClient") == 0) { |
| 31 | + if (self->set_client_handler != nullptr) |
| 32 | + self->set_client_handler(self, 0, "", self->handler_data); |
| 33 | + fl_method_channel_respond(channel, response_handle, nullptr, nullptr); |
| 34 | + } else if (strcmp(method, "TextInput.show") == 0) { |
| 35 | + if (self->show_handler != nullptr) |
| 36 | + self->show_handler(self, self->handler_data); |
| 37 | + fl_method_channel_respond(channel, response_handle, nullptr, nullptr); |
| 38 | + } else if (strcmp(method, "TextInput.setEditableSizeAndTransform") == 0) { |
| 39 | + fl_method_channel_respond_not_implemented(channel, response_handle, |
| 40 | + nullptr); |
| 41 | + } else if (strcmp(method, "TextInput.requestAutofill") == 0) { |
| 42 | + fl_method_channel_respond_not_implemented(channel, response_handle, |
| 43 | + nullptr); |
| 44 | + } else if (strcmp(method, "TextInput.setStyle") == 0) { |
| 45 | + fl_method_channel_respond_not_implemented(channel, response_handle, |
| 46 | + nullptr); |
| 47 | + } else if (strcmp(method, "TextInput.setEditingState") == 0) { |
| 48 | + const gchar* text = |
| 49 | + fl_value_get_string(fl_value_lookup_string(args, "text")); |
| 50 | + int64_t selection_base = |
| 51 | + fl_value_get_int(fl_value_lookup_string(args, "selectionBase")); |
| 52 | + int64_t selection_extent = |
| 53 | + fl_value_get_int(fl_value_lookup_string(args, "selectionExtent")); |
| 54 | + const gchar* selection_affinity_name = |
| 55 | + fl_value_get_string(fl_value_lookup_string(args, "selectionAffinity")); |
| 56 | + FlTextAffinity selection_affinity; |
| 57 | + if (g_strcmp0(selection_affinity_name, "TextAffinity.downstream") == 0) |
| 58 | + selection_affinity = FL_TEXT_AFFINITY_DOWNSTREAM; |
| 59 | + else if (g_strcmp0(selection_affinity_name, "TextAffinity.upstream") == 0) |
| 60 | + selection_affinity = FL_TEXT_AFFINITY_UPSTREAM; |
| 61 | + gboolean selection_is_directional = fl_value_get_bool( |
| 62 | + fl_value_lookup_string(args, "selectionIsDirectional")); |
| 63 | + int64_t composing_base = |
| 64 | + fl_value_get_int(fl_value_lookup_string(args, "composingBase")); |
| 65 | + int64_t composing_extent = |
| 66 | + fl_value_get_int(fl_value_lookup_string(args, "composingExtent")); |
| 67 | + |
| 68 | + if (self->set_editing_state_handler != nullptr) |
| 69 | + self->set_editing_state_handler(self, text, selection_base, |
| 70 | + selection_extent, selection_affinity, |
| 71 | + selection_is_directional, composing_base, |
| 72 | + composing_extent, self->handler_data); |
| 73 | + |
| 74 | + fl_method_channel_respond(channel, response_handle, nullptr, nullptr); |
| 75 | + } else if (strcmp(method, "TextInput.clearClient") == 0) { |
| 76 | + if (self->clear_client_handler != nullptr) |
| 77 | + self->clear_client_handler(self, self->handler_data); |
| 78 | + fl_method_channel_respond(channel, response_handle, nullptr, nullptr); |
| 79 | + } else if (strcmp(method, "TextInput.hide") == 0) { |
| 80 | + if (self->hide_handler != nullptr) |
| 81 | + self->hide_handler(self, self->handler_data); |
| 82 | + fl_method_channel_respond(channel, response_handle, nullptr, nullptr); |
| 83 | + } else |
| 84 | + fl_method_channel_respond_not_implemented(channel, response_handle, |
| 85 | + nullptr); |
| 86 | +} |
| 87 | + |
| 88 | +// Helper function to just check if a method returned an error |
| 89 | +static gboolean finish_method(GObject* object, |
| 90 | + GAsyncResult* result, |
| 91 | + GError** error) { |
| 92 | + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( |
| 93 | + FL_METHOD_CHANNEL(object), result, error); |
| 94 | + if (response == nullptr) |
| 95 | + return FALSE; |
| 96 | + return fl_method_response_get_result(response, error) != nullptr; |
| 97 | +} |
| 98 | + |
| 99 | +static void update_editing_state_response_cb(GObject* object, |
| 100 | + GAsyncResult* result, |
| 101 | + gpointer user_data) { |
| 102 | + g_autoptr(GError) error = nullptr; |
| 103 | + if (!finish_method(object, result, &error)) |
| 104 | + g_warning("Failed to call TextInputClient.updateEditingState: %s", |
| 105 | + error->message); |
| 106 | +} |
| 107 | + |
| 108 | +static void perform_action_response_cb(GObject* object, |
| 109 | + GAsyncResult* result, |
| 110 | + gpointer user_data) { |
| 111 | + g_autoptr(GError) error = nullptr; |
| 112 | + if (!finish_method(object, result, &error)) |
| 113 | + g_warning("Failed to call TextInputClient.performAction: %s", |
| 114 | + error->message); |
| 115 | +} |
| 116 | + |
| 117 | +static void fl_text_input_channel_class_init(FlTextInputChannelClass* klass) {} |
| 118 | + |
| 119 | +static void fl_text_input_channel_init(FlTextInputChannel* self) {} |
| 120 | + |
| 121 | +FlTextInputChannel* fl_text_input_channel_new( |
| 122 | + FlBinaryMessenger* messenger, |
| 123 | + FlTextInputChannelSetClientHandler set_client_handler, |
| 124 | + FlTextInputChannelShowHandler show_handler, |
| 125 | + FlTextInputChannelSetEditingStateHandler set_editing_state_handler, |
| 126 | + FlTextInputChannelClearClientHandler clear_client_handler, |
| 127 | + FlTextInputChannelHideHandler hide_handler, |
| 128 | + gpointer user_data) { |
| 129 | + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); |
| 130 | + |
| 131 | + g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); |
| 132 | + FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL( |
| 133 | + g_object_new(fl_text_input_channel_get_type(), "messenger", messenger, |
| 134 | + "name", "flutter/textinput", "codec", codec, nullptr)); |
| 135 | + fl_method_channel_set_method_call_handler(FL_METHOD_CHANNEL(self), |
| 136 | + method_call_cb, self); |
| 137 | + |
| 138 | + self->set_client_handler = set_client_handler; |
| 139 | + self->show_handler = show_handler; |
| 140 | + self->set_editing_state_handler = set_editing_state_handler; |
| 141 | + self->clear_client_handler = clear_client_handler; |
| 142 | + self->hide_handler = hide_handler; |
| 143 | + self->handler_data = user_data; |
| 144 | + |
| 145 | + return self; |
| 146 | +} |
| 147 | + |
| 148 | +void text_input_channel_request_existing_input_state( |
| 149 | + FlTextInputChannel* self, |
| 150 | + GCancellable* cancellable, |
| 151 | + GAsyncReadyCallback callback, |
| 152 | + gpointer user_data) { |
| 153 | + fl_method_channel_invoke_method(FL_METHOD_CHANNEL(self), |
| 154 | + "TextInputClient.requestExistingInputState", |
| 155 | + nullptr, cancellable, callback, user_data); |
| 156 | +} |
| 157 | + |
| 158 | +FlValue* text_input_channel_request_existing_input_state_finish( |
| 159 | + FlTextInputChannel* self, |
| 160 | + GAsyncResult* result, |
| 161 | + GError** error) { |
| 162 | + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( |
| 163 | + FL_METHOD_CHANNEL(self), result, error); |
| 164 | + if (response == nullptr) |
| 165 | + return nullptr; |
| 166 | + return fl_method_response_get_result(response, error); |
| 167 | +} |
| 168 | + |
| 169 | +void text_input_channel_update_editing_state(FlTextInputChannel* self, |
| 170 | + int64_t client_id, |
| 171 | + const gchar* text, |
| 172 | + int64_t selection_base, |
| 173 | + int64_t selection_extent, |
| 174 | + FlTextAffinity selection_affinity, |
| 175 | + gboolean selection_is_directional, |
| 176 | + int64_t composing_base, |
| 177 | + int64_t composing_extent) { |
| 178 | + g_printerr("TextInput.updateEditingState(%" G_GINT64_FORMAT |
| 179 | + ", \"%s\", %" G_GINT64_FORMAT ", %" G_GINT64_FORMAT |
| 180 | + ", %d, %s, %" G_GINT64_FORMAT ", %" G_GINT64_FORMAT ")\n", |
| 181 | + client_id, text, selection_base, selection_extent, |
| 182 | + selection_affinity, selection_is_directional ? "true" : "false", |
| 183 | + composing_base, composing_extent); |
| 184 | + |
| 185 | + g_autoptr(FlValue) args = fl_value_new_list(); |
| 186 | + fl_value_append_take(args, fl_value_new_int(client_id)); |
| 187 | + g_autoptr(FlValue) value = fl_value_new_map(); |
| 188 | + fl_value_set_string_take(value, "text", fl_value_new_string(text)); |
| 189 | + fl_value_set_string_take(value, "selectionBase", |
| 190 | + fl_value_new_int(selection_base)); |
| 191 | + fl_value_set_string_take(value, "selectionExtent", |
| 192 | + fl_value_new_int(selection_extent)); |
| 193 | + const gchar* selection_affinity_name = ""; |
| 194 | + switch (selection_affinity) { |
| 195 | + case FL_TEXT_AFFINITY_DOWNSTREAM: |
| 196 | + selection_affinity_name = "TextAffinity.downstream"; |
| 197 | + break; |
| 198 | + case FL_TEXT_AFFINITY_UPSTREAM: |
| 199 | + selection_affinity_name = "TextAffinity.upstream"; |
| 200 | + break; |
| 201 | + } |
| 202 | + fl_value_set_string_take(value, "selectionAffinity", |
| 203 | + fl_value_new_string(selection_affinity_name)); |
| 204 | + fl_value_set_string_take(value, "selectionIsDirectional", |
| 205 | + fl_value_new_bool(selection_is_directional)); |
| 206 | + fl_value_set_string_take(value, "composingBase", |
| 207 | + fl_value_new_int(composing_base)); |
| 208 | + fl_value_set_string_take(value, "composingExtent", |
| 209 | + fl_value_new_int(composing_extent)); |
| 210 | + fl_value_append(args, value); |
| 211 | + |
| 212 | + fl_method_channel_invoke_method( |
| 213 | + FL_METHOD_CHANNEL(self), "TextInputClient.updateEditingState", args, |
| 214 | + nullptr, update_editing_state_response_cb, self); |
| 215 | +} |
| 216 | + |
| 217 | +void text_input_channel_perform_action(FlTextInputChannel* self, |
| 218 | + int64_t client_id, |
| 219 | + FlTextInputAction action) { |
| 220 | + g_autoptr(FlValue) args = fl_value_new_list(); |
| 221 | + fl_value_append_take(args, fl_value_new_int(client_id)); |
| 222 | + const gchar* action_name = ""; |
| 223 | + switch (action) { |
| 224 | + case FL_TEXT_INPUT_ACTION_CONTINUE: |
| 225 | + action_name = "TextInputAction.continueAction"; |
| 226 | + break; |
| 227 | + case FL_TEXT_INPUT_ACTION_DONE: |
| 228 | + action_name = "TextInputAction.done"; |
| 229 | + break; |
| 230 | + case FL_TEXT_INPUT_ACTION_EMERGENCY_CALL: |
| 231 | + action_name = "TextInputAction.emergencyCall"; |
| 232 | + break; |
| 233 | + case FL_TEXT_INPUT_ACTION_GO: |
| 234 | + action_name = "TextInputAction.go"; |
| 235 | + break; |
| 236 | + case FL_TEXT_INPUT_ACTION_JOIN: |
| 237 | + action_name = "TextInputAction.join"; |
| 238 | + break; |
| 239 | + case FL_TEXT_INPUT_ACTION_NEWLINE: |
| 240 | + action_name = "TextInputAction.newline"; |
| 241 | + break; |
| 242 | + case FL_TEXT_INPUT_ACTION_NEXT: |
| 243 | + action_name = "TextInputAction.next"; |
| 244 | + break; |
| 245 | + case FL_TEXT_INPUT_ACTION_PREVIOUS: |
| 246 | + action_name = "TextInputAction.previous"; |
| 247 | + break; |
| 248 | + case FL_TEXT_INPUT_ACTION_ROUTE: |
| 249 | + action_name = "TextInputAction.route"; |
| 250 | + break; |
| 251 | + case FL_TEXT_INPUT_ACTION_SEARCH: |
| 252 | + action_name = "TextInputAction.search"; |
| 253 | + break; |
| 254 | + case FL_TEXT_INPUT_ACTION_SEND: |
| 255 | + action_name = "TextInputAction.send"; |
| 256 | + break; |
| 257 | + case FL_TEXT_INPUT_ACTION_UNSPECIFIED: |
| 258 | + action_name = "TextInputAction.unspecified"; |
| 259 | + break; |
| 260 | + } |
| 261 | + fl_value_append_take(args, fl_value_new_string(action_name)); |
| 262 | + |
| 263 | + fl_method_channel_invoke_method(FL_METHOD_CHANNEL(self), |
| 264 | + "TextInputClient.performAction", args, |
| 265 | + nullptr, perform_action_response_cb, self); |
| 266 | +} |
| 267 | + |
| 268 | +void text_input_channel_on_connection_closed(FlTextInputChannel* self, |
| 269 | + int64_t client_id) { |
| 270 | + g_autoptr(FlValue) args = fl_value_new_list(); |
| 271 | + fl_value_append_take(args, fl_value_new_int(client_id)); |
| 272 | + fl_method_channel_invoke_method(FL_METHOD_CHANNEL(self), |
| 273 | + "TextInputClient.onConnectionClosed", args, |
| 274 | + nullptr, nullptr, nullptr); |
| 275 | +} |
0 commit comments