diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index b4c9188fe0..b50c159b7c 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -844,6 +844,7 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/efl_page_transition_scroll.c \ lib/elementary/efl_page_indicator.c \ lib/elementary/efl_page_indicator_icon.c \ + lib/elementary/efl_ui_focus_graph.c \ $(NULL) diff --git a/src/lib/elementary/efl_ui_focus_graph.c b/src/lib/elementary/efl_ui_focus_graph.c new file mode 100644 index 0000000000..b7d365e98a --- /dev/null +++ b/src/lib/elementary/efl_ui_focus_graph.c @@ -0,0 +1,91 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif +#define EFL_UI_FOCUS_OBJECT_PROTECTED + + +#include +#include "elm_priv.h" +#include "efl_ui_focus_graph.h" + +static inline Efl_Ui_Focus_Object* +_convert(Efl_Ui_Focus_Graph_Context *ctx, Opaque_Graph_Member *member) +{ + return *((Efl_Ui_Focus_Object**)(((char*) member) + ctx->offset_focusable)); +} + + +static inline Eina_Position2D +_middle(Eina_Rect rect) +{ + return EINA_POSITION2D(rect.x + rect.w/2, rect.y + rect.h/2); +} + +static inline Eina_Position2D +_minus(Eina_Position2D r1, Eina_Position2D r2) +{ + return EINA_POSITION2D(r2.x - r1.x, r2.y - r1.y); +} + +static inline unsigned int +_order(Eina_Position2D pos) +{ + return pow(pos.x, 2) + pow(pos.y, 2); +} + +static inline Efl_Ui_Focus_Graph_Calc_Direction_Result* +_quadrant_get(Eina_Position2D pos, Efl_Ui_Focus_Graph_Calc_Result *result) +{ + if (pos.y > abs(pos.x)) return &result->top; + else if (pos.x >= abs(pos.y)) return &result->left; + else if (pos.y < -abs(pos.x)) return &result->bottom; + else if (pos.y >= -abs(pos.x)) return &result->right; + else return NULL; +} + +typedef struct { + unsigned int distance; +} Direction_Calc_Result; + +void +efl_ui_focus_graph_calc(Efl_Ui_Focus_Graph_Context *ctx, Eina_Iterator *nodes, Opaque_Graph_Member *origin_obj, Efl_Ui_Focus_Graph_Calc_Result *result) +{ + Opaque_Graph_Member *elem_obj; + Eina_Position2D origin_pos, elem_pos, relative_pos; + Eina_Rect origin, elem; + + memset(result, 0, sizeof(Efl_Ui_Focus_Graph_Calc_Result)); + + origin = efl_ui_focus_object_focus_geometry_get(_convert(ctx, origin_obj)); + origin_pos = _middle(origin); + + EINA_ITERATOR_FOREACH(nodes, elem_obj) + { + Efl_Ui_Focus_Graph_Calc_Direction_Result *res; + unsigned int distance; + + if (elem_obj == origin_obj) continue; + + elem = efl_ui_focus_object_focus_geometry_get(_convert(ctx, elem_obj)); + elem_pos = _middle(elem); + relative_pos = _minus(elem_pos, origin_pos); + distance = _order(relative_pos); + res = _quadrant_get(relative_pos, result); + EINA_SAFETY_ON_NULL_GOTO(res, cont); + + if (res->distance > distance || res->distance == 0) + { + res->relation = eina_list_free(res->relation); + res->relation = eina_list_append(res->relation, elem_obj); + res->distance = distance; + } + else if (res->distance == distance) + { + res->relation = eina_list_append(res->relation, elem_obj); + } + continue; +cont: + printf("%d - %d\n", relative_pos.x, relative_pos.y); + continue; + } +} diff --git a/src/lib/elementary/efl_ui_focus_graph.h b/src/lib/elementary/efl_ui_focus_graph.h new file mode 100644 index 0000000000..2910e66fbd --- /dev/null +++ b/src/lib/elementary/efl_ui_focus_graph.h @@ -0,0 +1,26 @@ +#ifndef EFL_UI_FOCUS_GRAPH_H +#define EFL_UI_FOCUS_GRAPH_H + +#include +#include +#include + +typedef struct _Opaque_Graph_Memeber Opaque_Graph_Member; + + +typedef struct { + Eina_List *relation; + unsigned int distance; +} Efl_Ui_Focus_Graph_Calc_Direction_Result; + +typedef struct { + Efl_Ui_Focus_Graph_Calc_Direction_Result right, left, top, bottom; +} Efl_Ui_Focus_Graph_Calc_Result; + +typedef struct { + size_t offset_focusable; //offset to the focusable +} Efl_Ui_Focus_Graph_Context; + +void efl_ui_focus_graph_calc(Efl_Ui_Focus_Graph_Context *context, Eina_Iterator *nodes, Opaque_Graph_Member *member, Efl_Ui_Focus_Graph_Calc_Result *result); + +#endif diff --git a/src/lib/elementary/efl_ui_focus_manager_calc.c b/src/lib/elementary/efl_ui_focus_manager_calc.c index c546512abf..6f6dd065a1 100644 --- a/src/lib/elementary/efl_ui_focus_manager_calc.c +++ b/src/lib/elementary/efl_ui_focus_manager_calc.c @@ -7,6 +7,8 @@ #include #include "elm_priv.h" +#include "efl_ui_focus_graph.h" + #define MY_CLASS EFL_UI_FOCUS_MANAGER_CALC_CLASS #define FOCUS_DATA(obj) Efl_Ui_Focus_Manager_Calc_Data *pd = efl_data_scope_get(obj, MY_CLASS); @@ -75,6 +77,7 @@ typedef struct { Efl_Ui_Focus_Manager *redirect; Efl_Ui_Focus_Object *redirect_entry; Eina_List *dirty; + Efl_Ui_Focus_Graph_Context graph_ctx; Node *root; } Efl_Ui_Focus_Manager_Calc_Data; @@ -319,230 +322,6 @@ _focus_stack_unfocus_last(Efl_Ui_Focus_Manager_Calc_Data *pd) return focusable; } -//CALCULATING STUFF - -static inline int -_distance(Eina_Rect node, Eina_Rect op, Dimension dim) -{ - int min, max, point; - int v1, v2; - - if (dim == DIMENSION_X) - { - min = op.x; - max = eina_rectangle_max_x(&op.rect); - point = node.x + node.w/2; - } - else - { - min = op.y; - max = eina_rectangle_max_y(&op.rect); - point = node.y + node.h/2; - } - - v1 = min - point; - v2 = max - point; - - if (abs(v1) < abs(v2)) - return v1; - else - return v2; -} - -static inline void -_min_max_gen(Dimension dim, Eina_Rect rect, int *min, int *max) -{ - if (dim == DIMENSION_X) - { - *min = rect.y; - *max = eina_rectangle_max_y(&rect.rect); - } - else - { - *min = rect.x; - *max = eina_rectangle_max_x(&rect.rect); - } -} - -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) -{ - int dim_min, dim_max, cur_pos_min = 0, cur_neg_min = 0; - Efl_Ui_Focus_Object *op; - Eina_Iterator *nodes; - Eina_Rect rect; - Node *n; - - *pos = NULL; - *neg = NULL; - - rect = efl_ui_focus_object_focus_geometry_get(node); - nodes = eina_hash_iterator_data_new(pd->node_hash); - _min_max_gen(dim, rect, &dim_min, &dim_max); - - EINA_ITERATOR_FOREACH(nodes, n) - { - Eina_Rect op_rect; - int min, max; - - 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); - - _min_max_gen(dim, op_rect, &min, &max); - - /* The only way the calculation does make sense is if the two number - * lines are not disconnected. - * If they are connected one point of the 4 lies between the min and max of the other line - */ - if (!((min <= max && max <= dim_min && dim_min <= dim_max) || - (dim_min <= dim_max && dim_max <= min && min <= max)) && - !eina_rectangle_intersection(&op_rect.rect, &rect.rect)) - { - //this thing hits horizontal - int tmp_dis; - - tmp_dis = _distance(rect, op_rect, dim); - - if (tmp_dis < 0) - { - if (tmp_dis == cur_neg_min) - { - //add it - *neg = eina_list_append(*neg, op); - } - else if (tmp_dis > cur_neg_min - || cur_neg_min == 0) //init case - { - //nuke the old and add -#ifdef CALC_DEBUG - printf("CORRECTION FOR %s-%s\n found anchor %s-%s in distance %d\n (%d,%d,%d,%d)\n (%d,%d,%d,%d)\n\n", DEBUG_TUPLE(node), DEBUG_TUPLE(op), - tmp_dis, - op_rect.x, op_rect.y, op_rect.w, op_rect.h, - rect.x, rect.y, rect.w, rect.h); -#endif - *neg = eina_list_free(*neg); - *neg = eina_list_append(NULL, op); - cur_neg_min = tmp_dis; - } - } - else - { - if (tmp_dis == cur_pos_min) - { - //add it - *pos = eina_list_append(*pos, op); - } - else if (tmp_dis < cur_pos_min - || cur_pos_min == 0) //init case - { - //nuke the old and add -#ifdef CALC_DEBUG - printf("CORRECTION FOR %s-%s\n found anchor %s-%s in distance %d\n (%d,%d,%d,%d)\n (%d,%d,%d,%d)\n\n", DEBUG_TUPLE(node), DEBUG_TUPLE(op), - tmp_dis, - op_rect.x, op_rect.y, op_rect.w, op_rect.h, - rect.x, rect.y, rect.w, rect.h); -#endif - *pos = eina_list_free(*pos); - *pos = eina_list_append(NULL, op); - cur_pos_min = tmp_dis; - } - } - - -#if 0 - printf("(%d,%d,%d,%d)%s vs(%d,%d,%d,%d)%s\n", rect.x, rect.y, rect.w, rect.h, elm_widget_part_text_get(node, NULL), op_rect.x, op_rect.y, op_rect.w, op_rect.h, elm_widget_part_text_get(op, NULL)); - printf("(%d,%d,%d,%d)\n", min, max, dim_min, dim_max); - printf("Candidate %d\n", tmp_dis); - if (anchor->anchor == NULL || abs(tmp_dis) < abs(distance)) //init case - { - distance = tmp_dis; - anchor->positive = tmp_dis > 0 ? EINA_FALSE : EINA_TRUE; - anchor->anchor = op; - //Helper for debugging wrong calculations - - } -#endif - } - - - } - eina_iterator_free(nodes); - nodes = NULL; - -} - -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_indirection(Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *node, Efl_Ui_Focus_Direction direction, Eina_List **lst) -{ - Efl_Ui_Focus_Object *op; - Eina_Iterator *nodes; - int min_distance = 0; - Node *n; - Eina_Rect rect; - - rect = efl_ui_focus_object_focus_geometry_get(node); - 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 = pow(pos.x, 2) + pow(pos.y, 2); - - 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); - } - } - } -} - #ifdef CALC_DEBUG static void _debug_node(Node *node) @@ -574,57 +353,47 @@ _debug_node(Node *node) } #endif -static void -convert_set(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *node, Eina_List *focusable_list, Efl_Ui_Focus_Direction dir, void (*converter)(Node *node, Efl_Ui_Focus_Direction direction, Eina_List *list)) +static Eina_Bool +_no_logical(const void *iter EINA_UNUSED, void *data, void *fdata EINA_UNUSED) { - Eina_List *partners = NULL; - Efl_Ui_Focus_Object *fobj; + Node *n = data; - EINA_LIST_FREE(focusable_list, fobj) - { - Node *entry; - - entry = node_get(obj, pd, fobj); - if (!entry) - { - CRI("Found a obj in graph without node-entry!"); - return; - } - partners = eina_list_append(partners, entry); - } - - converter(node, dir, partners); + return n->type != NODE_TYPE_ONLY_LOGICAL; } static void dirty_flush_node(Efl_Ui_Focus_Manager *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *node) { - Eina_List *x_partners_pos, *x_partners_neg; - Eina_List *y_partners_pos, *y_partners_neg; + Efl_Ui_Focus_Graph_Calc_Result result; - _calculate_node(pd, node->focusable, DIMENSION_X, &x_partners_pos, &x_partners_neg); - _calculate_node(pd, node->focusable, DIMENSION_Y, &y_partners_pos, &y_partners_neg); + efl_ui_focus_graph_calc(&pd->graph_ctx, eina_iterator_filter_new(eina_hash_iterator_data_new(pd->node_hash), _no_logical, NULL, NULL) , (Opaque_Graph_Member*) node, &result); - convert_set(obj, pd, node, x_partners_pos, EFL_UI_FOCUS_DIRECTION_RIGHT, border_partners_set); - convert_set(obj, pd, node, x_partners_neg, EFL_UI_FOCUS_DIRECTION_LEFT, border_partners_set); - convert_set(obj, pd, node, y_partners_neg, EFL_UI_FOCUS_DIRECTION_UP, border_partners_set); - convert_set(obj, pd, node, y_partners_pos, EFL_UI_FOCUS_DIRECTION_DOWN, border_partners_set); - - - /* - * Stage 2: if there is still no relation in a special direction, - * just take every single node that is in the given direction - * and take the one with the shortest direction - */ - for(int i = EFL_UI_FOCUS_DIRECTION_UP; i < EFL_UI_FOCUS_DIRECTION_LAST; i++) + for (int i = 0; i < 4; ++i) { - if (!DIRECTION_ACCESS(node, i).partners) - { - Eina_List *tmp = NULL; + Efl_Ui_Focus_Direction direction; + Efl_Ui_Focus_Graph_Calc_Direction_Result *res; - _calculate_node_indirection(pd, node->focusable, i, &tmp); - convert_set(obj, pd, node, tmp, i, border_onedirection_set); + if (i == 0) + { + direction = EFL_UI_FOCUS_DIRECTION_RIGHT; + res = &result.right; } + else if (i == 1) + { + direction = EFL_UI_FOCUS_DIRECTION_LEFT; + res = &result.left; + } + else if (i == 2) + { + direction = EFL_UI_FOCUS_DIRECTION_UP; + res = &result.top; + } + else if (i == 3) + { + direction = EFL_UI_FOCUS_DIRECTION_DOWN; + res = &result.bottom; + } + border_onedirection_set(node, direction, res->relation); } #ifdef CALC_DEBUG @@ -1091,6 +860,9 @@ _efl_ui_focus_manager_calc_efl_object_constructor(Eo *obj, Efl_Ui_Focus_Manager_ { obj = efl_constructor(efl_super(obj, MY_CLASS)); pd->node_hash = eina_hash_pointer_new(_free_node); + + pd->graph_ctx.offset_focusable = offsetof(Node, focusable); + return obj; }