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"
9G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoContext, g_object_unref)
12#if !PANGO_VERSION_CHECK(1, 49, 4)
13G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoLayout, g_object_unref)
31 FlAccessibleTextField,
32 fl_accessible_text_field,
35 G_IMPLEMENT_INTERFACE(ATK_TYPE_EDITABLE_TEXT,
38static 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,
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) {
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);
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);
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) {
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) {
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);
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,
265 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
266 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(node);
268 self->text_direction = direction;
275 FlAccessibleNodeClass* parent_class =
276 FL_ACCESSIBLE_NODE_CLASS(fl_accessible_text_field_parent_class);
288 FL_MESSAGE_CODEC(codec), extend_selection,
nullptr);
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) {
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);
531 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
532 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
543 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
544 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
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));
@ kFlutterSemanticsActionMoveCursorForwardByCharacter
Move the cursor forward by one character.
@ kFlutterSemanticsActionMoveCursorBackwardByCharacter
Move the cursor backward by one character.
@ kFlutterSemanticsActionMoveCursorForwardByWord
Move the cursor forward by one word.
@ kFlutterSemanticsActionSetSelection
Set the text selection to the given range.
@ kFlutterSemanticsActionPaste
Paste the current content of the clipboard.
@ kFlutterSemanticsActionCut
Cut the current selection and place it in the clipboard.
@ kFlutterSemanticsActionCopy
Copy the current selection to the clipboard.
@ kFlutterSemanticsActionMoveCursorBackwardByWord
Move the cursor backward by one word.
@ kFlutterSemanticsActionSetText
Replace the current text in the text field.
@ kFlutterTextDirectionRTL
Text is read from right to left.
void fl_accessible_node_perform_action(FlAccessibleNode *self, FlutterSemanticsAction action, GBytes *data)
#define FL_TYPE_ACCESSIBLE_NODE
static void perform_set_selection_action(FlAccessibleTextField *self, gint base, gint extent)
static gint fl_accessible_text_field_get_n_selections(AtkText *text)
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_text_field_insert_text(AtkEditableText *editable_text, const gchar *string, gint length, gint *position)
static gchar * fl_accessible_text_field_get_text(AtkText *text, gint start_offset, gint end_offset)
static void fl_accessible_text_field_dispose(GObject *object)
static gchar * fl_accessible_text_field_get_selection(AtkText *text, gint selection_num, gint *start_offset, gint *end_offset)
static void fl_accessible_text_field_paste_text(AtkEditableText *editable_text, gint position)
static gchar * get_line_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
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)
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_word_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
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 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_set_value(FlAccessibleNode *node, const gchar *value)
static void fl_accessible_editable_text_iface_init(AtkEditableTextIface *iface)
static gboolean fl_accessible_text_field_set_caret_offset(AtkText *text, gint offset)
static void fl_accessible_node_delete_text(AtkEditableText *editable_text, gint start_pos, gint end_pos)
static gchar * get_sentence_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
static void fl_accessible_text_field_cut_text(AtkEditableText *editable_text, gint start_pos, gint end_pos)
static gchar * get_char_at_offset(FlAccessibleTextField *self, gint offset, gint *start_offset, gint *end_offset)
static PangoContext * get_pango_context(FlAccessibleTextField *self)
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)
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
static gchar * get_paragraph_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 void fl_accessible_text_field_class_init(FlAccessibleTextFieldClass *klass)
return g_utf8_substring(value, start, end)
static gint fl_accessible_text_field_get_character_count(AtkText *text)
static gint fl_accessible_text_field_get_caret_offset(AtkText *text)
FlAccessibleNode * fl_accessible_text_field_new(FlEngine *engine, int32_t id)
static PangoLayout * create_pango_layout(FlAccessibleTextField *self)
void fl_accessible_text_field_perform_action(FlAccessibleNode *self, FlutterSemanticsAction action, GBytes *data)
G_MODULE_EXPORT GBytes * fl_message_codec_encode_message(FlMessageCodec *self, FlValue *message, GError **error)
G_MODULE_EXPORT FlStandardMessageCodec * fl_standard_message_codec_new()
G_MODULE_EXPORT FlValue * fl_value_new_map()
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
typedefG_BEGIN_DECLS struct _FlValue FlValue
FlutterTextDirection text_direction
FlAccessibleNode parent_instance