mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-22 23:37:43 +08:00
feat(xml): support <include_timeline> (#8902)
This commit is contained in:
committed by
GitHub
parent
e070798c12
commit
3e5ab289f9
@@ -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:
|
||||
|
||||
@@ -5,7 +5,7 @@ Animations
|
||||
==========
|
||||
|
||||
Overview
|
||||
--------
|
||||
********
|
||||
|
||||
XML animations are built on top of :ref:`Timeline animations <animations_timeline>`.
|
||||
|
||||
@@ -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 ``<screen>``\ s and ``<component>``\ s.
|
||||
For ``<widget>``\ s, timelines are supported only in LVGL's UI Editor,
|
||||
@@ -32,8 +32,9 @@ Example:
|
||||
<!-- Show the component and its children -->
|
||||
<timeline name="load">
|
||||
<animation prop="translate_x" target="self" start="-30" end="0" duration="500"/>
|
||||
<animation prop="opa" target="icon" start="0" end="255" duration="500"/>
|
||||
<animation prop="opa" target="text" start="0" end="255" duration="500" delay="200"/>
|
||||
|
||||
<include_timeline target="icon" timeline="show_up" delay="300"/>
|
||||
</timeline>
|
||||
|
||||
<!-- Shake horizontally -->
|
||||
@@ -52,7 +53,7 @@ Example:
|
||||
</view>
|
||||
</component>
|
||||
|
||||
In summary: inside ``<animations>``, you can define ``<timeline>``\s, each with a unique name
|
||||
In summary: inside ``<animations>``, you can define ``<timeline>``\ s, each with a unique name
|
||||
that you can reference later.
|
||||
|
||||
Inside a ``<timeline>``, you add ``<animation>``\ s to describe each step.
|
||||
@@ -64,11 +65,22 @@ Supported properties of ``<animation>`` 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``.
|
||||
|
||||
``<include_timeline>`` also can be used in ``<timeline>``\ 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 ``<view>``).
|
||||
- ``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 ``<play_timeline_event>``
|
||||
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 ``<animations>`` section are parsed,
|
||||
and the animation data is stored as a blueprint.
|
||||
and the ``<timeline>``'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 ``<include_timeline>``\ 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 ``<play_timeline_event>`` 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 ``<view>``.)
|
||||
|
||||
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 ``<list_item>``\s)
|
||||
Since each instance has its own timeline, you can have multiple components (e.g. 10 ``<list_item>``\ s)
|
||||
and play their ``load`` timelines independently with different delays.
|
||||
|
||||
When a ``<play_timeline_event>`` 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 ``<view>``.
|
||||
|
||||
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).
|
||||
|
||||
|
||||
|
||||
|
||||
+13
-48
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
+121
-29
@@ -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. <play_animation_event target="comp_name" timeline="timeline_name">)*/
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -46,6 +46,22 @@ typedef struct {
|
||||
lv_event_cb_t cb;
|
||||
} lv_xml_event_cb_t;
|
||||
|
||||
/**
|
||||
* Store the data of <include_timeline>
|
||||
*/
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user