1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 #define EFL_UI_FOCUS_OBJECT_PROTECTED
5 
6 
7 #include <Elementary.h>
8 #include "elm_priv.h"
9 #include "efl_ui_focus_graph.h"
10 
11 typedef enum {
12   Q_TOP = 1, Q_RIGHT = 2, Q_LEFT = 4, Q_BOTTOM = 8, Q_LAST = 16
13 } Quadrant;
14 
15 Quadrant q_helper[] = {Q_TOP, Q_RIGHT, Q_LEFT, Q_BOTTOM};
16 
17 static inline Efl_Ui_Focus_Object*
_convert(Efl_Ui_Focus_Graph_Context * ctx,Opaque_Graph_Member * member)18 _convert(Efl_Ui_Focus_Graph_Context *ctx, Opaque_Graph_Member *member)
19 {
20    return *((Efl_Ui_Focus_Object**)(((char*) member) + ctx->offset_focusable));
21 }
22 
23 static inline unsigned int
_distance(Eina_Rect o,Eina_Rect r2,Quadrant q)24 _distance(Eina_Rect o, Eina_Rect r2, Quadrant q)
25 {
26    int res = INT_MAX;
27    if (q == Q_TOP)
28      res = o.y - eina_rectangle_max_y(&r2.rect);
29    else if (q == Q_LEFT)
30      res = o.x - eina_rectangle_max_x(&r2.rect);
31    else if (q == Q_BOTTOM)
32      res = r2.y - eina_rectangle_max_y(&o.rect);
33    else if (q == Q_RIGHT)
34      res = r2.x - eina_rectangle_max_x(&o.rect);
35 
36    return res;
37 }
38 
39 static inline Efl_Ui_Focus_Graph_Calc_Direction_Result*
_result_get(Quadrant q,Efl_Ui_Focus_Graph_Calc_Result * result)40 _result_get(Quadrant q, Efl_Ui_Focus_Graph_Calc_Result *result)
41 {
42   if (q == Q_TOP) return &result->top;
43   else if (q == Q_LEFT) return &result->left;
44   else if (q == Q_BOTTOM) return &result->bottom;
45   else if (q == Q_RIGHT) return &result->right;
46   else return NULL;
47 }
48 
49 /*
50 
51 _quadrant_get return in which quadrant the elem is placed, oriented at origin
52 
53 All this is based on three levels
54 
55 lvl1:
56        |     |
57        | Top |
58   _____|_____|_____
59        |     |
60   left |clean|right
61   _____|_____|_____
62        |     |
63        | Bot |
64        |     |
65 lvl3:
66   \               /
67     \    Top    /
68       \ _____ /
69        |     |
70   left |clean|right
71        |_____|
72       /       \
73     /   Bot     \
74   /               \
75 
76 lvl3:
77        |     |
78   L & T| Top | R & T
79   _____|_____|_____
80        |     |
81   left |clean|right
82   _____|_____|_____
83        |     |
84   L & B| Bot | R & B
85        |     |
86 
87 
88  */
89 
90 static inline void
_quadrant_get(Eina_Rect origin,Eina_Rect elem,Quadrant * lvl2,Quadrant * lvl1,Quadrant * lvl3)91 _quadrant_get(Eina_Rect origin, Eina_Rect elem, Quadrant *lvl2, Quadrant *lvl1, Quadrant *lvl3)
92 {
93   int dis = 0;
94 
95   *lvl1 = 0;
96   *lvl2 = 0;
97   *lvl3 = 0;
98 
99   if (eina_rectangle_max_y(&elem.rect) <= origin.y)
100     {
101        dis = origin.y - elem.y;
102 
103        *lvl3 |= Q_TOP;
104 
105        if (eina_spans_intersect(origin.x - dis, 2*dis + origin.w, elem.x, elem.w))
106          *lvl2 |= Q_TOP;
107        if (eina_spans_intersect(origin.x, origin.w, elem.x, elem.w))
108          *lvl1 |= Q_TOP;
109     }
110   if (elem.y >= eina_rectangle_max_y(&origin.rect))
111     {
112        dis = eina_rectangle_max_y(&elem.rect) - origin.y;
113 
114        *lvl3 |= Q_BOTTOM;
115 
116        if (eina_spans_intersect(origin.x - dis, 2*dis + origin.w, elem.x, elem.w))
117          *lvl2 |= Q_BOTTOM;
118        if (eina_spans_intersect(origin.x, origin.w, elem.x, elem.w))
119          *lvl1 |= Q_BOTTOM;
120     }
121   if (elem.x >= eina_rectangle_max_x(&origin.rect))
122     {
123        dis = eina_rectangle_max_x(&elem.rect) - origin.x;
124 
125        *lvl3 |= Q_RIGHT;
126 
127        if (eina_spans_intersect(origin.y - dis, 2*dis + origin.h, elem.y, elem.h))
128          *lvl2 |= Q_RIGHT;
129        if (eina_spans_intersect(origin.y, origin.h, elem.y, elem.h))
130          *lvl1 |= Q_RIGHT;
131     }
132   if (eina_rectangle_max_x(&elem.rect) <= origin.x)
133     {
134        dis = origin.x - elem.x;
135 
136        *lvl3 |= Q_LEFT;
137 
138        if (eina_spans_intersect(origin.y - dis, 2*dis + origin.h, elem.y, elem.h))
139          *lvl2 |= Q_LEFT;
140        if (eina_spans_intersect(origin.y, origin.h, elem.y, elem.h))
141          *lvl1 |= Q_LEFT;
142     }
143 
144 }
145 
146 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)147 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)
148 {
149    Opaque_Graph_Member *elem_obj;
150    Eina_Rect origin, elem;
151 
152    for (int i = 0; i < 4; ++i)
153      {
154         Efl_Ui_Focus_Graph_Calc_Direction_Result *res;
155 
156         res = _result_get(q_helper[i], result);
157 
158         res->distance = INT_MAX;
159         res->lvl = INT_MAX;
160         res->relation = NULL;
161      }
162 
163    origin = efl_ui_focus_object_focus_geometry_get(_convert(ctx, origin_obj));
164 
165    //printf("=========> CALCING %p %s\n", _convert(ctx, origin_obj), efl_class_name_get(_convert(ctx, origin_obj)));
166 
167    EINA_ITERATOR_FOREACH(nodes, elem_obj)
168      {
169         Efl_Ui_Focus_Graph_Calc_Direction_Result *res;
170         unsigned int distance;
171         Quadrant lvl3, lvl2, lvl1;
172 
173         if (elem_obj == origin_obj) continue;
174 
175         elem = efl_ui_focus_object_focus_geometry_get(_convert(ctx, elem_obj));
176 
177         if (eina_rectangle_intersection(&origin.rect, &elem.rect)) continue;
178 
179         _quadrant_get(origin, elem, &lvl2, &lvl1, &lvl3);
180 
181         for (int i = 0; i < 4; ++i)
182           {
183             if (q_helper[i] & lvl3)
184               {
185                  int lvl;
186                  res = _result_get(q_helper[i], result);
187                  EINA_SAFETY_ON_NULL_GOTO(res, cont);
188 
189                  distance = _distance(origin, elem, q_helper[i]);
190 
191                  if (q_helper[i] & lvl1)
192                    lvl = 1;
193                  else if (q_helper[i] & lvl2)
194                    lvl = 2;
195                  else //if (q_helper[i] & lvl3)
196                    lvl = 3;
197 
198                  if (lvl < res->lvl)
199                    {
200                       res->relation = eina_list_free(res->relation);
201                       res->relation = eina_list_append(res->relation, elem_obj);
202                       res->distance = distance;
203                       res->lvl = lvl;
204                       //printf("=========> %p:%d LVL_DROP     %d \t %d \t %p %s\n", res, i, distance, lvl, origin_obj, efl_class_name_get(_convert(ctx, elem_obj)));
205                    }
206                  else if (lvl == res->lvl && res->distance > distance)
207                    {
208                       res->relation = eina_list_free(res->relation);
209                       res->relation = eina_list_append(res->relation, elem_obj);
210                       res->distance = distance;
211                       //printf("=========> %p:%d DIST_DROP    %d \t %d \t %p %s\n", res, i, distance, lvl, origin_obj, efl_class_name_get(_convert(ctx, elem_obj)));
212 
213                    }
214                  else if (lvl == res->lvl && res->distance >= distance)
215                    {
216                       res->relation = eina_list_append(res->relation, elem_obj);
217                       //printf("=========> %p:%d DIST_ADD     %d \t %d \t %p %s\n", res, i, distance, lvl, origin_obj, efl_class_name_get(_convert(ctx, elem_obj)));
218                    }
219               }
220           }
221 
222         continue;
223 cont:
224         continue;
225      }
226    eina_iterator_free(nodes);
227 }
228