summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaehyun Cho <jae_hyun.cho@samsung.com>2018-04-12 21:09:38 +0900
committerJaehyun Cho <jae_hyun.cho@samsung.com>2018-04-12 22:02:29 +0900
commit7fc81a4b66f57a00103cbc08aa0b6473f8c635e2 (patch)
tree4db1991e0197fa5301908d2f6df01c3a98d981d5
parent348bae4b24f216dbfa5118f92b7ebbc455e7018f (diff)
downloadefl-7fc81a4b66f57a00103cbc08aa0b6473f8c635e2.tar.gz
efl_ui_stack: Add Efl.Ui.Stack class
Efl.Ui.Stack is a container arranges objects in stack structure by pushing and popping them.
-rw-r--r--src/Makefile_Elementary.am3
-rw-r--r--src/lib/elementary/Elementary.h1
-rw-r--r--src/lib/elementary/efl_ui_stack.c972
-rw-r--r--src/lib/elementary/efl_ui_stack.eo131
-rw-r--r--src/lib/elementary/efl_ui_stack_private.h30
5 files changed, 1137 insertions, 0 deletions
diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am
index 02008ee6e1..0751a36ec7 100644
--- a/src/Makefile_Elementary.am
+++ b/src/Makefile_Elementary.am
@@ -11,6 +11,7 @@ elm_public_eolian_files = \
lib/elementary/efl_ui_check.eo \
lib/elementary/efl_ui_flip.eo \
lib/elementary/efl_ui_frame.eo \
+ lib/elementary/efl_ui_stack.eo \
lib/elementary/efl_ui_image.eo \
lib/elementary/efl_ui_image_zoomable.eo \
lib/elementary/efl_ui_layout.eo \
@@ -327,6 +328,7 @@ includesunstable_HEADERS = \
lib/elementary/efl_ui_widget_flip.h \
lib/elementary/elm_widget_flipselector.h \
lib/elementary/efl_ui_widget_frame.h \
+ lib/elementary/efl_ui_stack_private.h \
lib/elementary/elm_widget_gengrid.h \
lib/elementary/elm_widget_genlist.h \
lib/elementary/elm_widget_glview.h \
@@ -671,6 +673,7 @@ lib_elementary_libelementary_la_SOURCES = \
lib/elementary/elm_flipselector.c \
lib/elementary/elm_font.c \
lib/elementary/efl_ui_frame.c \
+ lib/elementary/efl_ui_stack.c \
lib/elementary/elm_gengrid.c \
lib/elementary/elm_genlist.c \
lib/elementary/elm_gesture_layer.c \
diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h
index a7161e4a9c..4099503810 100644
--- a/src/lib/elementary/Elementary.h
+++ b/src/lib/elementary/Elementary.h
@@ -336,6 +336,7 @@ typedef Eo Efl_Ui_Focus_Manager;
# include <efl_selection.eo.h>
# include <efl_ui_dnd.eo.h>
# include <efl_ui_dnd_container.eo.h>
+# include <efl_ui_stack.eo.h>
#endif
/* include deprecated calls last of all */
diff --git a/src/lib/elementary/efl_ui_stack.c b/src/lib/elementary/efl_ui_stack.c
new file mode 100644
index 0000000000..b827cc9766
--- /dev/null
+++ b/src/lib/elementary/efl_ui_stack.c
@@ -0,0 +1,972 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+
+#include "elm_priv.h"
+#include "efl_ui_stack_private.h"
+
+#define MY_CLASS EFL_UI_STACK_CLASS
+#define MY_CLASS_NAME "Efl.Ui.Stack"
+
+static Efl_Canvas_Animation *show_anim = NULL;
+static Efl_Canvas_Animation *hide_anim = NULL;
+
+static void
+_content_del_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+ Content_Data *cd = data;
+
+ //Popped content has already called deactivated event and unloaded event.
+ if (cd->popped_hidden) return;
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = cd->content;
+ efl_event_callback_call(cd->stack,
+ EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = cd->content;
+ efl_event_callback_call(cd->stack,
+ EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+}
+
+static Content_Data *
+_content_data_new(Eo *obj, Eo *content)
+{
+ Content_Data *cd = calloc(1, sizeof(Content_Data));
+ if (!cd)
+ {
+ ERR("Memory allocation error!");
+ return NULL;
+ }
+
+ cd->stack = obj;
+ cd->content = content;
+
+ efl_event_callback_add(cd->content, EFL_EVENT_DEL, _content_del_cb, cd);
+
+ return cd;
+}
+
+static void
+_content_data_del(Content_Data *cd)
+{
+ if (!cd) return;
+
+ if (cd->content)
+ efl_del(cd->content);
+
+ free(cd);
+}
+
+static void
+_anim_started_cb(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ efl_canvas_object_freeze_events_set(event->object, EINA_TRUE);
+
+ efl_event_callback_del(event->object, EFL_CANVAS_OBJECT_EVENT_ANIM_STARTED,
+ _anim_started_cb, NULL);
+}
+
+static void
+_anim_ended_cb(void *data, const Efl_Event *event)
+{
+ Transit_Data *td = data;
+ Efl_Canvas_Object_Animation_Event_Info *anim_event = event->info;
+
+ //Unset animation because originally there is no animation.
+ if (!td->orig_anim)
+ efl_canvas_object_event_animation_set(event->object,
+ anim_event->event_type, NULL);
+
+ efl_canvas_object_freeze_events_set(event->object,
+ td->freeze_events);
+
+ td->cd->on_pushing = EINA_FALSE;
+ td->cd->on_popping = EINA_FALSE;
+
+ if (anim_event->event_type == EFL_GFX_EVENT_SHOW)
+ {
+ //Activated Event
+ Efl_Ui_Stack_Event_Activated activated_info;
+ activated_info.content = event->object;
+ efl_event_callback_call(td->cd->stack,
+ EFL_UI_STACK_EVENT_ACTIVATED,
+ &activated_info);
+ }
+ else
+ {
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = event->object;
+ efl_event_callback_call(td->cd->stack,
+ EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = event->object;
+ efl_event_callback_call(td->cd->stack,
+ EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+ }
+
+ efl_event_callback_del(event->object, EFL_CANVAS_OBJECT_EVENT_ANIM_ENDED,
+ _anim_ended_cb, data);
+ free(data);
+}
+
+EOLIAN static void
+_efl_ui_stack_push(Eo *obj, Efl_Ui_Stack_Data *pd, Eo *content)
+{
+ if (!content) return;
+
+ //If the given content exists in the stack, promote the given content to the top.
+ Content_Data *cd = NULL;
+ EINA_INLIST_FOREACH(pd->stack, cd)
+ if (cd->content == content)
+ break;
+
+ Content_Data *top_cd = NULL;
+ if (pd->stack)
+ top_cd = EINA_INLIST_CONTAINER_GET(pd->stack->last, Content_Data);
+
+ if (cd)
+ {
+ //If given content is already the top content, then do nothing.
+ if (cd == top_cd)
+ return;
+
+ //Remove the given content(existing content) to promote it to the top.
+ pd->stack = eina_inlist_remove(pd->stack, EINA_INLIST_GET(cd));
+ }
+ else
+ {
+ cd = _content_data_new(obj, content);
+ if (!cd) return;
+
+ evas_object_smart_member_add(content, obj);
+ }
+
+ pd->stack = eina_inlist_append(pd->stack, EINA_INLIST_GET(cd));
+
+ //Loaded Event
+ Efl_Ui_Stack_Event_Loaded loaded_info;
+ loaded_info.content = content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_LOADED, &loaded_info);
+
+ /* Apply transition to top content.
+ * Hide top content with animation. */
+ if (top_cd)
+ {
+ Eo *top_content = top_cd->content;
+
+ Efl_Canvas_Animation *orig_hide_anim =
+ efl_canvas_object_event_animation_get(top_content, EFL_GFX_EVENT_HIDE);
+
+ /* If content is being pushed now, then finish current animation and hide
+ * the content without animation. */
+ if (top_cd->on_pushing)
+ {
+ //Finish current animation.
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_SHOW, NULL);
+
+ //Hide without animation.
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_HIDE, NULL);
+
+ efl_gfx_visible_set(top_content, EINA_FALSE);
+
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_HIDE,
+ orig_hide_anim);
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = top_content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = top_content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+ }
+ else
+ {
+ top_cd->on_pushing = EINA_TRUE;
+
+ //Hide with animation.
+ if (!orig_hide_anim)
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_HIDE,
+ hide_anim);
+
+ Transit_Data *td = calloc(1, sizeof(Transit_Data));
+ td->cd = top_cd;
+ td->orig_anim = !!(orig_hide_anim);
+ td->freeze_events = efl_canvas_object_freeze_events_get(top_content);
+
+ efl_event_callback_add(top_content,
+ EFL_CANVAS_OBJECT_EVENT_ANIM_STARTED,
+ _anim_started_cb, NULL);
+ efl_event_callback_add(top_content,
+ EFL_CANVAS_OBJECT_EVENT_ANIM_ENDED,
+ _anim_ended_cb, td);
+
+ efl_gfx_visible_set(top_content, EINA_FALSE);
+ }
+ }
+
+ /* Prepare transition for new content.
+ * Hide new content without animation. */
+ {
+ cd->on_pushing = EINA_TRUE;
+
+ Efl_Canvas_Animation *orig_hide_anim =
+ efl_canvas_object_event_animation_get(content, EFL_GFX_EVENT_HIDE);
+
+ //Hide without animation.
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_HIDE, NULL);
+ efl_gfx_visible_set(content, EINA_FALSE);
+
+ //Restore original hide animation
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_HIDE,
+ orig_hide_anim);
+ }
+
+ /* Apply transition to new content.
+ * Show new content with animation. */
+ {
+ evas_object_raise(content);
+
+ Efl_Canvas_Animation *orig_show_anim =
+ efl_canvas_object_event_animation_get(content, EFL_GFX_EVENT_SHOW);
+
+ //Show with animation
+ if (!orig_show_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_SHOW,
+ show_anim);
+
+ Transit_Data *td = calloc(1, sizeof(Transit_Data));
+ td->cd = cd;
+ td->orig_anim = !!(orig_show_anim);
+ td->freeze_events = efl_canvas_object_freeze_events_get(content);
+
+ efl_event_callback_add(content, EFL_CANVAS_OBJECT_EVENT_ANIM_STARTED,
+ _anim_started_cb, NULL);
+ efl_event_callback_add(content, EFL_CANVAS_OBJECT_EVENT_ANIM_ENDED,
+ _anim_ended_cb, td);
+
+ /* efl_ui_widget_resize_object_set() calls efl_gfx_visible_set()
+ * internally.
+ * Therefore, efl_ui_widget_resize_object_set() is called after
+ * setting animation and efl_gfx_visible_set() is not called. */
+ efl_ui_widget_resize_object_set(obj, content);
+ }
+}
+
+static void
+_pop_content_hide_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+ Content_Data *cd = data;
+
+ cd->popped_hidden = EINA_TRUE;
+
+ _content_data_del(cd);
+}
+
+EOLIAN static Eo *
+_efl_ui_stack_pop(Eo *obj, Efl_Ui_Stack_Data *pd)
+{
+ if (!pd->stack)
+ {
+ ERR("There is no content in the stack!");
+ return NULL;
+ }
+
+ Content_Data *top_cd = EINA_INLIST_CONTAINER_GET(pd->stack->last, Content_Data);
+ if (!top_cd) return NULL;
+
+ pd->stack = eina_inlist_remove(pd->stack, EINA_INLIST_GET(top_cd));
+
+ /* Apply transition to top content.
+ * Hide top content with animation. */
+ {
+ Eo *top_content = top_cd->content;
+
+ Efl_Canvas_Animation *orig_hide_anim =
+ efl_canvas_object_event_animation_get(top_content, EFL_GFX_EVENT_HIDE);
+
+ /* If content is being popped now, then finish current animation and show
+ * the content without animation. */
+ if (top_cd->on_popping)
+ {
+ //Finish current animation.
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_SHOW, NULL);
+
+ //Hide without animation.
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_HIDE, NULL);
+
+ efl_gfx_visible_set(top_content, EINA_FALSE);
+
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_HIDE,
+ orig_hide_anim);
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = top_content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = top_content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_SHOW,
+ NULL);
+ }
+ else
+ {
+ top_cd->on_popping = EINA_TRUE;
+
+ //Hide with animation.
+ if (!orig_hide_anim)
+ efl_canvas_object_event_animation_set(top_content,
+ EFL_GFX_EVENT_HIDE,
+ hide_anim);
+
+ //Deallocate content data when hide animation is finished.
+ efl_event_callback_add(top_content, EFL_GFX_EVENT_HIDE,
+ _pop_content_hide_cb, top_cd);
+
+ Transit_Data *td = calloc(1, sizeof(Transit_Data));
+ td->cd = top_cd;
+ td->orig_anim = !!(orig_hide_anim);
+ td->freeze_events = efl_canvas_object_freeze_events_get(top_content);
+
+ efl_event_callback_add(top_content,
+ EFL_CANVAS_OBJECT_EVENT_ANIM_STARTED,
+ _anim_started_cb, NULL);
+ efl_event_callback_add(top_content,
+ EFL_CANVAS_OBJECT_EVENT_ANIM_ENDED,
+ _anim_ended_cb, td);
+
+ efl_gfx_visible_set(top_content, EINA_FALSE);
+ }
+ }
+
+ if (pd->stack)
+ {
+ Content_Data *prev_cd = EINA_INLIST_CONTAINER_GET(pd->stack->last,
+ Content_Data);
+ if (prev_cd)
+ {
+ Eo *prev_content = prev_cd->content;
+
+ //If content is being pushed now, then finish current animation.
+ if (prev_cd->on_pushing)
+ {
+ efl_canvas_object_event_animation_set(prev_content,
+ EFL_GFX_EVENT_HIDE,
+ NULL);
+ }
+ prev_cd->on_popping = EINA_TRUE;
+
+ //Loaded Event
+ Efl_Ui_Stack_Event_Loaded loaded_info;
+ loaded_info.content = prev_content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_LOADED,
+ &loaded_info);
+
+ /* Apply transition to previous content.
+ * Show previous content with animation. */
+ {
+ Efl_Canvas_Animation *orig_show_anim =
+ efl_canvas_object_event_animation_get(prev_content,
+ EFL_GFX_EVENT_SHOW);
+
+ //Show with animation
+ if (!orig_show_anim)
+ efl_canvas_object_event_animation_set(prev_content,
+ EFL_GFX_EVENT_SHOW,
+ show_anim);
+
+ Transit_Data *td = calloc(1, sizeof(Transit_Data));
+ td->cd = prev_cd;
+ td->orig_anim = !!(orig_show_anim);
+ td->freeze_events =
+ efl_canvas_object_freeze_events_get(prev_content);
+
+ efl_event_callback_add(prev_content,
+ EFL_CANVAS_OBJECT_EVENT_ANIM_STARTED,
+ _anim_started_cb, NULL);
+ efl_event_callback_add(prev_content,
+ EFL_CANVAS_OBJECT_EVENT_ANIM_ENDED,
+ _anim_ended_cb, td);
+
+ /* efl_ui_widget_resize_object_set() calls efl_gfx_visible_set()
+ * internally.
+ * Therefore, efl_ui_widget_resize_object_set() is called after
+ * setting animation and efl_gfx_visible_set() is not called. */
+ efl_ui_widget_resize_object_set(obj, prev_content);
+ }
+
+ }
+ }
+
+ return NULL;
+}
+
+EOLIAN static void
+_efl_ui_stack_insert_before(Eo *obj, Efl_Ui_Stack_Data *pd,
+ Eo *base_content, Eo *content)
+{
+ if (!content) return;
+
+ Content_Data *base_cd = NULL;
+ EINA_INLIST_FOREACH(pd->stack, base_cd)
+ if (base_cd->content == base_content)
+ break;
+
+ if (!base_cd)
+ {
+ ERR("The given base content is not found in the stack!");
+ return;
+ }
+
+ Content_Data *cd = _content_data_new(obj, content);
+ if (!cd) return;
+
+ pd->stack = eina_inlist_prepend_relative(pd->stack,
+ EINA_INLIST_GET(cd),
+ EINA_INLIST_GET(base_cd));
+ evas_object_smart_member_add(content, obj);
+}
+
+EOLIAN static void
+_efl_ui_stack_insert_after(Eo *obj, Efl_Ui_Stack_Data *pd,
+ Eo *base_content, Eo *content)
+{
+ if (!content) return;
+
+ Content_Data *base_cd = NULL;
+ EINA_INLIST_FOREACH(pd->stack, base_cd)
+ if (base_cd->content == base_content)
+ break;
+
+ if (!base_cd)
+ {
+ ERR("The given base content is not found in the stack!");
+ return;
+ }
+
+ Content_Data *cd = _content_data_new(obj, content);
+ if (!cd) return;
+
+ pd->stack = eina_inlist_append_relative(pd->stack,
+ EINA_INLIST_GET(cd),
+ EINA_INLIST_GET(base_cd));
+ evas_object_smart_member_add(content, obj);
+
+ if (pd->stack->last == EINA_INLIST_GET(cd))
+ {
+ //Loaded Event
+ Efl_Ui_Stack_Event_Loaded loaded_info;
+ loaded_info.content = content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_LOADED,
+ &loaded_info);
+
+ /* Do not apply transition for insert.
+ * Hide top content without animation. */
+ {
+ Efl_Canvas_Animation *orig_hide_anim =
+ efl_canvas_object_event_animation_get(base_cd->content,
+ EFL_GFX_EVENT_HIDE);
+
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(base_cd->content,
+ EFL_GFX_EVENT_HIDE, NULL);
+
+ efl_gfx_visible_set(base_cd->content, EINA_FALSE);
+
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(base_cd->content,
+ EFL_GFX_EVENT_HIDE,
+ orig_hide_anim);
+ }
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = base_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = base_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+
+
+ /* Do not apply transition for insert.
+ * Show new content without animation. */
+ {
+ Efl_Canvas_Animation *orig_show_anim =
+ efl_canvas_object_event_animation_get(content,
+ EFL_GFX_EVENT_SHOW);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_SHOW,
+ NULL);
+
+ evas_object_raise(content);
+ /* efl_ui_widget_resize_object_set() calls efl_gfx_visible_set()
+ * internally.
+ * Therefore, efl_ui_widget_resize_object_set() is called after
+ * setting animation and efl_gfx_visible_set() is not called. */
+ efl_ui_widget_resize_object_set(obj, content);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_SHOW,
+ orig_show_anim);
+ }
+
+ //Activated Event
+ Efl_Ui_Stack_Event_Activated activated_info;
+ activated_info.content = content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_ACTIVATED,
+ &activated_info);
+ }
+}
+
+EOLIAN static void
+_efl_ui_stack_insert_at(Eo *obj, Efl_Ui_Stack_Data *pd,
+ int index, Eo *content)
+{
+ if (!content)
+ {
+ ERR("The given content is NULL!");
+ return;
+ }
+
+ int count = eina_inlist_count(pd->stack);
+ if ((index < 0) || (index > count))
+ {
+ ERR("The index(%d) should be from 0 to #contents in the stack(%d)!",
+ index, count);
+ return;
+ }
+
+ Content_Data *base_cd = NULL;
+
+ if (index == count)
+ {
+ base_cd = EINA_INLIST_CONTAINER_GET(pd->stack->last, Content_Data);
+ }
+ else
+ {
+ int i = 0;
+ EINA_INLIST_FOREACH(pd->stack, base_cd)
+ {
+ if (i == index)
+ break;
+
+ i++;
+ }
+ }
+
+ Content_Data *cd = _content_data_new(obj, content);
+ if (!cd) return;
+
+ if (index == count)
+ pd->stack = eina_inlist_append_relative(pd->stack,
+ EINA_INLIST_GET(cd),
+ EINA_INLIST_GET(base_cd));
+ else
+ pd->stack = eina_inlist_prepend_relative(pd->stack,
+ EINA_INLIST_GET(cd),
+ EINA_INLIST_GET(base_cd));
+
+ evas_object_smart_member_add(content, obj);
+
+ if (pd->stack->last == EINA_INLIST_GET(cd))
+ {
+ //Loaded Event
+ Efl_Ui_Stack_Event_Loaded loaded_info;
+ loaded_info.content = content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_LOADED,
+ &loaded_info);
+
+ /* Do not apply transition for insert.
+ * Hide top content without animation. */
+ {
+ Efl_Canvas_Animation *orig_hide_anim =
+ efl_canvas_object_event_animation_get(base_cd->content,
+ EFL_GFX_EVENT_HIDE);
+
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(base_cd->content,
+ EFL_GFX_EVENT_HIDE, NULL);
+
+ efl_gfx_visible_set(base_cd->content, EINA_FALSE);
+
+ if (orig_hide_anim)
+ efl_canvas_object_event_animation_set(base_cd->content,
+ EFL_GFX_EVENT_HIDE,
+ orig_hide_anim);
+ }
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = base_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = base_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+
+
+ /* Do not apply transition for insert.
+ * Show new content without animation. */
+ {
+ Efl_Canvas_Animation *orig_show_anim =
+ efl_canvas_object_event_animation_get(content,
+ EFL_GFX_EVENT_SHOW);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_SHOW,
+ NULL);
+
+ evas_object_raise(content);
+ /* efl_ui_widget_resize_object_set() calls efl_gfx_visible_set()
+ * internally.
+ * Therefore, efl_ui_widget_resize_object_set() is called after
+ * setting animation and efl_gfx_visible_set() is not called. */
+ efl_ui_widget_resize_object_set(obj, content);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(content, EFL_GFX_EVENT_SHOW,
+ orig_show_anim);
+ }
+
+ //Activated Event
+ Efl_Ui_Stack_Event_Activated activated_info;
+ activated_info.content = content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_ACTIVATED,
+ &activated_info);
+ }
+}
+
+EOLIAN static void
+_efl_ui_stack_remove(Eo *obj, Efl_Ui_Stack_Data *pd, Eo *content)
+{
+ if (!pd->stack)
+ {
+ ERR("There is no content in the stack!");
+ return;
+ }
+
+ if (!content)
+ {
+ ERR("The given content is NULL!");
+ return;
+ }
+
+ Content_Data *cd = NULL;
+
+ EINA_INLIST_FOREACH(pd->stack, cd)
+ {
+ if (cd->content == content)
+ break;
+ }
+ if (!cd)
+ {
+ ERR("The given content does not exist in the stack!");
+ return;
+ }
+
+ Eina_Bool remove_top = EINA_FALSE;
+ if (pd->stack->last == EINA_INLIST_GET(cd))
+ remove_top = EINA_TRUE;
+
+ pd->stack = eina_inlist_remove(pd->stack, EINA_INLIST_GET(cd));
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+ _content_data_del(cd);
+
+ if (remove_top)
+ {
+ if (pd->stack)
+ {
+ Content_Data *new_top_cd = EINA_INLIST_CONTAINER_GET(pd->stack->last,
+ Content_Data);
+ if (new_top_cd)
+ {
+ //Loaded Event
+ Efl_Ui_Stack_Event_Loaded loaded_info;
+ loaded_info.content = new_top_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_LOADED,
+ &loaded_info);
+
+ /* Do not apply transition for insert.
+ * Show new content without animation. */
+ {
+ Efl_Canvas_Animation *orig_show_anim =
+ efl_canvas_object_event_animation_get(new_top_cd->content,
+ EFL_GFX_EVENT_SHOW);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(new_top_cd->content,
+ EFL_GFX_EVENT_SHOW,
+ NULL);
+
+ evas_object_raise(new_top_cd->content);
+ /* efl_ui_widget_resize_object_set() calls efl_gfx_visible_set()
+ * internally.
+ * Therefore, efl_ui_widget_resize_object_set() is called after
+ * setting animation and efl_gfx_visible_set() is not called. */
+ efl_ui_widget_resize_object_set(obj, new_top_cd->content);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(new_top_cd->content,
+ EFL_GFX_EVENT_SHOW,
+ orig_show_anim);
+ }
+
+ //Activated Event
+ Efl_Ui_Stack_Event_Activated activated_info;
+ activated_info.content = new_top_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_ACTIVATED,
+ &activated_info);
+ }
+ }
+ }
+}
+
+EOLIAN static void
+_efl_ui_stack_remove_at(Eo *obj, Efl_Ui_Stack_Data *pd,
+ int index)
+{
+ if (!pd->stack)
+ {
+ ERR("There is no content in the stack!");
+ return;
+ }
+
+ int count = eina_inlist_count(pd->stack);
+ if ((index < 0) || (index >= count))
+ {
+ ERR("The index(%d) should be from 0 to (#contents - 1) in the stack(%d)!",
+ index, count);
+ return;
+ }
+
+ Content_Data *cd = NULL;
+ int i = 0;
+ EINA_INLIST_FOREACH(pd->stack, cd)
+ {
+ if (i == index)
+ break;
+ i++;
+ }
+
+ Eina_Bool remove_top = EINA_FALSE;
+ if (pd->stack->last == EINA_INLIST_GET(cd))
+ remove_top = EINA_TRUE;
+
+ pd->stack = eina_inlist_remove(pd->stack, EINA_INLIST_GET(cd));
+
+ //Deactivated Event
+ Efl_Ui_Stack_Event_Deactivated deactivated_info;
+ deactivated_info.content = cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_DEACTIVATED,
+ &deactivated_info);
+
+ //Unloaded Event
+ Efl_Ui_Stack_Event_Unloaded unloaded_info;
+ unloaded_info.content = cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_UNLOADED,
+ &unloaded_info);
+ _content_data_del(cd);
+
+ //FIXME: Apply transition here.
+ if (remove_top)
+ {
+ if (pd->stack)
+ {
+ Content_Data *new_top_cd = EINA_INLIST_CONTAINER_GET(pd->stack->last,
+ Content_Data);
+ if (new_top_cd)
+ {
+ //Loaded Event
+ Efl_Ui_Stack_Event_Loaded loaded_info;
+ loaded_info.content = new_top_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_LOADED,
+ &loaded_info);
+
+ /* Do not apply transition for insert.
+ * Show new content without animation. */
+ {
+ Efl_Canvas_Animation *orig_show_anim =
+ efl_canvas_object_event_animation_get(new_top_cd->content,
+ EFL_GFX_EVENT_SHOW);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(new_top_cd->content,
+ EFL_GFX_EVENT_SHOW,
+ NULL);
+
+ evas_object_raise(new_top_cd->content);
+ /* efl_ui_widget_resize_object_set() calls efl_gfx_visible_set()
+ * internally.
+ * Therefore, efl_ui_widget_resize_object_set() is called after
+ * setting animation and efl_gfx_visible_set() is not called. */
+ efl_ui_widget_resize_object_set(obj, new_top_cd->content);
+
+ if (orig_show_anim)
+ efl_canvas_object_event_animation_set(new_top_cd->content,
+ EFL_GFX_EVENT_SHOW,
+ orig_show_anim);
+ }
+
+ //Activated Event
+ Efl_Ui_Stack_Event_Activated activated_info;
+ activated_info.content = new_top_cd->content;
+ efl_event_callback_call(obj, EFL_UI_STACK_EVENT_ACTIVATED,
+ &activated_info);
+ }
+ }
+ }
+}
+
+EOLIAN static int
+_efl_ui_stack_index_get(Eo *obj EINA_UNUSED, Efl_Ui_Stack_Data *pd, Efl_Canvas_Object *content)
+{
+ if (!pd->stack)
+ {
+ ERR("There is no content in the stack!");
+ return -1;
+ }
+
+ if (!content)
+ {
+ ERR("The given content is NULL!");
+ return -1;
+ }
+
+ Content_Data *cd = NULL;
+ int index = 0;
+ int count = eina_inlist_count(pd->stack);
+
+ EINA_INLIST_FOREACH(pd->stack, cd)
+ {
+ if (cd->content == content)
+ break;
+ index++;
+ }
+
+ //The given content is not found.
+ if (index == count) return -1;
+
+ return index;
+}
+
+EOLIAN static Eo *
+_efl_ui_stack_content_get(Eo *obj EINA_UNUSED, Efl_Ui_Stack_Data *pd, int index)
+{
+ if (!pd->stack)
+ {
+ ERR("There is no content in the stack!");
+ return NULL;
+ }
+
+ int count = eina_inlist_count(pd->stack);
+ if ((index < 0) || (index >= count))
+ {
+ ERR("The index(%d) should be from 0 to (#contents - 1) in the stack(%d)!",
+ index, count);
+ return NULL;
+ }
+
+ Content_Data *cd = NULL;
+ int i = 0;
+ EINA_INLIST_FOREACH(pd->stack, cd)
+ {
+ if (i == index)
+ break;
+ i++;
+ }
+
+ if (cd)
+ return cd->content;
+
+ return NULL;
+}
+
+EOLIAN static Eo *
+_efl_ui_stack_top(Eo *obj EINA_UNUSED, Efl_Ui_Stack_Data *pd)
+{
+ if (!pd->stack) return NULL;
+
+ Content_Data *cd = EINA_INLIST_CONTAINER_GET(pd->stack->last, Content_Data);
+ if (!cd) return NULL;
+
+ return cd->content;
+}
+
+EOLIAN static Eo *
+_efl_ui_stack_efl_object_constructor(Eo *obj, Efl_Ui_Stack_Data *pd EINA_UNUSED)
+{
+ obj = efl_constructor(efl_super(obj, MY_CLASS));
+ efl_canvas_object_type_set(obj, MY_CLASS_NAME);
+
+ //Default Show Animation
+ show_anim = efl_add(EFL_CANVAS_ANIMATION_ALPHA_CLASS, obj);
+ efl_animation_alpha_set(show_anim, 0.0, 1.0);
+ efl_animation_duration_set(show_anim, 0.5);
+ efl_animation_final_state_keep_set(show_anim, EINA_TRUE);
+
+ //Default Hide Animation
+ hide_anim = efl_add(EFL_CANVAS_ANIMATION_ALPHA_CLASS, obj);
+ efl_animation_alpha_set(hide_anim, 1.0, 0.0);
+ efl_animation_duration_set(hide_anim, 0.5);
+ efl_animation_final_state_keep_set(hide_anim, EINA_TRUE);
+
+ return obj;
+}
+
+#include "efl_ui_stack.eo.c"
diff --git a/src/lib/elementary/efl_ui_stack.eo b/src/lib/elementary/efl_ui_stack.eo
new file mode 100644
index 0000000000..2635e15588
--- /dev/null
+++ b/src/lib/elementary/efl_ui_stack.eo
@@ -0,0 +1,131 @@
+struct Efl.Ui.Stack.Event_Loaded {
+ [[Information of loaded event.]]
+ content: Efl.Canvas.Object; [[Loaded content.]]
+}
+
+struct Efl.Ui.Stack.Event_Unloaded {
+ [[Information of unloaded event.]]
+ content: Efl.Canvas.Object; [[Unloaded content.]]
+}
+
+struct Efl.Ui.Stack.Event_Activated {
+ [[Information of activated event.]]
+ content: Efl.Canvas.Object; [[Activated content.]]
+}
+
+struct Efl.Ui.Stack.Event_Deactivated {
+ [[Information of deactivated event.]]
+ content: Efl.Canvas.Object; [[Deactivated content.]]
+}
+
+class Efl.Ui.Stack (Efl.Ui.Layout)
+{
+ [[Stack widget.
+
+ Stack widget arranges objects in stack structure by pushing and poping them.
+ ]]
+ methods {
+ push {
+ [[Pushes a new object to the top of the stack and shows it.
+ ]]
+ params {
+ @in content: Efl.Canvas.Object;
+ [[The pushed object which becomes the top content of the stack.]]
+ }
+ }
+ pop {
+ [[Pops the top content from the stack and deletes it.
+ ]]
+ return: Efl.Canvas.Object;
+ [[The top content which is removed from the stack.]]
+ }
+ insert_before {
+ [[Inserts an object before the given base content in the stack.
+ ]]
+ params {
+ @in base_content: Efl.Canvas.Object;
+ [[$content is inserted before this $base_content.]]
+ @in content: Efl.Canvas.Object;
+ [[The inserted object in the stack.]]
+ }
+ }
+ insert_after {
+ [[Inserts an object after the given base content in the stack.
+ ]]
+ params {
+ @in base_content: Efl.Canvas.Object;
+ [[$content is inserted after this $base_content.]]
+ @in content: Efl.Canvas.Object;
+ [[The inserted object in the stack.]]
+ }
+ }
+ insert_at {
+ [[Inserts an object at the given place in the stack.
+ ]]
+ params {
+ @in index: int;
+ [[The index of the inserted object in the stack.
+ $index begins from bottom to top of the stack.
+ $index of the bottom content is 0.
+ ]]
+ @in content: Efl.Canvas.Object;
+ [[The inserted object in the stack.]]
+ }
+ }
+ remove {
+ [[Removes the given content in the stack.
+ ]]
+ params {
+ @in content: Efl.Canvas.Object;
+ [[The removed content from the stack.]]
+ }
+ }
+ remove_at {
+ [[Removes a content matched to the given index in the stack.
+ ]]
+ params {
+ @in index: int;
+ [[The index of the removed object in the stack.
+ $index begins from bottom to top of the stack.
+ $index of the bottom content is 0.
+ ]]
+ }
+ }
+ index_get {
+ [[Gets the index of the given content in the stack.
+ The index begins from bottom to top of the stack.
+ The index of the bottom content is 0.
+ ]]
+ return: int;
+ [[The index of $content in the stack.]]
+ params {
+ @in content: Efl.Canvas.Object;
+ [[The content matched to the index to be returned in the stack.]]
+ }
+ }
+ content_get {
+ [[Gets the content matched to the given index in the stack.
+ ]]
+ return: Efl.Canvas.Object;
+ [[The content matched to $index in the stack.]]
+ params {
+ @in index: int;
+ [[The index of the content to be returned in the stack.]]
+ }
+ }
+ top {
+ [[Gets the top content in the stack.
+ ]]
+ return: Efl.Canvas.Object; [[The top content in the stack.]]
+ }
+ }
+ implements {
+ Efl.Object.constructor;
+ }
+ events {
+ loaded; [[Called when content is loaded right before transition.]]
+ unloaded; [[Called when content is unloaded right after being deactivated.]]
+ activated; [[Called when content is activated right after transition.]]
+ deactivated; [[Called when content is deactivated right after transition.]]
+ }
+}
diff --git a/src/lib/elementary/efl_ui_stack_private.h b/src/lib/elementary/efl_ui_stack_private.h
new file mode 100644
index 0000000000..dc67451d58
--- /dev/null
+++ b/src/lib/elementary/efl_ui_stack_private.h
@@ -0,0 +1,30 @@
+#ifndef EFL_UI_WIDGET_STACK_H
+#define EFL_UI_WIDGET_STACK_H
+
+typedef struct _Efl_Ui_Stack_Data Efl_Ui_Stack_Data;
+struct _Efl_Ui_Stack_Data
+{
+ Eina_Inlist *stack; /* the last item is the top item */
+};
+
+typedef struct _Content_Data Content_Data;
+struct _Content_Data
+{
+ EINA_INLIST;
+
+ Eo *stack;
+ Eo *content;
+ Eina_Bool on_pushing : 1;
+ Eina_Bool on_popping : 1;
+ Eina_Bool popped_hidden : 1;
+};
+
+typedef struct _Transit_Data Transit_Data;
+struct _Transit_Data
+{
+ Content_Data *cd;
+ Eina_Bool orig_anim;
+ Eina_Bool freeze_events;
+};
+
+#endif