Elementary: elm_frame documentation.
[framework/uifw/elementary.git] / src / lib / elm_panel.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Panel Panel
6  *
7  * A panel is a type of animated container that contains subobjects.  It
8  * can be expanded or contracted.
9  *
10  * Orientations are as follows:
11  * ELM_PANEL_ORIENT_TOP
12  * ELM_PANEL_ORIENT_BOTTOM
13  * ELM_PANEL_ORIENT_LEFT
14  * ELM_PANEL_ORIENT_RIGHT
15  * NOTE: Only LEFT and RIGHT orientations are implemented.
16  *
17  * THIS WIDGET IS UNDER CONSTRUCTION!
18  */
19
20 typedef struct _Widget_Data Widget_Data;
21 struct _Widget_Data
22 {
23    Evas_Object *scr, *bx, *content;
24    Elm_Panel_Orient orient;
25    Eina_Bool hidden : 1;
26 };
27
28 static const char *widtype = NULL;
29 static void _del_hook(Evas_Object *obj);
30 static void _theme_hook(Evas_Object *obj);
31 static void _on_focus_hook(void *data, Evas_Object *obj);
32 static void _sizing_eval(Evas_Object *obj);
33 static void _resize(void *data, Evas *evas, Evas_Object *obj, void *event);
34 static void _layout(Evas_Object *o, Evas_Object_Box_Data *priv, void *data);
35 static void _toggle_panel(void *data, Evas_Object *obj, const char *emission, const char *source);
36
37 static void
38 _del_hook(Evas_Object *obj)
39 {
40    Widget_Data *wd = elm_widget_data_get(obj);
41    if (!wd) return;
42    free(wd);
43 }
44
45 static void
46 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
47 {
48    Widget_Data *wd = elm_widget_data_get(obj);
49    if (!wd) return;
50
51    if (wd->scr)
52      {
53         elm_widget_mirrored_set(wd->bx, rtl);
54         elm_panel_orient_set(obj, elm_panel_orient_get(obj));
55      }
56 }
57
58 static void
59 _theme_hook(Evas_Object *obj)
60 {
61    Widget_Data *wd = elm_widget_data_get(obj);
62    if (!wd) return;
63    _elm_widget_mirrored_reload(obj);
64    if (wd->scr)
65      {
66         Evas_Object *edj;
67         const char *str;
68
69         _mirrored_set(obj, elm_widget_mirrored_get(obj));
70         elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base",
71                                             elm_widget_style_get(obj));
72         //   scale = (elm_widget_scale_get(obj) * _elm_config->scale);
73         //   edje_object_scale_set(wd->scr, scale);
74         edj = elm_smart_scroller_edje_object_get(wd->scr);
75         str = edje_object_data_get(edj, "focus_highlight");
76         if ((str) && (!strcmp(str, "on")))
77           elm_widget_highlight_in_theme_set(obj, EINA_TRUE);
78         else
79           elm_widget_highlight_in_theme_set(obj, EINA_FALSE);
80      }
81
82    _sizing_eval(obj);
83 }
84
85 static void
86 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
87 {
88    Widget_Data *wd = elm_widget_data_get(obj);
89    if (!wd) return;
90    if (elm_widget_focus_get(obj))
91      evas_object_focus_set(obj, EINA_TRUE);
92    else
93      evas_object_focus_set(obj, EINA_FALSE);
94 }
95
96 static Eina_Bool
97 _elm_panel_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
98 {
99    Widget_Data *wd = elm_widget_data_get(obj);
100    Evas_Object *cur;
101
102    if ((!wd) || (!wd->content))
103      return EINA_FALSE;
104
105    cur = wd->content;
106
107    /* Try Focus cycle in subitem */
108    if (!wd->hidden)
109      return elm_widget_focus_next_get(cur, dir, next);
110
111    /* Return */
112    *next = (Evas_Object *)obj;
113    return !elm_widget_focus_get(obj);
114 }
115
116 static void
117 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
118 {
119    Widget_Data *wd = elm_widget_data_get(obj);
120    if (!wd) return;
121    edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
122                            emission, source);
123 }
124
125 static void
126 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
127 {
128    Widget_Data *wd = elm_widget_data_get(obj);
129    if (!wd) return;
130    edje_object_signal_callback_add(elm_smart_scroller_edje_object_get(wd->scr),
131                                    emission, source, func_cb, data);
132 }
133
134 static void
135 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
136 {
137    Widget_Data *wd = elm_widget_data_get(obj);
138    edje_object_signal_callback_del_full(
139       elm_smart_scroller_edje_object_get(wd->scr), emission, source,
140       func_cb, data);
141 }
142
143 static void
144 _sizing_eval(Evas_Object *obj)
145 {
146    Widget_Data *wd = elm_widget_data_get(obj);
147    Evas_Coord mw = -1, mh = -1;
148    Evas_Coord vw = 0, vh = 0;
149    Evas_Coord w, h;
150    if (!wd) return;
151    evas_object_smart_calculate(wd->bx);
152    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr),
153                              &mw, &mh);
154    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
155    if (w < mw) w = mw;
156    if (h < mh) h = mh;
157    evas_object_resize(wd->scr, w, h);
158
159    evas_object_size_hint_min_get(wd->bx, &mw, &mh);
160    if (w > mw) mw = w;
161    if (h > mh) mh = h;
162    evas_object_resize(wd->bx, mw, mh);
163
164    elm_smart_scroller_child_viewport_size_get(wd->scr, &vw, &vh);
165    mw = mw + (w - vw);
166    mh = mh + (h - vh);
167    evas_object_size_hint_min_set(obj, mw, mh);
168    evas_object_size_hint_max_set(obj, -1, -1);
169 }
170
171 static void
172 _resize(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event __UNUSED__)
173 {
174    Widget_Data *wd = elm_widget_data_get(data);
175    Evas_Coord mw, mh, vw, vh, w, h;
176    if (!wd) return;
177    elm_smart_scroller_child_viewport_size_get(wd->scr, &vw, &vh);
178    evas_object_size_hint_min_get(wd->bx, &mw, &mh);
179    evas_object_geometry_get(wd->bx, NULL, NULL, &w, &h);
180    if ((vw >= mw) || (vh >= mh))
181      {
182         if ((w != vw) || (h != vh)) evas_object_resize(wd->bx, vw, vh);
183      }
184 }
185
186 static void
187 _layout(Evas_Object *o, Evas_Object_Box_Data *priv, void *data)
188 {
189    Widget_Data *wd = data;
190    if (!wd) return;
191    _els_box_layout(o, priv, EINA_TRUE, EINA_FALSE, EINA_FALSE);
192 }
193
194 static void
195 _toggle_panel(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__)
196 {
197    Widget_Data *wd = elm_widget_data_get(data);
198    if (!wd) return;
199    if (wd->hidden)
200      {
201         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
202                                 "elm,action,show", "elm");
203         wd->hidden = EINA_FALSE;
204         evas_object_repeat_events_set(obj, EINA_FALSE);
205      }
206    else
207      {
208         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
209                                 "elm,action,hide", "elm");
210         wd->hidden = EINA_TRUE;
211         evas_object_repeat_events_set(obj, EINA_TRUE);
212         if (elm_widget_focus_get(wd->content))
213           {
214              elm_widget_focused_object_clear(obj);
215              elm_widget_focus_steal(obj);
216           }
217      }
218 }
219
220 static Eina_Bool
221 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
222 {
223    if ((src != obj) || (type != EVAS_CALLBACK_KEY_DOWN)) return EINA_FALSE;
224
225    Evas_Event_Key_Down *ev = event_info;
226    Widget_Data *wd = elm_widget_data_get(obj);
227    if (!wd) return EINA_FALSE;
228
229    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
230
231    if ((strcmp(ev->keyname, "Return")) &&
232        (strcmp(ev->keyname, "KP_Enter")) &&
233        (strcmp(ev->keyname, "space")))
234      return EINA_FALSE;
235
236    _toggle_panel(obj, NULL, "elm,action,panel,toggle", "*");
237
238    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
239    return EINA_TRUE;
240 }
241
242 /**
243  * Adds a panel object
244  *
245  * @param parent The parent object
246  *
247  * @return The panel object, or NULL on failure
248  *
249  * @ingroup Panel
250  */
251 EAPI Evas_Object *
252 elm_panel_add(Evas_Object *parent)
253 {
254    Evas_Object *obj;
255    Evas *e;
256    Widget_Data *wd;
257
258    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
259
260    ELM_SET_WIDTYPE(widtype, "panel");
261    elm_widget_type_set(obj, "panel");
262    elm_widget_sub_object_add(parent, obj);
263    elm_widget_data_set(obj, wd);
264    elm_widget_del_hook_set(obj, _del_hook);
265    elm_widget_theme_hook_set(obj, _theme_hook);
266    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
267    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
268    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
269    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
270    elm_widget_focus_next_hook_set(obj, _elm_panel_focus_next_hook);
271    elm_widget_can_focus_set(obj, EINA_TRUE);
272    elm_widget_event_hook_set(obj, _event_hook);
273
274    wd->scr = elm_smart_scroller_add(e);
275    elm_smart_scroller_widget_set(wd->scr, obj);
276    _theme_hook(obj);
277    elm_smart_scroller_bounce_allow_set(wd->scr, EINA_FALSE, EINA_FALSE);
278    elm_widget_resize_object_set(obj, wd->scr);
279    elm_smart_scroller_policy_set(wd->scr, ELM_SMART_SCROLLER_POLICY_OFF,
280                                  ELM_SMART_SCROLLER_POLICY_OFF);
281
282    wd->hidden = EINA_FALSE;
283    wd->orient = ELM_PANEL_ORIENT_LEFT;
284
285    wd->bx = evas_object_box_add(e);
286    evas_object_size_hint_align_set(wd->bx, 0.5, 0.5);
287    evas_object_box_layout_set(wd->bx, _layout, wd, NULL);
288    elm_widget_sub_object_add(obj, wd->bx);
289    elm_smart_scroller_child_set(wd->scr, wd->bx);
290    evas_object_show(wd->bx);
291
292    edje_object_signal_callback_add(elm_smart_scroller_edje_object_get(wd->scr),
293                                    "elm,action,panel,toggle", "*",
294                                    _toggle_panel, obj);
295
296    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_RESIZE, _resize, obj);
297
298    _mirrored_set(obj, elm_widget_mirrored_get(obj));
299    _sizing_eval(obj);
300    return obj;
301 }
302
303 /**
304  * Sets the orientation of the panel
305  *
306  * @param parent The parent object
307  * @param orient The panel orientation.  Can be one of the following:
308  * ELM_PANEL_ORIENT_TOP
309  * ELM_PANEL_ORIENT_BOTTOM
310  * ELM_PANEL_ORIENT_LEFT
311  * ELM_PANEL_ORIENT_RIGHT
312  *
313  * NOTE: Only LEFT and RIGHT orientations are implemented.
314  *
315  * @ingroup Panel
316  */
317 EAPI void
318 elm_panel_orient_set(Evas_Object *obj, Elm_Panel_Orient orient)
319 {
320    ELM_CHECK_WIDTYPE(obj, widtype);
321    Widget_Data *wd = elm_widget_data_get(obj);
322    if (!wd) return;
323    wd->orient = orient;
324    switch (orient)
325      {
326       case ELM_PANEL_ORIENT_TOP:
327          elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "top");
328          break;
329       case ELM_PANEL_ORIENT_BOTTOM:
330          elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "bottom");
331          break;
332       case ELM_PANEL_ORIENT_LEFT:
333          if (!elm_widget_mirrored_get(obj))
334            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "left");
335          else
336            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "right");
337          break;
338       case ELM_PANEL_ORIENT_RIGHT:
339          if (!elm_widget_mirrored_get(obj))
340            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "right");
341          else
342            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "left");
343          break;
344      }
345
346    _sizing_eval(obj);
347 }
348
349 /**
350  * Get the orientation of the panel.
351  *
352  * @param obj The panel object
353  * @return The Elm_Panel_Orient, or ELM_PANEL_ORIENT_LEFT on failure.
354  *
355  * @ingroup Panel
356  */
357 EAPI Elm_Panel_Orient
358 elm_panel_orient_get(const Evas_Object *obj)
359 {
360    ELM_CHECK_WIDTYPE(obj, widtype) ELM_PANEL_ORIENT_LEFT;
361    Widget_Data *wd = elm_widget_data_get(obj);
362    if (!wd) return ELM_PANEL_ORIENT_LEFT;
363    return wd->orient;
364 }
365
366 /**
367  * Set the content of the panel.
368  *
369  * Once the content object is set, a previously set one will be deleted.
370  * If you want to keep that old content object, use the
371  * elm_panel_content_unset() function.
372  *
373  * @param obj The panel object
374  * @param content The panel content
375  *
376  * @ingroup Panel
377  */
378 EAPI void
379 elm_panel_content_set(Evas_Object *obj, Evas_Object *content)
380 {
381    ELM_CHECK_WIDTYPE(obj, widtype);
382    Widget_Data *wd = elm_widget_data_get(obj);
383    if (!wd) return;
384    if (wd->content == content) return;
385    if (wd->content)
386      evas_object_box_remove_all(wd->bx, EINA_TRUE);
387    wd->content = content;
388    if (content)
389      {
390         evas_object_box_append(wd->bx, wd->content);
391         evas_object_show(wd->content);
392      }
393    _sizing_eval(obj);
394 }
395
396 /**
397  * Get the content of the panel.
398  *
399  * Return the content object which is set for this widget.
400  *
401  * @param obj The panel object
402  * @return The content that is being used
403  *
404  * @ingroup Panel
405  */
406 EAPI Evas_Object *
407 elm_panel_content_get(const Evas_Object *obj)
408 {
409    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
410    Widget_Data *wd = elm_widget_data_get(obj);
411    if (!wd) return NULL;
412    return wd->content;
413 }
414
415 /**
416  * Unset the content of the panel.
417  *
418  * Unparent and return the content object which was set for this widget.
419  *
420  * @param obj The panel object
421  * @return The content that was being used
422  *
423  * @ingroup Panel
424  */
425 EAPI Evas_Object *
426 elm_panel_content_unset(Evas_Object *obj)
427 {
428    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
429    Widget_Data *wd = elm_widget_data_get(obj);
430    Evas_Object *content;
431    if (!wd) return NULL;
432    if (!wd->content) return NULL;
433    content = wd->content;
434    evas_object_box_remove_all(wd->bx, EINA_FALSE);
435    wd->content = NULL;
436    return content;
437 }
438
439 /**
440  * Set the state of the panel.
441  *
442  * @param obj The panel object
443  * @param hidden If true, the panel will run the edje animation to contract
444  *
445  * @ingroup Panel
446  */
447 EAPI void
448 elm_panel_hidden_set(Evas_Object *obj, Eina_Bool hidden)
449 {
450    ELM_CHECK_WIDTYPE(obj, widtype);
451    Widget_Data *wd = elm_widget_data_get(obj);
452    if (!wd) return;
453    if (wd->hidden == hidden) return;
454    _toggle_panel(obj, NULL, "elm,action,panel,toggle", "*");
455 }
456
457 /**
458  * Get the state of the panel.
459  *
460  * @param obj The panel object
461  * @param hidden If true, the panel is in the "hide" state
462  *
463  * @ingroup Panel
464  */
465 EAPI Eina_Bool
466 elm_panel_hidden_get(const Evas_Object *obj)
467 {
468    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
469    Widget_Data *wd = elm_widget_data_get(obj);
470    if (!wd) return EINA_FALSE;
471    return wd->hidden;
472 }
473
474 /**
475  * Toggle the state of the panel from code
476  *
477  * @param obj The panel object
478  *
479  * @ingroup Panel
480  */
481 EAPI void
482 elm_panel_toggle(Evas_Object *obj)
483 {
484    ELM_CHECK_WIDTYPE(obj, widtype);
485    Widget_Data *wd = elm_widget_data_get(obj);
486    if (!wd) return;
487    _toggle_panel(obj, NULL, "elm,action,panel,toggle", "*");
488 }