svn update: 49550 (latest:49550)
[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    if (wd) 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    _sizing_eval(obj);
312    return obj;
313 }
314
315 /**
316  * Sets the target object for the hover.
317  *
318  * @param obj The hover object
319  * @param target The object to center the hover onto. The hover
320  * will take up the entire space that the target object fills.
321  *
322  * @ingroup Hover
323  */
324 EAPI void
325 elm_hover_target_set(Evas_Object *obj, Evas_Object *target)
326 {
327    ELM_CHECK_WIDTYPE(obj, widtype);
328    Widget_Data *wd = elm_widget_data_get(obj);
329
330    if (wd->target)
331      evas_object_event_callback_del_full(wd->target, EVAS_CALLBACK_DEL,
332                                     _target_del, obj);
333    wd->target = target;
334    if (wd->target)
335      {
336         evas_object_event_callback_add(wd->target, EVAS_CALLBACK_DEL,
337                                        _target_del, obj);
338         elm_widget_hover_object_set(target, obj);
339         _sizing_eval(obj);
340      }
341 }
342
343
344 /**
345  * Sets the parent object for the hover.
346  *
347  * @param obj The hover object
348  * @param parent The object to locate the hover over.
349  *
350  * @ingroup Hover
351  */
352 EAPI void
353 elm_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
354 {
355    ELM_CHECK_WIDTYPE(obj, widtype);
356    Widget_Data *wd = elm_widget_data_get(obj);
357    if (!wd) return;
358    if (wd->parent)
359      {
360         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_MOVE,
361                                        _parent_move, obj);
362         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
363                                        _parent_resize, obj);
364         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_SHOW,
365                                        _parent_show, obj);
366         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_HIDE,
367                                        _parent_hide, obj);
368         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL,
369                                        _parent_del, obj);
370      }
371    wd->parent = parent;
372    if (wd->parent)
373      {
374         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_MOVE,
375                                        _parent_move, obj);
376         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE,
377                                        _parent_resize, obj);
378         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_SHOW,
379                                        _parent_show, obj);
380         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_HIDE,
381                                        _parent_hide, obj);
382         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL,
383                                        _parent_del, obj);
384 //      elm_widget_sub_object_add(parent, obj);
385      }
386    _sizing_eval(obj);
387 }
388
389 /**
390  * Gets the target object for the hover.
391  *
392  * @param obj The hover object
393  * @return The target object of the hover.
394  * 
395  * @ingroup Hover
396  */
397 EAPI Evas_Object *
398 elm_hover_target_get(Evas_Object *obj)
399 {
400    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
401    Widget_Data *wd = elm_widget_data_get(obj);
402    if (!wd) return NULL;
403
404    return wd->target;
405 }
406
407 /**
408  * Gets the parent object for the hover.
409  *
410  * @param obj The hover object
411  * @return The parent object to locate the hover over.
412  *
413  * @ingroup Hover
414  */
415 EAPI Evas_Object *
416 elm_hover_parent_get(Evas_Object *obj)
417 {
418    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
419    Widget_Data *wd = elm_widget_data_get(obj);
420    if (!wd) return NULL;
421
422    return wd->parent;
423 }
424
425 /**
426  * Sets the content of the hover object and the direction in which
427  * it will pop out.
428  *
429  * @param obj The hover object
430  * @param swallow The direction that the object will display in. Multiple
431  * objects can have the same swallow location. Objects placed in the same
432  * swallow will be placed starting at the middle of the hover and ending
433  * farther from the middle.
434  * Accepted values are "left" "right" "top" "bottom" "middle"
435  * @param content The content to place at @p swallow
436  *
437  * @ingroup Hover
438  */
439 EAPI void
440 elm_hover_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
441 {
442    ELM_CHECK_WIDTYPE(obj, widtype);
443    Widget_Data *wd = elm_widget_data_get(obj);
444    Subinfo *si;
445    const Eina_List *l;
446    char buf[1024];
447    if (!wd) return;
448    snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", swallow);
449    EINA_LIST_FOREACH(wd->subs, l, si)
450      {
451         if (!strcmp(buf, si->swallow))
452           {
453              if (content == si->obj) return;
454              elm_widget_sub_object_del(obj, si->obj);
455              break;
456           }
457      }
458    if (content)
459      {
460         elm_widget_sub_object_add(obj, content);
461         edje_object_part_swallow(wd->cov, buf, content);
462         si = ELM_NEW(Subinfo);
463         si->swallow = eina_stringshare_add(buf);
464         si->obj = content;
465         wd->subs = eina_list_append(wd->subs, si);
466         _sizing_eval(obj);
467      }
468 }
469
470 /**
471  * Returns the best swallow location for content in the hover.
472  *
473  * @param obj The hover object
474  * @return The edje location to place content into the hover.
475  * See also elm_hover_content_set()
476  *
477  * @ingroup Hover
478  */
479 EAPI const char *
480 elm_hover_best_content_location_get(const Evas_Object *obj, Elm_Hover_Axis pref_axis)
481 {
482    ELM_CHECK_WIDTYPE(obj, widtype) "left";
483    Widget_Data *wd = elm_widget_data_get(obj);
484    Evas_Coord x = 0, y = 0, w = 0, h = 0, x2 = 0, y2 = 0, w2 = 0, h2 = 0;
485    Evas_Coord spc_l, spc_r, spc_t, spc_b;
486    if (!wd) return "left";
487    if (wd->parent) evas_object_geometry_get(wd->parent, &x, &y, &w, &h);
488    if (wd->target) evas_object_geometry_get(wd->target, &x2, &y2, &w2, &h2);
489    spc_l = x2 - x;
490    spc_r = (x + w) - (x2 + w2);
491    if (spc_l < 0) spc_l = 0;
492    if (spc_r < 0) spc_r = 0;
493    spc_t = y2 - y;
494    spc_b = (y + h) - (y2 + h2);
495    if (spc_t < 0) spc_t = 0;
496    if (spc_b < 0) spc_b = 0;
497    if (pref_axis == ELM_HOVER_AXIS_HORIZONTAL)
498      {
499         if (spc_l < spc_r) return "right";
500         else return "left";
501      }
502    else if (pref_axis == ELM_HOVER_AXIS_VERTICAL)
503      {
504         if (spc_t < spc_b) return "bottom";
505         else return "top";
506      }
507    if (spc_l < spc_r)
508      {
509         if (spc_t > spc_r) return "top";
510         else if (spc_b > spc_r) return "bottom";
511         return "right";
512      }
513    if (spc_t > spc_r) return "top";
514    else if (spc_b > spc_r) return "bottom";
515    return "left";
516 }