diff --git a/docs/src/widgets/scale.rst b/docs/src/widgets/scale.rst index 3142baa4c8..18d933762f 100644 --- a/docs/src/widgets/scale.rst +++ b/docs/src/widgets/scale.rst @@ -283,6 +283,44 @@ The Style properties that are used during Scale drawing (and are thus useful) ar :LV_STYLE_TEXT_FONT: :cpp:func:`lv_style_set_text_font` +.. _lv_scale_needles: + +Needles +------- + +Needles are used to indicate a specific value for ``..._ROUND_...`` Scales only. +They can be lines or images and can be customized in terms of length, color, and +other properties. + + +.. _lv_scale_creating_needles: + +Creating Needles +~~~~~~~~~~~~~~~~ + +Create a :ref:`lv_line ` or a :ref:`lv_image ` Widget and then +attach it to the Scale as a needle with the appropriate function: + +- :cpp:expr:`lv_scale_set_line_needle_value(scale, needle_line, needle_length, value)` +- :cpp:expr:`lv_scale_set_image_needle_value(scale, needle_img, value)` + + +Data binding +~~~~~~~~~~~~ + +To get familiar with observers, subjects, and data bindings in general, visit the +:ref:`Observer ` page. + +This method of subscribing to an integer Subject affects the needle value of a Scale +Widget directly. Note that this is a one-way binding (Subject ==> Widget) so when +the subject changes, the Scale's needle will be updated too. + + +It supports only integer subjects. + +- :cpp:expr:`lv_scale_bind_line_needle_value(scale, needle_line, needle_length, &subject)` +- :cpp:expr:`lv_scale_bind_image_needle_value(scale, needle_img, &subject)` + .. _lv_scale_events: diff --git a/src/widgets/scale/lv_scale.c b/src/widgets/scale/lv_scale.c index 92c93355db..8d653a1476 100644 --- a/src/widgets/scale/lv_scale.c +++ b/src/widgets/scale/lv_scale.c @@ -32,6 +32,12 @@ /********************** * TYPEDEFS **********************/ +#if LV_USE_OBSERVER +typedef struct { + lv_obj_t * needle_line; + int32_t needle_length; +} bind_element_needle_t; +#endif /********************** * STATIC PROTOTYPES @@ -77,6 +83,8 @@ static void needle_deleted_cb(lv_event_t * e); #if LV_USE_OBSERVER static void scale_section_min_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject); static void scale_section_max_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject); + static void scale_line_needle_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject); + static void scale_image_needle_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject); #endif /*LV_USE_OBSERVER*/ /********************** @@ -609,6 +617,55 @@ lv_observer_t * lv_scale_bind_section_max_value(lv_obj_t * obj, lv_scale_section return observer; } +lv_observer_t * lv_scale_bind_line_needle_value(lv_obj_t * obj, lv_obj_t * needle_line, int32_t needle_length, + lv_subject_t * subject) +{ + LV_ASSERT_NULL(subject); + LV_ASSERT_OBJ(obj, MY_CLASS); + LV_ASSERT_NULL(needle_line); + + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Incompatible subject type: %d", subject->type); + return NULL; + } + + bind_element_needle_t * user_data = lv_zalloc(sizeof(bind_element_needle_t)); + if(user_data == NULL) { + LV_LOG_WARN("Couldn't allocate user_data"); + LV_ASSERT_MALLOC(user_data); + return NULL; + } + + user_data->needle_line = needle_line; + user_data->needle_length = needle_length; + + lv_observer_t * observer = lv_subject_add_observer_obj(subject, scale_line_needle_value_observer_cb, obj, user_data); + if(observer == NULL) { + LV_LOG_WARN("Couldn't create observer"); + lv_free(user_data); + return NULL; + } + observer->auto_free_user_data = 1; + + return observer; +} + +lv_observer_t * lv_scale_bind_image_needle_value(lv_obj_t * obj, lv_obj_t * needle_img, lv_subject_t * subject) +{ + LV_ASSERT_NULL(subject); + LV_ASSERT_OBJ(obj, MY_CLASS); + LV_ASSERT_NULL(needle_img); + + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Incompatible subject type: %d", subject->type); + return NULL; + } + + lv_observer_t * observer = lv_subject_add_observer_obj(subject, scale_image_needle_value_observer_cb, obj, needle_img); + + return observer; +} + #endif /*LV_USE_OBSERVER*/ /********************** @@ -1887,6 +1944,19 @@ static void scale_section_max_value_observer_cb(lv_observer_t * observer, lv_sub lv_scale_set_section_max_value(observer->target, section, subject->value.num); } +static void scale_line_needle_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject) +{ + bind_element_needle_t * bind_element = observer->user_data; + lv_scale_set_line_needle_value(observer->target, bind_element->needle_line, bind_element->needle_length, + subject->value.num); +} + +static void scale_image_needle_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject) +{ + lv_obj_t * needle_img = observer->user_data; + lv_scale_set_image_needle_value(observer->target, needle_img, subject->value.num); +} + #endif /*LV_USE_OBSERVER*/ diff --git a/src/widgets/scale/lv_scale.h b/src/widgets/scale/lv_scale.h index 8b1163ef0a..f754c0b117 100644 --- a/src/widgets/scale/lv_scale.h +++ b/src/widgets/scale/lv_scale.h @@ -383,6 +383,26 @@ lv_observer_t * lv_scale_bind_section_min_value(lv_obj_t * obj, lv_scale_section */ lv_observer_t * lv_scale_bind_section_max_value(lv_obj_t * obj, lv_scale_section_t * section, lv_subject_t * subject); +/** + * Bind an integer subject to a scale's line needle value + * @param obj pointer to a Scale + * @param needle_line pointer to a line needle + * @param needle_length length of the needle + * @param subject pointer to a Subject + * @return pointer to newly-created Observer + */ +lv_observer_t * lv_scale_bind_line_needle_value(lv_obj_t * obj, lv_obj_t * needle_line, int32_t needle_length, + lv_subject_t * subject); + +/** + * Bind an integer subject to a scale's image needle value + * @param obj pointer to a Scale + * @param needle_img pointer to an image needle + * @param subject pointer to a Subject + * @return pointer to newly-created Observer + */ +lv_observer_t * lv_scale_bind_image_needle_value(lv_obj_t * obj, lv_obj_t * needle_img, lv_subject_t * subject); + #endif /********************** diff --git a/tests/src/test_cases/test_observer.c b/tests/src/test_cases/test_observer.c index 5eb990fa58..4fe8065fba 100644 --- a/tests/src/test_cases/test_observer.c +++ b/tests/src/test_cases/test_observer.c @@ -996,6 +996,45 @@ void test_observer_dropdown_value(void) TEST_ASSERT_EQUAL(0, lv_subject_get_int(&subject)); } +void test_observer_scale_line_needle_value(void) +{ + lv_obj_t * obj = lv_scale_create(lv_screen_active()); + lv_scale_set_mode(obj, LV_SCALE_MODE_ROUND_INNER); + lv_scale_t * scale = (lv_scale_t *)obj; + lv_obj_t * needle = lv_line_create(obj); + + static lv_subject_t subject; + lv_subject_init_int(&subject, 30); + lv_scale_bind_line_needle_value(obj, needle, 50, &subject); + + lv_scale_needle_t * scale_needle = lv_array_at(&scale->needles, 0); + + TEST_ASSERT_EQUAL(30, scale_needle->value); + TEST_ASSERT_EQUAL(50, scale_needle->length); + + lv_subject_set_int(&subject, 40); + TEST_ASSERT_EQUAL(40, scale_needle->value); +} + +void test_observer_scale_image_needle_value(void) +{ + lv_obj_t * obj = lv_scale_create(lv_screen_active()); + lv_scale_set_mode(obj, LV_SCALE_MODE_ROUND_INNER); + lv_scale_t * scale = (lv_scale_t *)obj; + lv_obj_t * needle = lv_image_create(obj); + + static lv_subject_t subject; + lv_subject_init_int(&subject, 30); + lv_scale_bind_image_needle_value(obj, needle, &subject); + + lv_scale_needle_t * scale_needle = lv_array_at(&scale->needles, 0); + + TEST_ASSERT_EQUAL(30, scale_needle->value); + + lv_subject_set_int(&subject, 40); + TEST_ASSERT_EQUAL(40, scale_needle->value); +} + void test_observer_deinit(void) { static lv_subject_t subject;