efl_ui_focus_manager_calc: implement better relation calculation
authorMarcel Hollerbach <marcel@osg.samsung.com>
Fri, 8 Dec 2017 09:43:49 +0000 (10:43 +0100)
committerMarcel Hollerbach <marcel@osg.samsung.com>
Mon, 11 Dec 2017 09:46:52 +0000 (10:46 +0100)
The new calculation mechanism does not only look into the exact
directions up,right,down,left of a node, it also now checks the sectors,
bound by: x < node.x, x > node.max_x, y < node.y, y > node.max_y.

ref T6453

src/lib/elementary/efl_ui_focus_manager_calc.c
src/tests/elementary/elm_test_focus.c

index 3d5f415..8e0490c 100644 (file)
@@ -410,10 +410,77 @@ _calculate_node_stage1(Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *
 
 }
 
+static inline Eina_Position2D
+_relative_position_rects(Eina_Rect *a, Eina_Rect *b)
+{
+   Eina_Position2D a_pos = {a->rect.x + a->rect.w/2, a->rect.y + a->rect.h/2};
+   Eina_Position2D b_pos = {b->rect.x + b->rect.w/2, b->rect.y + b->rect.h/2};
+
+   return (Eina_Position2D){b_pos.x - a_pos.x, b_pos.y - b_pos.y};
+}
+
+static inline Eina_Rectangle_Outside
+_direction_to_outside(Efl_Ui_Focus_Direction direction)
+{
+   if (direction == EFL_UI_FOCUS_DIRECTION_RIGHT) return EINA_RECTANGLE_OUTSIDE_RIGHT;
+   if (direction == EFL_UI_FOCUS_DIRECTION_LEFT) return EINA_RECTANGLE_OUTSIDE_LEFT;
+   if (direction == EFL_UI_FOCUS_DIRECTION_DOWN) return EINA_RECTANGLE_OUTSIDE_BOTTOM;
+   if (direction == EFL_UI_FOCUS_DIRECTION_UP) return EINA_RECTANGLE_OUTSIDE_TOP;
+
+   return -1;
+}
+
+static inline void
+_calculate_node_stage2(Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *node, Eina_Rect rect, Efl_Ui_Focus_Direction direction, Eina_List **lst)
+{
+   Efl_Ui_Focus_Object *op;
+   Eina_Iterator *nodes;
+   int min_distance = 0;
+   Node *n;
+
+   nodes = eina_hash_iterator_data_new(pd->node_hash);
+
+   EINA_ITERATOR_FOREACH(nodes, n)
+     {
+        Eina_Rectangle_Outside outside, outside_dir;
+        Eina_Position2D pos;
+        int distance;
+        Eina_Rect op_rect;
+
+        op = n->focusable;
+
+        if (op == node) continue;
+        if (n->type == NODE_TYPE_ONLY_LOGICAL) continue;
+
+        op_rect = efl_ui_focus_object_focus_geometry_get(op);
+        outside = eina_rectangle_outside_position(&rect.rect, &op_rect.rect);
+        outside_dir = _direction_to_outside(direction);
+        //calculate relative position of the nodes
+        pos = _relative_position_rects(&rect, &op_rect);
+        //calculate distance
+        distance = sqrt(powerof2(pos.x) + powerof2(pos.y));
+
+        if (outside & outside_dir)
+          {
+             if (min_distance == 0 || min_distance > distance)
+               {
+                  min_distance = distance;
+                  *lst = eina_list_free(*lst);
+                  *lst = eina_list_append(*lst, op);
+               }
+             else if (min_distance == distance)
+               {
+                  *lst = eina_list_append(*lst, op);
+               }
+          }
+     }
+}
+
 static inline void
 _calculate_node(Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *node, Dimension dim, Eina_List **pos, Eina_List **neg)
 {
    Eina_Rect rect;
+   Efl_Ui_Focus_Direction direction;
 
    rect = efl_ui_focus_object_focus_geometry_get(node);
 
@@ -421,6 +488,25 @@ _calculate_node(Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *node, D
    *neg = NULL;
 
    _calculate_node_stage1(pd, node, rect, dim, pos, neg);
+
+  if (!*pos)
+    {
+       if (dim == DIMENSION_Y)
+         direction = EFL_UI_FOCUS_DIRECTION_DOWN;
+       else
+         direction = EFL_UI_FOCUS_DIRECTION_RIGHT;
+       _calculate_node_stage2(pd, node, rect, direction, pos);
+    }
+
+  if (!*neg)
+    {
+       if (dim == DIMENSION_Y)
+         direction = EFL_UI_FOCUS_DIRECTION_UP;
+       else
+         direction = EFL_UI_FOCUS_DIRECTION_LEFT;
+
+     _calculate_node_stage2(pd, node, rect, direction, neg);
+     }
 }
 
 #ifdef CALC_DEBUG
index e5c45c6..ce0b06a 100644 (file)
@@ -73,10 +73,10 @@ START_TEST(pos_check)
    efl_ui_focus_manager_focus_set(m, obj);
 
    CHECK(middle, east, west, north, south)
-   CHECK(east, NULL, middle, NULL, NULL)
-   CHECK(west, middle, NULL, NULL, NULL)
-   CHECK(north, NULL, NULL, NULL, middle)
-   CHECK(south, NULL, NULL, middle, NULL)
+   CHECK(east, NULL, middle, north, south)
+   CHECK(west, middle, NULL, north, south)
+   CHECK(north, east, west, NULL, middle)
+   CHECK(south, east, west, middle, NULL)
 
    efl_del(middle);
    efl_del(south);
@@ -88,6 +88,78 @@ START_TEST(pos_check)
 }
 END_TEST
 
+static Eina_Bool
+_equal_set(Eina_List *elems, Efl_Ui_Focus_Object *lst[])
+{
+   unsigned int i = 0;
+
+   for (i = 0; lst[i]; ++i)
+     {
+        Eina_Bool found = EINA_FALSE;
+        Eina_List *n;
+        Efl_Ui_Focus_Object *elem;
+
+        EINA_LIST_FOREACH(elems, n, elem)
+          {
+            if (lst[i] != elem) continue;
+
+            found = EINA_TRUE;
+            break;
+          }
+
+        if (!found) return EINA_FALSE;
+     }
+
+   if (eina_list_count(elems) != i) return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+START_TEST(pos_check2)
+{
+   Efl_Ui_Focus_Manager *m;
+   Efl_Ui_Focus_Relations *rel;
+   Efl_Ui_Focus_Object *root, *middle, *north_east, *north_west, *south_east, *south_west;
+
+   elm_init(1, NULL);
+
+   middle = elm_focus_test_object_new("middle", 40, 40, 5, 5);
+
+   north_east = elm_focus_test_object_new("north_east", 60, 20, 5, 5);
+   north_west = elm_focus_test_object_new("north_west", 20, 20, 5, 5);
+   south_east = elm_focus_test_object_new("south_east", 60, 60, 5, 5);
+   south_west = elm_focus_test_object_new("south_west", 20, 60, 5, 5);
+
+   m = elm_focus_test_manager_new(&root);
+   efl_ui_focus_manager_calc_register(m, middle, root, NULL);
+   efl_ui_focus_manager_calc_register(m, north_east, root, NULL);
+   efl_ui_focus_manager_calc_register(m, north_west, root, NULL);
+   efl_ui_focus_manager_calc_register(m, south_east, root, NULL);
+   efl_ui_focus_manager_calc_register(m, south_west, root, NULL);
+
+   rel = efl_ui_focus_manager_fetch(m, middle);
+
+#define ck_assert_set_eq(set, ...) \
+   { \
+      Efl_Ui_Focus_Object *tmp[] = { __VA_ARGS__ }; \
+      ck_assert_int_eq(_equal_set(set, tmp), EINA_TRUE); \
+   }
+
+   ck_assert_set_eq(rel->left, north_west, south_west, NULL);
+   ck_assert_set_eq(rel->right, north_east, south_east, NULL);
+   ck_assert_set_eq(rel->top, north_west, north_east, NULL);
+   ck_assert_set_eq(rel->down, south_west, south_east, NULL);
+
+#undef ck_assert_set_eq
+
+   efl_del(middle);
+   efl_del(north_east);
+   efl_del(north_west);
+   efl_del(south_east);
+   efl_del(south_west);
+
+   elm_shutdown();
+}
+END_TEST
 START_TEST(redirect)
 {
    elm_init(1, NULL);
@@ -539,6 +611,7 @@ void elm_test_focus(TCase *tc)
     tcase_add_test(tc, focus_register_twice);
     tcase_add_test(tc, focus_unregister_twice);
     tcase_add_test(tc, pos_check);
+    tcase_add_test(tc, pos_check2);
     tcase_add_test(tc, redirect);
     tcase_add_test(tc, border_check);
     tcase_add_test(tc, finalize_check);