Flutter Linux Embedder
fl_accessible_text_field.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 
8 
9 G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoContext, g_object_unref)
10 // PangoLayout g_autoptr macro weren't added until 1.49.4. Add them manually.
11 // https://gitlab.gnome.org/GNOME/pango/-/commit/0b84e14
12 #if !PANGO_VERSION_CHECK(1, 49, 4)
13 G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoLayout, g_object_unref)
14 #endif
15 
16 typedef bool (*FlTextBoundaryCallback)(const PangoLogAttr* attr);
17 
19  FlAccessibleNode parent_instance;
20 
23  GtkEntryBuffer* buffer;
24  FlutterTextDirection text_direction;
25 };
26 
27 static void fl_accessible_text_iface_init(AtkTextIface* iface);
28 static void fl_accessible_editable_text_iface_init(AtkEditableTextIface* iface);
29 
31  FlAccessibleTextField,
32  fl_accessible_text_field,
33  fl_accessible_node_get_type(),
34  G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT, fl_accessible_text_iface_init)
35  G_IMPLEMENT_INTERFACE(ATK_TYPE_EDITABLE_TEXT,
37 
38 static gchar* get_substring(FlAccessibleTextField* self,
39  glong start,
40  glong end) {
41  const gchar* value = gtk_entry_buffer_get_text(self->buffer);
42  glong length = g_utf8_strlen(value, -1);
43  if (end == -1) {
44  // g_utf8_substring() accepts -1 since 2.72
45  end = length;
46  }
47  start = CLAMP(start, 0, length);
48  end = CLAMP(end, start, length);
50 }
51 
52 static PangoContext* get_pango_context(FlAccessibleTextField* self) {
53  PangoFontMap* font_map = pango_cairo_font_map_get_default();
54  PangoContext* context = pango_font_map_create_context(font_map);
55  pango_context_set_base_dir(context,
56  self->text_direction == kFlutterTextDirectionRTL
57  ? PANGO_DIRECTION_RTL
58  : PANGO_DIRECTION_LTR);
59  return context;
60 }
61 
62 static PangoLayout* create_pango_layout(FlAccessibleTextField* self) {
63  g_autoptr(PangoContext) context = get_pango_context(self);
64  PangoLayout* layout = pango_layout_new(context);
65  pango_layout_set_text(layout, gtk_entry_buffer_get_text(self->buffer), -1);
66  return layout;
67 }
68 
69 static gchar* get_string_at_offset(FlAccessibleTextField* self,
70  gint start,
71  gint end,
72  FlTextBoundaryCallback is_start,
74  gint* start_offset,
75  gint* end_offset) {
76  g_autoptr(PangoLayout) layout = create_pango_layout(self);
77 
78  gint n_attrs = 0;
79  const PangoLogAttr* attrs =
80  pango_layout_get_log_attrs_readonly(layout, &n_attrs);
81 
82  start = CLAMP(start, 0, MAX(n_attrs - 1, 0));
83  end = CLAMP(end, 0, MAX(n_attrs - 1, 0));
84 
85  while (start > 0 && !is_start(&attrs[start])) {
86  --start;
87  }
88 
89  while (end < n_attrs && !is_end(&attrs[end])) {
90  ++end;
91  }
92 
93  if (start_offset != nullptr) {
94  *start_offset = start;
95  }
96  if (end_offset != nullptr) {
97  *end_offset = end;
98  }
99 
100  return get_substring(self, start, end);
101 }
102 
103 static gchar* get_char_at_offset(FlAccessibleTextField* self,
104  gint offset,
105  gint* start_offset,
106  gint* end_offset) {
107  return get_string_at_offset(
108  self, offset, offset + 1,
109  [](const PangoLogAttr* attr) -> bool { return attr->is_char_break; },
110  [](const PangoLogAttr* attr) -> bool { return attr->is_char_break; },
111  start_offset, end_offset);
112 }
113 
114 static gchar* get_word_at_offset(FlAccessibleTextField* self,
115  gint offset,
116  gint* start_offset,
117  gint* end_offset) {
118  return get_string_at_offset(
119  self, offset, offset,
120  [](const PangoLogAttr* attr) -> bool { return attr->is_word_start; },
121  [](const PangoLogAttr* attr) -> bool { return attr->is_word_end; },
122  start_offset, end_offset);
123 }
124 
125 static gchar* get_sentence_at_offset(FlAccessibleTextField* self,
126  gint offset,
127  gint* start_offset,
128  gint* end_offset) {
129  return get_string_at_offset(
130  self, offset, offset,
131  [](const PangoLogAttr* attr) -> bool { return attr->is_sentence_start; },
132  [](const PangoLogAttr* attr) -> bool { return attr->is_sentence_end; },
133  start_offset, end_offset);
134 }
135 
136 // Returns the line containing @offset, along with its start and end character
137 // offsets. The line excludes the trailing line break. Returns nullptr if
138 // @offset does not fall within any line.
139 static gchar* get_line_at_offset(FlAccessibleTextField* self,
140  gint offset,
141  gint* start_offset,
142  gint* end_offset) {
143  g_autoptr(PangoLayout) layout = create_pango_layout(self);
144  const gchar* text = gtk_entry_buffer_get_text(self->buffer);
145 
146  for (GSList* node = pango_layout_get_lines_readonly(layout); node != nullptr;
147  node = node->next) {
148  PangoLayoutLine* line = static_cast<PangoLayoutLine*>(node->data);
149  // PangoLayoutLine uses byte indices, but ATK offsets are character
150  // offsets, so convert before comparing.
151  gint line_start = g_utf8_pointer_to_offset(text, text + line->start_index);
152  gint line_end =
153  g_utf8_pointer_to_offset(text, text + line->start_index + line->length);
154  if (offset >= line_start && offset <= line_end) {
155  if (start_offset != nullptr) {
156  *start_offset = line_start;
157  }
158  if (end_offset != nullptr) {
159  *end_offset = line_end;
160  }
161  return get_substring(self, line_start, line_end);
162  }
163  }
164 
165  return nullptr;
166 }
167 
168 // Returns the paragraph containing @offset, along with its start and end
169 // character offsets. A paragraph may span multiple wrapped lines and extends
170 // from one paragraph start up to the following paragraph start. Returns nullptr
171 // if @offset does not fall within any paragraph.
172 static gchar* get_paragraph_at_offset(FlAccessibleTextField* self,
173  gint offset,
174  gint* start_offset,
175  gint* end_offset) {
176  g_autoptr(PangoLayout) layout = create_pango_layout(self);
177  const gchar* text = gtk_entry_buffer_get_text(self->buffer);
178 
179  PangoLayoutLine* start = nullptr;
180  PangoLayoutLine* end = nullptr;
181  gint n_lines = pango_layout_get_line_count(layout);
182  for (gint i = 0; i < n_lines; ++i) {
183  PangoLayoutLine* line = pango_layout_get_line(layout, i);
184  if (line->is_paragraph_start) {
185  end = line;
186  }
187  if (start != nullptr && end != nullptr) {
188  // PangoLayoutLine uses byte indices, but ATK offsets are character
189  // offsets, so convert before comparing.
190  gint para_start =
191  g_utf8_pointer_to_offset(text, text + start->start_index);
192  gint para_end =
193  g_utf8_pointer_to_offset(text, text + end->start_index + end->length);
194  if (offset >= para_start && offset <= para_end) {
195  if (start_offset != nullptr) {
196  *start_offset = para_start;
197  }
198  if (end_offset != nullptr) {
199  *end_offset = para_end;
200  }
201  return get_substring(self, para_start, para_end);
202  }
203  }
204  if (line->is_paragraph_start) {
205  start = line;
206  }
207  }
208 
209  return nullptr;
210 }
211 
212 static void perform_set_text_action(FlAccessibleTextField* self,
213  const char* text) {
215  g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new();
216  g_autoptr(GBytes) message =
217  fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), value, nullptr);
218 
219  fl_accessible_node_perform_action(FL_ACCESSIBLE_NODE(self),
220  kFlutterSemanticsActionSetText, message);
221 }
222 
223 static void perform_set_selection_action(FlAccessibleTextField* self,
224  gint base,
225  gint extent) {
228  fl_value_set_string_take(value, "extent", fl_value_new_int(extent));
229 
230  g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new();
231  g_autoptr(GBytes) message =
232  fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), value, nullptr);
233 
235  FL_ACCESSIBLE_NODE(self), kFlutterSemanticsActionSetSelection, message);
236 }
237 
238 // Implements GObject::dispose.
239 static void fl_accessible_text_field_dispose(GObject* object) {
240  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(object);
241 
242  g_clear_object(&self->buffer);
243 
244  G_OBJECT_CLASS(fl_accessible_text_field_parent_class)->dispose(object);
245 }
246 
247 // Implements FlAccessibleNode::set_value.
248 static void fl_accessible_text_field_set_value(FlAccessibleNode* node,
249  const gchar* value) {
250  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
251  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(node);
252 
253  if (g_strcmp0(gtk_entry_buffer_get_text(self->buffer), value) == 0) {
254  return;
255  }
256 
257  gtk_entry_buffer_set_text(self->buffer, value, -1);
258 }
259 
260 // Implements FlAccessibleNode::set_text_selection.
261 static void fl_accessible_text_field_set_text_selection(FlAccessibleNode* node,
262  gint base,
263  gint extent) {
264  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
265  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(node);
266 
267  gboolean caret_moved = extent != self->selection_extent;
268  gboolean has_selection = base != extent;
269  gboolean had_selection = self->selection_base != self->selection_extent;
270  gboolean selection_changed = (has_selection || had_selection) &&
271  (caret_moved || base != self->selection_base);
272 
273  self->selection_base = base;
274  self->selection_extent = extent;
275 
276  if (selection_changed) {
277  g_signal_emit_by_name(self, "text-selection-changed", nullptr);
278  }
279 
280  if (caret_moved) {
281  g_signal_emit_by_name(self, "text-caret-moved", extent, nullptr);
282  }
283 }
284 
285 // Implements FlAccessibleNode::set_text_direction.
287  FlAccessibleNode* node,
288  FlutterTextDirection direction) {
289  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
290  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(node);
291 
292  self->text_direction = direction;
293 }
294 
295 // Overrides FlAccessibleNode::perform_action.
296 void fl_accessible_text_field_perform_action(FlAccessibleNode* self,
297  FlutterSemanticsAction action,
298  GBytes* data) {
299  FlAccessibleNodeClass* parent_class =
300  FL_ACCESSIBLE_NODE_CLASS(fl_accessible_text_field_parent_class);
301 
302  switch (action) {
303  case kFlutterSemanticsActionMoveCursorForwardByCharacter:
304  case kFlutterSemanticsActionMoveCursorBackwardByCharacter:
305  case kFlutterSemanticsActionMoveCursorForwardByWord:
306  case kFlutterSemanticsActionMoveCursorBackwardByWord: {
307  // These actions require a boolean argument that indicates whether the
308  // selection should be extended or collapsed when moving the cursor.
309  g_autoptr(FlValue) extend_selection = fl_value_new_bool(false);
310  g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new();
312  FL_MESSAGE_CODEC(codec), extend_selection, nullptr);
313  parent_class->perform_action(self, action, message);
314  break;
315  }
316  default:
317  parent_class->perform_action(self, action, data);
318  break;
319  }
320 }
321 
322 // Implements AtkText::get_character_count.
324  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), 0);
325  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
326 
327  return gtk_entry_buffer_get_length(self->buffer);
328 }
329 
330 // Implements AtkText::get_text.
331 static gchar* fl_accessible_text_field_get_text(AtkText* text,
332  gint start_offset,
333  gint end_offset) {
334  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), nullptr);
335  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
336 
337  return get_substring(self, start_offset, end_offset);
338 }
339 
340 // Implements AtkText::get_string_at_offset.
342  AtkText* text,
343  gint offset,
344  AtkTextGranularity granularity,
345  gint* start_offset,
346  gint* end_offset) {
347  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), nullptr);
348  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
349 
350  switch (granularity) {
351  case ATK_TEXT_GRANULARITY_CHAR:
352  return get_char_at_offset(self, offset, start_offset, end_offset);
353  case ATK_TEXT_GRANULARITY_WORD:
354  return get_word_at_offset(self, offset, start_offset, end_offset);
355  case ATK_TEXT_GRANULARITY_SENTENCE:
356  return get_sentence_at_offset(self, offset, start_offset, end_offset);
357  case ATK_TEXT_GRANULARITY_LINE:
358  return get_line_at_offset(self, offset, start_offset, end_offset);
359  case ATK_TEXT_GRANULARITY_PARAGRAPH:
360  return get_paragraph_at_offset(self, offset, start_offset, end_offset);
361  default:
362  return nullptr;
363  }
364 }
365 
366 // Implements AtkText::get_text_at_offset (deprecated but still commonly used).
368  AtkText* text,
369  gint offset,
370  AtkTextBoundary boundary_type,
371  gint* start_offset,
372  gint* end_offset) {
373  switch (boundary_type) {
374  case ATK_TEXT_BOUNDARY_CHAR:
376  text, offset, ATK_TEXT_GRANULARITY_CHAR, start_offset, end_offset);
377  break;
378  case ATK_TEXT_BOUNDARY_WORD_START:
379  case ATK_TEXT_BOUNDARY_WORD_END:
381  text, offset, ATK_TEXT_GRANULARITY_WORD, start_offset, end_offset);
382  break;
383  case ATK_TEXT_BOUNDARY_SENTENCE_START:
384  case ATK_TEXT_BOUNDARY_SENTENCE_END:
386  text, offset, ATK_TEXT_GRANULARITY_SENTENCE, start_offset,
387  end_offset);
388  break;
389  case ATK_TEXT_BOUNDARY_LINE_START:
390  case ATK_TEXT_BOUNDARY_LINE_END:
392  text, offset, ATK_TEXT_GRANULARITY_LINE, start_offset, end_offset);
393  break;
394  default:
395  return nullptr;
396  }
397 }
398 
399 // Implements AtkText::get_caret_offset.
400 static gint fl_accessible_text_field_get_caret_offset(AtkText* text) {
401  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), -1);
402  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
403 
404  return self->selection_extent;
405 }
406 
407 // Implements AtkText::set_caret_offset.
408 static gboolean fl_accessible_text_field_set_caret_offset(AtkText* text,
409  gint offset) {
410  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), false);
411  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
412 
413  perform_set_selection_action(self, offset, offset);
414  return TRUE;
415 }
416 
417 // Implements AtkText::get_n_selections.
418 static gint fl_accessible_text_field_get_n_selections(AtkText* text) {
419  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), 0);
420  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
421 
422  if (self->selection_base == self->selection_extent) {
423  return 0;
424  }
425 
426  return 1;
427 }
428 
429 // Implements AtkText::get_selection.
430 static gchar* fl_accessible_text_field_get_selection(AtkText* text,
431  gint selection_num,
432  gint* start_offset,
433  gint* end_offset) {
434  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), nullptr);
435  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
436 
437  if (selection_num != 0 || self->selection_base == self->selection_extent) {
438  return nullptr;
439  }
440 
441  gint start = MIN(self->selection_base, self->selection_extent);
442  gint end = MAX(self->selection_base, self->selection_extent);
443 
444  if (start_offset != nullptr) {
445  *start_offset = start;
446  }
447  if (end_offset != nullptr) {
448  *end_offset = end;
449  }
450 
451  return get_substring(self, start, end);
452 }
453 
454 // Implements AtkText::add_selection.
455 static gboolean fl_accessible_text_field_add_selection(AtkText* text,
456  gint start_offset,
457  gint end_offset) {
458  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), false);
459  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
460 
461  if (self->selection_base != self->selection_extent) {
462  return FALSE;
463  }
464 
465  perform_set_selection_action(self, start_offset, end_offset);
466  return TRUE;
467 }
468 
469 // Implements AtkText::remove_selection.
470 static gboolean fl_accessible_text_field_remove_selection(AtkText* text,
471  gint selection_num) {
472  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), false);
473  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
474 
475  if (selection_num != 0 || self->selection_base == self->selection_extent) {
476  return FALSE;
477  }
478 
479  perform_set_selection_action(self, self->selection_extent,
480  self->selection_extent);
481  return TRUE;
482 }
483 
484 // Implements AtkText::set_selection.
485 static gboolean fl_accessible_text_field_set_selection(AtkText* text,
486  gint selection_num,
487  gint start_offset,
488  gint end_offset) {
489  g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), false);
490  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(text);
491 
492  if (selection_num != 0) {
493  return FALSE;
494  }
495 
496  perform_set_selection_action(self, start_offset, end_offset);
497  return TRUE;
498 }
499 
500 // Implements AtkEditableText::set_text_contents.
502  AtkEditableText* editable_text,
503  const gchar* string) {
504  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
505  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
506 
507  perform_set_text_action(self, string);
508 }
509 
510 // Implements AtkEditableText::insert_text.
511 static void fl_accessible_text_field_insert_text(AtkEditableText* editable_text,
512  const gchar* string,
513  gint length,
514  gint* position) {
515  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
516  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
517 
518  *position +=
519  gtk_entry_buffer_insert_text(self->buffer, *position, string, length);
520 
521  perform_set_text_action(self, gtk_entry_buffer_get_text(self->buffer));
522  perform_set_selection_action(self, *position, *position);
523 }
524 
525 // Implements AtkEditableText::delete_text.
526 static void fl_accessible_node_delete_text(AtkEditableText* editable_text,
527  gint start_pos,
528  gint end_pos) {
529  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
530  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
531 
532  gtk_entry_buffer_delete_text(self->buffer, start_pos, end_pos - start_pos);
533 
534  perform_set_text_action(self, gtk_entry_buffer_get_text(self->buffer));
535  perform_set_selection_action(self, start_pos, start_pos);
536 }
537 
538 // Implement AtkEditableText::copy_text.
539 static void fl_accessible_text_field_copy_text(AtkEditableText* editable_text,
540  gint start_pos,
541  gint end_pos) {
542  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
543  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
544 
545  perform_set_selection_action(self, start_pos, end_pos);
546 
547  fl_accessible_node_perform_action(FL_ACCESSIBLE_NODE(editable_text),
548  kFlutterSemanticsActionCopy, nullptr);
549 }
550 
551 // Implements AtkEditableText::cut_text.
552 static void fl_accessible_text_field_cut_text(AtkEditableText* editable_text,
553  gint start_pos,
554  gint end_pos) {
555  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
556  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
557 
558  perform_set_selection_action(self, start_pos, end_pos);
559 
560  fl_accessible_node_perform_action(FL_ACCESSIBLE_NODE(editable_text),
561  kFlutterSemanticsActionCut, nullptr);
562 }
563 
564 // Implements AtkEditableText::paste_text.
565 static void fl_accessible_text_field_paste_text(AtkEditableText* editable_text,
566  gint position) {
567  g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
568  FlAccessibleTextField* self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
569 
570  perform_set_selection_action(self, position, position);
571 
572  fl_accessible_node_perform_action(FL_ACCESSIBLE_NODE(editable_text),
573  kFlutterSemanticsActionPaste, nullptr);
574 }
575 
577  FlAccessibleTextFieldClass* klass) {
578  G_OBJECT_CLASS(klass)->dispose = fl_accessible_text_field_dispose;
579  FL_ACCESSIBLE_NODE_CLASS(klass)->set_value =
581  FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_selection =
583  FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_direction =
585  FL_ACCESSIBLE_NODE_CLASS(klass)->perform_action =
587 }
588 
589 static void fl_accessible_text_iface_init(AtkTextIface* iface) {
590  iface->get_character_count = fl_accessible_text_field_get_character_count;
591  iface->get_text = fl_accessible_text_field_get_text;
592  iface->get_text_at_offset = fl_accessible_text_field_get_text_at_offset;
593  iface->get_string_at_offset = fl_accessible_text_field_get_string_at_offset;
594 
595  iface->get_caret_offset = fl_accessible_text_field_get_caret_offset;
596  iface->set_caret_offset = fl_accessible_text_field_set_caret_offset;
597 
598  iface->get_n_selections = fl_accessible_text_field_get_n_selections;
599  iface->get_selection = fl_accessible_text_field_get_selection;
600  iface->add_selection = fl_accessible_text_field_add_selection;
601  iface->remove_selection = fl_accessible_text_field_remove_selection;
602  iface->set_selection = fl_accessible_text_field_set_selection;
603 }
604 
606  AtkEditableTextIface* iface) {
607  iface->set_text_contents = fl_accessible_text_field_set_text_contents;
608  iface->insert_text = fl_accessible_text_field_insert_text;
609  iface->delete_text = fl_accessible_node_delete_text;
610 
611  iface->copy_text = fl_accessible_text_field_copy_text;
612  iface->cut_text = fl_accessible_text_field_cut_text;
613  iface->paste_text = fl_accessible_text_field_paste_text;
614 }
615 
616 static void fl_accessible_text_field_init(FlAccessibleTextField* self) {
617  self->selection_base = -1;
618  self->selection_extent = -1;
619 
620  self->buffer = gtk_entry_buffer_new("", 0);
621 
622  g_signal_connect_object(
623  self->buffer, "inserted-text",
624  G_CALLBACK(+[](FlAccessibleTextField* self, guint position, gchar* chars,
625  guint n_chars) {
626  g_signal_emit_by_name(self, "text-insert", position, n_chars, chars,
627  nullptr);
628  }),
629  self, G_CONNECT_SWAPPED);
630 
631  g_signal_connect_object(self->buffer, "deleted-text",
632  G_CALLBACK(+[](FlAccessibleTextField* self,
633  guint position, guint n_chars) {
634  g_autofree gchar* chars = atk_text_get_text(
635  ATK_TEXT(self), position, position + n_chars);
636  g_signal_emit_by_name(self, "text-remove", position,
637  n_chars, chars, nullptr);
638  }),
639  self, G_CONNECT_SWAPPED);
640 }
641 
642 FlAccessibleNode* fl_accessible_text_field_new(FlEngine* engine,
643  FlutterViewId view_id,
644  int32_t id) {
645  return FL_ACCESSIBLE_NODE(g_object_new(fl_accessible_text_field_get_type(),
646  "engine", engine, "view-id", view_id,
647  "node-id", id, nullptr));
648 }
const char * message
g_autoptr(FlEngine) engine
void fl_accessible_node_perform_action(FlAccessibleNode *self, FlutterSemanticsAction action, GBytes *data)
static void perform_set_selection_action(FlAccessibleTextField *self, gint base, gint extent)
static gint fl_accessible_text_field_get_n_selections(AtkText *text)
static void fl_accessible_text_field_insert_text(AtkEditableText *editable_text, const gchar *string, gint length, gint *position)
static void fl_accessible_text_field_dispose(GObject *object)
static void fl_accessible_text_field_paste_text(AtkEditableText *editable_text, gint position)
static gboolean fl_accessible_text_field_set_selection(AtkText *text, gint selection_num, gint start_offset, gint end_offset)
static void fl_accessible_text_field_set_text_contents(AtkEditableText *editable_text, const gchar *string)
static void fl_accessible_text_field_copy_text(AtkEditableText *editable_text, gint start_pos, gint end_pos)
glong length
static gchar * fl_accessible_text_field_get_text_at_offset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
static void fl_accessible_text_field_init(FlAccessibleTextField *self)
static gboolean fl_accessible_text_field_remove_selection(AtkText *text, gint selection_num)
static gchar * get_sentence_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
static gchar * get_char_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
static gchar * get_string_at_offset(FlAccessibleTextField *self, gint start, gint end, FlTextBoundaryCallback is_start, FlTextBoundaryCallback is_end, gint *start_offset, gint *end_offset)
static gchar * get_word_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
G_DEFINE_TYPE_WITH_CODE(FlAccessibleTextField, fl_accessible_text_field, fl_accessible_node_get_type(), G_IMPLEMENT_INTERFACE(ATK_TYPE_EDITABLE_TEXT, fl_accessible_editable_text_iface_init)) static gchar *get_substring(FlAccessibleTextField *self
static PangoLayout * create_pango_layout(FlAccessibleTextField *self)
static void fl_accessible_text_field_set_text_direction(FlAccessibleNode *node, FlutterTextDirection direction)
static void fl_accessible_text_field_set_text_selection(FlAccessibleNode *node, gint base, gint extent)
bool(* FlTextBoundaryCallback)(const PangoLogAttr *attr)
static void fl_accessible_text_iface_init(AtkTextIface *iface)
static void fl_accessible_text_field_set_value(FlAccessibleNode *node, const gchar *value)
static PangoContext * get_pango_context(FlAccessibleTextField *self)
static gchar * get_paragraph_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
static void fl_accessible_editable_text_iface_init(AtkEditableTextIface *iface)
FlAccessibleNode * fl_accessible_text_field_new(FlEngine *engine, FlutterViewId view_id, int32_t id)
static gchar * fl_accessible_text_field_get_selection(AtkText *text, gint selection_num, gint *start_offset, gint *end_offset)
static gboolean fl_accessible_text_field_set_caret_offset(AtkText *text, gint offset)
static gchar * fl_accessible_text_field_get_string_at_offset(AtkText *text, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset)
static void fl_accessible_node_delete_text(AtkEditableText *editable_text, gint start_pos, gint end_pos)
static void fl_accessible_text_field_cut_text(AtkEditableText *editable_text, gint start_pos, gint end_pos)
static gboolean fl_accessible_text_field_add_selection(AtkText *text, gint start_offset, gint end_offset)
static void perform_set_text_action(FlAccessibleTextField *self, const char *text)
static void fl_accessible_text_field_class_init(FlAccessibleTextFieldClass *klass)
return g_utf8_substring(value, start, end)
static gchar * get_line_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
static gint fl_accessible_text_field_get_character_count(AtkText *text)
static gchar * fl_accessible_text_field_get_text(AtkText *text, gint start_offset, gint end_offset)
static gint fl_accessible_text_field_get_caret_offset(AtkText *text)
glong glong end
void fl_accessible_text_field_perform_action(FlAccessibleNode *self, FlutterSemanticsAction action, GBytes *data)
return TRUE
G_MODULE_EXPORT GBytes * fl_message_codec_encode_message(FlMessageCodec *self, FlValue *message, GError **error)
uint8_t value
G_MODULE_EXPORT FlStandardMessageCodec * fl_standard_message_codec_new()
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_new_string(const gchar *value)
Definition: fl_value.cc:276
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition: fl_value.cc:255
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition: fl_value.cc:262
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition: fl_value.cc:366
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
G_BEGIN_DECLS FlutterViewId view_id
FlutterTextDirection text_direction