atspi: consider scrollable parent for "GetNeighbor" 65/137765/4
authorShinwoo Kim <cinoo.kim@samsung.com>
Fri, 7 Jul 2017 02:44:14 +0000 (11:44 +0900)
committerGerrit Code Review <gerrit@review.ap-northeast-2.compute.internal>
Tue, 11 Jul 2017 11:05:18 +0000 (11:05 +0000)
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
src/lib/elm_widget.c

index 7b49d61..41cf475 100644 (file)
@@ -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)
 {
index 2944276..aef4d53 100644 (file)
@@ -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;