make frame not suck too much but still doesnt "shade" properly - need
[framework/uifw/elementary.git] / src / lib / elm_frame.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object *frm;
9    Evas_Object *content;
10    const char *label;
11    unsigned int recalc_count;
12    Eina_Bool collapsed : 1;
13    Eina_Bool collapsible : 1;
14    Eina_Bool anim : 1;
15 };
16
17 static const char SIG_CLICKED[] = "clicked";
18
19 static const Evas_Smart_Cb_Description _signals[] = {
20    {SIG_CLICKED, ""},
21    {NULL, NULL}
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 static void
33 _del_hook(Evas_Object *obj)
34 {
35    Widget_Data *wd = elm_widget_data_get(obj);
36    if (!wd) return;
37    if (wd->label) eina_stringshare_del(wd->label);
38    free(wd);
39 }
40
41 static void
42 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
43 {
44    Widget_Data *wd = elm_widget_data_get(obj);
45    if (!wd) return;
46    edje_object_mirrored_set(wd->frm, rtl);
47 }
48
49 static void
50 _theme_hook(Evas_Object *obj)
51 {
52    Widget_Data *wd = elm_widget_data_get(obj);
53    if (!wd) return;
54    _elm_widget_mirrored_reload(obj);
55    _mirrored_set(obj, elm_widget_mirrored_get(obj));
56    _elm_theme_object_set(obj, wd->frm, "frame", "base", elm_widget_style_get(obj));
57    edje_object_part_text_set(wd->frm, "elm.text", wd->label);
58    if (wd->content)
59      edje_object_part_swallow(wd->frm, "elm.swallow.content", wd->content);
60    edje_object_scale_set(wd->frm, elm_widget_scale_get(obj) * _elm_config->scale);
61    _sizing_eval(obj);
62 }
63
64 static Eina_Bool
65 _elm_frame_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
66 {
67    Widget_Data *wd = elm_widget_data_get(obj);
68    Evas_Object *cur;
69
70    if ((!wd) || (!wd->content))
71      return EINA_FALSE;
72
73    cur = wd->content;
74
75    /* Try Focus cycle in subitem */
76    return elm_widget_focus_next_get(cur, dir, next);
77 }
78
79 static void
80 _sizing_eval(Evas_Object *obj)
81 {
82    Widget_Data *wd = elm_widget_data_get(obj);
83    Evas_Coord minw = -1, minh = -1;
84    Evas_Coord cminw = -1, cminh = -1;
85    if (!wd) return;
86    edje_object_size_min_calc(wd->frm, &minw, &minh);
87    evas_object_size_hint_min_get(obj, &cminw, &cminh);
88    if ((minw == cminw) && (minh == cminh)) return;
89    printf("min: %3.3f %ix%i\n", ecore_loop_time_get(), minw, minh);
90    evas_object_size_hint_min_set(obj, minw, minh);
91    evas_object_size_hint_max_set(obj, -1, -1);
92 }
93
94 static void
95 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
96 {
97    Widget_Data *wd = elm_widget_data_get(data);
98    // FIXME: why is this needed? how does edje get this unswallowed or
99    // lose its callbacks to edje
100    if (!wd) return;
101    if (wd->anim) return;
102    edje_object_part_swallow(wd->frm, "elm.swallow.content", wd->content);
103    _sizing_eval(data);
104 }
105
106 static void
107 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    Evas_Object *sub = event_info;
111    if (!wd) return;
112    if (sub == wd->content)
113      {
114         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
115                                             _changed_size_hints, obj);
116         wd->content = NULL;
117         _sizing_eval(obj);
118      }
119 }
120
121 static void
122 _elm_frame_label_set(Evas_Object *obj, const char *item, const char *label)
123 {
124    ELM_CHECK_WIDTYPE(obj, widtype);
125    Widget_Data *wd = elm_widget_data_get(obj);
126    if (item && strcmp(item, "default")) return;
127    if (!wd) return;
128    eina_stringshare_replace(&(wd->label), label);
129    edje_object_part_text_set(wd->frm, "elm.text", wd->label);
130    _sizing_eval(obj);
131 }
132
133 static const char *
134 _elm_frame_label_get(const Evas_Object *obj, const char *item)
135 {
136    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
137    Widget_Data *wd = elm_widget_data_get(obj);
138    if (item && strcmp(item, "default")) return NULL;
139    if (!wd) return NULL;
140    return wd->label;
141 }
142
143 static void
144 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
145 {
146    ELM_CHECK_WIDTYPE(obj, widtype);
147    Widget_Data *wd;
148
149    if (part && strcmp(part, "default")) return;
150    wd = elm_widget_data_get(obj);
151    if (!wd) return;
152    if (wd->content == content) return;
153    if (wd->content) evas_object_del(wd->content);
154    wd->content = content;
155    if (content)
156      {
157         elm_widget_sub_object_add(obj, content);
158         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
159                                        _changed_size_hints, obj);
160         edje_object_part_swallow(wd->frm, "elm.swallow.content", content);
161      }
162    _sizing_eval(obj);
163 }
164
165 static Evas_Object *
166 _content_get_hook(const Evas_Object *obj, const char *part)
167 {
168    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
169    Widget_Data *wd;
170
171    if (part && strcmp(part, "default")) return NULL;
172    wd = elm_widget_data_get(obj);
173    if (!wd) return NULL;
174    return wd->content;
175 }
176
177 static Evas_Object *
178 _content_unset_hook(Evas_Object *obj, const char *part)
179 {
180    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
181    Widget_Data *wd;
182    Evas_Object *content;
183    if (part && strcmp(part, "default")) return NULL;
184    wd = elm_widget_data_get(obj);
185    if (!wd) return NULL;
186    if (!wd->content) return NULL;
187    content = wd->content;
188    elm_widget_sub_object_del(obj, wd->content);
189    evas_object_event_callback_del_full(wd->content,
190                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
191                                        _changed_size_hints, obj);
192    edje_object_part_unswallow(wd->frm, wd->content);
193    wd->content = NULL;
194    return content;
195 }
196
197 static void
198 _recalc(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
199 {
200    printf("recalc %3.3f\n", ecore_loop_time_get());
201    _sizing_eval(data);
202 }
203
204 static void
205 _recalc_done(void *data, Evas_Object *obj __UNUSED__, const char *sig __UNUSED__, const char *src __UNUSED__)
206 {
207    Widget_Data *wd = elm_widget_data_get(data);
208    if (!wd) return;
209    printf("recalc done %3.3f\n", ecore_loop_time_get());
210    evas_object_smart_callback_del(wd->frm, "recalc", _recalc);
211    wd->anim = EINA_FALSE;
212    _sizing_eval(data);
213 }
214
215 static void
216 _signal_click(void *data, Evas_Object *obj __UNUSED__, const char *sig __UNUSED__, const char *src __UNUSED__)
217 {
218    Widget_Data *wd = elm_widget_data_get(data);
219    if (!wd) return;
220    if (wd->anim) return;
221    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
222    if (!wd->collapsible) return;
223    printf("recalc begin %3.3f\n", ecore_loop_time_get());
224    evas_object_smart_callback_add(wd->frm, "recalc", _recalc, data);
225    edje_object_signal_emit(wd->frm, "elm,action,collapse", "elm");
226    wd->collapsed++;
227    wd->anim = EINA_TRUE;
228 }
229
230 EAPI Evas_Object *
231 elm_frame_add(Evas_Object *parent)
232 {
233    Evas_Object *obj;
234    Evas *e;
235    Widget_Data *wd;
236
237    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
238
239    ELM_SET_WIDTYPE(widtype, "frame");
240    elm_widget_type_set(obj, "frame");
241    elm_widget_sub_object_add(parent, obj);
242    elm_widget_data_set(obj, wd);
243    elm_widget_del_hook_set(obj, _del_hook);
244    elm_widget_theme_hook_set(obj, _theme_hook);
245    elm_widget_focus_next_hook_set(obj, _elm_frame_focus_next_hook);
246    elm_widget_can_focus_set(obj, EINA_FALSE);
247    elm_widget_text_set_hook_set(obj, _elm_frame_label_set);
248    elm_widget_text_get_hook_set(obj, _elm_frame_label_get);
249    elm_widget_content_set_hook_set(obj, _content_set_hook);
250    elm_widget_content_get_hook_set(obj, _content_get_hook);
251    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
252
253    wd->frm = edje_object_add(e);
254    _elm_theme_object_set(obj, wd->frm, "frame", "base", "default");
255    elm_widget_resize_object_set(obj, wd->frm);
256
257    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
258    edje_object_signal_callback_add(wd->frm, "elm,anim,done", "elm",
259                                    _recalc_done, obj);
260    edje_object_signal_callback_add(wd->frm, "elm,action,click", "elm",
261                                    _signal_click, obj);
262    evas_object_smart_callbacks_descriptions_set(obj, _signals);
263
264    _mirrored_set(obj, elm_widget_mirrored_get(obj));
265    _sizing_eval(obj);
266    return obj;
267 }
268
269 EAPI void
270 elm_frame_autocollapse_set(Evas_Object *obj, Eina_Bool enable)
271 {
272    Widget_Data *wd;
273    ELM_CHECK_WIDTYPE(obj, widtype);
274    wd = elm_widget_data_get(obj);
275    if (!wd) return;
276    wd->collapsible = !!enable;
277 }
278
279 EAPI Eina_Bool
280 elm_frame_autocollapse_get(Evas_Object *obj)
281 {
282    Widget_Data *wd;
283    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
284    wd = elm_widget_data_get(obj);
285    if (!wd) return EINA_FALSE;
286    return wd->collapsible;
287 }
288
289 EAPI void
290 elm_frame_collapse_set(Evas_Object *obj, Eina_Bool enable)
291 {
292    Widget_Data *wd;
293    ELM_CHECK_WIDTYPE(obj, widtype);
294    wd = elm_widget_data_get(obj);
295    if (!wd) return;
296    enable = !!enable;
297    if (wd->collapsed == enable) return;
298    edje_object_signal_emit(wd->frm, "elm,action,collapse", "elm");
299    evas_object_smart_callback_add(wd->frm, "recalc", (Evas_Smart_Cb)_recalc, obj);
300    wd->collapsed = enable;
301    wd->anim = EINA_TRUE;
302 }
303
304 EAPI Eina_Bool
305 elm_frame_collapse_get(Evas_Object *obj)
306 {
307    Widget_Data *wd;
308    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
309    wd = elm_widget_data_get(obj);
310    if (!wd) return EINA_FALSE;
311    return wd->collapsed;
312 }
313
314
315 EAPI void
316 elm_frame_label_set(Evas_Object *obj, const char *label)
317 {
318    _elm_frame_label_set(obj, NULL, label);
319 }
320
321 EAPI const char *
322 elm_frame_label_get(const Evas_Object *obj)
323 {
324    return _elm_frame_label_get(obj, NULL);
325 }
326
327 EAPI void
328 elm_frame_content_set(Evas_Object *obj, Evas_Object *content)
329 {
330    _content_set_hook(obj, NULL, content);
331 }
332
333 EAPI Evas_Object *
334 elm_frame_content_get(const Evas_Object *obj)
335 {
336    return _content_get_hook(obj, NULL);
337 }
338
339 EAPI Evas_Object *
340 elm_frame_content_unset(Evas_Object *obj)
341 {
342    return _content_unset_hook(obj, NULL);
343 }