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
10 #include "efl_ui_focus_graph.h"
11
12 #define MY_CLASS EFL_UI_FOCUS_MANAGER_CALC_CLASS
13 #define FOCUS_DATA(obj) Efl_Ui_Focus_Manager_Calc_Data *pd = efl_data_scope_get(obj, MY_CLASS);
14
15 #define NODE_DIRECTIONS_COUNT 4
16
17 #define DIRECTION_IS_LOGICAL(dir) (dir >= EFL_UI_FOCUS_DIRECTION_PREVIOUS && dir < EFL_UI_FOCUS_DIRECTION_UP)
18 #define DIRECTION_IS_2D(dir) (dir >= EFL_UI_FOCUS_DIRECTION_UP && dir < EFL_UI_FOCUS_DIRECTION_LAST)
19 #define DIRECTION_CHECK(dir) (dir >= EFL_UI_FOCUS_DIRECTION_PREVIOUS && dir < EFL_UI_FOCUS_DIRECTION_LAST)
20
21 //#define CALC_DEBUG
22 #define DEBUG_TUPLE(obj) efl_name_get(obj), efl_class_name_get(obj)
23
24 static int _focus_log_domain = -1;
25
26 #define F_CRI(...) EINA_LOG_DOM_CRIT(_focus_log_domain, __VA_ARGS__)
27 #define F_ERR(...) EINA_LOG_DOM_ERR(_focus_log_domain, __VA_ARGS__)
28 #define F_WRN(...) EINA_LOG_DOM_WARN(_focus_log_domain, __VA_ARGS__)
29 #define F_INF(...) EINA_LOG_DOM_INFO(_focus_log_domain, __VA_ARGS__)
30 #define F_DBG(...) EINA_LOG_DOM_DBG(_focus_log_domain, __VA_ARGS__)
31
32 #define DIRECTION_ACCESS(V, ID) ((V)->graph.directions[(ID) - 2])
33
34 typedef enum {
35 DIMENSION_X = 0,
36 DIMENSION_Y = 1,
37 } Dimension;
38
39 typedef struct _Border Border;
40 typedef struct _Node Node;
41
42 struct _Border {
43 Eina_List *one_direction; //partners that are linked in one direction
44 Eina_List *cleanup_nodes; //a list of nodes that needs to be cleaned up when this node is deleted
45 };
46
47 typedef enum {
48 NODE_TYPE_NORMAL = 0,
49 NODE_TYPE_ONLY_LOGICAL = 2,
50 } Node_Type;
51
52 struct _Node{
53 Node_Type type; //type of the node
54
55 Efl_Ui_Focus_Object *focusable;
56 Efl_Ui_Focus_Manager *manager;
57 Efl_Ui_Focus_Manager *redirect_manager;
58
59 struct _Tree_Node{
60 Node *parent; //the parent of the tree
61 Eina_List *children; //this saves the original set of elements
62 Eina_List *saved_order;
63 Eina_Bool clean_apply; //set if there was no new registration after a "update_order" call
64 }tree;
65
66 struct _Graph_Node {
67 Border directions[NODE_DIRECTIONS_COUNT];
68 } graph;
69
70 Eina_Bool on_list : 1;
71 Eina_Bool unused : 1;
72 Eina_Bool this_is_root : 1;
73 };
74
75 #define T(n) (n->tree)
76 #define G(n) (n->graph)
77
78 typedef struct {
79 Eina_List *focus_stack;
80 Eina_Hash *node_hash;
81 Efl_Ui_Focus_Manager *redirect;
82 Efl_Ui_Focus_Object *redirect_entry;
83 Eina_List *dirty;
84 Efl_Ui_Focus_Graph_Context graph_ctx;
85 int freeze;
86
87 Node *root;
88 Eina_Bool border_elements_changed;
89 } Efl_Ui_Focus_Manager_Calc_Data;
90
91 static Eina_Mempool *_node_mempool;
92
93 static Node*
node_mem_get(void)94 node_mem_get(void)
95 {
96 return eina_mempool_calloc(_node_mempool, sizeof(Node));
97 }
98
99 static void
node_mem_free(Node * n)100 node_mem_free(Node *n)
101 {
102 eina_mempool_free(_node_mempool, n);
103 }
104
105 static Node* _request_subchild(Node *node);
106 static void dirty_add(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *dirty);
107 static Node* _next(Node *node);
108 static void _prepare_node(Node *root);
109
110 static Eina_Bool
_focus_manager_active_get(Eo * obj)111 _focus_manager_active_get(Eo *obj)
112 {
113 Eo *root, *manager, *comp_parent, *redirect;
114
115 root = efl_ui_focus_manager_root_get(obj);
116
117 if (efl_isa(root, EFL_UI_FOCUS_MANAGER_WINDOW_ROOT_INTERFACE))
118 return EINA_TRUE;
119
120 manager = efl_ui_focus_object_focus_manager_get(root);
121
122 if (!manager) return EINA_FALSE;
123
124 redirect = efl_ui_focus_manager_redirect_get(manager);
125
126 if (!redirect) return EINA_FALSE;
127
128 if (efl_composite_part_is(obj))
129 comp_parent = efl_parent_get(obj); //a focus manager can be attached to something via composition
130 else
131 comp_parent = NULL;
132
133 if (redirect == obj ||
134 (redirect == comp_parent))
135 return EINA_TRUE;
136
137 return EINA_FALSE;
138 }
139
140 static void
_manager_in_chain_set(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)141 _manager_in_chain_set(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
142 {
143 Eo *manager, *root;
144
145 root = efl_ui_focus_manager_root_get(obj);
146 manager = efl_ui_focus_object_focus_manager_get(pd->root->focusable);
147
148 EINA_SAFETY_ON_NULL_RETURN(root);
149
150 if (!efl_isa(root, EFL_UI_FOCUS_MANAGER_WINDOW_ROOT_INTERFACE))
151 EINA_SAFETY_ON_NULL_RETURN(manager);
152
153 //so we dont run infinitly this does not fix it, but at least we only have a error
154 EINA_SAFETY_ON_TRUE_RETURN(manager == obj);
155
156 if (manager)
157 efl_ui_focus_manager_focus_set(manager, root);
158 else
159 DBG("No focus manager for focusable %s@%p",
160 efl_class_name_get(pd->root->focusable), root);
161 }
162
163
164 static void
border_onedirection_set(Node * node,Efl_Ui_Focus_Direction direction,Eina_List * list)165 border_onedirection_set(Node *node, Efl_Ui_Focus_Direction direction, Eina_List *list)
166 {
167 Node *partner;
168 Eina_List *lnode;
169 Border *border;
170 Efl_Ui_Focus_Direction complement;
171
172 EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(direction));
173 complement = efl_ui_focus_util_direction_complement(direction);
174 //this is basically a nop. The complement of 2D will *always* be 2D
175 EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(complement));
176
177 border = &DIRECTION_ACCESS(node, direction);
178
179 EINA_LIST_FREE(border->one_direction, partner)
180 {
181 Border *b = &DIRECTION_ACCESS(partner, complement);
182 b->cleanup_nodes = eina_list_remove(b->cleanup_nodes, node);
183 }
184
185 border->one_direction = list;
186
187 EINA_LIST_FOREACH(border->one_direction, lnode, partner)
188 {
189 Border *comp_border = &DIRECTION_ACCESS(partner, complement);
190
191 comp_border->cleanup_nodes = eina_list_append(comp_border->cleanup_nodes, node);
192 }
193 }
194
195 static void
border_onedirection_cleanup(Node * node,Efl_Ui_Focus_Direction direction)196 border_onedirection_cleanup(Node *node, Efl_Ui_Focus_Direction direction)
197 {
198 Node *partner;
199 Border *border;
200 Efl_Ui_Focus_Direction complement;
201
202 EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(direction));
203 complement = efl_ui_focus_util_direction_complement(direction);
204 //this is basically a nop. The complement of 2D will *always* be 2D
205 EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(complement));
206
207 border = &DIRECTION_ACCESS(node, direction);
208
209 EINA_LIST_FREE(border->cleanup_nodes, partner)
210 {
211 Border *b = &DIRECTION_ACCESS(partner, complement);
212 b->one_direction = eina_list_remove(b->one_direction, node);
213 }
214 }
215 /**
216 * Create a new node
217 */
218 static Node*
node_new(Efl_Ui_Focus_Object * focusable,Efl_Ui_Focus_Manager * manager)219 node_new(Efl_Ui_Focus_Object *focusable, Efl_Ui_Focus_Manager *manager)
220 {
221 Node *node;
222
223 node = node_mem_get();
224
225 node->focusable = focusable;
226 node->manager = manager;
227
228 return node;
229 }
230
231 /**
232 * Looks up given focus object from the focus manager.
233 *
234 * @returns node found, or NULL if focusable was not found in the manager.
235 */
236 static Node*
node_get(Efl_Ui_Focus_Manager * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * focusable)237 node_get(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *focusable)
238 {
239 Node *ret;
240
241 ret = eina_hash_find(pd->node_hash, &focusable);
242
243 if (ret) return ret;
244
245 ERR("Focusable %p (%s) not registered in manager %p", focusable, efl_class_name_get(focusable), obj);
246
247 return NULL;
248 }
249
250 /**
251 * Free a node item and unlink this item from all direction
252 */
253 static void
node_item_free(Node * item)254 node_item_free(Node *item)
255 {
256 Node *n;
257 Eina_List *l;
258 Eo *obj = item->manager;
259 FOCUS_DATA(obj);
260
261 /*cleanup graph parts*/
262
263 //add all neighbors of the node to the dirty list
264 if (!item->unused)
265 {
266 for(int i = EFL_UI_FOCUS_DIRECTION_UP; i < EFL_UI_FOCUS_DIRECTION_LAST; i++)
267 {
268 Node *partner;
269
270 #define MAKE_LIST_DIRTY(node, field) \
271 EINA_LIST_FOREACH(DIRECTION_ACCESS(node, i).field, l, partner) \
272 { \
273 if (partner->type != NODE_TYPE_ONLY_LOGICAL) \
274 { \
275 dirty_add(obj, pd, partner); \
276 pd->border_elements_changed = EINA_TRUE; \
277 } \
278 }
279
280 MAKE_LIST_DIRTY(item, one_direction)
281 MAKE_LIST_DIRTY(item, cleanup_nodes)
282
283 border_onedirection_cleanup(item, i);
284 border_onedirection_set(item, i, NULL);
285 }
286
287 }
288
289 //the unregistering of a item should ever result in atleast a coords_dirty call
290 if (pd->border_elements_changed)
291 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL);
292
293 /*cleanup manager householdings*/
294
295 //remove from the focus stack
296
297 if (!item->unused)
298 pd->focus_stack = eina_list_remove(pd->focus_stack, item);
299
300 //if this is the entry for redirect, NULL them out!
301 if (pd->redirect_entry == item->focusable)
302 pd->redirect_entry = NULL;
303
304 //remove from the dirty parts
305 if (item->on_list)
306 pd->dirty = eina_list_remove(pd->dirty, item);
307 item->on_list = EINA_FALSE;
308
309 /*merge tree items*/
310
311 //free the tree items
312 if (!item->tree.parent && item->tree.children)
313 {
314 ERR("Freeing the root with children is going to break the logical tree!");
315 }
316
317 if (item->tree.parent && item->tree.children)
318 {
319 Node *parent;
320
321 parent = item->tree.parent;
322 //reparent everything into the next layer
323 EINA_LIST_FOREACH(item->tree.children, l, n)
324 {
325 n->tree.parent = item->tree.parent;
326 }
327 parent->tree.children = eina_list_merge(parent->tree.children , item->tree.children);
328 }
329
330 if (item->tree.parent)
331 {
332 Node *parent;
333
334 parent = item->tree.parent;
335 T(parent).children = eina_list_remove(T(parent).children, item);
336 }
337
338 //free the safed order
339 ELM_SAFE_FREE(T(item).saved_order, eina_list_free);
340
341 node_mem_free(item);
342 }
343 //FOCUS-STACK HELPERS
344
345 static Efl_Ui_Focus_Object*
_focus_stack_unfocus_last(Efl_Ui_Focus_Manager_Calc_Data * pd)346 _focus_stack_unfocus_last(Efl_Ui_Focus_Manager_Calc_Data *pd)
347 {
348 Efl_Ui_Focus_Object *focusable = NULL;
349 Node *n;
350
351 n = eina_list_last_data_get(pd->focus_stack);
352
353 if (n)
354 focusable = n->focusable;
355
356 pd->focus_stack = eina_list_remove(pd->focus_stack, n);
357
358 if (n)
359 efl_ui_focus_object_focus_set(n->focusable, EINA_FALSE);
360
361 return focusable;
362 }
363
364 #ifdef CALC_DEBUG
365 static void
_debug_node(Node * node)366 _debug_node(Node *node)
367 {
368 Eina_List *tmp = NULL;
369
370 if (!node) return;
371
372 printf("NODE %s-%s\n", DEBUG_TUPLE(node->focusable));
373
374 #define DIR_LIST(dir) DIRECTION_ACCESS(node,dir).partners
375
376 #define DIR_OUT(dir)\
377 tmp = DIR_LIST(dir); \
378 { \
379 Eina_List *list_node; \
380 Node *partner; \
381 printf("-"#dir"-> ("); \
382 EINA_LIST_FOREACH(tmp, list_node, partner) \
383 printf("%s-%s,", DEBUG_TUPLE(partner->focusable)); \
384 printf(")\n"); \
385 }
386
387 DIR_OUT(EFL_UI_FOCUS_DIRECTION_RIGHT)
388 DIR_OUT(EFL_UI_FOCUS_DIRECTION_LEFT)
389 DIR_OUT(EFL_UI_FOCUS_DIRECTION_UP)
390 DIR_OUT(EFL_UI_FOCUS_DIRECTION_DOWN)
391
392 }
393 #endif
394
395 static Eina_Bool
_no_logical(const void * iter EINA_UNUSED,void * data,void * fdata EINA_UNUSED)396 _no_logical(const void *iter EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
397 {
398 Node *n = data;
399
400 return n->type != NODE_TYPE_ONLY_LOGICAL;
401 }
402
403 static void
dirty_flush_node(Efl_Ui_Focus_Manager * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * node)404 dirty_flush_node(Efl_Ui_Focus_Manager *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *node)
405 {
406 Efl_Ui_Focus_Graph_Calc_Result result;
407
408 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);
409 node->unused = EINA_FALSE;
410 for (int i = 0; i < 4; ++i)
411 {
412 Efl_Ui_Focus_Direction direction = -1;
413 Efl_Ui_Focus_Graph_Calc_Direction_Result *res = NULL;
414 Node *partner;
415 Eina_List *n;
416
417 if (i == 0)
418 {
419 direction = EFL_UI_FOCUS_DIRECTION_RIGHT;
420 res = &result.right;
421 }
422 else if (i == 1)
423 {
424 direction = EFL_UI_FOCUS_DIRECTION_LEFT;
425 res = &result.left;
426 }
427 else if (i == 2)
428 {
429 direction = EFL_UI_FOCUS_DIRECTION_UP;
430 res = &result.top;
431 }
432 else if (i == 3)
433 {
434 direction = EFL_UI_FOCUS_DIRECTION_DOWN;
435 res = &result.bottom;
436 }
437
438 EINA_LIST_FOREACH(res->relation, n, partner)
439 partner->unused = EINA_FALSE;
440
441 border_onedirection_set(node, direction, res->relation);
442 }
443
444 #ifdef CALC_DEBUG
445 _debug_node(node);
446 #endif
447 }
448
449 static void
dirty_flush(Efl_Ui_Focus_Manager * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * node)450 dirty_flush(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *node)
451 {
452 if (!pd->dirty && node->type == NODE_TYPE_NORMAL) return;
453
454 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_FLUSH_PRE, NULL);
455
456 dirty_flush_node(obj, pd, node);
457 }
458
459 static void
dirty_flush_all(Efl_Ui_Focus_Manager * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)460 dirty_flush_all(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
461 {
462 Node *node;
463
464 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_FLUSH_PRE, NULL);
465
466 EINA_LIST_FREE(pd->dirty, node)
467 {
468 node->on_list = EINA_FALSE;
469 dirty_flush_node(obj, pd, node);
470 }
471 }
472
473 static void
dirty_add(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * dirty)474 dirty_add(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *dirty)
475 {
476 if (dirty->type == NODE_TYPE_ONLY_LOGICAL)
477 {
478 ERR("Only not only logical nodes can be marked dirty");
479 return;
480 }
481 if (dirty->on_list) return;
482
483 //if (eina_list_data_find(pd->dirty, dirty)) return;
484 pd->dirty = eina_list_append(pd->dirty, dirty);
485 dirty->on_list = EINA_TRUE;
486
487 if (!pd->border_elements_changed)
488 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL);
489 pd->border_elements_changed = EINA_TRUE;
490 }
491
492
493 static void
_node_new_geometry_cb(void * data,const Efl_Event * event)494 _node_new_geometry_cb(void *data, const Efl_Event *event)
495 {
496 Node *node;
497 FOCUS_DATA(data)
498
499 if (pd->freeze > 0) {
500 return;
501 }
502
503 node = node_get(data, pd, event->object);
504 if (!node)
505 return;
506
507 dirty_add(data, pd, node);
508
509 return;
510 }
511
512 static void
_object_del_cb(void * data,const Efl_Event * event)513 _object_del_cb(void *data, const Efl_Event *event)
514 {
515 /*
516 * Lets just implicitly delete items that are deleted
517 * Otherwise we have later just a bunch of errors
518 */
519 efl_ui_focus_manager_calc_unregister(data, event->object);
520 }
521
522 EFL_CALLBACKS_ARRAY_DEFINE(regular_node,
523 {EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_GEOMETRY_CHANGED, _node_new_geometry_cb},
524 {EFL_EVENT_INVALIDATE, _object_del_cb},
525 );
526
527 EFL_CALLBACKS_ARRAY_DEFINE(logical_node,
528 {EFL_EVENT_INVALIDATE, _object_del_cb},
529 );
530
531
532 //=============================
533
534 static Node*
_register(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Node * parent,Node_Type type,Efl_Ui_Focus_Manager * redirect)535 _register(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Node *parent, Node_Type type, Efl_Ui_Focus_Manager *redirect)
536 {
537 Node *node;
538 node = eina_hash_find(pd->node_hash, &child);
539 if (node && !(node->type == type && T(node).parent == parent && node->redirect_manager == redirect))
540 {
541 ERR("Child %p is already registered in the graph (%s)", child, node->type == NODE_TYPE_ONLY_LOGICAL ? "logical" : "regular");
542 return NULL;
543 }
544
545 node = node_new(child, obj);
546 eina_hash_add(pd->node_hash, &child, node);
547 node->unused = EINA_TRUE;
548 //add the parent
549 if (parent)
550 {
551 T(node).parent = parent;
552 T(parent).children = eina_list_append(T(parent).children, node);
553 T(parent).clean_apply = EINA_FALSE;
554 }
555 node->type = NODE_TYPE_ONLY_LOGICAL;
556 node->redirect_manager = redirect;
557
558 return node;
559 }
560 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_register_logical(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Object * parent,Efl_Ui_Focus_Manager * redirect)561 _efl_ui_focus_manager_calc_register_logical(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent, Efl_Ui_Focus_Manager *redirect)
562 {
563 Node *node = NULL;
564 Node *pnode = NULL;
565
566 EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
567 EINA_SAFETY_ON_NULL_RETURN_VAL(parent, EINA_FALSE);
568
569 if (redirect)
570 EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(redirect, EFL_UI_FOCUS_MANAGER_INTERFACE), EINA_FALSE);
571
572 F_DBG("Manager: %p register %p %p %p", obj, child, parent, redirect);
573
574 pnode = node_get(obj, pd, parent);
575 if (!pnode) return EINA_FALSE;
576
577 node = _register(obj, pd, child, pnode, NODE_TYPE_ONLY_LOGICAL, redirect);
578 if (!node) return EINA_FALSE;
579
580 //listen to deletion
581 efl_event_callback_array_add(child, logical_node(), obj);
582
583 //set again
584 if (T(pnode).saved_order)
585 {
586 Eina_List *tmp;
587
588 tmp = eina_list_clone(T(pnode).saved_order);
589 efl_ui_focus_manager_calc_update_order(obj, parent, tmp);
590 }
591
592 return EINA_TRUE;
593 }
594
595
596 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_register(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Object * parent,Efl_Ui_Focus_Manager * redirect)597 _efl_ui_focus_manager_calc_register(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent, Efl_Ui_Focus_Manager *redirect)
598 {
599 Node *node = NULL;
600 Node *pnode = NULL;
601
602 EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
603 EINA_SAFETY_ON_NULL_RETURN_VAL(parent, EINA_FALSE);
604
605 if (redirect)
606 EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(redirect, EFL_UI_FOCUS_MANAGER_INTERFACE), EINA_FALSE);
607
608 F_DBG("Manager: %p register %p %p %p", obj, child, parent, redirect);
609
610 pnode = node_get(obj, pd, parent);
611 if (!pnode) return EINA_FALSE;
612
613 node = _register(obj, pd, child, pnode, NODE_TYPE_NORMAL, redirect);
614 if (!node) return EINA_FALSE;
615
616 //listen to changes
617 efl_event_callback_array_add(child, regular_node(), obj);
618
619 node->type = NODE_TYPE_NORMAL;
620 node->redirect_manager = redirect;
621
622 //mark dirty
623 dirty_add(obj, pd, node);
624
625 return EINA_TRUE;
626 }
627
628 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_update_redirect(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Manager * redirect)629 _efl_ui_focus_manager_calc_update_redirect(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Manager *redirect)
630 {
631 Node *node = node_get(obj, pd, child);
632 if (!node) return EINA_FALSE;
633
634 if (redirect)
635 EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(redirect, MY_CLASS), EINA_FALSE);
636
637 node->redirect_manager = redirect;
638
639 return EINA_TRUE;
640 }
641
642 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_update_parent(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Object * parent_obj)643 _efl_ui_focus_manager_calc_update_parent(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent_obj)
644 {
645 Node *node;
646 Node *parent;
647
648 EINA_SAFETY_ON_NULL_RETURN_VAL(parent_obj, EINA_FALSE);
649 EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
650
651 node = node_get(obj, pd, child);
652 parent = node_get(obj, pd, parent_obj);
653
654 if (!node || !parent) return EINA_FALSE;
655
656 if (T(node).parent)
657 {
658 Node *old_parent;
659
660 old_parent = T(node).parent;
661
662 T(old_parent).children = eina_list_remove(T(old_parent).children, node);
663 }
664
665 T(node).parent = parent;
666
667 if (T(node).parent)
668 {
669 T(parent).children = eina_list_append(T(parent).children, node);
670 }
671
672 return EINA_TRUE;
673 }
674
675 static Eina_List*
_set_a_without_b(Eina_List * a,Eina_List * b)676 _set_a_without_b(Eina_List *a, Eina_List *b)
677 {
678 Eina_List *a_out = NULL, *node;
679 void *data;
680
681 a_out = eina_list_clone(a);
682
683 EINA_LIST_FOREACH(b, node, data)
684 {
685 a_out = eina_list_remove(a_out, data);
686 }
687
688 return a_out;
689 }
690
691 static Eina_Bool
_equal_set(Eina_List * none_nodes,Eina_List * nodes)692 _equal_set(Eina_List *none_nodes, Eina_List *nodes)
693 {
694 Eina_List *n;
695 Node *node;
696
697 if (eina_list_count(nodes) != eina_list_count(none_nodes)) return EINA_FALSE;
698
699 EINA_LIST_FOREACH(nodes, n, node)
700 {
701 if (!eina_list_data_find(none_nodes, node))
702 return EINA_FALSE;
703 }
704
705 return EINA_TRUE;
706 }
707
708 EOLIAN static void
_efl_ui_focus_manager_calc_update_order(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * parent,Eina_List * order)709 _efl_ui_focus_manager_calc_update_order(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *parent, Eina_List *order)
710 {
711 Node *pnode;
712 Efl_Ui_Focus_Object *o;
713 Eina_List *node_order = NULL, *not_ordered, *n;
714
715 F_DBG("Manager_update_order on %p %p", obj, parent);
716
717 pnode = node_get(obj, pd, parent);
718 if (!pnode)
719 return;
720
721 ELM_SAFE_FREE(T(pnode).saved_order, eina_list_free);
722 T(pnode).saved_order = order;
723 T(pnode).clean_apply = EINA_TRUE;
724
725 //get all nodes from the subset
726 EINA_LIST_FOREACH(order, n, o)
727 {
728 Node *tmp;
729
730 tmp = eina_hash_find(pd->node_hash, &o);
731
732 if (!tmp) continue;
733 if (T(tmp).parent != pnode) continue;
734
735 node_order = eina_list_append(node_order, tmp);
736 }
737 if (eina_list_count(node_order) == eina_list_count(T(pnode).children))
738 {
739 eina_list_free(T(pnode).children);
740 T(pnode).children = node_order;
741 }
742 else
743 {
744 not_ordered = _set_a_without_b(T(pnode).children, node_order);
745 eina_list_free(T(pnode).children);
746 T(pnode).children = eina_list_merge(node_order, not_ordered);
747 }
748 T(pnode).clean_apply = EINA_TRUE;
749
750 return;
751 }
752
753 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_update_children(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * parent,Eina_List * order)754 _efl_ui_focus_manager_calc_update_children(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *parent, Eina_List *order)
755 {
756 Node *pnode;
757 Efl_Ui_Focus_Object *o;
758 Eina_Bool fail = EINA_FALSE;
759 Eina_List *node_order = NULL;
760
761 pnode = node_get(obj, pd, parent);
762 if (!pnode)
763 return EINA_FALSE;
764
765 //get all nodes from the subset
766 EINA_LIST_FREE(order, o)
767 {
768 Node *tmp;
769
770 tmp = node_get(obj, pd, o);
771 if (!tmp)
772 fail = EINA_TRUE;
773 node_order = eina_list_append(node_order, tmp);
774 }
775
776 if (fail)
777 {
778 eina_list_free(node_order);
779 return EINA_FALSE;
780 }
781
782 if (!_equal_set(node_order, T(pnode).children))
783 {
784 ERR("Set of children is not equal");
785 return EINA_FALSE;
786 }
787
788 eina_list_free(T(pnode).children);
789 T(pnode).children = node_order;
790
791 return EINA_TRUE;
792 }
793
794 static inline Node*
_request_subchild_except(Node * n,Eo * except)795 _request_subchild_except(Node *n, Eo *except)
796 {
797 n = _request_subchild(n);
798 while (n && n->focusable == except)
799 {
800 n = _next(n);
801 }
802
803 return n;
804 }
805
806 EOLIAN static void
_efl_ui_focus_manager_calc_unregister(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child)807 _efl_ui_focus_manager_calc_unregister(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child)
808 {
809 Node *node;
810
811 node = eina_hash_find(pd->node_hash, &child);
812
813 if (!node) return;
814
815 F_DBG("Manager: %p unregister %p", obj, child);
816
817 if (eina_list_last_data_get(pd->focus_stack) == node)
818 {
819 if (!efl_invalidated_get(pd->root->focusable) && _focus_manager_active_get(obj))
820 {
821 Node *n = NULL;
822
823 // if there is no redirect manager, then try to recover the focus property to a different element
824 //allow the removal of the redirect when we are removing the child that is the redirect manager
825 if (!pd->redirect || pd->redirect == child)
826 {
827 n = eina_list_nth(pd->focus_stack, eina_list_count(pd->focus_stack) - 2);
828 if (!n)
829 n = _request_subchild_except(pd->root, node->focusable);
830
831 if (n)
832 efl_ui_focus_manager_focus_set(obj, n->focusable);
833 }
834 // if there is a redirect manager, then remove the flag from the child
835 if (!n)
836 {
837 efl_ui_focus_object_focus_set(child, EINA_FALSE);
838 }
839 }
840 }
841
842 eina_hash_del_by_key(pd->node_hash, &child);
843 }
844
845 static void
_redirect_del(void * data,const Efl_Event * event EINA_UNUSED)846 _redirect_del(void *data, const Efl_Event *event EINA_UNUSED)
847 {
848 efl_ui_focus_manager_redirect_set(data, NULL);
849 }
850
851 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_set(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Manager * redirect)852 _efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_set(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Manager *redirect)
853 {
854 Efl_Ui_Focus_Manager *old_manager;
855
856 if (pd->redirect == redirect) return;
857
858 F_DBG("Manager: %p setting redirect from %p to %p", obj, pd->redirect, redirect);
859
860 old_manager = pd->redirect;
861
862 if (old_manager)
863 efl_event_callback_del(old_manager, EFL_EVENT_DEL, _redirect_del, obj);
864
865 pd->redirect = redirect;
866
867 if (pd->redirect)
868 efl_event_callback_add(pd->redirect, EFL_EVENT_DEL, _redirect_del, obj);
869
870 if (old_manager)
871 efl_ui_focus_manager_reset_history(old_manager);
872
873 //adjust focus property of the most upper element
874 if (_focus_manager_active_get(obj))
875 {
876 Node *n = NULL;
877
878 n = eina_list_last_data_get(pd->focus_stack);
879
880 if (!pd->redirect && old_manager)
881 {
882 if (n)
883 {
884 efl_ui_focus_object_focus_set(n->focusable, EINA_TRUE);
885 }
886 else
887 {
888 n = _request_subchild_except(pd->root, pd->redirect_entry);
889 if (n)
890 efl_ui_focus_manager_focus_set(obj, n->focusable);
891 }
892 }
893 else if (pd->redirect && !old_manager)
894 {
895 if (n)
896 efl_ui_focus_object_focus_set(n->focusable, EINA_FALSE);
897 }
898 }
899
900 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_REDIRECT_CHANGED , old_manager);
901
902 //just set the root of the new redirect as focused, so it is in a known state
903 if (redirect)
904 {
905 efl_ui_focus_manager_setup_on_first_touch(redirect, EFL_UI_FOCUS_DIRECTION_LAST, NULL);
906 }
907 }
908
909 EOLIAN static Efl_Ui_Focus_Manager *
_efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)910 _efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
911 {
912 return pd->redirect;
913 }
914
915 static void
_free_node(void * data)916 _free_node(void *data)
917 {
918 Node *node = data;
919
920 if (node->type == NODE_TYPE_ONLY_LOGICAL)
921 efl_event_callback_array_del(node->focusable, logical_node(), node->manager);
922 else
923 efl_event_callback_array_del(node->focusable, regular_node(), node->manager);
924
925 if (!node->this_is_root)
926 {
927 node_item_free(node);
928 }
929 }
930
931 EOLIAN static Efl_Object *
_efl_ui_focus_manager_calc_efl_object_constructor(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)932 _efl_ui_focus_manager_calc_efl_object_constructor(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
933 {
934 obj = efl_constructor(efl_super(obj, MY_CLASS));
935 pd->node_hash = eina_hash_pointer_new(_free_node);
936
937 pd->graph_ctx.offset_focusable = offsetof(Node, focusable);
938 pd->border_elements_changed = EINA_TRUE;
939
940 return obj;
941 }
942
943 EOLIAN static Efl_Object *
_efl_ui_focus_manager_calc_efl_object_provider_find(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd EINA_UNUSED,const Efl_Object * klass)944 _efl_ui_focus_manager_calc_efl_object_provider_find(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd EINA_UNUSED, const Efl_Object *klass)
945 {
946 if (klass == MY_CLASS)
947 return (Efl_Object *) obj;
948
949 return efl_provider_find(efl_super(obj, MY_CLASS), klass);
950 }
951
952 EOLIAN static void
_efl_ui_focus_manager_calc_efl_object_destructor(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)953 _efl_ui_focus_manager_calc_efl_object_destructor(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
954 {
955 Node *n;
956
957 pd->focus_stack = eina_list_free(pd->focus_stack);
958 EINA_LIST_FREE(pd->dirty, n)
959 {
960 n->on_list = EINA_FALSE;
961 }
962 eina_hash_free(pd->node_hash);
963
964 efl_ui_focus_manager_redirect_set(obj, NULL);
965
966 if (pd->root)
967 node_item_free(pd->root);
968 pd->root = NULL;
969
970 efl_destructor(efl_super(obj, MY_CLASS));
971 }
972
973 typedef struct {
974 Eina_Iterator iterator;
975 Eina_Iterator *real_iterator;
976 Efl_Ui_Focus_Manager *object;
977 Eina_Each_Cb filter_cb;
978 Eina_Rect viewport;
979 Eina_Bool use_viewport;
980 } Border_Elements_Iterator;
981
982 static Eina_Bool
_border_filter_cb(Node * node,Border_Elements_Iterator * pd EINA_UNUSED)983 _border_filter_cb(Node *node, Border_Elements_Iterator *pd EINA_UNUSED)
984 {
985 for(int i = EFL_UI_FOCUS_DIRECTION_UP ;i < EFL_UI_FOCUS_DIRECTION_LAST; i++)
986 {
987 if (!DIRECTION_ACCESS(node, i).one_direction)
988 {
989 return EINA_TRUE;
990 }
991 }
992 return EINA_FALSE;
993 }
994
995 static Eina_Bool
eina_rectangle_real_inside(Eina_Rect rect,Eina_Rect geom)996 eina_rectangle_real_inside(Eina_Rect rect, Eina_Rect geom)
997 {
998 int min_x, max_x, min_y, max_y;
999
1000 min_x = geom.rect.x;
1001 min_y = geom.rect.y;
1002 max_x = eina_rectangle_max_x(&geom.rect);
1003 max_y = eina_rectangle_max_y(&geom.rect);
1004
1005 Eina_Bool inside = eina_rectangle_coords_inside(&rect.rect, min_x, min_y) &&
1006 eina_rectangle_coords_inside(&rect.rect, min_x, max_y) &&
1007 eina_rectangle_coords_inside(&rect.rect, max_x, min_y) &&
1008 eina_rectangle_coords_inside(&rect.rect, max_x, max_y);
1009
1010 return inside;
1011 }
1012
1013 static Eina_Bool
_viewport_filter_cb(Node * node,Border_Elements_Iterator * pd)1014 _viewport_filter_cb(Node *node, Border_Elements_Iterator *pd)
1015 {
1016 Node *partner;
1017 Eina_Rect geom;
1018
1019 if (node->type == NODE_TYPE_ONLY_LOGICAL) return EINA_FALSE;
1020
1021 geom = efl_ui_focus_object_focus_geometry_get(node->focusable);
1022
1023 if (eina_rectangle_real_inside(pd->viewport, geom))
1024 {
1025 for (int i = 0; i < 4; ++i)
1026 {
1027 Eina_List *n, *lst = G(node).directions[i].one_direction;
1028
1029 if (!lst)
1030 return EINA_TRUE;
1031
1032 EINA_LIST_FOREACH(lst, n, partner)
1033 {
1034 Eina_Rect partner_geom;
1035 partner_geom = efl_ui_focus_object_focus_geometry_get(partner->focusable);
1036 if (!eina_rectangle_real_inside(pd->viewport, partner_geom))
1037 return EINA_TRUE;
1038 }
1039 }
1040 }
1041 return EINA_FALSE;
1042 }
1043
1044
1045 static Eina_Bool
_iterator_next(Border_Elements_Iterator * it,void ** data)1046 _iterator_next(Border_Elements_Iterator *it, void **data)
1047 {
1048 Node *node;
1049
1050 EINA_ITERATOR_FOREACH(it->real_iterator, node)
1051 {
1052 Eina_Bool use = EINA_FALSE;
1053 if (node->type == NODE_TYPE_ONLY_LOGICAL) continue;
1054
1055 if (!it->use_viewport)
1056 use = _border_filter_cb(node, it);
1057 else
1058 use = _viewport_filter_cb(node, it);
1059
1060 if (use)
1061 {
1062 *data = node->focusable;
1063 return EINA_TRUE;
1064 }
1065 }
1066
1067 return EINA_FALSE;
1068 }
1069
1070 static Eo *
_iterator_get_container(Border_Elements_Iterator * it)1071 _iterator_get_container(Border_Elements_Iterator *it)
1072 {
1073 return it->object;
1074 }
1075
1076 static void
_iterator_free(Border_Elements_Iterator * it)1077 _iterator_free(Border_Elements_Iterator *it)
1078 {
1079 eina_iterator_free(it->real_iterator);
1080 free(it);
1081 }
1082
1083 static void
_prepare_node(Node * root)1084 _prepare_node(Node *root)
1085 {
1086 Eina_List *n;
1087 Node *node;
1088
1089 efl_ui_focus_object_setup_order(root->focusable);
1090
1091 EINA_LIST_FOREACH(root->tree.children, n, node)
1092 {
1093 _prepare_node(node);
1094 }
1095 }
1096
1097 static Border_Elements_Iterator*
_elements_iterator_new(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)1098 _elements_iterator_new(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
1099 {
1100 Border_Elements_Iterator *it;
1101
1102 /* XXX const */
1103 dirty_flush_all((Eo *)obj, pd);
1104
1105 //now call prepare on every node
1106 _prepare_node(pd->root);
1107
1108 it = calloc(1, sizeof(Border_Elements_Iterator));
1109
1110 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1111
1112 it->real_iterator = eina_hash_iterator_data_new(pd->node_hash);
1113 it->iterator.version = EINA_ITERATOR_VERSION;
1114 it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next);
1115 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_iterator_get_container);
1116 it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free);
1117 it->object = (Eo *)obj;
1118
1119 return it;
1120 }
1121
1122 EOLIAN static Eina_Iterator*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)1123 _efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
1124 {
1125 pd->border_elements_changed = EINA_FALSE;
1126 return (Eina_Iterator*) _elements_iterator_new(obj, pd);
1127 }
1128
1129
1130 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_changed_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1131 _efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_changed_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1132 {
1133 return pd->border_elements_changed;
1134 }
1135
1136
1137 EOLIAN static Eina_Iterator*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_viewport_elements_get(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Eina_Rect viewport)1138 _efl_ui_focus_manager_calc_efl_ui_focus_manager_viewport_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Eina_Rect viewport)
1139 {
1140 Border_Elements_Iterator *it = _elements_iterator_new(obj, pd);
1141
1142 it->use_viewport = EINA_TRUE;
1143 it->viewport = viewport;
1144
1145 return (Eina_Iterator*) it;
1146 }
1147
1148 static Node*
_no_history_element(Eina_Hash * node_hash)1149 _no_history_element(Eina_Hash *node_hash)
1150 {
1151 //nothing is selected yet, just try to use the first element in the iterator
1152 Eina_Iterator *iter;
1153 Node *upper;
1154
1155 iter = eina_hash_iterator_data_new(node_hash);
1156
1157 EINA_ITERATOR_FOREACH(iter, upper)
1158 {
1159 if (upper->type == NODE_TYPE_NORMAL)
1160 break;
1161 }
1162
1163 eina_iterator_free(iter);
1164
1165 if (upper->type != NODE_TYPE_NORMAL)
1166 return NULL;
1167
1168 return upper;
1169 }
1170
1171 static void
_get_middle(Evas_Object * obj,Eina_Vector2 * elem)1172 _get_middle(Evas_Object *obj, Eina_Vector2 *elem)
1173 {
1174 Eina_Rect geom;
1175
1176 geom = efl_ui_focus_object_focus_geometry_get(obj);
1177 elem->x = geom.x + geom.w/2;
1178 elem->y = geom.y + geom.h/2;
1179 }
1180
1181 static Node*
_coords_movement(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * upper,Efl_Ui_Focus_Direction direction)1182 _coords_movement(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *upper, Efl_Ui_Focus_Direction direction)
1183 {
1184 Node *candidate;
1185 Eina_List *node_list;
1186 Eina_List *lst;
1187
1188 EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_IS_2D(direction), NULL);
1189
1190 //flush the node and prepare all the nodes
1191 _prepare_node(pd->root);
1192 dirty_flush(obj, pd, upper);
1193
1194 //decide which direction we take
1195 lst = DIRECTION_ACCESS(upper, direction).one_direction;
1196
1197 //we are searching which of the partners is lower to the history
1198 EINA_LIST_REVERSE_FOREACH(pd->focus_stack, node_list, candidate)
1199 {
1200 //we only calculate partners for normal nodes
1201 if (candidate->type == NODE_TYPE_NORMAL) continue;
1202
1203 if (eina_list_data_find(lst, candidate))
1204 {
1205 //this is the next accessible part
1206 return candidate;
1207 }
1208 }
1209
1210 //if we haven't found anything in the history, use the widget with the smallest distance
1211 {
1212 Eina_List *n;
1213 Node *node, *min = NULL;
1214 Eina_Vector2 elem, other;
1215 float min_distance = 0.0;
1216
1217 _get_middle(upper->focusable, &elem);
1218
1219 EINA_LIST_FOREACH(lst, n, node)
1220 {
1221 _get_middle(node->focusable, &other);
1222 float tmp = eina_vector2_distance_get(&other, &elem);
1223 if (!min || tmp < min_distance)
1224 {
1225 min = node;
1226 min_distance = tmp;
1227 }
1228 }
1229 candidate = min;
1230 }
1231 return candidate;
1232 }
1233
1234
1235 static Node*
_prev_item(Node * node)1236 _prev_item(Node *node)
1237 {
1238 Node *parent;
1239 Eina_List *lnode;
1240
1241 parent = T(node).parent;
1242
1243 //we are accessing the parents children, prepare!
1244 efl_ui_focus_object_setup_order(parent->focusable);
1245
1246 lnode = eina_list_data_find_list(T(parent).children, node);
1247 lnode = eina_list_prev(lnode);
1248
1249 if (lnode)
1250 return eina_list_data_get(lnode);
1251 return NULL;
1252 }
1253
1254 static Node*
_next(Node * node)1255 _next(Node *node)
1256 {
1257 Node *n;
1258 if (!node) return NULL;
1259
1260 //Case 1 we are having children
1261 //But only enter the children if it does NOT have a redirect manager
1262 if (T(node).children && !node->redirect_manager)
1263 {
1264 return eina_list_data_get(T(node).children);
1265 }
1266
1267 //case 2 we are the root and we don't have children, return ourself
1268 if (!T(node).parent)
1269 {
1270 return node;
1271 }
1272
1273 //case 3 we are not at the end of the parents list
1274 n = node;
1275 while(T(n).parent)
1276 {
1277 Node *parent;
1278 Eina_List *lnode;
1279
1280 parent = T(n).parent;
1281
1282 //we are accessing the parents children, prepare!
1283 efl_ui_focus_object_setup_order(parent->focusable);
1284
1285 lnode = eina_list_data_find_list(T(parent).children, n);
1286 lnode = eina_list_next(lnode);
1287
1288 if (lnode)
1289 {
1290 return eina_list_data_get(lnode);
1291 }
1292
1293 n = parent;
1294 }
1295
1296 //this is then the root again
1297 return NULL;
1298 }
1299
1300 static Node*
_prev(Node * node)1301 _prev(Node *node)
1302 {
1303 Node *n = NULL;
1304
1305 //this is the root there is no parent
1306 if (!T(node).parent)
1307 return NULL;
1308
1309 n =_prev_item(node);
1310
1311 //we are accessing prev items children, prepare them!
1312 if (n && n->focusable)
1313 efl_ui_focus_object_setup_order(n->focusable);
1314
1315 //case 1 there is a item in the parent previous to node, which has children
1316 if (n && T(n).children && !n->redirect_manager)
1317 {
1318 do
1319 {
1320 n = eina_list_last_data_get(T(n).children);
1321 }
1322 while (T(n).children && !n->redirect_manager);
1323
1324 return n;
1325 }
1326
1327 //case 2 there is a item in the parent previous to node, which has no children
1328 if (n)
1329 return n;
1330
1331 //case 3 there is a no item in the parent previous to this one
1332 return T(node).parent;
1333 }
1334
1335
1336 static Node*
_logical_movement(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd EINA_UNUSED,Node * upper,Efl_Ui_Focus_Direction direction,Eina_Bool accept_logical)1337 _logical_movement(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd EINA_UNUSED, Node *upper, Efl_Ui_Focus_Direction direction, Eina_Bool accept_logical)
1338 {
1339 Node* (*deliver)(Node *n);
1340 Node *result;
1341 Eina_List *stack = NULL;
1342
1343 EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_IS_LOGICAL(direction), NULL);
1344
1345 if (direction == EFL_UI_FOCUS_DIRECTION_NEXT)
1346 deliver = _next;
1347 else
1348 deliver = _prev;
1349
1350 //search as long as we have a none logical parent
1351 result = upper;
1352 do
1353 {
1354 //give up, if we have already been here
1355 if (!!eina_list_data_find(stack, result))
1356 {
1357 eina_list_free(stack);
1358 ERR("Warning cycle detected\n");
1359 return NULL;
1360 }
1361
1362 stack = eina_list_append(stack, result);
1363
1364 if (direction == EFL_UI_FOCUS_DIRECTION_NEXT)
1365 {
1366 //set again
1367 if (T(result).saved_order && !T(result).clean_apply)
1368 {
1369 Eina_List *tmp;
1370
1371 tmp = eina_list_clone(T(result).saved_order);
1372 efl_ui_focus_manager_calc_update_order(obj, result->focusable, tmp);
1373 }
1374 efl_ui_focus_object_setup_order(result->focusable);
1375 }
1376
1377 result = deliver(result);
1378 if (accept_logical)
1379 break;
1380 } while(result && result->type != NODE_TYPE_NORMAL && !result->redirect_manager);
1381
1382 eina_list_free(stack);
1383
1384 return result;
1385 }
1386
1387 static Efl_Ui_Focus_Object*
_request_move(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction,Node * upper,Eina_Bool accept_logical)1388 _request_move(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction, Node *upper, Eina_Bool accept_logical)
1389 {
1390 Node *dir = NULL;
1391
1392 if (!upper)
1393 upper = eina_list_last_data_get(pd->focus_stack);
1394
1395 if (!upper)
1396 {
1397 upper = _no_history_element(pd->node_hash);
1398 if (upper)
1399 return upper->focusable;
1400 return NULL;
1401
1402 }
1403
1404 if (direction == EFL_UI_FOCUS_DIRECTION_PREVIOUS
1405 || direction == EFL_UI_FOCUS_DIRECTION_NEXT)
1406 dir = _logical_movement(obj, pd, upper, direction, accept_logical);
1407 else
1408 dir = _coords_movement(obj, pd, upper, direction);
1409
1410 //return the widget
1411 if (dir)
1412 return dir->focusable;
1413 else
1414 return NULL;
1415 }
1416
1417 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_request_move(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction,Efl_Ui_Focus_Object * child,Eina_Bool logical)1418 _efl_ui_focus_manager_calc_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical)
1419 {
1420 Node *child_node;
1421 EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_CHECK(direction), NULL);
1422
1423 if (!child)
1424 child_node = eina_list_last_data_get(pd->focus_stack);
1425 else
1426 child_node = node_get(obj, pd, child);
1427
1428 if (!child_node)
1429 return NULL;
1430
1431 return _request_move(obj, pd, direction, child_node, logical);
1432 }
1433
1434 static int
_node_depth(Node * node)1435 _node_depth(Node *node)
1436 {
1437 int i = 0;
1438
1439 while (node && node->tree.parent)
1440 {
1441 node = node->tree.parent;
1442 i++;
1443 }
1444
1445 return i;
1446 }
1447
1448 static Node*
_request_subchild(Node * node)1449 _request_subchild(Node *node)
1450 {
1451 //important! if there are no children _next would return the parent of node which will exceed the limit of children of node
1452 Node *target = NULL;
1453 if (!node) return target;
1454
1455 if (node->tree.children)
1456 {
1457 int new_depth, old_depth = _node_depth(node);
1458
1459 target = node;
1460 //try to find a child that is not logical or has a redirect manager
1461 do
1462 {
1463 if (target != node)
1464 efl_ui_focus_object_setup_order(target->focusable);
1465
1466 target = _next(target);
1467 //abort if we are exceeding the childrens of node
1468 new_depth = _node_depth(target);
1469
1470 if (new_depth <= old_depth) target = NULL;
1471 }
1472 while (target && target->type == NODE_TYPE_ONLY_LOGICAL && !target->redirect_manager);
1473 F_DBG("Found node %p", target);
1474 }
1475
1476 return target;
1477 }
1478
1479 static inline void
_current_focused_parent_to_array(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Eina_Array * array)1480 _current_focused_parent_to_array(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Eina_Array *array)
1481 {
1482 Eo *focused = efl_ui_focus_manager_focus_get(obj);
1483 Node *f;
1484
1485 if (!focused) return;
1486
1487 for (f = node_get(obj, pd, focused); f && efl_ui_focus_manager_root_get(obj) != f->focusable; f = f->tree.parent)
1488 {
1489 eina_array_push(array, f->focusable);
1490 }
1491 }
1492
1493 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_set(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * focus)1494 _efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_set(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *focus)
1495 {
1496 Node *node, *last;
1497 Efl_Ui_Focus_Object *last_focusable = NULL, *new_focusable;
1498 Efl_Ui_Focus_Manager *redirect_manager;
1499 Node_Type node_type;
1500 Eina_Array *old_chain = eina_array_new(5), *chain = eina_array_new(5);
1501
1502 EINA_SAFETY_ON_NULL_GOTO(focus, end);
1503
1504 _current_focused_parent_to_array(obj, pd, old_chain);
1505
1506 //check if node is part of this manager object
1507 node = node_get(obj, pd, focus);
1508 if (!node)
1509 goto end;
1510
1511 if (node->type == NODE_TYPE_ONLY_LOGICAL && !node->redirect_manager)
1512 {
1513 Node *target = NULL;
1514
1515 F_DBG(" %p is logical, fetching the next subnode that is either a redirect or a regular", obj);
1516
1517 //important! if there are no children _next would return the parent of node which will exceed the limit of children of node
1518 efl_ui_focus_object_setup_order(node->focusable);
1519
1520 target = _request_subchild(node);
1521
1522 //check if we have found anything
1523 if (target)
1524 {
1525 node = target;
1526 }
1527 else
1528 {
1529 ERR("Could not fetch a node located at %p", node->focusable);
1530 goto end;
1531 }
1532 }
1533
1534 F_DBG("Manager: %p focusing object %p %s", obj, node->focusable, efl_class_name_get(node->focusable));
1535
1536
1537 if (eina_list_last_data_get(pd->focus_stack) == node)
1538 {
1539 //the correct one is focused
1540 if (node->redirect_manager == pd->redirect)
1541 goto end;
1542 }
1543
1544 //make sure this manager is in the chain of redirects
1545 _manager_in_chain_set(obj, pd);
1546
1547 node->unused = EINA_FALSE;
1548 node_type = node->type;
1549 new_focusable = node->focusable;
1550
1551 redirect_manager = node->redirect_manager;
1552
1553 last = eina_list_last_data_get(pd->focus_stack);
1554 if (last)
1555 last_focusable = last->focusable;
1556
1557 //remove the object from the list and add it again
1558 if (node_type == NODE_TYPE_NORMAL)
1559 {
1560 EINA_SAFETY_ON_TRUE_RETURN(node->unused);
1561 pd->focus_stack = eina_list_remove(pd->focus_stack, node);
1562 pd->focus_stack = eina_list_append(pd->focus_stack, node);
1563 }
1564
1565 //capture how we came to the redirect manager
1566 if (redirect_manager)
1567 {
1568 pd->redirect_entry = new_focusable;
1569 }
1570
1571 //set to NULL here, from the event earlier this pointer could be dead.
1572 node = NULL;
1573
1574 //unset redirect manager for the case that its a different one to the one we want
1575 if (pd->redirect && pd->redirect != redirect_manager)
1576 {
1577 Efl_Ui_Focus_Manager *m = obj;
1578
1579 //completely unset the current redirect chain
1580 while (efl_ui_focus_manager_redirect_get(m))
1581 {
1582 Efl_Ui_Focus_Manager *old = m;
1583
1584 m = efl_ui_focus_manager_redirect_get(m);
1585 efl_ui_focus_manager_redirect_set(old, NULL);
1586 }
1587 }
1588
1589 //now check if this is also a listener object
1590 if (redirect_manager)
1591 {
1592 //set the redirect
1593 efl_ui_focus_manager_redirect_set(obj, redirect_manager);
1594 }
1595
1596 /*
1597 Only emit those signals if we are already at the top of the focus stack.
1598 Otherwise focus_get in the callback to that signal might return false.
1599 */
1600 if (node_type == NODE_TYPE_NORMAL)
1601 {
1602 //populate the new change
1603 if (last_focusable)
1604 efl_ui_focus_object_focus_set(last_focusable, EINA_FALSE);
1605 if (new_focusable)
1606 efl_ui_focus_object_focus_set(new_focusable, EINA_TRUE);
1607 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, last_focusable);
1608 }
1609
1610 _current_focused_parent_to_array(obj, pd, chain);
1611 //first pop off all elements that are the same
1612 while (eina_array_count(chain) > 0 && eina_array_count(old_chain) > 0 &&
1613 eina_array_data_get(chain, (int)eina_array_count(chain) -1) == eina_array_data_get(old_chain, (int)eina_array_count(old_chain) - 1))
1614 {
1615 eina_array_pop(chain);
1616 eina_array_pop(old_chain);
1617 }
1618
1619 while (eina_array_count(chain) > 1)
1620 {
1621 Eina_Bool child_focus_changed = EINA_TRUE;
1622 efl_event_callback_call(eina_array_pop(chain), EFL_UI_FOCUS_OBJECT_EVENT_CHILD_FOCUS_CHANGED , &child_focus_changed);
1623 }
1624
1625 while (eina_array_count(old_chain) > 1)
1626 {
1627 Eina_Bool child_focus_changed = EINA_FALSE;
1628 efl_event_callback_call(eina_array_pop(old_chain), EFL_UI_FOCUS_OBJECT_EVENT_CHILD_FOCUS_CHANGED, &child_focus_changed);
1629 }
1630
1631 end:
1632 eina_array_free(old_chain);
1633 eina_array_free(chain);
1634 }
1635
1636 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_setup_on_first_touch(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction,Efl_Ui_Focus_Object * entry)1637 _efl_ui_focus_manager_calc_efl_ui_focus_manager_setup_on_first_touch(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *entry)
1638 {
1639 if (direction == EFL_UI_FOCUS_DIRECTION_PREVIOUS && entry)
1640 {
1641 Efl_Ui_Focus_Manager_Logical_End_Detail last;
1642 Efl_Ui_Focus_Manager *rec_manager = obj;
1643 do
1644 {
1645 last = efl_ui_focus_manager_logical_end(rec_manager);
1646 EINA_SAFETY_ON_NULL_RETURN(last.element);
1647 efl_ui_focus_manager_focus_set(rec_manager, last.element);
1648
1649 rec_manager = efl_ui_focus_manager_redirect_get(rec_manager);
1650 }
1651 while (rec_manager);
1652 }
1653 else if (DIRECTION_IS_2D(direction) && entry)
1654 efl_ui_focus_manager_focus_set(obj, entry);
1655 else
1656 efl_ui_focus_manager_focus_set(obj, pd->root->focusable);
1657 }
1658
1659
1660 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_move(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction)1661 _efl_ui_focus_manager_calc_efl_ui_focus_manager_move(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction)
1662 {
1663 Efl_Ui_Focus_Object *candidate = NULL;
1664 Efl_Ui_Focus_Manager *early, *late;
1665
1666 // for the case that focus is set to a new element, a new redirect
1667 // manager could have been set, to adjust everything
1668 // in the new reidirect manager we have to call the first touch function.
1669 Eina_Bool adjust_redirect_manager = EINA_FALSE;
1670
1671 EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_CHECK(direction), NULL);
1672
1673 early = efl_ui_focus_manager_redirect_get(obj);
1674
1675 if (pd->redirect)
1676 {
1677 Efl_Ui_Focus_Object *old_candidate = NULL;
1678 candidate = efl_ui_focus_manager_move(pd->redirect, direction);
1679
1680 if (!candidate)
1681 {
1682 Efl_Ui_Focus_Object *new_candidate = NULL;
1683
1684 if (DIRECTION_IS_LOGICAL(direction))
1685 {
1686 // lets just take the redirect_entry
1687 Node *n = node_get(obj, pd, pd->redirect_entry);
1688 new_candidate = _request_move(obj, pd, direction, n, EINA_FALSE);
1689
1690 if (new_candidate)
1691 {
1692 efl_ui_focus_manager_focus_set(obj, new_candidate);
1693 adjust_redirect_manager = EINA_TRUE;
1694 }
1695 else
1696 {
1697 //we set the redirect to NULL since it cannot
1698 //help us, later on the redirect manager can be
1699 //set to the same again, and it is strictly new setted up.
1700 efl_ui_focus_manager_redirect_set(obj, NULL);
1701 pd->redirect_entry = NULL;
1702 }
1703
1704 candidate = new_candidate;
1705 }
1706 else
1707 {
1708 Node *n;
1709
1710 old_candidate = efl_ui_focus_manager_focus_get(pd->redirect);
1711 n = eina_hash_find(pd->node_hash, &old_candidate);
1712
1713 if (n)
1714 new_candidate = _request_move(obj, pd, direction, n, EINA_FALSE);
1715
1716 if (new_candidate)
1717 {
1718 //redirect does not have smth. but we do have.
1719 efl_ui_focus_manager_focus_set(obj, new_candidate);
1720 adjust_redirect_manager = EINA_TRUE;
1721 }
1722 else
1723 {
1724 /*
1725 DO NOT unset the redirect here, the value has to stay,
1726 if one direction is not continuing in one direction, the
1727 other can continue thus we need to safe the redirect for this case.
1728 */
1729
1730 }
1731
1732 candidate = new_candidate;
1733 }
1734 }
1735 }
1736 else
1737 {
1738 Efl_Ui_Focus_Object *child = NULL;
1739
1740 if (!pd->focus_stack)
1741 {
1742 Node *child_node;
1743
1744 child_node = _no_history_element(pd->node_hash);
1745 if (child_node)
1746 child = child_node->focusable;
1747 }
1748
1749 candidate = efl_ui_focus_manager_request_move(obj, direction, child, EINA_FALSE);
1750
1751 F_DBG("Manager: %p moved to %p %s in direction %d", obj, candidate, efl_class_name_get(candidate), direction);
1752
1753 if (candidate)
1754 {
1755 efl_ui_focus_manager_focus_set(obj, candidate);
1756 adjust_redirect_manager = EINA_TRUE;
1757 }
1758 }
1759
1760 if (adjust_redirect_manager)
1761 {
1762 late = efl_ui_focus_manager_redirect_get(obj);
1763
1764 if (early != late)
1765 {
1766 //this is a new manager, we have to init its case!
1767 if (late)
1768 efl_ui_focus_manager_setup_on_first_touch(late, direction, candidate);
1769 }
1770 }
1771
1772 return candidate;
1773 }
1774
1775 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_efl_ui_focus_manager_root_set(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * root)1776 _efl_ui_focus_manager_calc_efl_ui_focus_manager_root_set(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *root)
1777 {
1778 Node *node;
1779
1780 if (pd->root)
1781 {
1782 ERR("Root element can only be set once!");
1783 return EINA_FALSE;
1784 }
1785
1786 node = _register(obj, pd, root, NULL, NODE_TYPE_ONLY_LOGICAL, NULL);
1787
1788 pd->root = node;
1789 pd->root->this_is_root = 1;
1790
1791 return EINA_TRUE;
1792 }
1793
1794 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_root_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1795 _efl_ui_focus_manager_calc_efl_ui_focus_manager_root_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1796 {
1797 if (!pd->root) return NULL;
1798
1799 return pd->root->focusable;
1800 }
1801
1802 EOLIAN static Efl_Object*
_efl_ui_focus_manager_calc_efl_object_finalize(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)1803 _efl_ui_focus_manager_calc_efl_object_finalize(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
1804 {
1805 Efl_Object *result;
1806
1807 if (!pd->root)
1808 {
1809 ERR("Constructing failed. No root element set.");
1810 return NULL;
1811 }
1812
1813 result = efl_finalize(efl_super(obj, MY_CLASS));
1814
1815 return result;
1816 }
1817
1818 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1819 _efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1820 {
1821 Node *upper = NULL;
1822
1823 if (pd->redirect && pd->redirect_entry) return pd->redirect_entry;
1824
1825 upper = eina_list_last_data_get(pd->focus_stack);
1826
1827 if (!upper)
1828 return NULL;
1829 return upper->focusable;
1830 }
1831
1832 typedef struct _Eina_Iterator_Focusable Eina_Iterator_Focusable;
1833 struct _Eina_Iterator_Focusable
1834 {
1835 Eina_Iterator iterator;
1836
1837 Eina_Iterator *redirect;
1838 };
1839
1840 static Eina_Bool
_node_focusable_iterator_next(Eina_Iterator_Focusable * it,void ** data)1841 _node_focusable_iterator_next(Eina_Iterator_Focusable *it, void **data)
1842 {
1843 Node *node = NULL;
1844 Eina_Bool r;
1845
1846 if (!it->redirect) return EINA_FALSE;
1847
1848 r = eina_iterator_next(it->redirect, (void **) &node);
1849 if (r && data) *data = node->focusable;
1850
1851 return r;
1852 }
1853
1854 static Eina_List *
_node_focusable_iterator_get_container(Eina_Iterator_Focusable * it)1855 _node_focusable_iterator_get_container(Eina_Iterator_Focusable *it)
1856 {
1857 if (!it->redirect) return NULL;
1858
1859 return eina_iterator_container_get(it->redirect);
1860 }
1861
1862 static void
_node_focusable_iterator_free(Eina_Iterator_Focusable * it)1863 _node_focusable_iterator_free(Eina_Iterator_Focusable *it)
1864 {
1865 eina_iterator_free(it->redirect);
1866 EINA_MAGIC_SET(&it->iterator, 0);
1867 free(it);
1868 }
1869
1870 static Eina_Iterator *
_node_focusable_iterator_new(Eina_List * nodes)1871 _node_focusable_iterator_new(Eina_List *nodes)
1872 {
1873 Eina_Iterator_Focusable *it;
1874
1875 it = calloc(1, sizeof (Eina_Iterator_Focusable));
1876 if (!it) return NULL;
1877
1878 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1879 it->redirect = eina_list_iterator_new(nodes);
1880
1881 it->iterator.version = EINA_ITERATOR_VERSION;
1882 it->iterator.next = FUNC_ITERATOR_NEXT(_node_focusable_iterator_next);
1883 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_node_focusable_iterator_get_container);
1884 it->iterator.free = FUNC_ITERATOR_FREE(_node_focusable_iterator_free);
1885
1886 return &it->iterator;
1887 }
1888
1889 EOLIAN static Efl_Ui_Focus_Relations*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_fetch(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child)1890 _efl_ui_focus_manager_calc_efl_ui_focus_manager_fetch(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child)
1891 {
1892 Efl_Ui_Focus_Relations *res;
1893 Node *n, *tmp;
1894
1895 n = node_get(obj, pd, child);
1896 if (!n)
1897 return NULL;
1898
1899 res = calloc(1, sizeof(Efl_Ui_Focus_Relations));
1900
1901 dirty_flush(obj, pd, n);
1902
1903 //make sure to setup_order so next and prev are correctly
1904 if (n->tree.parent)
1905 efl_ui_focus_object_setup_order(n->tree.parent->focusable);
1906 efl_ui_focus_object_setup_order(n->focusable);
1907
1908 // FIXME: the iterator must actually return the (Node*)->focusable object in it
1909 // Just redirect to default eina list iterator but offset the returned pointer?
1910 #define DIR_ITERATOR(dir) _node_focusable_iterator_new(DIRECTION_ACCESS(n,dir).one_direction);
1911
1912 res->right = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_RIGHT);
1913 res->left = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_LEFT);
1914 res->top = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_UP);
1915 res->down = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_DOWN);
1916 res->next = (tmp = _next(n)) ? tmp->focusable : NULL;
1917 res->prev = (tmp = _prev(n)) ? tmp->focusable : NULL;
1918 res->position_in_history = eina_list_data_idx(pd->focus_stack, n);
1919 res->node = child;
1920
1921 res->logical = (n->type == NODE_TYPE_ONLY_LOGICAL);
1922
1923 if (T(n).parent)
1924 res->parent = T(n).parent->focusable;
1925 res->redirect = n->redirect_manager;
1926 #undef DIR_ITERATOR
1927
1928 return res;
1929 }
1930
1931 EOLIAN static void
_efl_ui_focus_manager_calc_class_constructor(Efl_Class * c EINA_UNUSED)1932 _efl_ui_focus_manager_calc_class_constructor(Efl_Class *c EINA_UNUSED)
1933 {
1934 const char *choice, *tmp;
1935
1936 #ifdef EINA_DEFAULT_MEMPOOL
1937 choice = "pass_through";
1938 #else
1939 choice = "chained_mempool";
1940 #endif
1941 tmp = getenv("EINA_MEMPOOL");
1942 if (tmp && tmp[0])
1943 choice = tmp;
1944
1945 _node_mempool = eina_mempool_add
1946 (choice, "Focus-Node mempool", NULL, sizeof(Node), 20);
1947
1948 _focus_log_domain = eina_log_domain_register("elementary-focus", EINA_COLOR_CYAN);
1949 }
1950
1951 EOLIAN static void
_efl_ui_focus_manager_calc_class_destructor(Efl_Class * c EINA_UNUSED)1952 _efl_ui_focus_manager_calc_class_destructor(Efl_Class *c EINA_UNUSED)
1953 {
1954 eina_log_domain_unregister(_focus_log_domain);
1955 _focus_log_domain = -1;
1956 }
1957
1958 EOLIAN static Efl_Ui_Focus_Manager_Logical_End_Detail
_efl_ui_focus_manager_calc_efl_ui_focus_manager_logical_end(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1959 _efl_ui_focus_manager_calc_efl_ui_focus_manager_logical_end(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1960 {
1961 Node *child = pd->root;
1962 Efl_Ui_Focus_Manager_Logical_End_Detail ret = { 0, NULL};
1963 EINA_SAFETY_ON_NULL_RETURN_VAL(child, ret);
1964
1965 //we need to return the most lower right element
1966
1967 while ((child) && (T(child).children) && (!child->redirect_manager))
1968 child = eina_list_last_data_get(T(child).children);
1969 while ((child) && (child->type != NODE_TYPE_NORMAL) && (!child->redirect_manager))
1970 child = _prev(child);
1971
1972 if (child)
1973 {
1974 ret.is_regular_end = child->type == NODE_TYPE_NORMAL;
1975 ret.element = child->focusable;
1976 }
1977 return ret;
1978 }
1979
1980 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_reset_history(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1981 _efl_ui_focus_manager_calc_efl_ui_focus_manager_reset_history(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1982 {
1983 Efl_Ui_Focus_Object *last_focusable;
1984
1985 if (!pd->focus_stack) return;
1986
1987 last_focusable = _focus_stack_unfocus_last(pd);
1988
1989 pd->focus_stack = eina_list_free(pd->focus_stack);
1990
1991 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, last_focusable);
1992 }
1993
1994 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_pop_history_stack(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1995 _efl_ui_focus_manager_calc_efl_ui_focus_manager_pop_history_stack(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1996 {
1997 Efl_Ui_Focus_Object *last_focusable;
1998 Node *last = NULL;
1999
2000 if (pd->redirect)
2001 {
2002 Eina_List *n;
2003
2004 last_focusable = pd->redirect_entry;
2005 n = eina_list_last(pd->focus_stack);
2006
2007 while (n && (eina_list_data_get(n) != last_focusable))
2008 {
2009 n = eina_list_prev(n);
2010 }
2011
2012 last = eina_list_data_get(n);
2013 }
2014 else
2015 {
2016 last_focusable = _focus_stack_unfocus_last(pd);
2017 //get now the highest, and unfocus that!
2018 last = eina_list_last_data_get(pd->focus_stack);
2019 }
2020
2021 if (last)
2022 {
2023 efl_ui_focus_object_focus_set(last->focusable, EINA_TRUE);
2024 }
2025 else
2026 {
2027 last = _request_subchild_except(pd->root, last_focusable);
2028 if (last)
2029 efl_ui_focus_manager_focus_set(obj, last->focusable);
2030 }
2031
2032 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, last_focusable);
2033 }
2034
2035 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_request_subchild(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child_obj)2036 _efl_ui_focus_manager_calc_efl_ui_focus_manager_request_subchild(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child_obj)
2037 {
2038 Node *child, *target;
2039
2040 child = node_get(obj, pd, child_obj);
2041 if (!child) return NULL;
2042
2043 target = _request_subchild(child);
2044
2045 if (target) return target->focusable;
2046 return NULL;
2047 }
2048
2049 EOLIAN static void
_efl_ui_focus_manager_calc_efl_object_dbg_info_get(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Dbg_Info * root)2050 _efl_ui_focus_manager_calc_efl_object_dbg_info_get(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Dbg_Info *root)
2051 {
2052 efl_dbg_info_get(efl_super(obj, MY_CLASS), root);
2053 Efl_Dbg_Info *append, *group = EFL_DBG_INFO_LIST_APPEND(root, "Efl.Ui.Focus.Manager");
2054 Eina_Iterator *iter;
2055 Node *node;
2056
2057 append = EFL_DBG_INFO_LIST_APPEND(group, "children");
2058
2059 iter = eina_hash_iterator_data_new(pd->node_hash);
2060 EINA_ITERATOR_FOREACH(iter, node)
2061 {
2062 EFL_DBG_INFO_APPEND(append, "-", EINA_VALUE_TYPE_UINT64, node->focusable);
2063 }
2064 eina_iterator_free(iter);
2065 }
2066
2067 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_freeze(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)2068 _efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_freeze(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
2069 {
2070 pd->freeze ++;
2071 if (pd->freeze == 1)
2072 {
2073 Eina_Bool event_info = EINA_TRUE;
2074 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_DIRTY_LOGIC_FREEZE_CHANGED, &event_info);
2075 }
2076
2077 }
2078
2079 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_unfreeze(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)2080 _efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_unfreeze(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
2081 {
2082 pd->freeze --;
2083 if (!pd->freeze)
2084 {
2085 Eina_Bool event_info = EINA_FALSE;
2086 efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_DIRTY_LOGIC_FREEZE_CHANGED, &event_info);
2087 }
2088 }
2089
2090 static void
_efl_ui_focus_manager_calc_update_children_ownership_fallback(Efl_Ui_Focus_Object * parent,Eina_List * children)2091 _efl_ui_focus_manager_calc_update_children_ownership_fallback(Efl_Ui_Focus_Object *parent, Eina_List *children)
2092 {
2093 (void)parent;
2094 eina_list_free(children);
2095 }
2096
2097 EOAPI EFL_FUNC_BODYV_FALLBACK(efl_ui_focus_manager_calc_update_children, Eina_Bool, 0, _efl_ui_focus_manager_calc_update_children_ownership_fallback(parent, children);, EFL_FUNC_CALL(parent, children), Efl_Ui_Focus_Object *parent, Eina_List *children);
2098
2099 static void
_efl_ui_focus_manager_calc_update_order_ownership_fallback(Efl_Ui_Focus_Object * parent,Eina_List * children)2100 _efl_ui_focus_manager_calc_update_order_ownership_fallback(Efl_Ui_Focus_Object *parent, Eina_List *children)
2101 {
2102 (void)parent;
2103 eina_list_free(children);
2104 }
2105
2106 EOAPI EFL_VOID_FUNC_BODYV_FALLBACK(efl_ui_focus_manager_calc_update_order, _efl_ui_focus_manager_calc_update_order_ownership_fallback(parent, children);, EFL_FUNC_CALL(parent, children), Efl_Ui_Focus_Object *parent, Eina_List *children);
2107
2108 #define EFL_UI_FOCUS_MANAGER_CALC_EXTRA_OPS \
2109 EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_ui_focus_manager_calc_efl_object_dbg_info_get), \
2110 EFL_OBJECT_OP_FUNC(efl_ui_focus_manager_calc_update_children, _efl_ui_focus_manager_calc_update_children), \
2111 EFL_OBJECT_OP_FUNC(efl_ui_focus_manager_calc_update_order, _efl_ui_focus_manager_calc_update_order)
2112
2113 #include "efl_ui_focus_manager_calc.eo.c"
2114