diff --git a/include/lvgl/core/lv_obj_property_names.h b/include/lvgl/core/lv_obj_property_names.h index 7384687773..5745705dc6 100644 --- a/include/lvgl/core/lv_obj_property_names.h +++ b/include/lvgl/core/lv_obj_property_names.h @@ -19,7 +19,7 @@ extern const lv_property_name_t lv_dropdown_property_names[9]; extern const lv_property_name_t lv_image_property_names[11]; extern const lv_property_name_t lv_keyboard_property_names[4]; - extern const lv_property_name_t lv_label_property_names[4]; + extern const lv_property_name_t lv_label_property_names[5]; extern const lv_property_name_t lv_led_property_names[2]; extern const lv_property_name_t lv_line_property_names[1]; extern const lv_property_name_t lv_menu_property_names[2]; diff --git a/include/lvgl/widgets/lv_label.h b/include/lvgl/widgets/lv_label.h index 33035ec132..52c15058f6 100644 --- a/include/lvgl/widgets/lv_label.h +++ b/include/lvgl/widgets/lv_label.h @@ -58,8 +58,9 @@ typedef enum { enum _lv_property_label_id_t { LV_PROPERTY_ID(LABEL, TEXT, LV_PROPERTY_TYPE_TEXT, 0), LV_PROPERTY_ID(LABEL, LONG_MODE, LV_PROPERTY_TYPE_INT, 1), - LV_PROPERTY_ID(LABEL, TEXT_SELECTION_START, LV_PROPERTY_TYPE_INT, 2), - LV_PROPERTY_ID(LABEL, TEXT_SELECTION_END, LV_PROPERTY_TYPE_INT, 3), + LV_PROPERTY_ID(LABEL, MAX_LINES, LV_PROPERTY_TYPE_INT, 2), + LV_PROPERTY_ID(LABEL, TEXT_SELECTION_START, LV_PROPERTY_TYPE_INT, 3), + LV_PROPERTY_ID(LABEL, TEXT_SELECTION_END, LV_PROPERTY_TYPE_INT, 4), LV_PROPERTY_LABEL_END, }; #endif @@ -136,6 +137,14 @@ void lv_label_set_text_static(lv_obj_t * obj, const char * text); */ void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode); +/** + * Set the maximum number of lines that the label should display in + * `LV_LABEL_LONG_MODE_WRAP` and `LV_LABEL_LONG_MODE_DOTS` mode. + * @param obj pointer to a label object + * @param lines number of lines to display (unlimited if not positive) + */ +void lv_label_set_max_lines(lv_obj_t * obj, int32_t lines); + /** * Set where text selection should start * @param obj pointer to a label object @@ -189,6 +198,13 @@ char * lv_label_get_text(const lv_obj_t * obj); */ lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * obj); +/** + * Get the maximum number of lines that a label should display + * @param obj pointer to a label object + * @return the maximum number of lines, if positive + */ +int32_t lv_label_get_max_lines(const lv_obj_t * obj); + /** * Get the relative x and y coordinates of a letter * @param obj pointer to a label object diff --git a/scripts/gdb/lvglgdb/lvgl/widgets/lv_label.py b/scripts/gdb/lvglgdb/lvgl/widgets/lv_label.py index 28a94be0fb..f7fc8b6f50 100644 --- a/scripts/gdb/lvglgdb/lvgl/widgets/lv_label.py +++ b/scripts/gdb/lvglgdb/lvgl/widgets/lv_label.py @@ -29,6 +29,10 @@ class LVLabel(LVObject): """Offset where bytes have been replaced with dots""" return int(self._wv.safe_field("dot_begin", 0)) + @property + def max_lines(self): + return int(self._wv.safe_field("max_lines", 0)) + @property def sel_start(self): return int(self._wv.safe_field("sel_start", 0)) @@ -88,6 +92,7 @@ class LVLabel(LVObject): d["text"] = self.text d["translation_tag"] = self.translation_tag d["dot_begin"] = self.dot_begin + d["max_lines"] = self.max_lines d["sel_start"] = self.sel_start d["sel_end"] = self.sel_end d["size_cache"] = self.size_cache diff --git a/src/widgets/label/lv_label.c b/src/widgets/label/lv_label.c index 5962b331e1..b5a6a11b54 100644 --- a/src/widgets/label/lv_label.c +++ b/src/widgets/label/lv_label.c @@ -76,6 +76,11 @@ static const lv_property_ops_t lv_label_properties[] = { .setter = lv_label_set_long_mode, .getter = lv_label_get_long_mode, }, + { + .id = LV_PROPERTY_LABEL_MAX_LINES, + .setter = lv_label_set_max_lines, + .getter = lv_label_get_max_lines, + }, { .id = LV_PROPERTY_LABEL_TEXT_SELECTION_START, .setter = lv_label_set_text_selection_start, @@ -225,6 +230,14 @@ void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode) lv_label_mark_need_refr_text(obj); } +void lv_label_set_max_lines(lv_obj_t * obj, int32_t lines) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_label_t * label = (lv_label_t *)obj; + label->max_lines = lines; + lv_label_refr_text(obj); +} + void lv_label_set_text_selection_start(lv_obj_t * obj, uint32_t index) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -284,6 +297,13 @@ lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * obj) return label->long_mode; } +int32_t lv_label_get_max_lines(const lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + const lv_label_t * label = (const lv_label_t *)obj; + return label->max_lines; +} + void lv_label_get_letter_pos(const lv_obj_t * obj, uint32_t char_id, lv_point_t * pos) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -840,6 +860,11 @@ static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_text_get_size_attributes(&label->size_cache, label->text, font, &attributes); lv_label_set_dots(obj, dot_begin); + if(label->max_lines > 0) { + label->size_cache.y = LV_MIN(label->size_cache.y, + lv_font_get_line_height(font) * label->max_lines + line_space * (label->max_lines - 1)); + } + lv_text_leading_trim_t leading_trim = lv_obj_get_style_text_leading_trim(obj, LV_PART_MAIN); if(leading_trim != LV_TEXT_LEADING_TRIM_NONE) { diff --git a/src/widgets/label/lv_label_private.h b/src/widgets/label/lv_label_private.h index 7178403f16..a3cd144c59 100644 --- a/src/widgets/label/lv_label_private.h +++ b/src/widgets/label/lv_label_private.h @@ -36,6 +36,7 @@ struct _lv_label_t { #endif /*LV_USE_TRANSLATION*/ char dot[LV_LABEL_DOT_NUM + 1]; /**< Bytes that have been replaced with dots */ uint32_t dot_begin; /**< Offset where bytes have been replaced with dots */ + int32_t max_lines; #if LV_LABEL_LONG_TXT_HINT lv_draw_label_hint_t hint; diff --git a/src/widgets/property/lv_label_properties.c b/src/widgets/property/lv_label_properties.c index ad4d1532ac..d018cd895e 100644 --- a/src/widgets/property/lv_label_properties.c +++ b/src/widgets/property/lv_label_properties.c @@ -14,8 +14,9 @@ * Generated code from properties.py */ /* *INDENT-OFF* */ -const lv_property_name_t lv_label_property_names[4] = { +const lv_property_name_t lv_label_property_names[5] = { {"long_mode", LV_PROPERTY_LABEL_LONG_MODE,}, + {"max_lines", LV_PROPERTY_LABEL_MAX_LINES,}, {"text", LV_PROPERTY_LABEL_TEXT,}, {"text_selection_end", LV_PROPERTY_LABEL_TEXT_SELECTION_END,}, {"text_selection_start", LV_PROPERTY_LABEL_TEXT_SELECTION_START,}, diff --git a/tests/ref_imgs/widgets/label_max_lines.png b/tests/ref_imgs/widgets/label_max_lines.png new file mode 100644 index 0000000000..b773adca39 Binary files /dev/null and b/tests/ref_imgs/widgets/label_max_lines.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/label_max_lines.png b/tests/ref_imgs_vg_lite/widgets/label_max_lines.png new file mode 100644 index 0000000000..962e898ebd Binary files /dev/null and b/tests/ref_imgs_vg_lite/widgets/label_max_lines.png differ diff --git a/tests/src/test_cases/widgets/test_label.c b/tests/src/test_cases/widgets/test_label.c index d2445da6c2..3fcc856ef0 100644 --- a/tests/src/test_cases/widgets/test_label.c +++ b/tests/src/test_cases/widgets/test_label.c @@ -773,6 +773,34 @@ void test_label_long_mode_clip(void) TEST_ASSERT_EQUAL_SCREENSHOT(buf); } +void test_label_max_lines(void) +{ + lv_obj_clean(lv_screen_active()); + lv_obj_t * parent = lv_obj_create(lv_screen_active()); + lv_obj_set_size(parent, LV_PCT(100), LV_PCT(100)); + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(parent, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_text_line_space(parent, 10, LV_PART_MAIN); + + const char * texts[] = { + "Fits in single line", + "Length such that exactly three lines will be needed for this text", + "Text\nwith line breaks\nwhere display will need more than three lines", + }; + + for(int i = 0; i < 3; i++) { + lv_obj_t * test_label = lv_label_create(parent); + lv_obj_set_width(test_label, 200); + lv_obj_set_style_bg_color(test_label, lv_palette_main(LV_PALETTE_GREY), LV_PART_MAIN); + lv_obj_set_style_bg_opa(test_label, LV_OPA_100, LV_PART_MAIN); + lv_label_set_long_mode(test_label, LV_LABEL_LONG_MODE_DOTS); + lv_label_set_text(test_label, texts[i]); + lv_label_set_max_lines(test_label, 3); + } + + TEST_ASSERT_EQUAL_SCREENSHOT("widgets/label_max_lines.png"); +} + void test_label_wrap_mode_clip(void) { lv_obj_clean(lv_screen_active());