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_READY`: A process has finished
|
||||
- :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
|
||||
@@ -265,11 +268,11 @@ contains all data about the event. The following values can be retrieved from it
|
||||
- :cpp:expr:`lv_event_get_param(e)`: get the parameter passed as the last parameter of :cpp:func:`lv_obj_send_event_cb`
|
||||
|
||||
.. tip::
|
||||
When using C++, prefer :cpp:expr:`lv_event_get_target_obj(e)` over :cpp:expr:`lv_event_get_target(e)`
|
||||
When using C++, prefer :cpp:expr:`lv_event_get_target_obj(e)` over :cpp:expr:`lv_event_get_target(e)`
|
||||
when you know the target is a Widget, as it returns the correct type without requiring a cast.
|
||||
|
||||
.. warning::
|
||||
Only call :cpp:expr:`lv_event_get_target_obj(e)` when the event target is known to be a Widget.
|
||||
Only call :cpp:expr:`lv_event_get_target_obj(e)` when the event target is known to be a Widget.
|
||||
Calling it for Display or Indev targets is considered Undefined Behavior.
|
||||
|
||||
|
||||
|
||||
@@ -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(cmp_res == LV_STYLE_STATE_CMP_SAME) {
|
||||
obj->state = new_state;
|
||||
lv_obj_send_event(obj, LV_EVENT_STATE_CHANGED, &prev_state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1002,6 +1003,18 @@ static void update_obj_state(lv_obj_t * obj, lv_state_t new_state)
|
||||
|
||||
obj->state = new_state;
|
||||
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);
|
||||
uint32_t tsi = 0;
|
||||
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_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");
|
||||
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_event.h"
|
||||
#include "../indev/lv_indev.h"
|
||||
#include "lv_obj_style.h"
|
||||
|
||||
/*********************
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
**********************/
|
||||
|
||||
@@ -77,6 +77,10 @@ struct _lv_obj_t {
|
||||
uint16_t h_layout : 1;
|
||||
uint16_t w_layout : 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,
|
||||
FLAG_COND_EQ);
|
||||
|
||||
lv_obj_add_event_cb(obj, obj_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||
|
||||
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);
|
||||
observable->auto_free_user_data = 1;
|
||||
|
||||
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_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*/
|
||||
lv_area_t 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;
|
||||
|
||||
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_READY);
|
||||
ENUM_CASE(EVENT_CANCEL);
|
||||
ENUM_CASE(EVENT_STATE_CHANGED);
|
||||
|
||||
/** Other events*/
|
||||
ENUM_CASE(EVENT_CREATE);
|
||||
|
||||
@@ -78,6 +78,7 @@ typedef enum {
|
||||
LV_EVENT_REFRESH, /**< Notify Widget to refresh something on it (for user)*/
|
||||
LV_EVENT_READY, /**< A process has finished */
|
||||
LV_EVENT_CANCEL, /**< A process has been cancelled */
|
||||
LV_EVENT_STATE_CHANGED, /**< The state of the widget changed*/
|
||||
|
||||
/** Other events */
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
/*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) {
|
||||
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_observer_t * observer = lv_subject_add_observer_obj(subject, slider_value_observer_cb, obj, NULL);
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -163,9 +163,14 @@ 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, lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR));
|
||||
}
|
||||
else if(code == LV_EVENT_VALUE_CHANGED) {
|
||||
lv_switch_trigger_anim(obj);
|
||||
lv_obj_invalidate(obj);
|
||||
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_obj_invalidate(obj);
|
||||
}
|
||||
}
|
||||
else if(code == LV_EVENT_DRAW_MAIN) {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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_created", txt)) return LV_EVENT_CHILD_CREATED;
|
||||
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_load_start", txt)) return LV_EVENT_SCREEN_LOAD_START;
|
||||
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_test_wait(1000); /*Wait for the transitions*/
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT(img_name);
|
||||
|
||||
lv_obj_clean(scr_act);
|
||||
|
||||
@@ -156,6 +156,8 @@ void test_screeshots(void)
|
||||
lv_obj_add_state(sw, LV_STATE_CHECKED);
|
||||
lv_obj_set_size(sw, 50, 100);
|
||||
|
||||
lv_test_wait(1000); /*Wait for the transitions*/
|
||||
|
||||
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_test_wait(1000); /*Wait for the transitions*/
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_switch.png");
|
||||
}
|
||||
|
||||