Merge branch 'master' of jae.hwan.kim@165.213.180.234:/git/slp2.0/slp2.0-pkgs/EFL...
[framework/uifw/elementary.git] / src / lib / elm_hover.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Hover Hover
6  * @ingroup Elementary
7  *
8  * A Hover object will over over the @p Parent object at the @p Target
9  * location.  Anything in the background will be given a darker coloring
10  * to indicate that the hover object is on top.
11  *
12  * NOTE: The hover object will take up the entire space of @p Target object.
13  */
14
15 typedef struct _Widget_Data Widget_Data;
16 typedef struct _Subinfo Subinfo;
17
18 struct _Widget_Data
19 {
20    Evas_Object *hov, *cov;
21    Evas_Object *offset, *size;
22    Evas_Object *parent, *target;
23    Eina_List *subs;
24 };
25
26 struct _Subinfo
27 {
28    const char *swallow;
29    Evas_Object *obj;
30 };
31
32 static const char *widtype = NULL;
33 static void _del_pre_hook(Evas_Object *obj);
34 static void _del_hook(Evas_Object *obj);
35 static void _theme_hook(Evas_Object *obj);
36 static void _sizing_eval(Evas_Object *obj);
37 static void _reval_content(Evas_Object *obj);
38 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
39 static void _hov_show_do(Evas_Object *obj);
40 static void _hov_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
41 static void _hov_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
42 static void _hov_show(void *data, Evas *e, Evas_Object *obj, void *event_info);
43 static void _hov_hide(void *data, Evas *e, Evas_Object *obj, void *event_info);
44
45 static void
46 _del_pre_hook(Evas_Object *obj)
47 {
48    Widget_Data *wd = elm_widget_data_get(obj);
49    Subinfo *si;
50    if (!wd) return;
51    if (evas_object_visible_get(obj))
52      evas_object_smart_callback_call(obj, "clicked", NULL);
53    elm_hover_target_set(obj, NULL);
54    elm_hover_parent_set(obj, NULL);
55    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_MOVE, _hov_move, obj);
56    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_RESIZE, _hov_resize, obj);
57    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_SHOW, _hov_show, obj);
58    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_HIDE, _hov_hide, obj);
59
60    EINA_LIST_FREE(wd->subs, si)
61      {
62         eina_stringshare_del(si->swallow);
63         free(si);
64      }
65 }
66
67 static void
68 _del_hook(Evas_Object *obj)
69 {
70    Widget_Data *wd = elm_widget_data_get(obj);
71    if (!wd) return;
72    free(wd);
73 }
74
75 static void
76 _theme_hook(Evas_Object *obj)
77 {
78    Widget_Data *wd = elm_widget_data_get(obj);
79    if (!wd) return;
80    // FIXME: hover contents doesnt seem to propagate resizes properly
81    _elm_theme_object_set(obj, wd->cov, "hover", "base", elm_widget_style_get(obj));
82    edje_object_scale_set(wd->cov, elm_widget_scale_get(obj) *
83                          _elm_config->scale);
84    _reval_content(obj);
85    _sizing_eval(obj);
86    if (evas_object_visible_get(wd->cov)) _hov_show_do(obj);
87 }
88
89
90 static void
91 _sizing_eval(Evas_Object *obj)
92 {
93    Widget_Data *wd = elm_widget_data_get(obj);
94    Evas_Coord x = 0, y = 0, w = 0, h = 0, x2 = 0, y2 = 0, w2 = 0, h2 = 0;
95    if (!wd) return;
96    if (wd->parent) evas_object_geometry_get(wd->parent, &x, &y, &w, &h);
97    if (wd->hov) evas_object_geometry_get(wd->hov, &x2, &y2, &w2, &h2);
98    evas_object_move(wd->cov, x, y);
99    evas_object_resize(wd->cov, w, h);
100    evas_object_size_hint_min_set(wd->offset, x2 - x, y2 - y);
101    evas_object_size_hint_min_set(wd->size, w2, h2);
102    edje_object_part_swallow(wd->cov, "elm.swallow.offset", wd->offset);
103    edje_object_part_swallow(wd->cov, "elm.swallow.size", wd->size);
104 }
105
106 static void
107 _reval_content(Evas_Object *obj)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    const Eina_List *l;
111    const Subinfo *si;
112    if (!wd) return;
113    EINA_LIST_FOREACH(wd->subs, l, si)
114      edje_object_part_swallow(wd->cov, si->swallow, si->obj);
115 }
116
117 static void
118 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
119 {
120    Widget_Data *wd = elm_widget_data_get(obj);
121    Evas_Object *sub = event_info;
122    Eina_List *l;
123    Subinfo *si;
124    if (!wd) return;
125    EINA_LIST_FOREACH(wd->subs, l, si)
126      {
127         if (si->obj == sub)
128           {
129              wd->subs = eina_list_remove_list(wd->subs, l);
130              eina_stringshare_del(si->swallow);
131              free(si);
132              break;
133           }
134      }
135 }
136
137 static void
138 _hov_show_do(Evas_Object *obj)
139 {
140    Widget_Data *wd = elm_widget_data_get(obj);
141    const Eina_List *l;
142    const Subinfo *si;
143    if (!wd) return;
144    if (wd->cov)
145      {
146         evas_object_show(wd->cov);
147         edje_object_signal_emit(wd->cov, "elm,action,show", "elm");
148      }
149    EINA_LIST_FOREACH(wd->subs, l, si)
150      {
151         char buf[1024];
152
153         if (!strncmp(si->swallow, "elm.swallow.slot.", 17))
154           {
155              snprintf(buf, sizeof(buf), "elm,action,slot,%s,show",
156                       si->swallow + 17);
157              edje_object_signal_emit(wd->cov, buf, "elm");
158           }
159      }
160 }
161
162 static void
163 _hov_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
164 {
165    _sizing_eval(data);
166 }
167
168 static void
169 _hov_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
170 {
171    _sizing_eval(data);
172 }
173
174 static void
175 _hov_show(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
176 {
177    _hov_show_do(data);
178 }
179
180 static void
181 _hov_hide(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
182 {
183    Widget_Data *wd = elm_widget_data_get(data);
184    const Eina_List *l;
185    const Subinfo *si;
186    if (!wd) return;
187    if (wd->cov)
188      {
189         edje_object_signal_emit(wd->cov, "elm,action,hide", "elm");
190         evas_object_hide(wd->cov);
191      }
192    EINA_LIST_FOREACH(wd->subs, l, si)
193      {
194         char buf[1024];
195
196         if (!strncmp(si->swallow, "elm.swallow.slot.", 17))
197           {
198              snprintf(buf, sizeof(buf), "elm,action,slot,%s,hide",
199                       si->swallow + 17);
200              edje_object_signal_emit(wd->cov, buf, "elm");
201           }
202      }
203 }
204
205 static void
206 _target_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
207 {
208    Widget_Data *wd = elm_widget_data_get(data);
209    if (!wd) return;
210    wd->target = NULL;
211 }
212
213 static void
214 _signal_dismiss(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
215 {
216    Widget_Data *wd = elm_widget_data_get(data);
217    if (!wd) return;
218    evas_object_hide(data);
219    evas_object_smart_callback_call(data, "clicked", NULL);
220 }
221
222 static void
223 _parent_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
224 {
225    _sizing_eval(data);
226 }
227
228 static void
229 _parent_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
230 {
231    _sizing_eval(data);
232 }
233
234 static void
235 _parent_show(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
236 {
237 }
238
239 static void
240 _parent_hide(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
241 {
242    Widget_Data *wd = elm_widget_data_get(data);
243    if (!wd) return;
244    if (wd) evas_object_hide(wd->cov);
245 }
246
247 static void
248 _parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
249 {
250    Widget_Data *wd = elm_widget_data_get(data);
251    if (!wd) return;
252    elm_hover_parent_set(data, NULL);
253    _sizing_eval(data);
254 }
255
256 /**
257  * Adds a hover object to @p parent
258  *
259  * @param parent The parent object
260  * @return The hover object or NULL if one could not be created
261  *
262  * @ingroup Hover
263  */
264 EAPI Evas_Object *
265 elm_hover_add(Evas_Object *parent)
266 {
267    Evas_Object *obj;
268    Evas *e;
269    Widget_Data *wd;
270
271    wd = ELM_NEW(Widget_Data);
272    e = evas_object_evas_get(parent);
273    obj = elm_widget_add(e);
274    ELM_SET_WIDTYPE(widtype, "hover");
275    elm_widget_type_set(obj, "hover");
276    elm_widget_sub_object_add(parent, obj);
277    elm_widget_data_set(obj, wd);
278    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
279    elm_widget_theme_hook_set(obj, _theme_hook);
280    elm_widget_del_hook_set(obj, _del_hook);
281
282    wd->hov = evas_object_rectangle_add(e);
283    evas_object_pass_events_set(wd->hov, 1);
284    evas_object_color_set(wd->hov, 0, 0, 0, 0);
285    elm_widget_resize_object_set(obj, wd->hov);
286    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_MOVE, _hov_move, obj);
287    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_RESIZE, _hov_resize, obj);
288    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_SHOW, _hov_show, obj);
289    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_HIDE, _hov_hide, obj);
290
291    wd->cov = edje_object_add(e);
292    _elm_theme_object_set(obj, wd->cov, "hover", "base", "default");
293    elm_widget_sub_object_add(obj, wd->cov);
294    edje_object_signal_callback_add(wd->cov, "elm,action,dismiss", "",
295                                    _signal_dismiss, obj);
296
297    wd->offset = evas_object_rectangle_add(e);
298    evas_object_pass_events_set(wd->offset, 1);
299    evas_object_color_set(wd->offset, 0, 0, 0, 0);
300    elm_widget_sub_object_add(obj, wd->offset);
301
302    wd->size = evas_object_rectangle_add(e);
303    evas_object_pass_events_set(wd->size, 1);
304    evas_object_color_set(wd->size, 0, 0, 0, 0);
305    elm_widget_sub_object_add(obj, wd->size);
306
307    edje_object_part_swallow(wd->cov, "elm.swallow.offset", wd->offset);
308    edje_object_part_swallow(wd->cov, "elm.swallow.size", wd->size);
309
310    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
311
312    _sizing_eval(obj);
313    return obj;
314 }
315
316 /**
317  * Sets the target object for the hover.
318  *
319  * @param obj The hover object
320  * @param target The object to center the hover onto. The hover
321  * will take up the entire space that the target object fills.
322  *
323  * @ingroup Hover
324  */
325 EAPI void
326 elm_hover_target_set(Evas_Object *obj, Evas_Object *target)
327 {
328    ELM_CHECK_WIDTYPE(obj, widtype);
329    Widget_Data *wd = elm_widget_data_get(obj);
330
331    if (wd->target)
332      evas_object_event_callback_del_full(wd->target, EVAS_CALLBACK_DEL,
333                                     _target_del, obj);
334    wd->target = target;
335    if (wd->target)
336      {
337         evas_object_event_callback_add(wd->target, EVAS_CALLBACK_DEL,
338                                        _target_del, obj);
339         elm_widget_hover_object_set(target, obj);
340         _sizing_eval(obj);
341      }
342 }
343
344
345 /**
346  * Sets the parent object for the hover.
347  *
348  * @param obj The hover object
349  * @param parent The object to locate the hover over.
350  *
351  * @ingroup Hover
352  */
353 EAPI void
354 elm_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
355 {
356    ELM_CHECK_WIDTYPE(obj, widtype);
357    Widget_Data *wd = elm_widget_data_get(obj);
358    if (!wd) return;
359    if (wd->parent)
360      {
361         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_MOVE,
362                                        _parent_move, obj);
363         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
364                                        _parent_resize, obj);
365         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_SHOW,
366                                        _parent_show, obj);
367         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_HIDE,
368                                        _parent_hide, obj);
369         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL,
370                                        _parent_del, obj);
371      }
372    wd->parent = parent;
373    if (wd->parent)
374      {
375         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_MOVE,
376                                        _parent_move, obj);
377         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE,
378                                        _parent_resize, obj);
379         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_SHOW,
380                                        _parent_show, obj);
381         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_HIDE,
382                                        _parent_hide, obj);
383         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL,
384                                        _parent_del, obj);
385 //      elm_widget_sub_object_add(parent, obj);
386      }
387    _sizing_eval(obj);
388 }
389
390 /**
391  * Gets the target object for the hover.
392  *
393  * @param obj The hover object
394  * @return The target object of the hover.
395  * 
396  * @ingroup Hover
397  */
398 EAPI Evas_Object *
399 elm_hover_target_get(Evas_Object *obj)
400 {
401    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
402    Widget_Data *wd = elm_widget_data_get(obj);
403    if (!wd) return NULL;
404
405    return wd->target;
406 }
407
408 /**
409  * Gets the parent object for the hover.
410  *
411  * @param obj The hover object
412  * @return The parent object to locate the hover over.
413  *
414  * @ingroup Hover
415  */
416 EAPI Evas_Object *
417 elm_hover_parent_get(Evas_Object *obj)
418 {
419    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
420    Widget_Data *wd = elm_widget_data_get(obj);
421    if (!wd) return NULL;
422
423    return wd->parent;
424 }
425
426 /**
427  * Sets the content of the hover object and the direction in which
428  * it will pop out.
429  *
430  * @param obj The hover object
431  * @param swallow The direction that the object will display in. Multiple
432  * objects can have the same swallow location. Objects placed in the same
433  * swallow will be placed starting at the middle of the hover and ending
434  * farther from the middle.
435  * Accepted values are "left" "right" "top" "bottom" "middle"
436  * @param content The content to place at @p swallow
437  *
438  * @ingroup Hover
439  */
440 EAPI void
441 elm_hover_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
442 {
443    ELM_CHECK_WIDTYPE(obj, widtype);
444    Widget_Data *wd = elm_widget_data_get(obj);
445    Subinfo *si;
446    const Eina_List *l;
447    char buf[1024];
448    if (!wd) return;
449    snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", swallow);
450    EINA_LIST_FOREACH(wd->subs, l, si)
451      {
452         if (!strcmp(buf, si->swallow))
453           {
454              if (content == si->obj) return;
455              elm_widget_sub_object_del(obj, si->obj);
456              break;
457           }
458      }
459    if (content)
460      {
461         elm_widget_sub_object_add(obj, content);
462         edje_object_part_swallow(wd->cov, buf, content);
463         si = ELM_NEW(Subinfo);
464         si->swallow = eina_stringshare_add(buf);
465         si->obj = content;
466         wd->subs = eina_list_append(wd->subs, si);
467         _sizing_eval(obj);
468      }
469 }
470
471 /**
472  * Returns the best swallow location for content in the hover.
473  *
474  * @param obj The hover object
475  * @return The edje location to place content into the hover.
476  * See also elm_hover_content_set()
477  *
478  * @ingroup Hover
479  */
480 EAPI const char *
481 elm_hover_best_content_location_get(const Evas_Object *obj, Elm_Hover_Axis pref_axis)
482 {
483    ELM_CHECK_WIDTYPE(obj, widtype) "left";
484    Widget_Data *wd = elm_widget_data_get(obj);
485    Evas_Coord x = 0, y = 0, w = 0, h = 0, x2 = 0, y2 = 0, w2 = 0, h2 = 0;
486    Evas_Coord spc_l, spc_r, spc_t, spc_b;
487    if (!wd) return "left";
488    if (wd->parent) evas_object_geometry_get(wd->parent, &x, &y, &w, &h);
489    if (wd->target) evas_object_geometry_get(wd->target, &x2, &y2, &w2, &h2);
490    spc_l = x2 - x;
491    spc_r = (x + w) - (x2 + w2);
492    if (spc_l < 0) spc_l = 0;
493    if (spc_r < 0) spc_r = 0;
494    spc_t = y2 - y;
495    spc_b = (y + h) - (y2 + h2);
496    if (spc_t < 0) spc_t = 0;
497    if (spc_b < 0) spc_b = 0;
498    if (pref_axis == ELM_HOVER_AXIS_HORIZONTAL)
499      {
500         if (spc_l < spc_r) return "right";
501         else return "left";
502      }
503    else if (pref_axis == ELM_HOVER_AXIS_VERTICAL)
504      {
505         if (spc_t < spc_b) return "bottom";
506         else return "top";
507      }
508    if (spc_l < spc_r)
509      {
510         if (spc_t > spc_r) return "top";
511         else if (spc_b > spc_r) return "bottom";
512         return "right";
513      }
514    if (spc_t > spc_r) return "top";
515    else if (spc_b > spc_r) return "bottom";
516    return "left";
517 }