40bd3e6947a000f00a6a7f4889eeb9bcc65211e9
[platform/upstream/efl.git] / src / lib / elementary / elc_hoversel.c
1    #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #define EFL_ACCESS_OBJECT_PROTECTED
6 #define EFL_ACCESS_WIDGET_ACTION_PROTECTED
7 #define ELM_WIDGET_PROTECTED
8 #define ELM_WIDGET_ITEM_PROTECTED
9 #define EFL_UI_L10N_PROTECTED
10 #define EFL_ACCESS_COMPONENT_PROTECTED
11
12 #include <Elementary.h>
13 #include "elm_priv.h"
14 #include "elm_hoversel_eo.h"
15 #include "elm_hoversel_item_eo.h"
16 #include "elm_widget_hoversel.h"
17 #include "efl_ui_button_legacy_eo.h"
18
19 #define MY_CLASS ELM_HOVERSEL_CLASS
20
21 #define MY_CLASS_NAME "Elm_Hoversel"
22 #define MY_CLASS_NAME_LEGACY "elm_hoversel"
23
24 static const char SIG_SELECTED[] = "selected";
25 static const char SIG_DISMISSED[] = "dismissed";
26 static const char SIG_EXPANDED[] = "expanded";
27 static const char SIG_ITEM_FOCUSED[] = "item,focused";
28 static const char SIG_ITEM_UNFOCUSED[] = "item,unfocused";
29 static const char SIG_CLICKED[] = "clicked";
30
31 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
32    {SIG_SELECTED, ""},
33    {SIG_DISMISSED, ""},
34    {SIG_EXPANDED, ""},
35    {SIG_ITEM_FOCUSED, ""},
36    {SIG_ITEM_UNFOCUSED, ""},
37    {SIG_CLICKED, ""}, /**< handled by parent button class */
38    {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */
39    {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */
40    {NULL, NULL}
41 };
42
43 static Eina_Bool _key_action_move(Evas_Object *obj, const char *params);
44 static Eina_Bool _key_action_activate(Evas_Object *obj, const char *params);
45 static Eina_Bool _key_action_escape(Evas_Object *obj, const char *params);
46 static Eina_Bool _hoversel_efl_ui_widget_widget_input_event_handler(Eo *obj, Elm_Hoversel_Data *_pd EINA_UNUSED, const Efl_Event *eo_event, Evas_Object *src EINA_UNUSED);
47
48 static const Elm_Action key_actions[] = {
49    {"move", _key_action_move},
50    {"activate", _key_action_activate},
51    {"escape", _key_action_escape},
52    {NULL, NULL}
53 };
54
55 EOLIAN static void
56 _elm_hoversel_efl_ui_l10n_translation_update(Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
57 {
58    Eo *it;
59    Eina_List *l;
60
61    EINA_LIST_FOREACH(sd->items, l, it)
62      elm_wdg_item_translate(it);
63
64    efl_ui_l10n_translation_update(efl_super(obj, MY_CLASS));
65 }
66
67 EOLIAN static Eina_Error
68 _elm_hoversel_efl_ui_widget_theme_apply(Eo *obj, Elm_Hoversel_Data *sd)
69 {
70    Eina_Error int_ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
71    Eina_List *l;
72    Elm_Object_Item *eo_item;
73
74    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC);
75
76    char buf[4096];
77    const char *style;
78
79    style = eina_stringshare_add(elm_widget_style_get(obj));
80
81    if (sd->horizontal)
82      snprintf(buf, sizeof(buf), "hoversel_horizontal/%s", style);
83    else
84      snprintf(buf, sizeof(buf), "hoversel_vertical/%s", style);
85
86    /* hoversel's style has an extra bit: orientation */
87    elm_widget_theme_style_set(obj, buf);
88
89    int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
90    if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;
91
92    elm_widget_theme_style_set(obj, style);
93
94    if (sd->hover)
95      efl_ui_mirrored_set(sd->hover, efl_ui_mirrored_get(obj));
96
97    if (sd->horizontal)
98      snprintf(buf, sizeof(buf), "hoversel_horizontal_entry/%s", style);
99    else
100      snprintf(buf, sizeof(buf), "hoversel_vertical_entry/%s", style);
101
102    EINA_LIST_FOREACH(sd->items, l, eo_item)
103      {
104         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
105         elm_object_style_set(VIEW(item), buf);
106         elm_object_text_set(VIEW(item), item->label);
107         elm_widget_disabled_set
108           (VIEW(item), elm_wdg_item_disabled_get(eo_item));
109      }
110
111    eina_stringshare_del(style);
112    elm_hoversel_hover_end(obj);
113
114    return int_ret;
115 }
116
117 static void
118 _on_hover_clicked(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
119 {
120    const char *dismissstr;
121
122    dismissstr = elm_layout_data_get(obj, "dismiss");
123
124    if (!dismissstr || strcmp(dismissstr, "on"))
125      elm_hoversel_hover_end(data); // for backward compatibility
126    // TIZEN_ONLY(20160812): emit dismiss signal to all items for fade out effect
127    else if (dismissstr && !strcmp(dismissstr, "on"))
128      {
129         Eina_List *l;
130         Elm_Object_Item *eo_item;
131
132         ELM_HOVERSEL_DATA_GET(data, sd);
133         EINA_LIST_FOREACH(sd->items, l, eo_item)
134           {
135              ELM_HOVERSEL_ITEM_DATA_GET(eo_item, it);
136              elm_layout_signal_emit(VIEW(it), "elm,action,dismiss", "elm");
137           }
138      }
139    //
140 }
141
142 static void
143 _auto_update(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
144 {
145    Elm_Hoversel_Item_Data *item = data;
146    Evas_Object *obj2 = WIDGET(item);
147
148    ELM_HOVERSEL_DATA_GET(obj2, sd);
149
150    if (sd->auto_update)
151      {
152         Evas_Object *ic;
153
154         ic = elm_object_part_content_unset(obj2, "icon");
155         ELM_SAFE_FREE(ic, evas_object_del);
156
157         if (item->icon_file)
158           {
159              ic = elm_icon_add(obj2);
160              elm_image_resizable_set(ic, EINA_FALSE, EINA_TRUE);
161              if (item->icon_type == ELM_ICON_FILE)
162                elm_image_file_set(ic, item->icon_file, item->icon_group);
163              else if (item->icon_type == ELM_ICON_STANDARD)
164                elm_icon_standard_set(ic, item->icon_file);
165              elm_object_part_content_set(obj2, "icon", ic);
166           }
167
168         if(item->label)
169           elm_object_text_set(obj2, item->label);
170
171         Eina_List *l;
172         Elm_Object_Item *eo_item;
173
174         EINA_LIST_FOREACH(sd->items, l, eo_item)
175           {
176              if (eo_item == EO_OBJ(item))
177                elm_wdg_item_signal_emit(eo_item, "elm,state,selected", "elm");
178              else
179                elm_wdg_item_signal_emit(eo_item, "elm,state,unselected", "elm");
180           }
181      }
182 }
183
184 static void
185 _on_item_clicked(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
186 {
187    Elm_Hoversel_Item_Data *item = data;
188    Evas_Object *obj2 = WIDGET(item);
189    Elm_Object_Item *eo_it = EO_OBJ(item);
190
191    ELM_HOVERSEL_DATA_GET(obj2, sd);
192
193    if (item->func) item->func((void *)WIDGET_ITEM_DATA_GET(eo_it), obj2, eo_it);
194    evas_object_smart_callback_call(obj2, "selected", eo_it);
195
196    evas_object_event_callback_add(sd->hover, EVAS_CALLBACK_DEL, _auto_update, item);
197
198    elm_hoversel_hover_end(obj2);
199 }
200
201 static void
202 _item_focus_changed(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
203 {
204    Elm_Hoversel_Item_Data *it = data;
205
206    if (efl_ui_focus_object_focus_get(event->object))
207      {
208         efl_event_callback_legacy_call(WIDGET(it), ELM_HOVERSEL_EVENT_ITEM_FOCUSED, EO_OBJ(it));
209      }
210    else
211      {
212         efl_event_callback_legacy_call(WIDGET(it), ELM_HOVERSEL_EVENT_ITEM_UNFOCUSED, EO_OBJ(it));
213      }
214 }
215
216 //TIZEN_ONLY(20180607): Restore legacy focus
217 static void
218 _item_focused_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
219 {
220    Elm_Hoversel_Item_Data *it = data;
221
222    efl_event_callback_legacy_call(WIDGET(it), ELM_HOVERSEL_EVENT_ITEM_FOCUSED, EO_OBJ(it));
223 }
224
225 static void
226 _item_unfocused_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
227
228 {
229    Elm_Hoversel_Item_Data *it = data;
230
231    efl_event_callback_legacy_call(WIDGET(it), ELM_HOVERSEL_EVENT_ITEM_UNFOCUSED, EO_OBJ(it));
232 }
233 //
234
235 static void
236 _create_scroller(Evas_Object *obj, Elm_Hoversel_Data *sd)
237 {
238    //table
239    sd->tbl = elm_table_add(obj);
240    evas_object_size_hint_align_set(sd->tbl, EVAS_HINT_FILL, EVAS_HINT_FILL);
241    evas_object_size_hint_weight_set(sd->tbl, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
242
243    //spacer
244    sd->spacer = evas_object_rectangle_add(evas_object_evas_get(obj));
245    evas_object_size_hint_align_set(sd->spacer, EVAS_HINT_FILL, EVAS_HINT_FILL);
246    evas_object_size_hint_weight_set(sd->spacer, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
247    evas_object_color_set(sd->spacer, 0, 0, 0, 0);
248    elm_table_pack(sd->tbl, sd->spacer, 0, 0, 1, 1);
249
250    //Scroller
251    sd->scr = elm_scroller_add(sd->tbl);
252    elm_object_style_set(sd->scr, "popup/no_inset_shadow");
253    evas_object_size_hint_weight_set(sd->scr, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
254    evas_object_size_hint_align_set(sd->scr, EVAS_HINT_FILL, EVAS_HINT_FILL);
255    if (sd->horizontal)
256      {
257         elm_scroller_policy_set(sd->scr, ELM_SCROLLER_POLICY_AUTO, ELM_SCROLLER_POLICY_OFF);
258         elm_scroller_content_min_limit(sd->scr, EINA_FALSE, EINA_TRUE);
259         elm_scroller_bounce_set(sd->scr, EINA_TRUE, EINA_FALSE);
260      }
261    else
262      {
263         elm_scroller_policy_set(sd->scr, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
264         elm_scroller_content_min_limit(sd->scr, EINA_TRUE, EINA_FALSE);
265         elm_scroller_bounce_set(sd->scr, EINA_FALSE, EINA_TRUE);
266      }
267    elm_table_pack(sd->tbl, sd->scr, 0, 0, 1, 1);
268    evas_object_show(sd->scr);
269 }
270
271 static void
272 _sizing_eval(void *data)
273 {
274    Evas_Object *obj = data;
275    Elm_Object_Item *eo_item;
276    const Eina_List *l;
277    const char *max_size_str;
278    int max_size = 0;
279    char buf[128];
280    Evas_Coord box_w = -1, box_h = -1;
281    Eina_Rectangle base, adjusted, parent;
282
283    ELM_HOVERSEL_DATA_GET(obj, sd);
284    EINA_SAFETY_ON_NULL_RETURN(sd);
285
286    if (sd->resize_job)
287      sd->resize_job = NULL;
288
289    if ((!sd->expanded) || (!sd->bx)) return;
290
291    elm_layout_signal_emit(sd->hover, "elm,state,align,default", "elm");
292    edje_object_message_signal_process(elm_layout_edje_get(sd->hover));
293
294    EINA_LIST_FOREACH(sd->items, l, eo_item)
295      {
296         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
297         efl_canvas_group_calculate(VIEW(item));
298      }
299
300    elm_box_recalculate(sd->bx);
301    evas_object_size_hint_combined_min_get(sd->bx, &box_w, &box_h);
302
303    max_size_str = elm_layout_data_get(sd->hover, "max_size");
304    if (max_size_str)
305      max_size = (int)(atoi(max_size_str)
306                       * elm_config_scale_get()
307                       * elm_object_scale_get(obj))
308                       / edje_object_base_scale_get(elm_layout_edje_get(sd->hover));
309
310    if (sd->horizontal)
311      {
312         adjusted.w = (max_size > 0) ? MIN(box_w, max_size) : box_w ;
313         adjusted.h = box_h;
314      }
315    else
316      {
317         adjusted.w = box_w;
318         adjusted.h = (max_size > 0) ? MIN(box_h, max_size) : box_h ;
319      }
320
321    evas_object_size_hint_min_set(sd->spacer, adjusted.w, adjusted.h);
322    if (!sd->last_location)
323      sd->last_location = elm_hover_best_content_location_get(sd->hover, !sd->horizontal + 1);
324
325    evas_object_geometry_get(sd->hover_parent, &parent.x, &parent.y, &parent.w, &parent.h);
326    if (efl_isa(sd->hover_parent, EFL_UI_WIN_CLASS))
327      {
328         parent.x = 0;
329         parent.y = 0;
330      }
331
332    snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", sd->last_location);
333    edje_object_part_geometry_get(elm_layout_edje_get(sd->hover), buf, &adjusted.x, &adjusted.y, NULL, NULL);
334    adjusted.x += parent.x;
335    adjusted.y += parent.y;
336
337    evas_object_geometry_get(obj, &base.x, &base.y, &base.w, NULL);
338
339    if (sd->horizontal)
340      {
341         if ((sd->last_location) && (!strcmp(sd->last_location, "left")))
342           {
343              adjusted.x = parent.x;
344              if ((adjusted.x + adjusted.w) > base.x)
345                adjusted.w = base.x - adjusted.x;
346           }
347         else
348           {
349              if ((adjusted.x + adjusted.w) > (parent.x + parent.w))
350                adjusted.w = (parent.x + parent.w) - adjusted.x;
351           }
352
353         if (adjusted.y < 0) adjusted.y = parent.y;
354         if ((adjusted.y + adjusted.h) > (parent.y + parent.h))
355           adjusted.h = (parent.y + parent.h) - adjusted.y;
356      }
357    else
358      {
359         if ((sd->last_location) && (!strcmp(sd->last_location, "top")))
360           {
361              adjusted.y = parent.y;
362              if ((adjusted.y + adjusted.h) > base.y)
363                adjusted.h = base.y - adjusted.y;
364           }
365         else
366           {
367              if ((adjusted.y + adjusted.h) > (parent.y + parent.h))
368                adjusted.h = (parent.y + parent.h) - adjusted.y;
369           }
370
371         if (adjusted.x < 0) adjusted.x = parent.x;
372         if ((adjusted.x + adjusted.w) > (parent.x + parent.w))
373           {
374              if ((base.x + base.w) > (parent.x + parent.w))
375                {
376                   if (efl_ui_mirrored_get(obj))
377                     elm_object_signal_emit(sd->hover, "elm,state,align,right", "elm");
378                   else
379                     elm_object_signal_emit(sd->hover, "elm,state,align,default", "elm");
380
381                   if ((base.x + base.w - adjusted.w) < parent.x)
382                     adjusted.w = base.x + base.w - parent.x;
383                }
384              else
385                {
386                   if (efl_ui_mirrored_get(obj))
387                     elm_object_signal_emit(sd->hover, "elm,state,align,default", "elm");
388                   else
389                     elm_object_signal_emit(sd->hover, "elm,state,align,right", "elm");
390
391                   // TIZEN_ONLY(20201116): elm_hoversel: Fix spacer size calculation
392                   // NOTE: Resize spacer(adjusted) only when left area of spacer is
393                   //       smaller than size of spacer.
394                   if ((base.x + base.w ) - parent.x < adjusted.w)
395                     adjusted.w = (base.x + base.w ) - parent.x;
396                   //adjusted.w = (parent.x + parent.w) - adjusted.x;
397                   //
398                }
399           }
400      }
401    evas_object_size_hint_min_set(sd->spacer, adjusted.w, adjusted.h);
402
403    // TIZEN_ONLY(20150828): shrink item width expanding over hover parent
404    if (!sd->horizontal)
405      {
406         Eina_List *l;
407         Evas_Object *it;
408         Evas_Coord it_w, it_h;
409
410         l = elm_box_children_get(sd->bx);
411         EINA_LIST_FREE(l, it)
412           {
413              evas_object_size_hint_min_get(it, &it_w, &it_h);
414              if (it_w > adjusted.w)
415                evas_object_size_hint_min_set(it, adjusted.w, it_h);
416           }
417      }
418    //
419 }
420
421 static void
422 _hover_del(Evas_Object *obj)
423 {
424    Elm_Object_Item *eo_item;
425    Eina_List *l;
426
427    ELM_HOVERSEL_DATA_GET(obj, sd);
428
429    //TIZEN_ONLY(20230112): Handle default label object
430    _elm_win_default_label_obj_remove(obj);
431    //
432
433    sd->expanded = EINA_FALSE;
434
435    EINA_LIST_FOREACH(sd->items, l, eo_item)
436      {
437         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, it);
438         elm_box_unpack(sd->bx, VIEW(it));
439         evas_object_hide(VIEW(it));
440      }
441    ELM_SAFE_FREE(sd->hover, evas_object_del);
442    sd->bx = NULL;
443    sd->scr = NULL;
444    sd->last_location = NULL;
445
446    efl_event_callback_legacy_call(obj, ELM_HOVERSEL_EVENT_DISMISSED, NULL);
447 }
448
449 static void
450 _hover_end_finished(void *data,
451                     Evas_Object *obj EINA_UNUSED,
452                     const char *emission EINA_UNUSED,
453                     const char *source EINA_UNUSED)
454 {
455    const char *dismissstr;
456
457    ELM_HOVERSEL_DATA_GET(data, sd);
458
459    dismissstr = elm_layout_data_get(sd->hover, "dismiss");
460
461    if (dismissstr && !strcmp(dismissstr, "on"))
462      {
463         _hover_del(data);
464      }
465 }
466
467 static char *
468 _access_info_cb(void *data, Evas_Object *obj)
469 {
470    const char *txt;
471    Elm_Hoversel_Item_Data *it;
472
473    if (data != NULL)
474      {
475         it = (Elm_Hoversel_Item_Data *)data;
476         if (it->label) return strdup(it->label);
477      }
478    else
479      {
480         txt = elm_widget_access_info_get(obj);
481         if (!txt) txt = elm_layout_text_get(obj, NULL);
482         if (txt) return strdup(txt);
483      }
484
485    return NULL;
486 }
487
488 static void
489 _access_widget_item_register(Elm_Hoversel_Data *sd)
490 {
491    const Eina_List *l;
492    Elm_Object_Item *eo_item;
493    Elm_Access_Info *ai;
494
495    EINA_LIST_FOREACH(sd->items, l, eo_item)
496      {
497         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
498         _elm_access_widget_item_register(item->base);
499         ai = _elm_access_info_get(item->base->access_obj);
500         _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, item);
501      }
502 }
503
504 static char *
505 _access_state_cb(void *data EINA_UNUSED, Evas_Object *obj)
506 {
507    if (elm_widget_disabled_get(obj))
508      return strdup(E_("State: Disabled"));
509
510    return NULL;
511 }
512
513
514 static void
515 _hover_key_down(void *data, const Efl_Event *ev)
516 {
517    ELM_HOVERSEL_DATA_GET(ev->object, sd);
518
519    _hoversel_efl_ui_widget_widget_input_event_handler(data, sd, ev, ev->object);
520 }
521
522 static void
523 _activate(Evas_Object *obj)
524 {
525    Elm_Object_Item *eo_item;
526    const Eina_List *l;
527    char buf[4096];
528
529    ELM_HOVERSEL_DATA_GET(obj, sd);
530
531    if (sd->expanded)
532      {
533         elm_hoversel_hover_end(obj);
534         return;
535      }
536
537    if (elm_widget_disabled_get(obj)) return;
538    if (!sd->items) return;
539
540    sd->expanded = EINA_TRUE;
541
542    efl_wref_add(elm_hover_add(sd->hover_parent), &sd->hover);
543    efl_event_callback_add(sd->hover, EFL_EVENT_KEY_DOWN, _hover_key_down, obj);
544
545    elm_widget_sub_object_add(obj, sd->hover);
546    evas_object_layer_set(sd->hover, evas_object_layer_get(sd->hover_parent));
547
548    if (sd->horizontal)
549      snprintf(buf, sizeof(buf), "hoversel_horizontal/%s",
550               elm_widget_style_get(obj));
551    else
552      snprintf(buf, sizeof(buf), "hoversel_vertical/%s",
553               elm_widget_style_get(obj));
554
555    elm_object_style_set(sd->hover, buf);
556
557    evas_object_smart_callback_add(sd->hover, "clicked", _on_hover_clicked, obj);
558    elm_layout_signal_callback_add
559      (sd->hover, "elm,action,hide,finished", "elm", _hover_end_finished, obj);
560    elm_hover_target_set(sd->hover, obj);
561
562    /* hover's content */
563    sd->bx = elm_box_add(sd->hover);
564    elm_box_homogeneous_set(sd->bx, EINA_TRUE);
565    elm_box_horizontal_set(sd->bx, sd->horizontal);
566    evas_object_size_hint_align_set(sd->bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
567    evas_object_size_hint_weight_set(sd->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
568
569    EINA_LIST_FOREACH(sd->items, l, eo_item)
570      {
571         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
572         evas_object_show(VIEW(item));
573         elm_box_pack_end(sd->bx, VIEW(item));
574      }
575
576    _create_scroller(obj, sd);
577    elm_object_content_set(sd->scr, sd->bx);
578
579    if (sd->resize_job)
580      ELM_SAFE_FREE(sd->resize_job, ecore_job_del);
581    _sizing_eval(obj);
582    elm_object_part_content_set(sd->hover, sd->last_location, sd->tbl);
583
584    if (_elm_config->access_mode) _access_widget_item_register(sd);
585
586    efl_event_callback_legacy_call(obj, ELM_HOVERSEL_EVENT_EXPANDED, NULL);
587    efl_canvas_group_calculate(sd->hover);
588    _sizing_eval(obj);
589
590    //TIZEN_ONLY(20230112): Handle default label object
591    _elm_win_default_label_obj_append(obj);
592    //
593
594    evas_object_show(sd->hover);
595 }
596
597 static void
598 _on_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
599 {
600    _activate(data);
601 }
602
603 static void
604 _on_parent_del(void *data, const Efl_Event *ev EINA_UNUSED)
605 {
606    elm_hoversel_hover_parent_set(data, NULL);
607 }
608
609 EOLIAN static void
610 _elm_hoversel_item_elm_widget_item_disable(Eo *eo_hoversel_it,
611                                            Elm_Hoversel_Item_Data *hoversel_it EINA_UNUSED)
612 {
613    elm_widget_disabled_set
614      (VIEW(hoversel_it), elm_wdg_item_disabled_get(eo_hoversel_it));
615 }
616
617 static void
618 _elm_hoversel_item_elm_widget_item_part_text_set(Eo *eo_it EINA_UNUSED,
619                                             Elm_Hoversel_Item_Data *it,
620                                             const char *part,
621                                             const char *label)
622 {
623    if (part && strcmp(part, "default")) return;
624    eina_stringshare_replace(&it->label, label);
625
626    if (VIEW(it))
627      elm_object_text_set(VIEW(it), label);
628 }
629
630 static const char *
631 _elm_hoversel_item_elm_widget_item_part_text_get(const Eo *eo_it EINA_UNUSED,
632                                             Elm_Hoversel_Item_Data *it,
633                                             const char *part)
634 {
635    if (part && strcmp(part, "default")) return NULL;
636    return it->label;
637 }
638
639 EOLIAN static void
640 _elm_hoversel_item_elm_widget_item_signal_emit(Eo *eo_it EINA_UNUSED,
641                                          Elm_Hoversel_Item_Data *it,
642                                          const char *emission,
643                                          const char *source)
644 {
645    elm_object_signal_emit(VIEW(it), emission, source);
646 }
647
648 EOLIAN static void
649 _elm_hoversel_item_elm_widget_item_style_set(Eo *eo_it EINA_UNUSED,
650                                              Elm_Hoversel_Item_Data *it,
651                                              const char *style)
652 {
653    elm_object_style_set(VIEW(it), style);
654 }
655
656 EOLIAN static const char *
657 _elm_hoversel_item_elm_widget_item_style_get(const Eo *eo_it EINA_UNUSED,
658                                              Elm_Hoversel_Item_Data *it)
659 {
660    return elm_object_style_get(VIEW(it));
661 }
662
663 EOLIAN static void
664 _elm_hoversel_item_elm_widget_item_item_focus_set(Eo *eo_it EINA_UNUSED,
665                                              Elm_Hoversel_Item_Data *it,
666                                              Eina_Bool focused)
667 {
668    elm_object_focus_set(VIEW(it), focused);
669 }
670
671 EOLIAN static Eina_Bool
672 _elm_hoversel_item_elm_widget_item_item_focus_get(const Eo *eo_it EINA_UNUSED,
673                                              Elm_Hoversel_Item_Data *it)
674 {
675    return efl_ui_focus_object_focus_get(VIEW(it));
676 }
677
678 EOLIAN static void
679 _elm_hoversel_item_efl_object_destructor(Eo *eo_item, Elm_Hoversel_Item_Data *item)
680 {
681    ELM_HOVERSEL_DATA_GET_OR_RETURN(WIDGET(item), sd);
682
683    evas_object_event_callback_del_full(sd->hover, EVAS_CALLBACK_DEL, _auto_update, eo_item);
684    elm_hoversel_hover_end(WIDGET(item));
685    sd->items = eina_list_remove(sd->items, eo_item);
686    eina_stringshare_del(item->label);
687    eina_stringshare_del(item->icon_file);
688    eina_stringshare_del(item->icon_group);
689
690    efl_destructor(efl_super(eo_item, ELM_HOVERSEL_ITEM_CLASS));
691 }
692
693 static void
694 _on_geometry_changed(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
695 {
696    Elm_Hoversel_Data *pd = data;
697
698    if (pd->resize_job)
699      ELM_SAFE_FREE(pd->resize_job, ecore_job_del);
700
701    pd->resize_job = ecore_job_add(_sizing_eval, obj);
702 }
703
704 static void
705 _on_parent_resize(void *data, const Efl_Event *ev EINA_UNUSED)
706 {
707    Evas_Object *obj = (Evas_Object *)data;
708    ELM_HOVERSEL_DATA_GET(obj, pd);
709
710    _on_geometry_changed(pd, NULL, obj, NULL);
711 }
712
713 EOLIAN static void
714 _elm_hoversel_efl_canvas_group_group_add(Eo *obj, Elm_Hoversel_Data *pd)
715 {
716    efl_canvas_group_add(efl_super(obj, MY_CLASS));
717
718    evas_object_smart_callback_add(obj, "clicked", _on_clicked, obj);
719    //What are you doing here?
720    efl_ui_widget_theme_apply(obj);
721
722    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _on_geometry_changed, pd);
723    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _on_geometry_changed, pd);
724
725    _elm_access_text_set
726      (_elm_access_info_get(obj), ELM_ACCESS_TYPE, E_("Hoversel"));
727    _elm_access_callback_set
728      (_elm_access_info_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL);
729    _elm_access_callback_set
730      (_elm_access_info_get(obj), ELM_ACCESS_STATE, _access_state_cb, obj);
731 }
732
733 EOLIAN static void
734 _elm_hoversel_efl_canvas_group_group_del(Eo *obj, Elm_Hoversel_Data *sd)
735 {
736    Elm_Object_Item *eo_item;
737
738    EINA_LIST_FREE(sd->items, eo_item)
739      {
740         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, it);
741         ELM_SAFE_FREE(VIEW(it), evas_object_del);
742         efl_del(eo_item);
743      }
744    elm_hoversel_hover_parent_set(obj, NULL);
745
746    efl_canvas_group_del(efl_super(obj, MY_CLASS));
747 }
748
749 EOLIAN static void
750 _elm_hoversel_efl_gfx_entity_visible_set(Eo *obj, Elm_Hoversel_Data *sd, Eina_Bool vis)
751 {
752    if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis))
753      return;
754
755    efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), vis);
756    if (sd->hover)
757      efl_gfx_entity_visible_set(sd->hover, vis);
758 }
759
760 EOLIAN static void
761 _elm_hoversel_efl_ui_autorepeat_autorepeat_enabled_set(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd EINA_UNUSED, Eina_Bool enabled)
762 {
763    if (enabled)
764      ERR("You cannot enable autorepeat on this object");
765    efl_ui_autorepeat_enabled_set(efl_super(obj, MY_CLASS), EINA_FALSE);
766 }
767
768 EAPI Evas_Object *
769 elm_hoversel_add(Evas_Object *parent)
770 {
771    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
772    return elm_legacy_add(MY_CLASS, parent);
773 }
774
775 EOLIAN static Eo *
776 _elm_hoversel_efl_object_constructor(Eo *obj, Elm_Hoversel_Data *_pd EINA_UNUSED)
777 {
778    obj = efl_constructor(efl_super(obj, MY_CLASS));
779    efl_ui_autorepeat_enabled_set(obj, EINA_FALSE);
780    efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY);
781    evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks);
782    //TIZEN_ONLY(20220812): Set COMBO_BOX role for Hoversel
783    efl_access_object_role_set(obj, EFL_ACCESS_ROLE_COMBO_BOX);
784    //
785    legacy_object_focus_handle(obj);
786    elm_hoversel_hover_parent_set(obj, efl_parent_get(obj));
787
788    return obj;
789 }
790
791 EOLIAN static void
792 _elm_hoversel_efl_object_destructor(Eo *obj, Elm_Hoversel_Data *_pd)
793 {
794    if (_pd->resize_job)
795      ELM_SAFE_FREE(_pd->resize_job, ecore_job_del);
796
797    elm_obj_hoversel_clear(obj);
798    efl_destructor(efl_super(obj, MY_CLASS));
799 }
800
801 EFL_CALLBACKS_ARRAY_DEFINE(_on_parent,
802                           { EFL_EVENT_DEL, _on_parent_del },
803                           { EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _on_parent_resize });
804
805 EOLIAN static void
806 _elm_hoversel_hover_parent_set(Eo *obj, Elm_Hoversel_Data *sd, Evas_Object *parent)
807 {
808    if (sd->hover_parent)
809      {
810         efl_event_callback_array_del(sd->hover_parent, _on_parent(), obj);
811      }
812
813    sd->hover_parent = parent;
814    if (sd->hover_parent)
815      {
816         efl_event_callback_array_add(sd->hover_parent, _on_parent(), obj);
817      }
818 }
819
820 EOLIAN static Evas_Object*
821 _elm_hoversel_hover_parent_get(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
822 {
823    return sd->hover_parent;
824 }
825
826 EOLIAN static void
827 _elm_hoversel_horizontal_set(Eo *obj, Elm_Hoversel_Data *sd, Eina_Bool horizontal)
828 {
829    sd->horizontal = !!horizontal;
830
831    if (sd->scr)
832      {
833         if (sd->horizontal)
834           {
835              elm_scroller_policy_set(sd->scr, ELM_SCROLLER_POLICY_AUTO, ELM_SCROLLER_POLICY_OFF);
836              elm_scroller_content_min_limit(sd->scr, EINA_FALSE, EINA_TRUE);
837              elm_scroller_bounce_set(sd->scr, EINA_TRUE, EINA_FALSE);
838           }
839         else
840           {
841              elm_scroller_policy_set(sd->scr, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
842              elm_scroller_content_min_limit(sd->scr, EINA_TRUE, EINA_FALSE);
843              elm_scroller_bounce_set(sd->scr, EINA_FALSE, EINA_TRUE);
844           }
845      }
846
847    efl_ui_widget_theme_apply(obj);
848 }
849
850 EOLIAN static Eina_Bool
851 _elm_hoversel_horizontal_get(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
852 {
853    return sd->horizontal;
854 }
855
856 EOLIAN static void
857 _elm_hoversel_hover_begin(Eo *obj, Elm_Hoversel_Data *sd)
858 {
859    if (sd->hover) return;
860
861    _activate(obj);
862 }
863
864 EOLIAN static void
865 _elm_hoversel_hover_end(Eo *obj, Elm_Hoversel_Data *sd)
866 {
867    const char *dismissstr;
868
869    if (!sd->hover) return;
870
871    dismissstr = elm_layout_data_get(sd->hover, "dismiss");
872
873    if (dismissstr && !strcmp(dismissstr, "on"))
874      {
875         elm_hover_dismiss(sd->hover);
876      }
877    else
878      {
879         _hover_del(obj);
880      } // for backward compatibility
881    elm_object_focus_set(obj, EINA_TRUE);
882 }
883
884 EOLIAN static Eina_Bool
885 _elm_hoversel_expanded_get(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
886 {
887    return (sd->hover) ? EINA_TRUE : EINA_FALSE;
888 }
889
890 EOLIAN static void
891 _elm_hoversel_clear(Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
892 {
893    Elm_Object_Item *it;
894
895    EINA_LIST_FREE(sd->items, it)
896      {
897         efl_del(it);
898      }
899 }
900
901 EOLIAN static const Eina_List*
902 _elm_hoversel_items_get(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
903 {
904    return sd->items;
905 }
906
907 EOLIAN static Eo *
908 _elm_hoversel_item_efl_object_constructor(Eo *obj, Elm_Hoversel_Item_Data *it)
909 {
910    obj = efl_constructor(efl_super(obj, ELM_HOVERSEL_ITEM_CLASS));
911    it->base = efl_data_scope_get(obj, ELM_WIDGET_ITEM_CLASS);
912
913    return obj;
914 }
915
916 EOLIAN static Elm_Object_Item*
917 _elm_hoversel_item_add(Eo *obj, Elm_Hoversel_Data *sd, const char *label, const char *icon_file, Elm_Icon_Type icon_type, Evas_Smart_Cb func, const void *data)
918 {
919    Evas_Object *bt, *ic;
920    char buf[4096];
921
922    Eo *eo_item = efl_add(ELM_HOVERSEL_ITEM_CLASS, obj);
923    if (!eo_item) return NULL;
924
925    ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
926
927    item->label = eina_stringshare_add(label);
928    item->icon_file = eina_stringshare_add(icon_file);
929    item->icon_type = icon_type;
930    item->func = func;
931    WIDGET_ITEM_DATA_SET(eo_item, data);
932
933    if (sd->horizontal)
934      snprintf(buf, sizeof(buf), "hoversel_horizontal_entry/%s",
935               elm_widget_style_get(obj));
936    else
937      snprintf(buf, sizeof(buf), "hoversel_vertical_entry/%s",
938               elm_widget_style_get(obj));
939
940    bt = elm_button_add(obj);
941    VIEW_SET(item, bt);
942    efl_ui_mirrored_set(bt, efl_ui_mirrored_get(obj));
943    elm_object_style_set(bt, buf);
944    elm_object_text_set(bt, item->label);
945
946    if (item->icon_file)
947      {
948         ic = elm_icon_add(bt);
949         elm_image_resizable_set(ic, EINA_FALSE, EINA_TRUE);
950         if (item->icon_type == ELM_ICON_FILE)
951           elm_image_file_set(ic, item->icon_file, item->icon_group);
952         else if (item->icon_type == ELM_ICON_STANDARD)
953           elm_icon_standard_set(ic, item->icon_file);
954         elm_object_part_content_set(bt, "icon", ic);
955      }
956
957     evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0);
958     evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL);
959     evas_object_smart_callback_add(bt, "clicked", _on_item_clicked, item);
960
961     //TIZEN_ONLY(20180607): Restore legacy focus
962     evas_object_smart_callback_add(bt, "focused", _item_focused_cb, item);
963     evas_object_smart_callback_add(bt, "unfocused", _item_unfocused_cb, item);
964     //
965     efl_event_callback_add(bt, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _item_focus_changed, item);
966
967
968    sd->items = eina_list_append(sd->items, eo_item);
969
970    return eo_item;
971 }
972
973 EOLIAN static void
974 _elm_hoversel_item_icon_set(Eo *eo_item EINA_UNUSED,
975                             Elm_Hoversel_Item_Data *item,
976                             const char *icon_file,
977                             const char *icon_group,
978                             Elm_Icon_Type icon_type)
979 {
980    eina_stringshare_replace(&item->icon_file, icon_file);
981    eina_stringshare_replace(&item->icon_group, icon_group);
982
983    item->icon_type = icon_type;
984 }
985
986 EOLIAN static void
987 _elm_hoversel_item_icon_get(const Eo *eo_item EINA_UNUSED,
988                             Elm_Hoversel_Item_Data *item,
989                             const char **icon_file,
990                             const char **icon_group,
991                             Elm_Icon_Type *icon_type)
992 {
993    if (icon_file) *icon_file = item->icon_file;
994    if (icon_group) *icon_group = item->icon_group;
995    if (icon_type) *icon_type = item->icon_type;
996 }
997
998 static Elm_Object_Item *
999 item_focused_get(Elm_Hoversel_Data *sd)
1000 {
1001    Elm_Object_Item *eo_item;
1002    Eina_List *l;
1003
1004    EINA_LIST_FOREACH(sd->items, l, eo_item)
1005      {
1006         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
1007         if (elm_object_focus_get(VIEW(item)))
1008           return eo_item;
1009      }
1010    return NULL;
1011 }
1012
1013 static Eina_Bool
1014 item_focused_set(Elm_Object_Item *eo_item, Eina_Bool focus)
1015 {
1016    ELM_HOVERSEL_ITEM_DATA_GET(eo_item, item);
1017    if (elm_object_disabled_get(VIEW(item)))
1018       return EINA_FALSE;
1019    elm_object_focus_set(VIEW(item), focus);
1020    return EINA_TRUE;
1021 }
1022
1023 static Eina_Bool
1024 _key_action_move(Evas_Object *obj, const char *params)
1025 {
1026    Eina_List *l;
1027    Elm_Object_Item *focused_item, *eo_item;
1028    Eina_Bool ret, next = EINA_FALSE;
1029
1030    ELM_HOVERSEL_DATA_GET(obj, sd);
1031    const char *dir = params;
1032
1033    if (!sd->hover) return EINA_FALSE;
1034    _elm_widget_focus_auto_show(obj);
1035    if (!strcmp(dir, "down") || !strcmp(dir, "right"))
1036      {
1037         focused_item = item_focused_get(sd);
1038         EINA_LIST_FOREACH(sd->items, l, eo_item)
1039           {
1040              if (next)
1041                {
1042                   ret = item_focused_set(eo_item, EINA_TRUE);
1043                   if (ret) return EINA_TRUE;
1044                }
1045              if (eo_item == focused_item) next = EINA_TRUE;
1046           }
1047         EINA_LIST_FOREACH(sd->items, l, eo_item)
1048           {
1049              if (eo_item == focused_item) return EINA_FALSE;
1050
1051              ret = item_focused_set(eo_item, EINA_TRUE);
1052              if (ret) return EINA_TRUE;
1053           }
1054         return EINA_FALSE;
1055      }
1056    else if (!strcmp(dir, "up") || !strcmp(dir, "left"))
1057      {
1058         focused_item = item_focused_get(sd);
1059         EINA_LIST_REVERSE_FOREACH(sd->items, l, eo_item)
1060           {
1061              if (next)
1062                {
1063                   ret = item_focused_set(eo_item, EINA_TRUE);
1064                   if (ret) return EINA_TRUE;
1065                }
1066              if (eo_item == focused_item) next = EINA_TRUE;
1067           }
1068         EINA_LIST_REVERSE_FOREACH(sd->items, l, eo_item)
1069           {
1070              if (eo_item == focused_item) return EINA_FALSE;
1071
1072              ret = item_focused_set(eo_item, EINA_TRUE);
1073              if (ret) return EINA_TRUE;
1074           }
1075         return EINA_FALSE;
1076      }
1077    else return EINA_FALSE;
1078 }
1079
1080 static Eina_Bool
1081 _key_action_activate(Evas_Object *obj, const char *params EINA_UNUSED)
1082 {
1083    _activate(obj);
1084    return EINA_TRUE;
1085 }
1086
1087 static Eina_Bool
1088 _key_action_escape(Evas_Object *obj, const char *params EINA_UNUSED)
1089 {
1090    ELM_HOVERSEL_DATA_GET(obj, sd);
1091    if (!sd->hover) return EINA_FALSE;
1092    elm_hoversel_hover_end(obj);
1093    return EINA_TRUE;
1094 }
1095
1096 // _hoversel_elm_widget_widget_event()
1097 ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(hoversel, Elm_Hoversel_Data)
1098
1099 EOLIAN static Eina_Bool
1100 _elm_hoversel_efl_ui_widget_widget_input_event_handler(Eo *obj, Elm_Hoversel_Data *sd, const Efl_Event *eo_event, Evas_Object *src)
1101 {
1102    if (efl_ui_widget_input_event_handler(efl_super(obj, MY_CLASS), eo_event, src))
1103      return EINA_TRUE; // note: this was FALSE but likely wrong
1104
1105    return _hoversel_efl_ui_widget_widget_input_event_handler(obj, sd, eo_event, src);
1106 }
1107
1108 static void
1109 _elm_hoversel_class_constructor(Efl_Class *klass)
1110 {
1111    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1112 }
1113
1114 EOLIAN const Efl_Access_Action_Data *
1115 _elm_hoversel_efl_access_widget_action_elm_actions_get(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *pd EINA_UNUSED)
1116 {
1117    static Efl_Access_Action_Data atspi_actions[] = {
1118           { "activate", "activate", NULL, _key_action_activate},
1119           { "move,up", "move", "up", _key_action_move},
1120           { "move,down", "move", "down", _key_action_move},
1121           { "move,left", "move", "left", _key_action_move},
1122           { "move,right", "move", "right", _key_action_move},
1123           { "escape", "escape", NULL, _key_action_escape},
1124           { NULL, NULL, NULL, NULL}
1125    };
1126    return &atspi_actions[0];
1127 }
1128
1129 //TIZEN_ONLY(20230119) : fix accessible_at_point getter
1130 EOLIAN static Eo *
1131 _elm_hoversel_efl_access_component_accessible_at_point_get(const Eo *obj, Elm_Hoversel_Data *sd, Eina_Bool screen_coords, int x, int y)
1132 {
1133    Evas_Coord wx, wy, ww, wh;
1134    int ee_x, ee_y;
1135
1136    if (screen_coords)
1137      {
1138         Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
1139         if (!ee) return NULL;
1140         ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
1141         x -= ee_x;
1142         y -= ee_y;
1143      }
1144
1145    Elm_Object_Item *eo_item;
1146    Eina_List *l;
1147    EINA_LIST_FOREACH(sd->items, l, eo_item)
1148      {
1149         ELM_HOVERSEL_ITEM_DATA_GET(eo_item, it);
1150         evas_object_geometry_get(VIEW(it), &wx, &wy, &ww, &wh);
1151         Efl_Access_State_Set ss = efl_access_object_state_set_get(VIEW(it));
1152         Eina_Bool is_item_showing = !!STATE_TYPE_GET(ss, EFL_ACCESS_STATE_TYPE_SHOWING);
1153         if (is_item_showing && !((x > wx + ww) || (y > wy + wh) || (x < wx - ww) || (y < wy - wh)))
1154           return VIEW(it);
1155      }
1156
1157    return NULL;
1158 }
1159 //
1160
1161 EOLIAN void
1162 _elm_hoversel_auto_update_set(Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd, Eina_Bool auto_update)
1163 {
1164    sd->auto_update = !!auto_update;
1165 }
1166
1167 EOLIAN Eina_Bool
1168 _elm_hoversel_auto_update_get(const Eo *obj EINA_UNUSED, Elm_Hoversel_Data *sd)
1169 {
1170    return sd->auto_update;
1171 }
1172
1173 /* Internal EO APIs and hidden overrides */
1174
1175 #define ELM_HOVERSEL_EXTRA_OPS \
1176    EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_hoversel)
1177
1178 #include "elm_hoversel_item_eo.c"
1179 #include "elm_hoversel_eo.c"