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