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