diff --git a/demos/benchmark/assets/lv_font_benchmark_montserrat_12_aligned.c b/demos/benchmark/assets/lv_font_benchmark_montserrat_12_aligned.c index 5a4a919f90..e95f1e74b3 100644 --- a/demos/benchmark/assets/lv_font_benchmark_montserrat_12_aligned.c +++ b/demos/benchmark/assets/lv_font_benchmark_montserrat_12_aligned.c @@ -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, diff --git a/docs/src/details/main-modules/animation.rst b/docs/src/details/main-modules/animation.rst index f7af4be748..94e3ee9398 100644 --- a/docs/src/details/main-modules/animation.rst +++ b/docs/src/details/main-modules/animation.rst @@ -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: diff --git a/docs/src/details/xml/build_ui/main_tags/animations.rst b/docs/src/details/xml/build_ui/main_tags/animations.rst index 5b61b67a07..1b83ae9fde 100644 --- a/docs/src/details/xml/build_ui/main_tags/animations.rst +++ b/docs/src/details/xml/build_ui/main_tags/animations.rst @@ -1,7 +1,111 @@ -.. _xml_animations: + .. _xml_animations: ========== Animations ========== -TODO +Overview +-------- + +XML animations are built on top of :ref:`Timeline animations `. + +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 ````\ s and ````\ s. +For ````\ s, timelines are supported only in LVGL's UI Editor, +where C code can also be exported from them. + +Example: + +.. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + + +In summary: inside ````, you can define ````\ s, each with a unique name +that you can reference later. + +Inside a ````, you add ````\ s to describe each step. +Supported properties of ```` 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 ````). +- ``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 ```` +as a child of any widget. + +Example: + +.. code-block:: xml + + + + + + + + + +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 ```` 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 ```` is added to a UI element, the target and timeline +names are saved as strings. (It can't use pointers as the event can reference UI elements +that will be created only later in the ````.) + +Finally, when the 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 ````\ s) +and play their ``load`` timelines independently with different delays. diff --git a/src/core/lv_global.h b/src/core/lv_global.h index 119bc80f71..d510c6ca74 100644 --- a/src/core/lv_global.h +++ b/src/core/lv_global.h @@ -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 diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index 7b79822c6d..c53298935e 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -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)); diff --git a/src/core/lv_obj.h b/src/core/lv_obj.h index e6b4ff44ce..ead6ba5a5b 100644 --- a/src/core/lv_obj.h +++ b/src/core/lv_obj.h @@ -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. diff --git a/src/misc/lv_anim_timeline.c b/src/misc/lv_anim_timeline.c index 68abf5940a..8ecb16fcce 100644 --- a/src/misc/lv_anim_timeline.c +++ b/src/misc/lv_anim_timeline.c @@ -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; + } +} diff --git a/src/misc/lv_anim_timeline.h b/src/misc/lv_anim_timeline.h index 0e9651d530..98fa183469 100644 --- a/src/misc/lv_anim_timeline.h +++ b/src/misc/lv_anim_timeline.h @@ -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 **********************/ diff --git a/src/misc/lv_anim_timeline_private.h b/src/misc/lv_anim_timeline_private.h new file mode 100644 index 0000000000..da26056468 --- /dev/null +++ b/src/misc/lv_anim_timeline_private.h @@ -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*/ diff --git a/src/misc/lv_fs.c b/src/misc/lv_fs.c index e90d00c7f7..6a614566b4 100644 --- a/src/misc/lv_fs.c +++ b/src/misc/lv_fs.c @@ -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*/ diff --git a/src/misc/lv_types.h b/src/misc/lv_types.h index 43fef09164..a026b28275 100644 --- a/src/misc/lv_types.h +++ b/src/misc/lv_types.h @@ -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; diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index c1ef11fca6..019893d9c6 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -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 */ diff --git a/src/others/xml/lv_xml.h b/src/others/xml/lv_xml.h index 748c449b85..3f8a72a1d7 100644 --- a/src/others/xml/lv_xml.h +++ b/src/others/xml/lv_xml.h @@ -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 **********************/ diff --git a/src/others/xml/lv_xml_base_types.c b/src/others/xml/lv_xml_base_types.c index d8a6039ef0..79b75a5355 100644 --- a/src/others/xml/lv_xml_base_types.c +++ b/src/others/xml/lv_xml_base_types.c @@ -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 **********************/ diff --git a/src/others/xml/lv_xml_base_types.h b/src/others/xml/lv_xml_base_types.h index 726f4eaa04..dcf10a5a22 100644 --- a/src/others/xml/lv_xml_base_types.h +++ b/src/others/xml/lv_xml_base_types.h @@ -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 **********************/ diff --git a/src/others/xml/lv_xml_component.c b/src/others/xml/lv_xml_component.c index 4422e3a6f7..b460f587d0 100644 --- a/src/others/xml/lv_xml_component.c +++ b/src/others/xml/lv_xml_component.c @@ -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. */ 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 */ diff --git a/src/others/xml/lv_xml_component_private.h b/src/others/xml/lv_xml_component_private.h index 903d2635f4..90fbf525b3 100644 --- a/src/others/xml/lv_xml_component_private.h +++ b/src/others/xml/lv_xml_component_private.h @@ -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; diff --git a/src/others/xml/lv_xml_parser.c b/src/others/xml/lv_xml_parser.c index 34376998d2..127859cf7a 100644 --- a/src/others/xml/lv_xml_parser.c +++ b/src/others/xml/lv_xml_parser.c @@ -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; diff --git a/src/others/xml/lv_xml_parser.h b/src/others/xml/lv_xml_parser.h index 0ddc225bd0..6f9e891610 100644 --- a/src/others/xml/lv_xml_parser.h +++ b/src/others/xml/lv_xml_parser.h @@ -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; diff --git a/src/others/xml/lv_xml_style.c b/src/others/xml/lv_xml_style.c index 47c459924e..18c6200726 100644 --- a/src/others/xml/lv_xml_style.c +++ b/src/others/xml/lv_xml_style.c @@ -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 */ diff --git a/src/others/xml/lv_xml_style.h b/src/others/xml/lv_xml_style.h index 41af39e0a8..d794d1e523 100644 --- a/src/others/xml/lv_xml_style.h +++ b/src/others/xml/lv_xml_style.h @@ -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 diff --git a/src/others/xml/parsers/lv_xml_obj_parser.c b/src/others/xml/parsers/lv_xml_obj_parser.c index fe1e820010..8da671c566 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.c +++ b/src/others/xml/parsers/lv_xml_obj_parser.c @@ -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 "); + return; + } + + if(timeline_str == NULL) { + LV_LOG_WARN("`timeline` is missing in "); + 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 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 */ diff --git a/src/others/xml/parsers/lv_xml_obj_parser.h b/src/others/xml/parsers/lv_xml_obj_parser.h index 8eca5ef242..86d64d5e01 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.h +++ b/src/others/xml/parsers/lv_xml_obj_parser.h @@ -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 **********************/ diff --git a/tests/ref_imgs/xml/timeline_1.png b/tests/ref_imgs/xml/timeline_1.png new file mode 100644 index 0000000000..87bb5eccd0 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_1.png differ diff --git a/tests/ref_imgs/xml/timeline_10.png b/tests/ref_imgs/xml/timeline_10.png new file mode 100644 index 0000000000..5f185a8a48 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_10.png differ diff --git a/tests/ref_imgs/xml/timeline_2.png b/tests/ref_imgs/xml/timeline_2.png new file mode 100644 index 0000000000..46840998fd Binary files /dev/null and b/tests/ref_imgs/xml/timeline_2.png differ diff --git a/tests/ref_imgs/xml/timeline_3.png b/tests/ref_imgs/xml/timeline_3.png new file mode 100644 index 0000000000..a298e0a46c Binary files /dev/null and b/tests/ref_imgs/xml/timeline_3.png differ diff --git a/tests/ref_imgs/xml/timeline_4.png b/tests/ref_imgs/xml/timeline_4.png new file mode 100644 index 0000000000..72dc9a2572 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_4.png differ diff --git a/tests/ref_imgs/xml/timeline_5.png b/tests/ref_imgs/xml/timeline_5.png new file mode 100644 index 0000000000..25dc9a4803 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_5.png differ diff --git a/tests/ref_imgs/xml/timeline_6.png b/tests/ref_imgs/xml/timeline_6.png new file mode 100644 index 0000000000..80b6c677c2 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_6.png differ diff --git a/tests/ref_imgs/xml/timeline_7.png b/tests/ref_imgs/xml/timeline_7.png new file mode 100644 index 0000000000..5f185a8a48 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_7.png differ diff --git a/tests/ref_imgs/xml/timeline_8.png b/tests/ref_imgs/xml/timeline_8.png new file mode 100644 index 0000000000..0abda54cf0 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_8.png differ diff --git a/tests/ref_imgs/xml/timeline_9.png b/tests/ref_imgs/xml/timeline_9.png new file mode 100644 index 0000000000..89e5fa5bb5 Binary files /dev/null and b/tests/ref_imgs/xml/timeline_9.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_1.png b/tests/ref_imgs_vg_lite/xml/timeline_1.png new file mode 100644 index 0000000000..675928d584 Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_1.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_10.png b/tests/ref_imgs_vg_lite/xml/timeline_10.png new file mode 100644 index 0000000000..378a63d8dd Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_10.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_2.png b/tests/ref_imgs_vg_lite/xml/timeline_2.png new file mode 100644 index 0000000000..7046e9080e Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_2.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_3.png b/tests/ref_imgs_vg_lite/xml/timeline_3.png new file mode 100644 index 0000000000..61317c6f4a Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_3.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_4.png b/tests/ref_imgs_vg_lite/xml/timeline_4.png new file mode 100644 index 0000000000..26aaa10479 Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_4.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_5.png b/tests/ref_imgs_vg_lite/xml/timeline_5.png new file mode 100644 index 0000000000..741b228554 Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_5.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_6.png b/tests/ref_imgs_vg_lite/xml/timeline_6.png new file mode 100644 index 0000000000..127a77aab0 Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_6.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_7.png b/tests/ref_imgs_vg_lite/xml/timeline_7.png new file mode 100644 index 0000000000..378a63d8dd Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_7.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_8.png b/tests/ref_imgs_vg_lite/xml/timeline_8.png new file mode 100644 index 0000000000..7046e9080e Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_8.png differ diff --git a/tests/ref_imgs_vg_lite/xml/timeline_9.png b/tests/ref_imgs_vg_lite/xml/timeline_9.png new file mode 100644 index 0000000000..dd35d13b7e Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/timeline_9.png differ diff --git a/tests/src/test_assets/xml/list_item_anim.xml b/tests/src/test_assets/xml/list_item_anim.xml new file mode 100644 index 0000000000..e7872285ae --- /dev/null +++ b/tests/src/test_assets/xml/list_item_anim.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/src/test_assets/xml/my_button_anim.xml b/tests/src/test_assets/xml/my_button_anim.xml new file mode 100644 index 0000000000..fd60210192 --- /dev/null +++ b/tests/src/test_assets/xml/my_button_anim.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/src/test_cases/xml/test_xml_timeline.c b/tests/src/test_cases/xml/test_xml_timeline.c new file mode 100644 index 0000000000..86eedc39c3 --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_timeline.c @@ -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 diff --git a/xmls/lv_obj.xml b/xmls/lv_obj.xml index 48f2867ab2..77dd688cf9 100644 --- a/xmls/lv_obj.xml +++ b/xmls/lv_obj.xml @@ -80,6 +80,14 @@ Example + + + + + + + +