From 5c61931bd1d9d04824d287ffda6716414eb837df Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Sat, 11 Sep 2010 00:52:33 +0000 Subject: [PATCH] Welcome Tooltips. Tooltips are set using elm_object_tooltip_content_cb_set(), that returns the desired Evas_Object to use as contents, or using the helper function elm_object_tooltip_text_set() that uses that underneath. The behavior is controlled part on elm_config, theme and user application. In elm_config one defines the tooltip timeout. The theme defines the padding around cursor x/y and window border x/y, as well as the look and feel. Last but not least, the user application may want to change the tooltip style with elm_object_tooltip_style_set(). Have fun! This code was initially written by Tiago Falcao and Fabiano Fidencio, I did some API review and changed some bits. TODO: elm widget item support, like with toolbar items. SVN revision: 52150 --- legacy/elementary/config/default/base.src | 1 + legacy/elementary/config/illume/base.src | 1 + legacy/elementary/config/standard/base.src | 1 + legacy/elementary/data/themes/default.edc | 84 +++ legacy/elementary/src/bin/Makefile.am | 3 +- legacy/elementary/src/bin/test.c | 2 + legacy/elementary/src/bin/test_tooltip.c | 237 ++++++++ legacy/elementary/src/lib/Elementary.h.in | 19 + legacy/elementary/src/lib/Makefile.am | 1 + legacy/elementary/src/lib/elm_config.c | 10 + legacy/elementary/src/lib/elm_priv.h | 5 + legacy/elementary/src/lib/elm_widget.c | 8 + legacy/elementary/src/lib/els_tooltip.c | 642 +++++++++++++++++++++ 13 files changed, 1013 insertions(+), 1 deletion(-) create mode 100644 legacy/elementary/src/bin/test_tooltip.c create mode 100644 legacy/elementary/src/lib/els_tooltip.c diff --git a/legacy/elementary/config/default/base.src b/legacy/elementary/config/default/base.src index f305f68d2d..a4ba56092f 100644 --- a/legacy/elementary/config/default/base.src +++ b/legacy/elementary/config/default/base.src @@ -20,4 +20,5 @@ group "Elm_Config" struct { value "fps" double: 60.0; value "theme" string: "default"; value "modules" string: ""; + value "tooltip_delay" double: 1.0; } diff --git a/legacy/elementary/config/illume/base.src b/legacy/elementary/config/illume/base.src index f305f68d2d..a4ba56092f 100644 --- a/legacy/elementary/config/illume/base.src +++ b/legacy/elementary/config/illume/base.src @@ -20,4 +20,5 @@ group "Elm_Config" struct { value "fps" double: 60.0; value "theme" string: "default"; value "modules" string: ""; + value "tooltip_delay" double: 1.0; } diff --git a/legacy/elementary/config/standard/base.src b/legacy/elementary/config/standard/base.src index f498b484c8..dd6bcef290 100644 --- a/legacy/elementary/config/standard/base.src +++ b/legacy/elementary/config/standard/base.src @@ -20,4 +20,5 @@ group "Elm_Config" struct { value "fps" double: 60.0; value "theme" string: "default"; value "modules" string: ""; + value "tooltip_delay" double: 1.0; } diff --git a/legacy/elementary/data/themes/default.edc b/legacy/elementary/data/themes/default.edc index 6f7aced682..da4e5c05ae 100644 --- a/legacy/elementary/data/themes/default.edc +++ b/legacy/elementary/data/themes/default.edc @@ -4424,6 +4424,90 @@ collections { } } +/////////////////////////////////////////////////////////////////////////////// + group { name: "elm/tooltip/base/default"; + //this group is a design similar to the inwin group + data { + item: "pad_x" "5"; + item: "pad_y" "5"; + item: "pad_border_x" "10"; + item: "pad_border_y" "10"; + } + images { + image: "shad_circ.png" COMP; + image: "bt_dis_base.png" COMP; + image: "bt_dis_hilight.png" COMP; + } + parts { + part { name: "base"; + type: RECT; + mouse_events: 0; + repeat_events: 1; + description { state: "default" 0.0; + color: 0 0 0 64; + rel1.offset: 10 10; + rel2.offset: -10 -10; + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 1.0; + } + } + part { name: "shad"; + mouse_events: 0; + description { state: "default" 0.0; + image.normal: "shad_circ.png"; + rel1.to: "elm.swallow.content"; + rel1.offset: -64 -64; + rel2.to: "elm.swallow.content"; + rel2.offset: 63 63; + fill.smooth: 0; + } + } + part { name: "pop"; + mouse_events: 1; + description { state: "default" 0.0; + rel1.to: "elm.swallow.content"; + rel1.offset: -5 -5; + rel2.to: "elm.swallow.content"; + rel2.offset: 4 4; + image { + normal: "bt_dis_base.png"; + border: 4 4 4 4; + } + image.middle: SOLID; + } + } + part { name: "popover"; + mouse_events: 0; + description { state: "default" 0.0; + rel1.to: "pop"; + rel2.to: "pop"; + rel2.relative: 1.0 0.5; + image { + normal: "bt_dis_hilight.png"; + border: 4 4 4 0; + } + } + } + part { name: "elm.swallow.content"; + type: SWALLOW; + description { state: "default" 0.0; + rel1.to: "base"; + rel2.to: "base"; + } + } + } + } + group { name: "elm/tooltip/base/transparent"; + parts { + part { name: "elm.swallow.content"; + type: SWALLOW; + mouse_events: 0; + scale: 1; + description { state: "default" 0.0; } + } + } + } + /////////////////////////////////////////////////////////////////////////////// group { name: "elm/hover/base/default"; images { diff --git a/legacy/elementary/src/bin/Makefile.am b/legacy/elementary/src/bin/Makefile.am index 04e5d277b7..cb1621280b 100644 --- a/legacy/elementary/src/bin/Makefile.am +++ b/legacy/elementary/src/bin/Makefile.am @@ -81,7 +81,8 @@ test_floating.c \ test_launcher.c \ test_anim.c \ test_calendar.c \ -test_drag.c +test_drag.c \ +test_tooltip.c elementary_test_LDADD = $(top_builddir)/src/lib/libelementary.la @ELEMENTARY_EWEATHER_LIBS@ elementary_test_LDFLAGS = diff --git a/legacy/elementary/src/bin/test.c b/legacy/elementary/src/bin/test.c index 06a9638514..13c8aecc37 100644 --- a/legacy/elementary/src/bin/test.c +++ b/legacy/elementary/src/bin/test.c @@ -80,6 +80,7 @@ void test_launcher(void *data, Evas_Object *obj, void *event_info); void test_launcher2(void *data, Evas_Object *obj, void *event_info); void test_launcher3(void *data, Evas_Object *obj, void *event_info); void test_anim(void *data, Evas_Object *obj, void *event_info); +void test_tooltip(void *data, Evas_Object *obj, void *event_info); void test_drag_source(void *data, Evas_Object *obj, void *event_info); void test_drag_dest(void *data, Evas_Object *obj, void *event_info); @@ -288,6 +289,7 @@ my_win_main(char *autorun) ADD_TEST("Animation", test_anim); ADD_TEST("Calendar", test_calendar); ADD_TEST("Calendar 2", test_calendar2); + ADD_TEST("Tooltip", test_tooltip); #undef ADD_TEST if (autorun) diff --git a/legacy/elementary/src/bin/test_tooltip.c b/legacy/elementary/src/bin/test_tooltip.c new file mode 100644 index 0000000000..acf6c65ec5 --- /dev/null +++ b/legacy/elementary/src/bin/test_tooltip.c @@ -0,0 +1,237 @@ +#include +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif +#ifndef ELM_LIB_QUICKLAUNCH + +static void +_tt_text_replace(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + static int count = 0; + char buf[64]; + snprintf(buf, sizeof(buf), "count=%d", count); + count++; + elm_object_tooltip_text_set(obj, buf); +} + +static void +_tt_timer_del(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Ecore_Timer *timer = evas_object_data_del(obj, "test-timer"); + if (!timer) return; + ecore_timer_del(timer); +} + +static Eina_Bool +_tt_text_replace_timer_cb(void *data) +{ + _tt_text_replace(NULL, data, NULL); + return EINA_TRUE; +} + +static void +_tt_text_replace_timed(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Ecore_Timer *timer = evas_object_data_get(obj, "test-timer"); + if (timer) + { + ecore_timer_del(timer); + evas_object_data_del(obj, "test-timer"); + elm_button_label_set(obj, "Simple text tooltip, click to start changed timed"); + return; + } + + timer = ecore_timer_add(1.5, _tt_text_replace_timer_cb, obj); + evas_object_data_set(obj, "test-timer", timer); + elm_button_label_set(obj, "Simple text tooltip, click to stop changed timed"); +} + +static Evas_Object * +_tt_icon(void *data __UNUSED__, Evas_Object *obj) +{ + Evas_Object *ic = elm_icon_add(obj); + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "%s/images/logo_small.png", PACKAGE_DATA_DIR); + elm_icon_file_set(ic, buf, NULL); + elm_icon_scale_set(ic, 0, 0); + evas_object_resize(ic, 64, 64); + return ic; +} + +static Evas_Object * +_tt_icon2(void *data __UNUSED__, Evas_Object *obj) +{ + Evas_Object *ic = elm_icon_add(obj); + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "%s/images/icon_00.png", PACKAGE_DATA_DIR); + elm_icon_file_set(ic, buf, NULL); + elm_icon_scale_set(ic, 0, 0); + evas_object_resize(ic, 64, 64); + return ic; +} + +static void +_tt_icon_del(void *data, Evas_Object *obj __UNUSED__, void *event_info) +{ + // test to check for del_cb behavior! + printf("_tt_icon_del: data=%ld (== 123?), event_info=%p\n", + (long)data, event_info); +} + +static Eina_Bool +_tt_icon_replace_timer_cb(void *data) +{ + static int current = 0; + + elm_object_tooltip_content_cb_set + (data, current ? _tt_icon2 : _tt_icon, NULL, NULL); + + current = !current; + return EINA_TRUE; +} + +static void +_tt_icon_replace_timed(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Ecore_Timer *timer = evas_object_data_get(obj, "test-timer"); + if (timer) + { + ecore_timer_del(timer); + evas_object_data_del(obj, "test-timer"); + elm_button_label_set(obj, "Icon tooltip, click to start changed timed"); + return; + } + + timer = ecore_timer_add(1.5, _tt_icon_replace_timer_cb, obj); + evas_object_data_set(obj, "test-timer", timer); + elm_button_label_set(obj, "Icon tooltip, click to stop changed timed"); +} + +static Eina_Bool +_tt_style_replace_timer_cb(void *data) +{ + static int current = 0; + elm_object_tooltip_style_set(data, current ? NULL : "transparent"); + current = !current; + return EINA_TRUE; +} + +static void +_tt_style_replace_timed(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Ecore_Timer *timer = evas_object_data_get(obj, "test-timer"); + if (timer) + { + ecore_timer_del(timer); + evas_object_data_del(obj, "test-timer"); + elm_button_label_set(obj, "Icon tooltip style, click to start changed timed"); + return; + } + + timer = ecore_timer_add(1.5, _tt_style_replace_timer_cb, obj); + evas_object_data_set(obj, "test-timer", timer); + elm_button_label_set(obj, "Icon tooltip style, click to stop changed timed"); +} + +static void +_tt_visible_lock_toggle(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + static int locked = 0; + + locked = !locked; + if (locked) + { + elm_button_label_set(obj, "Locked tooltip visibility"); + elm_object_tooltip_text_set(obj, "This tooltip is locked visible,
click the button to unlock!"); + elm_object_tooltip_show(obj); + } + else + { + elm_button_label_set(obj, "Unlocked tooltip visibility"); + elm_object_tooltip_text_set(obj, "This tooltip is unlocked visible,
click the button to lock!"); + elm_object_tooltip_hide(obj); + } + +} + + +void +test_tooltip(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *win, *bg, *bx, *bt; + + win = elm_win_add(NULL, "tooltip", ELM_WIN_BASIC); + elm_win_title_set(win, "Tooltip"); + elm_win_autodel_set(win, 1); + + bg = elm_bg_add(win); + elm_win_resize_object_add(win, bg); + evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(bg); + + bx = elm_box_add(win); + evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, bx); + evas_object_show(bx); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Simple text tooltip"); + elm_object_tooltip_text_set(bt, "Simple text tooltip"); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Simple text tooltip, click to change"); + elm_object_tooltip_text_set(bt, "Initial"); + evas_object_smart_callback_add(bt, "clicked", _tt_text_replace, NULL); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Simple text tooltip, click to start changed timed"); + elm_object_tooltip_text_set(bt, "Initial"); + evas_object_smart_callback_add(bt, "clicked", _tt_text_replace_timed, NULL); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + evas_object_event_callback_add(bt, EVAS_CALLBACK_DEL, _tt_timer_del, NULL); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Icon tooltip"); + elm_object_tooltip_content_cb_set(bt, _tt_icon, (void*)123L, _tt_icon_del); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Icon tooltip, click to start changed timed"); + elm_object_tooltip_content_cb_set(bt, _tt_icon, NULL, NULL); + evas_object_smart_callback_add(bt, "clicked", _tt_icon_replace_timed, NULL); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + evas_object_event_callback_add(bt, EVAS_CALLBACK_DEL, _tt_timer_del, NULL); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Transparent Icon tooltip"); + elm_object_tooltip_content_cb_set(bt, _tt_icon, NULL, NULL); + elm_object_tooltip_style_set(bt, "transparent"); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Icon tooltip style, click to start changed timed"); + elm_object_tooltip_content_cb_set(bt, _tt_icon, NULL, NULL); + evas_object_smart_callback_add(bt, "clicked", _tt_style_replace_timed, NULL); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + evas_object_event_callback_add(bt, EVAS_CALLBACK_DEL, _tt_timer_del, NULL); + + bt = elm_button_add(win); + elm_button_label_set(bt, "Unlocked tooltip visibility"); + elm_object_tooltip_text_set(bt, "This tooltip is unlocked visible,
click the button to lock!"); + evas_object_smart_callback_add(bt, "clicked", _tt_visible_lock_toggle, NULL); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + + evas_object_show(win); +} +#endif diff --git a/legacy/elementary/src/lib/Elementary.h.in b/legacy/elementary/src/lib/Elementary.h.in index 5d3e758e49..c7a92e23d3 100644 --- a/legacy/elementary/src/lib/Elementary.h.in +++ b/legacy/elementary/src/lib/Elementary.h.in @@ -224,6 +224,7 @@ extern "C" { ELM_CLOCK_ALL = (1 << 6) - 1 } Elm_Clock_Digedit; + #ifndef ELM_LIB_QUICKLAUNCH #define ELM_MAIN() int main(int argc, char **argv) {elm_init(argc, argv); return elm_main(argc, argv);} #else @@ -978,6 +979,24 @@ extern "C" { * "clicked" - when the user clicks on a toolbar item and becomes selected */ + typedef Evas_Object *(*Elm_Tooltip_Content_Cb) (void *data, Evas_Object *obj); + + /* available styles: + * default + * transparent (no background or shadow, just show the provided content) + */ + EAPI double elm_tooltip_delay_get(void); + EAPI Eina_Bool elm_tooltip_delay_set(double delay); + EAPI void elm_object_tooltip_show(Evas_Object *obj); + EAPI void elm_object_tooltip_hide(Evas_Object *obj); + EAPI void elm_object_tooltip_text_set(Evas_Object *obj, const char *text); + EAPI void elm_object_tooltip_content_cb_set(Evas_Object *obj, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb); + EAPI void elm_object_tooltip_unset(Evas_Object *obj); + + EAPI void elm_object_tooltip_style_set(Evas_Object *obj, const char *style); + EAPI const char *elm_object_tooltip_style_get(const Evas_Object *obj); + + typedef struct _Elm_Menu_Item Elm_Menu_Item; EAPI Evas_Object *elm_menu_add(Evas_Object *parent); EAPI void elm_menu_parent_set(Evas_Object *obj, Evas_Object *parent); diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index 0dd72c41e2..e209dc9fb7 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -88,6 +88,7 @@ elc_hoversel.c \ elc_notepad.c \ elc_scrolled_entry.c \ \ +els_tooltip.c \ els_pan.c \ els_pan.h \ els_scroller.c \ diff --git a/legacy/elementary/src/lib/elm_config.c b/legacy/elementary/src/lib/elm_config.c index fef7c634d2..12a409de87 100644 --- a/legacy/elementary/src/lib/elm_config.c +++ b/legacy/elementary/src/lib/elm_config.c @@ -224,6 +224,7 @@ _desc_init(void) EET_DATA_DESCRIPTOR_ADD_BASIC(_config_edd, Elm_Config, "fps", fps, EET_T_DOUBLE); EET_DATA_DESCRIPTOR_ADD_BASIC(_config_edd, Elm_Config, "theme", theme, EET_T_STRING); EET_DATA_DESCRIPTOR_ADD_BASIC(_config_edd, Elm_Config, "modules", modules, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_config_edd, Elm_Config, "tooltip_delay", tooltip_delay, EET_T_DOUBLE); } static void @@ -404,6 +405,7 @@ _config_load(void) _elm_config->fps = 60.0; _elm_config->theme = eina_stringshare_add("default"); _elm_config->modules = NULL; + _elm_config->tooltip_delay = 1.0; } static void @@ -588,6 +590,14 @@ _env_get(void) s = getenv("ELM_MODULES"); if (s) eina_stringshare_replace(&_elm_config->modules, s); + + s = getenv("ELM_TOOLTIP_DELAY"); + if (s) + { + double delay = atof(s); + if (delay >= 0.0) + _elm_config->tooltip_delay = delay; + } } void diff --git a/legacy/elementary/src/lib/elm_priv.h b/legacy/elementary/src/lib/elm_priv.h index aac6122ffb..e89b633be1 100644 --- a/legacy/elementary/src/lib/elm_priv.h +++ b/legacy/elementary/src/lib/elm_priv.h @@ -91,6 +91,7 @@ struct _Elm_Config double fps; const char *theme; const char *modules; + double tooltip_delay; }; typedef struct _Elm_Module Elm_Module; @@ -217,6 +218,10 @@ EAPI int elm_widget_drag_child_locked_y_get(const Evas_Object *obj); EAPI Eina_Bool elm_widget_is(const Evas_Object *obj); EAPI Evas_Object *elm_widget_parent_widget_get(const Evas_Object *obj); +typedef struct _Elm_Tooltip Elm_Tooltip; +void elm_widget_tooltip_set(Evas_Object *obj, Elm_Tooltip *tt); +void elm_tooltip_theme(Elm_Tooltip *tt); + EAPI Eina_List *_elm_stringlist_get(const char *str); EAPI void _elm_stringlist_free(Eina_List *list); diff --git a/legacy/elementary/src/lib/elm_widget.c b/legacy/elementary/src/lib/elm_widget.c index ff4217f31d..960b6981d0 100644 --- a/legacy/elementary/src/lib/elm_widget.c +++ b/legacy/elementary/src/lib/elm_widget.c @@ -21,6 +21,7 @@ struct _Smart_Data Eina_List *subobjs; Evas_Object *resize_obj; Evas_Object *hover_obj; + Elm_Tooltip *tooltip; void (*del_func) (Evas_Object *obj); void (*del_pre_func) (Evas_Object *obj); void (*focus_func) (Evas_Object *obj); @@ -203,6 +204,7 @@ elm_widget_theme(Evas_Object *obj) elm_widget_theme(child); if (sd->resize_obj) elm_widget_theme(sd->resize_obj); if (sd->hover_obj) elm_widget_theme(sd->hover_obj); + if (sd->tooltip) elm_tooltip_theme(sd->tooltip); if (sd->theme_func) sd->theme_func(obj); } @@ -1017,6 +1019,12 @@ elm_widget_type_get(const Evas_Object *obj) return ""; } +void +elm_widget_tooltip_set(Evas_Object *obj, Elm_Tooltip *tt) +{ + API_ENTRY return; + sd->tooltip = tt; +} diff --git a/legacy/elementary/src/lib/els_tooltip.c b/legacy/elementary/src/lib/els_tooltip.c new file mode 100644 index 0000000000..e912d582c2 --- /dev/null +++ b/legacy/elementary/src/lib/els_tooltip.c @@ -0,0 +1,642 @@ +#include +#include "elm_priv.h" + +/** + * @defgroup Tooltips Tooltips + * + * The Tooltip is an (internal, for now) smart object used to show a + * content in a frame on mouse hover of objects(or widgets), with + * tips/information about them. + */ + +static const char _tooltip_key[] = "_elm_tooltip"; + +#define ELM_TOOLTIP_GET_OR_RETURN(tt, obj, ...) \ + Elm_Tooltip *tt; \ + do \ + { \ + if (!(obj)) \ + { \ + CRITICAL("Null pointer: " #obj); \ + return __VA_ARGS__; \ + } \ + tt = evas_object_data_get((obj), _tooltip_key); \ + if (!tt) \ + { \ + ERR("Object does not have tooltip: " #obj); \ + return __VA_ARGS__; \ + } \ + } \ + while (0) + +struct _Elm_Tooltip +{ + Elm_Tooltip_Content_Cb func; + Evas_Smart_Cb del_cb; + const void *data; + const char *style; + Evas *evas; + Evas_Object *owner; + Evas_Object *tooltip, *content; + Ecore_Timer *show_timer; + Ecore_Job *reconfigure_job; + Ecore_Job *hide_job; + struct { + Evas_Coord x, y, bx, by; + } pad; + Eina_Bool visible_lock:1; + Eina_Bool changed_style:1; +}; + +static void _elm_tooltip_reconfigure(Elm_Tooltip *tt); +static void _elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt); +static void _elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt); +static void _elm_tooltip_hide_job_start(Elm_Tooltip *tt); +static void _elm_tooltip_hide_job_stop(Elm_Tooltip *tt); +static void _elm_tooltip_show_timer_stop(Elm_Tooltip *tt); +static void _elm_tooltip_hide(Elm_Tooltip *tt); + + +static void +_elm_tooltip_content_changed_hints_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + _elm_tooltip_reconfigure_job_start(data); +} + +static void +_elm_tooltip_content_del_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Elm_Tooltip *tt = data; + tt->content = NULL; + tt->visible_lock = EINA_FALSE; + _elm_tooltip_hide(tt); +} + +static void +_elm_tooltip_obj_move_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Elm_Tooltip *tt = data; + _elm_tooltip_reconfigure_job_start(tt); +} + +static void +_elm_tooltip_obj_resize_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Elm_Tooltip *tt = data; + _elm_tooltip_reconfigure_job_start(tt); +} + +static void +_elm_tooltip_obj_mouse_move_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Elm_Tooltip *tt = data; + _elm_tooltip_reconfigure_job_start(tt); +} + +static void +_elm_tooltip_show(Elm_Tooltip *tt) +{ + _elm_tooltip_show_timer_stop(tt); + _elm_tooltip_hide_job_stop(tt); + + if (tt->tooltip) return; + tt->tooltip = edje_object_add(tt->evas); + if (!tt->tooltip) return; + + evas_object_event_callback_add + (tt->owner, EVAS_CALLBACK_MOVE, _elm_tooltip_obj_move_cb, tt); + evas_object_event_callback_add + (tt->owner, EVAS_CALLBACK_RESIZE, _elm_tooltip_obj_resize_cb, tt); + evas_object_event_callback_add + (tt->owner, EVAS_CALLBACK_MOUSE_MOVE, _elm_tooltip_obj_mouse_move_cb, tt); + + evas_object_pass_events_set(tt->tooltip, EINA_TRUE); + tt->changed_style = EINA_TRUE; + _elm_tooltip_reconfigure_job_start(tt); +} + +static void +_elm_tooltip_content_del(Elm_Tooltip *tt) +{ + if (!tt->content) return; + + evas_object_event_callback_del_full + (tt->content, EVAS_CALLBACK_CHANGED_SIZE_HINTS, + _elm_tooltip_content_changed_hints_cb, tt); + evas_object_event_callback_del_full + (tt->content, EVAS_CALLBACK_DEL, + _elm_tooltip_content_del_cb, tt); + evas_object_del(tt->content); + tt->content = NULL; +} + + +static void +_elm_tooltip_hide(Elm_Tooltip *tt) +{ + _elm_tooltip_show_timer_stop(tt); + _elm_tooltip_hide_job_stop(tt); + _elm_tooltip_reconfigure_job_stop(tt); + + if (!tt->tooltip) return; + if (tt->visible_lock) return; + + _elm_tooltip_content_del(tt); + + evas_object_event_callback_del_full + (tt->owner, EVAS_CALLBACK_MOVE, _elm_tooltip_obj_move_cb, tt); + evas_object_event_callback_del_full + (tt->owner, EVAS_CALLBACK_RESIZE, _elm_tooltip_obj_resize_cb, tt); + evas_object_event_callback_del_full + (tt->owner, EVAS_CALLBACK_MOUSE_MOVE, _elm_tooltip_obj_mouse_move_cb, tt); + + evas_object_del(tt->tooltip); + tt->tooltip = NULL; +} + +static void +_elm_tooltip_reconfigure_job(void *data) +{ + Elm_Tooltip *tt = data; + tt->reconfigure_job = NULL; + _elm_tooltip_reconfigure(data); +} + +static void +_elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt) +{ + if (!tt->reconfigure_job) return; + ecore_job_del(tt->reconfigure_job); + tt->reconfigure_job = NULL; +} + +static void +_elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt) +{ + if (tt->reconfigure_job) ecore_job_del(tt->reconfigure_job); + tt->reconfigure_job = ecore_job_add + (_elm_tooltip_reconfigure_job, tt); +} + +static void +_elm_tooltip_hide_job(void *data) +{ + Elm_Tooltip *tt = data; + tt->hide_job = NULL; + _elm_tooltip_hide(data); +} + +static void +_elm_tooltip_hide_job_stop(Elm_Tooltip *tt) +{ + if (!tt->hide_job) return; + ecore_job_del(tt->hide_job); + tt->hide_job = NULL; +} + +static void +_elm_tooltip_hide_job_start(Elm_Tooltip *tt) +{ + if (tt->hide_job) ecore_job_del(tt->hide_job); + tt->hide_job = ecore_job_add(_elm_tooltip_hide_job, tt); +} + +static void +_elm_tooltip_reconfigure(Elm_Tooltip *tt) +{ + Evas_Coord ox, oy, ow, oh, px, py, tx, ty, tw, th, cw, ch; + Evas_Coord eminw, eminh, ominw, ominh; + + _elm_tooltip_reconfigure_job_stop(tt); + + if (!tt->tooltip) return; + if (tt->changed_style) + { + const char *style = tt->style ? tt->style : "default"; + const char *str; + if (!_elm_theme_object_set + (tt->owner, tt->tooltip, "tooltip", "base", style)) + { + ERR("Could not apply the theme to the tooltip! style=%s", style); + evas_object_del(tt->tooltip); + tt->tooltip = NULL; + return; + } + + tt->pad.x = 0; + tt->pad.y = 0; + tt->pad.bx = 0; + tt->pad.by = 0; + + str = edje_object_data_get(tt->tooltip, "pad_x"); + if (str) tt->pad.x = atoi(str); + str = edje_object_data_get(tt->tooltip, "pad_y"); + if (str) tt->pad.y = atoi(str); + + str = edje_object_data_get(tt->tooltip, "pad_border_x"); + if (str) tt->pad.bx = atoi(str); + str = edje_object_data_get(tt->tooltip, "pad_border_y"); + if (str) tt->pad.by = atoi(str); + + evas_object_pass_events_set(tt->tooltip, EINA_TRUE); + tt->changed_style = EINA_FALSE; + if (tt->tooltip) + edje_object_part_swallow + (tt->tooltip, "elm.swallow.content", tt->content); + } + + if (!tt->content) + { + tt->content = tt->func((void *)tt->data, tt->owner); + if (!tt->content) + { + WRN("could not create tooltip content!"); + evas_object_del(tt->tooltip); + tt->tooltip = NULL; + return; + } + evas_object_pass_events_set(tt->content, EINA_TRUE); + edje_object_part_swallow + (tt->tooltip, "elm.swallow.content", tt->content); + evas_object_event_callback_add + (tt->content, EVAS_CALLBACK_CHANGED_SIZE_HINTS, + _elm_tooltip_content_changed_hints_cb, tt); + evas_object_event_callback_add + (tt->content, EVAS_CALLBACK_DEL, + _elm_tooltip_content_del_cb, tt); + + } + + evas_object_size_hint_min_get(tt->content, &ominw, &ominh); + edje_object_size_min_get(tt->tooltip, &eminw, &eminh); + + if (ominw < eminw) ominw = eminw; + if (ominh < eminh) ominh = eminh; + + edje_object_size_min_restricted_calc + (tt->tooltip, &tw, &th, ominw, ominh); + + evas_output_size_get(tt->evas, &cw, &ch); + evas_pointer_canvas_xy_get(tt->evas, &px, &py); + + evas_object_geometry_get(tt->owner, &ox, &oy, &ow, &oh); + + if ((px >= ox) && (py >= oy) && (px <= ox + ow) && (py <= oy + oh)) + { + tx = px; + ty = py; + + if (tx + tw + tt->pad.x < cw) tx += tt->pad.x; + if (ty + th + tt->pad.y < ch) ty += tt->pad.y; + } + else + { + tx = ox + (ow / 2) - (tw / 2); + if (ch < (th + oy + oh)) ty = oy - th; + else ty = oy + oh; + } + + if (tt->pad.bx * 2 + tw < cw) + { + if (tx < tt->pad.bx) tx = tt->pad.bx; + else if (tx + tw >= cw - tt->pad.bx) tx = cw - tw - tt->pad.bx; + } + + if (tt->pad.by * 2 + th < ch) + { + if (ty < tt->pad.by) ty = tt->pad.by; + else if (ty + th >= ch - tt->pad.by) ty = ch - th - tt->pad.by; + } + + evas_object_move(tt->tooltip, tx, ty); + evas_object_resize(tt->tooltip, tw, th); + evas_object_show(tt->tooltip); +} + +static void +_elm_tooltip_show_timer_stop(Elm_Tooltip *tt) +{ + if (!tt->show_timer) return; + ecore_timer_del(tt->show_timer); + tt->show_timer = NULL; +} + +static Eina_Bool +_elm_tooltip_timer_show_cb(void *data) +{ + Elm_Tooltip *tt = data; + tt->show_timer = NULL; + _elm_tooltip_show(tt); + return ECORE_CALLBACK_CANCEL; +} + +static void +_elm_tooltip_obj_mouse_in_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Elm_Tooltip *tt = data; + + _elm_tooltip_hide_job_stop(tt); + if ((tt->show_timer) || (tt->tooltip)) return; + + tt->show_timer = ecore_timer_add + (_elm_config->tooltip_delay, _elm_tooltip_timer_show_cb, tt); +} + +static void +_elm_tooltip_obj_mouse_out_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + _elm_tooltip_hide_job_start(data); +} + +static void +_elm_tooltip_obj_del_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + elm_object_tooltip_unset(obj); +} + +static Evas_Object * +_elm_tooltip_label_create(void *data, Evas_Object *obj) +{ + Evas_Object *label = elm_label_add(obj); + if (!label) + return NULL; + elm_label_label_set(label, data); + return label; +} + +static void +_elm_tooltip_label_del_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + eina_stringshare_del(data); +} + +static void +_elm_tooltip_data_clean(Elm_Tooltip *tt) +{ + if (tt->del_cb) + tt->del_cb((void *)tt->data, tt->owner, tt->content); + + _elm_tooltip_content_del(tt); + + tt->data = NULL; + tt->del_cb = NULL; +} + +/** + * Notify tooltip should recalculate its theme. + * @internal + */ +void +elm_tooltip_theme(Elm_Tooltip *tt) +{ + if (!tt->tooltip) return; + tt->changed_style = EINA_TRUE; + _elm_tooltip_reconfigure_job_start(tt); +} + +/** + * Force show tooltip of object + * + * @param obj Target object + * + * Force show the tooltip and disable hide on mouse_out. + * If another content is set as tooltip, the visible tooltip will hididen and + * showed again with new content. + * This can force show more than one tooltip at a time. + * + * @ingroup Tooltips + */ +EAPI void +elm_object_tooltip_show(Evas_Object *obj) +{ + ELM_TOOLTIP_GET_OR_RETURN(tt, obj); + tt->visible_lock = EINA_TRUE; + _elm_tooltip_show(tt); +} + +/** + * Force hide tooltip of object + * + * @param obj Target object + * + * Force hide the tooltip and (re)enable future mouse interations. + * + * @ingroup Tooltips + */ +EAPI void +elm_object_tooltip_hide(Evas_Object *obj) +{ + ELM_TOOLTIP_GET_OR_RETURN(tt, obj); + tt->visible_lock = EINA_FALSE; + _elm_tooltip_hide_job_start(tt); +} + +/** + * Set the text to be shown in the tooltip object + * + * @param obj Target object + * @param text The text to set in the content + * + * Setup the text as tooltip to object. The object can have only one tooltip, + * so any previous tooltip data is removed. + * This method call internaly the elm_tooltip_content_cb_set(). + * + * @ingroup Tooltips + */ +EAPI void +elm_object_tooltip_text_set(Evas_Object *obj, const char *text) +{ + EINA_SAFETY_ON_NULL_RETURN(obj); + EINA_SAFETY_ON_NULL_RETURN(text); + + text = eina_stringshare_add(text); + elm_object_tooltip_content_cb_set + (obj, _elm_tooltip_label_create, text, _elm_tooltip_label_del_cb); +} + +/** + * Set the content to be shown in the tooltip object + * + * @param obj Target object + * @param func Fuction to be create tooltip content, called when need show + * tooltip. + * @param data Generic data pointer to be passed to func. + * @param del_cb Notfies when data should be deleted, this is either + * when another callback is set (or a text with + * elm_object_tooltip_text_set()) or when the owner @a obj dies. + * + * Setup the tooltip to object. The object can have only one tooltip, + * so any previous tooltip data is removed. @p func(with @p data) will + * be called every time that need show the tooltip and it should + * return a valid Evas_Object. This object is then managed fully by + * tooltip system and is deleted when the tooltip is gone. + * + * @param obj the object being attached a tooltip. + * @param func the function used to create the tooltip contents. + * @param data what to provide to @a func as callback data/context. + * @param del_cb called when data is not needed anymore, either when + * another callback replaces @func, the tooltip is unset with + * elm_object_tooltip_unset() or the owner object @a obj + * dies. This callback receives as the first parameter the + * given @a data, and as @c event_info the content object + * created with @a func, if any (do not modify this object, it + * is just for informational purposes and often may be NULL). + * + * @ingroup Tooltips + */ +EAPI void +elm_object_tooltip_content_cb_set(Evas_Object *obj, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb) +{ + Elm_Tooltip *tt = NULL; + Eina_Bool just_created; + + EINA_SAFETY_ON_NULL_GOTO(obj, error); + + if (!func) + { + elm_object_tooltip_unset(obj); + return; + } + + tt = evas_object_data_get(obj, _tooltip_key); + if (tt) + { + if ((tt->func == func) && (tt->data == data) && + (tt->del_cb == del_cb)) + return; + _elm_tooltip_data_clean(tt); + just_created = EINA_FALSE; + } + else + { + tt = ELM_NEW(Elm_Tooltip); + if (!tt) goto error; + + tt->owner = obj; + tt->evas = evas_object_evas_get(obj); + evas_object_data_set(obj, _tooltip_key, tt); + + just_created = EINA_TRUE; + + evas_object_event_callback_add + (obj, EVAS_CALLBACK_MOUSE_IN, _elm_tooltip_obj_mouse_in_cb, tt); + evas_object_event_callback_add + (obj, EVAS_CALLBACK_MOUSE_OUT, _elm_tooltip_obj_mouse_out_cb, tt); + evas_object_event_callback_add + (obj, EVAS_CALLBACK_DEL, _elm_tooltip_obj_del_cb, tt); + + elm_widget_tooltip_set(obj, tt); + } + + tt->func = func; + tt->data = data; + tt->del_cb = del_cb; + + if (!just_created) + { + _elm_tooltip_hide_job_stop(tt); + _elm_tooltip_reconfigure_job_start(tt); + } + return; + + error: + if (tt) _elm_tooltip_hide(tt); + if (del_cb) del_cb((void *)data, obj, NULL); +} + +/** + * Unset tooltip from object + * + * @param obj Target object + * + * Remove tooltip from object. The callback provided as del_cb to + * elm_object_tooltip_content_cb_set() will be called to notify it is + * not used anymore. + * + * @see elm_object_tooltip_content_cb_set() + * + * @ingroup Tooltips + */ +EAPI void +elm_object_tooltip_unset(Evas_Object *obj) +{ + ELM_TOOLTIP_GET_OR_RETURN(tt, obj); + + tt->visible_lock = EINA_FALSE; + _elm_tooltip_hide(tt); + _elm_tooltip_data_clean(tt); + + evas_object_event_callback_del_full + (obj, EVAS_CALLBACK_MOUSE_IN, _elm_tooltip_obj_mouse_in_cb, tt); + evas_object_event_callback_del_full + (obj, EVAS_CALLBACK_MOUSE_OUT, _elm_tooltip_obj_mouse_out_cb, tt); + evas_object_event_callback_del_full + (obj, EVAS_CALLBACK_DEL, _elm_tooltip_obj_del_cb, tt); + + evas_object_data_del(obj, _tooltip_key); + eina_stringshare_del(tt->style); + free(tt); +} + +/** + * Sets a different style for this object tooltip. + * + * @note before you set a style you should define a tooltip with + * elm_object_tooltip_content_cb_set() or + * elm_object_tooltip_text_set(). + * + * @param obj an object with tooltip already set. + * @param style the theme style to use (default, transparent, ...) + */ +EAPI void +elm_object_tooltip_style_set(Evas_Object *obj, const char *style) +{ + ELM_TOOLTIP_GET_OR_RETURN(tt, obj); + if (!eina_stringshare_replace(&tt->style, style)) return; + elm_tooltip_theme(tt); +} + +/** + * Get the style for this object tooltip. + * + * @param obj an object with tooltip already set. + * @return style the theme style in use, defaults to "default". If the + * object does not have a tooltip set, then NULL is returned. + */ +EAPI const char * +elm_object_tooltip_style_get(const Evas_Object *obj) +{ + ELM_TOOLTIP_GET_OR_RETURN(tt, obj, NULL); + return tt->style ? tt->style : "default"; +} + +/** + * Get the configured tooltip delay + * + * This gets the globally configured tooltip delay in seconds + * + * @return The tooltip delay + * @ingroup Tooltips + */ +EAPI double +elm_tooltip_delay_get(void) +{ + return _elm_config->tooltip_delay; +} + +/** + * Set the configured tooltip delay + * + * This sets the globally configured delay to tooltip + * + * @param delay The delay to show the tooltip + * @return EINA_TRUE if value is valid and setted + * @ingroup Tooltips + */ +EAPI Eina_Bool +elm_tooltip_delay_set(double delay) +{ + if (delay < 0.0) return EINA_FALSE; + _elm_config->tooltip_delay = delay; + return EINA_TRUE; +}