fix(widgets): make animations on state change more consistent (#9174)
Co-authored-by: Liam Howatt <30486941+liamHowatt@users.noreply.github.com>
@@ -181,6 +181,9 @@ Special Events
|
|||||||
- :cpp:enumerator:`LV_EVENT_REFRESH`: Notify Widget to refresh something on it (for the user)
|
- :cpp:enumerator:`LV_EVENT_REFRESH`: Notify Widget to refresh something on it (for the user)
|
||||||
- :cpp:enumerator:`LV_EVENT_READY`: A process has finished
|
- :cpp:enumerator:`LV_EVENT_READY`: A process has finished
|
||||||
- :cpp:enumerator:`LV_EVENT_CANCEL`: A process has been cancelled
|
- :cpp:enumerator:`LV_EVENT_CANCEL`: A process has been cancelled
|
||||||
|
- :cpp:enumerator:`LV_EVENT_STATE_CHANGED`: The state of a widget has been changed.
|
||||||
|
E.g. :cpp:enumerator:`LV_STATE_PRESSED` was added. In the event :cpp:expr:`lv_event_get_prev_state(e)`
|
||||||
|
returns the previous state and :cpp:expr:`lv_obj_get_state(obj)` returns the current state.
|
||||||
|
|
||||||
|
|
||||||
Other Events
|
Other Events
|
||||||
|
|||||||
@@ -994,6 +994,7 @@ static void update_obj_state(lv_obj_t * obj, lv_state_t new_state)
|
|||||||
/*If there is no difference in styles there is nothing else to do*/
|
/*If there is no difference in styles there is nothing else to do*/
|
||||||
if(cmp_res == LV_STYLE_STATE_CMP_SAME) {
|
if(cmp_res == LV_STYLE_STATE_CMP_SAME) {
|
||||||
obj->state = new_state;
|
obj->state = new_state;
|
||||||
|
lv_obj_send_event(obj, LV_EVENT_STATE_CHANGED, &prev_state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,6 +1003,18 @@ static void update_obj_state(lv_obj_t * obj, lv_state_t new_state)
|
|||||||
|
|
||||||
obj->state = new_state;
|
obj->state = new_state;
|
||||||
lv_obj_update_layer_type(obj);
|
lv_obj_update_layer_type(obj);
|
||||||
|
|
||||||
|
/*Skip transitions if the widget is not rendered yet. */
|
||||||
|
if(!obj->rendered) {
|
||||||
|
lv_obj_invalidate(obj);
|
||||||
|
if(cmp_res == LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
|
||||||
|
lv_obj_refresh_ext_draw_size(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_send_event(obj, LV_EVENT_STATE_CHANGED, &prev_state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lv_obj_style_transition_dsc_t * ts = lv_malloc_zeroed(sizeof(lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
|
lv_obj_style_transition_dsc_t * ts = lv_malloc_zeroed(sizeof(lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
|
||||||
uint32_t tsi = 0;
|
uint32_t tsi = 0;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
@@ -1058,6 +1071,8 @@ static void update_obj_state(lv_obj_t * obj, lv_state_t new_state)
|
|||||||
lv_obj_invalidate(obj);
|
lv_obj_invalidate(obj);
|
||||||
lv_obj_refresh_ext_draw_size(obj);
|
lv_obj_refresh_ext_draw_size(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv_obj_send_event(obj, LV_EVENT_STATE_CHANGED, &prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -346,7 +346,18 @@ lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e)
|
|||||||
LV_LOG_WARN("Not interpreted with this event code");
|
LV_LOG_WARN("Not interpreted with this event code");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_state_t lv_event_get_prev_state(lv_event_t * e)
|
||||||
|
{
|
||||||
|
if(e->code == LV_EVENT_STATE_CHANGED) {
|
||||||
|
lv_state_t * state = lv_event_get_param(e);
|
||||||
|
return state ? *state : 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LV_LOG_WARN("Not interpreted with this event code");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ extern "C" {
|
|||||||
#include "../misc/lv_types.h"
|
#include "../misc/lv_types.h"
|
||||||
#include "../misc/lv_event.h"
|
#include "../misc/lv_event.h"
|
||||||
#include "../indev/lv_indev.h"
|
#include "../indev/lv_indev.h"
|
||||||
|
#include "lv_obj_style.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
@@ -193,6 +194,14 @@ void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
|
|||||||
*/
|
*/
|
||||||
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e);
|
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previous state before the state change.
|
||||||
|
* Can be used in `LV_EVENT_STATE_CHANGED` event
|
||||||
|
* @param e pointer to an event
|
||||||
|
* @return the previous state
|
||||||
|
*/
|
||||||
|
lv_state_t lv_event_get_prev_state(lv_event_t * e);
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* MACROS
|
* MACROS
|
||||||
**********************/
|
**********************/
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ struct _lv_obj_t {
|
|||||||
uint16_t h_layout : 1;
|
uint16_t h_layout : 1;
|
||||||
uint16_t w_layout : 1;
|
uint16_t w_layout : 1;
|
||||||
uint16_t is_deleting : 1;
|
uint16_t is_deleting : 1;
|
||||||
|
|
||||||
|
/** The widget is rendered at least once already.
|
||||||
|
* It's used to skip initial animations and transitions. */
|
||||||
|
uint16_t rendered : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
|
|||||||
@@ -856,7 +856,9 @@ lv_observer_t * lv_obj_bind_checked(lv_obj_t * obj, lv_subject_t * subject)
|
|||||||
{
|
{
|
||||||
lv_observer_t * observable = bind_to_bitfield(subject, obj, obj_state_observer_cb, LV_STATE_CHECKED, 0, true,
|
lv_observer_t * observable = bind_to_bitfield(subject, obj, obj_state_observer_cb, LV_STATE_CHECKED, 0, true,
|
||||||
FLAG_COND_EQ);
|
FLAG_COND_EQ);
|
||||||
|
|
||||||
lv_obj_add_event_cb(obj, obj_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
lv_obj_add_event_cb(obj, obj_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||||
|
|
||||||
return observable;
|
return observable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,6 +998,7 @@ static lv_observer_t * bind_to_bitfield(lv_subject_t * subject, lv_obj_t * obj,
|
|||||||
|
|
||||||
lv_observer_t * observable = lv_subject_add_observer_obj(subject, cb, obj, p);
|
lv_observer_t * observable = lv_subject_add_observer_obj(subject, cb, obj, p);
|
||||||
observable->auto_free_user_data = 1;
|
observable->auto_free_user_data = 1;
|
||||||
|
|
||||||
return observable;
|
return observable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj)
|
|||||||
lv_area_t clip_area_ori = layer->_clip_area;
|
lv_area_t clip_area_ori = layer->_clip_area;
|
||||||
lv_area_t clip_coords_for_obj;
|
lv_area_t clip_coords_for_obj;
|
||||||
|
|
||||||
|
/*The widget will be rendered.
|
||||||
|
*So setters from now should use animations. */
|
||||||
|
obj->rendered = 1;
|
||||||
|
|
||||||
/*Truncate the clip area to `obj size + ext size` area*/
|
/*Truncate the clip area to `obj size + ext size` area*/
|
||||||
lv_area_t obj_coords_ext;
|
lv_area_t obj_coords_ext;
|
||||||
lv_obj_get_coords(obj, &obj_coords_ext);
|
lv_obj_get_coords(obj, &obj_coords_ext);
|
||||||
@@ -258,6 +262,7 @@ void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
layer->_clip_area = clip_area_ori;
|
layer->_clip_area = clip_area_ori;
|
||||||
|
|
||||||
LV_PROFILER_REFR_END;
|
LV_PROFILER_REFR_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ const char * lv_event_code_get_name(lv_event_code_t code)
|
|||||||
ENUM_CASE(EVENT_REFRESH);
|
ENUM_CASE(EVENT_REFRESH);
|
||||||
ENUM_CASE(EVENT_READY);
|
ENUM_CASE(EVENT_READY);
|
||||||
ENUM_CASE(EVENT_CANCEL);
|
ENUM_CASE(EVENT_CANCEL);
|
||||||
|
ENUM_CASE(EVENT_STATE_CHANGED);
|
||||||
|
|
||||||
/** Other events*/
|
/** Other events*/
|
||||||
ENUM_CASE(EVENT_CREATE);
|
ENUM_CASE(EVENT_CREATE);
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ typedef enum {
|
|||||||
LV_EVENT_REFRESH, /**< Notify Widget to refresh something on it (for user)*/
|
LV_EVENT_REFRESH, /**< Notify Widget to refresh something on it (for user)*/
|
||||||
LV_EVENT_READY, /**< A process has finished */
|
LV_EVENT_READY, /**< A process has finished */
|
||||||
LV_EVENT_CANCEL, /**< A process has been cancelled */
|
LV_EVENT_CANCEL, /**< A process has been cancelled */
|
||||||
|
LV_EVENT_STATE_CHANGED, /**< The state of the widget changed*/
|
||||||
|
|
||||||
/** Other events */
|
/** Other events */
|
||||||
LV_EVENT_CREATE, /**< Object is being created */
|
LV_EVENT_CREATE, /**< Object is being created */
|
||||||
|
|||||||
@@ -737,12 +737,15 @@ static void lv_bar_init_anim(lv_obj_t * obj, lv_bar_anim_t * bar_anim)
|
|||||||
|
|
||||||
static void bar_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
|
static void bar_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
|
||||||
{
|
{
|
||||||
|
lv_obj_t * obj = lv_observer_get_target_obj(observer);
|
||||||
|
/*If the bar is not rendered yet show the new state immediately*/
|
||||||
|
lv_anim_enable_t anim_on = obj->rendered ? LV_ANIM_ON : LV_ANIM_OFF;
|
||||||
if(subject->type == LV_SUBJECT_TYPE_INT) {
|
if(subject->type == LV_SUBJECT_TYPE_INT) {
|
||||||
lv_bar_set_value(observer->target, subject->value.num, LV_ANIM_OFF);
|
lv_bar_set_value(observer->target, subject->value.num, anim_on);
|
||||||
}
|
}
|
||||||
#if LV_USE_FLOAT
|
#if LV_USE_FLOAT
|
||||||
else {
|
else {
|
||||||
lv_bar_set_value(observer->target, (int32_t)subject->value.float_v, LV_ANIM_OFF);
|
lv_bar_set_value(observer->target, (int32_t)subject->value.float_v, anim_on);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -964,8 +964,11 @@ static void roller_value_changed_event_cb(lv_event_t * e)
|
|||||||
|
|
||||||
static void roller_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
|
static void roller_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
|
||||||
{
|
{
|
||||||
|
/*If the roller is not rendered yet show the new state immediately*/
|
||||||
|
lv_obj_t * obj = lv_observer_get_target_obj(observer);
|
||||||
|
lv_anim_enable_t anim_on = obj->rendered ? LV_ANIM_ON : LV_ANIM_OFF;
|
||||||
if((int32_t)lv_roller_get_selected(observer->target) != subject->value.num) {
|
if((int32_t)lv_roller_get_selected(observer->target) != subject->value.num) {
|
||||||
lv_roller_set_selected(observer->target, subject->value.num, LV_ANIM_OFF);
|
lv_roller_set_selected(observer->target, subject->value.num, anim_on);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -234,7 +234,6 @@ lv_observer_t * lv_slider_bind_value(lv_obj_t * obj, lv_subject_t * subject)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_add_event_cb(obj, slider_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
lv_obj_add_event_cb(obj, slider_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||||
|
|
||||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, slider_value_observer_cb, obj, NULL);
|
lv_observer_t * observer = lv_subject_add_observer_obj(subject, slider_value_observer_cb, obj, NULL);
|
||||||
return observer;
|
return observer;
|
||||||
}
|
}
|
||||||
@@ -682,12 +681,15 @@ static void slider_value_changed_event_cb(lv_event_t * e)
|
|||||||
|
|
||||||
static void slider_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
|
static void slider_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
|
||||||
{
|
{
|
||||||
|
lv_obj_t * obj = lv_observer_get_target_obj(observer);
|
||||||
|
/*If the slider is not rendered yet show the new state immediately*/
|
||||||
|
lv_anim_enable_t anim_on = obj->rendered ? LV_ANIM_ON : LV_ANIM_OFF;
|
||||||
if(subject->type == LV_SUBJECT_TYPE_INT) {
|
if(subject->type == LV_SUBJECT_TYPE_INT) {
|
||||||
lv_slider_set_value(observer->target, subject->value.num, LV_ANIM_OFF);
|
lv_slider_set_value(observer->target, subject->value.num, anim_on);
|
||||||
}
|
}
|
||||||
#if LV_USE_FLOAT
|
#if LV_USE_FLOAT
|
||||||
else {
|
else {
|
||||||
lv_slider_set_value(observer->target, (int32_t)subject->value.float_v, LV_ANIM_OFF);
|
lv_slider_set_value(observer->target, (int32_t)subject->value.float_v, anim_on);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,10 +163,15 @@ static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
|||||||
*s = LV_MAX(*s, knob_size);
|
*s = LV_MAX(*s, knob_size);
|
||||||
*s = LV_MAX(*s, lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR));
|
*s = LV_MAX(*s, lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR));
|
||||||
}
|
}
|
||||||
else if(code == LV_EVENT_VALUE_CHANGED) {
|
else if(code == LV_EVENT_STATE_CHANGED) {
|
||||||
|
lv_state_t prev_state = lv_event_get_prev_state(e);
|
||||||
|
lv_state_t diff = prev_state ^ lv_obj_get_state(obj);
|
||||||
|
|
||||||
|
if(diff & LV_STATE_CHECKED) {
|
||||||
lv_switch_trigger_anim(obj);
|
lv_switch_trigger_anim(obj);
|
||||||
lv_obj_invalidate(obj);
|
lv_obj_invalidate(obj);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if(code == LV_EVENT_DRAW_MAIN) {
|
else if(code == LV_EVENT_DRAW_MAIN) {
|
||||||
draw_main(e);
|
draw_main(e);
|
||||||
}
|
}
|
||||||
@@ -296,6 +301,9 @@ static void lv_switch_anim_completed(lv_anim_t * a)
|
|||||||
static void lv_switch_trigger_anim(lv_obj_t * obj)
|
static void lv_switch_trigger_anim(lv_obj_t * obj)
|
||||||
{
|
{
|
||||||
LV_ASSERT_OBJ(obj, MY_CLASS);
|
LV_ASSERT_OBJ(obj, MY_CLASS);
|
||||||
|
/*If the widget is not rendered yet show state changes immediately*/
|
||||||
|
if(!obj->rendered) return;
|
||||||
|
|
||||||
lv_switch_t * sw = (lv_switch_t *)obj;
|
lv_switch_t * sw = (lv_switch_t *)obj;
|
||||||
|
|
||||||
uint32_t anim_dur_full = lv_obj_get_style_anim_duration(obj, LV_PART_MAIN);
|
uint32_t anim_dur_full = lv_obj_get_style_anim_duration(obj, LV_PART_MAIN);
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ lv_event_code_t lv_xml_trigger_text_to_enum_value(const char * txt)
|
|||||||
if(lv_streq("child_changed", txt)) return LV_EVENT_CHILD_CHANGED;
|
if(lv_streq("child_changed", txt)) return LV_EVENT_CHILD_CHANGED;
|
||||||
if(lv_streq("child_created", txt)) return LV_EVENT_CHILD_CREATED;
|
if(lv_streq("child_created", txt)) return LV_EVENT_CHILD_CREATED;
|
||||||
if(lv_streq("child_deleted", txt)) return LV_EVENT_CHILD_DELETED;
|
if(lv_streq("child_deleted", txt)) return LV_EVENT_CHILD_DELETED;
|
||||||
|
if(lv_streq("state_changed", txt)) return LV_EVENT_STATE_CHANGED;
|
||||||
if(lv_streq("screen_unload_start", txt)) return LV_EVENT_SCREEN_UNLOAD_START;
|
if(lv_streq("screen_unload_start", txt)) return LV_EVENT_SCREEN_UNLOAD_START;
|
||||||
if(lv_streq("screen_load_start", txt)) return LV_EVENT_SCREEN_LOAD_START;
|
if(lv_streq("screen_load_start", txt)) return LV_EVENT_SCREEN_LOAD_START;
|
||||||
if(lv_streq("screen_loaded", txt)) return LV_EVENT_SCREEN_LOADED;
|
if(lv_streq("screen_loaded", txt)) return LV_EVENT_SCREEN_LOADED;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -141,6 +141,8 @@ static void test_widgets(const char * img_name)
|
|||||||
|
|
||||||
lv_spinner_create(scr_act);
|
lv_spinner_create(scr_act);
|
||||||
|
|
||||||
|
lv_test_wait(1000); /*Wait for the transitions*/
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL_SCREENSHOT(img_name);
|
TEST_ASSERT_EQUAL_SCREENSHOT(img_name);
|
||||||
|
|
||||||
lv_obj_clean(scr_act);
|
lv_obj_clean(scr_act);
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ void test_screeshots(void)
|
|||||||
lv_obj_add_state(sw, LV_STATE_CHECKED);
|
lv_obj_add_state(sw, LV_STATE_CHECKED);
|
||||||
lv_obj_set_size(sw, 50, 100);
|
lv_obj_set_size(sw, 50, 100);
|
||||||
|
|
||||||
|
lv_test_wait(1000); /*Wait for the transitions*/
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/switch_1.png");
|
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/switch_1.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ void test_xml_switch_widget(void)
|
|||||||
|
|
||||||
lv_xml_create(scr, "lv_switch", attrs_1);
|
lv_xml_create(scr, "lv_switch", attrs_1);
|
||||||
|
|
||||||
|
lv_test_wait(1000); /*Wait for the transitions*/
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_switch.png");
|
TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_switch.png");
|
||||||
}
|
}
|
||||||
|
|||||||