feat(xml): add color percentage support to animations (#9209)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Gabor Kiss-Vamosi
2025-11-14 07:39:30 +01:00
committed by GitHub
parent 3d3bcd5dd3
commit 5472a77c6e
5 changed files with 118 additions and 74 deletions
@@ -61,7 +61,7 @@ that you can reference later.
Inside a ``<timeline>``, you add ``<animation>``\ s to describe each step. Inside a ``<timeline>``, you add ``<animation>``\ s to describe each step.
Supported properties of ``<animation>`` are: Supported properties of ``<animation>`` are:
- ``prop``: Style property to animate. All integer style properties are supported (colors are not). - ``prop``: Style property to animate. All integer, percentage and color style properties are supported.
- ``selector``: Style selector, e.g. ``knob|pressed``. Default: ``main|default``. - ``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>``). - ``target``: Name of the UI element to animate. ``self`` refers to the root element of the Component (the ``<view>``).
- ``start``: Start value (integer only). - ``start``: Start value (integer only).
-2
View File
@@ -23,8 +23,6 @@ void lv_example_xml_2(void)
lv_obj_t * obj = (lv_obj_t *) lv_xml_create(lv_screen_active(), "view", NULL); lv_obj_t * obj = (lv_obj_t *) lv_xml_create(lv_screen_active(), "view", NULL);
lv_obj_set_pos(obj, 10, 10); lv_obj_set_pos(obj, 10, 10);
lv_xml_component_unregister("my_button");
const char * slider_attrs[] = { const char * slider_attrs[] = {
"x", "200", "x", "200",
"y", "-15", "y", "-15",
+36 -9
View File
@@ -1,15 +1,42 @@
<!-- A simple button that defines a show up animation for itself -->
<component> <component>
<consts>
<px name="size" value="100"/>
<color name="orange" value="0xffa020"/>
</consts>
<api> <api>
<prop name="btn_text" type="string"/> <prop name="label_text" type="string" default="Click me" />
</api> </api>
<view extends="lv_button" width="#size"> <styles>
<my_h3 text="$btn_text" align="center" color="#orange"/> <style
name="style_base"
bg_opa="100%"
bg_color="0x000044"
width="content"
height="content"
radius="10"
pad_hor="30"
pad_ver="20"
text_color="0xfff"
/>
<style name="style_pressed" recolor="0xfff" recolor_opa="20%" />
</styles>
<animations>
<!-- Fade in and move up
This animation can be played later in an event by a parent component or screen -->
<timeline name="show_up">
<animation target="self" prop="opa" start="0" end="255" duration="200" early_apply="true" />
<animation target="self" prop="bg_color" start="0xff0000" end="0x00ff00" duration="2000" early_apply="true" />
<animation target="self" prop="translate_y" start="20" end="0" duration="200" early_apply="true" />
<animation target="self" prop="width" start="0%" end="50%" duration="200" early_apply="true" />
</timeline>
</animations>
<view extends="lv_button">
<remove_style_all />
<style name="style_base" />
<style name="style_pressed" selector="pressed" />
<lv_label text="$label_text" align="center" />
<play_timeline_event target="self" timeline="show_up" />
</view> </view>
</component> </component>
+6 -21
View File
@@ -9,26 +9,11 @@
<style name="btn_pr_style" bg_opa="255"/> <style name="btn_pr_style" bg_opa="255"/>
</styles> </styles>
<view extends="lv_obj" name="main" width="280" height="content" style_bg_color="#light_blue" <view width="280" height="content"
style_layout="grid" style_grid_column_dsc_array="100 fr(1)" style_grid_row_dsc_array="50 200" flex_flow="column">
<my_button/>
> <my_button/>
<lv_obj style_grid_cell_column_pos="0" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch" <my_button/>
style_grid_cell_row_pos="0" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch"> <my_button/>
<lv_label text="0/0"/>
</lv_obj>
<lv_obj style_grid_cell_column_pos="0" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="1" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="0/1"/>
</lv_obj>
<lv_obj style_grid_cell_column_pos="1" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="0" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="1/0"/>
</lv_obj>
<lv_obj style_grid_cell_column_pos="1" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="1" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="1/1"/>
</lv_obj>
</view> </view>
</component> </component>
+72 -38
View File
@@ -32,9 +32,19 @@
**********************/ **********************/
typedef enum { typedef enum {
STYLE_PROP_TYPE_INT, STYLE_PROP_TYPE_INT,
STYLE_PROP_TYPE_OPA,
STYLE_PROP_TYPE_COLOR,
STYLE_PROP_TYPE_UNKNOWN STYLE_PROP_TYPE_UNKNOWN
} style_prop_anim_type_t; } style_prop_anim_type_t;
typedef struct {
lv_style_selector_t selector;
lv_style_prop_t prop;
style_prop_anim_type_t prop_type;
lv_color_t color_start;
lv_color_t color_end;
} anim_data_t;
/********************** /**********************
* STATIC PROTOTYPES * STATIC PROTOTYPES
**********************/ **********************/
@@ -46,8 +56,7 @@ static void process_image_element(lv_xml_parser_state_t * state, const char * ty
static void process_prop_element(lv_xml_parser_state_t * state, 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 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 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 anim_exec_cb(lv_anim_t * a, int32_t v);
static void int_anim_exec_cb(lv_anim_t * a, int32_t v);
/********************** /**********************
* STATIC VARIABLES * STATIC VARIABLES
@@ -318,6 +327,7 @@ lv_result_t lv_xml_component_unregister(const char * name)
lv_xml_anim_timeline_child_t * child; lv_xml_anim_timeline_child_t * child;
LV_LL_READ(&timeline->anims_ll, child) { LV_LL_READ(&timeline->anims_ll, child) {
if(child->is_anim) { if(child->is_anim) {
lv_free(child->data.anim.user_data); /*It was anim_data_t*/
lv_free(child->data.anim.var); /*It was the name of the target object*/ lv_free(child->data.anim.var); /*It was the name of the target object*/
} }
else { else {
@@ -592,10 +602,6 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char
lv_style_selector_t selector = lv_xml_style_selector_text_to_enum(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);
if(target_str[0] == '#') target_str = lv_xml_get_const(&state->scope, &target_str[1]); 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(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(start_str[0] == '#') start_str = lv_xml_get_const(&state->scope, &start_str[1]);
@@ -615,20 +621,41 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char
return; return;
} }
uint32_t selector_and_prop = ((prop & 0xff) << 24) | selector;
lv_xml_anim_timeline_child_t * child = lv_ll_ins_tail(&at->anims_ll); lv_xml_anim_timeline_child_t * child = lv_ll_ins_tail(&at->anims_ll);
child->is_anim = true; child->is_anim = true;
lv_anim_t * a = &child->data.anim; lv_anim_t * a = &child->data.anim;
anim_data_t * anim_data = lv_malloc(sizeof(anim_data_t));
anim_data->selector = selector;
anim_data->prop = prop;
anim_data->prop_type = prop_type;
lv_anim_init(a); lv_anim_init(a);
lv_anim_set_var(a, lv_strdup(target_str));
if(prop_type == STYLE_PROP_TYPE_INT) {
int32_t start = lv_xml_to_size(start_str);
int32_t end = lv_xml_to_size(end_str);
lv_anim_set_values(a, start, end); lv_anim_set_values(a, start, end);
lv_anim_set_custom_exec_cb(a, int_anim_exec_cb); lv_anim_set_custom_exec_cb(a, anim_exec_cb);
}
else if(prop_type == STYLE_PROP_TYPE_OPA) {
int32_t start = lv_xml_to_opa(start_str);
int32_t end = lv_xml_to_opa(end_str);
lv_anim_set_values(a, start, end);
lv_anim_set_custom_exec_cb(a, anim_exec_cb);
}
else if(prop_type == STYLE_PROP_TYPE_COLOR) {
anim_data->color_start = lv_xml_to_color(start_str);
anim_data->color_end = lv_xml_to_color(end_str);
lv_anim_set_values(a, 0, 255);
lv_anim_set_custom_exec_cb(a, anim_exec_cb);
}
lv_anim_set_var(a, lv_strdup(target_str));
lv_anim_set_duration(a, lv_xml_atoi(duration_str)); lv_anim_set_duration(a, lv_xml_atoi(duration_str));
lv_anim_set_delay(a, lv_xml_atoi(delay_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_early_apply(a, lv_xml_to_bool(early_apply_str));
lv_anim_set_user_data(a, (void *)((uintptr_t)selector_and_prop)); lv_anim_set_user_data(a, anim_data);
} }
static void process_include_timeline_element(lv_xml_parser_state_t * state, const char ** attrs) static void process_include_timeline_element(lv_xml_parser_state_t * state, const char ** attrs)
@@ -989,34 +1016,22 @@ static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop)
case LV_STYLE_MARGIN_RIGHT: case LV_STYLE_MARGIN_RIGHT:
case LV_STYLE_MARGIN_TOP: case LV_STYLE_MARGIN_TOP:
case LV_STYLE_MARGIN_BOTTOM: case LV_STYLE_MARGIN_BOTTOM:
case LV_STYLE_BG_OPA:
case LV_STYLE_BG_MAIN_STOP: case LV_STYLE_BG_MAIN_STOP:
case LV_STYLE_BG_GRAD_STOP: case LV_STYLE_BG_GRAD_STOP:
case LV_STYLE_BG_IMAGE_RECOLOR_OPA: case LV_STYLE_BG_IMAGE_RECOLOR_OPA:
case LV_STYLE_BORDER_WIDTH: case LV_STYLE_BORDER_WIDTH:
case LV_STYLE_BORDER_OPA:
case LV_STYLE_OUTLINE_WIDTH: case LV_STYLE_OUTLINE_WIDTH:
case LV_STYLE_OUTLINE_OPA:
case LV_STYLE_OUTLINE_PAD: case LV_STYLE_OUTLINE_PAD:
case LV_STYLE_SHADOW_WIDTH: case LV_STYLE_SHADOW_WIDTH:
case LV_STYLE_SHADOW_OFFSET_X: case LV_STYLE_SHADOW_OFFSET_X:
case LV_STYLE_SHADOW_OFFSET_Y: case LV_STYLE_SHADOW_OFFSET_Y:
case LV_STYLE_SHADOW_SPREAD: 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_LETTER_SPACE:
case LV_STYLE_TEXT_LINE_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_WIDTH:
case LV_STYLE_LINE_DASH_WIDTH: case LV_STYLE_LINE_DASH_WIDTH:
case LV_STYLE_LINE_DASH_GAP: case LV_STYLE_LINE_DASH_GAP:
case LV_STYLE_ARC_OPA:
case LV_STYLE_ARC_WIDTH: 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_WIDTH:
case LV_STYLE_TRANSFORM_HEIGHT: case LV_STYLE_TRANSFORM_HEIGHT:
case LV_STYLE_TRANSLATE_X: case LV_STYLE_TRANSLATE_X:
@@ -1027,33 +1042,52 @@ static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop)
case LV_STYLE_TRANSFORM_ROTATION: case LV_STYLE_TRANSFORM_ROTATION:
case LV_STYLE_TRANSFORM_PIVOT_X: case LV_STYLE_TRANSFORM_PIVOT_X:
case LV_STYLE_TRANSFORM_PIVOT_Y: case LV_STYLE_TRANSFORM_PIVOT_Y:
case LV_STYLE_RECOLOR_OPA:
return STYLE_PROP_TYPE_INT; return STYLE_PROP_TYPE_INT;
case LV_STYLE_ARC_OPA:
case LV_STYLE_OPA:
case LV_STYLE_OPA_LAYERED:
case LV_STYLE_BG_OPA:
case LV_STYLE_BORDER_OPA:
case LV_STYLE_OUTLINE_OPA:
case LV_STYLE_SHADOW_OPA:
case LV_STYLE_TEXT_OPA:
case LV_STYLE_LINE_OPA:
case LV_STYLE_IMAGE_OPA:
case LV_STYLE_IMAGE_RECOLOR_OPA:
case LV_STYLE_RECOLOR_OPA:
case LV_STYLE_COLOR_FILTER_OPA:
return STYLE_PROP_TYPE_OPA;
case LV_STYLE_ARC_COLOR:
case LV_STYLE_BG_COLOR:
case LV_STYLE_BG_GRAD_COLOR:
case LV_STYLE_BORDER_COLOR:
case LV_STYLE_LINE_COLOR:
case LV_STYLE_OUTLINE_COLOR:
case LV_STYLE_SHADOW_COLOR:
case LV_STYLE_TEXT_COLOR:
return STYLE_PROP_TYPE_COLOR;
default: default:
return STYLE_PROP_TYPE_UNKNOWN; return STYLE_PROP_TYPE_UNKNOWN;
} }
} }
static int32_t anim_value_to_int(lv_style_prop_t prop_type, const char * value_str) static void anim_exec_cb(lv_anim_t * a, int32_t v)
{ {
if(prop_type == STYLE_PROP_TYPE_INT) { anim_data_t * anim_data = lv_anim_get_user_data(a);
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; lv_style_value_t style_value;
if(anim_data->prop_type == STYLE_PROP_TYPE_INT || anim_data->prop_type == STYLE_PROP_TYPE_OPA) {
style_value.num = v; style_value.num = v;
lv_obj_set_local_style_prop(a->var, prop, style_value, selector); }
else if(anim_data->prop_type == STYLE_PROP_TYPE_COLOR) {
style_value.color = lv_color_mix(anim_data->color_end, anim_data->color_start, v);
}
lv_obj_set_local_style_prop(a->var, anim_data->prop, style_value, anim_data->selector);
} }