elementry / entry, menu, notify, list, gengrid, actionslider, image, icon, anchorview...
[framework/uifw/elementary.git] / src / lib / elm_bubble.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Bubble Bubble
6  *
7  * The Bubble is an widget used to show a text in a frame as speech is
8  * represented in comics.
9  *
10  * Signals that you can add callbacks for are:
11  *
12  * "clicked" - This is called when a user has clicked the bubble.
13  */
14
15 typedef struct _Widget_Data Widget_Data;
16
17 struct _Widget_Data
18 {
19    Evas_Object *bbl;
20    Evas_Object *content, *icon;
21    const char *label, *info, *corner;
22 };
23
24 static const char *widtype = NULL;
25 static void _del_hook(Evas_Object *obj);
26 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
27 static void _theme_hook(Evas_Object *obj);
28 static void _sizing_eval(Evas_Object *obj);
29 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
30 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
31
32 #define SIG_CLICKED "clicked"
33 static const Evas_Smart_Cb_Description _signals[] =
34 {
35      {SIG_CLICKED, ""},
36      {NULL, NULL}
37 };
38
39 static void
40 _del_hook(Evas_Object *obj)
41 {
42    Widget_Data *wd = elm_widget_data_get(obj);
43    if (!wd) return;
44    if (wd->label) eina_stringshare_del(wd->label);
45    if (wd->info) eina_stringshare_del(wd->info);
46    if (wd->corner) eina_stringshare_del(wd->corner);
47    free(wd);
48 }
49
50 static void
51 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
52 {
53    Widget_Data *wd = elm_widget_data_get(obj);
54    if (!wd) return;
55    edje_object_mirrored_set(wd->bbl, rtl);
56 }
57
58 static void
59 _theme_hook(Evas_Object *obj)
60 {
61    Widget_Data *wd = elm_widget_data_get(obj);
62    if (!wd) return;
63    _elm_widget_mirrored_reload(obj);
64    _mirrored_set(obj, elm_widget_mirrored_get(obj));
65    _elm_theme_object_set(obj, wd->bbl, "bubble", wd->corner,
66                          elm_widget_style_get(obj));
67    edje_object_part_text_set(wd->bbl, "elm.text", wd->label);
68    if (wd->label) edje_object_signal_emit(wd->bbl, "elm,state,text,visible", "elm");
69    else edje_object_signal_emit(wd->bbl, "elm,state,text,hidden", "elm");
70    edje_object_part_text_set(wd->bbl, "elm.info", wd->info);
71    if (wd->info) edje_object_signal_emit(wd->bbl, "elm,state,info,visible", "elm");
72    else edje_object_signal_emit(wd->bbl, "elm,state,info,hidden", "elm");
73    if (wd->content)
74      {
75         edje_object_part_swallow(wd->bbl, "elm.swallow.content", wd->content);
76         edje_object_message_signal_process(wd->bbl);
77      }
78    if (wd->icon)
79      edje_object_signal_emit(wd->bbl, "elm,state,icon,visible", "elm");
80    else
81      edje_object_signal_emit(wd->bbl, "elm,state,icon,hidden", "elm");
82    edje_object_scale_set(wd->bbl,
83                          elm_widget_scale_get(obj) * _elm_config->scale);
84    _sizing_eval(obj);
85 }
86
87 static Eina_Bool
88 _elm_bubble_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
89 {
90    Widget_Data *wd = elm_widget_data_get(obj);
91    Evas_Object *cur;
92
93    if ((!wd) || (!wd->content))
94      return EINA_FALSE;
95
96    cur = wd->content;
97
98    /* Try Focus cycle in subitem */
99    return elm_widget_focus_next_get(cur, dir, next);
100 }
101
102 static void
103 _sizing_eval(Evas_Object *obj)
104 {
105    Widget_Data *wd = elm_widget_data_get(obj);
106    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
107    if (!wd) return;
108    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
109    edje_object_size_min_restricted_calc(wd->bbl, &minw, &minh, minw, minh);
110    evas_object_size_hint_min_set(obj, minw, minh);
111    evas_object_size_hint_max_set(obj, maxw, maxh);
112 }
113
114 static void
115 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
116 {
117    Widget_Data *wd = elm_widget_data_get(data);
118    if (!wd) return;
119    _sizing_eval(data);
120 }
121
122 static void
123 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
124 {
125    Widget_Data *wd = elm_widget_data_get(obj);
126    Evas_Object *sub = event_info;
127    if (!wd) return;
128    evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
129                                        _changed_size_hints, obj);
130    if (sub == wd->content) wd->content = NULL;
131    else if (sub == wd->icon)
132      {
133         edje_object_signal_emit(wd->bbl, "elm,state,icon,hidden", "elm");
134         wd->icon = NULL;
135         edje_object_message_signal_process(wd->bbl);
136      }
137    _sizing_eval(obj);
138 }
139
140 static void
141 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
142 {
143    Evas_Event_Mouse_Up *ev = event_info;
144    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
145      return;
146    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
147 }
148
149 /**
150  * Add a new bubble to the parent
151  *
152  * @param parent The parent object
153  * @return The new object or NULL if it cannot be created
154  *
155  * This function adds a text bubble to the given parent evas object.
156  *
157  * @ingroup Bubble
158  */
159 EAPI Evas_Object *
160 elm_bubble_add(Evas_Object *parent)
161 {
162    Evas_Object *obj;
163    Evas *e;
164    Widget_Data *wd;
165
166    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
167
168    ELM_SET_WIDTYPE(widtype, "bubble");
169    elm_widget_type_set(obj, "bubble");
170    elm_widget_sub_object_add(parent, obj);
171    elm_widget_data_set(obj, wd);
172    elm_widget_del_hook_set(obj, _del_hook);
173    elm_widget_theme_hook_set(obj, _theme_hook);
174    elm_widget_focus_next_hook_set(obj, _elm_bubble_focus_next_hook);
175    elm_widget_can_focus_set(obj, EINA_FALSE);
176
177    wd->corner = eina_stringshare_add("base");
178
179    wd->bbl = edje_object_add(e);
180    elm_widget_resize_object_set(obj, wd->bbl);
181
182    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
183    evas_object_event_callback_add(wd->bbl, EVAS_CALLBACK_MOUSE_UP,
184                                   _mouse_up, obj);
185
186    evas_object_smart_callbacks_descriptions_set(obj, _signals);
187    _mirrored_set(obj, elm_widget_mirrored_get(obj));
188    _elm_theme_object_set(obj, wd->bbl, "bubble", wd->corner,
189                          elm_widget_style_get(obj));
190    _sizing_eval(obj);
191    return obj;
192 }
193
194 /**
195  * Set the label of the bubble
196  *
197  * @param obj The bubble object
198  * @param label The string to set in the label
199  *
200  * This function sets the title of the bubble that is shown on top of
201  * the bubble.
202  *
203  * @ingroup Bubble
204  */
205 EAPI void
206 elm_bubble_label_set(Evas_Object *obj, const char *label)
207 {
208    ELM_CHECK_WIDTYPE(obj, widtype);
209    Widget_Data *wd = elm_widget_data_get(obj);
210    if (!wd) return;
211    eina_stringshare_replace(&wd->label, label);
212    edje_object_part_text_set(wd->bbl, "elm.text", label);
213    if (label) edje_object_signal_emit(wd->bbl, "elm,state,text,visible", "elm");
214    else edje_object_signal_emit(wd->bbl, "elm,state,text,hidden", "elm");
215    _sizing_eval(obj);
216 }
217
218 /**
219  * Get the label of the bubble
220  *
221  * @param obj The bubble object
222  * @return The string of set in the label
223  *
224  * This function gets the title of the bubble that is shown on top of
225  * the bubble.
226  *
227  * @ingroup Bubble
228  */
229 EAPI const char*
230 elm_bubble_label_get(const Evas_Object *obj)
231 {
232    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
233    Widget_Data *wd = elm_widget_data_get(obj);
234    if (!wd) return NULL;
235    return wd->label;
236 }
237
238 /**
239  * Set the info of the bubble
240  *
241  * @param obj The bubble object
242  * @param info The given info about the bubble
243  *
244  * This function sets the text shown on the top right of bubble.
245  * In the Anchorblock example of the Elementary tests application it
246  * shows time.
247  *
248  * @ingroup Bubble
249  *
250  */
251 EAPI void
252 elm_bubble_info_set(Evas_Object *obj, const char *info)
253 {
254    ELM_CHECK_WIDTYPE(obj, widtype);
255    Widget_Data *wd = elm_widget_data_get(obj);
256    if (!wd) return;
257    eina_stringshare_replace(&wd->info, info);
258    edje_object_part_text_set(wd->bbl, "elm.info", info);
259    if (info) edje_object_signal_emit(wd->bbl, "elm,state,info,visible", "elm");
260    else edje_object_signal_emit(wd->bbl, "elm,state,info,hidden", "elm");
261    _sizing_eval(obj);
262 }
263
264 /**
265  * Get the info of the bubble
266  *
267  * @param obj The bubble object
268  *
269  * @return The "info" string of the bubble
270  *
271  * This function gets the text set to be displayed at the top right of
272  * the bubble.
273  *
274  * @ingroup Bubble
275  *
276  */
277 EAPI const char *
278 elm_bubble_info_get(const Evas_Object *obj)
279 {
280    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
281    Widget_Data *wd = elm_widget_data_get(obj);
282    if (!wd) return NULL;
283    return wd->info;
284 }
285
286 /**
287  * Set the content to be shown in the bubble
288  *
289  * Once the content object is set, a previously set one will be deleted.
290  * If you want to keep the old content object, use the
291  * elm_bubble_content_unset() function.
292  *
293  * @param obj The bubble object
294  * @param content The given content of the bubble
295  *
296  * This function sets the content shown on the middle of the bubble.
297  * In the Anchorblock example of the Elementary tests application it
298  * shows time.
299  *
300  * @ingroup Bubble
301  */
302 EAPI void
303 elm_bubble_content_set(Evas_Object *obj, Evas_Object *content)
304 {
305    ELM_CHECK_WIDTYPE(obj, widtype);
306    Widget_Data *wd = elm_widget_data_get(obj);
307    if (!wd) return;
308    if (wd->content == content) return;
309    if (wd->content) evas_object_del(wd->content);
310    wd->content = content;
311    if (content)
312      {
313         elm_widget_sub_object_add(obj, content);
314         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
315                                        _changed_size_hints, obj);
316         edje_object_part_swallow(wd->bbl, "elm.swallow.content", content);
317      }
318    _sizing_eval(obj);
319 }
320
321 /**
322  * Get the content shown in the bubble
323  *
324  * Return the content object which is set for this widget.
325  *
326  * @param obj The bubble object
327  * @return The content that is being used
328  *
329  * @ingroup Bubble
330  */
331 EAPI Evas_Object *
332 elm_bubble_content_get(const Evas_Object *obj)
333 {
334    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
335    Widget_Data *wd = elm_widget_data_get(obj);
336    if (!wd) return NULL;
337    return wd->content;
338 }
339
340 /**
341  * Unset the content shown in the bubble
342  *
343  * Unparent and return the content object which was set for this widget.
344  *
345  * @param obj The bubble object
346  * @return The content that was being used
347  *
348  * @ingroup Bubble
349  */
350 EAPI Evas_Object *
351 elm_bubble_content_unset(Evas_Object *obj)
352 {
353    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
354    Widget_Data *wd = elm_widget_data_get(obj);
355    Evas_Object *content;
356    if (!wd) return NULL;
357    if (!wd->content) return NULL;
358    content = wd->content;
359    elm_widget_sub_object_del(obj, content);
360    edje_object_part_unswallow(wd->bbl, content);
361    wd->content = NULL;
362    return content;
363 }
364
365 /**
366  * Set the icon of the bubble
367  *
368  * Once the icon object is set, a previously set one will be deleted.
369  * If you want to keep the old content object, use the
370  * elm_icon_content_unset() function.
371  *
372  * @param obj The bubble object
373  * @param icon The given icon for the bubble
374  *
375  * @ingroup Bubble
376  */
377 EAPI void
378 elm_bubble_icon_set(Evas_Object *obj, Evas_Object *icon)
379 {
380    ELM_CHECK_WIDTYPE(obj, widtype);
381    Widget_Data *wd = elm_widget_data_get(obj);
382    if (!wd) return;
383    if (wd->icon == icon) return;
384    if (wd->icon) evas_object_del(wd->icon);
385    wd->icon = icon;
386    if (icon)
387      {
388         elm_widget_sub_object_add(obj, icon);
389         edje_object_part_swallow(wd->bbl, "elm.swallow.icon", icon);
390         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
391                                        _changed_size_hints, obj);
392         edje_object_signal_emit(wd->bbl, "elm,state,icon,visible", "elm");
393         edje_object_message_signal_process(wd->bbl);
394      }
395    _sizing_eval(obj);
396 }
397
398 /**
399  * Get the icon of the bubble
400  *
401  * @param obj The bubble object
402  * @return The icon for the bubble
403  *
404  * This function gets the icon shown on the top left of bubble.
405  *
406  * @ingroup Bubble
407  */
408 EAPI Evas_Object *
409 elm_bubble_icon_get(const Evas_Object *obj)
410 {
411    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
412    Widget_Data *wd = elm_widget_data_get(obj);
413    if (!wd) return NULL;
414    return wd->icon;
415 }
416
417 /**
418  * Unset the icon of the bubble
419  *
420  * Unparent and return the icon object which was set for this widget.
421  *
422  * @param obj The bubble object
423  * @return The icon that was being used
424  *
425  * @ingroup Bubble
426  */
427 EAPI Evas_Object *
428 elm_bubble_icon_unset(Evas_Object *obj)
429 {
430    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
431    Widget_Data *wd = elm_widget_data_get(obj);
432    Evas_Object *icon;
433    if (!wd) return NULL;
434    if (!wd->icon) return NULL;
435    icon = wd->icon;
436    elm_widget_sub_object_del(obj, icon);
437    edje_object_part_unswallow(wd->bbl, icon);
438    wd->icon = NULL;
439    return icon;
440 }
441
442 /**
443  * Set the corner of the bubble
444  *
445  * @param obj The bubble object.
446  * @param corner The given corner for the bubble.
447  *
448  * This function sets the corner of the bubble.
449  * The corner will be used to find the group in the theme
450  * For example, if you set the corner to "bottom_right",
451  * the following group will be searched:
452  * "elm/bubble/bottom_right/default",
453  * considering default style.
454  *
455  * @ingroup Bubble
456  */
457 EAPI void
458 elm_bubble_corner_set(Evas_Object *obj, const char *corner)
459 {
460    ELM_CHECK_WIDTYPE(obj, widtype);
461    Widget_Data *wd = elm_widget_data_get(obj);
462    if (!wd) return;
463    EINA_SAFETY_ON_NULL_RETURN(corner);
464    eina_stringshare_replace(&wd->corner, corner);
465    _theme_hook(obj);
466 }
467
468 /**
469  * Get the corner of the bubble
470  *
471  * @param obj The bubble object.
472  * @return The given corner for the bubble.
473  *
474  * This function gets the corner of the bubble.
475  *
476  * @ingroup Bubble
477  */
478 EAPI const char*
479 elm_bubble_corner_get(const Evas_Object *obj)
480 {
481    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
482    Widget_Data *wd = elm_widget_data_get(obj);
483    if (!wd) return NULL;
484    return wd->corner;
485 }