efl_ui_focus: move graph calculation to seperated file

This commit is contained in:
Marcel Hollerbach 2018-05-01 19:04:45 +02:00
parent 9669ca9ab9
commit 258b96be35
4 changed files with 153 additions and 263 deletions

View File

@ -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)

View File

@ -0,0 +1,91 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#define EFL_UI_FOCUS_OBJECT_PROTECTED
#include <Elementary.h>
#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;
}
}

View File

@ -0,0 +1,26 @@
#ifndef EFL_UI_FOCUS_GRAPH_H
#define EFL_UI_FOCUS_GRAPH_H
#include <stdio.h>
#include <stdlib.h>
#include <Eina.h>
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

View File

@ -7,6 +7,8 @@
#include <Elementary.h>
#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;
}