feat(arc_label): support recolor feature

This commit is contained in:
Benign X
2025-02-11 14:45:15 +08:00
committed by Felipe Neves
parent f55fee85fc
commit 78e3375964
+151 -78
View File
@@ -46,7 +46,10 @@ static void lv_arc_label_constructor(const lv_obj_class_t * class_p, lv_obj_t *
static void arc_label_draw_main(lv_event_t * e);
static void lv_arc_label_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,
const lv_value_precise_t angle_size, int32_t letter_space);
const lv_value_precise_t angle_size, int32_t letter_space, bool recolor);
static const char * recolor_cmd_get_next(const char * text_in, uint32_t len_in,
const char ** text_out, uint32_t * len_out,
lv_color_t * color_out);
/**********************
* STATIC VARIABLES
@@ -367,6 +370,9 @@ static void arc_label_draw_main(lv_event_t * e)
lv_obj_t * obj = lv_event_get_current_target(e);
lv_arc_label_t * arc_label = (lv_arc_label_t *)obj;
const char * text = arc_label->text;
const char * text_start = text;
lv_area_t coords;
lv_obj_get_content_coords(obj, &coords);
@@ -407,126 +413,193 @@ static void arc_label_draw_main(lv_event_t * e)
angle_start = angle_offset;
break;
case LV_ARC_LABEL_TEXT_ALIGN_CENTER:
angle_start = (arc_label->angle_size + angle_offset - calc_arc_text_total_angle(arc_label->text, font, arc_r,
arc_label->angle_size,
letter_space)) / 2;
angle_start = (arc_label->angle_size + angle_offset - calc_arc_text_total_angle(text_start, font, arc_r,
arc_label->angle_size, letter_space, arc_label->recolor)) / 2;
break;
case LV_ARC_LABEL_TEXT_ALIGN_TRAILING:
angle_start = arc_label->angle_size - calc_arc_text_total_angle(arc_label->text, font, arc_r, arc_label->angle_size,
letter_space);
angle_start = arc_label->angle_size - calc_arc_text_total_angle(text_start, font, arc_r, arc_label->angle_size,
letter_space, arc_label->recolor);
break;
default:
break;
}
uint32_t word_i = 0;
uint32_t processed_word_count = 0;
lv_value_precise_t prev_letter_w = 0;
lv_value_precise_t total_arc_length = arc_label->angle_size * M_PI / 180 * arc_r;
lv_value_precise_t curr_total_arc_length = angle_start * M_PI / 180 * arc_r;
while(curr_total_arc_length <= total_arc_length) {
uint32_t letter;
uint32_t letter_next;
lv_text_encoded_letter_next_2(arc_label->text, &letter, &letter_next, &word_i);
const lv_value_precise_t letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(processed_word_count > 0) {
const lv_value_precise_t arc_offset = (prev_letter_w + letter_w + letter_space) / (lv_value_precise_t)2;
curr_total_arc_length += arc_offset;
if(curr_total_arc_length > total_arc_length) {
break;
while(text) {
uint32_t word_i = 0;
uint32_t text_len = LV_TEXT_LEN_MAX;
lv_color_t recolor_color = color;
if(arc_label->recolor) text = recolor_cmd_get_next(text, LV_TEXT_LEN_MAX, &text_start, &text_len, &recolor_color);
else text = NULL;
while(word_i < text_len && curr_total_arc_length <= total_arc_length) {
uint32_t letter;
uint32_t letter_next;
lv_text_encoded_letter_next_2(text_start, &letter, &letter_next, &word_i);
const lv_value_precise_t letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(processed_word_count > 0) {
const lv_value_precise_t arc_offset = (prev_letter_w + letter_w + letter_space) / (lv_value_precise_t)2;
curr_total_arc_length += arc_offset;
if(curr_total_arc_length > total_arc_length) {
break;
}
}
}
const lv_value_precise_t curr_angle = arc_label->angle_start + (arc_label->dir == LV_ARC_LABEL_DIR_CLOCKWISE ?
curr_total_arc_length : total_arc_length - curr_total_arc_length) * 180 / M_PI / arc_r;
const lv_value_precise_t curr_angle = arc_label->angle_start + (arc_label->dir == LV_ARC_LABEL_DIR_CLOCKWISE ?
curr_total_arc_length : total_arc_length - curr_total_arc_length) * 180 / M_PI / arc_r;
#if LV_USE_FLOAT
const lv_value_precise_t x = cos(curr_angle * M_PI / 180) * arc_r;
const lv_value_precise_t y = sin(curr_angle * M_PI / 180) * arc_r;
const lv_value_precise_t x = cos(curr_angle * M_PI / 180) * arc_r;
const lv_value_precise_t y = sin(curr_angle * M_PI / 180) * arc_r;
#else
const lv_value_precise_t x = lv_trigo_cos(curr_angle) * arc_r / 32767;
const lv_value_precise_t y = lv_trigo_sin(curr_angle) * arc_r / 32767;
const lv_value_precise_t x = lv_trigo_cos(curr_angle) * arc_r / 32767;
const lv_value_precise_t y = lv_trigo_sin(curr_angle) * arc_r / 32767;
#endif
lv_point_t point = {
x + lv_area_get_width(&coords) / 2 + coords.x1 + arc_label->center_offset.x,
y + lv_area_get_height(&coords) / 2 + coords.y1 + arc_label->center_offset.y,
};
lv_point_t point = {
x + lv_area_get_width(&coords) / 2 + coords.x1 + arc_label->center_offset.x,
y + lv_area_get_height(&coords) / 2 + coords.y1 + arc_label->center_offset.y,
};
lv_draw_letter_dsc_t dsc;
lv_draw_letter_dsc_init(&dsc);
dsc.font = font;
dsc.color = color;
dsc.opa = opa;
if(arc_label->dir == LV_ARC_LABEL_DIR_CLOCKWISE) dsc.rotation = (curr_angle + 90) * 10;
else dsc.rotation = (curr_angle - 90) * 10;
lv_draw_letter_dsc_t dsc;
lv_draw_letter_dsc_init(&dsc);
dsc.font = font;
dsc.color = arc_label->recolor ? recolor_color : color;
dsc.opa = opa;
if(arc_label->dir == LV_ARC_LABEL_DIR_CLOCKWISE) dsc.rotation = (curr_angle + 90) * 10;
else dsc.rotation = (curr_angle - 90) * 10;
dsc.unicode = letter;
if(dsc.unicode == 0) {
break;
}
dsc.unicode = letter;
if(dsc.unicode == 0) {
break;
}
lv_draw_letter(layer, &dsc, &point);
lv_draw_letter(layer, &dsc, &point);
prev_letter_w = letter_w;
processed_word_count++;
prev_letter_w = letter_w;
processed_word_count++;
#if LV_ARC_LABEL_DEBUG
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = lv_color_make(0x11, 0x45, 0x14);
line_dsc.opa = LV_OPA_30;
line_dsc.width = 2;
line_dsc.p1 = (lv_point_precise_t) {
.x = point.x,
.y = point.y
};
line_dsc.p2 = (lv_point_precise_t) {
.x = lv_area_get_width(&coords) / 2 + coords.x1,
.y = lv_area_get_height(&coords) / 2 + coords.y1
};
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = lv_color_make(0x11, 0x45, 0x14);
line_dsc.opa = LV_OPA_30;
line_dsc.width = 2;
line_dsc.p1 = (lv_point_precise_t) {
.x = point.x,
.y = point.y
};
line_dsc.p2 = (lv_point_precise_t) {
.x = lv_area_get_width(&coords) / 2 + coords.x1,
.y = lv_area_get_height(&coords) / 2 + coords.y1
};
lv_draw_line(layer, &line_dsc);
lv_draw_line(layer, &line_dsc);
#endif
}
}
}
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 lv_value_precise_t angle_size, const int32_t letter_space, const bool recolor)
{
uint32_t word_i = 0;
const char * text_start = text;
uint32_t processed_letter_count = 0;
lv_value_precise_t prev_letter_w = 0;
const lv_value_precise_t angle_size_in_arc_length = angle_size * M_PI / 180 * radius;
lv_value_precise_t total_arc_length = 0;
while(total_arc_length < angle_size_in_arc_length) {
if(total_arc_length > angle_size_in_arc_length) {
break;
}
uint32_t letter;
uint32_t letter_next;
lv_text_encoded_letter_next_2(text, &letter, &letter_next, &word_i);
const lv_value_precise_t letter_w = lv_font_get_glyph_width(font, letter, letter_next);
while(text) {
uint32_t word_i = 0;
uint32_t text_len = LV_TEXT_LEN_MAX;
if(recolor) text = recolor_cmd_get_next(text, LV_TEXT_LEN_MAX, &text_start, &text_len, NULL);
else text = NULL;
if(processed_letter_count == 0) {
while(word_i <= text_len && total_arc_length < angle_size_in_arc_length) {
if(total_arc_length > angle_size_in_arc_length) {
break;
}
uint32_t letter;
uint32_t letter_next;
lv_text_encoded_letter_next_2(text_start, &letter, &letter_next, &word_i);
const lv_value_precise_t letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(processed_letter_count == 0) {
processed_letter_count++;
continue;
}
const lv_value_precise_t arc_offset = (prev_letter_w + letter_w + letter_space) / (lv_value_precise_t)2;
total_arc_length += arc_offset;
if(letter == 0) {
break;
}
prev_letter_w = letter_w;
processed_letter_count++;
continue;
}
const lv_value_precise_t arc_offset = (prev_letter_w + letter_w + letter_space) / (lv_value_precise_t)2;
total_arc_length += arc_offset;
if(letter == 0) {
break;
}
prev_letter_w = letter_w;
processed_letter_count++;
}
return total_arc_length * 180 / M_PI / radius;
}
static const char * recolor_cmd_get_next(const char * text_in, uint32_t len_in,
const char ** text_out, uint32_t * len_out,
lv_color_t * color_out)
{
if(!text_in || len_in == 0 || *text_in == '\0') return NULL;
const char * text = text_in;
const char * text_end = text_in + len_in;
bool has_cmd = false;
if(*text == LV_TXT_COLOR_CMD[0]) {
if(len_in < 8) {
if(text_out) *text_out = text_in;
if(len_out) *len_out = len_in;
return NULL;
}
has_cmd = true;
text++;
int32_t index = 0;
uint8_t color_buf[6];
while(*text && index < 6) {
uint8_t ch = text[index];
if(!((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))) break;
if(ch >= 'a' && ch <= 'f') ch -= 'a' - 10;
else if(ch >= 'A' && ch <= 'F') ch -= 'A' - 10;
else ch -= '0';
color_buf[index] = ch;
index++;
}
const bool has_valid_param = index == 6 && text[index] == ' ';
text += index;
if(has_valid_param && color_out)
*color_out = lv_color_make(color_buf[0] << 4 | color_buf[1],
color_buf[2] << 4 | color_buf[3],
color_buf[4] << 4 | color_buf[5]);
while(text < text_end && *text && *text++ != ' ') { }
}
const char * text_segment_start = text;
while(text < text_end && *text && *text != LV_TXT_COLOR_CMD[0]) text++;
if(text_out) *text_out = text_segment_start;
if(len_out) *len_out = text - text_segment_start;
if(*text == '\0') return NULL;
if(has_cmd && *text == LV_TXT_COLOR_CMD[0]) text++;
return text < text_end && *text ? text : NULL;
}
#endif