3d, gesturelayer, index, naviframe, transit, fileselector, frame, glview, layout...
[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,
30                                 Evas *e, Evas_Object *obj,
31                                 void *event_info);
32 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
33
34 static void
35 _del_hook(Evas_Object *obj)
36 {
37    Widget_Data *wd = elm_widget_data_get(obj);
38    if (!wd) return;
39    if (wd->label) eina_stringshare_del(wd->label);
40    free(wd);
41 }
42
43 static void
44 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
45 {
46    Widget_Data *wd = elm_widget_data_get(obj);
47    if (!wd) return;
48    edje_object_mirrored_set(wd->frm, rtl);
49 }
50
51 static void
52 _theme_hook(Evas_Object *obj)
53 {
54    Widget_Data *wd = elm_widget_data_get(obj);
55    if (!wd) return;
56    _elm_widget_mirrored_reload(obj);
57    _mirrored_set(obj, elm_widget_mirrored_get(obj));
58    _elm_theme_object_set(obj, wd->frm, "frame", "base",
59                          elm_widget_style_get(obj));
60    edje_object_part_text_set(wd->frm, "elm.text", wd->label);
61    if (wd->content)
62      edje_object_part_swallow(wd->frm, "elm.swallow.content", wd->content);
63    edje_object_scale_set(wd->frm,
64                          elm_widget_scale_get(obj) * _elm_config->scale);
65    _sizing_eval(obj);
66 }
67
68 static Eina_Bool
69 _elm_frame_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
70 {
71    Widget_Data *wd = elm_widget_data_get(obj);
72
73    if ((!wd) || (!wd->content))
74      return EINA_FALSE;
75
76    /* Try Focus cycle in subitem */
77    return elm_widget_focus_next_get(wd->content, dir, next);
78 }
79
80 static void
81 _sizing_eval(Evas_Object *obj)
82 {
83    Widget_Data *wd = elm_widget_data_get(obj);
84    Evas_Coord minw = -1, minh = -1;
85    Evas_Coord cminw = -1, cminh = -1;
86    if (!wd) return;
87    edje_object_size_min_calc(wd->frm, &minw, &minh);
88    evas_object_size_hint_min_get(obj, &cminw, &cminh);
89    if ((minw == cminw) && (minh == cminh)) return;
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    if (!wd) return;
99    if (wd->anim) return;
100    // FIXME: why is this needed? how does edje get this unswallowed or
101    // lose its callbacks to edje
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,
115                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
116                                             _changed_size_hints, obj);
117         wd->content = NULL;
118         _sizing_eval(obj);
119      }
120 }
121
122 static void
123 _elm_frame_label_set(Evas_Object *obj, const char *item, const char *label)
124 {
125    ELM_CHECK_WIDTYPE(obj, widtype);
126    Widget_Data *wd = elm_widget_data_get(obj);
127    if (item && strcmp(item, "default")) return;
128    if (!wd) return;
129    eina_stringshare_replace(&(wd->label), label);
130    edje_object_part_text_set(wd->frm, "elm.text", wd->label);
131    _sizing_eval(obj);
132 }
133
134 static const char *
135 _elm_frame_label_get(const Evas_Object *obj, const char *item)
136 {
137    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
138    Widget_Data *wd = elm_widget_data_get(obj);
139    if (!wd) return NULL;
140    if (item && strcmp(item, "default")) return NULL;
141    return wd->label;
142 }
143
144 static void
145 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
146 {
147    ELM_CHECK_WIDTYPE(obj, widtype);
148    Widget_Data *wd;
149
150    if (part && strcmp(part, "default")) return;
151    wd = elm_widget_data_get(obj);
152    if (!wd) return;
153    if (wd->content == content) return;
154    if (wd->content) evas_object_del(wd->content);
155    wd->content = content;
156    if (content)
157      {
158         elm_widget_sub_object_add(obj, content);
159         evas_object_event_callback_add(content,
160                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
161                                        _changed_size_hints, obj);
162         edje_object_part_swallow(wd->frm, "elm.swallow.content", content);
163      }
164    _sizing_eval(obj);
165 }
166
167 static Evas_Object *
168 _content_get_hook(const Evas_Object *obj, const char *part)
169 {
170    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
171    Widget_Data *wd;
172
173    if (part && strcmp(part, "default")) return NULL;
174    wd = elm_widget_data_get(obj);
175    if (!wd) return NULL;
176
177    return wd->content;
178 }
179
180 static Evas_Object *
181 _content_unset_hook(Evas_Object *obj, const char *part)
182 {
183    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
184    Widget_Data *wd;
185    Evas_Object *content;
186    if (part && strcmp(part, "default")) return NULL;
187    wd = elm_widget_data_get(obj);
188    if (!wd || !wd->content) return NULL;
189    content = wd->content;
190    elm_widget_sub_object_del(obj, wd->content);
191    evas_object_event_callback_del_full(wd->content,
192                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
193                                        _changed_size_hints, obj);
194    edje_object_part_unswallow(wd->frm, wd->content);
195    wd->content = NULL;
196    return content;
197 }
198
199 static void
200 _recalc(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
201 {
202    _sizing_eval(data);
203 }
204
205 static void
206 _recalc_done(void *data, Evas_Object *obj __UNUSED__, const char *sig __UNUSED__, const char *src __UNUSED__)
207 {
208    Widget_Data *wd = elm_widget_data_get(data);
209    if (!wd) return;
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    if (wd->collapsible)
222      {
223         evas_object_smart_callback_add(wd->frm, "recalc", _recalc, data);
224         edje_object_signal_emit(wd->frm, "elm,action,toggle", "elm");
225         wd->collapsed++;
226         wd->anim = EINA_TRUE;
227      }
228    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
229 }
230
231 EAPI Evas_Object *
232 elm_frame_add(Evas_Object *parent)
233 {
234    Evas_Object *obj;
235    Evas *e;
236    Widget_Data *wd;
237
238    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
239
240    ELM_SET_WIDTYPE(widtype, "frame");
241    elm_widget_type_set(obj, "frame");
242    elm_widget_sub_object_add(parent, obj);
243    elm_widget_data_set(obj, wd);
244    elm_widget_del_hook_set(obj, _del_hook);
245    elm_widget_theme_hook_set(obj, _theme_hook);
246    elm_widget_focus_next_hook_set(obj, _elm_frame_focus_next_hook);
247    elm_widget_can_focus_set(obj, EINA_FALSE);
248    elm_widget_text_set_hook_set(obj, _elm_frame_label_set);
249    elm_widget_text_get_hook_set(obj, _elm_frame_label_get);
250    elm_widget_content_set_hook_set(obj, _content_set_hook);
251    elm_widget_content_get_hook_set(obj, _content_get_hook);
252    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
253
254    wd->frm = edje_object_add(e);
255    _elm_theme_object_set(obj, wd->frm, "frame", "base", "default");
256    elm_widget_resize_object_set(obj, wd->frm);
257
258    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
259    edje_object_signal_callback_add(wd->frm, "elm,anim,done", "elm",
260                                    _recalc_done, obj);
261    edje_object_signal_callback_add(wd->frm, "elm,action,click", "elm",
262                                    _signal_click, obj);
263    evas_object_smart_callbacks_descriptions_set(obj, _signals);
264
265    _mirrored_set(obj, elm_widget_mirrored_get(obj));
266    _sizing_eval(obj);
267    return obj;
268 }
269
270 EAPI void
271 elm_frame_autocollapse_set(Evas_Object *obj, Eina_Bool autocollapse)
272 {
273    Widget_Data *wd;
274    ELM_CHECK_WIDTYPE(obj, widtype);
275    wd = elm_widget_data_get(obj);
276    if (!wd) return;
277    wd->collapsible = !!autocollapse;
278 }
279
280 EAPI Eina_Bool
281 elm_frame_autocollapse_get(const Evas_Object *obj)
282 {
283    Widget_Data *wd;
284    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
285    wd = elm_widget_data_get(obj);
286    if (!wd) return EINA_FALSE;
287    return wd->collapsible;
288 }
289
290 EAPI void
291 elm_frame_collapse_set(Evas_Object *obj, Eina_Bool collapse)
292 {
293    Widget_Data *wd;
294    ELM_CHECK_WIDTYPE(obj, widtype);
295    wd = elm_widget_data_get(obj);
296    if (!wd) return;
297    collapse = !!collapse;
298    if (wd->collapsed == collapse) return;
299    edje_object_signal_emit(wd->frm, "elm,action,switch", "elm");
300    edje_object_message_signal_process(wd->frm);
301    wd->collapsed = !!collapse;
302    wd->anim = EINA_FALSE;
303    _sizing_eval(obj);
304 }
305
306 EAPI void
307 elm_frame_collapse_go(Evas_Object *obj, Eina_Bool collapse)
308 {
309    Widget_Data *wd;
310    ELM_CHECK_WIDTYPE(obj, widtype);
311    wd = elm_widget_data_get(obj);
312    if (!wd) return;
313    collapse = !!collapse;
314    if (wd->collapsed == collapse) return;
315    edje_object_signal_emit(wd->frm, "elm,action,toggle", "elm");
316    evas_object_smart_callback_add(wd->frm, "recalc", _recalc, obj);
317    wd->collapsed = collapse;
318    wd->anim = EINA_TRUE;
319 }
320
321 EAPI Eina_Bool
322 elm_frame_collapse_get(const Evas_Object *obj)
323 {
324    Widget_Data *wd;
325    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
326    wd = elm_widget_data_get(obj);
327    if (!wd) return EINA_FALSE;
328    return wd->collapsed;
329 }