From 87cc19b94d3c36f41d15744914aade783eb0d77e Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Fri, 8 Dec 2017 10:43:49 +0100 Subject: [PATCH] efl_ui_focus_manager_calc: implement better relation calculation 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 | 86 ++++++++++++++++++++++++++ src/tests/elementary/elm_test_focus.c | 81 ++++++++++++++++++++++-- 2 files changed, 163 insertions(+), 4 deletions(-) diff --git a/src/lib/elementary/efl_ui_focus_manager_calc.c b/src/lib/elementary/efl_ui_focus_manager_calc.c index 3d5f415..8e0490c 100644 --- a/src/lib/elementary/efl_ui_focus_manager_calc.c +++ b/src/lib/elementary/efl_ui_focus_manager_calc.c @@ -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 diff --git a/src/tests/elementary/elm_test_focus.c b/src/tests/elementary/elm_test_focus.c index e5c45c6..ce0b06a 100644 --- a/src/tests/elementary/elm_test_focus.c +++ b/src/tests/elementary/elm_test_focus.c @@ -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); -- 2.7.4