1 #include <Elementary.h>
5 * @defgroup Tooltips Tooltips
7 * The Tooltip is an (internal, for now) smart object used to show a
8 * content in a frame on mouse hover of objects(or widgets), with
9 * tips/information about them.
12 static const char _tooltip_key[] = "_elm_tooltip";
14 #define ELM_TOOLTIP_GET_OR_RETURN(tt, obj, ...) \
20 CRITICAL("Null pointer: " #obj); \
23 tt = evas_object_data_get((obj), _tooltip_key); \
26 ERR("Object does not have tooltip: " #obj); \
34 Elm_Tooltip_Content_Cb func;
39 Evas_Object *eventarea, *owner;
40 Evas_Object *tooltip, *content;
41 Ecore_Timer *show_timer;
42 Ecore_Timer *hide_timer;
43 Ecore_Job *reconfigure_job;
45 Evas_Coord x, y, bx, by;
50 double hide_timeout; /* from theme */
51 Eina_Bool visible_lock:1;
52 Eina_Bool changed_style:1;
55 static void _elm_tooltip_reconfigure(Elm_Tooltip *tt);
56 static void _elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt);
57 static void _elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt);
58 static void _elm_tooltip_hide_anim_start(Elm_Tooltip *tt);
59 static void _elm_tooltip_hide_anim_stop(Elm_Tooltip *tt);
60 static void _elm_tooltip_show_timer_stop(Elm_Tooltip *tt);
61 static void _elm_tooltip_hide(Elm_Tooltip *tt);
62 static void _elm_tooltip_data_clean(Elm_Tooltip *tt);
66 _elm_tooltip_content_changed_hints_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
68 _elm_tooltip_reconfigure_job_start(data);
72 _elm_tooltip_content_del_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
74 Elm_Tooltip *tt = data;
76 tt->visible_lock = EINA_FALSE;
77 _elm_tooltip_hide(tt);
81 _elm_tooltip_obj_move_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
83 Elm_Tooltip *tt = data;
84 _elm_tooltip_reconfigure_job_start(tt);
88 _elm_tooltip_obj_resize_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
90 Elm_Tooltip *tt = data;
91 _elm_tooltip_reconfigure_job_start(tt);
95 _elm_tooltip_obj_mouse_move_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
97 Elm_Tooltip *tt = data;
98 _elm_tooltip_reconfigure_job_start(tt);
102 _elm_tooltip_show(Elm_Tooltip *tt)
104 _elm_tooltip_show_timer_stop(tt);
105 _elm_tooltip_hide_anim_stop(tt);
109 _elm_tooltip_reconfigure_job_start(tt);
112 tt->tooltip = edje_object_add(tt->evas);
113 if (!tt->tooltip) return;
115 evas_object_layer_set(tt->tooltip, ELM_OBJECT_LAYER_TOOLTIP);
117 evas_object_event_callback_add
118 (tt->eventarea, EVAS_CALLBACK_MOVE, _elm_tooltip_obj_move_cb, tt);
119 evas_object_event_callback_add
120 (tt->eventarea, EVAS_CALLBACK_RESIZE, _elm_tooltip_obj_resize_cb, tt);
121 evas_object_event_callback_add
122 (tt->eventarea, EVAS_CALLBACK_MOUSE_MOVE, _elm_tooltip_obj_mouse_move_cb, tt);
124 evas_object_pass_events_set(tt->tooltip, EINA_TRUE);
125 tt->changed_style = EINA_TRUE;
126 _elm_tooltip_reconfigure_job_start(tt);
130 _elm_tooltip_content_del(Elm_Tooltip *tt)
132 if (!tt->content) return;
134 evas_object_event_callback_del_full
135 (tt->content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
136 _elm_tooltip_content_changed_hints_cb, tt);
137 evas_object_event_callback_del_full
138 (tt->content, EVAS_CALLBACK_DEL,
139 _elm_tooltip_content_del_cb, tt);
140 evas_object_hide(tt->content);
141 evas_object_del(tt->content);
147 _elm_tooltip_hide(Elm_Tooltip *tt)
149 _elm_tooltip_show_timer_stop(tt);
150 _elm_tooltip_hide_anim_stop(tt);
151 _elm_tooltip_reconfigure_job_stop(tt);
153 if (!tt->tooltip) return;
154 if (tt->visible_lock) return;
156 _elm_tooltip_content_del(tt);
158 evas_object_event_callback_del_full
159 (tt->eventarea, EVAS_CALLBACK_MOVE, _elm_tooltip_obj_move_cb, tt);
160 evas_object_event_callback_del_full
161 (tt->eventarea, EVAS_CALLBACK_RESIZE, _elm_tooltip_obj_resize_cb, tt);
162 evas_object_event_callback_del_full
163 (tt->eventarea, EVAS_CALLBACK_MOUSE_MOVE, _elm_tooltip_obj_mouse_move_cb, tt);
165 evas_object_del(tt->tooltip);
170 _elm_tooltip_reconfigure_job(void *data)
172 Elm_Tooltip *tt = data;
173 tt->reconfigure_job = NULL;
174 _elm_tooltip_reconfigure(data);
178 _elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt)
180 if (!tt->reconfigure_job) return;
181 ecore_job_del(tt->reconfigure_job);
182 tt->reconfigure_job = NULL;
186 _elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt)
188 if (tt->reconfigure_job) ecore_job_del(tt->reconfigure_job);
189 tt->reconfigure_job = ecore_job_add
190 (_elm_tooltip_reconfigure_job, tt);
194 _elm_tooltip_hide_anim_cb(void *data)
196 Elm_Tooltip *tt = data;
197 tt->hide_timer = NULL;
198 _elm_tooltip_hide(tt);
203 _elm_tooltip_hide_anim_start(Elm_Tooltip *tt)
205 if (tt->hide_timer) return;
206 edje_object_signal_emit(tt->tooltip, "elm,action,hide", "elm");
207 tt->hide_timer = ecore_timer_add
208 (tt->hide_timeout, _elm_tooltip_hide_anim_cb, tt);
212 _elm_tooltip_hide_anim_stop(Elm_Tooltip *tt)
214 if (!tt->hide_timer) return;
216 edje_object_signal_emit(tt->tooltip, "elm,action,show", "elm");
217 ecore_timer_del(tt->hide_timer);
218 tt->hide_timer = NULL;
222 _elm_tooltip_reconfigure(Elm_Tooltip *tt)
224 Evas_Coord ox, oy, ow, oh, px, py, tx, ty, tw, th, cw, ch;
225 Evas_Coord eminw, eminh, ominw, ominh;
227 Eina_Bool inside_eventarea;
229 _elm_tooltip_reconfigure_job_stop(tt);
231 if (tt->hide_timer) return;
232 if (!tt->tooltip) return;
233 if (tt->changed_style)
235 const char *style = tt->style ? tt->style : "default";
237 if (!_elm_theme_object_set
238 (tt->owner, tt->tooltip, "tooltip", "base", style))
240 ERR("Could not apply the theme to the tooltip! style=%s", style);
241 evas_object_del(tt->tooltip);
253 tt->hide_timeout = 0.0;
255 str = edje_object_data_get(tt->tooltip, "pad_x");
256 if (str) tt->pad.x = atoi(str);
257 str = edje_object_data_get(tt->tooltip, "pad_y");
258 if (str) tt->pad.y = atoi(str);
260 str = edje_object_data_get(tt->tooltip, "pad_border_x");
261 if (str) tt->pad.bx = atoi(str);
262 str = edje_object_data_get(tt->tooltip, "pad_border_y");
263 if (str) tt->pad.by = atoi(str);
265 str = edje_object_data_get(tt->tooltip, "hide_timeout");
268 tt->hide_timeout = atof(str);
269 if (tt->hide_timeout < 0.0) tt->hide_timeout = 0.0;
272 evas_object_pass_events_set(tt->tooltip, EINA_TRUE);
273 tt->changed_style = EINA_FALSE;
275 edje_object_part_swallow
276 (tt->tooltip, "elm.swallow.content", tt->content);
278 edje_object_signal_emit(tt->tooltip, "elm,action,show", "elm");
283 tt->content = tt->func((void *)tt->data, tt->owner);
286 WRN("could not create tooltip content!");
287 evas_object_del(tt->tooltip);
291 evas_object_layer_set(tt->content, ELM_OBJECT_LAYER_TOOLTIP);
292 evas_object_pass_events_set(tt->content, EINA_TRUE);
293 edje_object_part_swallow
294 (tt->tooltip, "elm.swallow.content", tt->content);
295 evas_object_event_callback_add
296 (tt->content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
297 _elm_tooltip_content_changed_hints_cb, tt);
298 evas_object_event_callback_add
299 (tt->content, EVAS_CALLBACK_DEL,
300 _elm_tooltip_content_del_cb, tt);
304 evas_object_size_hint_min_get(tt->content, &ominw, &ominh);
305 edje_object_size_min_get(tt->tooltip, &eminw, &eminh);
307 if (ominw < eminw) ominw = eminw;
308 if (ominh < eminh) ominh = eminh;
310 if (ominw < 1) ominw = 10; /* at least it is noticeable */
311 if (ominh < 1) ominh = 10; /* at least it is noticeable */
313 edje_object_size_min_restricted_calc
314 (tt->tooltip, &tw, &th, ominw, ominh);
316 evas_output_size_get(tt->evas, &cw, &ch);
317 evas_pointer_canvas_xy_get(tt->evas, &px, &py);
319 evas_object_geometry_get(tt->eventarea, &ox, &oy, &ow, &oh);
321 inside_eventarea = ((px >= ox) && (py >= oy) &&
322 (px <= ox + ow) && (py <= oy + oh));
323 if (inside_eventarea)
328 if (tx + tw + tt->pad.x < cw) tx += tt->pad.x;
329 if (ty + th + tt->pad.y < ch) ty += tt->pad.y;
333 tx = ox + (ow / 2) - (tw / 2);
334 if (ch < (th + oy + oh)) ty = oy - th;
338 if (tt->pad.bx * 2 + tw < cw)
340 if (tx < tt->pad.bx) tx = tt->pad.bx;
341 else if (tx + tw >= cw - tt->pad.bx) tx = cw - tw - tt->pad.bx;
344 if (tt->pad.by * 2 + th < ch)
346 if (ty < tt->pad.by) ty = tt->pad.by;
347 else if (ty + th >= ch - tt->pad.by) ty = ch - th - tt->pad.by;
350 evas_object_move(tt->tooltip, tx, ty);
351 evas_object_resize(tt->tooltip, tw, th);
352 evas_object_show(tt->tooltip);
354 if (inside_eventarea)
356 rel_x = (px - tx) / (double)tw;
357 rel_y = (py - ty) / (double)th;
361 rel_x = (ox + (ow / 2) - tx) / (double)tw;
362 rel_y = (oy + (oh / 2) - ty) / (double)th;
365 #define FDIF(a, b) (fabs((a) - (b)) > 0.0001)
366 if ((FDIF(rel_x, tt->rel_pos.x)) || (FDIF(rel_y, tt->rel_pos.y)))
368 Edje_Message_Float_Set *msg;
370 msg = alloca(sizeof(Edje_Message_Float_Set) + sizeof(double));
374 tt->rel_pos.x = rel_x;
375 tt->rel_pos.y = rel_y;
377 edje_object_message_send(tt->tooltip, EDJE_MESSAGE_FLOAT_SET, 1, msg);
383 _elm_tooltip_show_timer_stop(Elm_Tooltip *tt)
385 if (!tt->show_timer) return;
386 ecore_timer_del(tt->show_timer);
387 tt->show_timer = NULL;
391 _elm_tooltip_timer_show_cb(void *data)
393 Elm_Tooltip *tt = data;
394 tt->show_timer = NULL;
395 _elm_tooltip_show(tt);
396 return ECORE_CALLBACK_CANCEL;
400 _elm_tooltip_obj_mouse_in_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
402 Elm_Tooltip *tt = data;
404 _elm_tooltip_hide_anim_stop(tt);
406 if ((tt->show_timer) || (tt->tooltip)) return;
408 tt->show_timer = ecore_timer_add
409 (_elm_config->tooltip_delay, _elm_tooltip_timer_show_cb, tt);
413 _elm_tooltip_obj_mouse_out_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
415 Elm_Tooltip *tt = data;
417 if (tt->visible_lock) return;
421 _elm_tooltip_show_timer_stop(tt);
425 _elm_tooltip_hide_anim_start(tt);
428 static void _elm_tooltip_obj_free_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__);
431 _elm_tooltip_unset(Elm_Tooltip *tt)
433 tt->visible_lock = EINA_FALSE;
434 _elm_tooltip_hide(tt);
435 _elm_tooltip_data_clean(tt);
439 evas_object_event_callback_del_full
440 (tt->eventarea, EVAS_CALLBACK_MOUSE_IN,
441 _elm_tooltip_obj_mouse_in_cb, tt);
442 evas_object_event_callback_del_full
443 (tt->eventarea, EVAS_CALLBACK_MOUSE_OUT,
444 _elm_tooltip_obj_mouse_out_cb, tt);
445 evas_object_event_callback_del_full
446 (tt->eventarea, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
448 evas_object_data_del(tt->eventarea, _tooltip_key);
452 evas_object_event_callback_del_full
453 (tt->owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
454 elm_widget_tooltip_del(tt->owner, tt);
457 eina_stringshare_del(tt->style);
462 _elm_tooltip_obj_free_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
464 Elm_Tooltip *tt = data;
465 if (tt->eventarea == obj) tt->eventarea = NULL;
466 if (tt->owner == obj) tt->owner = NULL;
467 _elm_tooltip_unset(tt);
471 _elm_tooltip_label_create(void *data, Evas_Object *obj)
473 Evas_Object *label = elm_label_add(obj);
476 elm_object_style_set(label, "tooltip");
477 elm_object_text_set(label, data);
482 _elm_tooltip_label_del_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
484 eina_stringshare_del(data);
488 _elm_tooltip_data_clean(Elm_Tooltip *tt)
490 if (tt->del_cb) tt->del_cb((void *)tt->data, tt->owner, NULL);
492 _elm_tooltip_content_del(tt);
499 * Notify tooltip should recalculate its theme.
503 elm_tooltip_theme(Elm_Tooltip *tt)
505 if (!tt->tooltip) return;
506 tt->changed_style = EINA_TRUE;
507 _elm_tooltip_reconfigure_job_start(tt);
512 * Set the content to be shown in the tooltip object for specific event area.
514 * Setup the tooltip to object. The object @a eventarea can have only
515 * one tooltip, so any previous tooltip data is removed. @p func(with
516 * @p data) will be called every time that need show the tooltip and
517 * it should return a valid Evas_Object. This object is then managed
518 * fully by tooltip system and is deleted when the tooltip is gone.
520 * This is an internal function that is used by objects with sub-items
521 * that want to provide different tooltips for each of them. The @a
522 * owner object should be an elm_widget and will be used to track
523 * theme changes and to feed @a func and @a del_cb. The @a eventarea
524 * may be any object and is the one that should be used later on with
525 * elm_object_tooltip apis, such as elm_object_tooltip_hide(),
526 * elm_object_tooltip_show() or elm_object_tooltip_unset().
528 * @param eventarea the object being attached a tooltip.
529 * @param owner the elm_widget that owns this object, will be used to
530 * track theme changes and to be used in @a func or @a del_cb.
531 * @param func the function used to create the tooltip contents. The
532 * @a Evas_Object parameters will receive @a owner as value.
533 * @param data what to provide to @a func as callback data/context.
534 * @param del_cb called when data is not needed anymore, either when
535 * another callback replaces @func, the tooltip is unset with
536 * elm_object_tooltip_unset() or the owner object @a obj
537 * dies. This callback receives as the first parameter the
538 * given @a data, and @c event_info is NULL.
544 elm_object_sub_tooltip_content_cb_set(Evas_Object *eventarea, Evas_Object *owner, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb)
546 Elm_Tooltip *tt = NULL;
547 Eina_Bool just_created;
549 EINA_SAFETY_ON_NULL_GOTO(owner, error);
550 EINA_SAFETY_ON_NULL_GOTO(eventarea, error);
554 elm_object_tooltip_unset(eventarea);
558 tt = evas_object_data_get(eventarea, _tooltip_key);
561 if (tt->owner != owner)
563 if (tt->owner != eventarea)
564 evas_object_event_callback_del_full
565 (tt->owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
567 elm_widget_tooltip_del(tt->owner, tt);
569 if (owner != eventarea)
570 evas_object_event_callback_add
571 (owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
573 elm_widget_tooltip_add(tt->owner, tt);
576 if ((tt->func == func) && (tt->data == data) &&
577 (tt->del_cb == del_cb))
579 _elm_tooltip_data_clean(tt);
580 just_created = EINA_FALSE;
584 tt = ELM_NEW(Elm_Tooltip);
588 tt->eventarea = eventarea;
589 tt->evas = evas_object_evas_get(eventarea);
590 evas_object_data_set(eventarea, _tooltip_key, tt);
592 just_created = EINA_TRUE;
594 evas_object_event_callback_add
595 (eventarea, EVAS_CALLBACK_MOUSE_IN,
596 _elm_tooltip_obj_mouse_in_cb, tt);
597 evas_object_event_callback_add
598 (eventarea, EVAS_CALLBACK_MOUSE_OUT,
599 _elm_tooltip_obj_mouse_out_cb, tt);
600 evas_object_event_callback_add
601 (eventarea, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
603 if (owner != eventarea)
604 evas_object_event_callback_add
605 (owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
607 elm_widget_tooltip_add(tt->owner, tt);
614 if (!just_created) _elm_tooltip_reconfigure_job_start(tt);
618 if (del_cb) del_cb((void *)data, owner, NULL);
622 * Force show tooltip of object
624 * @param obj Target object
626 * Force show the tooltip and disable hide on mouse_out.
627 * If another content is set as tooltip, the visible tooltip will hididen and
628 * showed again with new content.
629 * This can force show more than one tooltip at a time.
634 elm_object_tooltip_show(Evas_Object *obj)
636 ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
637 tt->visible_lock = EINA_TRUE;
638 _elm_tooltip_show(tt);
642 * Force hide tooltip of object
644 * @param obj Target object
646 * Force hide the tooltip and (re)enable future mouse interations.
651 elm_object_tooltip_hide(Evas_Object *obj)
653 ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
654 tt->visible_lock = EINA_FALSE;
655 _elm_tooltip_hide_anim_start(tt);
659 * Set the text to be shown in the tooltip object
661 * @param obj Target object
662 * @param text The text to set in the content
664 * Setup the text as tooltip to object. The object can have only one tooltip,
665 * so any previous tooltip data is removed.
666 * This method call internaly the elm_tooltip_content_cb_set().
671 elm_object_tooltip_text_set(Evas_Object *obj, const char *text)
673 EINA_SAFETY_ON_NULL_RETURN(obj);
674 EINA_SAFETY_ON_NULL_RETURN(text);
676 text = eina_stringshare_add(text);
677 elm_object_tooltip_content_cb_set
678 (obj, _elm_tooltip_label_create, text, _elm_tooltip_label_del_cb);
682 * Set the content to be shown in the tooltip object
684 * Setup the tooltip to object. The object can have only one tooltip,
685 * so any previous tooltip data is removed. @p func(with @p data) will
686 * be called every time that need show the tooltip and it should
687 * return a valid Evas_Object. This object is then managed fully by
688 * tooltip system and is deleted when the tooltip is gone.
690 * @param obj the object being attached a tooltip.
691 * @param func the function used to create the tooltip contents.
692 * @param data what to provide to @a func as callback data/context.
693 * @param del_cb called when data is not needed anymore, either when
694 * another callback replaces @func, the tooltip is unset with
695 * elm_object_tooltip_unset() or the owner object @a obj
696 * dies. This callback receives as the first parameter the
697 * given @a data, and @c event_info is NULL.
702 elm_object_tooltip_content_cb_set(Evas_Object *obj, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb)
704 elm_object_sub_tooltip_content_cb_set(obj, obj, func, data, del_cb);
708 * Unset tooltip from object
710 * @param obj Target object
712 * Remove tooltip from object. The callback provided as del_cb to
713 * elm_object_tooltip_content_cb_set() will be called to notify it is
716 * @see elm_object_tooltip_content_cb_set()
721 elm_object_tooltip_unset(Evas_Object *obj)
723 ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
724 _elm_tooltip_unset(tt);
728 * Sets a different style for this object tooltip.
730 * @note before you set a style you should define a tooltip with
731 * elm_object_tooltip_content_cb_set() or
732 * elm_object_tooltip_text_set().
734 * @param obj an object with tooltip already set.
735 * @param style the theme style to use (default, transparent, ...)
738 elm_object_tooltip_style_set(Evas_Object *obj, const char *style)
740 ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
741 if (!eina_stringshare_replace(&tt->style, style)) return;
742 elm_tooltip_theme(tt);
746 * Get the style for this object tooltip.
748 * @param obj an object with tooltip already set.
749 * @return style the theme style in use, defaults to "default". If the
750 * object does not have a tooltip set, then NULL is returned.
753 elm_object_tooltip_style_get(const Evas_Object *obj)
755 ELM_TOOLTIP_GET_OR_RETURN(tt, obj, NULL);
756 return tt->style ? tt->style : "default";
760 * Get the configured tooltip delay
762 * This gets the globally configured tooltip delay in seconds
764 * @return The tooltip delay
768 elm_tooltip_delay_get(void)
770 return _elm_config->tooltip_delay;
774 * Set the configured tooltip delay
776 * This sets the globally configured delay to tooltip
778 * @param delay The delay to show the tooltip
779 * @return EINA_TRUE if value is valid and setted
783 elm_tooltip_delay_set(double delay)
785 if (delay < 0.0) return EINA_FALSE;
786 _elm_config->tooltip_delay = delay;