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);
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
58 : PANGO_DIRECTION_LTR);
64 PangoLayout* layout = pango_layout_new(context);
65 pango_layout_set_text(layout, gtk_entry_buffer_get_text(self->buffer), -1);
79 const PangoLogAttr* attrs =
80 pango_layout_get_log_attrs_readonly(layout, &n_attrs);
83 end = CLAMP(
end, 0, MAX(n_attrs - 1, 0));
85 while (
start > 0 && !is_start(&attrs[
start])) {
89 while (
end < n_attrs && !is_end(&attrs[
end])) {
93 if (start_offset !=
nullptr) {
94 *start_offset =
start;
96 if (end_offset !=
nullptr) {
100 return get_substring(
self,
start,
end);
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);
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);
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);
142 GSList* lines = pango_layout_get_lines_readonly(layout);
143 while (lines !=
nullptr) {
144 PangoLayoutLine* line =
static_cast<PangoLayoutLine*
>(lines->data);
145 if (offset >= line->start_index &&
146 offset <= line->start_index + line->length) {
147 if (start_offset !=
nullptr) {
148 *start_offset = line->start_index;
150 if (end_offset !=
nullptr) {
151 *end_offset = line->start_index + line->length;
153 return get_substring(
self, line->start_index,
154 line->start_index + line->length);
168 PangoLayoutLine*
start =
nullptr;
169 PangoLayoutLine*
end =
nullptr;
170 gint n_lines = pango_layout_get_line_count(layout);
171 for (gint
i = 0;
i < n_lines; ++
i) {
172 PangoLayoutLine* line = pango_layout_get_line(layout,
i);
173 if (line->is_paragraph_start) {
176 if (
start !=
nullptr &&
end !=
nullptr && offset >=
start->start_index &&
177 offset <= end->start_index +
end->length) {
178 if (start_offset !=
nullptr) {
179 *start_offset =
start->start_index;
181 if (end_offset !=
nullptr) {
182 *end_offset =
end->start_index +
end->length;
184 return get_substring(
self,
start->start_index,
185 end->start_index +
end->length);
187 if (line->is_paragraph_start) {
203 kFlutterSemanticsActionSetText,
message);
218 FL_ACCESSIBLE_NODE(
self), kFlutterSemanticsActionSetSelection,
message);
223 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(
object);
225 g_clear_object(&self->buffer);
227 G_OBJECT_CLASS(fl_accessible_text_field_parent_class)->dispose(
object);
232 const gchar*
value) {
233 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
234 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(node);
236 if (g_strcmp0(gtk_entry_buffer_get_text(self->buffer),
value) == 0) {
240 gtk_entry_buffer_set_text(self->buffer,
value, -1);
247 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
248 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(node);
250 gboolean caret_moved = extent !=
self->selection_extent;
251 gboolean has_selection = base != extent;
252 gboolean had_selection =
self->selection_base !=
self->selection_extent;
253 gboolean selection_changed = (has_selection || had_selection) &&
254 (caret_moved || base != self->selection_base);
256 self->selection_base = base;
257 self->selection_extent = extent;
259 if (selection_changed) {
260 g_signal_emit_by_name(
self,
"text-selection-changed",
nullptr);
264 g_signal_emit_by_name(
self,
"text-caret-moved", extent,
nullptr);
270 FlAccessibleNode* node,
271 FlutterTextDirection direction) {
272 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(node));
273 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(node);
275 self->text_direction = direction;
280 FlutterSemanticsAction action,
282 FlAccessibleNodeClass* parent_class =
283 FL_ACCESSIBLE_NODE_CLASS(fl_accessible_text_field_parent_class);
286 case kFlutterSemanticsActionMoveCursorForwardByCharacter:
287 case kFlutterSemanticsActionMoveCursorBackwardByCharacter:
288 case kFlutterSemanticsActionMoveCursorForwardByWord:
289 case kFlutterSemanticsActionMoveCursorBackwardByWord: {
295 FL_MESSAGE_CODEC(codec), extend_selection,
nullptr);
296 parent_class->perform_action(
self, action,
message);
300 parent_class->perform_action(
self, action, data);
307 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), 0);
308 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
310 return gtk_entry_buffer_get_length(self->buffer);
317 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
nullptr);
318 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
320 return get_substring(
self, start_offset, end_offset);
327 AtkTextGranularity granularity,
330 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
nullptr);
331 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
333 switch (granularity) {
334 case ATK_TEXT_GRANULARITY_CHAR:
336 case ATK_TEXT_GRANULARITY_WORD:
338 case ATK_TEXT_GRANULARITY_SENTENCE:
340 case ATK_TEXT_GRANULARITY_LINE:
342 case ATK_TEXT_GRANULARITY_PARAGRAPH:
353 AtkTextBoundary boundary_type,
356 switch (boundary_type) {
357 case ATK_TEXT_BOUNDARY_CHAR:
359 text, offset, ATK_TEXT_GRANULARITY_CHAR, start_offset, end_offset);
361 case ATK_TEXT_BOUNDARY_WORD_START:
362 case ATK_TEXT_BOUNDARY_WORD_END:
364 text, offset, ATK_TEXT_GRANULARITY_WORD, start_offset, end_offset);
366 case ATK_TEXT_BOUNDARY_SENTENCE_START:
367 case ATK_TEXT_BOUNDARY_SENTENCE_END:
369 text, offset, ATK_TEXT_GRANULARITY_SENTENCE, start_offset,
372 case ATK_TEXT_BOUNDARY_LINE_START:
373 case ATK_TEXT_BOUNDARY_LINE_END:
375 text, offset, ATK_TEXT_GRANULARITY_LINE, start_offset, end_offset);
384 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), -1);
385 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
387 return self->selection_extent;
393 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
394 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
402 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text), 0);
403 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
405 if (self->selection_base == self->selection_extent) {
417 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
nullptr);
418 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
420 if (selection_num != 0 || self->selection_base == self->selection_extent) {
424 gint
start = MIN(self->selection_base, self->selection_extent);
425 gint
end = MAX(self->selection_base, self->selection_extent);
427 if (start_offset !=
nullptr) {
428 *start_offset =
start;
430 if (end_offset !=
nullptr) {
434 return get_substring(
self,
start,
end);
441 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
442 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
444 if (self->selection_base != self->selection_extent) {
454 gint selection_num) {
455 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
456 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
458 if (selection_num != 0 || self->selection_base == self->selection_extent) {
463 self->selection_extent);
472 g_return_val_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(text),
false);
473 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(text);
475 if (selection_num != 0) {
485 AtkEditableText* editable_text,
486 const gchar*
string) {
487 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
488 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
498 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
499 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
502 gtk_entry_buffer_insert_text(self->buffer, *position,
string,
length);
512 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
513 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
515 gtk_entry_buffer_delete_text(self->buffer, start_pos, end_pos - start_pos);
525 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
526 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
531 kFlutterSemanticsActionCopy,
nullptr);
538 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
539 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
544 kFlutterSemanticsActionCut,
nullptr);
550 g_return_if_fail(FL_IS_ACCESSIBLE_TEXT_FIELD(editable_text));
551 FlAccessibleTextField*
self = FL_ACCESSIBLE_TEXT_FIELD(editable_text);
556 kFlutterSemanticsActionPaste,
nullptr);
560 FlAccessibleTextFieldClass* klass) {
562 FL_ACCESSIBLE_NODE_CLASS(klass)->set_value =
564 FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_selection =
566 FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_direction =
568 FL_ACCESSIBLE_NODE_CLASS(klass)->perform_action =
589 AtkEditableTextIface* iface) {
600 self->selection_base = -1;
601 self->selection_extent = -1;
603 self->buffer = gtk_entry_buffer_new(
"", 0);
605 g_signal_connect_object(
606 self->buffer,
"inserted-text",
607 G_CALLBACK(+[](FlAccessibleTextField*
self, guint position, gchar* chars,
609 g_signal_emit_by_name(
self,
"text-insert", position, n_chars, chars,
612 self, G_CONNECT_SWAPPED);
614 g_signal_connect_object(self->buffer,
"deleted-text",
615 G_CALLBACK(+[](FlAccessibleTextField*
self,
616 guint position, guint n_chars) {
617 g_autofree gchar* chars = atk_text_get_text(
618 ATK_TEXT(
self), position, position + n_chars);
619 g_signal_emit_by_name(
self,
"text-remove", position,
620 n_chars, chars,
nullptr);
622 self, G_CONNECT_SWAPPED);
628 return FL_ACCESSIBLE_NODE(g_object_new(fl_accessible_text_field_get_type(),
629 "engine", engine,
"view-id",
view_id,
630 "node-id",
id,
nullptr));
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)
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)
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
G_BEGIN_DECLS FlutterViewId view_id
FlutterTextDirection text_direction
FlAccessibleNode parent_instance