diff --git a/docs/src/details/auxiliary-modules/observer/observer.rst b/docs/src/details/auxiliary-modules/observer/observer.rst index 6f2fb9111f..bbdfcab2a7 100644 --- a/docs/src/details/auxiliary-modules/observer/observer.rst +++ b/docs/src/details/auxiliary-modules/observer/observer.rst @@ -160,7 +160,17 @@ The following functions are used to get a Subject's previous value: :Pointer: const void * :cpp:expr:`lv_subject_get_previous_pointer(subject)` :Color: lv_color_t :cpp:expr:`lv_subject_get_previous_color(subject)` +Setting a Range for a Subject +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The range of the integer and float subjects can be limited by setting minimum and maximum values: + +- :cpp:expr:`lv_subject_set_min_value_int(subject, value)` +- :cpp:expr:`lv_subject_set_max_value_int(subject, value)` +- :cpp:expr:`lv_subject_set_min_value_float(subject, value)` +- :cpp:expr:`lv_subject_set_max_value_float(subject, value)` + +The default range is the smallest and largest possible values, so there is no limit by default. .. _observer_observer: @@ -469,21 +479,45 @@ To simplify this, *subject set* and *increment* actions can be attached directly Internally, these are implemented as special event callbacks. Note: these callbacks are **not** automatically removed when a subject is de-initialized. -Increment -~~~~~~~~~ -:cpp:expr:`lv_obj_add_subject_increment_event(obj, subject, step, min, max)` -Increments the subject's value by `step`, clamped between `min` and `max`. +Toggle +~~~~~~ + +:cpp:expr:`lv_obj_add_subject_toggle_event(obj, subject)` + +Toggle the subjects value when the trigger happens. IF the value was not 0, it will be 0. +If it was 0 it will be 1. For example: -:cpp:expr:`lv_obj_add_subject_increment_event(button1, subject1, LV_EVENT_CLICKED, 5, -10, 80)` +:cpp:expr:`lv_obj_add_subject_toggle_event(button1, subject1, LV_EVENT_CLICKED)` -This will increment `subject1` by 5 when `button1` is clicked. -The resulting value will be constrained to the range -10 to 80. +This will toggle `subject1` between 0 and 1 each time `button1` is clicked. + +Increment +~~~~~~~~~ + +:cpp:expr:`lv_obj_add_subject_increment_event(obj, subject, trigger, step, rollover)` +increments an integer subject's value by `step`. + +It works on both integer and float subject. + +``rollover`` can be ``true`` or ``false``. If ``true`` and the subject's minimum or maximum +value is exceeded, the other end value is set. That is, going beyond +the maximum value sets the minimum value, and vice versa. Using a negative `step` will decrement the value instead. + +For example: + +:cpp:expr:`lv_obj_add_subject_increment_event(button1, subject1, LV_EVENT_CLICKED, 5, false)` + +This will increment `subject1` by 5 when `button1` is clicked, stopping at the limits set by +:cpp:expr:`lv_subject_set_min_value_int()` and :cpp:expr:`lv_subject_set_max_value_int()` +(same for float subjects). + + Set to a Value ~~~~~~~~~~~~~~ diff --git a/src/others/observer/lv_observer.c b/src/others/observer/lv_observer.c index 6d328574f6..1cb34ca913 100644 --- a/src/others/observer/lv_observer.c +++ b/src/others/observer/lv_observer.c @@ -18,6 +18,10 @@ * DEFINES *********************/ +#ifndef FLT_MAX + #define FLT_MAX 3.402823466e+38F /* float max value */ +#endif + /********************** * TYPEDEFS **********************/ @@ -58,16 +62,15 @@ typedef struct { typedef struct { lv_subject_t * subject; int32_t step; - int32_t min; - int32_t max; + bool rollover; } subject_increment_user_data_t; /********************** * STATIC PROTOTYPES **********************/ +static void subject_toggle_cb(lv_event_t * e); static void subject_set_int_cb(lv_event_t * e); - #if LV_USE_FLOAT static void subject_set_float_cb(lv_event_t * e); #endif @@ -107,6 +110,8 @@ void lv_subject_init_int(lv_subject_t * subject, int32_t value) subject->type = LV_SUBJECT_TYPE_INT; subject->value.num = value; subject->prev_value.num = value; + subject->min_value.num = INT32_MIN; + subject->max_value.num = INT32_MAX; lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t)); } @@ -117,6 +122,8 @@ void lv_subject_set_int(lv_subject_t * subject, int32_t value) return; } + value = LV_CLAMP(subject->min_value.num, value, subject->max_value.num); + subject->prev_value.num = subject->value.num; subject->value.num = value; lv_subject_notify_if_changed(subject); @@ -142,6 +149,26 @@ int32_t lv_subject_get_previous_int(lv_subject_t * subject) return subject->prev_value.num; } +void lv_subject_set_min_value_int(lv_subject_t * subject, int32_t min_value) +{ + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_INT"); + return; + } + + subject->min_value.num = min_value; +} + +void lv_subject_set_max_value_int(lv_subject_t * subject, int32_t max_value) +{ + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_INT"); + return; + } + + subject->max_value.num = max_value; +} + #if LV_USE_FLOAT void lv_subject_init_float(lv_subject_t * subject, float value) @@ -150,6 +177,8 @@ void lv_subject_init_float(lv_subject_t * subject, float value) subject->type = LV_SUBJECT_TYPE_FLOAT; subject->value.float_v = value; subject->prev_value.float_v = value; + subject->min_value.float_v = -FLT_MAX; + subject->max_value.float_v = FLT_MAX; lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t)); } @@ -160,6 +189,8 @@ void lv_subject_set_float(lv_subject_t * subject, float value) return; } + value = LV_CLAMP(subject->min_value.float_v, value, subject->max_value.float_v); + subject->prev_value.float_v = subject->value.float_v; subject->value.float_v = value; lv_subject_notify_if_changed(subject); @@ -185,6 +216,27 @@ float lv_subject_get_previous_float(lv_subject_t * subject) return subject->prev_value.float_v; } +void lv_subject_set_min_value_float(lv_subject_t * subject, float min_value) +{ + if(subject->type != LV_SUBJECT_TYPE_FLOAT) { + LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_FLOAT"); + return; + } + + subject->min_value.float_v = min_value; +} + +void lv_subject_set_max_value_float(lv_subject_t * subject, float max_value) +{ + if(subject->type != LV_SUBJECT_TYPE_FLOAT) { + LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_FLOAT"); + return; + } + + subject->max_value.float_v = max_value; +} + + #endif /*LV_USE_FLOAT*/ void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value) @@ -525,8 +577,13 @@ void lv_subject_notify(lv_subject_t * subject) } void lv_obj_add_subject_increment_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, int32_t step, - int32_t min, int32_t max) + bool rollover) { + if(subject->type != LV_SUBJECT_TYPE_INT && subject->type != LV_SUBJECT_TYPE_FLOAT) { + LV_LOG_WARN("Subject type must be `int` or `float` (was %d)", subject->type); + return; + } + subject_increment_user_data_t * user_data = lv_malloc(sizeof(subject_increment_user_data_t)); if(user_data == NULL) { LV_ASSERT_MALLOC(user_data); @@ -535,15 +592,28 @@ void lv_obj_add_subject_increment_event(lv_obj_t * obj, lv_subject_t * subject, } user_data->step = step; - user_data->min = min; - user_data->max = max; user_data->subject = subject; + user_data->rollover = rollover; lv_obj_add_event_cb(obj, subject_increment_cb, trigger, user_data); lv_obj_add_event_cb(obj, lv_event_free_user_data_cb, LV_EVENT_DELETE, user_data); } +void lv_obj_add_subject_toggle_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger) +{ + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Subject type must be `int` (was %d)", subject->type); + return; + } + lv_obj_add_event_cb(obj, subject_toggle_cb, trigger, subject); +} + void lv_obj_add_subject_set_int_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, int32_t value) { + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Subject type must be `int` (was %d)", subject->type); + return; + } + subject_set_int_user_data_t * user_data = lv_malloc(sizeof(subject_set_int_user_data_t)); if(user_data == NULL) { LV_ASSERT_MALLOC(user_data); @@ -561,6 +631,11 @@ void lv_obj_add_subject_set_int_event(lv_obj_t * obj, lv_subject_t * subject, lv #if LV_USE_FLOAT void lv_obj_add_subject_set_float_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, float value) { + if(subject->type != LV_SUBJECT_TYPE_FLOAT) { + LV_LOG_WARN("Subject type must be `float` (was %d)", subject->type); + return; + } + subject_set_float_user_data_t * user_data = lv_malloc(sizeof(subject_set_float_user_data_t)); if(user_data == NULL) { LV_ASSERT_MALLOC(user_data); @@ -579,6 +654,12 @@ void lv_obj_add_subject_set_float_event(lv_obj_t * obj, lv_subject_t * subject, void lv_obj_add_subject_set_string_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, const char * value) { + + if(subject->type != LV_SUBJECT_TYPE_STRING) { + LV_LOG_WARN("Subject type must be `string` (was %d)", subject->type); + return; + } + subject_set_string_user_data_t * user_data = lv_malloc(sizeof(subject_set_int_user_data_t)); if(user_data == NULL) { LV_ASSERT_MALLOC(user_data); @@ -601,7 +682,7 @@ lv_observer_t * lv_obj_bind_style(lv_obj_t * obj, const lv_style_t * style, lv_s LV_ASSERT_NULL(obj); if(subject->type != LV_SUBJECT_TYPE_INT) { - LV_LOG_WARN("Incompatible subject type: %d", subject->type); + LV_LOG_WARN("Subject type must be `int` (was %d)", subject->type); return NULL; } @@ -729,7 +810,6 @@ lv_obj_t * lv_observer_get_target_obj(lv_observer_t * observer) void * lv_observer_get_user_data(const lv_observer_t * observer) { LV_ASSERT_NULL(observer); - return observer->user_data; } @@ -737,6 +817,14 @@ void * lv_observer_get_user_data(const lv_observer_t * observer) * STATIC FUNCTIONS **********************/ +static void subject_toggle_cb(lv_event_t * e) +{ + lv_subject_t * subject = lv_event_get_user_data(e); + int32_t v = lv_subject_get_int(subject); + v = !v; + + lv_subject_set_int(subject, v); +} static void subject_set_int_cb(lv_event_t * e) { @@ -744,6 +832,7 @@ static void subject_set_int_cb(lv_event_t * e) lv_subject_set_int(user_data->subject, user_data->value); } + #if LV_USE_FLOAT static void subject_set_float_cb(lv_event_t * e) { @@ -762,19 +851,36 @@ static void subject_increment_cb(lv_event_t * e) { subject_increment_user_data_t * user_data = lv_event_get_user_data(e); - if(user_data->subject->type == LV_SUBJECT_TYPE_INT) { int32_t value = lv_subject_get_int(user_data->subject); value += user_data->step; - value = LV_CLAMP(user_data->min, value, user_data->max); + + if(user_data->rollover) { + if(value > user_data->subject->max_value.num) { + value = user_data->subject->min_value.num; + } + else if(value < user_data->subject->min_value.num) { + value = user_data->subject->max_value.num; + } + } + lv_subject_set_int(user_data->subject, value); } #if LV_USE_FLOAT else if(user_data->subject->type == LV_SUBJECT_TYPE_FLOAT) { float value = lv_subject_get_float(user_data->subject); + + if(user_data->rollover) { + if(value > user_data->subject->max_value.float_v) { + value = user_data->subject->min_value.float_v; + } + else if(value < user_data->subject->min_value.float_v) { + value = user_data->subject->max_value.float_v; + } + } + value += (float)user_data->step; - value = LV_CLAMP(user_data->min, value, user_data->max); - lv_subject_set_float(user_data->subject, (float)value); + lv_subject_set_float(user_data->subject, value); } #endif } diff --git a/src/others/observer/lv_observer.h b/src/others/observer/lv_observer.h index d2eeb67213..735cf307a3 100644 --- a/src/others/observer/lv_observer.h +++ b/src/others/observer/lv_observer.h @@ -58,6 +58,8 @@ typedef struct { lv_ll_t subs_ll; /**< Subscribers */ lv_subject_value_t value; /**< Current value */ lv_subject_value_t prev_value; /**< Previous value */ + lv_subject_value_t min_value; /**< Minimum value for min. int or float*/ + lv_subject_value_t max_value; /**< Maximum value for max. int or float*/ void * user_data; /**< Additional parameter, can be used freely by user */ uint32_t type : 4; /**< One of the LV_SUBJECT_TYPE_... values */ uint32_t size : 24; /**< String buffer size or group length */ @@ -104,6 +106,21 @@ int32_t lv_subject_get_int(lv_subject_t * subject); */ int32_t lv_subject_get_previous_int(lv_subject_t * subject); + +/** + * Set a minimum value for an integer subject + * @param subject pointer to Subject + * @param min_value the minimum value + */ +void lv_subject_set_min_value_int(lv_subject_t * subject, int32_t min_value); + +/** + * Set a maximum value for an integer subject + * @param subject pointer to Subject + * @param max_value the maximum value + */ +void lv_subject_set_max_value_int(lv_subject_t * subject, int32_t max_value); + #if LV_USE_FLOAT /** @@ -134,6 +151,20 @@ float lv_subject_get_float(lv_subject_t * subject); */ float lv_subject_get_previous_float(lv_subject_t * subject); +/** + * Set a minimum value for a float subject + * @param subject pointer to Subject + * @param min_value the minimum value + */ +void lv_subject_set_min_value_float(lv_subject_t * subject, float min_value); + +/** + * Set a maximum value for a float subject + * @param subject pointer to Subject + * @param max_value the maximum value + */ +void lv_subject_set_max_value_float(lv_subject_t * subject, float max_value); + #endif /*LV_USE_FLOAT*/ /** @@ -348,11 +379,20 @@ void lv_subject_notify(lv_subject_t * subject); * @param subject pointer to a subject to change * @param trigger the trigger on which the subject should be changed * @param step value to add on trigger - * @param min the minimum value - * @param max the maximum value + * @param rollover if true and the subject's maximum value is exceeded the minimum value is set, + * if the minimum value is reached, the maximum value will be set on rollover. */ void lv_obj_add_subject_increment_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, int32_t step, - int32_t min, int32_t max); + bool rollover); + +/** + * Toggle the value of an integer subject on an event. If it was != 0 it will be 0. + * If it was 0, it will be 1. + * @param obj pointer to a widget + * @param subject pointer to a subject to toggle + * @param trigger the trigger on which the subject should be changed + */ +void lv_obj_add_subject_toggle_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger); /** * Set the value of an integer subject. diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index ecb8ae27c9..0b591c03a6 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -208,6 +208,8 @@ void lv_xml_init(void) lv_obj_xml_remove_style_all_apply); lv_xml_widget_register("lv_obj-event_cb", lv_obj_xml_event_cb_create, lv_obj_xml_event_cb_apply); + lv_xml_widget_register("lv_obj-subject_toggle_event", lv_obj_xml_subject_toggle_create, + lv_obj_xml_subject_toggle_apply); lv_xml_widget_register("lv_obj-subject_set_int_event", lv_obj_xml_subject_set_create, lv_obj_xml_subject_set_apply); lv_xml_widget_register("lv_obj-subject_set_float_event", lv_obj_xml_subject_set_create, lv_obj_xml_subject_set_apply); lv_xml_widget_register("lv_obj-subject_set_string_event", lv_obj_xml_subject_set_create, lv_obj_xml_subject_set_apply); diff --git a/src/others/xml/parsers/lv_xml_obj_parser.c b/src/others/xml/parsers/lv_xml_obj_parser.c index 3ecab86b53..d43fe314a1 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.c +++ b/src/others/xml/parsers/lv_xml_obj_parser.c @@ -277,6 +277,41 @@ void lv_obj_xml_event_cb_apply(lv_xml_parser_state_t * state, const char ** attr if(user_data) lv_obj_add_event_cb(obj, lv_event_free_user_data_cb, LV_EVENT_DELETE, user_data); } +void * lv_obj_xml_subject_toggle_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_subject_toggle_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + /*If the tag_name is */ + const char * subject_str = lv_xml_get_value_of(attrs, "subject"); + const char * trigger_str = lv_xml_get_value_of(attrs, "trigger"); + + if(subject_str == NULL) { + LV_LOG_WARN("`subject` is missing in "); + return; + } + + lv_event_code_t trigger = LV_EVENT_CLICKED; + if(trigger_str) 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; + } + + lv_subject_t * subject = lv_xml_get_subject(&state->scope, subject_str); + if(subject == NULL) { + LV_LOG_WARN("Subject `%s` doesn't exist in ", subject_str); + return; + } + + void * item = lv_xml_state_get_item(state); + lv_obj_add_subject_toggle_event(item, subject, trigger); +} + void * lv_obj_xml_subject_set_create(lv_xml_parser_state_t * state, const char ** attrs) { LV_UNUSED(attrs); @@ -368,8 +403,7 @@ void lv_obj_xml_subject_increment_apply(lv_xml_parser_state_t * state, const cha const char * subject_str = lv_xml_get_value_of(attrs, "subject"); const char * trigger_str = lv_xml_get_value_of(attrs, "trigger"); const char * step_str = lv_xml_get_value_of(attrs, "step"); - const char * min_str = lv_xml_get_value_of(attrs, "min"); - const char * max_str = lv_xml_get_value_of(attrs, "max"); + const char * rollover_str = lv_xml_get_value_of(attrs, "rollover"); if(subject_str == NULL) { LV_LOG_WARN("`subject` is missing in "); @@ -377,11 +411,12 @@ void lv_obj_xml_subject_increment_apply(lv_xml_parser_state_t * state, const cha } if(step_str == NULL) step_str = "1"; + if(rollover_str == NULL) rollover_str = "false"; lv_event_code_t trigger = LV_EVENT_CLICKED; if(trigger_str) 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); + LV_LOG_WARN("Couldn't apply because `%s` trigger is invalid.", trigger_str); return; } @@ -399,9 +434,8 @@ void lv_obj_xml_subject_increment_apply(lv_xml_parser_state_t * state, const cha void * item = lv_xml_state_get_item(state); int32_t step = lv_xml_atoi(step_str); - int32_t min_v = min_str ? lv_xml_atoi(min_str) : INT32_MIN; - int32_t max_v = max_str ? lv_xml_atoi(max_str) : INT32_MAX; - lv_obj_add_subject_increment_event(item, subject, trigger, step, min_v, max_v); + bool rollover = lv_xml_to_bool(rollover_str); + lv_obj_add_subject_increment_event(item, subject, trigger, step, rollover); } void * lv_obj_xml_bind_style_create(lv_xml_parser_state_t * state, const char ** attrs) diff --git a/src/others/xml/parsers/lv_xml_obj_parser.h b/src/others/xml/parsers/lv_xml_obj_parser.h index 86d64d5e01..618fe86a1f 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.h +++ b/src/others/xml/parsers/lv_xml_obj_parser.h @@ -39,6 +39,9 @@ void lv_obj_xml_remove_style_all_apply(lv_xml_parser_state_t * state, const char void * lv_obj_xml_event_cb_create(lv_xml_parser_state_t * state, const char ** attrs); void lv_obj_xml_event_cb_apply(lv_xml_parser_state_t * state, const char ** attrs); +void * lv_obj_xml_subject_toggle_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_obj_xml_subject_toggle_apply(lv_xml_parser_state_t * state, const char ** attrs); + void * lv_obj_xml_subject_set_create(lv_xml_parser_state_t * state, const char ** attrs); void lv_obj_xml_subject_set_apply(lv_xml_parser_state_t * state, const char ** attrs); diff --git a/xmls/lv_obj.xml b/xmls/lv_obj.xml index 71d3cc1d91..ff6d1e79e9 100644 --- a/xmls/lv_obj.xml +++ b/xmls/lv_obj.xml @@ -88,6 +88,11 @@ Example + + + + + @@ -110,8 +115,7 @@ Example - - +