Merge branch 'master' of hyoyoung.chang@165.213.180.234:/git/slp/pkgs/elementary
[framework/uifw/elementary.git] / src / lib / elm_layout.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Layout Layout
6  * @ingroup Elementary
7  *
8  * This takes a standard Edje design file and wraps it very thinly
9  * in a widget and handles swallowing widgets into swallow regions
10  * in the Edje object, allowing Edje to be used as a design and
11  * layout tool
12  */
13
14 typedef struct _Widget_Data Widget_Data;
15 typedef struct _Subinfo Subinfo;
16
17 struct _Widget_Data
18 {
19    Evas_Object *lay;
20    Eina_List *subs;
21    Eina_Bool needs_size_calc:1;
22    const char *clas, *group, *style;
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_hook(Evas_Object *obj);
33 static void _theme_hook(Evas_Object *obj);
34 static void _sizing_eval(Evas_Object *obj);
35 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
36 static void _show_hook(void *data, Evas *e, Evas_Object *obj, void *event_info);
37 static void _hide_hook(void *data, Evas *e, Evas_Object *obj, void *event_info);
38 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
39
40 static void
41 _del_hook(Evas_Object *obj)
42 {
43    Widget_Data *wd = elm_widget_data_get(obj);
44    Subinfo *si;
45    if (!wd) return;
46    EINA_LIST_FREE(wd->subs, si)
47      {
48    eina_stringshare_del(si->swallow);
49    free(si);
50      }
51    free(wd);
52 }
53
54 static void
55 _theme_hook(Evas_Object *obj)
56 {
57    Widget_Data *wd = elm_widget_data_get(obj);
58    if (!wd) return;
59    
60    _elm_theme_object_set(obj, wd->lay, wd->clas, wd->group, wd->style);
61    edje_object_scale_set(wd->lay, elm_widget_scale_get(obj) *
62                          _elm_config->scale);
63    _sizing_eval(obj);
64 }
65
66 static void
67 _changed_hook(Evas_Object *obj)
68 {
69    Widget_Data *wd = elm_widget_data_get(obj);
70    if (!wd) return;
71    if (wd->needs_size_calc)
72      {
73    _sizing_eval(obj);
74    wd->needs_size_calc = 0;
75      }
76 }
77
78 static void
79 _show_hook(void *data, Evas *e, Evas_Object *obj, void *event_info)
80 {
81    Widget_Data *wd = elm_widget_data_get(obj);
82    Eina_List *l;
83    Subinfo *si;
84    if (!wd) return;
85    EINA_LIST_FOREACH(wd->subs, l, si)
86      {
87         evas_object_show(si->obj);
88      }
89 }
90
91 static void
92 _hide_hook(void *data, Evas *e, Evas_Object *obj, void *event_info)
93 {
94    Widget_Data *wd = elm_widget_data_get(obj);
95    Eina_List *l;
96    Subinfo *si;
97    if (!wd) return;
98    EINA_LIST_FOREACH(wd->subs, l, si)
99      {
100         evas_object_hide(si->obj);
101      }
102 }
103
104 static void
105 _sizing_eval(Evas_Object *obj)
106 {
107    Widget_Data *wd = elm_widget_data_get(obj);
108    Evas_Coord minw = -1, minh = -1;
109    if (!wd) return;
110    edje_object_size_min_calc(wd->lay, &minw, &minh);
111    evas_object_size_hint_min_set(obj, minw, minh);
112    evas_object_size_hint_max_set(obj, -1, -1);
113 }
114
115 static void
116 _request_sizing_eval(Evas_Object *obj)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    if (!wd) return;
120    if (wd->needs_size_calc) return;
121    wd->needs_size_calc = 1;
122    evas_object_smart_changed(obj);
123 }
124
125 static void
126 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
127 {
128    _request_sizing_eval(data);
129 }
130
131 static void
132 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
133 {
134    Widget_Data *wd = elm_widget_data_get(obj);
135    Evas_Object *sub = event_info;
136    Eina_List *l;
137    Subinfo *si;
138    if (!wd) return;
139    EINA_LIST_FOREACH(wd->subs, l, si)
140      {
141    if (si->obj == sub)
142      {
143         evas_object_event_callback_del_full(sub,
144                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
145                                             _changed_size_hints,
146                                             obj);
147         wd->subs = eina_list_remove_list(wd->subs, l);
148         eina_stringshare_del(si->swallow);
149         free(si);
150         break;
151      }
152      }
153 }
154
155 static void
156 _signal_size_eval(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
157 {
158    _request_sizing_eval(data);
159 }
160
161 /**
162  * Add a new layout to the parent
163  *
164  * @param parent The parent object
165  * @return The new object or NULL if it cannot be created
166  *
167  * @ingroup Layout
168  */
169 EAPI Evas_Object *
170 elm_layout_add(Evas_Object *parent)
171 {
172    Evas_Object *obj;
173    Evas *e;
174    Widget_Data *wd;
175
176    wd = ELM_NEW(Widget_Data);
177    e = evas_object_evas_get(parent);
178    obj = elm_widget_add(e);
179    ELM_SET_WIDTYPE(widtype, "layout");
180    elm_widget_type_set(obj, "layout");
181    elm_widget_sub_object_add(parent, obj);
182    elm_widget_data_set(obj, wd);
183    elm_widget_del_hook_set(obj, _del_hook);
184    elm_widget_theme_hook_set(obj, _theme_hook);
185    elm_widget_changed_hook_set(obj, _changed_hook);
186
187    wd->lay = edje_object_add(e);
188    elm_widget_resize_object_set(obj, wd->lay);
189    edje_object_signal_callback_add(wd->lay, "size,eval", "elm",
190                                    _signal_size_eval, obj);
191    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
192    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _hide_hook, obj);
193    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show_hook, obj);
194
195    _request_sizing_eval(obj);
196    return obj;
197 }
198
199 /**
200  * Set the file that will be used as layout
201  *
202  * @param obj The layout object
203  * @param file The path to file (edj) that will be used as layout
204  * @param group The group that the layout belongs in edje file
205  *
206  * @return (1 = sucess, 0 = error)
207  *
208  * @ingroup Layout
209  */
210 EAPI Eina_Bool
211 elm_layout_file_set(Evas_Object *obj, const char *file, const char *group)
212 {
213    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
214    Widget_Data *wd = elm_widget_data_get(obj);
215    if (!wd) return EINA_FALSE;
216    Eina_Bool ret = edje_object_file_set(wd->lay, file, group);
217    if (ret) _request_sizing_eval(obj);
218    return ret;
219 }
220
221 /**
222  * Set the edje group from the elementary theme that will be used as layout
223  *
224  * @param obj The layout object
225  * @param clas the clas of the group
226  * @param group the group
227  * @param style the style to used
228  *
229  * @return (1 = sucess, 0 = error)
230  *
231  * @ingroup Layout
232  */
233 EAPI Eina_Bool
234 elm_layout_theme_set(Evas_Object *obj, const char *clas, const char *group, const char *style)
235 {
236    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
237    Widget_Data *wd = elm_widget_data_get(obj);
238    if (!wd) return EINA_FALSE;
239    Eina_Bool ret = _elm_theme_object_set(obj, wd->lay, clas, group, style);
240    wd->clas = clas;
241    wd->group = group;
242    wd->style = style;
243    if (ret) _request_sizing_eval(obj);
244    return ret;
245 }
246
247 /**
248  * Set the layout content
249  *
250  * @param obj The layout object
251  * @param swallow The swallow group name in the edje file
252  * @param content The content will be filled in this layout object
253  *
254  * @ingroup Layout
255  */
256 EAPI void
257 elm_layout_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
258 {
259    ELM_CHECK_WIDTYPE(obj, widtype);
260    Widget_Data *wd = elm_widget_data_get(obj);
261    Subinfo *si;
262    const Eina_List *l;
263    if (!wd) return;
264    EINA_LIST_FOREACH(wd->subs, l, si)
265      {
266    if (!strcmp(swallow, si->swallow))
267      {
268         if (content == si->obj) return;
269         elm_widget_sub_object_del(obj, si->obj);
270         break;
271      }
272      }
273    if (content)
274      {
275    elm_widget_sub_object_add(obj, content);
276    evas_object_event_callback_add(content,
277                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
278                    _changed_size_hints, obj);
279    edje_object_part_swallow(wd->lay, swallow, content);
280    si = ELM_NEW(Subinfo);
281    si->swallow = eina_stringshare_add(swallow);
282    si->obj = content;
283    wd->subs = eina_list_append(wd->subs, si);
284    _request_sizing_eval(obj);
285      }
286 }
287
288 /**
289  * Unset the layout content
290  *
291  * Unparent and return the content object which was set for this widget
292  *
293  * @param obj The layout object
294  * @param swallow The swallow group name in the edje file
295  * @return The content that was being used
296  *
297  * @ingroup Layout
298  */
299 EAPI Evas_Object *
300 elm_layout_content_unset(Evas_Object *obj, const char *swallow)
301 {
302    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
303    Widget_Data *wd = elm_widget_data_get(obj);
304    Subinfo *si;
305    const Eina_List *l;
306    if (!wd) return NULL;
307    EINA_LIST_FOREACH(wd->subs, l, si)
308      {
309    if (!strcmp(swallow, si->swallow))
310      {
311         Evas_Object *content;
312         if (!si->obj) return NULL;
313         content = si->obj; /* si will die in _sub_del due elm_widget_sub_object_del() */
314         elm_widget_sub_object_del(obj, content);
315         edje_object_part_unswallow(wd->lay, content);
316         return content;
317      }
318      }
319    return NULL;
320 }
321
322 /**
323  * Get the edje layout
324  *
325  * @param obj The layout object
326  * 
327  * This returns the edje object. It is not expected to be used to then swallow
328  * objects via edje_object_part_swallow() for example. Use 
329  * elm_layout_content_set() instead so child object handling and sizing is
330  * done properly. This is more intended for setting text, emitting signals,
331  * hooking to singal callbacks etc.
332  *
333  * @return A Evas_Object with the edje layout settings loaded
334  * with function elm_layout_file_set
335  *
336  * @ingroup Layout
337  */
338 EAPI Evas_Object *
339 elm_layout_edje_get(const Evas_Object *obj)
340 {
341    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
342    Widget_Data *wd = elm_widget_data_get(obj);
343    if (!wd) return NULL;
344    return wd->lay;
345 }
346
347 /**
348  * Get the edje layout
349  * 
350  * Manually forms a sizing re-evaluation when contents changed state so that
351  * minimum size might have changed and needs re-evaluation. Also note that
352  * a standard signal of "size,eval" "elm" emitted by the edje object will
353  * cause this to happen too
354  *
355  * @param obj The layout object
356  *
357  * @ingroup Layout
358  */
359 EAPI void
360 elm_layout_sizing_eval(Evas_Object *obj)
361 {
362    ELM_CHECK_WIDTYPE(obj, widtype);
363    _request_sizing_eval(obj);
364 }