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,
33 fl_accessible_node_get_type(),
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));
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)
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)
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)
FlAccessibleNode * fl_accessible_text_field_new(FlEngine *engine, int32_t id)
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)
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 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)
G_MODULE_EXPORT FlValue * fl_value_new_map()
typedefG_BEGIN_DECLS struct _FlValue FlValue
FlutterTextDirection text_direction
FlAccessibleNode parent_instance