feat(arclabel): support lv_arclabel_get_text_angle to get real rendered text size in degree (#9546)

This commit is contained in:
Benign X
2026-01-09 16:22:44 +08:00
committed by GitHub
parent 61b0a529e1
commit d0ce7258a8
3 changed files with 83 additions and 36 deletions
+67 -35
View File
@@ -51,6 +51,8 @@ static void lv_arclabel_event(const lv_obj_class_t * class_p, lv_event_t * e);
static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_font_t * font, uint32_t radius, static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_font_t * font, uint32_t radius,
const lv_value_precise_t angle_size, int32_t letter_space, bool recolor, const lv_value_precise_t angle_size, int32_t letter_space, bool recolor,
const lv_arclabel_overflow_t overflow, bool end_overlap, bool * need_ellipsis, uint32_t * letter_count); const lv_arclabel_overflow_t overflow, bool end_overlap, bool * need_ellipsis, uint32_t * letter_count);
static lv_value_precise_t arclabel_calc_arc_text_total_angle(lv_obj_t * obj, int32_t * arc_radius, bool * need_ellipsis,
uint32_t * letter_count);
static const char * recolor_cmd_get_next(const char * text_in, uint32_t len_in, static const char * recolor_cmd_get_next(const char * text_in, uint32_t len_in,
const char ** text_out, uint32_t * len_out, const char ** text_out, uint32_t * len_out,
lv_color_t * color_out); lv_color_t * color_out);
@@ -356,6 +358,13 @@ bool lv_arclabel_get_end_overlap(lv_obj_t * obj)
return ((lv_arclabel_t *) obj)->end_overlap; return ((lv_arclabel_t *) obj)->end_overlap;
} }
lv_value_precise_t lv_arclabel_get_text_angle(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return arclabel_calc_arc_text_total_angle(obj, NULL, NULL, NULL);
}
/*===================== /*=====================
* Other functions * Other functions
*====================*/ *====================*/
@@ -418,42 +427,14 @@ static void arclabel_draw_main(lv_event_t * e)
const lv_opa_t opa = LV_OPA_MIX2(layer->opa, lv_obj_get_style_text_opa(obj, LV_PART_MAIN)); const lv_opa_t opa = LV_OPA_MIX2(layer->opa, lv_obj_get_style_text_opa(obj, LV_PART_MAIN));
const int32_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN); const int32_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
const int32_t line_height = font->line_height; int32_t arc_r = 0;
const int32_t base_line = font->base_line;
int32_t arc_r_delta = 0;
int32_t arc_r = arclabel->radius;
if(arc_r == LV_SIZE_CONTENT) arc_r = LV_PCT(100);
if(LV_COORD_IS_PCT(arc_r)) {
const int32_t width = lv_area_get_width(&coords);
const int32_t height = lv_area_get_height(&coords);
arc_r = lv_pct_to_px(arc_r, LV_MIN(width, height)) / 2;
}
switch(arclabel->text_align_v) {
case LV_ARCLABEL_TEXT_ALIGN_LEADING:
arc_r_delta = line_height - base_line;
break;
case LV_ARCLABEL_TEXT_ALIGN_CENTER:
arc_r_delta = line_height / 2 - base_line;
break;
case LV_ARCLABEL_TEXT_ALIGN_TRAILING:
arc_r_delta = -base_line;
break;
default:
break;
}
arc_r += arclabel->dir == LV_ARCLABEL_DIR_CLOCKWISE ? -arc_r_delta : arc_r_delta;
bool need_ellipsis = false; bool need_ellipsis = false;
uint32_t processed_total_word_count = 0;
const lv_value_precise_t total_visible_angle = arclabel_calc_arc_text_total_angle(obj, &arc_r, &need_ellipsis,
&processed_total_word_count);
const int32_t offset = arclabel->offset; const int32_t offset = arclabel->offset;
const lv_value_precise_t angle_offset = rad_to_deg(offset, arc_r); const lv_value_precise_t angle_offset = rad_to_deg(offset, arc_r);
uint32_t processed_total_word_count = 0;
lv_value_precise_t total_visible_angle = calc_arc_text_total_angle(text_start, font, arc_r,
arclabel->angle_size, letter_space, arclabel->recolor,
arclabel->overflow, arclabel->end_overlap, &need_ellipsis, &processed_total_word_count);
lv_value_precise_t angle_start = 0; lv_value_precise_t angle_start = 0;
switch(arclabel->text_align_h) { switch(arclabel->text_align_h) {
@@ -568,6 +549,54 @@ static void arclabel_draw_main(lv_event_t * e)
} }
} }
static lv_value_precise_t arclabel_calc_arc_text_total_angle(lv_obj_t * obj, int32_t * arc_radius, bool * need_ellipsis,
uint32_t * letter_count)
{
lv_arclabel_t * arclabel = (lv_arclabel_t *)obj;
const char * text = arclabel->text;
const char * text_start = text;
lv_area_t coords;
lv_obj_get_content_coords(obj, &coords);
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
const int32_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
const int32_t line_height = font->line_height;
const int32_t base_line = font->base_line;
int32_t arc_r_delta = 0;
int32_t arc_r = arclabel->radius;
if(arc_r == LV_SIZE_CONTENT) arc_r = LV_PCT(100);
if(LV_COORD_IS_PCT(arc_r)) {
const int32_t width = lv_area_get_width(&coords);
const int32_t height = lv_area_get_height(&coords);
arc_r = lv_pct_to_px(arc_r, LV_MIN(width, height)) / 2;
}
switch(arclabel->text_align_v) {
case LV_ARCLABEL_TEXT_ALIGN_LEADING:
arc_r_delta = line_height - base_line;
break;
case LV_ARCLABEL_TEXT_ALIGN_CENTER:
arc_r_delta = line_height / 2 - base_line;
break;
case LV_ARCLABEL_TEXT_ALIGN_TRAILING:
arc_r_delta = -base_line;
break;
default:
break;
}
arc_r += arclabel->dir == LV_ARCLABEL_DIR_CLOCKWISE ? -arc_r_delta : arc_r_delta;
if(arc_radius != NULL) *arc_radius = arc_r;
lv_value_precise_t total_visible_angle = calc_arc_text_total_angle(text_start, font, arc_r,
arclabel->angle_size, letter_space, arclabel->recolor,
arclabel->overflow, arclabel->end_overlap, need_ellipsis, letter_count);
return total_visible_angle;
}
static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_font_t * font, const uint32_t radius, static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_font_t * font, const uint32_t radius,
const lv_value_precise_t angle_size, const int32_t letter_space, const bool recolor, const lv_value_precise_t angle_size, const int32_t letter_space, const bool recolor,
const lv_arclabel_overflow_t overflow, bool end_overlap, bool * need_ellipsis, uint32_t * letter_count) const lv_arclabel_overflow_t overflow, bool end_overlap, bool * need_ellipsis, uint32_t * letter_count)
@@ -580,6 +609,7 @@ static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_
lv_value_precise_t total_arc_length = 0; lv_value_precise_t total_arc_length = 0;
lv_value_precise_t pre_total_arc_length = 0; lv_value_precise_t pre_total_arc_length = 0;
uint32_t pre_processed_letter_count = 0; uint32_t pre_processed_letter_count = 0;
lv_value_precise_t ellipsis_arc_length = 0;
bool full = false; bool full = false;
uint32_t letter = 0; uint32_t letter = 0;
uint32_t letter_next = 0; uint32_t letter_next = 0;
@@ -587,7 +617,7 @@ static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_
lv_value_precise_t available_arc_length = angle_size_in_arc_length; lv_value_precise_t available_arc_length = angle_size_in_arc_length;
if(overflow == LV_ARCLABEL_OVERFLOW_ELLIPSIS) { if(overflow == LV_ARCLABEL_OVERFLOW_ELLIPSIS) {
lv_value_precise_t dot_width = lv_font_get_glyph_width(font, '.', '.'); lv_value_precise_t dot_width = lv_font_get_glyph_width(font, '.', '.');
lv_value_precise_t ellipsis_arc_length = 3 * dot_width + 2 * letter_space; ellipsis_arc_length = 3 * dot_width + 2 * letter_space;
if(available_arc_length > ellipsis_arc_length) available_arc_length -= ellipsis_arc_length; if(available_arc_length > ellipsis_arc_length) available_arc_length -= ellipsis_arc_length;
else available_arc_length = 0; else available_arc_length = 0;
} }
@@ -628,7 +658,9 @@ static lv_value_precise_t calc_arc_text_total_angle(const char * text, const lv_
} }
if(full && (overflow == LV_ARCLABEL_OVERFLOW_ELLIPSIS || overflow == LV_ARCLABEL_OVERFLOW_CLIP)) { if(full && (overflow == LV_ARCLABEL_OVERFLOW_ELLIPSIS || overflow == LV_ARCLABEL_OVERFLOW_CLIP)) {
total_arc_length = pre_total_arc_length; total_arc_length = pre_total_arc_length + (overflow == LV_ARCLABEL_OVERFLOW_ELLIPSIS
? ellipsis_arc_length
: 0);
processed_letter_count = pre_processed_letter_count; processed_letter_count = pre_processed_letter_count;
} }
+11
View File
@@ -273,6 +273,17 @@ lv_arclabel_overflow_t lv_arclabel_get_overflow(lv_obj_t * obj);
*/ */
bool lv_arclabel_get_end_overlap(lv_obj_t * obj); bool lv_arclabel_get_end_overlap(lv_obj_t * obj);
/**
* Get the text angle for an arc label object.
* @note The text angle is calculated at runtime. You can get the updated value
* after the arclabel's size has been updated.
* Returns the real rendered text angle in degrees except in
* `LV_ARCLABEL_OVERFLOW_VISIBLE` mode.
* @param obj pointer to an arc label object
* @return the text angle (if `LV_USE_FLOAT` is enabled it can be fractional too.)
*/
lv_value_precise_t lv_arclabel_get_text_angle(lv_obj_t * obj);
/*===================== /*=====================
* Other functions * Other functions
*====================*/ *====================*/
+5 -1
View File
@@ -165,10 +165,13 @@ void test_arclabel_overflow(void)
lv_arclabel_set_dir(arclabel, LV_ARCLABEL_DIR_CLOCKWISE); lv_arclabel_set_dir(arclabel, LV_ARCLABEL_DIR_CLOCKWISE);
lv_arclabel_set_overflow(arclabel, overflows[i]); lv_arclabel_set_overflow(arclabel, overflows[i]);
lv_obj_center(arclabel); lv_obj_center(arclabel);
}
lv_obj_refr_size(arclabel);
TEST_ASSERT_GREATER_THAN_FLOAT(178, lv_arclabel_get_text_angle(arclabel));
}
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/arclabel_overflow" EXT_NAME); TEST_ASSERT_EQUAL_SCREENSHOT("widgets/arclabel_overflow" EXT_NAME);
} }
void test_arclabel_opacity(void) void test_arclabel_opacity(void)
{ {
if(!font) { if(!font) {
@@ -220,4 +223,5 @@ void test_arclabel_opacity(void)
} }
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/arclabel_opacity" EXT_NAME); TEST_ASSERT_EQUAL_SCREENSHOT("widgets/arclabel_opacity" EXT_NAME);
} }
#endif #endif