1 #include <Elementary.h>
5 * @defgroup Pager Pager
8 * The pager is an object that allows flipping (with animation) between 1 or
9 * more “pages” of objects, much like a stack of windows within the window.
11 * Objects can be pushed or popped from the stack or deleted as normal.
12 * Pushes and pops will animate (and a pop will delete the object once the
13 * animation is finished). Any object in the pager can be promoted to the top
14 * (from its current stacking position) as well. Objects are pushed to the
15 * top with elm_pager_content_push() and when the top item is no longer
16 * wanted, simply pop it with elm_pager_content_pop() and it will also be
17 * deleted. Any object you wish to promote to the top that is already in the
18 * pager, simply use elm_pager_content_promote(). If an object is no longer
19 * needed and is not the top item, just delete it as normal. You can query
20 * which objects are the top and bottom with elm_pager_content_bottom_get()
21 * and elm_pager_content_top_get().
24 typedef struct _Widget_Data Widget_Data;
25 typedef struct _Item Item;
31 Evas_Object *rect, *clip;
32 Eina_Bool disable_animation: 1;
37 Evas_Object *obj, *base, *content;
38 Evas_Coord minw, minh;
42 static const char *widtype = NULL;
43 static void _del_hook(Evas_Object *obj);
44 static void _theme_hook(Evas_Object *obj);
45 static void _sizing_eval(Evas_Object *obj);
46 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
47 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
50 _del_hook(Evas_Object *obj)
52 Widget_Data *wd = elm_widget_data_get(obj);
54 evas_object_del(wd->clip);
55 evas_object_del(wd->rect);
60 _theme_hook(Evas_Object *obj)
62 Widget_Data *wd = elm_widget_data_get(obj);
66 EINA_LIST_FOREACH(wd->stack, l, it)
68 _elm_theme_object_set(obj, it->base, "pager", "base",
69 elm_widget_style_get(obj));
70 edje_object_scale_set(it->base, elm_widget_scale_get(obj) *
77 _elm_pager_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
79 Widget_Data *wd = elm_widget_data_get(obj);
82 if ((!wd) || (!wd->top))
85 cur = wd->top->content;
87 /* Try Focus cycle in subitem */
88 return elm_widget_focus_next_get(cur, dir, next);
92 _sizing_eval(Evas_Object *obj)
94 Widget_Data *wd = elm_widget_data_get(obj);
95 Evas_Coord minw = -1, minh = -1;
99 EINA_LIST_FOREACH(wd->stack, l, it)
101 if (it->minw > minw) minw = it->minw;
102 if (it->minh > minh) minh = it->minh;
104 evas_object_size_hint_min_set(obj, minw, minh);
105 evas_object_size_hint_max_set(obj, -1, -1);
109 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
112 Evas_Coord minw = -1, minh = -1;
113 evas_object_size_hint_min_get(it->content, &minw, &minh);
114 // FIXME: why is this needed? how does edje get this unswallowed or
115 // lose its callbacks to edje
116 edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
117 edje_object_size_min_calc(it->base, &it->minw, &it->minh);
118 _sizing_eval(it->obj);
122 _eval_top(Evas_Object *obj)
124 Widget_Data *wd = elm_widget_data_get(obj);
125 Eina_Bool animate=EINA_TRUE;
128 if (!wd->stack) return;
129 ittop = eina_list_last(wd->stack)->data;
130 if (ittop != wd->top)
133 const char *onshow, *onhide;
138 if(wd->disable_animation)
140 edje_object_signal_emit(o, "elm,action,hide,noanimate", "elm");
142 else if (wd->top->popme)
143 edje_object_signal_emit(o, "elm,action,pop", "elm");
145 edje_object_signal_emit(o, "elm,action,hide", "elm");
146 onhide = edje_object_data_get(o, "onhide");
149 if (!strcmp(onhide, "raise")) evas_object_raise(o);
150 else if (!strcmp(onhide, "lower")) evas_object_lower(o);
155 animate = EINA_FALSE;
157 wd->oldtop = wd->top;
161 if ((!animate)||(wd->disable_animation))
163 edje_object_signal_emit(o, "elm,action,show,noanimate", "elm");
167 if (elm_object_focus_get(wd->oldtop->content))
168 elm_object_focus(wd->top->content);
169 if (wd->oldtop->popme)
170 edje_object_signal_emit(o, "elm,action,show", "elm");
172 edje_object_signal_emit(o, "elm,action,push", "elm");
175 edje_object_signal_emit(o, "elm,action,push", "elm");
176 onshow = edje_object_data_get(o, "onshow");
179 if (!strcmp(onshow, "raise")) evas_object_raise(o);
180 else if (!strcmp(onshow, "lower")) evas_object_lower(o);
186 _move(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
188 Widget_Data *wd = elm_widget_data_get(data);
193 evas_object_geometry_get(obj, &x, &y, NULL, NULL);
194 evas_object_move(wd->clip, x, y);
195 EINA_LIST_FOREACH(wd->stack, l, it)
196 evas_object_move(it->base, x, y);
200 _sub_del(void *data, Evas_Object *obj __UNUSED__, void *event_info)
202 Widget_Data *wd = elm_widget_data_get(data);
203 Evas_Object *sub = event_info;
207 EINA_LIST_FOREACH(wd->stack, l, it)
209 if (it->content == sub)
211 wd->stack = eina_list_remove_list(wd->stack, l);
212 evas_object_event_callback_del_full
213 (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, it);
214 evas_object_del(it->base);
223 _show(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
225 Widget_Data *wd = elm_widget_data_get(data);
227 evas_object_show(wd->clip);
231 _hide(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
233 Widget_Data *wd = elm_widget_data_get(data);
235 evas_object_hide(wd->clip);
239 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
241 Widget_Data *wd = elm_widget_data_get(data);
246 evas_object_geometry_get(obj, NULL, NULL, &w, &h);
247 evas_object_resize(wd->clip, w, h);
248 EINA_LIST_FOREACH(wd->stack, l, it) evas_object_resize(it->base, w, h);
252 _signal_hide_finished(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
255 Evas_Object *obj2 = it->obj;
256 evas_object_hide(it->base);
257 edje_object_signal_emit(it->base, "elm,action,reset", "elm");
258 evas_object_smart_callback_call(obj2, "hide,finished", it->content);
259 edje_object_message_signal_process(it->base);
260 evas_object_hide(it->content);
261 if (it->popme) evas_object_del(it->content);
266 * Add a new pager to the parent
268 * @param parent The parent object
269 * @return The new object or NULL if it cannot be created
274 elm_pager_add(Evas_Object *parent)
280 EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
282 wd = ELM_NEW(Widget_Data);
283 e = evas_object_evas_get(parent);
285 obj = elm_widget_add(e);
286 ELM_SET_WIDTYPE(widtype, "pager");
287 elm_widget_type_set(obj, "pager");
288 elm_widget_sub_object_add(parent, obj);
289 elm_widget_data_set(obj, wd);
290 elm_widget_del_hook_set(obj, _del_hook);
291 elm_widget_theme_hook_set(obj, _theme_hook);
292 elm_widget_focus_next_hook_set(obj, _elm_pager_focus_next_hook);
293 elm_widget_can_focus_set(obj, EINA_FALSE);
295 wd->clip = evas_object_rectangle_add(e);
297 wd->rect = evas_object_rectangle_add(e);
298 evas_object_color_set(wd->rect, 255, 255, 255, 0);
299 evas_object_clip_set(wd->rect, wd->clip);
301 evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _move, obj);
302 evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
303 evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
304 evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _hide, obj);
306 evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
314 * Push an object to the top of the pager stack (and show it)
316 * The object pushed becomes a child of the pager and will be controlled
317 * it and deleted when the pager is deleted.
319 * @param obj The pager object
320 * @param content The object to push
325 elm_pager_content_push(Evas_Object *obj, Evas_Object *content)
327 ELM_CHECK_WIDTYPE(obj, widtype);
328 Widget_Data *wd = elm_widget_data_get(obj);
329 Item *it = ELM_NEW(Item);
330 Evas_Coord x, y, w, h;
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, it->base, "pager", "base", elm_widget_style_get(obj));
344 edje_object_signal_callback_add(it->base, "elm,action,hide,finished", "",
345 _signal_hide_finished, it);
346 evas_object_event_callback_add(it->content,
347 EVAS_CALLBACK_CHANGED_SIZE_HINTS,
348 _changed_size_hints, it);
349 edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
350 edje_object_size_min_calc(it->base, &it->minw, &it->minh);
351 evas_object_data_set(it->base, "_elm_leaveme", obj);
352 evas_object_show(it->content);
353 wd->stack = eina_list_append(wd->stack, it);
359 * Pop the object that is on top of the stack
361 * This pops the object that is on top (visible) in the pager, makes it
362 * disappear, then deletes the object. The object that was underneath it
363 * on the stack will become visible.
365 * @param obj The pager object
370 elm_pager_content_pop(Evas_Object *obj)
372 ELM_CHECK_WIDTYPE(obj, widtype);
373 Widget_Data *wd = elm_widget_data_get(obj);
377 if (!wd->stack) return;
378 it = eina_list_last(wd->stack)->data;
379 it->popme = EINA_TRUE;
380 ll = eina_list_last(wd->stack);
391 edje_object_signal_emit(o, "elm,action,pop", "elm");
392 onhide = edje_object_data_get(o, "onhide");
395 if (!strcmp(onhide, "raise")) evas_object_raise(o);
396 else if (!strcmp(onhide, "lower")) evas_object_lower(o);
403 elm_pager_content_promote(obj, it->content);
411 Evas_Object *obj = data;
412 evas_object_del(obj);
416 * Pop to the object that is on the stack
418 * This pops the objects that are on the stack, makes them
419 * disappear, then deletes the objects. The content will become visible.
421 * @param obj The pager object
422 * @param content The object to show
427 elm_pager_to_content_pop(Evas_Object *obj, Evas_Object *content)
429 ELM_CHECK_WIDTYPE(obj, widtype);
430 Widget_Data *wd = elm_widget_data_get(obj);
434 if (!wd->stack) return;
435 it = eina_list_last(wd->stack)->data;
436 it->popme = EINA_TRUE;
437 ll = eina_list_last(wd->stack);
443 if(it->content != content)
445 wd->stack = eina_list_remove_list(wd->stack, ll);
446 ecore_job_add(_del_job, it->content);
459 * Promote an object already in the pager stack to the top of the stack
461 * This will take the indicated object and promote it to the top of the stack
462 * as if it had been pushed there. The object must already be inside the
463 * pager stack to work.
465 * @param obj The pager object
466 * @param content The object to promote
471 elm_pager_content_promote(Evas_Object *obj, Evas_Object *content)
473 ELM_CHECK_WIDTYPE(obj, widtype);
474 Widget_Data *wd = elm_widget_data_get(obj);
478 EINA_LIST_FOREACH(wd->stack, l, it)
480 if (it->content == content)
482 wd->stack = eina_list_remove_list(wd->stack, l);
483 wd->stack = eina_list_append(wd->stack, it);
491 * Return the object at the bottom of the pager stack
493 * @param obj The pager object
494 * @return The bottom object or NULL if none
499 elm_pager_content_bottom_get(const Evas_Object *obj)
501 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
502 Widget_Data *wd = elm_widget_data_get(obj);
504 if (!wd) return NULL;
505 if (!wd->stack) return NULL;
506 it = wd->stack->data;
511 * Return the object at the top of the pager stack
513 * @param obj The pager object
514 * @return The top object or NULL if none
519 elm_pager_content_top_get(const Evas_Object *obj)
521 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
522 Widget_Data *wd = elm_widget_data_get(obj);
523 if (!wd) return NULL;
524 if (!wd->top) return NULL;
525 return wd->top->content;
529 * This disables content animation on push/pop.
531 * @param obj The pager object
532 * @param disable if EINA_TRUE animation is disabled.
537 elm_pager_animation_disabled_set(Evas_Object *obj, Eina_Bool disable)
539 ELM_CHECK_WIDTYPE(obj, widtype)NULL;
540 Widget_Data *wd = elm_widget_data_get(obj);
541 wd->disable_animation = disable;