diff --git a/docs/src/details/main-modules/animation.rst b/docs/src/details/main-modules/animation.rst index 8976c1789b..c463b2fddd 100644 --- a/docs/src/details/main-modules/animation.rst +++ b/docs/src/details/main-modules/animation.rst @@ -366,13 +366,6 @@ Call :cpp:expr:`lv_anim_timeline_delete(timeline)` function to delete the Animat Timeline before deleting the Widget. Otherwise, the program may crash or behave abnormally. -If a base object is set with :cpp:expr:`lv_anim_timeline_set_base_object(timeline, obj)`, -``var`` in the added animations is assumed to be a widget name (or path) string. -The actual widgets are retrieved by :cpp:expr:`lv_obj_get_child_by_name` before -calling the ``exec_cb`` of the animation. That is, the ``exec_cb`` gets a pointer to -the widget, and not the name/path. - - .. image:: /_static/images/anim-timeline.png .. _animations_example: diff --git a/docs/src/details/xml/ui_elements/animations.rst b/docs/src/details/xml/ui_elements/animations.rst index 42335bf8de..26deb04e97 100644 --- a/docs/src/details/xml/ui_elements/animations.rst +++ b/docs/src/details/xml/ui_elements/animations.rst @@ -5,7 +5,7 @@ Animations ========== Overview --------- +******** XML animations are built on top of :ref:`Timeline animations `. @@ -16,7 +16,7 @@ Each component can define its own timeline animations, which can then be played component itself or by any parent component. Defining Timelines ------------------- +****************** Timelines can be defined inside ````\ s and ````\ s. For ````\ s, timelines are supported only in LVGL's UI Editor, @@ -32,8 +32,9 @@ Example: - + + @@ -52,7 +53,7 @@ Example: -In summary: inside ````, you can define ````\s, each with a unique name +In summary: inside ````, you can define ````\ s, each with a unique name that you can reference later. Inside a ````, you add ````\ s to describe each step. @@ -64,11 +65,22 @@ Supported properties of ```` are: - ``start``: Start value (integer only). - ``end``: End value (integer only). - ``duration``: Duration of the animation in milliseconds. -- ``delay``: Delay before starting in milliseconds. -- ``early_apply``: If ``true``, the start value is applied immediately, even during the delay. +- ``delay``: Delay before starting in milliseconds. Default is 0. +- ``early_apply``: If ``true``, the start value is applied immediately, even during the delay. Default is ``false``. + +```` also can be used in ````\ s to "merge" the animations +of another timeline. Imagine that in the example above ``my_icon`` defines a ``"show_up"`` timeline +which fades in and scales up the icon. All these are described in the ``my_icon.xml`` in an +encapsulated way but can be referenced in other timelines. + +To include a timeline, the following properties shall be used: + +- ``target``: name of the target UI element whose timeline should be included. ``self`` refers to the root element of the component (the ````). +- ``timeline``: name of the timeline to include. Shall be defined in the ``target``'s XML file. +- ``delay``: Delay before starting in milliseconds. Default is 0. Playing Timelines ------------------ +***************** Timelines can be triggered by events (e.g. click) using ```` as a child of any widget. @@ -90,25 +102,33 @@ If ``target="self"``, the timeline is looked up in the current component/widget/ (i.e. in the current XML file). You can also set a ``delay`` and ``reverse="true"`` when playing a timeline. - Under the Hood --------------- +************** Understanding how timelines work internally helps in using them effectively. When an XML file is registered, the contents of the ```` section are parsed, -and the animation data is stored as a blueprint. +and the ````'s data is stored as a "blueprint". The descriptors store the targets' +names as strings. -When an instance of a component or screen is created, ``lv_anim_timeline``\s are -created and initialized from the saved blueprint. Each instance gets its own copy of the timelines. +When an instance of a component or screen is created, as the last step ``lv_anim_timeline``\ s are +created and initialized from the saved "blueprints". If ````\ s are also used, +the requested timeline is included in the component's timeline at this point too. +As all the children are also created at this point, the saved animation target names are resolved +to pointers by using :cpp:expr:`lv_obj_find_by_name`. -When a ```` is added to a UI element, the target and timeline -names are saved as strings. (It can't use pointers as the event can reference UI elements -that will be created only later in the ````.) -Finally, when the play timeline event is triggered, LVGL finds the target widget by the saved name, -retrieves the specified timeline, and starts it. +The created timeline instances and their names are saved in the component's instance. -Since each instance has its own timeline, you can have multiple components (e.g. 10 ````\s) +Since each instance has its own timeline, you can have multiple components (e.g. 10 ````\ s) and play their ``load`` timelines independently with different delays. +When a ```` is added to a UI element, the target and timeline +names are saved as strings. It cannot use pointers as the event can reference UI elements +that will be created only later in the ````. + +Finally, when the play timeline event is triggered, the selected timeline is retrieved by its name from the target +and started according to the other parameters (reverse, delay, etc). + + + diff --git a/src/misc/lv_anim_timeline.c b/src/misc/lv_anim_timeline.c index b206719fb6..a3cb876169 100644 --- a/src/misc/lv_anim_timeline.c +++ b/src/misc/lv_anim_timeline.c @@ -29,7 +29,7 @@ static void anim_timeline_exec_cb(void * var, int32_t v); static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_time); static int32_t anim_timeline_path_cb(const lv_anim_t * a); -static void exec_anim(lv_anim_timeline_t * at, lv_anim_t * a, int32_t v); +static void exec_anim(lv_anim_t * a, int32_t v); /********************** * STATIC VARIABLES @@ -156,16 +156,6 @@ void lv_anim_timeline_set_user_data(lv_anim_timeline_t * at, void * user_data) at->user_data = user_data; } -#if LV_USE_OBJ_NAME - -void lv_anim_timeline_set_base_obj(lv_anim_timeline_t * at, lv_obj_t * base_obj) -{ - LV_ASSERT_NULL(at); - at->base_obj = base_obj; -} - -#endif - uint32_t lv_anim_timeline_get_playtime(lv_anim_timeline_t * at) { LV_ASSERT_NULL(at); @@ -222,16 +212,16 @@ void * lv_anim_timeline_get_user_data(lv_anim_timeline_t * at) return at->user_data; } - -#if LV_USE_OBJ_NAME - -lv_obj_t * lv_anim_timeline_get_base_obj(lv_anim_timeline_t * at) +void lv_anim_timeline_merge(lv_anim_timeline_t * dest, const lv_anim_timeline_t * src, int32_t delay) { - LV_ASSERT_NULL(at); - return at->base_obj; + uint32_t i; + for(i = 0; i < src->anim_dsc_cnt; i++) { + uint32_t anim_delay = src->anim_dsc[i].start_time + delay; + lv_anim_timeline_add(dest, anim_delay, &src->anim_dsc[i].anim); + } } -#endif + /********************** * STATIC FUNCTIONS **********************/ @@ -259,7 +249,7 @@ static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_tim } value = a->start_value; - exec_anim(at, a, value); + exec_anim(a, value); if(anim_timeline_is_started) { if(at->reverse) { @@ -279,7 +269,7 @@ static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_tim a->act_time = act_time - start_time; value = a->path_cb(a); - exec_anim(at, a, value); + exec_anim(a, value); if(anim_timeline_is_started) { if(at->reverse) { @@ -314,7 +304,7 @@ static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_tim } value = a->end_value; - exec_anim(at, a, value); + exec_anim(a, value); if(anim_timeline_is_started) { if(at->reverse) { @@ -341,38 +331,13 @@ static void anim_timeline_exec_cb(void * var, int32_t v) anim_timeline_set_act_time(at, v); } -static void exec_anim(lv_anim_timeline_t * at, lv_anim_t * a, int32_t v) +static void exec_anim(lv_anim_t * a, int32_t v) { - /*a->var stores children names if at->base_obj is set. */ -#if LV_USE_OBJ_NAME - lv_obj_t * obj_resolved; - if(at->base_obj) { - if(lv_streq(a->var, "self")) obj_resolved = at->base_obj; - else if(lv_streq(a->var, "")) obj_resolved = at->base_obj; - else obj_resolved = lv_obj_find_by_name(at->base_obj, a->var); - if(obj_resolved == NULL) { - LV_LOG_WARN("Widget was not found with name `%s` as child of %p", (const char *)a->var, (void *)at->base_obj); - return; - } - } - else { - obj_resolved = a->var; - } -#else - LV_UNUSED(at); - lv_obj_t * obj_resolved = a->var; -#endif - - if(a->exec_cb) { - a->exec_cb(obj_resolved, v); + a->exec_cb(a->var, v); } if(a->custom_exec_cb) { - /*Temporarily replace the var with the resolved object*/ - void * var_ori = a->var; - a->var = obj_resolved; a->custom_exec_cb(a, v); - a->var = var_ori; } } diff --git a/src/misc/lv_anim_timeline.h b/src/misc/lv_anim_timeline.h index 138abf75a7..941d315526 100644 --- a/src/misc/lv_anim_timeline.h +++ b/src/misc/lv_anim_timeline.h @@ -114,17 +114,6 @@ void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress); */ void lv_anim_timeline_set_user_data(lv_anim_timeline_t * at, void * user_data); -#if LV_USE_OBJ_NAME -/** - * Set base object. - * If set, it's assumed that the `var` of animations is a widget name (path). - * The widget pointer will be retrieved by finding them by name on this widget. - * @param at pointer to the animation timeline. - * @param base_obj pointer to a widget - */ -void lv_anim_timeline_set_base_obj(lv_anim_timeline_t * at, lv_obj_t * base_obj); -#endif - /** * Get the time used to play the animation timeline. * @param at pointer to the animation timeline. @@ -171,18 +160,13 @@ uint32_t lv_anim_timeline_get_repeat_delay(lv_anim_timeline_t * at); */ void * lv_anim_timeline_get_user_data(lv_anim_timeline_t * at); - -#if LV_USE_OBJ_NAME /** - * Get base object. - * If set, it's assumed that the `var` of animations is a widget name (path). - * The widget pointer will be retrieved by finding them by name on this widget. - * @param at pointer to the animation timeline. - * @return pointer to the base widget + * Merge (add) all animations of a timeline to another + * @param dest merge animation into this timeline + * @param src merge the animations of this timeline + * @param delay add the animations with this extra delay */ -lv_obj_t * lv_anim_timeline_get_base_obj(lv_anim_timeline_t * at); -#endif - +void lv_anim_timeline_merge(lv_anim_timeline_t * dest, const lv_anim_timeline_t * src, int32_t delay); /********************** * MACROS diff --git a/src/misc/lv_anim_timeline_private.h b/src/misc/lv_anim_timeline_private.h index da26056468..cf94baed30 100644 --- a/src/misc/lv_anim_timeline_private.h +++ b/src/misc/lv_anim_timeline_private.h @@ -48,10 +48,6 @@ struct _lv_anim_timeline_t { /** Wait before repeat*/ uint32_t repeat_delay; - /** If set, it's assumed that the `var` of animations is a widget name (path). - * The widget pointer will be retrieved by finding them by name on this widget.*/ - lv_obj_t * base_obj; - /** For any custom data*/ void * user_data; }; diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index 0b591c03a6..6205fa02af 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -71,6 +71,7 @@ **********************/ static void view_start_element_handler(void * user_data, const char * name, const char ** attrs); static void view_end_element_handler(void * user_data, const char * name); +static void create_timeline_instances(lv_xml_parser_state_t * state); static void get_timeline_from_event_cb(lv_event_t * e); static void free_timelines_event_cb(lv_event_t * e); @@ -298,34 +299,7 @@ void * lv_xml_create_in_scope(lv_obj_t * parent, lv_xml_component_scope_t * pare } #endif - /*Create the timelines as well*/ - if(!lv_ll_is_empty(&scope->timeline_ll)) { - lv_xml_timeline_t * at_xml; - lv_anim_timeline_t ** timeline_array; - timeline_array = lv_malloc((lv_ll_get_len(&scope->timeline_ll) + 1) * sizeof(lv_anim_timeline_t *)); - uint32_t i = 0; - LV_LL_READ(&scope->timeline_ll, at_xml) { - lv_anim_timeline_t * at = lv_anim_timeline_create(); - at->user_data = lv_strdup(at_xml->name); - - lv_anim_t * a_stored; - LV_LL_READ(&at_xml->anims_ll, a_stored) { - int32_t delay = -a_stored->act_time; - lv_anim_timeline_add(at, delay, a_stored); - } - - at->base_obj = state.view; - timeline_array[i] = at; - i++; - } - - - timeline_array[i] = NULL; /*Closing to avoid storing the length*/ - - - lv_obj_add_event_cb(state.view, get_timeline_from_event_cb, lv_event_xml_store_timeline, timeline_array); - lv_obj_add_event_cb(state.view, free_timelines_event_cb, LV_EVENT_DELETE, timeline_array); - } + create_timeline_instances(&state); lv_ll_clear(&state.parent_ll); XML_ParserFree(parser); @@ -525,7 +499,7 @@ lv_result_t lv_xml_register_timeline(lv_xml_component_scope_t * scope, const cha at = lv_ll_ins_head(&scope->timeline_ll); at->name = lv_strdup(name); - lv_ll_init(&at->anims_ll, sizeof(lv_anim_t)); + lv_ll_init(&at->anims_ll, sizeof(lv_xml_anim_timeline_child_t)); return LV_RESULT_OK; } @@ -893,6 +867,124 @@ static void view_end_element_handler(void * user_data, const char * name) } } +static lv_anim_timeline_t * get_timeline_by_name(lv_obj_t * obj, const char * timeline_name) +{ + /*Get all the timelines of the target*/ + lv_anim_timeline_t ** timeline_array = NULL; + lv_obj_send_event(obj, lv_event_xml_store_timeline, &timeline_array); + if(timeline_array == NULL) { + LV_LOG_WARN("No time lines are stored in target"); + return NULL; + } + + /*Find the timeline with the requested timeline name*/ + uint32_t i; + for(i = 0; timeline_array[i]; i++) { + const char * name = lv_anim_timeline_get_user_data(timeline_array[i]); + if(lv_streq(name, timeline_name)) return timeline_array[i]; + } + + return NULL; +} + +static void create_timeline_instances(lv_xml_parser_state_t * state) +{ + /*The timeline descriptors ("blueprints") created when the components was registered + *are stored in the "scope". + *Based on the descriptors timeline and animation instances will be created for this this component*/ + lv_xml_component_scope_t * scope = &state->scope; + + if(lv_ll_is_empty(&scope->timeline_ll)) return; + + /*At this stage all children are created so any UI elements that + *the animations and timelines can reference are exist. */ + lv_xml_timeline_t * timeline_dsc; + + /*Create an array to store the created timeline pointers*/ + lv_anim_timeline_t ** timeline_array; + timeline_array = lv_malloc((lv_ll_get_len(&scope->timeline_ll) + 1) * sizeof(lv_anim_timeline_t *)); + LV_ASSERT_MALLOC(timeline_array); + if(timeline_array == NULL) { + LV_LOG_WARN("Couldn't allocate memory"); + return; + } + + /*Read the timeline descriptors of the component and create + *timeline instances based on them.*/ + uint32_t timeline_index = 0; + LV_LL_READ(&scope->timeline_ll, timeline_dsc) { + /*Save the name of the timeline. It will reference by this name in XML + * (e.g. )*/ + lv_anim_timeline_t * my_timeline = lv_anim_timeline_create(); + my_timeline->user_data = lv_strdup(timeline_dsc->name); + LV_ASSERT_MALLOC(my_timeline->user_data); + if(my_timeline->user_data == NULL) { + lv_anim_timeline_delete(my_timeline); + lv_free(timeline_array); + LV_LOG_WARN("Couldn't allocate memory"); + return; + } + /*Check all saved animation or incluce_timeline data of the component + *and add them to the timeline instance. */ + lv_xml_anim_timeline_child_t * timeline_child; + LV_LL_READ(&timeline_dsc->anims_ll, timeline_child) { + /*Simple add the animation descriptors to instance's timeline*/ + if(timeline_child->is_anim) { + lv_anim_t * a = &timeline_child->data.anim; + lv_obj_t * target = NULL; + if(lv_streq(a->var, "self")) target = state->view; + else target = lv_obj_find_by_name(state->view, a->var); + + if(target == NULL) { + LV_LOG_WARN("No target widget is found with `%s` name", (char *)a->var); + continue; + } + + int32_t delay = -a->act_time; + lv_anim_timeline_add(my_timeline, delay, a); + + /*Once the animation descriptor is duplicated and saved in the timeline + *replace the target name a pointer to the target. + *TODO add an event to every referenced widget to remove their anim from the + * timeline when they are deleted.*/ + lv_anim_t * new_a = &my_timeline->anim_dsc[my_timeline->anim_dsc_cnt - 1].anim; + new_a->var = target; + } + /*Or include (merge) the referenced timelines*/ + else { + lv_xml_anim_timeline_include_t * incl = &timeline_child->data.incl; + /*Get the target first*/ + lv_obj_t * target; + if(lv_streq(incl->target_name, "self")) target = state->view; + else target = lv_obj_find_by_name(state->view, incl->target_name); + + if(target == NULL) { + LV_LOG_WARN("No target widget is found with `%s` name", incl->target_name); + continue; + } + + lv_anim_timeline_t * include_timeline = get_timeline_by_name(target, incl->timeline_name); + if(include_timeline == NULL) { + LV_LOG_WARN("Timeline `%s` is not found in `%s` component", incl->timeline_name, incl->target_name); + continue; + } + + /*Copy all animations of include_timeline to this instance's timeline*/ + lv_anim_timeline_merge(my_timeline, include_timeline, incl->delay); + } + } + + timeline_array[timeline_index] = my_timeline; + timeline_index++; + } + + timeline_array[timeline_index] = NULL; /*Closing to avoid storing the length*/ + + lv_obj_add_event_cb(state->view, get_timeline_from_event_cb, lv_event_xml_store_timeline, timeline_array); + lv_obj_add_event_cb(state->view, free_timelines_event_cb, LV_EVENT_DELETE, timeline_array); +} + + static void get_timeline_from_event_cb(lv_event_t * e) { void ** out = lv_event_get_param(e); diff --git a/src/others/xml/lv_xml_component.c b/src/others/xml/lv_xml_component.c index b460f587d0..a84e6310fd 100644 --- a/src/others/xml/lv_xml_component.c +++ b/src/others/xml/lv_xml_component.c @@ -315,9 +315,15 @@ lv_result_t lv_xml_component_unregister(const char * name) lv_xml_timeline_t * timeline; LV_LL_READ(&scope->timeline_ll, timeline) { - lv_anim_t * a; - LV_LL_READ(&timeline->anims_ll, a) { - lv_free(a->var); /*It was the name of the target object*/ + lv_xml_anim_timeline_child_t * child; + LV_LL_READ(&timeline->anims_ll, child) { + if(child->is_anim) { + lv_free(child->data.anim.var); /*It was the name of the target object*/ + } + else { + lv_free((void *) child->data.incl.target_name); + lv_free((void *) child->data.incl.timeline_name); + } } lv_ll_clear(&timeline->anims_ll); lv_free((char *)timeline->name); @@ -579,11 +585,6 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char int32_t start = anim_value_to_int(prop_type, start_str); int32_t end = anim_value_to_int(prop_type, end_str); - lv_xml_timeline_t * at = state->context; - if(at == NULL) { - LV_LOG_WARN("There was no parent timeline for the animation"); - return; - } if(target_str[0] == '#') target_str = lv_xml_get_const(&state->scope, &target_str[1]); if(prop_str[0] == '#') prop_str = lv_xml_get_const(&state->scope, &prop_str[1]); @@ -593,6 +594,12 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char if(delay_str[0] == '#') delay_str = lv_xml_get_const(&state->scope, &delay_str[1]); if(early_apply_str[0] == '#') early_apply_str = lv_xml_get_const(&state->scope, &early_apply_str[1]); + lv_xml_timeline_t * at = state->context; + if(at == NULL) { + LV_LOG_WARN("There was no parent timeline for the animation"); + return; + } + if(!target_str || !prop_str || !start_str || !end_str || !duration_str || !delay_str || !early_apply_str) { LV_LOG_WARN("Couldn't resolve one or more constants. Skipping the animation."); return; @@ -600,7 +607,9 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char uint32_t selector_and_prop = ((prop & 0xff) << 24) | selector; - lv_anim_t * a = lv_ll_ins_tail(&at->anims_ll); + lv_xml_anim_timeline_child_t * child = lv_ll_ins_tail(&at->anims_ll); + child->is_anim = true; + lv_anim_t * a = &child->data.anim; lv_anim_init(a); lv_anim_set_var(a, lv_strdup(target_str)); @@ -612,6 +621,61 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char lv_anim_set_user_data(a, (void *)((uintptr_t)selector_and_prop)); } +static void process_include_timeline_element(lv_xml_parser_state_t * state, const char ** attrs) +{ + lv_xml_timeline_t * at = state->context; + if(at == NULL) { + LV_LOG_INFO("No parent timeline is set, skipping"); + return; + } + + const char * target_str = lv_xml_get_value_of(attrs, "target"); + const char * timeline_str = lv_xml_get_value_of(attrs, "timeline"); + const char * delay_str = lv_xml_get_value_of(attrs, "delay"); + + + if(target_str == NULL) { + LV_LOG_WARN("'target' is missing from timeline include"); + return; + } + + if(timeline_str == NULL) { + LV_LOG_WARN("'timeline' is missing from timeline include"); + return; + } + + if(delay_str == NULL) delay_str = "0"; + if(target_str[0] == '#') target_str = lv_xml_get_const(&state->scope, &target_str[1]); + if(timeline_str[0] == '#') timeline_str = lv_xml_get_const(&state->scope, &timeline_str[1]); + if(delay_str[0] == '#') delay_str = lv_xml_get_const(&state->scope, &delay_str[1]); + + if(!target_str || !timeline_str || !delay_str) { + LV_LOG_WARN("Couldn't resolve one or more constants. Skipping the timeline include."); + return; + } + + lv_xml_anim_timeline_child_t * child = lv_ll_ins_tail(&at->anims_ll); + LV_ASSERT_MALLOC(child); + if(child == NULL) { + LV_LOG_WARN("Couldn't allocate memory"); + return; + } + + child->is_anim = false; + child->data.incl.delay = lv_xml_atoi(delay_str); + child->data.incl.target_name = lv_strdup(target_str); + LV_ASSERT_MALLOC(child->data.incl.target_name); + child->data.incl.timeline_name = lv_strdup(timeline_str); + LV_ASSERT_MALLOC(child->data.incl.timeline_name); + + if(child->data.incl.target_name == NULL || child->data.incl.timeline_name == NULL) { + LV_LOG_WARN("Couldn't allocate memory"); + lv_free((void *)child->data.incl.target_name); + lv_free((void *)child->data.incl.timeline_name); + lv_ll_remove(&at->anims_ll, child); + } +} + static void process_grad_element(lv_xml_parser_state_t * state, const char * tag_name, const char ** attrs) { lv_xml_grad_t * grad = lv_ll_ins_tail(&state->scope.gradient_ll); @@ -846,6 +910,9 @@ static void start_metadata_handler(void * user_data, const char * name, const ch case LV_XML_PARSER_SECTION_ANIMATION: process_animation_element(state, attrs); break; + case LV_XML_PARSER_SECTION_INCLUDE_TIMELINE: + process_include_timeline_element(state, attrs); + break; default: break; } diff --git a/src/others/xml/lv_xml_parser.c b/src/others/xml/lv_xml_parser.c index 127859cf7a..871600d195 100644 --- a/src/others/xml/lv_xml_parser.c +++ b/src/others/xml/lv_xml_parser.c @@ -85,6 +85,10 @@ void lv_xml_parser_start_section(lv_xml_parser_state_t * state, const char * nam state->section = LV_XML_PARSER_SECTION_ANIMATION; return; } + else if(lv_streq(name, "include_timeline")) { + state->section = LV_XML_PARSER_SECTION_INCLUDE_TIMELINE; + return; + } else if(lv_streq(name, "timeline")) { state->section = LV_XML_PARSER_SECTION_TIMELINE; return; diff --git a/src/others/xml/lv_xml_parser.h b/src/others/xml/lv_xml_parser.h index 6f9e891610..520f32ad60 100644 --- a/src/others/xml/lv_xml_parser.h +++ b/src/others/xml/lv_xml_parser.h @@ -39,6 +39,7 @@ typedef enum { LV_XML_PARSER_SECTION_IMAGES, LV_XML_PARSER_SECTION_SUBJECTS, LV_XML_PARSER_SECTION_ANIMATION, + LV_XML_PARSER_SECTION_INCLUDE_TIMELINE, LV_XML_PARSER_SECTION_TIMELINE, LV_XML_PARSER_SECTION_VIEW } lv_xml_parser_section_t; diff --git a/src/others/xml/lv_xml_private.h b/src/others/xml/lv_xml_private.h index 6dcba1fc8b..1d1f2376b8 100644 --- a/src/others/xml/lv_xml_private.h +++ b/src/others/xml/lv_xml_private.h @@ -46,6 +46,22 @@ typedef struct { lv_event_cb_t cb; } lv_xml_event_cb_t; +/** + * Store the data of + */ +typedef struct { + const char * target_name; /**< Include the timeline of this widget*/ + const char * timeline_name; /**< Include this timeline */ + int32_t delay; +} lv_xml_anim_timeline_include_t; + +typedef struct { + bool is_anim; + union { + lv_anim_t anim; + lv_xml_anim_timeline_include_t incl; + } data; +} lv_xml_anim_timeline_child_t; /********************** * GLOBAL PROTOTYPES