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