| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/shell/platform/linux/fl_accessible_node.h" | |
| #include "flutter/shell/platform/linux/fl_engine_private.h" | |
| // Maps Flutter semantics flags to ATK flags. | |
| static struct { | |
| AtkStateType state; | |
| FlutterSemanticsFlag flag; | |
| gboolean invert; | |
| } flag_mapping[] = { | |
| {ATK_STATE_SHOWING, kFlutterSemanticsFlagIsObscured, TRUE}, | |
| {ATK_STATE_VISIBLE, kFlutterSemanticsFlagIsHidden, TRUE}, | |
| {ATK_STATE_CHECKABLE, kFlutterSemanticsFlagHasCheckedState, FALSE}, | |
| {ATK_STATE_FOCUSABLE, kFlutterSemanticsFlagIsFocusable, FALSE}, | |
| {ATK_STATE_FOCUSED, kFlutterSemanticsFlagIsFocused, FALSE}, | |
| {ATK_STATE_CHECKED, | |
| static_cast<FlutterSemanticsFlag>(kFlutterSemanticsFlagIsChecked | | |
| kFlutterSemanticsFlagIsToggled), | |
| FALSE}, | |
| {ATK_STATE_SELECTED, kFlutterSemanticsFlagIsSelected, FALSE}, | |
| {ATK_STATE_ENABLED, kFlutterSemanticsFlagIsEnabled, FALSE}, | |
| {ATK_STATE_SENSITIVE, kFlutterSemanticsFlagIsEnabled, FALSE}, | |
| {ATK_STATE_READ_ONLY, kFlutterSemanticsFlagIsReadOnly, FALSE}, | |
| {ATK_STATE_EDITABLE, kFlutterSemanticsFlagIsTextField, FALSE}, | |
| {ATK_STATE_INVALID, static_cast<FlutterSemanticsFlag>(0), FALSE}, | |
| }; | |
| // Maps Flutter semantics actions to ATK actions. | |
| typedef struct { | |
| FlutterSemanticsAction action; | |
| const gchar* name; | |
| } ActionData; | |
| static ActionData action_mapping[] = { | |
| {kFlutterSemanticsActionTap, "Tap"}, | |
| {kFlutterSemanticsActionLongPress, "LongPress"}, | |
| {kFlutterSemanticsActionScrollLeft, "ScrollLeft"}, | |
| {kFlutterSemanticsActionScrollRight, "ScrollRight"}, | |
| {kFlutterSemanticsActionScrollUp, "ScrollUp"}, | |
| {kFlutterSemanticsActionScrollDown, "ScrollDown"}, | |
| {kFlutterSemanticsActionIncrease, "Increase"}, | |
| {kFlutterSemanticsActionDecrease, "Decrease"}, | |
| {kFlutterSemanticsActionShowOnScreen, "ShowOnScreen"}, | |
| {kFlutterSemanticsActionMoveCursorForwardByCharacter, | |
| "MoveCursorForwardByCharacter"}, | |
| {kFlutterSemanticsActionMoveCursorBackwardByCharacter, | |
| "MoveCursorBackwardByCharacter"}, | |
| {kFlutterSemanticsActionCopy, "Copy"}, | |
| {kFlutterSemanticsActionCut, "Cut"}, | |
| {kFlutterSemanticsActionPaste, "Paste"}, | |
| {kFlutterSemanticsActionDidGainAccessibilityFocus, | |
| "DidGainAccessibilityFocus"}, | |
| {kFlutterSemanticsActionDidLoseAccessibilityFocus, | |
| "DidLoseAccessibilityFocus"}, | |
| {kFlutterSemanticsActionCustomAction, "CustomAction"}, | |
| {kFlutterSemanticsActionDismiss, "Dismiss"}, | |
| {kFlutterSemanticsActionMoveCursorForwardByWord, "MoveCursorForwardByWord"}, | |
| {kFlutterSemanticsActionMoveCursorBackwardByWord, | |
| "MoveCursorBackwardByWord"}, | |
| {kFlutterSemanticsActionFocus, "Focus"}, | |
| {static_cast<FlutterSemanticsAction>(0), nullptr}}; | |
| struct FlAccessibleNodePrivate { | |
| AtkObject parent_instance; | |
| // Weak reference to the engine this node is created for. | |
| FlEngine* engine; | |
| // Weak reference to the parent node of this one or %NULL. | |
| AtkObject* parent; | |
| int32_t id; | |
| gchar* name; | |
| gint index; | |
| gint x, y, width, height; | |
| GPtrArray* actions; | |
| gsize actions_length; | |
| GPtrArray* children; | |
| FlutterSemanticsFlag flags; | |
| }; | |
| enum { kProp0, kPropEngine, kPropId, kPropLast }; | |
| #define FL_ACCESSIBLE_NODE_GET_PRIVATE(node) \ | |
| ((FlAccessibleNodePrivate*)fl_accessible_node_get_instance_private( \ | |
| FL_ACCESSIBLE_NODE(node))) | |
| static void fl_accessible_node_component_interface_init( | |
| AtkComponentIface* iface); | |
| static void fl_accessible_node_action_interface_init(AtkActionIface* iface); | |
| G_DEFINE_TYPE_WITH_CODE( | |
| FlAccessibleNode, | |
| fl_accessible_node, | |
| ATK_TYPE_OBJECT, | |
| G_ADD_PRIVATE(FlAccessibleNode) | |
| G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT, | |
| fl_accessible_node_component_interface_init) | |
| G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, | |
| fl_accessible_node_action_interface_init)) | |
| // Returns TRUE if [flag] has changed between [old_flags] and [flags]. | |
| static gboolean flag_is_changed(FlutterSemanticsFlag old_flags, | |
| FlutterSemanticsFlag flags, | |
| FlutterSemanticsFlag flag) { | |
| return (old_flags & flag) != (flags & flag); | |
| } | |
| // Returns TRUE if [flag] is set in [flags]. | |
| static gboolean has_flag(FlutterSemanticsFlag flags, | |
| FlutterSemanticsFlag flag) { | |
| return (flags & flag) != 0; | |
| } | |
| // Returns TRUE if [action] is set in [actions]. | |
| static gboolean has_action(FlutterSemanticsAction actions, | |
| FlutterSemanticsAction action) { | |
| return (actions & action) != 0; | |
| } | |
| // Gets the nth action. | |
| static ActionData* get_action(FlAccessibleNodePrivate* priv, gint index) { | |
| if (index < 0 || static_cast<guint>(index) >= priv->actions->len) { | |
| return nullptr; | |
| } | |
| return static_cast<ActionData*>(g_ptr_array_index(priv->actions, index)); | |
| } | |
| // Checks if [object] is in [children]. | |
| static gboolean has_child(GPtrArray* children, AtkObject* object) { | |
| for (guint i = 0; i < children->len; i++) { | |
| if (g_ptr_array_index(children, i) == object) { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| static void fl_accessible_node_set_property(GObject* object, | |
| guint prop_id, | |
| const GValue* value, | |
| GParamSpec* pspec) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(object); | |
| switch (prop_id) { | |
| case kPropEngine: | |
| g_assert(priv->engine == nullptr); | |
| priv->engine = FL_ENGINE(g_value_get_object(value)); | |
| g_object_add_weak_pointer(object, | |
| reinterpret_cast<gpointer*>(&priv->engine)); | |
| break; | |
| case kPropId: | |
| priv->id = g_value_get_int(value); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); | |
| break; | |
| } | |
| } | |
| static void fl_accessible_node_dispose(GObject* object) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(object); | |
| if (priv->engine != nullptr) { | |
| g_object_remove_weak_pointer(object, | |
| reinterpret_cast<gpointer*>(&(priv->engine))); | |
| priv->engine = nullptr; | |
| } | |
| if (priv->parent != nullptr) { | |
| g_object_remove_weak_pointer(object, | |
| reinterpret_cast<gpointer*>(&(priv->parent))); | |
| priv->parent = nullptr; | |
| } | |
| g_clear_pointer(&priv->name, g_free); | |
| g_clear_pointer(&priv->actions, g_ptr_array_unref); | |
| g_clear_pointer(&priv->children, g_ptr_array_unref); | |
| G_OBJECT_CLASS(fl_accessible_node_parent_class)->dispose(object); | |
| } | |
| // Implements AtkObject::get_name. | |
| static const gchar* fl_accessible_node_get_name(AtkObject* accessible) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| return priv->name; | |
| } | |
| // Implements AtkObject::get_parent. | |
| static AtkObject* fl_accessible_node_get_parent(AtkObject* accessible) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| return priv->parent; | |
| } | |
| // Implements AtkObject::get_index_in_parent. | |
| static gint fl_accessible_node_get_index_in_parent(AtkObject* accessible) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| return priv->index; | |
| } | |
| // Implements AtkObject::get_n_children. | |
| static gint fl_accessible_node_get_n_children(AtkObject* accessible) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| return priv->children->len; | |
| } | |
| // Implements AtkObject::ref_child. | |
| static AtkObject* fl_accessible_node_ref_child(AtkObject* accessible, gint i) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| if (i < 0 || static_cast<guint>(i) >= priv->children->len) { | |
| return nullptr; | |
| } | |
| return ATK_OBJECT(g_object_ref(g_ptr_array_index(priv->children, i))); | |
| } | |
| // Implements AtkObject::get_role. | |
| static AtkRole fl_accessible_node_get_role(AtkObject* accessible) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| if ((priv->flags & kFlutterSemanticsFlagIsButton) != 0) { | |
| return ATK_ROLE_PUSH_BUTTON; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsInMutuallyExclusiveGroup) != 0 && | |
| (priv->flags & kFlutterSemanticsFlagHasCheckedState) != 0) { | |
| return ATK_ROLE_RADIO_BUTTON; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagHasCheckedState) != 0) { | |
| return ATK_ROLE_CHECK_BOX; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagHasToggledState) != 0) { | |
| return ATK_ROLE_TOGGLE_BUTTON; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsSlider) != 0) { | |
| return ATK_ROLE_SLIDER; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsTextField) != 0 && | |
| (priv->flags & kFlutterSemanticsFlagIsObscured) != 0) { | |
| return ATK_ROLE_PASSWORD_TEXT; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsTextField) != 0) { | |
| return ATK_ROLE_TEXT; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsHeader) != 0) { | |
| return ATK_ROLE_HEADER; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsLink) != 0) { | |
| return ATK_ROLE_LINK; | |
| } | |
| if ((priv->flags & kFlutterSemanticsFlagIsImage) != 0) { | |
| return ATK_ROLE_IMAGE; | |
| } | |
| return ATK_ROLE_PANEL; | |
| } | |
| // Implements AtkObject::ref_state_set. | |
| static AtkStateSet* fl_accessible_node_ref_state_set(AtkObject* accessible) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(accessible); | |
| AtkStateSet* state_set = atk_state_set_new(); | |
| for (int i = 0; flag_mapping[i].state != ATK_STATE_INVALID; i++) { | |
| gboolean enabled = has_flag(priv->flags, flag_mapping[i].flag); | |
| if (flag_mapping[i].invert) { | |
| enabled = !enabled; | |
| } | |
| if (enabled) { | |
| atk_state_set_add_state(state_set, flag_mapping[i].state); | |
| } | |
| } | |
| return state_set; | |
| } | |
| // Implements AtkComponent::get_extents. | |
| static void fl_accessible_node_get_extents(AtkComponent* component, | |
| gint* x, | |
| gint* y, | |
| gint* width, | |
| gint* height, | |
| AtkCoordType coord_type) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(component); | |
| *x = 0; | |
| *y = 0; | |
| if (priv->parent != nullptr) { | |
| atk_component_get_extents(ATK_COMPONENT(priv->parent), x, y, nullptr, | |
| nullptr, coord_type); | |
| } | |
| *x += priv->x; | |
| *y += priv->y; | |
| *width = priv->width; | |
| *height = priv->height; | |
| } | |
| // Implements AtkComponent::get_layer. | |
| static AtkLayer fl_accessible_node_get_layer(AtkComponent* component) { | |
| return ATK_LAYER_WIDGET; | |
| } | |
| // Implements AtkAction::do_action. | |
| static gboolean fl_accessible_node_do_action(AtkAction* action, gint i) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(action); | |
| if (priv->engine == nullptr) { | |
| return FALSE; | |
| } | |
| ActionData* data = get_action(priv, i); | |
| if (data == nullptr) { | |
| return FALSE; | |
| } | |
| fl_accessible_node_perform_action(FL_ACCESSIBLE_NODE(action), data->action, | |
| nullptr); | |
| return TRUE; | |
| } | |
| // Implements AtkAction::get_n_actions. | |
| static gint fl_accessible_node_get_n_actions(AtkAction* action) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(action); | |
| return priv->actions->len; | |
| } | |
| // Implements AtkAction::get_name. | |
| static const gchar* fl_accessible_node_get_name(AtkAction* action, gint i) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(action); | |
| ActionData* data = get_action(priv, i); | |
| if (data == nullptr) { | |
| return nullptr; | |
| } | |
| return data->name; | |
| } | |
| // Implements FlAccessibleNode::set_name. | |
| static void fl_accessible_node_set_name_impl(FlAccessibleNode* self, | |
| const gchar* name) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| g_free(priv->name); | |
| priv->name = g_strdup(name); | |
| } | |
| // Implements FlAccessibleNode::set_extents. | |
| static void fl_accessible_node_set_extents_impl(FlAccessibleNode* self, | |
| gint x, | |
| gint y, | |
| gint width, | |
| gint height) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| priv->x = x; | |
| priv->y = y; | |
| priv->width = width; | |
| priv->height = height; | |
| } | |
| // Implements FlAccessibleNode::set_flags. | |
| static void fl_accessible_node_set_flags_impl(FlAccessibleNode* self, | |
| FlutterSemanticsFlag flags) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| FlutterSemanticsFlag old_flags = priv->flags; | |
| priv->flags = flags; | |
| for (int i = 0; flag_mapping[i].state != ATK_STATE_INVALID; i++) { | |
| if (flag_is_changed(old_flags, flags, flag_mapping[i].flag)) { | |
| gboolean enabled = has_flag(flags, flag_mapping[i].flag); | |
| if (flag_mapping[i].invert) { | |
| enabled = !enabled; | |
| } | |
| atk_object_notify_state_change(ATK_OBJECT(self), flag_mapping[i].state, | |
| enabled); | |
| } | |
| } | |
| } | |
| // Implements FlAccessibleNode::set_actions. | |
| static void fl_accessible_node_set_actions_impl( | |
| FlAccessibleNode* self, | |
| FlutterSemanticsAction actions) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| // NOTE(robert-ancell): It appears that AtkAction doesn't have a method of | |
| // notifying that actions have changed, and even if it did an ATK client | |
| // might access the old IDs before checking for new ones. Keep an eye | |
| // out for a case where Flutter changes the actions on an item and see | |
| // if we can resolve this in another way. | |
| g_ptr_array_remove_range(priv->actions, 0, priv->actions->len); | |
| for (int i = 0; action_mapping[i].name != nullptr; i++) { | |
| if (has_action(actions, action_mapping[i].action)) { | |
| g_ptr_array_add(priv->actions, &action_mapping[i]); | |
| } | |
| } | |
| } | |
| // Implements FlAccessibleNode::set_value. | |
| static void fl_accessible_node_set_value_impl(FlAccessibleNode* self, | |
| const gchar* value) {} | |
| // Implements FlAccessibleNode::set_text_selection. | |
| static void fl_accessible_node_set_text_selection_impl(FlAccessibleNode* self, | |
| gint base, | |
| gint extent) {} | |
| // Implements FlAccessibleNode::set_text_direction. | |
| static void fl_accessible_node_set_text_direction_impl( | |
| FlAccessibleNode* self, | |
| FlutterTextDirection direction) {} | |
| // Implements FlAccessibleNode::perform_action. | |
| static void fl_accessible_node_perform_action_impl( | |
| FlAccessibleNode* self, | |
| FlutterSemanticsAction action, | |
| GBytes* data) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| fl_engine_dispatch_semantics_action(priv->engine, priv->id, action, data); | |
| } | |
| static void fl_accessible_node_class_init(FlAccessibleNodeClass* klass) { | |
| G_OBJECT_CLASS(klass)->set_property = fl_accessible_node_set_property; | |
| G_OBJECT_CLASS(klass)->dispose = fl_accessible_node_dispose; | |
| ATK_OBJECT_CLASS(klass)->get_name = fl_accessible_node_get_name; | |
| ATK_OBJECT_CLASS(klass)->get_parent = fl_accessible_node_get_parent; | |
| ATK_OBJECT_CLASS(klass)->get_index_in_parent = | |
| fl_accessible_node_get_index_in_parent; | |
| ATK_OBJECT_CLASS(klass)->get_n_children = fl_accessible_node_get_n_children; | |
| ATK_OBJECT_CLASS(klass)->ref_child = fl_accessible_node_ref_child; | |
| ATK_OBJECT_CLASS(klass)->get_role = fl_accessible_node_get_role; | |
| ATK_OBJECT_CLASS(klass)->ref_state_set = fl_accessible_node_ref_state_set; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_name = fl_accessible_node_set_name_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_extents = | |
| fl_accessible_node_set_extents_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_flags = | |
| fl_accessible_node_set_flags_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_actions = | |
| fl_accessible_node_set_actions_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_value = | |
| fl_accessible_node_set_value_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_selection = | |
| fl_accessible_node_set_text_selection_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->set_text_direction = | |
| fl_accessible_node_set_text_direction_impl; | |
| FL_ACCESSIBLE_NODE_CLASS(klass)->perform_action = | |
| fl_accessible_node_perform_action_impl; | |
| g_object_class_install_property( | |
| G_OBJECT_CLASS(klass), kPropEngine, | |
| g_param_spec_object( | |
| "engine", "engine", "Flutter engine", fl_engine_get_type(), | |
| static_cast<GParamFlags>(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS))); | |
| g_object_class_install_property( | |
| G_OBJECT_CLASS(klass), kPropId, | |
| g_param_spec_int( | |
| "id", "id", "Accessibility node ID", 0, G_MAXINT, 0, | |
| static_cast<GParamFlags>(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS))); | |
| } | |
| static void fl_accessible_node_component_interface_init( | |
| AtkComponentIface* iface) { | |
| iface->get_extents = fl_accessible_node_get_extents; | |
| iface->get_layer = fl_accessible_node_get_layer; | |
| } | |
| static void fl_accessible_node_action_interface_init(AtkActionIface* iface) { | |
| iface->do_action = fl_accessible_node_do_action; | |
| iface->get_n_actions = fl_accessible_node_get_n_actions; | |
| iface->get_name = fl_accessible_node_get_name; | |
| } | |
| static void fl_accessible_node_init(FlAccessibleNode* self) { | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| priv->actions = g_ptr_array_new(); | |
| priv->children = g_ptr_array_new_with_free_func(g_object_unref); | |
| } | |
| FlAccessibleNode* fl_accessible_node_new(FlEngine* engine, int32_t id) { | |
| FlAccessibleNode* self = FL_ACCESSIBLE_NODE(g_object_new( | |
| fl_accessible_node_get_type(), "engine", engine, "id", id, nullptr)); | |
| return self; | |
| } | |
| void fl_accessible_node_set_parent(FlAccessibleNode* self, | |
| AtkObject* parent, | |
| gint index) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| priv->parent = parent; | |
| priv->index = index; | |
| g_object_add_weak_pointer(G_OBJECT(self), | |
| reinterpret_cast<gpointer*>(&(priv->parent))); | |
| } | |
| void fl_accessible_node_set_children(FlAccessibleNode* self, | |
| GPtrArray* children) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self); | |
| // Remove nodes that are no longer required. | |
| for (guint i = 0; i < priv->children->len;) { | |
| AtkObject* object = ATK_OBJECT(g_ptr_array_index(priv->children, i)); | |
| if (has_child(children, object)) { | |
| i++; | |
| } else { | |
| g_signal_emit_by_name(self, "children-changed::remove", i, object, | |
| nullptr); | |
| g_ptr_array_remove_index(priv->children, i); | |
| } | |
| } | |
| // Add new nodes. | |
| for (guint i = 0; i < children->len; i++) { | |
| AtkObject* object = ATK_OBJECT(g_ptr_array_index(children, i)); | |
| if (!has_child(priv->children, object)) { | |
| g_ptr_array_add(priv->children, g_object_ref(object)); | |
| g_signal_emit_by_name(self, "children-changed::add", i, object, nullptr); | |
| } | |
| } | |
| } | |
| void fl_accessible_node_set_name(FlAccessibleNode* self, const gchar* name) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_name(self, name); | |
| } | |
| void fl_accessible_node_set_extents(FlAccessibleNode* self, | |
| gint x, | |
| gint y, | |
| gint width, | |
| gint height) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_extents(self, x, y, width, | |
| height); | |
| } | |
| void fl_accessible_node_set_flags(FlAccessibleNode* self, | |
| FlutterSemanticsFlag flags) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_flags(self, flags); | |
| } | |
| void fl_accessible_node_set_actions(FlAccessibleNode* self, | |
| FlutterSemanticsAction actions) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_actions(self, actions); | |
| } | |
| void fl_accessible_node_set_value(FlAccessibleNode* self, const gchar* value) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_value(self, value); | |
| } | |
| void fl_accessible_node_set_text_selection(FlAccessibleNode* self, | |
| gint base, | |
| gint extent) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_text_selection(self, base, | |
| extent); | |
| } | |
| void fl_accessible_node_set_text_direction(FlAccessibleNode* self, | |
| FlutterTextDirection direction) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->set_text_direction(self, | |
| direction); | |
| } | |
| void fl_accessible_node_perform_action(FlAccessibleNode* self, | |
| FlutterSemanticsAction action, | |
| GBytes* data) { | |
| g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self)); | |
| return FL_ACCESSIBLE_NODE_GET_CLASS(self)->perform_action(self, action, data); | |
| } |