9 G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoContext, g_object_unref)
12 #if !PANGO_VERSION_CHECK(1, 49, 4)
13 G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoLayout, g_object_unref)
31 FlAccessibleTextField,
32 fl_accessible_text_field,
35 G_IMPLEMENT_INTERFACE(ATK_TYPE_EDITABLE_TEXT,
38 static gchar* get_substring(FlAccessibleTextField*
self,
41 const gchar*
value = gtk_entry_buffer_get_text(self->buffer);
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
55 : PANGO_DIRECTION_LTR);
61 PangoLayout* layout = pango_layout_new(context);
62 pango_layout_set_text(layout, gtk_entry_buffer_get_text(self->buffer), -1);
76 const PangoLogAttr* attrs =
77 pango_layout_get_log_attrs_readonly(layout, &n_attrs);
79 while (
start > 0 && !is_start(&attrs[
start])) {
82 if (start_offset !=
nullptr) {
83 *start_offset =
start;
86 while (
end < n_attrs && !is_end(&attrs[
end])) {
89 if (end_offset !=
nullptr) {
93 return get_substring(
self,
start,
end);
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);
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);
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);
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;
143 if (end_offset !=
nullptr) {
144 *end_offset = line->start_index + line->length;
146 return get_substring(
self, line->start_index,
147 line->start_index + line->length);
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) {
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;
174 if (end_offset !=
nullptr) {
175 *end_offset =
end->start_index +
end->length;
177 return get_substring(
self,
start->start_index,
178 end->start_index +
end->length);
180 if (line->is_paragraph_start) {
192 g_autoptr(GBytes) message =
196 kFlutterSemanticsActionSetText, message);
207 g_autoptr(GBytes) message =
211 FL_ACCESSIBLE_NODE(
self), kFlutterSemanticsActionSetSelection, message);
216 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(
object);
218 g_clear_object(&self->buffer);
220 G_OBJECT_CLASS(fl_accessible_text_field_parent_class)->dispose(
object);
225 const gchar*
value) {
226 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
227 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(node);
229 if (g_strcmp0(gtk_entry_buffer_get_text(self->buffer),
value) == 0) {
233 gtk_entry_buffer_set_text(self->buffer,
value, -1);
240 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
241 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(node);
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);
249 self->selection_base = base;
250 self->selection_extent = extent;
252 if (selection_changed) {
253 g_signal_emit_by_name(
self,
"text-selection-changed",
nullptr);
257 g_signal_emit_by_name(
self,
"text-caret-moved", extent,
nullptr);
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);
268 self->text_direction = direction;
273 FlutterSemanticsAction action,
275 FlAccessibleNodeClass* parent_class =
276 FL_ACCESSIBLE_NODE_CLASS(fl_accessible_text_field_parent_class);
279 case kFlutterSemanticsActionMoveCursorForwardByCharacter:
280 case kFlutterSemanticsActionMoveCursorBackwardByCharacter:
281 case kFlutterSemanticsActionMoveCursorForwardByWord:
282 case kFlutterSemanticsActionMoveCursorBackwardByWord: {
288 FL_MESSAGE_CODEC(codec), extend_selection,
nullptr);
289 parent_class->perform_action(
self, action, message);
293 parent_class->perform_action(
self, action, data);
300 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), 0);
301 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
303 return gtk_entry_buffer_get_length(self->buffer);
310 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
nullptr);
311 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
313 return get_substring(
self, start_offset, end_offset);
320 AtkTextGranularity granularity,
323 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
nullptr);
324 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
326 switch (granularity) {
327 case ATK_TEXT_GRANULARITY_CHAR:
329 case ATK_TEXT_GRANULARITY_WORD:
331 case ATK_TEXT_GRANULARITY_SENTENCE:
333 case ATK_TEXT_GRANULARITY_LINE:
335 case ATK_TEXT_GRANULARITY_PARAGRAPH:
346 AtkTextBoundary boundary_type,
349 switch (boundary_type) {
350 case ATK_TEXT_BOUNDARY_CHAR:
352 text, offset, ATK_TEXT_GRANULARITY_CHAR, start_offset, end_offset);
354 case ATK_TEXT_BOUNDARY_WORD_START:
355 case ATK_TEXT_BOUNDARY_WORD_END:
357 text, offset, ATK_TEXT_GRANULARITY_WORD, start_offset, end_offset);
359 case ATK_TEXT_BOUNDARY_SENTENCE_START:
360 case ATK_TEXT_BOUNDARY_SENTENCE_END:
362 text, offset, ATK_TEXT_GRANULARITY_SENTENCE, start_offset,
365 case ATK_TEXT_BOUNDARY_LINE_START:
366 case ATK_TEXT_BOUNDARY_LINE_END:
368 text, offset, ATK_TEXT_GRANULARITY_LINE, start_offset, end_offset);
377 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), -1);
378 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
380 return self->selection_extent;
386 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
387 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
395 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), 0);
396 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
398 if (self->selection_base == self->selection_extent) {
410 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
nullptr);
411 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
413 if (selection_num != 0 || self->selection_base == self->selection_extent) {
417 gint
start = MIN(self->selection_base, self->selection_extent);
418 gint
end = MAX(self->selection_base, self->selection_extent);
420 if (start_offset !=
nullptr) {
421 *start_offset =
start;
423 if (end_offset !=
nullptr) {
427 return get_substring(
self,
start,
end);
434 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
435 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
437 if (self->selection_base != self->selection_extent) {
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);
451 if (selection_num != 0 || self->selection_base == self->selection_extent) {
456 self->selection_extent);
465 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
466 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
468 if (selection_num != 0) {
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);
491 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
492 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
495 gtk_entry_buffer_insert_text(self->buffer, *position,
string,
length);
505 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
506 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
508 gtk_entry_buffer_delete_text(self->buffer, start_pos, end_pos - start_pos);
518 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
519 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
524 kFlutterSemanticsActionCopy,
nullptr);
531 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
532 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
537 kFlutterSemanticsActionCut,
nullptr);
543 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
544 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
549 kFlutterSemanticsActionPaste,
nullptr);
553 FlAccessibleTextFieldClass* klass) {
555 FL_ACCESSIBLE_NODE_CLASS(klass)->set_value =
557 FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_selection =
559 FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_direction =
561 FL_ACCESSIBLE_NODE_CLASS(klass)->perform_action =
582 AtkEditableTextIface* iface) {
593 self->selection_base = -1;
594 self->selection_extent = -1;
596 self->buffer = gtk_entry_buffer_new(
"", 0);
598 g_signal_connect_object(
599 self->buffer,
"inserted-text",
600 G_CALLBACK(+[](FlAccessibleTextField*
self, guint position, gchar* chars,
602 g_signal_emit_by_name(
self,
"text-insert", position, n_chars, chars,
605 self, G_CONNECT_SWAPPED);
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);
615 self, G_CONNECT_SWAPPED);
619 return FL_ACCESSIBLE_NODE(g_object_new(fl_accessible_text_field_get_type(),
620 "engine", engine,
"id",
id,
nullptr));