From 0c8a75442a4fc8222312aa4a685387b626a52ebd Mon Sep 17 00:00:00 2001 From: Lukasz Stanislawski Date: Sun, 4 Oct 2015 22:30:31 +0200 Subject: [PATCH] atspi: add new relationship append/remove/clear API Allow to add some extra contextul information about accessibility objects which can be used by Assistive Technology to provide better user experience. API is still marked as beta. @feature --- legacy/elementary/src/lib/elm_atspi_bridge.c | 12 +- .../src/lib/elm_interface_atspi_accessible.c | 180 +++++++++++++++++- .../src/lib/elm_interface_atspi_accessible.eo | 38 +++- .../src/lib/elm_interface_atspi_accessible.h | 39 +++- legacy/elementary/src/lib/elm_widget.c | 54 ++---- legacy/elementary/src/lib/elm_widget.eo | 1 - 6 files changed, 270 insertions(+), 54 deletions(-) diff --git a/legacy/elementary/src/lib/elm_atspi_bridge.c b/legacy/elementary/src/lib/elm_atspi_bridge.c index 9341153508..b4056553aa 100644 --- a/legacy/elementary/src/lib/elm_atspi_bridge.c +++ b/legacy/elementary/src/lib/elm_atspi_bridge.c @@ -786,11 +786,12 @@ _accessible_get_relation_set(const Eldbus_Service_Interface *iface EINA_UNUSED, { const char *obj_path = eldbus_message_path_get(msg); Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME); - Eo *obj = _bridge_object_from_path(bridge, obj_path); + Eo *rel_obj, *obj = _bridge_object_from_path(bridge, obj_path); Eldbus_Message *ret = NULL; Eldbus_Message_Iter *iter = NULL, *iter_array = NULL, *iter_array2 = NULL, *iter_struct; Elm_Atspi_Relation *rel; - Eina_List *rels; + Eina_List *l, *l2; + Elm_Atspi_Relation_Set rels; ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN, msg); @@ -803,17 +804,18 @@ _accessible_get_relation_set(const Eldbus_Service_Interface *iface EINA_UNUSED, eo_do(obj, rels = elm_interface_atspi_accessible_relation_set_get()); - EINA_LIST_FREE(rels, rel) + EINA_LIST_FOREACH(rels, l, rel) { iter_struct = eldbus_message_iter_container_new(iter_array, 'r', NULL); eldbus_message_iter_basic_append(iter_struct, 'u', _elm_relation_to_atspi_relation(rel->type)); iter_array2 = eldbus_message_iter_container_new(iter_struct, 'a', "(so)"); EINA_SAFETY_ON_NULL_GOTO(iter_array2, fail); - _bridge_iter_object_reference_append(bridge, iter_array2, rel->obj); + EINA_LIST_FOREACH(rel->objects, l2, rel_obj) + _bridge_iter_object_reference_append(bridge, iter_array2, rel_obj); eldbus_message_iter_container_close(iter_struct, iter_array2); eldbus_message_iter_container_close(iter_array, iter_struct); - free(rel); } + elm_atspi_relation_set_free(rels); eldbus_message_iter_container_close(iter, iter_array); return ret; diff --git a/legacy/elementary/src/lib/elm_interface_atspi_accessible.c b/legacy/elementary/src/lib/elm_interface_atspi_accessible.c index 709d142ff0..0c0a213fcd 100644 --- a/legacy/elementary/src/lib/elm_interface_atspi_accessible.c +++ b/legacy/elementary/src/lib/elm_interface_atspi_accessible.c @@ -127,6 +127,7 @@ struct _Elm_Interface_Atspi_Accessible_Data const char *name; const char *description; const char *translation_domain; + Elm_Atspi_Relation_Set relations; Elm_Interface_Atspi_Accessible *parent; }; @@ -290,12 +291,10 @@ _elm_interface_atspi_accessible_state_set_get(Eo *obj EINA_UNUSED, Elm_Interface return 0; } -EOLIAN Eina_List* +EOLIAN Elm_Atspi_Relation_Set _elm_interface_atspi_accessible_relation_set_get(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *pd EINA_UNUSED) { - WRN("The %s object does not implement the \"accessible_relation_set\" function.", - eo_class_name_get(eo_class_get(obj))); - return NULL; + return elm_atspi_relation_set_clone(pd->relations); } EAPI void elm_atspi_attributes_list_free(Eina_List *list) @@ -369,4 +368,177 @@ _elm_interface_atspi_accessible_translation_domain_get(Eo *obj EINA_UNUSED, Elm_ return pd->translation_domain; } +EAPI void +elm_atspi_relation_free(Elm_Atspi_Relation *relation) +{ + eina_list_free(relation->objects); + free(relation); +} + +EAPI Elm_Atspi_Relation * +elm_atspi_relation_clone(const Elm_Atspi_Relation *relation) +{ + Elm_Atspi_Relation *ret = calloc(sizeof(Elm_Atspi_Relation), 1); + if (!ret) return NULL; + + ret->type = relation->type; + ret->objects = eina_list_clone(relation->objects); + return ret; +} + +static Eina_Bool +_on_rel_obj_del(void *data, Eo *obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Elm_Atspi_Relation_Set *set = data; + Elm_Atspi_Relation *rel; + Eina_List *l, *l2, *p, *p2; + Eo *rel_obj; + + EINA_LIST_FOREACH_SAFE(*set, l, l2, rel) + { + EINA_LIST_FOREACH_SAFE(rel->objects, p, p2, rel_obj) + { + if (rel_obj == obj) + rel->objects = eina_list_remove_list(rel->objects, p); + } + if (!rel->objects) + { + *set = eina_list_remove_list(*set, l); + free(rel); + } + } + return EINA_TRUE; +} + +EAPI Eina_Bool +elm_atspi_relation_set_relation_append(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj) +{ + Elm_Atspi_Relation *rel; + Eina_List *l; + + if (!eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN)) + return EINA_FALSE; + + EINA_LIST_FOREACH(*set, l, rel) + { + if (rel->type == type) + { + if (!eina_list_data_find(rel->objects, rel_obj)) + { + rel->objects = eina_list_append(rel->objects, rel_obj); + eo_do(rel_obj, eo_event_callback_add(EO_BASE_EVENT_DEL, _on_rel_obj_del, set)); + } + return EINA_TRUE; + } + } + + rel = calloc(sizeof(Elm_Atspi_Relation), 1); + if (!rel) return EINA_FALSE; + + rel->type = type; + rel->objects = eina_list_append(rel->objects, rel_obj); + *set = eina_list_append(*set, rel); + + eo_do(rel_obj, eo_event_callback_add(EO_BASE_EVENT_DEL, _on_rel_obj_del, set)); + return EINA_TRUE; +} + +EAPI void +elm_atspi_relation_set_relation_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj) +{ + Eina_List *l; + Elm_Atspi_Relation *rel; + + EINA_LIST_FOREACH(*set, l, rel) + { + if (rel->type == type) + { + if (eina_list_data_find(rel->objects, rel_obj)) + { + eo_do(rel_obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set)); + rel->objects = eina_list_remove(rel->objects, rel_obj); + } + if (!rel->objects) + { + *set = eina_list_remove(*set, rel); + elm_atspi_relation_free(rel); + } + return; + } + } +} + +EAPI void +elm_atspi_relation_set_relation_type_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type) +{ + Eina_List *l; + Elm_Atspi_Relation *rel; + Eo *obj; + + EINA_LIST_FOREACH(*set, l, rel) + { + if (rel->type == type) + { + EINA_LIST_FOREACH(rel->objects, l, obj) + eo_do(obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set)); + *set = eina_list_remove(*set, rel); + elm_atspi_relation_free(rel); + return; + } + } +} + +EAPI void +elm_atspi_relation_set_free(Elm_Atspi_Relation_Set set) +{ + Elm_Atspi_Relation *rel; + Eina_List *l; + Eo *obj; + + EINA_LIST_FREE(set, rel) + { + EINA_LIST_FOREACH(rel->objects, l, obj) + eo_do(obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set)); + elm_atspi_relation_free(rel); + } +} + +EAPI Elm_Atspi_Relation_Set +elm_atspi_relation_set_clone(const Elm_Atspi_Relation_Set set) +{ + Elm_Atspi_Relation_Set ret = NULL; + Eina_List *l; + Elm_Atspi_Relation *rel; + + EINA_LIST_FOREACH(set, l, rel) + { + Elm_Atspi_Relation *cpy = elm_atspi_relation_clone(rel); + ret = eina_list_append(ret, cpy); + } + + return ret; +} + +EOLIAN static Eina_Bool +_elm_interface_atspi_accessible_relationship_append(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd, Elm_Atspi_Relation_Type type, const Elm_Interface_Atspi_Accessible *relation_obj) +{ + return elm_atspi_relation_set_relation_append(&sd->relations, type, relation_obj); +} + +EOLIAN static void +_elm_interface_atspi_accessible_relationship_remove(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd, Elm_Atspi_Relation_Type type, const Elm_Interface_Atspi_Accessible *relation_obj) +{ + if (relation_obj) + elm_atspi_relation_set_relation_remove(&sd->relations, type, relation_obj); + else + elm_atspi_relation_set_relation_type_remove(&sd->relations, type); +} + +EOLIAN static void +_elm_interface_atspi_accessible_relationships_clear(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd) +{ + elm_atspi_relation_set_free(sd->relations); + sd->relations = NULL; +} + #include "elm_interface_atspi_accessible.eo.c" diff --git a/legacy/elementary/src/lib/elm_interface_atspi_accessible.eo b/legacy/elementary/src/lib/elm_interface_atspi_accessible.eo index f6a4ed7e2b..65b6b95c97 100644 --- a/legacy/elementary/src/lib/elm_interface_atspi_accessible.eo +++ b/legacy/elementary/src/lib/elm_interface_atspi_accessible.eo @@ -27,7 +27,7 @@ mixin Elm_Interface_Atspi_Accessible () [[Gets an string describing ATSPI widget role name. Lists and elements Should be free by a user.]] } values { - relations: own(list *); + relations: Elm_Atspi_Relation_Set; } } @property role @protected { @@ -147,6 +147,42 @@ mixin Elm_Interface_Atspi_Accessible () domain: const(char)*; [[ translation domain ]] } } + relationship_append @protected { + [[Defines the relationship between two accessible objects. + + Adds unique relation between source object and relation_object of a + given type. + + Relationships can be queried by Assistive Technology clients to + provide customized feedback, improving overall user experience. + + Relationship_append API is asymmetric, which means that + appending, for example, relation ELM_ATSPI_RELATION_FLOWS_TO from object A to B, + do NOT append relation ELM_ATSPI_RELATION_FLOWS_FROM from object B to + object A. + + return: EINA_TRUE is relationship was successfully appended, EINA_FALSE + otherwise]] + return: bool; + params { + @in type: Elm_Atspi_Relation_Type; + @in relation_object: const(Elm_Interface_Atspi_Accessible)*; + } + } + relationship_remove @protected { + [[Removes the relationship between two accessible objects. + + If relation_object is NULL function removes all relations + of given type. + ]] + params { + @in type: Elm_Atspi_Relation_Type; + @in relation_object: const(Elm_Interface_Atspi_Accessible)*; + } + } + relationships_clear @protected { + [[Removes all relationships in accessible object.]] + } } events { property,changed: const(char)*; diff --git a/legacy/elementary/src/lib/elm_interface_atspi_accessible.h b/legacy/elementary/src/lib/elm_interface_atspi_accessible.h index 0012caa41e..8ebe244a10 100644 --- a/legacy/elementary/src/lib/elm_interface_atspi_accessible.h +++ b/legacy/elementary/src/lib/elm_interface_atspi_accessible.h @@ -252,7 +252,7 @@ typedef struct _Elm_Atspi_Attribute Elm_Atspi_Attribute; struct _Elm_Atspi_Relation { Elm_Atspi_Relation_Type type; - const Eo *obj; + Eina_List *objects; }; typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation; @@ -262,6 +262,43 @@ typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation; */ EAPI void elm_atspi_attributes_list_free(Eina_List *list); +typedef Eina_List *Elm_Atspi_Relation_Set; + +/** + * Frees relation. + */ +EAPI void elm_atspi_relation_free(Elm_Atspi_Relation *relation); + +/** + * Clones relation. + */ +EAPI Elm_Atspi_Relation * elm_atspi_relation_clone(const Elm_Atspi_Relation *relation); + +/** + * Appends relation to relation set + */ +EAPI Eina_Bool elm_atspi_relation_set_relation_append(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj); + +/** + * Removes relation from relation set + */ +EAPI void elm_atspi_relation_set_relation_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj); + +/** + * Removes all relation from relation set of a given type + */ +EAPI void elm_atspi_relation_set_relation_type_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type); + +/** + * Frees Elm_Atspi_Relation_Set + */ +EAPI void elm_atspi_relation_set_free(Elm_Atspi_Relation_Set set); + +/** + * Clones Elm_Atspi_Relation_Set + */ +EAPI Elm_Atspi_Relation_Set elm_atspi_relation_set_clone(const Elm_Atspi_Relation_Set set); + #ifdef EFL_EO_API_SUPPORT /** diff --git a/legacy/elementary/src/lib/elm_widget.c b/legacy/elementary/src/lib/elm_widget.c index 73d024a532..4a9022f78b 100644 --- a/legacy/elementary/src/lib/elm_widget.c +++ b/legacy/elementary/src/lib/elm_widget.c @@ -4431,9 +4431,12 @@ _elm_widget_item_eo_base_destructor(Eo *eo_item, Elm_Widget_Item_Data *item) } eina_hash_free(item->labels); - eo_do(eo_item, elm_interface_atspi_accessible_description_set(NULL)); - eo_do(eo_item, elm_interface_atspi_accessible_name_set(NULL)); - eo_do(eo_item, elm_interface_atspi_accessible_translation_domain_set(NULL)); + eo_do(eo_item, + elm_interface_atspi_accessible_description_set(NULL), + elm_interface_atspi_accessible_name_set(NULL), + elm_interface_atspi_accessible_translation_domain_set(NULL), + elm_interface_atspi_accessible_relationships_clear() + ); if (_elm_config->atspi_mode && item->widget) elm_interface_atspi_accessible_children_changed_del_signal_emit(item->widget, eo_item); @@ -5706,9 +5709,12 @@ _elm_widget_eo_base_constructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) EOLIAN static void _elm_widget_eo_base_destructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) { - eo_do(obj, elm_interface_atspi_accessible_description_set(NULL)); - eo_do(obj, elm_interface_atspi_accessible_name_set(NULL)); - eo_do(obj, elm_interface_atspi_accessible_translation_domain_set(NULL)); + eo_do(obj, + elm_interface_atspi_accessible_description_set(NULL), + elm_interface_atspi_accessible_name_set(NULL), + elm_interface_atspi_accessible_translation_domain_set(NULL), + elm_interface_atspi_accessible_relationships_clear() + ); elm_interface_atspi_accessible_removed(obj); eo_do_super(obj, ELM_WIDGET_CLASS, eo_destructor()); @@ -5881,42 +5887,6 @@ _elm_widget_elm_interface_atspi_accessible_attributes_get(Eo *obj, Elm_Widget_Sm return ret; } -static Elm_Atspi_Relation* -_relation_new(Elm_Atspi_Relation_Type type, Eo *obj) -{ - Elm_Atspi_Relation *rel = calloc(1, sizeof(Elm_Atspi_Relation)); - if (!rel) return NULL; - - rel->type = type; - rel->obj = obj; - - return rel; -} - -EOLIAN static Eina_List* -_elm_widget_elm_interface_atspi_accessible_relation_set_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED) -{ - Eina_List *list = NULL; - Elm_Atspi_Relation *rel; - Evas_Object *rel_obj; - - rel_obj = elm_object_focus_next_object_get(obj, ELM_FOCUS_NEXT); - if (eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN)) - { - rel = _relation_new(ELM_ATSPI_RELATION_FLOWS_TO, rel_obj); - list = eina_list_append(list, rel); - } - - rel_obj = elm_object_focus_next_object_get(obj, ELM_FOCUS_PREVIOUS); - if (eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN)) - { - rel = _relation_new(ELM_ATSPI_RELATION_FLOWS_FROM, rel_obj); - list = eina_list_append(list, rel); - } - - return list; -} - EOLIAN static void _elm_widget_item_elm_interface_atspi_component_extents_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords, int *x, int *y, int *w, int *h) { diff --git a/legacy/elementary/src/lib/elm_widget.eo b/legacy/elementary/src/lib/elm_widget.eo index f53d2c478a..35bed55f1d 100644 --- a/legacy/elementary/src/lib/elm_widget.eo +++ b/legacy/elementary/src/lib/elm_widget.eo @@ -771,7 +771,6 @@ abstract Elm.Widget (Evas.Object_Smart, Elm_Interface_Atspi_Accessible, Elm_Inte Elm_Interface_Atspi_Accessible.children.get; Elm_Interface_Atspi_Accessible.parent.get; Elm_Interface_Atspi_Accessible.attributes.get; - Elm_Interface_Atspi_Accessible.relation_set.get; Elm_Interface_Atspi_Component.focus_grab; } events {