Edje Animations
This tutorial demonstrates how you can turn Edje animations on and off, pause, stop and resume them, and check the status of Edje parts.
Creating the Application Layout
Create a simple application with a menu and separate screens for each Edje Animation function:
-
Create a simple main layout with a swallow for demo animation and another for controls:
collections { group { name: "demo"; parts { part { name: "swallow.demo"; type: SWALLOW; description { state: "default" 0.0; rel2 { relative: 1.0 0.0; to_y: "swallow.control"; } } } part { name: "swallow.control"; type: SWALLOW; description { state: "default" 0.0; rel1 { relative: 0.0 0.8; } } } } } }
-
Create a group for the cycled animation:
group { name: "cycled_animation"; parts { part { name: "bg"; type: RECT; description { state: "default" 0.0; color: 100 100 100 255; } description { state: "clicked" 0.0; color: 200 100 100 255; } } part { name: "a"; type: RECT; description { state: "default" 0.0; color: 0 255 0 255; rel2 { relative: 0.3 0.3; } } description { state: "red" 0.0; color: 255 0 0 255; rel1 { relative: 0.35 0.7; } rel2 { relative: 0.65 1.0; } } description { state: "blue" 0.0; color: 0 0 255 255; rel1 { relative: 0.7 0.0; } rel2 { relative: 1.0 0.3; } } } } programs { program { name: "bg_click"; signal: "mouse,clicked,*"; source: "bg"; action: STATE_SET "clicked" 0.0; target: "bg"; } program { name: "step1"; signal: "load"; source: ""; action: STATE_SET "red" 0.0; transition: LINEAR 1.0; target: "a"; after: "step2"; } program { name: "step2"; action: STATE_SET "blue" 0.0; transition: LINEAR 1.0; target: "a"; after: "step3"; } program { name: "step3"; action: STATE_SET "default" 0.0; transition: LINEAR 1.0; target: "a"; after: "step1"; } } }
-
Create another group for animation that can be activated by clicking:
group { name: "clickme"; parts { part { name: "bg"; type: RECT; description { state: "default" 0.0; color: 100 100 100 255; } } part { name: "text_bg"; type: RECT; description { state: "default" 0.0; rel1 { to: "text"; } rel2 { to: "text"; } } } part { name: "text"; type: TEXT; description { state: "default" 0.0; align: 0.0 0.0; color: 0 0 0 255; text { text: ":-)"; font: "Sans"; size: 42; min: 1 1; max: 1 1; } } description { state: "state2" 0.0; inherit: "default" 0.0; align: 1.0 1.0; text.text: ":-D"; } } part { name: "text_bg2"; type: RECT; description { state: "default" 0.0; color: 0 200 0 255; rel1 { to: "text2"; } rel2 { to: "text2"; } } description { state: "state2" 0.0; inherit: "default" 0.0; color: 200 0 0 255; } } part { name: "text2"; type: TEXT; description { state: "default" 0.0; align: 0.0 1.0; visible: 0; text { text: "Click me"; font: "Sans"; size: 42; min: 1 1; max: 1 1; } } description { state: "state2" 0.0; inherit: "default" 0.0; visible: 1; } } part { name: "text3"; type: TEXT; description { state: "default" 0.0; align: 0.0 1.0; visible: 1; text { text: "Click me"; font: "Sans"; size: 42; min: 1 1; max: 1 1; } } description { state: "state2" 0.0; inherit: "default" 0.0; visible: 0; } } } programs { program { name: "clicked"; signal: "mouse,clicked,1"; source: "text3"; action: STATE_SET "state2" 0.00; transition: LINEAR 3.00000; target: "text"; target: "text2"; target: "text3"; target: "text_bg2"; } program { name: "return"; signal: "mouse,clicked,1"; source: "text2"; action: STATE_SET "default" 0.00; transition: LINEAR 1.00000; target: "text"; target: "text2"; target: "text3"; target: "text_bg2"; } } }
Creating the Application Logic
- Create new project named test.
- Add 2 new files to the application project: edje_animation.h and edje_animation.c.
- To access the application data from the newly created files, move the appdata_s structure from the test.c file to the test.h file and remove the static specifier.
- Add the following code to the edje_animation.h file:
#ifndef __edje_animation_H__ #define __edje_animation_H__ #include "test.h" typedef struct _edje_animation_item_s { const char *name; Evas_Object* (*func)(appdata_s *ap); } Edje_Animation_Menu_Item; #endif // __edje_animation_H__
This structure will define menu items and their callbacks.
-
Create a global menu array in the edje_animation.c file and fill it with necessary items:
Edje_Animation_Menu_Item edje_animation_items[] = { {"test1", _test1_func }, {"test2", _test2_func }, {NULL, NULL } // Do not delete };
-
Use the array to fill the main menu:
extern Edje_Animation_Menu_Item edje_animation_items[]; static void create_base_gui(appdata_s *ad) { // Genlist ad->itc = elm_genlist_item_class_new(); ad->itc->func.text_get =_genlist_item_text_get; ad->genlist = elm_genlist_add(ad->win); evas_object_size_hint_weight_set(ad->genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); int i; Elm_Object_Item *eoi = NULL; for (i = 0; edje_animation_items[i].name; ++i) { eoi = elm_genlist_item_append(ad->genlist, ad->itc, edje_animation_items[i].name, NULL, ELM_GENLIST_ITEM_NONE, _genlist_clicked, ad); elm_object_item_data_set(eoi, (void *)&edje_animation_items[i]); } }
- All examples use the same basic layout:
static void app_get_resource(const char *edj_file_in, char *edj_path_out, int edj_path_max) { char *res_path = app_get_resource_path(); if (res_path) { snprintf(edj_path_out, edj_path_max, "%s%s", res_path, edj_file_in); free(res_path); } } static void _example_layout_create(appdata_s *ad, const char *layout_group, const char *demo_group, Evas_Object **layout_out, Evas_Object **animation_out) { char edj_path[PATH_MAX] = {0, }; app_get_resource(EDJ_FILE, edj_path, (int)PATH_MAX); *layout_out = elm_layout_add(ad->win); elm_layout_file_set(*layout_out, edj_path, layout_group); evas_object_size_hint_weight_set(*layout_out, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); *animation_out = edje_object_add(evas_object_evas_get(ad->win)); edje_object_file_set(*animation_out, edj_path, demo_group); evas_object_size_hint_weight_set(*animation_out, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_layout_content_set(*layout_out, SWALLOW_DEMO, *animation_out); evas_object_show(*animation_out); }
- You can try out the following operations to see how the application works:
- Check the time for each frame.
The edje_frametime_get() function allows you to check how much time is elapsed for single frame. This is a value of an internal parameter and not actual frame time. You can change it using the edje_frametime_set() function.
Note Changing the frame time affects all objects on the same canvas, not only on the given one. Use the edje_frametime_get() function and edje_frametime_set() function in the edje_animation.c file:
// edje_frametime_set/get static void _frametime_test_spinner_changed_cb(void *data, Evas_Object *obj, void *event_info) { edje_frametime_set(elm_spinner_value_get(obj)); } Evas_Object * frametime_test(appdata_s *ad) { Evas_Object *layout, *animation, *spinner; _example_layout_create(ad, GRP_DEMO, GRP_CYCLED_ANIM, &layout, &animation); spinner = elm_spinner_add(ad->win); elm_spinner_label_format_set(spinner, "Frame_t: %1.3f"); double framerate = edje_frametime_get(); elm_spinner_min_max_set(spinner, 0.001, 2.0); elm_spinner_step_set(spinner, 0.005); elm_spinner_value_set(spinner, framerate); evas_object_smart_callback_add(spinner, "changed", _frametime_test_spinner_changed_cb, NULL); elm_layout_content_set(layout, SWALLOW_CONTROL, spinner); evas_object_show(spinner); return layout; } Edje_Animation_Menu_Item edje_animation_items[] = { {"frametime test", frametime_test}, {NULL, NULL} };
The spinner displays the current frame time value and enables changing it. Extreme values (> 0.9) are affecting animation.
Figure: Displaying the frame time
- Pause and resume animation and check its status.
The edje_object_play_set() function allows you to pause or resume animation for a given object. A resumed animation starts from the same point.
To check whether the animation is paused, use the edje_object_play_get() function.
// edje_object_play_set/get static void _play_test_button_cb(void *data, Evas_Object *obj, void *event_info) { Evas_Object *anim = data; if (edje_object_play_get(anim)) { edje_object_play_set(anim, EINA_FALSE); elm_object_text_set(obj, "Play"); } else { edje_object_play_set(anim, EINA_TRUE); elm_object_text_set(obj, "Pause"); } } Evas_Object * play_test(appdata_s *ad) { Evas_Object *layout, *animation, *button; _example_layout_create(ad, GRP_DEMO, GRP_CYCLED_ANIM, &layout, &animation); button = elm_button_add(ad->win); elm_object_text_set(button, "Pause"); evas_object_smart_callback_add(button, "clicked", _play_test_button_cb, animation); elm_layout_content_set(layout, SWALLOW_CONTROL, button); evas_object_show(button); return layout; } Edje_Animation_Menu_Item edje_animation_items[] = { {"frametime test", frametime_test}, {"play test", play_test}, {NULL, NULL} };
- Stop the animation.
The edje_object_freeze() function allows you to stop animation rendering. This function puts all changes on hold. Successive freezes are nested, requiring an equal number of thaws. Using the edje_object_thaw() function, you can apply all changes immediately.
// edje_object_freeze/thaw static void _freeze_test_button_cb(void *data, Evas_Object *obj, void *event_info) { Evas_Object *anim = data; static int i = 0; if(!i) { i = edje_object_freeze(anim); elm_object_text_set(obj, "Thaw"); } else { i = edje_object_thaw(anim); elm_object_text_set(obj, "Freeze"); } } Evas_Object * freeze_test(appdata_s *ad) { Evas_Object *layout, *animation, *button; _example_layout_create(ad, GRP_DEMO, GRP_CYCLED_ANIM, &layout, &animation); button = elm_button_add(ad->win); elm_object_text_set(button, "Freeze"); evas_object_smart_callback_add(button, "clicked", _freeze_test_button_cb, animation); elm_layout_content_set(layout, SWALLOW_CONTROL, button); evas_object_show(button); return layout; } Edje_Animation_Menu_Item edje_animation_items[] = { {"frametime test", frametime_test}, {"play test", play_test}, {"freeze/thaw test", freeze_test}, {NULL, NULL} };
The edje_freeze() function and edje_thaw() function freeze or thaw all objects in the entire application.
- Enable and disable the animation.
All transitions in edje programs are ignored and parts go directly to their final states.
// edje_object_animation_set/get static void _animation_test_button_cb(void *data, Evas_Object *obj, void *event_info) { Evas_Object *anim = data; if (edje_object_animation_get(anim)) { edje_object_animation_set(anim, EINA_FALSE); elm_object_text_set(obj, "Turn animation on"); } else { edje_object_animation_set(anim, EINA_TRUE); elm_object_text_set(obj, "Turn animation off"); } } Evas_Object * animation_test(appdata_s *ad) { Evas_Object *layout, *animation, *button; _example_layout_create(ad, GRP_DEMO, GRP_CLICKME, &layout, &animation); button = elm_button_add(ad->win); elm_object_text_set(button, "Turn animation off"); evas_object_smart_callback_add(button, "clicked", _animation_test_button_cb, animation); elm_layout_content_set(layout, SWALLOW_CONTROL, button); evas_object_show(button); return layout; } Edje_Animation_Menu_Item edje_animation_items[] = { {"frametime test", frametime_test}, {"play test", play_test}, {"freeze/thaw test", freeze_test}, {"animation test", animation_test}, {NULL, NULL} };
- Get the current state of given part.
Parts that are in transition return the transition's starting state.
// edje_object_part_state_get static void _state_get_test_button_cb(void *data, Evas_Object *obj, void *event_info) { Evas_Object *anim = data; const char *state; double state_val = 0; state = edje_object_part_state_get(anim, "text", &state_val); edje_object_part_text_set(anim, "text", state); } Evas_Object * state_get_test(appdata_s *ad) { Evas_Object *layout, *animation, *button; _example_layout_create(ad, GRP_DEMO, GRP_CLICKME, &layout, &animation); button = elm_button_add(ad->win); elm_object_text_set(button, "Get state"); evas_object_smart_callback_add(button, "clicked", _state_get_test_button_cb, animation); elm_layout_content_set(layout, SWALLOW_CONTROL, button); evas_object_show(button); return layout; } Edje_Animation_Menu_Item edje_animation_items[] = { {"frametime test", frametime_test}, {"play test", play_test}, {"freeze/thaw test", freeze_test}, {"animation test", animation_test}, {"part state get test", state_get_test}, {NULL, NULL} };
- Check the time for each frame.