EFL migration revision 67547
[framework/uifw/elementary.git] / src / lib / elm_pager.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5 typedef struct _Item Item;
6
7 struct _Widget_Data
8 {
9    Eina_List *stack;
10    Item *top, *oldtop;
11    Evas_Object *rect, *clip;
12 };
13
14 struct _Item
15 {
16    Evas_Object *obj, *base, *content;
17    Evas_Coord minw, minh;
18    Eina_Bool popme : 1;
19 };
20
21 static const char *widtype = NULL;
22 static void _del_hook(Evas_Object *obj);
23 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
24 static void _theme_hook(Evas_Object *obj);
25 static void _sizing_eval(Evas_Object *obj);
26 static void _changed_size_hints(void *data,
27                                 Evas *e,
28                                 Evas_Object *obj,
29                                 void *event_info);
30 static void _content_del(void *data,
31                          Evas *e,
32                          Evas_Object *obj,
33                          void *event_info);
34 static Eina_List *_item_get(Evas_Object *obj, Evas_Object *content);
35
36 static const char SIG_HIDE_FINISHED[] = "hide,finished";
37 static const char SIG_SHOW_FINISHED[] = "show,finished";
38
39 static const Evas_Smart_Cb_Description _signals[] = {
40    {SIG_HIDE_FINISHED, ""},
41    {SIG_SHOW_FINISHED, ""},
42    {NULL, NULL}
43 };
44
45 static void
46 _del_hook(Evas_Object *obj)
47 {
48    Widget_Data *wd = elm_widget_data_get(obj);
49    if (!wd) return;
50    free(wd);
51 }
52
53 static void
54 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
55 {
56    Widget_Data *wd = elm_widget_data_get(obj);
57    Eina_List *l;
58    Item *it;
59    if (!wd) return;
60    EINA_LIST_FOREACH(wd->stack, l, it)
61       edje_object_mirrored_set(it->base, rtl);
62 }
63
64 static void
65 _theme_hook(Evas_Object *obj)
66 {
67    Widget_Data *wd = elm_widget_data_get(obj);
68    Eina_List *l;
69    Item *it;
70    if (!wd) return;
71    _elm_widget_mirrored_reload(obj);
72    _mirrored_set(obj, elm_widget_mirrored_get(obj));
73    EINA_LIST_FOREACH(wd->stack, l, it)
74      {
75         _elm_theme_object_set(obj, it->base,  "pager", "base",
76                               elm_widget_style_get(obj));
77         edje_object_scale_set(it->base, elm_widget_scale_get(obj) *
78                               _elm_config->scale);
79      }
80    _sizing_eval(obj);
81 }
82
83 static Eina_List *
84 _item_get(Evas_Object *obj, Evas_Object *content)
85 {
86    Widget_Data *wd = elm_widget_data_get(obj);
87    Item *it;
88    Eina_List *l;
89    if (!wd) return NULL;
90
91    EINA_LIST_FOREACH(wd->stack, l, it)
92      {
93         if (it->content == content)
94           return l;
95      }
96
97    return NULL;
98 }
99
100 static Eina_Bool
101 _elm_pager_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
102 {
103    Widget_Data *wd = elm_widget_data_get(obj);
104    Evas_Object *cur;
105
106    if ((!wd) || (!wd->top))
107      return EINA_FALSE;
108
109    cur = wd->top->content;
110
111    /* Try Focus cycle in subitem */
112    return elm_widget_focus_next_get(cur, dir, next);
113 }
114
115 static void
116 _sizing_eval(Evas_Object *obj)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    Evas_Coord minw = -1, minh = -1;
120    Eina_List *l;
121    Item *it;
122    if (!wd) return;
123    EINA_LIST_FOREACH(wd->stack, l, it)
124      {
125         if (it->minw > minw) minw = it->minw;
126         if (it->minh > minh) minh = it->minh;
127      }
128    evas_object_size_hint_min_set(obj, minw, minh);
129    evas_object_size_hint_max_set(obj, -1, -1);
130 }
131
132 static void
133 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
134 {
135    Item *it = data;
136    Evas_Coord minw = -1, minh = -1;
137    evas_object_size_hint_min_get(it->content, &minw, &minh);
138    // FIXME: why is this needed? how does edje get this unswallowed or
139    // lose its callbacks to edje
140    edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
141    edje_object_size_min_calc(it->base, &it->minw, &it->minh);
142    _sizing_eval(it->obj);
143 }
144
145 static void
146 _eval_top(Evas_Object *obj)
147 {
148    Widget_Data *wd = elm_widget_data_get(obj);
149    Item *ittop;
150    if (!wd) return;
151    if (!wd->stack) return;
152    ittop = eina_list_last(wd->stack)->data;
153    if (ittop != wd->top)
154      {
155         Evas_Object *o;
156         const char *onshow, *onhide;
157
158         if (wd->top)
159           {
160              o = wd->top->base;
161              if (wd->top->popme)
162                {
163                   edje_object_signal_emit(o, "elm,action,pop", "elm");
164                   wd->stack = eina_list_remove(wd->stack, wd->top);
165                }
166              else
167                edje_object_signal_emit(o, "elm,action,hide", "elm");
168              onhide = edje_object_data_get(o, "onhide");
169              if (onhide)
170                {
171                   if (!strcmp(onhide, "raise")) evas_object_raise(o);
172                   else if (!strcmp(onhide, "lower")) evas_object_lower(o);
173                }
174           }
175         wd->oldtop = wd->top;
176         wd->top = ittop;
177         o = wd->top->base;
178         evas_object_show(o);
179         if (wd->oldtop)
180           {
181              if (elm_object_focus_get(wd->oldtop->content))
182                elm_widget_focused_object_clear(wd->oldtop->content);
183              if (wd->oldtop->popme)
184                edje_object_signal_emit(o, "elm,action,show", "elm");
185              else
186                edje_object_signal_emit(o, "elm,action,push", "elm");
187           }
188         else
189           edje_object_signal_emit(o, "elm,action,push", "elm");
190         onshow = edje_object_data_get(o, "onshow");
191         if (onshow)
192           {
193              if (!strcmp(onshow, "raise")) evas_object_raise(o);
194              else if (!strcmp(onshow, "lower")) evas_object_lower(o);
195           }
196      }
197 }
198
199 static void
200 _move(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
201 {
202    Widget_Data *wd = elm_widget_data_get(data);
203    Evas_Coord x, y;
204    Eina_List *l;
205    Item *it;
206    if (!wd) return;
207    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
208    EINA_LIST_FOREACH(wd->stack, l, it)
209      evas_object_move(it->base, x, y);
210 }
211
212 static void
213 _content_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
214 {
215    Widget_Data *wd = elm_widget_data_get(data);
216    Eina_List *l;
217    Item *it;
218    if (!wd) return;
219    l = _item_get(data, obj);
220    if (!l) return;
221    it = l->data;
222    wd->stack = eina_list_remove_list(wd->stack, l);
223    evas_object_event_callback_del_full
224       (obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, it);
225    evas_object_del(it->base);
226    _eval_top(data);
227    free(it);
228 }
229
230 static void
231 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
232 {
233    Widget_Data *wd = elm_widget_data_get(data);
234    Evas_Coord w, h;
235    Eina_List *l;
236    Item *it;
237    if (!wd) return;
238    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
239    EINA_LIST_FOREACH(wd->stack, l, it) evas_object_resize(it->base, w, h);
240 }
241
242 static void
243 _show_finished_cb(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
244 {
245    Item *it = data;
246    Evas_Object *obj = it->obj;
247    Evas_Object *content = it->content;
248
249     evas_object_smart_callback_call(obj, SIG_SHOW_FINISHED, content);
250 }
251
252 static void
253 _hide_finished_cb(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
254 {
255    Item *it = data;
256    Evas_Object *obj2 = it->obj;
257    Evas_Object *content = it->content;
258
259    if (it->popme)
260      {
261         evas_object_del(it->base);
262         evas_object_event_callback_del_full(content,
263                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
264                                             _changed_size_hints,
265                                             it);
266         evas_object_event_callback_del(content,
267                                        EVAS_CALLBACK_DEL,
268                                        _content_del);
269         evas_object_del(content);
270         free(it);
271      }
272    else
273      {
274         evas_object_hide(it->base);
275         edje_object_signal_emit(it->base, "elm,action,reset", "elm");
276         edje_object_message_signal_process(it->base);
277         evas_object_hide(content);
278      }
279     evas_object_smart_callback_call(obj2, SIG_HIDE_FINISHED, content);
280     _sizing_eval(obj2);
281 }
282
283 EAPI Evas_Object *
284 elm_pager_add(Evas_Object *parent)
285 {
286    Evas_Object *obj;
287    Evas *e;
288    Widget_Data *wd;
289
290    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
291
292    ELM_SET_WIDTYPE(widtype, "pager");
293    elm_widget_type_set(obj, "pager");
294    elm_widget_sub_object_add(parent, obj);
295    elm_widget_data_set(obj, wd);
296    elm_widget_del_hook_set(obj, _del_hook);
297    elm_widget_theme_hook_set(obj, _theme_hook);
298    elm_widget_focus_next_hook_set(obj, _elm_pager_focus_next_hook);
299    elm_widget_can_focus_set(obj, EINA_FALSE);
300
301    wd->clip = evas_object_rectangle_add(e);
302    elm_widget_resize_object_set(obj, wd->clip);
303    elm_widget_sub_object_add(obj, wd->clip);
304
305    wd->rect = evas_object_rectangle_add(e);
306    elm_widget_sub_object_add(obj, wd->rect);
307    evas_object_color_set(wd->rect, 255, 255, 255, 0);
308    evas_object_clip_set(wd->rect, wd->clip);
309
310    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _move, obj);
311    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
312
313    evas_object_smart_callbacks_descriptions_set(obj, _signals);
314
315    _mirrored_set(obj, elm_widget_mirrored_get(obj));
316    _sizing_eval(obj);
317    return obj;
318 }
319
320 EAPI void
321 elm_pager_content_push(Evas_Object *obj, Evas_Object *content)
322 {
323    ELM_CHECK_WIDTYPE(obj, widtype);
324    Widget_Data *wd = elm_widget_data_get(obj);
325    Evas_Coord x, y, w, h;
326    Item *it;
327
328    if ((!wd) || (!content)) return;
329    if (_item_get(obj, content)) return;
330
331    it = ELM_NEW(Item);
332    if (!it) return;
333    it->obj = obj;
334    it->content = content;
335    it->base = edje_object_add(evas_object_evas_get(obj));
336    evas_object_smart_member_add(it->base, obj);
337    evas_object_geometry_get(obj, &x, &y, &w, &h);
338    evas_object_move(it->base, x, y);
339    evas_object_resize(it->base, w, h);
340    evas_object_clip_set(it->base, wd->clip);
341    elm_widget_sub_object_add(obj, it->base);
342    elm_widget_sub_object_add(obj, it->content);
343    _elm_theme_object_set(obj,
344                          it->base,
345                          "pager",
346                          "base",
347                          elm_widget_style_get(obj));
348    edje_object_signal_callback_add
349        (it->base, "elm,action,show,finished", "", _show_finished_cb, it);
350    edje_object_signal_callback_add(it->base,
351                                    "elm,action,hide,finished",
352                                    "",
353                                    _hide_finished_cb,
354                                    it);
355    evas_object_event_callback_add(it->content,
356                                   EVAS_CALLBACK_CHANGED_SIZE_HINTS,
357                                   _changed_size_hints,
358                                   it);
359    evas_object_event_callback_add(it->content,
360                                   EVAS_CALLBACK_DEL,
361                                   _content_del,
362                                   obj);
363    edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
364    edje_object_size_min_calc(it->base, &it->minw, &it->minh);
365    evas_object_data_set(it->base, "_elm_leaveme", obj);
366    evas_object_show(it->content);
367    wd->stack = eina_list_append(wd->stack, it);
368    _eval_top(obj);
369    _sizing_eval(obj);
370 }
371
372 EAPI void
373 elm_pager_content_pop(Evas_Object *obj)
374 {
375    ELM_CHECK_WIDTYPE(obj, widtype);
376    Widget_Data *wd = elm_widget_data_get(obj);
377    Eina_List *ll;
378    Item *it;
379    if (!wd) return;
380    if (!wd->stack) return;
381    it = eina_list_last(wd->stack)->data;
382    it->popme = EINA_TRUE;
383    ll = eina_list_last(wd->stack);
384    if (ll)
385      {
386         ll = ll->prev;
387         if (!ll)
388           {
389              Evas_Object *o;
390              const char *onhide;
391
392              wd->top = it;
393              o = wd->top->base;
394              edje_object_signal_emit(o, "elm,action,pop", "elm");
395              wd->stack = eina_list_remove(wd->stack, it);
396              onhide = edje_object_data_get(o, "onhide");
397              if (onhide)
398                {
399                   if (!strcmp(onhide, "raise")) evas_object_raise(o);
400                   else if (!strcmp(onhide, "lower")) evas_object_lower(o);
401                }
402              wd->top = NULL;
403           }
404         else
405           {
406              it = ll->data;
407              elm_pager_content_promote(obj, it->content);
408           }
409      }
410 }
411
412 EAPI void
413 elm_pager_content_promote(Evas_Object *obj, Evas_Object *content)
414 {
415    ELM_CHECK_WIDTYPE(obj, widtype);
416    Widget_Data *wd = elm_widget_data_get(obj);
417    Eina_List *l;
418    if (!wd) return;
419    l = _item_get(obj, content);
420    if (!l) return;
421
422    wd->stack = eina_list_demote_list(wd->stack, l);
423    _eval_top(obj);
424 }
425
426 EAPI Evas_Object *
427 elm_pager_content_bottom_get(const Evas_Object *obj)
428 {
429    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
430    Widget_Data *wd = elm_widget_data_get(obj);
431    Item *it;
432    if (!wd) return NULL;
433    if (!wd->stack) return NULL;
434    it = wd->stack->data;
435    return it->content;
436 }
437
438 EAPI Evas_Object *
439 elm_pager_content_top_get(const Evas_Object *obj)
440 {
441    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
442    Widget_Data *wd = elm_widget_data_get(obj);
443    if (!wd) return NULL;
444    if (!wd->top) return NULL;
445    return wd->top->content;
446 }