feat(xml): add animation support (#8599)

This commit is contained in:
Gabor Kiss-Vamosi
2025-08-15 22:13:11 +02:00
committed by GitHub
parent 807d847123
commit a005318bb2
47 changed files with 1260 additions and 260 deletions
@@ -19,11 +19,11 @@
#endif
#if !LV_VERSION_CHECK(9, 3, 0)
#error "At least LVGL v9.3 is required to use the stride attribute of the fonts"
#error "At least LVGL v9.3 is required to use the stride attribute of the fonts"
#endif
#ifndef LV_FONT_BENCHMARK_MONTSERRAT_12_ALIGNED
#define LV_FONT_BENCHMARK_MONTSERRAT_12_ALIGNED 1
#define LV_FONT_BENCHMARK_MONTSERRAT_12_ALIGNED 1
#endif
#if LV_FONT_BENCHMARK_MONTSERRAT_12_ALIGNED
@@ -3192,8 +3192,7 @@ static const uint16_t unicode_list_1[] = {
};
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
static const lv_font_fmt_txt_cmap_t cmaps[] = {
{
.range_start = 32, .range_length = 95, .glyph_id_start = 1,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
@@ -3210,8 +3209,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
/*Map glyph_ids to kern left classes*/
static const uint8_t kern_left_class_mapping[] =
{
static const uint8_t kern_left_class_mapping[] = {
0, 0, 1, 2, 0, 3, 4, 5,
2, 6, 7, 8, 9, 10, 9, 10,
11, 12, 0, 13, 14, 15, 16, 17,
@@ -3234,8 +3232,7 @@ static const uint8_t kern_left_class_mapping[] =
};
/*Map glyph_ids to kern right classes*/
static const uint8_t kern_right_class_mapping[] =
{
static const uint8_t kern_right_class_mapping[] = {
0, 0, 1, 2, 0, 3, 4, 5,
2, 6, 7, 8, 9, 10, 9, 10,
11, 12, 13, 14, 15, 16, 17, 12,
@@ -3258,8 +3255,7 @@ static const uint8_t kern_right_class_mapping[] =
};
/*Kern values between classes*/
static const int8_t kern_class_values[] =
{
static const int8_t kern_class_values[] = {
0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 2, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0,
@@ -3638,8 +3634,7 @@ static const int8_t kern_class_values[] =
/*Collect the kern class' data in one place*/
static const lv_font_fmt_txt_kern_classes_t kern_classes =
{
static const lv_font_fmt_txt_kern_classes_t kern_classes = {
.class_pair_values = kern_class_values,
.left_class_mapping = kern_left_class_mapping,
.right_class_mapping = kern_right_class_mapping,
@@ -3652,8 +3647,8 @@ static const lv_font_fmt_txt_kern_classes_t kern_classes =
*--------------------*/
#if LVGL_VERSION_MAJOR == 8
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
#endif
#if LVGL_VERSION_MAJOR >= 8
@@ -3699,7 +3694,9 @@ lv_font_t lv_font_benchmark_montserrat_12_aligned = {
.underline_position = -1,
.underline_thickness = 1,
#endif
#if LV_VERSION_CHECK(9, 3, 0)
.static_bitmap = 1,
#endif
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
.fallback = NULL,
@@ -362,6 +362,13 @@ Call :cpp:expr:`lv_anim_timeline_delete(timeline)` function to delete the Animat
**Note**: If you need to delete a Widget during Animation, be sure to delete the
Animation 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:
@@ -1,7 +1,111 @@
.. _xml_animations:
.. _xml_animations:
==========
Animations
==========
TODO
Overview
--------
XML animations are built on top of :ref:`Timeline animations <animations_timeline>`.
Timelines are composed of simple animations. For example: *"change the ``bg_opa``
of ``my_button_2`` from 0 to 255 in 500 ms."*
Each component can define its own timeline animations, which can then be played by the
component itself or by any parent components.
Defining Timelines
------------------
Timelines can be defined inside ``<screen>``\ s and ``<component>``\ s.
For ``<widget>``\ s, timelines are supported only in LVGL's UI Editor,
where C code can also be exported from them.
Example:
.. code-block:: xml
<animations>
<!-- 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"/>
</timeline>
<!-- Shake horizontally -->
<timeline name="shake">
<animation prop="translate_x" target="self" start="0" end="-30" duration="150"/>
<animation prop="translate_x" target="self" start="-30" end="30" duration="300" delay="150"/>
<animation prop="translate_x" target="self" start="30" end="0" duration="150" delay="450"/>
</timeline>
</animations>
<view>
<lv_button width="200">
<my_icon name="icon" src="image1"/>
<lv_label name="text" text="Click me"/>
</lv_button>
</view>
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.
Supported properties of ``<animation>`` are:
- ``prop``: Style property to animate. All integer style properties are supported (colors are not).
- ``selector``: Style selector, e.g. ``knob|pressed``. Default: ``main|default``.
- ``target``: Name of the UI element to animate. ``self`` refers to the root element of the component (the ``<view>``).
- ``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.
Playing Timelines
-----------------
Timelines can be triggered by events (e.g. click) using ``<play_timeline_event>``
as a child of any widget.
Example:
.. code-block:: xml
<view>
<lv_label name="title" text="Hello world!"/>
<custom_button name="button" y="20">
<play_timeline_event trigger="clicked" target="button" timeline="bounce"/>
<lv_label text="Click me"/>
</custom_button>
</view>
You set a ``target`` UI element and select one of its ``timeline``s to play.
If ``target="self"``, the timeline is looked up in the current component/widget/screen
(i.e. in the current XML file).
You can also set ``delay`` and ``reverse="true"`` when playing a timeline.
Under the Hood
--------------
Understanding how timelines work internally helps use 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.
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.
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 trigger event happens, LVGL finds the target widget by the saved name,
retrieves the specified timeline, and starts it.
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.
+1
View File
@@ -261,6 +261,7 @@ typedef struct _lv_global_t {
#if LV_USE_XML
const char * xml_path_prefix;
uint32_t lv_event_xml_store_timeline;
#endif
#if LV_USE_DRAW_EVE
+39
View File
@@ -23,6 +23,7 @@
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_types.h"
#include "../misc/lv_anim_timeline.h"
#include "../tick/lv_tick.h"
#include "../stdlib/lv_string.h"
#include "lv_obj_draw_private.h"
@@ -49,6 +50,12 @@ typedef struct {
} target;
} screen_load_anim_dsc_t;
typedef struct {
lv_anim_timeline_t * at;
uint32_t delay;
bool reverse;
} timeline_play_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
@@ -65,6 +72,7 @@ static void lv_obj_children_remove_state(lv_obj_t * obj, lv_state_t state);
static void null_on_delete_cb(lv_event_t * e);
static void screen_load_on_trigger_event_cb(lv_event_t * e);
static void screen_create_on_trigger_event_cb(lv_event_t * e);
static void play_timeline_on_trigger_event_cb(lv_event_t * e);
static void free_user_data_on_delete_event_cb(lv_event_t * e);
static void delete_on_screen_unloaded_event_cb(lv_event_t * e);
@@ -518,6 +526,20 @@ void lv_obj_add_screen_create_event(lv_obj_t * obj, lv_event_code_t trigger, lv_
lv_obj_add_event_cb(obj, free_user_data_on_delete_event_cb, LV_EVENT_DELETE, dsc);
}
void lv_obj_add_play_timeline_event(lv_obj_t * obj, lv_event_code_t trigger, lv_anim_timeline_t * at, uint32_t delay,
bool reverse)
{
timeline_play_dsc_t * dsc = lv_malloc(sizeof(timeline_play_dsc_t));
LV_ASSERT_MALLOC(dsc);
lv_memzero(dsc, sizeof(timeline_play_dsc_t));
dsc->at = at;
dsc->delay = delay;
dsc->reverse = reverse;
lv_obj_add_event_cb(obj, play_timeline_on_trigger_event_cb, trigger, dsc);
lv_obj_add_event_cb(obj, free_user_data_on_delete_event_cb, LV_EVENT_DELETE, dsc);
}
void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
{
obj->user_data = user_data;
@@ -1117,6 +1139,23 @@ static void screen_create_on_trigger_event_cb(lv_event_t * e)
lv_obj_add_event_cb(screen, delete_on_screen_unloaded_event_cb, LV_EVENT_SCREEN_UNLOADED, NULL);
}
static void play_timeline_on_trigger_event_cb(lv_event_t * e)
{
timeline_play_dsc_t * dsc = lv_event_get_user_data(e);
LV_ASSERT_NULL(dsc);
if(dsc->reverse) {
lv_anim_timeline_set_progress(dsc->at, LV_ANIM_TIMELINE_PROGRESS_MAX);
lv_anim_timeline_set_reverse(dsc->at, true);
}
else {
lv_anim_timeline_set_progress(dsc->at, 0);
lv_anim_timeline_set_reverse(dsc->at, false);
}
lv_anim_timeline_set_delay(dsc->at, dsc->delay);
lv_anim_timeline_start(dsc->at);
}
static void free_user_data_on_delete_event_cb(lv_event_t * e)
{
lv_free(lv_event_get_user_data(e));
+12
View File
@@ -368,6 +368,18 @@ void lv_obj_add_screen_load_event(lv_obj_t * obj, lv_event_code_t trigger, lv_ob
void lv_obj_add_screen_create_event(lv_obj_t * obj, lv_event_code_t trigger, lv_screen_create_cb_t screen_create_cb,
lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay);
/**
* Play a timeline animation on a trigger
* @param obj pointer to widget which should trigger playing the animation
* @param trigger an event code, e.g. `LV_EVENT_CLICKED`
* @param at pointer to an animation timeline
* @param delay wait time before starting the animation
* @param reverse true: play in reverse
*/
void lv_obj_add_play_timeline_event(lv_obj_t * obj, lv_event_code_t trigger, lv_anim_timeline_t * at, uint32_t delay,
bool reverse);
#if LV_USE_OBJ_ID
/**
* Set an id for an object.
+97 -26
View File
@@ -8,9 +8,12 @@
*********************/
#include "lv_anim_private.h"
#include "lv_assert.h"
#include "lv_anim_timeline.h"
#include "lv_anim_timeline_private.h"
#include "../stdlib/lv_mem.h"
#include "../stdlib/lv_string.h"
#if LV_USE_OBJ_NAME
#include "../core/lv_obj_tree.h"
#endif
/*********************
* DEFINES
@@ -19,23 +22,6 @@
/**********************
* TYPEDEFS
**********************/
/*Data of anim_timeline_dsc*/
typedef struct {
lv_anim_t anim;
uint32_t start_time;
uint8_t is_started : 1;
uint8_t is_completed : 1;
} lv_anim_timeline_dsc_t;
/*Data of anim_timeline*/
struct _lv_anim_timeline_t {
lv_anim_timeline_dsc_t * anim_dsc; /**< Dynamically allocated anim dsc array*/
uint32_t anim_dsc_cnt; /**< The length of anim dsc array*/
uint32_t act_time; /**< Current time of the animation*/
bool reverse; /**< Reverse playback*/
uint32_t repeat_count; /**< Repeat count*/
uint32_t repeat_delay; /**< Wait before repeat*/
};
/**********************
* STATIC PROTOTYPES
@@ -43,6 +29,7 @@ struct _lv_anim_timeline_t {
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 VARIABLES
@@ -92,7 +79,7 @@ uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at)
uint32_t playtime = lv_anim_timeline_get_playtime(at);
uint32_t repeat = at->repeat_count;
uint32_t delay = at->repeat_delay;
uint32_t repeat_delay = at->repeat_delay;
uint32_t start = at->act_time;
uint32_t end = at->reverse ? 0 : playtime;
uint32_t duration = end > start ? end - start : start - end;
@@ -104,15 +91,21 @@ uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at)
}
}
/*Apply the delay only if playing from any ends*/
uint32_t delay = 0;
if(!at->reverse && at->act_time == 0) delay = at->delay;
else if(at->reverse && at->act_time == playtime) delay = at->delay;
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, at);
lv_anim_set_exec_cb(&a, anim_timeline_exec_cb);
lv_anim_set_values(&a, start, end);
lv_anim_set_duration(&a, duration);
lv_anim_set_delay(&a, delay);
lv_anim_set_path_cb(&a, anim_timeline_path_cb);
lv_anim_set_repeat_count(&a, repeat);
lv_anim_set_repeat_delay(&a, delay);
lv_anim_set_repeat_delay(&a, repeat_delay);
lv_anim_start(&a);
return playtime;
}
@@ -130,6 +123,12 @@ void lv_anim_timeline_set_reverse(lv_anim_timeline_t * at, bool reverse)
at->reverse = reverse;
}
void lv_anim_timeline_set_delay(lv_anim_timeline_t * at, uint32_t delay)
{
LV_ASSERT_NULL(at);
at->delay = delay;
}
void lv_anim_timeline_set_repeat_count(lv_anim_timeline_t * at, uint32_t cnt)
{
LV_ASSERT_NULL(at);
@@ -151,6 +150,22 @@ void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress)
anim_timeline_set_act_time(at, act_time);
}
void lv_anim_timeline_set_user_data(lv_anim_timeline_t * at, void * user_data)
{
LV_ASSERT_NULL(at);
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);
@@ -175,6 +190,13 @@ bool lv_anim_timeline_get_reverse(lv_anim_timeline_t * at)
return at->reverse;
}
uint32_t lv_anim_timeline_get_delay(lv_anim_timeline_t * at)
{
LV_ASSERT_NULL(at);
return at->delay;
}
uint16_t lv_anim_timeline_get_progress(lv_anim_timeline_t * at)
{
LV_ASSERT_NULL(at);
@@ -194,6 +216,22 @@ uint32_t lv_anim_timeline_get_repeat_delay(lv_anim_timeline_t * at)
return at->repeat_delay;
}
void * lv_anim_timeline_get_user_data(lv_anim_timeline_t * at)
{
LV_ASSERT_NULL(at);
return at->user_data;
}
#if LV_USE_OBJ_NAME
lv_obj_t * lv_anim_timeline_get_base_obj(lv_anim_timeline_t * at)
{
LV_ASSERT_NULL(at);
return at->base_obj;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
@@ -221,8 +259,7 @@ static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_tim
}
value = a->start_value;
if(a->exec_cb) a->exec_cb(a->var, value);
if(a->custom_exec_cb) a->custom_exec_cb(a, value);
exec_anim(at, a, value);
if(anim_timeline_is_started) {
if(at->reverse) {
@@ -242,8 +279,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);
if(a->exec_cb) a->exec_cb(a->var, value);
if(a->custom_exec_cb) a->custom_exec_cb(a, value);
exec_anim(at, a, value);
if(anim_timeline_is_started) {
if(at->reverse) {
@@ -278,8 +314,7 @@ static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_tim
}
value = a->end_value;
if(a->exec_cb) a->exec_cb(a->var, value);
if(a->custom_exec_cb) a->custom_exec_cb(a, value);
exec_anim(at, a, value);
if(anim_timeline_is_started) {
if(at->reverse) {
@@ -305,3 +340,39 @@ static void anim_timeline_exec_cb(void * var, int32_t v)
lv_anim_timeline_t * at = var;
anim_timeline_set_act_time(at, v);
}
static void exec_anim(lv_anim_timeline_t * at, 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_get_child_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);
}
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;
}
}
+66 -7
View File
@@ -25,7 +25,14 @@ extern "C" {
* TYPEDEFS
**********************/
typedef struct _lv_anim_timeline_t lv_anim_timeline_t;
/*Data of anim_timeline_dsc*/
typedef struct _lv_anim_timeline_dsc_t {
lv_anim_t anim;
uint32_t start_time;
uint8_t is_started : 1;
uint8_t is_completed : 1;
} lv_anim_timeline_dsc_t;
/**********************
* GLOBAL PROTOTYPES
@@ -54,7 +61,7 @@ void lv_anim_timeline_add(lv_anim_timeline_t * at, uint32_t start_time, const lv
/**
* Start the animation timeline.
* @param at pointer to the animation timeline.
* @return total time spent in animation timeline.
* @return total time spent in animation timeline.
*/
uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at);
@@ -71,6 +78,14 @@ void lv_anim_timeline_pause(lv_anim_timeline_t * at);
*/
void lv_anim_timeline_set_reverse(lv_anim_timeline_t * at, bool reverse);
/**
* Set the time to wait before starting the the animation.
* Applies only when playing from the very start, or reverse from the very end.
* @param at pointer to an animation timeline
* @param delay the delay time in milliseconds
*/
void lv_anim_timeline_set_delay(lv_anim_timeline_t * at, uint32_t delay);
/**
* Make the animation timeline repeat itself.
* @param at pointer to the animation timeline.
@@ -92,24 +107,49 @@ void lv_anim_timeline_set_repeat_delay(lv_anim_timeline_t * at, uint32_t delay);
*/
void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress);
/**
* Set the user_data of a an animation timeline
* @param at pointer to the animation timeline.
* @param user_data pointer to any data. Only the pointer will be saved.
*/
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.
* @return total time spent in animation timeline.
* @param at pointer to the animation timeline.
* @return total time spent in animation timeline.
*/
uint32_t lv_anim_timeline_get_playtime(lv_anim_timeline_t * at);
/**
* Get whether the animation timeline is played in reverse.
* @param at pointer to the animation timeline.
* @return return true if it is reverse playback.
* @param at pointer to the animation timeline.
* @return return true if it is reverse playback.
*/
bool lv_anim_timeline_get_reverse(lv_anim_timeline_t * at);
/**
* Get the wait time when playing from the very start, or reverse from the very end.
* @param at pointer to an animation timeline
* @return the remaining time in milliseconds
*/
uint32_t lv_anim_timeline_get_delay(lv_anim_timeline_t * at);
/**
* Get the progress of the animation timeline.
* @param at pointer to the animation timeline.
* @return return value 0~65535 to map 0~100% animation progress.
* @return return value 0~65535 to map 0~100% animation progress.
*/
uint16_t lv_anim_timeline_get_progress(lv_anim_timeline_t * at);
@@ -125,6 +165,25 @@ uint32_t lv_anim_timeline_get_repeat_count(lv_anim_timeline_t * at);
*/
uint32_t lv_anim_timeline_get_repeat_delay(lv_anim_timeline_t * at);
/**
* Get the user_data of a an animation timeline
* @param at pointer to the animation timeline.
*/
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
*/
lv_obj_t * lv_anim_timeline_get_base_obj(lv_anim_timeline_t * at);
#endif
/**********************
* MACROS
**********************/
+71
View File
@@ -0,0 +1,71 @@
/**
* @file lv_anim_timeline_private.h
*
*/
#ifndef LV_ANIM_TIMELINE_PRIVATE_H
#define LV_ANIM_TIMELINE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_anim_timeline.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_anim_timeline_dsc_t;
/*Data of anim_timeline*/
struct _lv_anim_timeline_t {
/** Dynamically allocated anim dsc array*/
struct _lv_anim_timeline_dsc_t * anim_dsc;
/** The length of anim dsc array*/
uint32_t anim_dsc_cnt;
/** Current time of the animation*/
uint32_t act_time;
/** Reverse playback*/
bool reverse;
/** Delay before starting the animation from any ends*/
uint32_t delay;
/** Repeat count*/
uint32_t repeat_count;
/** 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;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_ANIM_TIMELINE_PRIVATE_H*/
+1 -1
View File
@@ -489,7 +489,7 @@ const char * lv_fs_get_last(const char * path)
size_t i;
for(i = len; i > 0; i--) {
if(path[i] == '/' || path[i] == '\\') break;
if(path[i] == '/' || path[i] == '\\' || path[i] == ':') break;
}
/*No '/' or '\' in the path so return with path itself*/
+2
View File
@@ -130,6 +130,8 @@ typedef struct _lv_theme_t lv_theme_t;
typedef struct _lv_anim_t lv_anim_t;
typedef struct _lv_anim_timeline_t lv_anim_timeline_t;
typedef struct _lv_font_t lv_font_t;
typedef struct _lv_font_class_t lv_font_class_t;
typedef struct _lv_font_info_t lv_font_info_t;
+109 -8
View File
@@ -43,11 +43,13 @@
#include "../../libs/expat/expat.h"
#include "../../draw/lv_draw_image.h"
#include "../../core/lv_global.h"
#include "../../misc/lv_anim_timeline_private.h"
/*********************
* DEFINES
*********************/
#define xml_path_prefix LV_GLOBAL_DEFAULT()->xml_path_prefix
#define lv_event_xml_store_timeline LV_GLOBAL_DEFAULT()->lv_event_xml_store_timeline
/**********************
* TYPEDEFS
@@ -58,6 +60,8 @@
**********************/
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 get_timeline_from_event_cb(lv_event_t * e);
static void free_timelines_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
@@ -75,6 +79,9 @@ void lv_xml_init(void)
{
xml_path_prefix = lv_strdup("");
/*It will be sued to store animation time lines in user_data*/
lv_event_xml_store_timeline = lv_event_register_id();
lv_xml_component_init();
lv_xml_register_font(NULL, "lv_font_default", lv_font_get_default());
@@ -129,6 +136,8 @@ void lv_xml_init(void)
lv_obj_xml_screen_load_event_apply);
lv_xml_widget_register("lv_obj-screen_create_event", lv_obj_xml_screen_create_event_create,
lv_obj_xml_screen_create_event_apply);
lv_xml_widget_register("lv_obj-play_timeline_event", lv_obj_xml_play_timeline_event_create,
lv_obj_xml_play_timeline_event_apply);
lv_xml_widget_register("lv_obj-bind_style", lv_obj_xml_bind_style_create, lv_obj_xml_bind_style_apply);
lv_xml_widget_register("lv_obj-bind_flag_if_eq", lv_obj_xml_bind_flag_create, lv_obj_xml_bind_flag_apply);
@@ -202,6 +211,35 @@ 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);
}
lv_ll_clear(&state.parent_ll);
XML_ParserFree(parser);
@@ -298,7 +336,7 @@ lv_result_t lv_xml_register_font(lv_xml_component_scope_t * scope, const char *
lv_xml_font_t * f;
LV_LL_READ(&scope->font_ll, f) {
if(lv_streq(f->name, name)) {
LV_LOG_INFO("Font %s is already registered. Don't register it again.", name);
LV_LOG_INFO("Font `%s` is already registered. Don't register it again.", name);
return LV_RESULT_OK;
}
}
@@ -342,11 +380,10 @@ lv_result_t lv_xml_register_subject(lv_xml_component_scope_t * scope, const char
return LV_RESULT_INVALID;
}
lv_xml_subject_t * s;
LV_LL_READ(&scope->subjects_ll, s) {
if(lv_streq(s->name, name)) {
LV_LOG_INFO("Subject %s is already registered. Don't register it again.", name);
LV_LOG_INFO("Subject `%s` is already registered. Don't register it again.", name);
return LV_RESULT_OK;
}
}
@@ -382,6 +419,54 @@ lv_subject_t * lv_xml_get_subject(lv_xml_component_scope_t * scope, const char *
return NULL;
}
lv_result_t lv_xml_register_timeline(lv_xml_component_scope_t * scope, const char * name)
{
if(scope == NULL) scope = lv_xml_component_get_scope("globals");
if(scope == NULL) {
LV_LOG_WARN("No component found to register subject `%s`", name);
return LV_RESULT_INVALID;
}
lv_xml_timeline_t * at;
LV_LL_READ(&scope->timeline_ll, at) {
if(lv_streq(at->name, name)) {
LV_LOG_INFO("Animation timeline `%s` is already registered. Don't register it again.", name);
return LV_RESULT_OK;
}
}
at = lv_ll_ins_head(&scope->timeline_ll);
at->name = lv_strdup(name);
lv_ll_init(&at->anims_ll, sizeof(lv_anim_t));
return LV_RESULT_OK;
}
void * lv_xml_get_timeline(lv_xml_component_scope_t * scope, const char * name)
{
lv_xml_timeline_t * at;
if(scope) {
LV_LL_READ(&scope->timeline_ll, at) {
if(lv_streq(at->name, name)) return at;
}
}
/*If not found in the component check the global space*/
if((scope == NULL || scope->name == NULL) || !lv_streq(scope->name, "globals")) {
scope = lv_xml_component_get_scope("globals");
if(scope) {
LV_LL_READ(&scope->timeline_ll, at) {
if(lv_streq(at->name, name)) return at;
}
}
}
LV_LOG_WARN("No timeline was found with name \"%s\".", name);
return NULL;
}
lv_result_t lv_xml_register_const(lv_xml_component_scope_t * scope, const char * name, const char * value)
{
if(scope == NULL) scope = lv_xml_component_get_scope("globals");
@@ -393,7 +478,7 @@ lv_result_t lv_xml_register_const(lv_xml_component_scope_t * scope, const char *
lv_xml_const_t * cnst;
LV_LL_READ(&scope->const_ll, cnst) {
if(lv_streq(cnst->name, name)) {
LV_LOG_INFO("Const %s is already registered. Don't register it again.", name);
LV_LOG_INFO("Const `%s` is already registered. Don't register it again.", name);
return LV_RESULT_OK;
}
}
@@ -446,7 +531,7 @@ lv_result_t lv_xml_register_image(lv_xml_component_scope_t * scope, const char *
lv_xml_image_t * img;
LV_LL_READ(&scope->image_ll, img) {
if(lv_streq(img->name, name)) {
LV_LOG_INFO("Image %s is already registered. Don't register it again.", name);
LV_LOG_INFO("Image `%s` is already registered. Don't register it again.", name);
return LV_RESULT_OK;
}
}
@@ -503,7 +588,7 @@ lv_result_t lv_xml_register_event_cb(lv_xml_component_scope_t * scope, const cha
lv_xml_event_cb_t * e;
LV_LL_READ(&scope->event_ll, e) {
if(lv_streq(e->name, name)) {
LV_LOG_INFO("Event_cb %s is already registered. Don't register it again.", name);
LV_LOG_INFO("Event_cb `%s` is already registered. Don't register it again.", name);
return LV_RESULT_OK;
}
}
@@ -636,8 +721,8 @@ static void view_start_element_handler(void * user_data, const char * name, cons
{
lv_xml_parser_state_t * state = (lv_xml_parser_state_t *)user_data;
state->tag_name = name;
bool is_view = false;
bool is_view = false;
if(lv_streq(name, "view")) {
const char * extends = lv_xml_get_value_of(attrs, "extends");
name = extends ? extends : "lv_obj";
@@ -647,7 +732,7 @@ static void view_start_element_handler(void * user_data, const char * name, cons
lv_obj_t ** current_parent_p = lv_ll_get_tail(&state->parent_ll);
if(current_parent_p == NULL) {
if(state->parent == NULL) {
LV_LOG_ERROR("There is no parent object available for %s. This also should never happen.", name);
LV_LOG_ERROR("There is no parent object available for %s. This should never happen.", name);
return;
}
else {
@@ -721,5 +806,21 @@ static void view_end_element_handler(void * user_data, const char * name)
}
}
static void get_timeline_from_event_cb(lv_event_t * e)
{
void ** out = lv_event_get_param(e);
*out = lv_event_get_user_data(e);
}
static void free_timelines_event_cb(lv_event_t * e)
{
lv_anim_timeline_t ** at_array = lv_event_get_user_data(e);
uint32_t i;
for(i = 0; at_array[i]; i++) {
lv_free(lv_anim_timeline_get_user_data(at_array[i]));
lv_anim_timeline_delete(at_array[i]);
}
lv_free(at_array);
}
#endif /* LV_USE_XML */
+4
View File
@@ -91,6 +91,10 @@ lv_result_t lv_xml_register_event_cb(lv_xml_component_scope_t * scope, const cha
lv_event_cb_t lv_xml_get_event_cb(lv_xml_component_scope_t * scope, const char * name);
lv_result_t lv_xml_register_timeline(lv_xml_component_scope_t * scope, const char * name);
void * lv_xml_get_timeline(lv_xml_component_scope_t * scope, const char * name);
/**********************
* MACROS
**********************/
+178
View File
@@ -326,6 +326,184 @@ lv_screen_load_anim_t lv_xml_screen_load_anim_text_to_enum_value(const char * tx
return LV_SCREEN_LOAD_ANIM_NONE;
}
lv_style_prop_t lv_xml_style_prop_to_enum(const char * txt)
{
if(lv_streq(txt, "width")) return LV_STYLE_WIDTH;
if(lv_streq(txt, "min_width")) return LV_STYLE_MIN_WIDTH;
if(lv_streq(txt, "max_width")) return LV_STYLE_MAX_WIDTH;
else if(lv_streq(txt, "height")) return LV_STYLE_HEIGHT;
else if(lv_streq(txt, "min_height")) return LV_STYLE_MIN_HEIGHT;
else if(lv_streq(txt, "max_height")) return LV_STYLE_MAX_HEIGHT;
else if(lv_streq(txt, "length")) return LV_STYLE_LENGTH;
else if(lv_streq(txt, "radius")) return LV_STYLE_RADIUS;
else if(lv_streq(txt, "pad_left")) return LV_STYLE_PAD_LEFT;
else if(lv_streq(txt, "pad_right")) return LV_STYLE_PAD_RIGHT;
else if(lv_streq(txt, "pad_top")) return LV_STYLE_PAD_TOP;
else if(lv_streq(txt, "pad_bottom")) return LV_STYLE_PAD_BOTTOM;
else if(lv_streq(txt, "pad_row")) return LV_STYLE_PAD_ROW;
else if(lv_streq(txt, "pad_column")) return LV_STYLE_PAD_COLUMN;
else if(lv_streq(txt, "pad_radial")) return LV_STYLE_PAD_RADIAL;
else if(lv_streq(txt, "margin_left")) return LV_STYLE_MARGIN_LEFT;
else if(lv_streq(txt, "margin_right")) return LV_STYLE_MARGIN_RIGHT;
else if(lv_streq(txt, "margin_top")) return LV_STYLE_MARGIN_TOP;
else if(lv_streq(txt, "margin_bottom")) return LV_STYLE_MARGIN_BOTTOM;
else if(lv_streq(txt, "base_dir")) return LV_STYLE_BASE_DIR;
else if(lv_streq(txt, "clip_corner")) return LV_STYLE_CLIP_CORNER;
else if(lv_streq(txt, "bg_opa")) return LV_STYLE_BG_OPA;
else if(lv_streq(txt, "bg_color")) return LV_STYLE_BG_COLOR;
else if(lv_streq(txt, "bg_grad_dir")) return LV_STYLE_BG_GRAD_DIR;
else if(lv_streq(txt, "bg_grad_color")) return LV_STYLE_BG_GRAD_COLOR;
else if(lv_streq(txt, "bg_main_stop")) return LV_STYLE_BG_MAIN_STOP;
else if(lv_streq(txt, "bg_grad_stop")) return LV_STYLE_BG_GRAD_STOP;
else if(lv_streq(txt, "bg_grad")) return LV_STYLE_BG_GRAD;
else if(lv_streq(txt, "bg_image_src")) return LV_STYLE_BG_IMAGE_SRC;
else if(lv_streq(txt, "bg_image_tiled")) return LV_STYLE_BG_IMAGE_TILED;
else if(lv_streq(txt, "bg_image_recolor")) return LV_STYLE_BG_IMAGE_RECOLOR;
else if(lv_streq(txt, "bg_image_recolor_opa")) return LV_STYLE_BG_IMAGE_RECOLOR_OPA;
else if(lv_streq(txt, "border_color")) return LV_STYLE_BORDER_COLOR;
else if(lv_streq(txt, "border_width")) return LV_STYLE_BORDER_WIDTH;
else if(lv_streq(txt, "border_opa")) return LV_STYLE_BORDER_OPA;
else if(lv_streq(txt, "border_side")) return LV_STYLE_BORDER_SIDE;
else if(lv_streq(txt, "border_post")) return LV_STYLE_BORDER_POST;
else if(lv_streq(txt, "outline_color")) return LV_STYLE_OUTLINE_COLOR;
else if(lv_streq(txt, "outline_width")) return LV_STYLE_OUTLINE_WIDTH;
else if(lv_streq(txt, "outline_opa")) return LV_STYLE_OUTLINE_OPA;
else if(lv_streq(txt, "outline_pad")) return LV_STYLE_OUTLINE_PAD;
else if(lv_streq(txt, "shadow_width")) return LV_STYLE_SHADOW_WIDTH;
else if(lv_streq(txt, "shadow_color")) return LV_STYLE_SHADOW_COLOR;
else if(lv_streq(txt, "shadow_offset_x")) return LV_STYLE_SHADOW_OFFSET_X;
else if(lv_streq(txt, "shadow_offset_y")) return LV_STYLE_SHADOW_OFFSET_Y;
else if(lv_streq(txt, "shadow_spread")) return LV_STYLE_SHADOW_SPREAD;
else if(lv_streq(txt, "shadow_opa")) return LV_STYLE_SHADOW_OPA;
else if(lv_streq(txt, "text_color")) return LV_STYLE_TEXT_COLOR;
else if(lv_streq(txt, "text_font")) return LV_STYLE_TEXT_FONT;
else if(lv_streq(txt, "text_opa")) return LV_STYLE_TEXT_OPA;
else if(lv_streq(txt, "text_align")) return LV_STYLE_TEXT_ALIGN;
else if(lv_streq(txt, "text_letter_space")) return LV_STYLE_TEXT_LETTER_SPACE;
else if(lv_streq(txt, "text_line_space")) return LV_STYLE_TEXT_LINE_SPACE;
else if(lv_streq(txt, "text_decor")) return LV_STYLE_TEXT_DECOR;
else if(lv_streq(txt, "image_opa")) return LV_STYLE_IMAGE_OPA;
else if(lv_streq(txt, "image_recolor")) return LV_STYLE_IMAGE_RECOLOR;
else if(lv_streq(txt, "image_recolor_opa")) return LV_STYLE_IMAGE_RECOLOR_OPA;
else if(lv_streq(txt, "line_color")) return LV_STYLE_LINE_COLOR;
else if(lv_streq(txt, "line_opa")) return LV_STYLE_LINE_OPA;
else if(lv_streq(txt, "line_width")) return LV_STYLE_LINE_WIDTH;
else if(lv_streq(txt, "line_dash_width")) return LV_STYLE_LINE_DASH_WIDTH;
else if(lv_streq(txt, "line_dash_gap")) return LV_STYLE_LINE_DASH_GAP;
else if(lv_streq(txt, "line_rounded")) return LV_STYLE_LINE_ROUNDED;
else if(lv_streq(txt, "arc_color")) return LV_STYLE_ARC_COLOR;
else if(lv_streq(txt, "arc_opa")) return LV_STYLE_ARC_OPA;
else if(lv_streq(txt, "arc_width")) return LV_STYLE_ARC_WIDTH;
else if(lv_streq(txt, "arc_rounded")) return LV_STYLE_ARC_ROUNDED;
else if(lv_streq(txt, "arc_image_src")) return LV_STYLE_ARC_IMAGE_SRC;
else if(lv_streq(txt, "opa")) return LV_STYLE_OPA;
else if(lv_streq(txt, "opa_layered")) return LV_STYLE_OPA_LAYERED;
else if(lv_streq(txt, "color_filter_opa")) return LV_STYLE_COLOR_FILTER_OPA;
else if(lv_streq(txt, "anim_duration")) return LV_STYLE_ANIM_DURATION;
else if(lv_streq(txt, "blend_mode")) return LV_STYLE_BLEND_MODE;
else if(lv_streq(txt, "transform_width")) return LV_STYLE_TRANSFORM_WIDTH;
else if(lv_streq(txt, "transform_height")) return LV_STYLE_TRANSFORM_HEIGHT;
else if(lv_streq(txt, "translate_x")) return LV_STYLE_TRANSLATE_X;
else if(lv_streq(txt, "translate_y")) return LV_STYLE_TRANSLATE_Y;
else if(lv_streq(txt, "translate_radial")) return LV_STYLE_TRANSLATE_RADIAL;
else if(lv_streq(txt, "transform_scale_x")) return LV_STYLE_TRANSFORM_SCALE_X;
else if(lv_streq(txt, "transform_scale_y")) return LV_STYLE_TRANSFORM_SCALE_Y;
else if(lv_streq(txt, "transform_rotation")) return LV_STYLE_TRANSFORM_ROTATION;
else if(lv_streq(txt, "transform_pivot_x")) return LV_STYLE_TRANSFORM_PIVOT_X;
else if(lv_streq(txt, "transform_pivot_y")) return LV_STYLE_TRANSFORM_PIVOT_Y;
else if(lv_streq(txt, "transform_skew_x")) return LV_STYLE_TRANSFORM_SKEW_X;
else if(lv_streq(txt, "transform_skew_y")) return LV_STYLE_TRANSFORM_SKEW_Y;
else if(lv_streq(txt, "bitmap_mask_src")) return LV_STYLE_BITMAP_MASK_SRC;
else if(lv_streq(txt, "rotary_sensitivity")) return LV_STYLE_ROTARY_SENSITIVITY;
else if(lv_streq(txt, "recolor")) return LV_STYLE_RECOLOR;
else if(lv_streq(txt, "recolor_opa")) return LV_STYLE_RECOLOR_OPA;
else if(lv_streq(txt, "layout")) return LV_STYLE_LAYOUT;
else if(lv_streq(txt, "flex_flow")) return LV_STYLE_FLEX_FLOW;
else if(lv_streq(txt, "flex_grow")) return LV_STYLE_FLEX_GROW;
else if(lv_streq(txt, "flex_main_place")) return LV_STYLE_FLEX_MAIN_PLACE;
else if(lv_streq(txt, "flex_cross_place")) return LV_STYLE_FLEX_CROSS_PLACE;
else if(lv_streq(txt, "flex_track_place")) return LV_STYLE_FLEX_TRACK_PLACE;
else if(lv_streq(txt, "grid_column_align")) return LV_STYLE_GRID_COLUMN_ALIGN;
else if(lv_streq(txt, "grid_row_align")) return LV_STYLE_GRID_ROW_ALIGN;
else if(lv_streq(txt, "grid_cell_column_pos")) return LV_STYLE_GRID_CELL_COLUMN_POS;
else if(lv_streq(txt, "grid_cell_column_span")) return LV_STYLE_GRID_CELL_COLUMN_SPAN;
else if(lv_streq(txt, "grid_cell_x_align")) return LV_STYLE_GRID_CELL_X_ALIGN;
else if(lv_streq(txt, "grid_cell_row_pos")) return LV_STYLE_GRID_CELL_ROW_POS;
else if(lv_streq(txt, "grid_cell_row_span")) return LV_STYLE_GRID_CELL_ROW_SPAN;
else if(lv_streq(txt, "grid_cell_y_align")) return LV_STYLE_GRID_CELL_Y_ALIGN;
return LV_STYLE_PROP_INV;
}
lv_state_t lv_xml_style_state_to_enum(const char * txt)
{
if(lv_streq("default", txt)) return LV_STATE_DEFAULT;
else if(lv_streq("pressed", txt)) return LV_STATE_PRESSED;
else if(lv_streq("checked", txt)) return LV_STATE_CHECKED;
else if(lv_streq("scrolled", txt)) return LV_STATE_SCROLLED;
else if(lv_streq("focused", txt)) return LV_STATE_FOCUSED;
else if(lv_streq("focus_key", txt)) return LV_STATE_FOCUS_KEY;
else if(lv_streq("edited", txt)) return LV_STATE_EDITED;
else if(lv_streq("hovered", txt)) return LV_STATE_HOVERED;
else if(lv_streq("disabled", txt)) return LV_STATE_DISABLED;
else if(lv_streq("user_1", txt)) return LV_STATE_USER_1;
else if(lv_streq("user_2", txt)) return LV_STATE_USER_2;
else if(lv_streq("user_3", txt)) return LV_STATE_USER_3;
else if(lv_streq("user_4", txt)) return LV_STATE_USER_4;
return 0; /*Return 0 in lack of a better option. */
}
lv_part_t lv_xml_style_part_to_enum(const char * txt)
{
if(lv_streq("main", txt)) return LV_PART_MAIN;
else if(lv_streq("scrollbar", txt)) return LV_PART_SCROLLBAR;
else if(lv_streq("indicator", txt)) return LV_PART_INDICATOR;
else if(lv_streq("knob", txt)) return LV_PART_KNOB;
else if(lv_streq("selected", txt)) return LV_PART_SELECTED;
else if(lv_streq("items", txt)) return LV_PART_ITEMS;
else if(lv_streq("cursor", txt)) return LV_PART_CURSOR;
return 0; /*Return 0 in lack of a better option. */
}
lv_style_selector_t lv_xml_style_selector_text_to_enum(const char * str)
{
if(str == NULL) return 0;
lv_style_selector_t selector = 0;
char buf[256];
lv_strncpy(buf, str, sizeof(buf));
char * bufp = buf;
const char * next = lv_xml_split_str(&bufp, '|');
while(next) {
/* Handle different states and parts */
selector |= lv_xml_style_state_to_enum(next);
selector |= lv_xml_style_part_to_enum(next);
/* Move to the next token */
next = lv_xml_split_str(&bufp, '|');
}
return selector;
}
/**********************
* STATIC FUNCTIONS
**********************/
+31
View File
@@ -146,6 +146,37 @@ lv_event_code_t lv_xml_trigger_text_to_enum_value(const char * txt);
*/
lv_screen_load_anim_t lv_xml_screen_load_anim_text_to_enum_value(const char * txt);
/**
* Convert a style property string to enum
* @param txt e.g. "bg_color"
* @return the related enum, e.g. `LV_STYLE_BG_COLOR` or
* `LV_STYLE_PROP_INV` if not found.
*/
lv_style_prop_t lv_xml_style_prop_to_enum(const char * txt);
/**
* Convert a style state to enum
* @param txt e.g. "pressed"
* @return the enum `LV_STATE_PRESSED`
*/
lv_state_t lv_xml_style_state_to_enum(const char * txt);
/**
* Convert a style part to enum
* @param txt e.g. "knob"
* @return the enum `LV_PART_KNOB`
*/
lv_part_t lv_xml_style_part_to_enum(const char * txt);
/**
* Convert ORed style parts and states to an ORed selector
* @param txt e.g. "knob|pressed"
* @return the enum `LV_PART_KNOB|LV_STATE_PRESSED`
*/
lv_style_selector_t lv_xml_style_selector_text_to_enum(const char * str);
/**********************
* MACROS
**********************/
+234 -3
View File
@@ -30,6 +30,10 @@
/**********************
* TYPEDEFS
**********************/
typedef enum {
STYLE_PROP_TYPE_INT,
STYLE_PROP_TYPE_UNKNOWN
} style_prop_anim_type_t;
/**********************
* STATIC PROTOTYPES
@@ -41,6 +45,9 @@ static void process_font_element(lv_xml_parser_state_t * state, const char * typ
static void process_image_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs);
static void process_prop_element(lv_xml_parser_state_t * state, const char ** attrs);
static char * extract_view_content(const char * xml_definition);
static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop);
static int32_t anim_value_to_int(lv_style_prop_t prop_type, const char * value_str);
static void int_anim_exec_cb(lv_anim_t * a, int32_t v);
/**********************
* STATIC VARIABLES
@@ -77,6 +84,7 @@ void lv_xml_component_scope_init(lv_xml_component_scope_t * scope)
lv_ll_init(&scope->event_ll, sizeof(lv_xml_event_cb_t));
lv_ll_init(&scope->image_ll, sizeof(lv_xml_image_t));
lv_ll_init(&scope->font_ll, sizeof(lv_xml_font_t));
lv_ll_init(&scope->timeline_ll, sizeof(lv_xml_timeline_t));
}
@@ -146,7 +154,7 @@ lv_result_t lv_xml_component_register_from_data(const char * name, const char *
XML_SetElementHandler(parser, start_metadata_handler, end_metadata_handler);
if(XML_Parse(parser, xml_def, lv_strlen(xml_def), XML_TRUE) == XML_STATUS_ERROR) {
LV_LOG_ERROR("XML parsing error: %s on line %lu",
LV_LOG_ERROR("XML parsing error; %s on line %lu",
XML_ErrorString(XML_GetErrorCode(parser)),
(unsigned long)XML_GetCurrentLineNumber(parser));
XML_ParserFree(parser);
@@ -305,6 +313,17 @@ lv_result_t lv_xml_component_unregister(const char * name)
}
lv_ll_clear(&scope->subjects_ll);
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_ll_clear(&timeline->anims_ll);
lv_free((char *)timeline->name);
}
lv_ll_clear(&scope->timeline_ll);
lv_free(scope);
return LV_RESULT_OK;
@@ -477,6 +496,122 @@ static void process_subject_element(lv_xml_parser_state_t * state, const char *
lv_xml_register_subject(&state->scope, name, subject);
}
static void process_timeline_element(lv_xml_parser_state_t * state, const char ** attrs)
{
const char * name = lv_xml_get_value_of(attrs, "name");
if(name == NULL) {
LV_LOG_WARN("'name' is missing from a timeline");
return;
}
/*If already registered skip all. Don't set state->context
*so animations won't be added later either*/
lv_xml_timeline_t * at;
LV_LL_READ(&state->scope.timeline_ll, at) {
if(lv_streq(at->name, name)) {
LV_LOG_INFO("Timeline %s is already registered. Don't register it again.", name);
return;
}
}
lv_xml_register_timeline(&state->scope, name);
/*Save the created timeline so that animations can be added to it later*/
state->context = lv_xml_get_timeline(&state->scope, name);
}
static void process_animation_element(lv_xml_parser_state_t * state, const char ** attrs)
{
if(state->context == NULL) {
LV_LOG_INFO("No parent timeline is set, skipping");
return;
}
const char * target_str = lv_xml_get_value_of(attrs, "target");
const char * prop_str = lv_xml_get_value_of(attrs, "prop");
const char * start_str = lv_xml_get_value_of(attrs, "start");
const char * end_str = lv_xml_get_value_of(attrs, "end");
const char * duration_str = lv_xml_get_value_of(attrs, "duration");
const char * delay_str = lv_xml_get_value_of(attrs, "delay");
const char * early_apply_str = lv_xml_get_value_of(attrs, "early_apply");
const char * selector_str = lv_xml_get_value_of(attrs, "selector");
if(target_str == NULL) {
LV_LOG_WARN("'target' is missing from a animation");
return;
}
if(prop_str == NULL) {
LV_LOG_WARN("'prop' is missing from a animation");
return;
}
lv_style_prop_t prop = lv_xml_style_prop_to_enum(prop_str);
if(prop == LV_STYLE_PROP_INV) {
LV_LOG_WARN("Unknown style property; '%s'", prop_str);
return;
}
style_prop_anim_type_t prop_type = style_prop_anim_get_type(prop);
if(prop_type == STYLE_PROP_TYPE_UNKNOWN) {
LV_LOG_WARN("Style property '%s' is not animateable", prop_str);
return;
}
if(start_str == NULL) {
LV_LOG_WARN("'start' is missing from a animation");
return;
}
if(end_str == NULL) {
LV_LOG_WARN("'end' is missing from a animation");
return;
}
if(duration_str == NULL) duration_str = "1000";
if(delay_str == NULL) delay_str = "0";
if(early_apply_str == NULL) early_apply_str = "false";
if(selector_str == NULL) selector_str = "";
lv_style_selector_t selector = lv_xml_style_selector_text_to_enum(selector_str);
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]);
if(start_str[0] == '#') start_str = lv_xml_get_const(&state->scope, &start_str[1]);
if(end_str[0] == '#') end_str = lv_xml_get_const(&state->scope, &end_str[1]);
if(duration_str[0] == '#') duration_str = lv_xml_get_const(&state->scope, &duration_str[1]);
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]);
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;
}
uint32_t selector_and_prop = ((prop & 0xff) << 24) | selector;
lv_anim_t * a = lv_ll_ins_tail(&at->anims_ll);
lv_anim_init(a);
lv_anim_set_var(a, lv_strdup(target_str));
lv_anim_set_values(a, start, end);
lv_anim_set_custom_exec_cb(a, int_anim_exec_cb);
lv_anim_set_duration(a, lv_xml_atoi(duration_str));
lv_anim_set_delay(a, lv_xml_atoi(delay_str));
lv_anim_set_early_apply(a, lv_xml_to_bool(early_apply_str));
lv_anim_set_user_data(a, (void *)((uintptr_t)selector_and_prop));
}
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);
@@ -605,7 +740,7 @@ static void process_grad_element(lv_xml_parser_state_t * state, const char * tag
dsc->dir = LV_GRAD_DIR_VER;
}
else {
LV_LOG_WARN("Unknown gradient type: %s", tag_name);
LV_LOG_WARN("Unknown gradient type; %s", tag_name);
}
}
@@ -705,7 +840,12 @@ static void start_metadata_handler(void * user_data, const char * name, const ch
if(old_section != state->section) return; /*Ignore the section opening, e.g. <subjects>*/
process_subject_element(state, name, attrs);
break;
case LV_XML_PARSER_SECTION_TIMELINE:
process_timeline_element(state, attrs);
break;
case LV_XML_PARSER_SECTION_ANIMATION:
process_animation_element(state, attrs);
break;
default:
break;
}
@@ -749,4 +889,95 @@ static char * extract_view_content(const char * xml_definition)
return view_content;
}
static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop)
{
switch(prop) {
case LV_STYLE_WIDTH:
case LV_STYLE_MIN_WIDTH:
case LV_STYLE_MAX_WIDTH:
case LV_STYLE_HEIGHT:
case LV_STYLE_MIN_HEIGHT:
case LV_STYLE_MAX_HEIGHT:
case LV_STYLE_LENGTH:
case LV_STYLE_RADIUS:
case LV_STYLE_PAD_LEFT:
case LV_STYLE_PAD_RIGHT:
case LV_STYLE_PAD_TOP:
case LV_STYLE_PAD_BOTTOM:
case LV_STYLE_PAD_ROW:
case LV_STYLE_PAD_COLUMN:
case LV_STYLE_PAD_RADIAL:
case LV_STYLE_MARGIN_LEFT:
case LV_STYLE_MARGIN_RIGHT:
case LV_STYLE_MARGIN_TOP:
case LV_STYLE_MARGIN_BOTTOM:
case LV_STYLE_BG_OPA:
case LV_STYLE_BG_MAIN_STOP:
case LV_STYLE_BG_GRAD_STOP:
case LV_STYLE_BG_IMAGE_RECOLOR_OPA:
case LV_STYLE_BORDER_WIDTH:
case LV_STYLE_BORDER_OPA:
case LV_STYLE_OUTLINE_WIDTH:
case LV_STYLE_OUTLINE_OPA:
case LV_STYLE_OUTLINE_PAD:
case LV_STYLE_SHADOW_WIDTH:
case LV_STYLE_SHADOW_OFFSET_X:
case LV_STYLE_SHADOW_OFFSET_Y:
case LV_STYLE_SHADOW_SPREAD:
case LV_STYLE_SHADOW_OPA:
case LV_STYLE_TEXT_OPA:
case LV_STYLE_TEXT_LETTER_SPACE:
case LV_STYLE_TEXT_LINE_SPACE:
case LV_STYLE_IMAGE_OPA:
case LV_STYLE_IMAGE_RECOLOR_OPA:
case LV_STYLE_LINE_OPA:
case LV_STYLE_LINE_WIDTH:
case LV_STYLE_LINE_DASH_WIDTH:
case LV_STYLE_LINE_DASH_GAP:
case LV_STYLE_ARC_OPA:
case LV_STYLE_ARC_WIDTH:
case LV_STYLE_OPA:
case LV_STYLE_OPA_LAYERED:
case LV_STYLE_COLOR_FILTER_OPA:
case LV_STYLE_TRANSFORM_WIDTH:
case LV_STYLE_TRANSFORM_HEIGHT:
case LV_STYLE_TRANSLATE_X:
case LV_STYLE_TRANSLATE_Y:
case LV_STYLE_TRANSLATE_RADIAL:
case LV_STYLE_TRANSFORM_SCALE_X:
case LV_STYLE_TRANSFORM_SCALE_Y:
case LV_STYLE_TRANSFORM_ROTATION:
case LV_STYLE_TRANSFORM_PIVOT_X:
case LV_STYLE_TRANSFORM_PIVOT_Y:
case LV_STYLE_RECOLOR_OPA:
return STYLE_PROP_TYPE_INT;
default:
return STYLE_PROP_TYPE_UNKNOWN;
}
}
static int32_t anim_value_to_int(lv_style_prop_t prop_type, const char * value_str)
{
if(prop_type == STYLE_PROP_TYPE_INT) {
return lv_xml_atoi(value_str);
}
return 0;
}
static void int_anim_exec_cb(lv_anim_t * a, int32_t v)
{
uint32_t data = (lv_uintptr_t)lv_anim_get_user_data(a);
lv_style_prop_t prop = data >> 24;
lv_style_selector_t selector = data & 0x00ffffff;
lv_style_value_t style_value;
style_value.num = v;
lv_obj_set_local_style_prop(a->var, prop, style_value, selector);
}
#endif /* LV_USE_XML */
@@ -34,6 +34,7 @@ struct _lv_xml_component_scope_t {
lv_ll_t param_ll;
lv_ll_t gradient_ll;
lv_ll_t subjects_ll;
lv_ll_t timeline_ll;
lv_ll_t font_ll;
lv_ll_t image_ll;
lv_ll_t event_ll;
@@ -54,6 +55,11 @@ typedef struct {
lv_subject_t * subject;
} lv_xml_subject_t;
typedef struct {
const char * name;
lv_ll_t anims_ll;
} lv_xml_timeline_t;
typedef struct {
const char * name;
const char * def;
+8
View File
@@ -81,6 +81,14 @@ void lv_xml_parser_start_section(lv_xml_parser_state_t * state, const char * nam
state->section = LV_XML_PARSER_SECTION_SUBJECTS;
return;
}
else if(lv_streq(name, "animation")) {
state->section = LV_XML_PARSER_SECTION_ANIMATION;
return;
}
else if(lv_streq(name, "timeline")) {
state->section = LV_XML_PARSER_SECTION_TIMELINE;
return;
}
else if(lv_streq(name, "view")) {
state->section = LV_XML_PARSER_SECTION_VIEW;
return;
+3
View File
@@ -38,6 +38,8 @@ typedef enum {
LV_XML_PARSER_SECTION_FONTS,
LV_XML_PARSER_SECTION_IMAGES,
LV_XML_PARSER_SECTION_SUBJECTS,
LV_XML_PARSER_SECTION_ANIMATION,
LV_XML_PARSER_SECTION_TIMELINE,
LV_XML_PARSER_SECTION_VIEW
} lv_xml_parser_section_t;
@@ -48,6 +50,7 @@ struct _lv_xml_parser_state_t {
lv_obj_t * parent;
lv_obj_t * item;
lv_obj_t * view; /*Pointer to the created view during component creation*/
void * context; /*Custom data that can be stored during parsing*/
const char ** parent_attrs;
lv_xml_component_scope_t * parent_scope;
lv_xml_parser_section_t section;
+2 -160
View File
@@ -30,7 +30,6 @@
/**********************
* STATIC PROTOTYPES
**********************/
static lv_style_prop_t style_prop_text_to_enum(const char * txt);
/**********************
* STATIC VARIABLES
@@ -49,38 +48,6 @@ static lv_style_prop_t style_prop_text_to_enum(const char * txt);
* GLOBAL FUNCTIONS
**********************/
lv_state_t lv_xml_style_state_to_enum(const char * txt)
{
if(lv_streq("default", txt)) return LV_STATE_DEFAULT;
else if(lv_streq("pressed", txt)) return LV_STATE_PRESSED;
else if(lv_streq("checked", txt)) return LV_STATE_CHECKED;
else if(lv_streq("scrolled", txt)) return LV_STATE_SCROLLED;
else if(lv_streq("focused", txt)) return LV_STATE_FOCUSED;
else if(lv_streq("focus_key", txt)) return LV_STATE_FOCUS_KEY;
else if(lv_streq("edited", txt)) return LV_STATE_EDITED;
else if(lv_streq("hovered", txt)) return LV_STATE_HOVERED;
else if(lv_streq("disabled", txt)) return LV_STATE_DISABLED;
else if(lv_streq("user_1", txt)) return LV_STATE_USER_1;
else if(lv_streq("user_2", txt)) return LV_STATE_USER_2;
else if(lv_streq("user_3", txt)) return LV_STATE_USER_3;
else if(lv_streq("user_4", txt)) return LV_STATE_USER_4;
return 0; /*Return 0 in lack of a better option. */
}
lv_part_t lv_xml_style_part_to_enum(const char * txt)
{
if(lv_streq("main", txt)) return LV_PART_MAIN;
else if(lv_streq("scrollbar", txt)) return LV_PART_SCROLLBAR;
else if(lv_streq("indicator", txt)) return LV_PART_INDICATOR;
else if(lv_streq("knob", txt)) return LV_PART_KNOB;
else if(lv_streq("selected", txt)) return LV_PART_SELECTED;
else if(lv_streq("items", txt)) return LV_PART_ITEMS;
else if(lv_streq("cursor", txt)) return LV_PART_CURSOR;
return 0; /*Return 0 in lack of a better option. */
}
lv_result_t lv_xml_style_register(lv_xml_component_scope_t * scope, const char ** attrs)
{
const char * style_name = lv_xml_get_value_of(attrs, "name");
@@ -152,7 +119,7 @@ lv_result_t lv_xml_style_register(lv_xml_component_scope_t * scope, const char *
}
if(lv_streq(value, "remove")) {
lv_style_prop_t prop = style_prop_text_to_enum(name);
lv_style_prop_t prop = lv_xml_style_prop_to_enum(name);
if(prop != LV_STYLE_PROP_INV) lv_style_remove_prop(style, prop);
else if(lv_streq(name, "pad_all")) {
lv_style_remove_prop(style, LV_STYLE_PAD_TOP);
@@ -291,6 +258,7 @@ lv_result_t lv_xml_style_register(lv_xml_component_scope_t * scope, const char *
else SET_STYLE_IF(transform_pivot_x, lv_xml_atoi(value));
else SET_STYLE_IF(transform_pivot_y, lv_xml_atoi(value));
else SET_STYLE_IF(transform_skew_x, lv_xml_atoi(value));
else SET_STYLE_IF(transform_skew_y, lv_xml_atoi(value));
else SET_STYLE_IF(bitmap_mask_src, lv_xml_get_image(scope, value));
else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value));
else SET_STYLE_IF(recolor, lv_xml_to_color(value));
@@ -393,134 +361,8 @@ lv_grad_dsc_t * lv_xml_component_get_grad(lv_xml_component_scope_t * scope, cons
return NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_style_prop_t style_prop_text_to_enum(const char * txt)
{
if(lv_streq(txt, "width")) return LV_STYLE_WIDTH;
if(lv_streq(txt, "min_width")) return LV_STYLE_MIN_WIDTH;
if(lv_streq(txt, "max_width")) return LV_STYLE_MAX_WIDTH;
else if(lv_streq(txt, "height")) return LV_STYLE_HEIGHT;
else if(lv_streq(txt, "min_height")) return LV_STYLE_MIN_HEIGHT;
else if(lv_streq(txt, "max_height")) return LV_STYLE_MAX_HEIGHT;
else if(lv_streq(txt, "length")) return LV_STYLE_LENGTH;
else if(lv_streq(txt, "radius")) return LV_STYLE_RADIUS;
else if(lv_streq(txt, "pad_left")) return LV_STYLE_PAD_LEFT;
else if(lv_streq(txt, "pad_right")) return LV_STYLE_PAD_RIGHT;
else if(lv_streq(txt, "pad_top")) return LV_STYLE_PAD_TOP;
else if(lv_streq(txt, "pad_bottom")) return LV_STYLE_PAD_BOTTOM;
else if(lv_streq(txt, "pad_row")) return LV_STYLE_PAD_ROW;
else if(lv_streq(txt, "pad_column")) return LV_STYLE_PAD_COLUMN;
else if(lv_streq(txt, "pad_radial")) return LV_STYLE_PAD_RADIAL;
else if(lv_streq(txt, "margin_left")) return LV_STYLE_MARGIN_LEFT;
else if(lv_streq(txt, "margin_right")) return LV_STYLE_MARGIN_RIGHT;
else if(lv_streq(txt, "margin_top")) return LV_STYLE_MARGIN_TOP;
else if(lv_streq(txt, "margin_bottom")) return LV_STYLE_MARGIN_BOTTOM;
else if(lv_streq(txt, "base_dir")) return LV_STYLE_BASE_DIR;
else if(lv_streq(txt, "clip_corner")) return LV_STYLE_CLIP_CORNER;
else if(lv_streq(txt, "bg_opa")) return LV_STYLE_BG_OPA;
else if(lv_streq(txt, "bg_color")) return LV_STYLE_BG_COLOR;
else if(lv_streq(txt, "bg_grad_dir")) return LV_STYLE_BG_GRAD_DIR;
else if(lv_streq(txt, "bg_grad_color")) return LV_STYLE_BG_GRAD_COLOR;
else if(lv_streq(txt, "bg_main_stop")) return LV_STYLE_BG_MAIN_STOP;
else if(lv_streq(txt, "bg_grad_stop")) return LV_STYLE_BG_GRAD_STOP;
else if(lv_streq(txt, "bg_grad")) return LV_STYLE_BG_GRAD;
else if(lv_streq(txt, "bg_image_src")) return LV_STYLE_BG_IMAGE_SRC;
else if(lv_streq(txt, "bg_image_tiled")) return LV_STYLE_BG_IMAGE_TILED;
else if(lv_streq(txt, "bg_image_recolor")) return LV_STYLE_BG_IMAGE_RECOLOR;
else if(lv_streq(txt, "bg_image_recolor_opa")) return LV_STYLE_BG_IMAGE_RECOLOR_OPA;
else if(lv_streq(txt, "border_color")) return LV_STYLE_BORDER_COLOR;
else if(lv_streq(txt, "border_width")) return LV_STYLE_BORDER_WIDTH;
else if(lv_streq(txt, "border_opa")) return LV_STYLE_BORDER_OPA;
else if(lv_streq(txt, "border_side")) return LV_STYLE_BORDER_SIDE;
else if(lv_streq(txt, "border_post")) return LV_STYLE_BORDER_POST;
else if(lv_streq(txt, "outline_color")) return LV_STYLE_OUTLINE_COLOR;
else if(lv_streq(txt, "outline_width")) return LV_STYLE_OUTLINE_WIDTH;
else if(lv_streq(txt, "outline_opa")) return LV_STYLE_OUTLINE_OPA;
else if(lv_streq(txt, "outline_pad")) return LV_STYLE_OUTLINE_PAD;
else if(lv_streq(txt, "shadow_width")) return LV_STYLE_SHADOW_WIDTH;
else if(lv_streq(txt, "shadow_color")) return LV_STYLE_SHADOW_COLOR;
else if(lv_streq(txt, "shadow_offset_x")) return LV_STYLE_SHADOW_OFFSET_X;
else if(lv_streq(txt, "shadow_offset_y")) return LV_STYLE_SHADOW_OFFSET_Y;
else if(lv_streq(txt, "shadow_spread")) return LV_STYLE_SHADOW_SPREAD;
else if(lv_streq(txt, "shadow_opa")) return LV_STYLE_SHADOW_OPA;
else if(lv_streq(txt, "text_color")) return LV_STYLE_TEXT_COLOR;
else if(lv_streq(txt, "text_font")) return LV_STYLE_TEXT_FONT;
else if(lv_streq(txt, "text_opa")) return LV_STYLE_TEXT_OPA;
else if(lv_streq(txt, "text_align")) return LV_STYLE_TEXT_ALIGN;
else if(lv_streq(txt, "text_letter_space")) return LV_STYLE_TEXT_LETTER_SPACE;
else if(lv_streq(txt, "text_line_space")) return LV_STYLE_TEXT_LINE_SPACE;
else if(lv_streq(txt, "text_decor")) return LV_STYLE_TEXT_DECOR;
else if(lv_streq(txt, "image_opa")) return LV_STYLE_IMAGE_OPA;
else if(lv_streq(txt, "image_recolor")) return LV_STYLE_IMAGE_RECOLOR;
else if(lv_streq(txt, "image_recolor_opa")) return LV_STYLE_IMAGE_RECOLOR_OPA;
else if(lv_streq(txt, "line_color")) return LV_STYLE_LINE_COLOR;
else if(lv_streq(txt, "line_opa")) return LV_STYLE_LINE_OPA;
else if(lv_streq(txt, "line_width")) return LV_STYLE_LINE_WIDTH;
else if(lv_streq(txt, "line_dash_width")) return LV_STYLE_LINE_DASH_WIDTH;
else if(lv_streq(txt, "line_dash_gap")) return LV_STYLE_LINE_DASH_GAP;
else if(lv_streq(txt, "line_rounded")) return LV_STYLE_LINE_ROUNDED;
else if(lv_streq(txt, "arc_color")) return LV_STYLE_ARC_COLOR;
else if(lv_streq(txt, "arc_opa")) return LV_STYLE_ARC_OPA;
else if(lv_streq(txt, "arc_width")) return LV_STYLE_ARC_WIDTH;
else if(lv_streq(txt, "arc_rounded")) return LV_STYLE_ARC_ROUNDED;
else if(lv_streq(txt, "arc_image_src")) return LV_STYLE_ARC_IMAGE_SRC;
else if(lv_streq(txt, "opa")) return LV_STYLE_OPA;
else if(lv_streq(txt, "opa_layered")) return LV_STYLE_OPA_LAYERED;
else if(lv_streq(txt, "color_filter_opa")) return LV_STYLE_COLOR_FILTER_OPA;
else if(lv_streq(txt, "anim_duration")) return LV_STYLE_ANIM_DURATION;
else if(lv_streq(txt, "blend_mode")) return LV_STYLE_BLEND_MODE;
else if(lv_streq(txt, "transform_width")) return LV_STYLE_TRANSFORM_WIDTH;
else if(lv_streq(txt, "transform_height")) return LV_STYLE_TRANSFORM_HEIGHT;
else if(lv_streq(txt, "translate_x")) return LV_STYLE_TRANSLATE_X;
else if(lv_streq(txt, "translate_y")) return LV_STYLE_TRANSLATE_Y;
else if(lv_streq(txt, "translate_radial")) return LV_STYLE_TRANSLATE_RADIAL;
else if(lv_streq(txt, "transform_scale_x")) return LV_STYLE_TRANSFORM_SCALE_X;
else if(lv_streq(txt, "transform_scale_y")) return LV_STYLE_TRANSFORM_SCALE_Y;
else if(lv_streq(txt, "transform_rotation")) return LV_STYLE_TRANSFORM_ROTATION;
else if(lv_streq(txt, "transform_pivot_x")) return LV_STYLE_TRANSFORM_PIVOT_X;
else if(lv_streq(txt, "transform_pivot_y")) return LV_STYLE_TRANSFORM_PIVOT_Y;
else if(lv_streq(txt, "transform_skew_x")) return LV_STYLE_TRANSFORM_SKEW_X;
else if(lv_streq(txt, "bitmap_mask_src")) return LV_STYLE_BITMAP_MASK_SRC;
else if(lv_streq(txt, "rotary_sensitivity")) return LV_STYLE_ROTARY_SENSITIVITY;
else if(lv_streq(txt, "recolor")) return LV_STYLE_RECOLOR;
else if(lv_streq(txt, "recolor_opa")) return LV_STYLE_RECOLOR_OPA;
else if(lv_streq(txt, "layout")) return LV_STYLE_LAYOUT;
else if(lv_streq(txt, "flex_flow")) return LV_STYLE_FLEX_FLOW;
else if(lv_streq(txt, "flex_grow")) return LV_STYLE_FLEX_GROW;
else if(lv_streq(txt, "flex_main_place")) return LV_STYLE_FLEX_MAIN_PLACE;
else if(lv_streq(txt, "flex_cross_place")) return LV_STYLE_FLEX_CROSS_PLACE;
else if(lv_streq(txt, "flex_track_place")) return LV_STYLE_FLEX_TRACK_PLACE;
else if(lv_streq(txt, "grid_column_align")) return LV_STYLE_GRID_COLUMN_ALIGN;
else if(lv_streq(txt, "grid_row_align")) return LV_STYLE_GRID_ROW_ALIGN;
else if(lv_streq(txt, "grid_cell_column_pos")) return LV_STYLE_GRID_CELL_COLUMN_POS;
else if(lv_streq(txt, "grid_cell_column_span")) return LV_STYLE_GRID_CELL_COLUMN_SPAN;
else if(lv_streq(txt, "grid_cell_x_align")) return LV_STYLE_GRID_CELL_X_ALIGN;
else if(lv_streq(txt, "grid_cell_row_pos")) return LV_STYLE_GRID_CELL_ROW_POS;
else if(lv_streq(txt, "grid_cell_row_span")) return LV_STYLE_GRID_CELL_ROW_SPAN;
else if(lv_streq(txt, "grid_cell_y_align")) return LV_STYLE_GRID_CELL_Y_ALIGN;
return LV_STYLE_PROP_INV;
}
#endif /* LV_USE_XML */
-14
View File
@@ -40,20 +40,6 @@ typedef struct _lv_xml_style_t {
*/
lv_result_t lv_xml_style_register(lv_xml_component_scope_t * scope, const char ** attrs);
/**
* Convert a style state to enum
* @param txt e.g. "pressed"
* @return the enum `LV_STATE_PRESSED`
*/
lv_state_t lv_xml_style_state_to_enum(const char * txt);
/**
* Convert a style part to enum
* @param txt e.g. "knob"
* @return the enum `LV_PART_KNOB`
*/
lv_part_t lv_xml_style_part_to_enum(const char * txt);
/**
* Decompose a string like `"style1:pressed:checked:knob"` to style name and selector
* @param txt the input string
+134 -25
View File
@@ -15,6 +15,7 @@
/*********************
* DEFINES
*********************/
#define lv_event_xml_store_timeline LV_GLOBAL_DEFAULT()->lv_event_xml_store_timeline
/**********************
* TYPEDEFS
@@ -29,17 +30,26 @@ typedef struct {
const char * screen_name;
} screen_load_anim_dsc_t;
typedef struct {
const char * timeline_name;
const char * target_name;
uint32_t delay;
bool reverse;
lv_obj_t * base_obj; /**< Get the objs by name from here (the view) */
} play_anim_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_obj_flag_t flag_to_enum(const char * txt);
static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const char * name, const char * value);
static lv_style_selector_t get_selector(const char * str);
static void free_user_data_event_cb(lv_event_t * e);
static void screen_create_on_trigger_event_cb(lv_event_t * e);
static void screen_load_on_trigger_event_cb(lv_event_t * e);
static void delete_on_screen_unloaded_event_cb(lv_event_t * e);
static void free_screen_create_user_data_on_delete_event_cb(lv_event_t * e);
static void play_anim_on_trigger_event_cb(lv_event_t * e);
static void free_play_anim_user_data_on_delete_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
@@ -181,7 +191,7 @@ void lv_obj_xml_style_apply(lv_xml_parser_state_t * state, const char ** attrs)
}
const char * selector_str = lv_xml_get_value_of(attrs, "selector");
lv_style_selector_t selector = get_selector(selector_str);
lv_style_selector_t selector = lv_xml_style_selector_text_to_enum(selector_str);
void * item = lv_xml_state_get_parent(state);
lv_obj_add_style(item, &xml_style->style, selector);
@@ -210,7 +220,7 @@ void lv_obj_xml_remove_style_apply(lv_xml_parser_state_t * state, const char **
style = &xml_style->style;
}
lv_style_selector_t selector = get_selector(selector_str);
lv_style_selector_t selector = lv_xml_style_selector_text_to_enum(selector_str);
void * item = lv_xml_state_get_item(state);
lv_obj_remove_style(item, style, selector);
@@ -437,7 +447,7 @@ void lv_obj_xml_bind_style_apply(lv_xml_parser_state_t * state, const char ** at
int32_t ref_value = lv_xml_atoi(ref_value_str);
const char * selector_str = lv_xml_get_value_of(attrs, "selector");
lv_style_selector_t selector = get_selector(selector_str);
lv_style_selector_t selector = lv_xml_style_selector_text_to_enum(selector_str);
void * item = lv_xml_state_get_parent(state);
lv_obj_bind_style(item, &xml_style->style, selector, subject, ref_value);
@@ -647,6 +657,62 @@ void lv_obj_xml_screen_create_event_apply(lv_xml_parser_state_t * state, const c
lv_obj_add_event_cb(item, free_screen_create_user_data_on_delete_event_cb, LV_EVENT_DELETE, dsc);
}
void * lv_obj_xml_play_timeline_event_create(lv_xml_parser_state_t * state, const char ** attrs)
{
LV_UNUSED(attrs);
void * item = lv_xml_state_get_parent(state);
return item;
}
void lv_obj_xml_play_timeline_event_apply(lv_xml_parser_state_t * state, const char ** attrs)
{
if(state->view == NULL) {
/*Shouldn't happen*/
LV_LOG_WARN("view is not set, can't add the event");
return;
}
const char * target_str = lv_xml_get_value_of(attrs, "target");
const char * delay_str = lv_xml_get_value_of(attrs, "delay");
const char * trigger_str = lv_xml_get_value_of(attrs, "trigger");
const char * timeline_str = lv_xml_get_value_of(attrs, "timeline");
const char * reverse_str = lv_xml_get_value_of(attrs, "reverse");
if(target_str == NULL) {
LV_LOG_WARN("`target` is missing in <lv_obj-play_animation_event>");
return;
}
if(timeline_str == NULL) {
LV_LOG_WARN("`timeline` is missing in <lv_obj-play_animation_event>");
return;
}
if(delay_str == NULL) delay_str = "0";
if(trigger_str == NULL) trigger_str = "clicked";
if(reverse_str == NULL) reverse_str = "false";
lv_event_code_t trigger = lv_xml_trigger_text_to_enum_value(trigger_str);
if(trigger == LV_EVENT_LAST) {
LV_LOG_WARN("Couldn't apply <screen_load_event> because `%s` trigger is invalid.", trigger_str);
return;
}
play_anim_dsc_t * dsc = lv_malloc(sizeof(play_anim_dsc_t));
LV_ASSERT_MALLOC(dsc);
lv_memzero(dsc, sizeof(play_anim_dsc_t));
dsc->target_name = lv_strdup(target_str);
dsc->timeline_name = lv_strdup(timeline_str);
dsc->delay = lv_xml_atoi(delay_str);
dsc->reverse = lv_xml_to_bool(reverse_str);
dsc->base_obj = state->view;
void * item = lv_xml_state_get_item(state);
lv_obj_add_event_cb(item, play_anim_on_trigger_event_cb, trigger, dsc);
lv_obj_add_event_cb(item, free_play_anim_user_data_on_delete_event_cb, LV_EVENT_DELETE, dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
@@ -804,6 +870,7 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch
else SET_STYLE_IF(transform_pivot_x, lv_xml_atoi(value));
else SET_STYLE_IF(transform_pivot_y, lv_xml_atoi(value));
else SET_STYLE_IF(transform_skew_x, lv_xml_atoi(value));
else SET_STYLE_IF(transform_skew_y, lv_xml_atoi(value));
else SET_STYLE_IF(bitmap_mask_src, lv_xml_get_image(&state->scope, value));
else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value));
else SET_STYLE_IF(recolor, lv_xml_to_color(value));
@@ -827,27 +894,6 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch
else SET_STYLE_IF(grid_cell_y_align, lv_xml_grid_align_to_enum(value));
}
static lv_style_selector_t get_selector(const char * str)
{
if(str == NULL) return 0;
lv_style_selector_t selector = 0;
char buf[256];
lv_strncpy(buf, str, sizeof(buf));
char * bufp = buf;
const char * next = lv_xml_split_str(&bufp, '|');
while(next) {
/* Handle different states and parts */
selector |= lv_xml_style_state_to_enum(next);
selector |= lv_xml_style_part_to_enum(next);
/* Move to the next token */
next = lv_xml_split_str(&bufp, '|');
}
return selector;
}
static void free_user_data_event_cb(lv_event_t * e)
{
@@ -895,4 +941,67 @@ static void free_screen_create_user_data_on_delete_event_cb(lv_event_t * e)
lv_free(dsc);
}
static void play_anim_on_trigger_event_cb(lv_event_t * e)
{
play_anim_dsc_t * dsc = lv_event_get_user_data(e);
LV_ASSERT_NULL(dsc);
lv_obj_t * target;
if(lv_streq(dsc->target_name, "self")) {
target = dsc->base_obj;
}
else {
target = lv_obj_get_child_by_name(dsc->base_obj, dsc->target_name);
}
if(target == NULL) {
LV_LOG_WARN("No target widget is found with `%s` name", dsc->target_name);
return;
}
lv_anim_timeline_t * timeline = NULL;
lv_anim_timeline_t ** timeline_array = NULL;
lv_obj_send_event(target, lv_event_xml_store_timeline, &timeline_array);
if(timeline_array == NULL) {
LV_LOG_WARN("No time lines are stored in `%s`", dsc->target_name);
return;
}
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, dsc->timeline_name)) {
timeline = timeline_array[i];
break;
}
}
if(timeline == NULL) {
LV_LOG_WARN("No timeline is found for `%s` with `%s` name", dsc->target_name, dsc->timeline_name);
return;
}
if(dsc->reverse) {
lv_anim_timeline_set_reverse(timeline, true);
lv_anim_timeline_set_progress(timeline, LV_ANIM_TIMELINE_PROGRESS_MAX);
}
else {
lv_anim_timeline_set_reverse(timeline, false);
lv_anim_timeline_set_progress(timeline, 0);
}
lv_anim_timeline_set_delay(timeline, dsc->delay);
lv_anim_timeline_start(timeline);
}
static void free_play_anim_user_data_on_delete_event_cb(lv_event_t * e)
{
play_anim_dsc_t * dsc = lv_event_get_user_data(e);
lv_free((void *)dsc->target_name);
lv_free((void *)dsc->timeline_name);
lv_free(dsc);
}
#endif /* LV_USE_XML */
@@ -60,6 +60,9 @@ void lv_obj_xml_screen_load_event_apply(lv_xml_parser_state_t * state, const cha
void * lv_obj_xml_screen_create_event_create(lv_xml_parser_state_t * state, const char ** attrs);
void lv_obj_xml_screen_create_event_apply(lv_xml_parser_state_t * state, const char ** attrs);
void * lv_obj_xml_play_timeline_event_create(lv_xml_parser_state_t * state, const char ** attrs);
void lv_obj_xml_play_timeline_event_apply(lv_xml_parser_state_t * state, const char ** attrs);
/**********************
* MACROS
**********************/
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1,34 @@
<component>
<consts>
<int name="delay1" value="200"/>
<int name="delay2" value="400"/>
<int name="duration" value="300"/>
</consts>
<animations>
<timeline name="load">
<animation prop="translate_x" target="title" start="-30" end="0" duration="500"/>
<animation prop="opa" target="title" start="0" end="255" duration="500"/>
<animation prop="translate_y" target="my_button_anim_1" start="-20" end="0" duration="#duration" delay="#delay2" early_apply="true"/>
<animation prop="opa" target="my_button_anim_1" start="0" end="255" duration="#duration" delay="#delay2" early_apply="true"/>
<!-- Animate from the bottom if checked -->
<animation prop="translate_y" selector="checked" target="my_button_anim_1" start="20" end="0" duration="#duration" delay="#delay2" early_apply="true"/>
</timeline>
</animations>
<view height="200">
<lv_label text="List title" name="title"/>
<my_button_anim y="20" text="Button 1">
<play_timeline_event trigger="clicked" target="self" timeline="load"/>
</my_button_anim>
<my_button_anim y="60" text="Button 2" checkable="true">
<play_timeline_event trigger="pressed" target="my_button_anim_1" timeline="grow"/>
<play_timeline_event trigger="released" target="my_button_anim_1" timeline="shrink" delay="1000"/>
</my_button_anim>
</view>
</component>
@@ -0,0 +1,24 @@
<component>
<animations>
<timeline name="grow">
<animation prop="pad_left" target="self" start="10" end="30" duration="500"/>
<animation prop="pad_right" target="self" start="10" end="30" duration="500"/>
<animation prop="text_letter_space" target="lv_label_0" start="0" end="5" duration="500"/>
</timeline>
<timeline name="shrink">
<animation prop="pad_left" target="self" start="30" end="10" duration="500" delay="300"/>
<animation prop="pad_right" target="self" start="30" end="10" duration="500" delay="300"/>
<animation prop="text_letter_space" target="lv_label_0" start="5" end="0" duration="500" delay="300"/>
</timeline>
</animations>
<api>
<prop name="text" type="string" default="Click me" help="the text to display on the button"/>
</api>
<view extends="lv_button" style_pad_hor="10">
<lv_label text="$text"/>
</view>
</component>
@@ -0,0 +1,69 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
void setUp(void)
{
/* Function run before every test */
}
void tearDown(void)
{
/* Function run after every test */
lv_obj_clean(lv_screen_active());
}
void test_xml_complex(void)
{
lv_xml_component_register_from_file("A:src/test_assets/xml/my_button_anim.xml");
lv_xml_component_register_from_file("A:src/test_assets/xml/list_item_anim.xml");
lv_obj_t * obj = lv_xml_create(lv_screen_active(), "list_item_anim", NULL);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_1.png");
lv_test_mouse_move_to_obj(lv_obj_get_child_by_name(obj, "my_button_anim_0"));
/*Click Button 1 to trigger a load animation*/
lv_test_mouse_click_at(45, 45);
lv_test_wait(200);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_2.png");
lv_test_wait(200);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_3.png");
lv_test_wait(500);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_1.png"); /*Loaded, initial state*/
/*Click Button 2 to trigger a grow/shrink animation*/
lv_test_mouse_click_at(45, 90);
lv_test_wait(200);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_4.png");
lv_test_wait(300);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_5.png");
/*1000 delay on the play_timeline_event trigger="released" in list_item_anim.xml
*and 300 delay in the shrink animation in my_button_anim.xml.
*But the clock started to tick on release, so 500 ms already elapsed
*-50ms as lv_test_mouse_click_at has 50ms delay after release.
*Use the same image as we are just waiting for the delay*/
lv_test_wait(1000 + 300 - 500 - 50);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_5.png");
lv_test_wait(300);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_6.png");
lv_test_wait(200);
/*Animated back to normal size, but checked*/
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_7.png");
/*Click Button 1 to trigger a load animation,
*but Button 2 is now checked and loaded from the bottom*/
lv_test_mouse_click_at(45, 45);
lv_test_wait(200);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_8.png");
lv_test_wait(200);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_9.png");
lv_test_wait(500);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/timeline_10.png");
}
#endif
+8
View File
@@ -80,6 +80,14 @@ Example
<arg name="delay" type="int" default="0"/>
</element>
<element name="play_timeline_event" access="add" type="void">
<arg name="trigger" type="lv_event" default="clicked"/>
<arg name="target" type="lv_obj"/>
<arg name="timeline" type="timeline"/>
<arg name="delay" type="int" default="0"/>
<arg name="reverse" type="bool" default="false"/>
</element>
<element name="subject_set_int_event" access="add" type="void">
<arg name="subject" type="subject"/>
<arg name="trigger" type="lv_event" default="clicked"/>