Flutter Linux Embedder
fl_text_input_handler_test.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 <utility>
6 
10 #include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
11 #include "flutter/shell/platform/linux/testing/fl_test.h"
12 #include "flutter/shell/platform/linux/testing/mock_im_context.h"
13 #include "flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h"
14 #include "flutter/testing/testing.h"
15 
16 #include "gmock/gmock.h"
17 #include "gtest/gtest.h"
18 
19 static FlValue* build_map(std::map<const gchar*, FlValue*> args) {
21  for (auto it = args.begin(); it != args.end(); ++it) {
22  fl_value_set_string_take(value, it->first, it->second);
23  }
24  return value;
25 }
26 
27 static FlValue* build_list(std::vector<FlValue*> args) {
29  for (auto it = args.begin(); it != args.end(); ++it) {
31  }
32  return value;
33 }
34 
35 struct InputConfig {
36  int64_t client_id = -1;
37  const gchar* input_type = "TextInputType.text";
38  const gchar* input_action = "TextInputAction.none";
39  gboolean enable_delta_model = false;
40 };
41 
43  return build_list({
45  build_map({
46  {"inputAction", fl_value_new_string(config.input_action)},
47  {"inputType", build_map({
48  {"name", fl_value_new_string(config.input_type)},
49  })},
50  {"enableDeltaModel", fl_value_new_bool(config.enable_delta_model)},
51  }),
52  });
53 }
54 
55 struct EditingState {
56  const gchar* text = "";
57  int selection_base = -1;
58  int selection_extent = -1;
59  int composing_base = -1;
60  int composing_extent = -1;
61 };
62 
64  return build_map({
65  {"text", fl_value_new_string(state.text)},
66  {"selectionBase", fl_value_new_int(state.selection_base)},
67  {"selectionExtent", fl_value_new_int(state.selection_extent)},
68  {"selectionAffinity", fl_value_new_string("TextAffinity.downstream")},
69  {"selectionIsDirectional", fl_value_new_bool(false)},
70  {"composingBase", fl_value_new_int(state.composing_base)},
71  {"composingExtent", fl_value_new_int(state.composing_extent)},
72  });
73 }
74 
75 struct EditingDelta {
76  const gchar* old_text = "";
77  const gchar* delta_text = "";
78  int delta_start = -1;
79  int delta_end = -1;
80  int selection_base = -1;
81  int selection_extent = -1;
82  int composing_base = -1;
83  int composing_extent = -1;
84 };
85 
87  return build_map({
88  {"oldText", fl_value_new_string(delta.old_text)},
89  {"deltaText", fl_value_new_string(delta.delta_text)},
90  {"deltaStart", fl_value_new_int(delta.delta_start)},
91  {"deltaEnd", fl_value_new_int(delta.delta_end)},
92  {"selectionBase", fl_value_new_int(delta.selection_base)},
93  {"selectionExtent", fl_value_new_int(delta.selection_extent)},
94  {"selectionAffinity", fl_value_new_string("TextAffinity.downstream")},
95  {"selectionIsDirectional", fl_value_new_bool(false)},
96  {"composingBase", fl_value_new_int(delta.composing_base)},
97  {"composingExtent", fl_value_new_int(delta.composing_extent)},
98  });
99 }
100 
101 static void set_client(FlMockBinaryMessenger* messenger, InputConfig config) {
102  gboolean called = FALSE;
103  g_autoptr(FlValue) args = build_input_config(config);
104  fl_mock_binary_messenger_invoke_json_method(
105  messenger, "flutter/textinput", "TextInput.setClient", args,
106  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
107  gpointer user_data) {
108  gboolean* called = static_cast<gboolean*>(user_data);
109  *called = TRUE;
110 
111  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
112 
113  g_autoptr(FlValue) expected_result = fl_value_new_null();
115  FL_METHOD_SUCCESS_RESPONSE(response)),
116  expected_result));
117  },
118  &called);
119  EXPECT_TRUE(called);
120 }
121 
122 static void set_editing_state(FlMockBinaryMessenger* messenger,
124  gboolean called = FALSE;
125  g_autoptr(FlValue) args = build_editing_state(state);
126  fl_mock_binary_messenger_invoke_json_method(
127  messenger, "flutter/textinput", "TextInput.setEditingState", args,
128  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
129  gpointer user_data) {
130  gboolean* called = static_cast<gboolean*>(user_data);
131  *called = TRUE;
132 
133  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
134 
135  g_autoptr(FlValue) expected_result = fl_value_new_null();
137  FL_METHOD_SUCCESS_RESPONSE(response)),
138  expected_result));
139  },
140  &called);
141  EXPECT_TRUE(called);
142 }
143 
144 static void send_key_event(FlTextInputHandler* handler,
145  gint keyval,
146  gint state = 0) {
147  GdkEvent* gdk_event = gdk_event_new(GDK_KEY_PRESS);
148  gdk_event->key.keyval = keyval;
149  gdk_event->key.state = state;
150  g_autoptr(FlKeyEvent) key_event = fl_key_event_new_from_gdk_event(gdk_event);
151  fl_text_input_handler_filter_keypress(handler, key_event);
152 }
153 
154 TEST(FlTextInputHandlerTest, MessageHandler) {
155  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
156  ::testing::NiceMock<flutter::testing::MockIMContext> context;
157  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
158 
159  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
160  FL_BINARY_MESSENGER(messenger), context, delegate);
161  EXPECT_NE(handler, nullptr);
162 
163  EXPECT_TRUE(
164  fl_mock_binary_messenger_has_handler(messenger, "flutter/textinput"));
165 
166  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
167 }
168 
169 TEST(FlTextInputHandlerTest, SetClient) {
170  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
171  ::testing::NiceMock<flutter::testing::MockIMContext> context;
172  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
173 
174  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
175  FL_BINARY_MESSENGER(messenger), context, delegate);
176  EXPECT_NE(handler, nullptr);
177 
178  set_client(messenger, {.client_id = 1});
179 
180  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
181 }
182 
183 TEST(FlTextInputHandlerTest, Show) {
184  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
185  ::testing::NiceMock<flutter::testing::MockIMContext> context;
186  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
187 
188  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
189  FL_BINARY_MESSENGER(messenger), context, delegate);
190  EXPECT_NE(handler, nullptr);
191 
192  EXPECT_CALL(context,
193  gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)));
194 
195  gboolean called = FALSE;
196  fl_mock_binary_messenger_invoke_json_method(
197  messenger, "flutter/textinput", "TextInput.show", nullptr,
198  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
199  gpointer user_data) {
200  gboolean* called = static_cast<gboolean*>(user_data);
201  *called = TRUE;
202 
203  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
204 
205  g_autoptr(FlValue) expected_result = fl_value_new_null();
207  FL_METHOD_SUCCESS_RESPONSE(response)),
208  expected_result));
209  },
210  &called);
211  EXPECT_TRUE(called);
212 
213  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
214 }
215 
216 TEST(FlTextInputHandlerTest, Hide) {
217  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
218  ::testing::NiceMock<flutter::testing::MockIMContext> context;
219  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
220 
221  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
222  FL_BINARY_MESSENGER(messenger), context, delegate);
223  EXPECT_NE(handler, nullptr);
224 
225  EXPECT_CALL(context,
226  gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
227 
228  gboolean called = FALSE;
229  fl_mock_binary_messenger_invoke_json_method(
230  messenger, "flutter/textinput", "TextInput.hide", nullptr,
231  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
232  gpointer user_data) {
233  gboolean* called = static_cast<gboolean*>(user_data);
234  *called = TRUE;
235 
236  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
237 
238  g_autoptr(FlValue) expected_result = fl_value_new_null();
240  FL_METHOD_SUCCESS_RESPONSE(response)),
241  expected_result));
242  },
243  &called);
244  EXPECT_TRUE(called);
245 
246  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
247 }
248 
249 TEST(FlTextInputHandlerTest, ClearClient) {
250  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
251  ::testing::NiceMock<flutter::testing::MockIMContext> context;
252  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
253 
254  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
255  FL_BINARY_MESSENGER(messenger), context, delegate);
256  EXPECT_NE(handler, nullptr);
257 
258  gboolean called = FALSE;
259  fl_mock_binary_messenger_invoke_json_method(
260  messenger, "flutter/textinput", "TextInput.clearClient", nullptr,
261  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
262  gpointer user_data) {
263  gboolean* called = static_cast<gboolean*>(user_data);
264  *called = TRUE;
265 
266  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
267 
268  g_autoptr(FlValue) expected_result = fl_value_new_null();
270  FL_METHOD_SUCCESS_RESPONSE(response)),
271  expected_result));
272  },
273  &called);
274  EXPECT_TRUE(called);
275 
276  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
277 }
278 
279 TEST(FlTextInputHandlerTest, PerformAction) {
280  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
281  ::testing::NiceMock<flutter::testing::MockIMContext> context;
282  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
283 
284  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
285  FL_BINARY_MESSENGER(messenger), context, delegate);
286  EXPECT_NE(handler, nullptr);
287 
288  set_client(messenger, {
289  .client_id = 1,
290  .input_type = "TextInputType.multiline",
291  .input_action = "TextInputAction.newline",
292  });
293  set_editing_state(messenger, {
294  .text = "Flutter",
295  .selection_base = 7,
296  .selection_extent = 7,
297  });
298 
299  // Client will update editing state and perform action
300  int call_count = 0;
301  fl_mock_binary_messenger_set_json_method_channel(
302  messenger, "flutter/textinput",
303  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
304  FlValue* args, gpointer user_data) {
305  int* call_count = static_cast<int*>(user_data);
306 
307  if (strcmp(name, "TextInputClient.updateEditingState") == 0) {
308  g_autoptr(FlValue) expected_args = build_list({
309  fl_value_new_int(1), // client_id
311  .text = "Flutter\n",
312  .selection_base = 8,
313  .selection_extent = 8,
314  }),
315  });
316  EXPECT_TRUE(fl_value_equal(args, expected_args));
317  EXPECT_EQ(*call_count, 0);
318  (*call_count)++;
319  } else if (strcmp(name, "TextInputClient.performAction") == 0) {
320  g_autoptr(FlValue) expected_args = build_list({
321  fl_value_new_int(1), // client_id
322  fl_value_new_string("TextInputAction.newline"),
323  });
324  EXPECT_TRUE(fl_value_equal(args, expected_args));
325  EXPECT_EQ(*call_count, 1);
326  (*call_count)++;
327  }
328 
329  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
330  },
331  &call_count);
332 
333  send_key_event(handler, GDK_KEY_Return);
334  EXPECT_EQ(call_count, 2);
335 
336  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
337 }
338 
339 // Regression test for https://github.com/flutter/flutter/issues/125879.
340 TEST(FlTextInputHandlerTest, MultilineWithSendAction) {
341  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
342  ::testing::NiceMock<flutter::testing::MockIMContext> context;
343  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
344 
345  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
346  FL_BINARY_MESSENGER(messenger), context, delegate);
347  EXPECT_NE(handler, nullptr);
348 
349  set_client(messenger, {
350  .client_id = 1,
351  .input_type = "TextInputType.multiline",
352  .input_action = "TextInputAction.send",
353  });
354  set_editing_state(messenger, {
355  .text = "Flutter",
356  .selection_base = 7,
357  .selection_extent = 7,
358  });
359 
360  // Because the input action is not set to TextInputAction.newline, the next
361  // expected call is "TextInputClient.performAction". If the input action was
362  // set to TextInputAction.newline the next call would be
363  // "TextInputClient.updateEditingState" (this case is tested in the test named
364  // 'PerformAction').
365  int call_count = 0;
366  fl_mock_binary_messenger_set_json_method_channel(
367  messenger, "flutter/textinput",
368  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
369  FlValue* args, gpointer user_data) {
370  int* call_count = static_cast<int*>(user_data);
371 
372  EXPECT_STREQ(name, "TextInputClient.performAction");
373  g_autoptr(FlValue) expected_args = nullptr;
374  switch (*call_count) {
375  case 0:
376  // Perform action.
377  expected_args = build_list({
378  fl_value_new_int(1), // client_id
379  fl_value_new_string("TextInputAction.send"),
380  });
381  break;
382  default:
383  g_assert_not_reached();
384  break;
385  }
386  EXPECT_TRUE(fl_value_equal(args, expected_args));
387  (*call_count)++;
388 
389  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
390  },
391  &call_count);
392 
393  send_key_event(handler, GDK_KEY_Return);
394  EXPECT_EQ(call_count, 1);
395 
396  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
397 }
398 
399 TEST(FlTextInputHandlerTest, MoveCursor) {
400  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
401  ::testing::NiceMock<flutter::testing::MockIMContext> context;
402  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
403 
404  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
405  FL_BINARY_MESSENGER(messenger), context, delegate);
406  EXPECT_NE(handler, nullptr);
407 
408  set_client(messenger, {.client_id = 1});
409  set_editing_state(messenger, {
410  .text = "Flutter",
411  .selection_base = 4,
412  .selection_extent = 4,
413  });
414 
415  int call_count = 0;
416  fl_mock_binary_messenger_set_json_method_channel(
417  messenger, "flutter/textinput",
418  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
419  FlValue* args, gpointer user_data) {
420  int* call_count = static_cast<int*>(user_data);
421 
422  EXPECT_STREQ(name, "TextInputClient.updateEditingState");
423  g_autoptr(FlValue) expected_args = nullptr;
424  switch (*call_count) {
425  case 0:
426  // move cursor to beginning
427  expected_args = build_list({
428  fl_value_new_int(1), // client_id
430  .text = "Flutter",
431  .selection_base = 0,
432  .selection_extent = 0,
433  }),
434  });
435  break;
436  case 1:
437  // move cursor to end
438  expected_args = build_list({
439  fl_value_new_int(1), // client_id
441  .text = "Flutter",
442  .selection_base = 7,
443  .selection_extent = 7,
444  }),
445  });
446  break;
447  default:
448  g_assert_not_reached();
449  break;
450  }
451  EXPECT_TRUE(fl_value_equal(args, expected_args));
452  (*call_count)++;
453 
454  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
455  },
456  &call_count);
457 
458  send_key_event(handler, GDK_KEY_Home);
459  send_key_event(handler, GDK_KEY_End);
460  EXPECT_EQ(call_count, 2);
461 
462  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
463 }
464 
465 TEST(FlTextInputHandlerTest, Select) {
466  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
467  ::testing::NiceMock<flutter::testing::MockIMContext> context;
468  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
469 
470  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
471  FL_BINARY_MESSENGER(messenger), context, delegate);
472  EXPECT_NE(handler, nullptr);
473 
474  set_client(messenger, {.client_id = 1});
475  set_editing_state(messenger, {
476  .text = "Flutter",
477  .selection_base = 4,
478  .selection_extent = 4,
479  });
480 
481  int call_count = 0;
482  fl_mock_binary_messenger_set_json_method_channel(
483  messenger, "flutter/textinput",
484  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
485  FlValue* args, gpointer user_data) {
486  int* call_count = static_cast<int*>(user_data);
487 
488  EXPECT_STREQ(name, "TextInputClient.updateEditingState");
489  g_autoptr(FlValue) expected_args = nullptr;
490  switch (*call_count) {
491  case 0:
492  // select to end
493  expected_args = build_list({
494  fl_value_new_int(1), // client_id
496  .text = "Flutter",
497  .selection_base = 4,
498  .selection_extent = 7,
499  }),
500  });
501  break;
502  case 1:
503  // select to beginning
504  expected_args = build_list({
505  fl_value_new_int(1), // client_id
507  .text = "Flutter",
508  .selection_base = 4,
509  .selection_extent = 0,
510  }),
511  });
512  break;
513  default:
514  g_assert_not_reached();
515  break;
516  }
517  EXPECT_TRUE(fl_value_equal(args, expected_args));
518  (*call_count)++;
519 
520  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
521  },
522  &call_count);
523 
524  send_key_event(handler, GDK_KEY_End, GDK_SHIFT_MASK);
525  send_key_event(handler, GDK_KEY_Home, GDK_SHIFT_MASK);
526  EXPECT_EQ(call_count, 2);
527 
528  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
529 }
530 
531 TEST(FlTextInputHandlerTest, Composing) {
532  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
533  ::testing::NiceMock<flutter::testing::MockIMContext> context;
534  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
535 
536  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
537  FL_BINARY_MESSENGER(messenger), context, delegate);
538  EXPECT_NE(handler, nullptr);
539 
540  // update
541  EXPECT_CALL(context,
542  gtk_im_context_get_preedit_string(
543  ::testing::Eq<GtkIMContext*>(context),
544  ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
545  .WillOnce(
546  ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter")),
547  ::testing::SetArgPointee<3>(0)));
548 
549  int call_count = 0;
550  fl_mock_binary_messenger_set_json_method_channel(
551  messenger, "flutter/textinput",
552  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
553  FlValue* args, gpointer user_data) {
554  int* call_count = static_cast<int*>(user_data);
555 
556  EXPECT_STREQ(name, "TextInputClient.updateEditingState");
557  g_autoptr(FlValue) expected_args = nullptr;
558  switch (*call_count) {
559  case 0:
560  expected_args = build_list({
561  fl_value_new_int(-1), // client_id
563  .text = "Flutter",
564  .selection_base = 0,
565  .selection_extent = 0,
566  .composing_base = 0,
567  .composing_extent = 7,
568  }),
569  });
570  break;
571  case 1:
572  // commit
573  expected_args = build_list({
574  fl_value_new_int(-1), // client_id
576  .text = "engine",
577  .selection_base = 6,
578  .selection_extent = 6,
579  }),
580  });
581  break;
582  case 2:
583  // end
584  expected_args = build_list({
585  fl_value_new_int(-1), // client_id
587  .text = "engine",
588  .selection_base = 6,
589  .selection_extent = 6,
590  }),
591  });
592  break;
593  default:
594  g_assert_not_reached();
595  break;
596  }
597  EXPECT_TRUE(fl_value_equal(args, expected_args));
598  (*call_count)++;
599 
600  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
601  },
602  &call_count);
603 
604  g_signal_emit_by_name(context, "preedit-start", nullptr);
605  g_signal_emit_by_name(context, "preedit-changed", nullptr);
606  g_signal_emit_by_name(context, "commit", "engine", nullptr);
607  g_signal_emit_by_name(context, "preedit-end", nullptr);
608  EXPECT_EQ(call_count, 3);
609 
610  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
611 }
612 
613 TEST(FlTextInputHandlerTest, SurroundingText) {
614  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
615  ::testing::NiceMock<flutter::testing::MockIMContext> context;
616  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
617 
618  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
619  FL_BINARY_MESSENGER(messenger), context, delegate);
620  EXPECT_NE(handler, nullptr);
621 
622  set_client(messenger, {.client_id = 1});
623  set_editing_state(messenger, {
624  .text = "Flutter",
625  .selection_base = 3,
626  .selection_extent = 3,
627  });
628 
629  // retrieve
630  EXPECT_CALL(context, gtk_im_context_set_surrounding(
631  ::testing::Eq<GtkIMContext*>(context),
632  ::testing::StrEq("Flutter"), 7, 3));
633 
634  gboolean retrieved = false;
635  g_signal_emit_by_name(context, "retrieve-surrounding", &retrieved, nullptr);
636  EXPECT_TRUE(retrieved);
637 
638  int call_count = 0;
639  fl_mock_binary_messenger_set_json_method_channel(
640  messenger, "flutter/textinput",
641  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
642  FlValue* args, gpointer user_data) {
643  int* call_count = static_cast<int*>(user_data);
644 
645  EXPECT_STREQ(name, "TextInputClient.updateEditingState");
646  g_autoptr(FlValue) expected_args = nullptr;
647  switch (*call_count) {
648  case 0:
649  // delete
650  expected_args = build_list({
651  fl_value_new_int(1), // client_id
653  .text = "Flutr",
654  .selection_base = 3,
655  .selection_extent = 3,
656  }),
657  });
658  break;
659  default:
660  g_assert_not_reached();
661  break;
662  }
663  EXPECT_TRUE(fl_value_equal(args, expected_args));
664  (*call_count)++;
665 
666  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
667  },
668  &call_count);
669 
670  gboolean deleted = false;
671  g_signal_emit_by_name(context, "delete-surrounding", 1, 2, &deleted, nullptr);
672  EXPECT_TRUE(deleted);
673  EXPECT_EQ(call_count, 1);
674 
675  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
676 }
677 
678 TEST(FlTextInputHandlerTest, SetMarkedTextRect) {
679  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
680  ::testing::NiceMock<flutter::testing::MockIMContext> context;
681  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
682 
683  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
684  FL_BINARY_MESSENGER(messenger), context, delegate);
685  EXPECT_NE(handler, nullptr);
686 
687  g_signal_emit_by_name(context, "preedit-start", nullptr);
688 
689  // set editable size and transform
690  g_autoptr(FlValue) size_and_transform = build_map({
691  {
692  "transform",
693  build_list({
703  fl_value_new_float(10),
704  fl_value_new_float(11),
705  fl_value_new_float(12),
706  fl_value_new_float(13),
707  fl_value_new_float(14),
708  fl_value_new_float(15),
709  fl_value_new_float(16),
710  }),
711  },
712  });
713  gboolean called = FALSE;
714  fl_mock_binary_messenger_invoke_json_method(
715  messenger, "flutter/textinput", "TextInput.setEditableSizeAndTransform",
716  size_and_transform,
717  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
718  gpointer user_data) {
719  gboolean* called = static_cast<gboolean*>(user_data);
720  *called = TRUE;
721 
722  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
723 
724  g_autoptr(FlValue) expected_result = fl_value_new_null();
726  FL_METHOD_SUCCESS_RESPONSE(response)),
727  expected_result));
728  },
729  &called);
730  EXPECT_TRUE(called);
731 
733  ::testing::Eq<FlTextInputViewDelegate*>(delegate),
734  ::testing::Eq(27), ::testing::Eq(32), ::testing::_,
735  ::testing::_))
736  .WillOnce(::testing::DoAll(::testing::SetArgPointee<3>(123),
737  ::testing::SetArgPointee<4>(456)));
738 
739  EXPECT_CALL(context, gtk_im_context_set_cursor_location(
740  ::testing::Eq<GtkIMContext*>(context),
741  ::testing::Pointee(::testing::AllOf(
742  ::testing::Field(&GdkRectangle::x, 123),
743  ::testing::Field(&GdkRectangle::y, 456),
744  ::testing::Field(&GdkRectangle::width, 0),
745  ::testing::Field(&GdkRectangle::height, 0)))));
746 
747  // set marked text rect
748  g_autoptr(FlValue) rect = build_map({
749  {"x", fl_value_new_float(1)},
750  {"y", fl_value_new_float(2)},
751  {"width", fl_value_new_float(3)},
752  {"height", fl_value_new_float(4)},
753  });
754  called = FALSE;
755  fl_mock_binary_messenger_invoke_json_method(
756  messenger, "flutter/textinput", "TextInput.setMarkedTextRect", rect,
757  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
758  gpointer user_data) {
759  gboolean* called = static_cast<gboolean*>(user_data);
760  *called = TRUE;
761 
762  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
763 
764  g_autoptr(FlValue) expected_result = fl_value_new_null();
766  FL_METHOD_SUCCESS_RESPONSE(response)),
767  expected_result));
768  },
769  &called);
770  EXPECT_TRUE(called);
771 
772  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
773 }
774 
775 TEST(FlTextInputHandlerTest, TextInputTypeNone) {
776  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
777  ::testing::NiceMock<flutter::testing::MockIMContext> context;
778  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
779 
780  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
781  FL_BINARY_MESSENGER(messenger), context, delegate);
782  EXPECT_NE(handler, nullptr);
783 
784  set_client(messenger, {
785  .client_id = 1,
786  .input_type = "TextInputType.none",
787  });
788 
789  EXPECT_CALL(context,
790  gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)))
791  .Times(0);
792  EXPECT_CALL(context,
793  gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
794 
795  gboolean called = FALSE;
796  fl_mock_binary_messenger_invoke_json_method(
797  messenger, "flutter/textinput", "TextInput.show", nullptr,
798  [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
799  gpointer user_data) {
800  gboolean* called = static_cast<gboolean*>(user_data);
801  *called = TRUE;
802 
803  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
804 
805  g_autoptr(FlValue) expected_result = fl_value_new_null();
807  FL_METHOD_SUCCESS_RESPONSE(response)),
808  expected_result));
809  },
810  &called);
811  EXPECT_TRUE(called);
812 
813  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
814 }
815 
816 TEST(FlTextInputHandlerTest, TextEditingDelta) {
817  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
818  ::testing::NiceMock<flutter::testing::MockIMContext> context;
819  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
820 
821  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
822  FL_BINARY_MESSENGER(messenger), context, delegate);
823  EXPECT_NE(handler, nullptr);
824 
825  set_client(messenger, {
826  .client_id = 1,
827  .enable_delta_model = true,
828  });
829  set_editing_state(messenger, {
830  .text = "Flutter",
831  .selection_base = 7,
832  .selection_extent = 7,
833  });
834 
835  // update editing state with deltas
836  int call_count = 0;
837  fl_mock_binary_messenger_set_json_method_channel(
838  messenger, "flutter/textinput",
839  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
840  FlValue* args, gpointer user_data) {
841  int* call_count = static_cast<int*>(user_data);
842 
843  EXPECT_STREQ(name, "TextInputClient.updateEditingStateWithDeltas");
844  g_autoptr(FlValue) expected_args = nullptr;
845  switch (*call_count) {
846  case 0:
847  expected_args = build_list({
848  fl_value_new_int(1), // client_id
849  build_map({{
850  "deltas",
851  build_list({
853  .old_text = "Flutter",
854  .delta_text = "Flutter",
855  .delta_start = 7,
856  .delta_end = 7,
857  .selection_base = 0,
858  .selection_extent = 0,
859  }),
860  }),
861  }}),
862  });
863  break;
864  default:
865  g_assert_not_reached();
866  break;
867  }
868  EXPECT_TRUE(fl_value_equal(args, expected_args));
869  (*call_count)++;
870 
871  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
872  },
873  &call_count);
874 
875  send_key_event(handler, GDK_KEY_Home);
876  EXPECT_EQ(call_count, 1);
877 
878  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
879 }
880 
881 TEST(FlTextInputHandlerTest, ComposingDelta) {
882  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
883  ::testing::NiceMock<flutter::testing::MockIMContext> context;
884  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
885 
886  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
887  FL_BINARY_MESSENGER(messenger), context, delegate);
888  EXPECT_NE(handler, nullptr);
889 
890  // set config
891  set_client(messenger, {
892  .client_id = 1,
893  .enable_delta_model = true,
894  });
895 
896  g_signal_emit_by_name(context, "preedit-start", nullptr);
897 
898  // update
899  EXPECT_CALL(context,
900  gtk_im_context_get_preedit_string(
901  ::testing::Eq<GtkIMContext*>(context),
902  ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
903  .WillOnce(
904  ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter ")),
905  ::testing::SetArgPointee<3>(8)));
906 
907  int call_count = 0;
908  fl_mock_binary_messenger_set_json_method_channel(
909  messenger, "flutter/textinput",
910  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
911  FlValue* args, gpointer user_data) {
912  int* call_count = static_cast<int*>(user_data);
913 
914  EXPECT_STREQ(name, "TextInputClient.updateEditingStateWithDeltas");
915  g_autoptr(FlValue) expected_args = nullptr;
916  switch (*call_count) {
917  case 0:
918  expected_args = build_list({
919  fl_value_new_int(1), // client_id
920  build_map({{
921  "deltas",
922  build_list({
924  .old_text = "",
925  .delta_text = "Flutter ",
926  .delta_start = 0,
927  .delta_end = 0,
928  .selection_base = 8,
929  .selection_extent = 8,
930  .composing_base = 0,
931  .composing_extent = 8,
932  }),
933  }),
934  }}),
935  });
936  break;
937  case 1:
938  // commit
939  expected_args = build_list({
940  fl_value_new_int(1), // client_id
941  build_map({{
942  "deltas",
943  build_list({
945  .old_text = "Flutter ",
946  .delta_text = "Flutter engine",
947  .delta_start = 0,
948  .delta_end = 8,
949  .selection_base = 14,
950  .selection_extent = 14,
951  .composing_base = -1,
952  .composing_extent = -1,
953  }),
954  }),
955  }}),
956  });
957  break;
958  case 2:
959  // end
960  expected_args = build_list({
961  fl_value_new_int(1), // client_id
962  build_map({{
963  "deltas",
964  build_list({
966  .old_text = "Flutter engine",
967  .selection_base = 14,
968  .selection_extent = 14,
969  }),
970  }),
971  }}),
972  });
973  break;
974  default:
975  g_assert_not_reached();
976  break;
977  }
978  EXPECT_TRUE(fl_value_equal(args, expected_args));
979  (*call_count)++;
980 
981  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
982  },
983  &call_count);
984 
985  g_signal_emit_by_name(context, "preedit-changed", nullptr);
986  g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr);
987  g_signal_emit_by_name(context, "preedit-end", nullptr);
988  EXPECT_EQ(call_count, 3);
989 
990  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
991 }
992 
993 TEST(FlTextInputHandlerTest, NonComposingDelta) {
994  g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
995  ::testing::NiceMock<flutter::testing::MockIMContext> context;
996  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
997 
998  g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new(
999  FL_BINARY_MESSENGER(messenger), context, delegate);
1000  EXPECT_NE(handler, nullptr);
1001 
1002  // set config
1003  set_client(messenger, {
1004  .client_id = 1,
1005  .enable_delta_model = true,
1006  });
1007 
1008  int call_count = 0;
1009  fl_mock_binary_messenger_set_json_method_channel(
1010  messenger, "flutter/textinput",
1011  [](FlMockBinaryMessenger* messenger, GTask* task, const gchar* name,
1012  FlValue* args, gpointer user_data) {
1013  int* call_count = static_cast<int*>(user_data);
1014 
1015  EXPECT_STREQ(name, "TextInputClient.updateEditingStateWithDeltas");
1016  g_autoptr(FlValue) expected_args = nullptr;
1017  switch (*call_count) {
1018  case 0:
1019  // commit F
1020  expected_args = build_list({
1021  fl_value_new_int(1), // client_id
1022  build_map({{
1023  "deltas",
1024  build_list({
1026  .old_text = "",
1027  .delta_text = "F",
1028  .delta_start = 0,
1029  .delta_end = 0,
1030  .selection_base = 1,
1031  .selection_extent = 1,
1032  .composing_base = -1,
1033  .composing_extent = -1,
1034  }),
1035  }),
1036  }}),
1037  });
1038  break;
1039  case 1:
1040  // commit l
1041  expected_args = build_list({
1042  fl_value_new_int(1), // client_id
1043  build_map({{
1044  "deltas",
1045  build_list({
1047  .old_text = "F",
1048  .delta_text = "l",
1049  .delta_start = 1,
1050  .delta_end = 1,
1051  .selection_base = 2,
1052  .selection_extent = 2,
1053  .composing_base = -1,
1054  .composing_extent = -1,
1055  }),
1056  }),
1057  }}),
1058  });
1059  break;
1060  case 2:
1061  // commit u
1062  expected_args = build_list({
1063  fl_value_new_int(1), // client_id
1064  build_map({{
1065  "deltas",
1066  build_list({
1068  .old_text = "Fl",
1069  .delta_text = "u",
1070  .delta_start = 2,
1071  .delta_end = 2,
1072  .selection_base = 3,
1073  .selection_extent = 3,
1074  .composing_base = -1,
1075  .composing_extent = -1,
1076  }),
1077  }),
1078  }}),
1079  });
1080  break;
1081  case 3:
1082  // commit t
1083  expected_args = build_list({
1084  fl_value_new_int(1), // client_id
1085  build_map({{
1086  "deltas",
1087  build_list({
1089  .old_text = "Flu",
1090  .delta_text = "t",
1091  .delta_start = 3,
1092  .delta_end = 3,
1093  .selection_base = 4,
1094  .selection_extent = 4,
1095  .composing_base = -1,
1096  .composing_extent = -1,
1097  }),
1098  }),
1099  }}),
1100  });
1101  break;
1102  case 4:
1103  // commit t again
1104  expected_args = build_list({
1105  fl_value_new_int(1), // client_id
1106  build_map({{
1107  "deltas",
1108  build_list({
1110  .old_text = "Flut",
1111  .delta_text = "t",
1112  .delta_start = 4,
1113  .delta_end = 4,
1114  .selection_base = 5,
1115  .selection_extent = 5,
1116  .composing_base = -1,
1117  .composing_extent = -1,
1118  }),
1119  }),
1120  }}),
1121  });
1122  break;
1123  case 5:
1124  // commit e
1125  expected_args = build_list({
1126  fl_value_new_int(1), // client_id
1127  build_map({{
1128  "deltas",
1129  build_list({
1131  .old_text = "Flutt",
1132  .delta_text = "e",
1133  .delta_start = 5,
1134  .delta_end = 5,
1135  .selection_base = 6,
1136  .selection_extent = 6,
1137  .composing_base = -1,
1138  .composing_extent = -1,
1139  }),
1140  }),
1141  }}),
1142  });
1143  break;
1144  case 6:
1145  // commit r
1146  expected_args = build_list({
1147  fl_value_new_int(1), // client_id
1148  build_map({{
1149  "deltas",
1150  build_list({
1152  .old_text = "Flutte",
1153  .delta_text = "r",
1154  .delta_start = 6,
1155  .delta_end = 6,
1156  .selection_base = 7,
1157  .selection_extent = 7,
1158  .composing_base = -1,
1159  .composing_extent = -1,
1160  }),
1161  }),
1162  }}),
1163  });
1164  break;
1165  default:
1166  g_assert_not_reached();
1167  break;
1168  }
1169  EXPECT_TRUE(fl_value_equal(args, expected_args));
1170  (*call_count)++;
1171 
1172  return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
1173  },
1174  &call_count);
1175 
1176  g_signal_emit_by_name(context, "commit", "F", nullptr);
1177  g_signal_emit_by_name(context, "commit", "l", nullptr);
1178  g_signal_emit_by_name(context, "commit", "u", nullptr);
1179  g_signal_emit_by_name(context, "commit", "t", nullptr);
1180  g_signal_emit_by_name(context, "commit", "t", nullptr);
1181  g_signal_emit_by_name(context, "commit", "e", nullptr);
1182  g_signal_emit_by_name(context, "commit", "r", nullptr);
1183  EXPECT_EQ(call_count, 7);
1184 
1185  fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
1186 }
EditingDelta::delta_end
int delta_end
Definition: fl_text_input_handler_test.cc:79
build_list
static FlValue * build_list(std::vector< FlValue * > args)
Definition: fl_text_input_handler_test.cc:27
set_editing_state
static void set_editing_state(FlMockBinaryMessenger *messenger, EditingState state)
Definition: fl_text_input_handler_test.cc:122
fl_binary_messenger_shutdown
void fl_binary_messenger_shutdown(FlBinaryMessenger *self)
Definition: fl_binary_messenger.cc:500
EditingDelta
Definition: fl_text_input_handler_test.cc:75
fl_key_event_new_from_gdk_event
FlKeyEvent * fl_key_event_new_from_gdk_event(GdkEvent *event)
Definition: fl_key_event.cc:53
InputConfig::enable_delta_model
gboolean enable_delta_model
Definition: fl_text_input_handler_test.cc:39
fl_text_input_view_delegate_translate_coordinates
void fl_text_input_view_delegate_translate_coordinates(FlTextInputViewDelegate *self, gint view_x, gint view_y, gint *window_x, gint *window_y)
Definition: fl_text_input_view_delegate.cc:14
fl_value_set_string_take
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition: fl_value.cc:650
fl_value_new_list
G_MODULE_EXPORT FlValue * fl_value_new_list()
Definition: fl_value.cc:349
EditingDelta::composing_extent
int composing_extent
Definition: fl_text_input_handler_test.cc:83
TEST
TEST(FlTextInputHandlerTest, MessageHandler)
Definition: fl_text_input_handler_test.cc:154
set_client
static void set_client(FlMockBinaryMessenger *messenger, InputConfig config)
Definition: fl_text_input_handler_test.cc:101
EditingState::composing_base
int composing_base
Definition: fl_text_input_handler_test.cc:59
fl_value_new_bool
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition: fl_value.cc:255
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
fl_text_input_handler.h
fl_value_new_null
G_MODULE_EXPORT FlValue * fl_value_new_null()
Definition: fl_value.cc:251
fl_text_input_handler_new
FlTextInputHandler * fl_text_input_handler_new(FlBinaryMessenger *messenger, GtkIMContext *im_context, FlTextInputViewDelegate *view_delegate)
Definition: fl_text_input_handler.cc:455
InputConfig::client_id
int64_t client_id
Definition: fl_text_input_handler_test.cc:36
EditingDelta::delta_text
const gchar * delta_text
Definition: fl_text_input_handler_test.cc:77
EditingState::selection_base
int selection_base
Definition: fl_text_input_handler_test.cc:57
EditingDelta::delta_start
int delta_start
Definition: fl_text_input_handler_test.cc:78
fl_value_new_int
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition: fl_value.cc:262
EditingDelta::selection_extent
int selection_extent
Definition: fl_text_input_handler_test.cc:81
fl_method_success_response_new
G_MODULE_EXPORT FlMethodSuccessResponse * fl_method_success_response_new(FlValue *result)
Definition: fl_method_response.cc:126
state
AtkStateType state
Definition: fl_accessible_node.cc:10
user_data
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
Definition: fl_event_channel.h:90
build_map
static FlValue * build_map(std::map< const gchar *, FlValue * > args)
Definition: fl_text_input_handler_test.cc:19
fl_value_new_map
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition: fl_value.cc:366
EditingState::text
const gchar * text
Definition: fl_text_input_handler_test.cc:56
fl_text_input_handler_filter_keypress
gboolean fl_text_input_handler_filter_keypress(FlTextInputHandler *self, FlKeyEvent *event)
Definition: fl_text_input_handler.cc:476
EditingDelta::old_text
const gchar * old_text
Definition: fl_text_input_handler_test.cc:76
EditingState
Definition: fl_text_input_handler_test.cc:55
fl_method_success_response_get_result
G_MODULE_EXPORT FlValue * fl_method_success_response_get_result(FlMethodSuccessResponse *self)
Definition: fl_method_response.cc:138
TRUE
return TRUE
Definition: fl_pixel_buffer_texture_test.cc:53
EditingState::composing_extent
int composing_extent
Definition: fl_text_input_handler_test.cc:60
fl_value_append_take
G_MODULE_EXPORT void fl_value_append_take(FlValue *self, FlValue *value)
Definition: fl_value.cc:600
fl_value_equal
G_MODULE_EXPORT bool fl_value_equal(FlValue *a, FlValue *b)
Definition: fl_value.cc:471
InputConfig
Definition: fl_text_input_handler_test.cc:35
EditingDelta::composing_base
int composing_base
Definition: fl_text_input_handler_test.cc:82
height
const uint8_t uint32_t uint32_t * height
Definition: fl_pixel_buffer_texture_test.cc:39
fl_binary_messenger_private.h
send_key_event
static void send_key_event(FlTextInputHandler *handler, gint keyval, gint state=0)
Definition: fl_text_input_handler_test.cc:144
build_input_config
static FlValue * build_input_config(InputConfig config)
Definition: fl_text_input_handler_test.cc:42
args
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
Definition: fl_event_channel.h:89
build_editing_state
static FlValue * build_editing_state(EditingState state)
Definition: fl_text_input_handler_test.cc:63
InputConfig::input_type
const gchar * input_type
Definition: fl_text_input_handler_test.cc:37
EditingState::selection_extent
int selection_extent
Definition: fl_text_input_handler_test.cc:58
InputConfig::input_action
const gchar * input_action
Definition: fl_text_input_handler_test.cc:38
fl_value_new_float
G_MODULE_EXPORT FlValue * fl_value_new_float(double value)
Definition: fl_value.cc:269
width
const uint8_t uint32_t * width
Definition: fl_pixel_buffer_texture_test.cc:38
flutter::MessageHandler
std::function< void(const T &message, const MessageReply< T > &reply)> MessageHandler
Definition: basic_message_channel.h:51
fl_method_codec_private.h
build_editing_delta
static FlValue * build_editing_delta(EditingDelta delta)
Definition: fl_text_input_handler_test.cc:86
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
fl_value_new_string
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
Definition: fl_value.cc:276
EditingDelta::selection_base
int selection_base
Definition: fl_text_input_handler_test.cc:80