From b2ff65aecbbf3cd6e6f471a831151ff9b3f3302a Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Fri, 7 Jul 2017 11:44:14 +0900 Subject: [PATCH] atspi: consider scrollable parent for "GetNeighbor" To determine the next object, find top scrollable parent of next object excluding scrollable parents of start object, and check if the next object exists inside of the top scrollable parent viewport. If there is not top scrollable parent, then use the next object. (layout) | |----------|----------| | | | (btn1) (scroller) (btn3) | | |----------|----------| | | | (btn2) (genlist) (toolbar) [case: top scrollable parent is scroller] If current highlighted(start) object is btn1, and the 10th genlist item shows just under the btn1 (i.e. btn2, and 1st ~ 9th items are scrolled out), then the next object should be the 10th item. [case: top scrollable parent is NULL] After the 10th genlist item grabs highlight, the previous object should be the 9th genlist item, not the btn1. In this case scrollable paretns of start object are scroller, and genlist. the next object has same scrollable parents. So the top scrollable parent is NULL. If the 10th genlist item is the next object of btn1, then the btn1 object could be the previous object of 10th genlist. But this patch set make the 9th genlist item previous. This behavior is quite dependent on UX definition. Change-Id: I8ded2d533f0b77202be25713eafca652e1886bcb --- src/lib/elm_atspi_bridge.c | 259 ++++++++++++++++++++++++--------------------- src/lib/elm_widget.c | 20 +++- 2 files changed, 154 insertions(+), 125 deletions(-) diff --git a/src/lib/elm_atspi_bridge.c b/src/lib/elm_atspi_bridge.c index 7b49d61..41cf475 100644 --- a/src/lib/elm_atspi_bridge.c +++ b/src/lib/elm_atspi_bridge.c @@ -4134,12 +4134,6 @@ _component_get_accessible_at_point(const Eldbus_Service_Interface *iface EINA_UN } // TIZEN_ONLY(20170310) - implementation of get object under coordinates for accessibility -typedef struct { - void **objects; - unsigned int capacity; - unsigned int size; -} vector; - typedef enum { NEIGHBOR_SEARCH_MODE_NORMAL = 0, NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1, @@ -4157,55 +4151,8 @@ typedef struct accessibility_navigation_pointer_table { void *(*get_object_at_point)(struct accessibility_navigation_pointer_table *t, void *ptr, int x, int y, unsigned char coordinates_are_screen_based); unsigned char (*object_contains)(struct accessibility_navigation_pointer_table *t, void *ptr, int x, int y, unsigned char coordinates_are_screen_based); unsigned char (*object_is_proxy)(struct accessibility_navigation_pointer_table *t, void *ptr); - void (*get_children)(struct accessibility_navigation_pointer_table *t, void *ptr, vector *v); } accessibility_navigation_pointer_table; -static vector vector_init(void) -{ - vector v; - v.objects = NULL; - v.capacity = 0; - v.size = 0; - return v; -} - -static void vector_free(vector *v) -{ - free(v->objects); - v->objects = NULL; - v->capacity = 0; - v->size = 0; -} -static void vector_reserve(vector *v, unsigned int s) -{ - if (s > v->capacity) - { - v->objects = (void**)realloc(v->objects, sizeof(v->objects[0]) * s); - v->capacity = s; - } -} - -static void vector_resize(vector *v, unsigned int s) -{ - vector_reserve(v, s); - v->size = s; -} - -static void *vector_get(vector *v, unsigned int index) -{ - return v->objects[index]; -} - -static void vector_set(vector *v, unsigned int index, void *data) -{ - v->objects[index] = data; -} - -static unsigned int vector_size(vector *v) -{ - return v->size; -} - #define CALL(fncname, ...) table->fncname(table, __VA_ARGS__) static unsigned char _accept_object_check_role(accessibility_navigation_pointer_table *table, void *obj) { @@ -4365,70 +4312,169 @@ static void *_calculate_navigable_accessible_at_point_impl(accessibility_navigat return return_value; } - - - - - - static void *_find_non_defunct_child(accessibility_navigation_pointer_table *table, - vector *objects, unsigned int current_index, unsigned char forward) + Eina_List *children, unsigned int current_index, unsigned char forward) { - for(; current_index < vector_size(objects); forward ? ++current_index : --current_index) + unsigned int children_count = eina_list_count(children); + for(; current_index < children_count; forward ? ++current_index : --current_index) { - void *n = vector_get(objects, current_index); + void *n = eina_list_nth(children, current_index); if (n && !_object_is_defunct(table, n)) return n; } return NULL; } static void *_directional_depth_first_search_try_non_defunct_child(accessibility_navigation_pointer_table *table, - void *node, vector *children, unsigned char forward) + void *node, Eina_List *children, unsigned char forward) { - if (vector_size(children) > 0) + unsigned int children_count = eina_list_count(children); + if (children_count > 0) { unsigned char is_showing = _get_scrollable_parent(table, node) == NULL ? _object_is_showing(table, node) : 1; if (is_showing) { - return _find_non_defunct_child(table, children, forward ? 0 : vector_size(children) - 1, forward); + return _find_non_defunct_child(table, children, forward ? 0 : children_count - 1, forward); } } return NULL; } +static Eina_List *_scrollable_parent_list_get(Eo *obj) +{ + Eina_List *ret = NULL; + Eo *parent; + + if (obj) + { + eo_do(obj, parent = elm_interface_atspi_accessible_parent_get()); + while (parent) + { + if (eo_isa(parent, ELM_INTERFACE_SCROLLABLE_MIXIN)) + { + ret = eina_list_append(ret, parent); + } + eo_do(parent, parent = elm_interface_atspi_accessible_parent_get()); + } + } + + return ret; +} + +static void _viewport_geometry_get(Eo *obj, int *x, int *y, int *w, int *h) +{ + eo_do(obj, elm_interface_scrollable_content_viewport_geometry_get(x, y, w, h)); + /* widget implements scrollable interface but does not use scoller + in this case, use widget geometry */ + if (*w == 0 || *h == 0) + { + INF("%s is zero sized content viewport", eo_class_name_get(eo_class_get(obj))); + eo_do(obj, elm_interface_atspi_component_extents_get(EINA_FALSE, x, y, w, h)); + } +} + +static Eina_Bool +_new_scrollable_parent_viewport_geometry_get(Eo *node, Eo *start, + int *x, int *y, int *w, int *h) +{ + Eina_Bool ret = EINA_FALSE; + Eina_List *n_spl; + Eina_List *s_spl; + + n_spl = _scrollable_parent_list_get(node); + s_spl = _scrollable_parent_list_get(start); + + Eo *sp; + Eina_List *l; + EINA_LIST_FOREACH(s_spl, l, sp) + { + n_spl = eina_list_remove(n_spl, sp); + } + + Evas_Coord sx = 0, sy = 0, sw = 0, sh = 0; + + unsigned int count = eina_list_count(n_spl); + if (count > 0) + { + sp = eina_list_nth(n_spl, count - 1); + _viewport_geometry_get(sp, &sx, &sy, &sw, &sh); + ret = EINA_TRUE; + } + + *x = sx; + *y = sy; + *w = sw; + *h = sh; + + return ret; +} + +static Eina_List *_valid_children_get(Eina_List *children, Eo *start) +{ + Eo *child = NULL; + child = eina_list_nth(children, 0); + + if (child) + { + Evas_Coord x = 0, y = 0, w = 0, h = 0; + Evas_Coord sx = 0, sy = 0, sw = 0, sh = 0; + + if (_new_scrollable_parent_viewport_geometry_get(child, start, + &sx, &sy, &sw, &sh)) + { + Eina_List *l, *l_next; + EINA_LIST_FOREACH_SAFE(children, l, l_next, child) + { + eo_do(child, + elm_interface_atspi_component_extents_get(EINA_FALSE, + &x, &y, &w, &h)); + if (w == 0 || h == 0 || + !ELM_RECTS_INTERSECT(x, y, w, h, sx, sy, sw, sh)) + children = eina_list_remove_list(children, l); + } + } + } + return children; +} + static void *_get_next_non_defunct_sibling(accessibility_navigation_pointer_table *table, - void *obj, unsigned char forward) + void *obj, void *start, unsigned char forward) { if (!obj) return NULL; void *parent = CALL(get_parent, obj); if (!parent) return NULL; - vector children = vector_init(); - CALL(get_children, parent, &children); - if (vector_size(&children) == 0) + Eina_List *children; + eo_do(parent, children = elm_interface_atspi_accessible_children_get()); + children = _valid_children_get(children, start); + + unsigned int children_count = eina_list_count(children); + if (children_count == 0) { - vector_free(&children); - return NULL; + eina_list_free(children); + return NULL; } unsigned int current = 0; - for(; current < vector_size(&children) && vector_get(&children, current) != obj; ++current) ; - if (current >= vector_size(&children)) + for(; current < children_count && eina_list_nth(children, current) != obj; ++current) ; + if (current >= children_count) { - vector_free(&children); - return NULL; + eina_list_free(children); + return NULL; } forward ? ++current : --current; - void *ret = _find_non_defunct_child(table, &children, current, forward); - vector_free(&children); + void *ret = _find_non_defunct_child(table, children, current, forward); + eina_list_free(children); return ret; } -static void *_directional_depth_first_search_try_non_defunct_sibling(accessibility_navigation_pointer_table *table, - unsigned char *all_children_visited_ptr, void *node, void *root, unsigned char forward) +static void * +_directional_depth_first_search_try_non_defunct_sibling(accessibility_navigation_pointer_table *table, + unsigned char *all_children_visited_ptr, + void *node, void *start, void *root, + unsigned char forward) { while(1) { - void *sibling = _get_next_non_defunct_sibling(table, node, forward); + void *sibling = _get_next_non_defunct_sibling(table, node, start, forward); if (sibling != NULL) { node = sibling; @@ -4602,20 +4648,22 @@ static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *ta // always accept proxy object from different world if (!force_next && CALL(object_is_proxy, node)) return node; - vector children = vector_init(); - CALL(get_children, node, &children); + Eina_List *children; + eo_do(node, children = elm_interface_atspi_accessible_children_get()); + children = _valid_children_get(children, start); // do accept: // 1. not start node // 2. parent after all children in backward traversing // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element. // Objects with those roles shouldnt be reachable, when navigating next / prev. - unsigned char all_children_visited_or_moving_forward = (vector_size(&children) == 0 || forward || all_children_visited); - if (!force_next && node != start && all_children_visited_or_moving_forward && _accept_object(table, node)) + unsigned char all_children_visited_or_moving_forward = (eina_list_count(children) == 0 || forward || all_children_visited); + if (!force_next && node != start && all_children_visited_or_moving_forward && + _accept_object(table, node)) { if (start == NULL || _object_role_is_acceptable_when_navigating_next_prev(table, node)) { - vector_free(&children); + eina_list_free(children); return node; } } @@ -4669,17 +4717,17 @@ static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *ta } else { void *child = !force_next && !all_children_visited ? - _directional_depth_first_search_try_non_defunct_child(table, node, &children, forward) : NULL; + _directional_depth_first_search_try_non_defunct_child(table, node, children, forward) : NULL; if (child != NULL) want_cycle_detection = 1; else { if (!force_next && node == root) { - vector_free(&children); + eina_list_free(children); return NULL; } all_children_visited = 1; - child = _directional_depth_first_search_try_non_defunct_sibling(table, &all_children_visited, node, root, forward); + child = _directional_depth_first_search_try_non_defunct_sibling(table, &all_children_visited, node, start, root, forward); } node = child; } @@ -4687,17 +4735,14 @@ static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *ta force_next = 0; if (want_cycle_detection && cycle_detection_check_if_in_cycle(&cycle_detection, node)) { - vector_free(&children); + eina_list_free(children); return NULL; } - vector_free(&children); + eina_list_free(children); } return NULL; } - - - typedef struct accessibility_navigation_pointer_table_impl { accessibility_navigation_pointer_table ptrs; Eo *bridge; @@ -4783,22 +4828,6 @@ unsigned char _object_is_proxy_impl(struct accessibility_navigation_pointer_tabl return our_bus_name && obj_bus_name && strcmp(our_bus_name, obj_bus_name) != 0; } -void _get_children_impl(struct accessibility_navigation_pointer_table *table EINA_UNUSED, void *ptr, vector *v) -{ - Eina_List *l, *l2; - Eo *obj = (Eo*)ptr; - eo_do(obj, l = elm_interface_atspi_accessible_children_get()); - vector_resize(v, eina_list_count(l)); - - void *dt; - unsigned int index = 0; - EINA_LIST_FOREACH(l, l2, dt) - { - vector_set(v, index, dt); - ++index; - } -} - accessibility_navigation_pointer_table_impl construct_accessibility_navigation_pointer_table(Eo *bridge) { accessibility_navigation_pointer_table_impl table; @@ -4812,7 +4841,6 @@ accessibility_navigation_pointer_table_impl construct_accessibility_navigation_p INIT(get_object_at_point); INIT(object_contains); INIT(object_is_proxy); - INIT(get_children); #undef INIT table.bridge = bridge; return table; @@ -4830,20 +4858,11 @@ static Eo *_calculate_neighbor(Eo *bridge, Eo *root, Eo *start, Eina_Bool forwar { accessibility_navigation_pointer_table_impl table = construct_accessibility_navigation_pointer_table(bridge); Eo *result = (Eo*)_calculate_neighbor_impl(&table.ptrs, root, start, forward ? 1 : 0, (GetNeighborSearchMode)search_mode); + return result; } // - - - - - - - - - - static Eldbus_Message * _component_get_extents(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) { diff --git a/src/lib/elm_widget.c b/src/lib/elm_widget.c index 2944276..aef4d53 100644 --- a/src/lib/elm_widget.c +++ b/src/lib/elm_widget.c @@ -5053,7 +5053,7 @@ _accessible_object_on_scroll_is(Eo* obj) Evas_Object *target = obj; Evas_Object *parent = NULL; - Evas_Coord x, y, w, h, wx, wy, ww, wh, nx, ny; + Evas_Coord x, y, w, h, wx, wy, ww = 0, wh = 0, nx = 0, ny = 0; evas_object_geometry_get(target, &x, &y ,&w, &h); @@ -5069,6 +5069,15 @@ _accessible_object_on_scroll_is(Eo* obj) evas_object_geometry_get(parent, &wx, &wy, NULL, NULL); eo_do(parent, elm_interface_scrollable_content_size_get(&ww, &wh)); eo_do(parent, elm_interface_scrollable_content_pos_get(&nx, &ny)); + + /* widget implements scrollable interface but does not use scoller + in this case, use widget geometry */ + if (ww == 0 || wh == 0) + { + INF("%s is zero sized scrollable content", eo_class_name_get(eo_class_get(parent))); + evas_object_geometry_get(parent, NULL, NULL, &ww, &wh); + } + wx -= nx; wy -= ny; @@ -6443,7 +6452,7 @@ _accessible_scrollable_parent_list_get(Eo *obj) void _accessible_highlight_region_show(Eo* obj) { - if(!obj) return ; + if (!obj) return; Evas_Object *target = obj; Evas_Object *parent = NULL; @@ -6456,7 +6465,8 @@ _accessible_highlight_region_show(Eo* obj) evas_object_geometry_get(target, &target_x, &target_y, &target_w, &target_h); plist = _accessible_scrollable_parent_list_get(target); - if(!plist) return ; + if (!plist) return; + EINA_LIST_FOREACH(plist, l, parent) { if(!_accessible_object_on_screen_is(target, target_x, target_y, target_w, target_h, EINA_TRUE)) @@ -6465,8 +6475,8 @@ _accessible_highlight_region_show(Eo* obj) plist_sub = eina_list_prepend(plist_sub, parent); EINA_LIST_FOREACH(plist_sub, l2, parent_sub) { - Evas_Coord scroll_x, scroll_y; - Evas_Coord scroll_x_back, scroll_y_back; + Evas_Coord scroll_x = 0, scroll_y = 0; + Evas_Coord scroll_x_back = 0, scroll_y_back = 0; Evas_Coord x, y, w, h; Evas_Coord px, py; -- 2.7.4