From ab4fa83f62ea8f23e137bd6f245502c5c6cdaaa0 Mon Sep 17 00:00:00 2001 From: Jaehyun Cho Date: Thu, 12 Apr 2018 21:09:38 +0900 Subject: [PATCH] efl_ui_stack: Add Efl.Ui.Stack class Efl.Ui.Stack is a container arranges objects in stack structure by pushing and popping them. --- src/Makefile_Elementary.am | 3 + src/lib/elementary/Elementary.h | 1 + src/lib/elementary/efl_ui_stack.c | 972 ++++++++++++++++++++++ src/lib/elementary/efl_ui_stack.eo | 131 +++ src/lib/elementary/efl_ui_stack_private.h | 30 + 5 files changed, 1137 insertions(+) create mode 100644 src/lib/elementary/efl_ui_stack.c create mode 100644 src/lib/elementary/efl_ui_stack.eo create mode 100644 src/lib/elementary/efl_ui_stack_private.h diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index 0ae0a2d21b..a9e1dc252b 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 \ @@ -326,6 +327,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 \ @@ -668,6 +670,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 # include # include +# include #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 + +#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