1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #include <Elementary.h>
6 #include "elm_priv.h"
7 
8 //we need those for legacy compatible code
9 #include "elm_genlist_eo.h"
10 #include "elm_gengrid_eo.h"
11 
12 #define API_ENTRY()\
13    EINA_SAFETY_ON_NULL_RETURN(obj); \
14    EINA_SAFETY_ON_FALSE_RETURN(efl_isa(obj, EFL_UI_WIDGET_CLASS)); \
15    ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd); \
16    EINA_SAFETY_ON_FALSE_RETURN(elm_widget_is_legacy(obj));
17 
18 #define API_ENTRY_VAL(val)\
19    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, val); \
20    EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(obj, EFL_UI_WIDGET_CLASS), val); \
21    ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, val); \
22    EINA_SAFETY_ON_FALSE_RETURN_VAL(elm_widget_is_legacy(obj), val);
23 
24 #define MARK_WINDOW_LEGACY_USAGE() \
25    if (pd->shared_win_data) \
26      ((Efl_Ui_Shared_Win_Data*)pd->shared_win_data)->legacy_focus_api_used = EINA_TRUE;
27 
28 #define MAPPING() \
29         MAP(PREVIOUS, prev) \
30         MAP(NEXT, next) \
31         MAP(UP, up) \
32         MAP(DOWN, down) \
33         MAP(LEFT, left) \
34         MAP(RIGHT, right)
35 
36 
37 
38 static Eina_List*
_custom_chain_get(const Efl_Ui_Widget * node)39 _custom_chain_get(const Efl_Ui_Widget *node)
40 {
41    ELM_WIDGET_DATA_GET_OR_RETURN(node, pd, NULL);
42 
43    return pd->legacy_focus.custom_chain;
44 }
45 
46 static void
_flush_manager(Efl_Ui_Widget * obj,Elm_Widget_Smart_Data * pd)47 _flush_manager(Efl_Ui_Widget *obj, Elm_Widget_Smart_Data *pd)
48 {
49    Efl_Ui_Focus_Manager *manager;
50 
51    manager = efl_ui_focus_object_focus_manager_get(obj);
52    if (manager)
53      {
54         Eina_List *order = NULL;
55 
56         if (pd->legacy_focus.custom_chain)
57           order = eina_list_clone(pd->legacy_focus.custom_chain);
58         else
59           {
60              for (unsigned int i = 0; i < eina_array_count(pd->children); ++i)
61                {
62                   Eo *sobj = eina_array_data_get(pd->children, i);
63                   order = eina_list_append(order, sobj);
64                }
65           }
66 
67         efl_ui_focus_manager_calc_update_order(manager, obj, order);
68      }
69 }
70 
71 static void
_manager_changed(void * data EINA_UNUSED,const Efl_Event * ev)72 _manager_changed(void *data EINA_UNUSED, const Efl_Event *ev)
73 {
74    ELM_WIDGET_DATA_GET_OR_RETURN(ev->object, pd);
75 
76    _flush_manager(ev->object, pd);
77 }
78 
79 static void
_custom_chain_set(Efl_Ui_Widget * node,Eina_List * lst)80 _custom_chain_set(Efl_Ui_Widget *node, Eina_List *lst)
81 {
82    ELM_WIDGET_DATA_GET_OR_RETURN(node, pd);
83    Efl_Ui_Widget *list_item;
84    Eina_List *n;
85 
86    pd->legacy_focus.custom_chain = eina_list_free(pd->legacy_focus.custom_chain);
87    pd->legacy_focus.custom_chain = lst;
88 
89    EINA_LIST_FOREACH(pd->legacy_focus.custom_chain, n, list_item)
90      {
91         EINA_SAFETY_ON_FALSE_RETURN(efl_isa(list_item, EFL_UI_WIDGET_CLASS));
92         EINA_SAFETY_ON_FALSE_RETURN(efl_ui_widget_parent_get(list_item) == node);
93      }
94 
95    _elm_widget_full_eval_children(node, pd);
96 
97    if (pd->legacy_focus.custom_chain && !pd->legacy_focus.listen_to_manager)
98      {
99         efl_event_callback_add(node, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_MANAGER_CHANGED, _manager_changed, NULL);
100         pd->legacy_focus.listen_to_manager = EINA_TRUE;
101      }
102    else if (!pd->legacy_focus.custom_chain && pd->legacy_focus.listen_to_manager)
103      {
104         efl_event_callback_del(node, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_MANAGER_CHANGED, _manager_changed, NULL);
105         pd->legacy_focus.listen_to_manager = EINA_FALSE;
106      }
107 
108    _flush_manager(node, pd);
109 }
110 
111 EAPI void
elm_object_focus_next_object_set(Evas_Object * obj,Evas_Object * next,Elm_Focus_Direction dir)112 elm_object_focus_next_object_set(Evas_Object        *obj,
113                                  Evas_Object        *next,
114                                  Elm_Focus_Direction dir)
115 {
116    API_ENTRY()
117    EINA_SAFETY_ON_FALSE_RETURN(efl_isa(next, EFL_UI_WIDGET_CLASS));
118    ELM_WIDGET_DATA_GET_OR_RETURN(next, next_pd);
119    MARK_WINDOW_LEGACY_USAGE()
120 
121    #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction) pd->legacy_focus.field = next;
122    MAPPING()
123    #undef MAP
124    dir = (Elm_Focus_Direction)efl_ui_focus_util_direction_complement((Efl_Ui_Focus_Direction)dir);
125    #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction) next_pd->legacy_focus.field = obj;
126    MAPPING()
127    #undef MAP
128 }
129 
130 EAPI void
elm_object_focus_custom_chain_set(Evas_Object * obj,Eina_List * objs)131 elm_object_focus_custom_chain_set(Evas_Object *obj,
132                                   Eina_List   *objs)
133 {
134    API_ENTRY()
135    MARK_WINDOW_LEGACY_USAGE()
136 
137    _custom_chain_set(obj, objs);
138 }
139 
140 EAPI void
elm_object_focus_custom_chain_unset(Evas_Object * obj)141 elm_object_focus_custom_chain_unset(Evas_Object *obj)
142 {
143    API_ENTRY()
144    MARK_WINDOW_LEGACY_USAGE()
145 
146    _custom_chain_set(obj, NULL);
147 }
148 
149 EAPI const Eina_List *
elm_object_focus_custom_chain_get(const Evas_Object * obj)150 elm_object_focus_custom_chain_get(const Evas_Object *obj)
151 {
152    API_ENTRY_VAL(NULL)
153 
154    return _custom_chain_get(obj);
155 }
156 
157 EAPI void
elm_object_focus_custom_chain_append(Evas_Object * obj,Evas_Object * child,Evas_Object * relative_child)158 elm_object_focus_custom_chain_append(Evas_Object *obj,
159                                      Evas_Object *child,
160                                      Evas_Object *relative_child)
161 {
162    API_ENTRY()
163    MARK_WINDOW_LEGACY_USAGE()
164    Eina_List *tmp;
165 
166    tmp = eina_list_clone(pd->legacy_focus.custom_chain);
167    tmp = eina_list_append_relative(tmp, child, relative_child);
168    _custom_chain_set(obj, tmp);
169 }
170 
171 EAPI void
elm_object_focus_custom_chain_prepend(Evas_Object * obj,Evas_Object * child,Evas_Object * relative_child)172 elm_object_focus_custom_chain_prepend(Evas_Object *obj,
173                                       Evas_Object *child,
174                                       Evas_Object *relative_child)
175 {
176    API_ENTRY()
177    MARK_WINDOW_LEGACY_USAGE()
178    Eina_List *tmp;
179 
180    tmp = eina_list_clone(pd->legacy_focus.custom_chain);
181    tmp = eina_list_prepend_relative(tmp, child, relative_child);
182    _custom_chain_set(obj, tmp);
183 }
184 
185 EINA_DEPRECATED EAPI void
elm_object_focus_cycle(Evas_Object * obj,Elm_Focus_Direction dir)186 elm_object_focus_cycle(Evas_Object        *obj,
187                        Elm_Focus_Direction dir)
188 {
189    elm_object_focus_next(obj, dir);
190 }
191 
192 static Evas_Object*
_get_legacy_target(EINA_UNUSED Evas_Object * eo,Elm_Widget_Smart_Data * pd,Elm_Focus_Direction dir)193 _get_legacy_target(EINA_UNUSED Evas_Object *eo, Elm_Widget_Smart_Data *pd, Elm_Focus_Direction dir)
194 {
195    Evas_Object *result = NULL;
196 
197    #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction && pd->legacy_focus.item_ ##field) result = elm_object_item_widget_get(pd->legacy_focus.item_ ##field);
198    MAPPING()
199    #undef MAP
200 
201    if (!result)
202      {
203         #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction && pd->legacy_focus.field) result = pd->legacy_focus.field;
204         MAPPING()
205         #undef MAP
206      }
207 
208    return result;
209 }
210 
211 static Eina_Array*
_focus_parent_chain_gen(Efl_Ui_Focus_Object * obj)212 _focus_parent_chain_gen(Efl_Ui_Focus_Object *obj)
213 {
214    Eina_Array *result = eina_array_new(5);
215 
216    for (Eo *parent = obj; parent; parent = efl_ui_focus_object_focus_parent_get(parent))
217      {
218         eina_array_push(result, parent);
219      }
220 
221    return result;
222 }
223 
224 EAPI void
elm_object_focus_next(Evas_Object * obj,Elm_Focus_Direction dir)225 elm_object_focus_next(Evas_Object        *obj,
226                       Elm_Focus_Direction dir)
227 {
228    Eina_Bool legacy_focus_move = EINA_FALSE;
229    Efl_Ui_Widget *o = NULL, *top;
230    Efl_Ui_Focus_Object *logical;
231    Efl_Ui_Focus_Manager *manager_top;
232    API_ENTRY()
233 
234    top = elm_object_top_widget_get(obj);
235    EINA_SAFETY_ON_FALSE_RETURN(efl_isa(top, EFL_UI_WIN_CLASS));
236 
237    manager_top = efl_ui_focus_util_active_manager(obj);
238    logical = efl_ui_focus_manager_focus_get(manager_top);
239 
240    if (elm_widget_is(logical))
241      {
242         Efl_Ui_Focus_Object *legacy_target = NULL;
243         ELM_WIDGET_DATA_GET_OR_RETURN(logical, pd_logical);
244 
245         legacy_target = _get_legacy_target(obj, pd_logical, dir);
246 
247         if (!legacy_target)
248           {
249              Eina_Array *old_chain = _focus_parent_chain_gen(logical);
250              Eina_Array *new_chain = _focus_parent_chain_gen(efl_ui_focus_manager_request_move(top, (Efl_Ui_Focus_Direction)dir, NULL, EINA_FALSE));
251 
252              //first pop off all elements that are the same
253              while (eina_array_count(new_chain) > 0 && eina_array_count(old_chain) > 0 &&
254                     eina_array_data_get(new_chain, (int)eina_array_count(new_chain) -1) == eina_array_data_get(old_chain, (int)eina_array_count(old_chain) - 1))
255                {
256                   eina_array_pop(new_chain);
257                   eina_array_pop(old_chain);
258                }
259 
260              for (unsigned int i = 0; i < eina_array_count(old_chain); ++i)
261                {
262                   Evas_Object *parent = eina_array_data_get(old_chain, i);
263                   if (!elm_widget_is(parent)) continue;
264                   ELM_WIDGET_DATA_GET(parent, ppd);
265                   if (!ppd)
266                     {
267                        ERR("Failed to get Elm widget data for parent");
268                        break;
269                     }
270                   legacy_target = _get_legacy_target(parent, ppd, dir);
271                   if (legacy_target) break;
272                }
273              eina_array_free(new_chain);
274              eina_array_free(old_chain);
275           }
276 
277         if (legacy_target)
278           {
279              efl_ui_focus_util_focus(legacy_target);
280              if (elm_object_focused_object_get(top) == legacy_target)
281                {
282                   legacy_focus_move = EINA_TRUE;
283                   o = legacy_target;
284                }
285           }
286      }
287 
288    if (!legacy_focus_move)
289      o = efl_ui_focus_manager_move(top, (Efl_Ui_Focus_Direction)dir);
290    if (!o)
291      {
292         if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_NEXT || (Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_PREVIOUS)
293           {
294              Efl_Ui_Focus_Object *root;
295 
296              root = efl_ui_focus_manager_root_get(top);
297              efl_ui_focus_manager_setup_on_first_touch(top, (Efl_Ui_Focus_Direction)dir, root);
298           }
299      }
300 }
301 
302 EAPI Evas_Object *
elm_object_focus_next_object_get(const Evas_Object * obj,Elm_Focus_Direction dir)303 elm_object_focus_next_object_get(const Evas_Object  *obj,
304                                  Elm_Focus_Direction dir)
305 {
306    Efl_Ui_Widget *top = elm_object_top_widget_get(obj);
307    API_ENTRY_VAL(NULL)
308 
309    #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction && pd->legacy_focus.field) return pd->legacy_focus.field;
310    MAPPING()
311    #undef MAP
312 
313    return efl_ui_focus_manager_request_move(efl_ui_focus_util_active_manager(top), (Efl_Ui_Focus_Direction)dir, NULL, EINA_FALSE);
314 }
315 
316 EAPI Elm_Object_Item *
elm_object_focus_next_item_get(const Evas_Object * obj,Elm_Focus_Direction dir EINA_UNUSED)317 elm_object_focus_next_item_get(const Evas_Object  *obj,
318                                Elm_Focus_Direction dir EINA_UNUSED)
319 {
320    API_ENTRY_VAL(NULL)
321    MARK_WINDOW_LEGACY_USAGE()
322 
323    #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction && pd->legacy_focus.item_ ##field) return pd->legacy_focus.item_ ##field;
324    MAPPING()
325    #undef MAP
326 
327    return NULL;
328 }
329 
330 EAPI void
elm_object_focus_next_item_set(Evas_Object * obj,Elm_Object_Item * next_item EINA_UNUSED,Elm_Focus_Direction dir EINA_UNUSED)331 elm_object_focus_next_item_set(Evas_Object     *obj,
332                                Elm_Object_Item *next_item EINA_UNUSED,
333                                Elm_Focus_Direction dir EINA_UNUSED)
334 {
335    API_ENTRY()
336    MARK_WINDOW_LEGACY_USAGE()
337 
338    #define MAP(direction, field)  if ((Efl_Ui_Focus_Direction)dir == EFL_UI_FOCUS_DIRECTION_ ##direction) pd->legacy_focus.item_ ##field = next_item;
339    MAPPING()
340    #undef MAP
341 }
342 
343 EAPI Evas_Object *
elm_object_focused_object_get(const Evas_Object * obj)344 elm_object_focused_object_get(const Evas_Object *obj)
345 {
346    API_ENTRY_VAL(NULL)
347    Efl_Ui_Focus_Manager *man = elm_object_top_widget_get(obj);
348 
349    while(efl_ui_focus_manager_redirect_get(man))
350      {
351         man = efl_ui_focus_manager_redirect_get(man);
352 
353         // legacy compatible code, earlier those containers have not exposed theire items
354         if (efl_isa(man, ELM_GENGRID_CLASS) ||
355             efl_isa(man, ELM_TOOLBAR_CLASS) ||
356             efl_isa(man, ELM_GENLIST_CLASS)) return man;
357      }
358 
359    return efl_ui_focus_manager_focus_get(man);
360 }
361 
362 EAPI Eina_Bool
elm_object_focus_get(const Evas_Object * obj)363 elm_object_focus_get(const Evas_Object *obj)
364 {
365    API_ENTRY_VAL(EINA_FALSE)
366 
367    if (!elm_widget_is(obj))
368      return evas_object_focus_get(obj);
369 
370    return _elm_widget_top_win_focused_get(obj) && (efl_ui_focus_object_child_focus_get(obj) | efl_ui_focus_object_focus_get(obj));
371 }
372 
373 EAPI void
elm_object_focus_set(Evas_Object * obj,Eina_Bool focus)374 elm_object_focus_set(Evas_Object *obj,
375                      Eina_Bool    focus)
376 {
377    // ugly, but, special case for inlined windows
378    if (efl_isa(obj, EFL_UI_WIN_CLASS))
379      {
380         Evas_Object *inlined = elm_win_inlined_image_object_get(obj);
381         if (inlined)
382           {
383              evas_object_focus_set(inlined, focus);
384              return;
385           }
386      }
387    else if (elm_widget_is(obj))
388      {
389         if (focus)
390           efl_ui_focus_util_focus(obj);
391         else
392           {
393              if (efl_ui_focus_manager_focus_get(efl_ui_focus_object_focus_manager_get(obj)) == obj)
394                efl_ui_focus_manager_pop_history_stack(efl_ui_focus_object_focus_manager_get(obj));
395           }
396      }
397    else
398      {
399         evas_object_focus_set(obj, focus);
400      }
401 }
402 
403 //legacy helpers that are used in code
404 typedef struct {
405   Eina_Bool focused;
406   Eo *emittee;
407 } Legacy_Manager_Focus_State;
408 
409 static void
_focus_manager_focused(void * data,const Efl_Event * ev)410 _focus_manager_focused(void *data, const Efl_Event *ev)
411 {
412    Legacy_Manager_Focus_State *state = data;
413    Eina_Bool currently_focused = !!efl_ui_focus_manager_focus_get(ev->object);
414 
415    if (currently_focused == state->focused) return;
416 
417    if (currently_focused)
418      evas_object_smart_callback_call(state->emittee, "focused", NULL);
419    else
420      evas_object_smart_callback_call(state->emittee, "unfocused", NULL);
421 
422    state->focused = currently_focused;
423 }
424 
425 static void
_focus_manager_del(void * data,const Efl_Event * ev EINA_UNUSED)426 _focus_manager_del(void *data, const Efl_Event *ev EINA_UNUSED)
427 {
428    free(data);
429 }
430 
431 void
legacy_efl_ui_focus_manager_widget_legacy_signals(Efl_Ui_Focus_Manager * manager,Efl_Ui_Focus_Manager * emittee)432 legacy_efl_ui_focus_manager_widget_legacy_signals(Efl_Ui_Focus_Manager *manager, Efl_Ui_Focus_Manager *emittee)
433 {
434    Legacy_Manager_Focus_State *state = calloc(1, sizeof(Legacy_Manager_Focus_State));
435 
436    state->emittee = emittee;
437    state->focused = EINA_FALSE;
438 
439    efl_event_callback_add(manager, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, _focus_manager_focused, state);
440    efl_event_callback_add(manager, EFL_EVENT_DEL, _focus_manager_del, state);
441 }
442 
443 typedef struct {
444   Eina_Bool focused;
445   Efl_Ui_Focus_Manager *registered_manager;
446   Eo *emittee;
447 } Legacy_Object_Focus_State;
448 
449 static void
_manager_focus_changed(void * data,const Efl_Event * ev EINA_UNUSED)450 _manager_focus_changed(void *data, const Efl_Event *ev EINA_UNUSED)
451 {
452    Legacy_Object_Focus_State *state = data;
453    Eina_Bool currently_focused = efl_ui_focus_object_child_focus_get(state->emittee);
454 
455    if (currently_focused == state->focused) return;
456 
457    if (currently_focused)
458      evas_object_smart_callback_call(state->emittee, "focused", NULL);
459    else
460      evas_object_smart_callback_call(state->emittee, "unfocused", NULL);
461    state->focused = currently_focused;
462 }
463 
464 static void
_manager_focus_object_changed(void * data,const Efl_Event * ev EINA_UNUSED)465 _manager_focus_object_changed(void *data, const Efl_Event *ev EINA_UNUSED)
466 {
467    Legacy_Object_Focus_State *state = data;
468    if (state->registered_manager)
469      efl_event_callback_del(state->registered_manager, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, _manager_focus_changed, state);
470    state->registered_manager = efl_ui_focus_object_focus_manager_get(state->emittee);
471    if (state->registered_manager)
472      efl_event_callback_add(state->registered_manager, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, _manager_focus_changed, state);
473 }
474 
475 void
legacy_child_focus_handle(Efl_Ui_Focus_Object * object)476 legacy_child_focus_handle(Efl_Ui_Focus_Object *object)
477 {
478    Legacy_Object_Focus_State *state = calloc(1, sizeof(Legacy_Object_Focus_State));
479    state->emittee = object;
480 
481    efl_event_callback_add(object, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_MANAGER_CHANGED, _manager_focus_object_changed, state);
482    efl_event_callback_add(object, EFL_EVENT_DEL, _focus_manager_del, state);
483 }
484 
485 static void
_focus_event_changed(void * data EINA_UNUSED,const Efl_Event * event)486 _focus_event_changed(void *data EINA_UNUSED, const Efl_Event *event)
487 {
488    if (efl_ui_focus_object_focus_get(event->object))
489      evas_object_smart_callback_call(event->object, "focused", NULL);
490    else
491      evas_object_smart_callback_call(event->object, "unfocused", NULL);
492 }
493 
494 void
legacy_object_focus_handle(Efl_Ui_Focus_Object * object)495 legacy_object_focus_handle(Efl_Ui_Focus_Object *object)
496 {
497    efl_event_callback_add(object, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _focus_event_changed, NULL);
498 }
499