use the new escape text set to clear up issues if theme has TEXT vs
[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_escaped_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_escaped_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    edje_object_part_unswallow(wd->frm, content);
192    return content;
193 }
194
195 static void
196 _recalc(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
197 {
198    _sizing_eval(data);
199 }
200
201 static void
202 _recalc_done(void *data, Evas_Object *obj __UNUSED__, const char *sig __UNUSED__, const char *src __UNUSED__)
203 {
204    Widget_Data *wd = elm_widget_data_get(data);
205    if (!wd) return;
206    evas_object_smart_callback_del(wd->frm, "recalc", _recalc);
207    wd->anim = EINA_FALSE;
208    _sizing_eval(data);
209 }
210
211 static void
212 _signal_click(void *data, Evas_Object *obj __UNUSED__, const char *sig __UNUSED__, const char *src __UNUSED__)
213 {
214    Widget_Data *wd = elm_widget_data_get(data);
215    if (!wd) return;
216    if (wd->anim) return;
217    if (wd->collapsible)
218      {
219         evas_object_smart_callback_add(wd->frm, "recalc", _recalc, data);
220         edje_object_signal_emit(wd->frm, "elm,action,toggle", "elm");
221         wd->collapsed++;
222         wd->anim = EINA_TRUE;
223      }
224    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
225 }
226
227 EAPI Evas_Object *
228 elm_frame_add(Evas_Object *parent)
229 {
230    Evas_Object *obj;
231    Evas *e;
232    Widget_Data *wd;
233
234    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
235
236    ELM_SET_WIDTYPE(widtype, "frame");
237    elm_widget_type_set(obj, "frame");
238    elm_widget_sub_object_add(parent, obj);
239    elm_widget_data_set(obj, wd);
240    elm_widget_del_hook_set(obj, _del_hook);
241    elm_widget_theme_hook_set(obj, _theme_hook);
242    elm_widget_focus_next_hook_set(obj, _elm_frame_focus_next_hook);
243    elm_widget_can_focus_set(obj, EINA_FALSE);
244    elm_widget_text_set_hook_set(obj, _elm_frame_label_set);
245    elm_widget_text_get_hook_set(obj, _elm_frame_label_get);
246    elm_widget_content_set_hook_set(obj, _content_set_hook);
247    elm_widget_content_get_hook_set(obj, _content_get_hook);
248    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
249
250    wd->frm = edje_object_add(e);
251    _elm_theme_object_set(obj, wd->frm, "frame", "base", "default");
252    elm_widget_resize_object_set(obj, wd->frm);
253
254    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
255    edje_object_signal_callback_add(wd->frm, "elm,anim,done", "elm",
256                                    _recalc_done, obj);
257    edje_object_signal_callback_add(wd->frm, "elm,action,click", "elm",
258                                    _signal_click, obj);
259    evas_object_smart_callbacks_descriptions_set(obj, _signals);
260
261    _mirrored_set(obj, elm_widget_mirrored_get(obj));
262    _sizing_eval(obj);
263    return obj;
264 }
265
266 EAPI void
267 elm_frame_autocollapse_set(Evas_Object *obj, Eina_Bool autocollapse)
268 {
269    Widget_Data *wd;
270    ELM_CHECK_WIDTYPE(obj, widtype);
271    wd = elm_widget_data_get(obj);
272    if (!wd) return;
273    wd->collapsible = !!autocollapse;
274 }
275
276 EAPI Eina_Bool
277 elm_frame_autocollapse_get(const Evas_Object *obj)
278 {
279    Widget_Data *wd;
280    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
281    wd = elm_widget_data_get(obj);
282    if (!wd) return EINA_FALSE;
283    return wd->collapsible;
284 }
285
286 EAPI void
287 elm_frame_collapse_set(Evas_Object *obj, Eina_Bool collapse)
288 {
289    Widget_Data *wd;
290    ELM_CHECK_WIDTYPE(obj, widtype);
291    wd = elm_widget_data_get(obj);
292    if (!wd) return;
293    collapse = !!collapse;
294    if (wd->collapsed == collapse) return;
295    edje_object_signal_emit(wd->frm, "elm,action,switch", "elm");
296    edje_object_message_signal_process(wd->frm);
297    wd->collapsed = !!collapse;
298    wd->anim = EINA_FALSE;
299    _sizing_eval(obj);
300 }
301
302 EAPI void
303 elm_frame_collapse_go(Evas_Object *obj, Eina_Bool collapse)
304 {
305    Widget_Data *wd;
306    ELM_CHECK_WIDTYPE(obj, widtype);
307    wd = elm_widget_data_get(obj);
308    if (!wd) return;
309    collapse = !!collapse;
310    if (wd->collapsed == collapse) return;
311    edje_object_signal_emit(wd->frm, "elm,action,toggle", "elm");
312    evas_object_smart_callback_add(wd->frm, "recalc", _recalc, obj);
313    wd->collapsed = collapse;
314    wd->anim = EINA_TRUE;
315 }
316
317 EAPI Eina_Bool
318 elm_frame_collapse_get(const Evas_Object *obj)
319 {
320    Widget_Data *wd;
321    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
322    wd = elm_widget_data_get(obj);
323    if (!wd) return EINA_FALSE;
324    return wd->collapsed;
325 }