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
This commit is contained in:
Marcel Hollerbach 2017-12-08 10:43:49 +01:00
parent 22c7fb86d9
commit 87cc19b94d
2 changed files with 163 additions and 4 deletions

View 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

View 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);