diff --git a/src/widgets/arc_label/lv_arc_label.c b/src/widgets/arc_label/lv_arc_label.c index c4daeb879a..261b0efbf8 100644 --- a/src/widgets/arc_label/lv_arc_label.c +++ b/src/widgets/arc_label/lv_arc_label.c @@ -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