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