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