diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index e39b312f7e..d2e5f2a11a 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -33,9 +33,9 @@ * TYPEDEFS **********************/ enum { - CMD_STATE_WAIT, - CMD_STATE_PAR, - CMD_STATE_IN, + RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER, + RECOLOR_CMD_STATE_PARAMETER, + RECOLOR_CMD_STATE_TEXT_INPUT, }; typedef unsigned char cmd_state_t; @@ -269,11 +269,12 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ fill_dsc.opa = dsc->opa; int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1; int32_t line_start_x; - uint32_t i; - uint32_t par_start = 0; + uint32_t next_char_offset; + uint32_t recolor_command_start_index = 0; int32_t letter_w; - cmd_state_t cmd_state = CMD_STATE_WAIT; + cmd_state_t recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER; lv_color_t recolor = lv_color_black(); /* Holds the selected color inside the recolor command */ + uint8_t is_first_space_after_cmd = 0; /*Write out all lines*/ while(dsc->text[line_start] != '\0') { @@ -281,8 +282,8 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ line_start_x = pos.x; /*Write all letter of a line*/ - cmd_state = CMD_STATE_WAIT; - i = 0; + recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER; + next_char_offset = 0; #if LV_USE_BIDI char * bidi_txt = lv_malloc(line_end - line_start + 1); LV_ASSERT_MALLOC(bidi_txt); @@ -291,65 +292,98 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ const char * bidi_txt = dsc->text + line_start; #endif - while(i < line_end - line_start) { + while(next_char_offset < line_end - line_start) { uint32_t logical_char_pos = 0; + + /* Check if the text selection is enabled */ if(sel_start != 0xFFFF && sel_end != 0xFFFF) { #if LV_USE_BIDI logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start); - uint32_t t = lv_text_encoded_get_char_id(bidi_txt, i); + uint32_t t = lv_text_encoded_get_char_id(bidi_txt, next_char_offset); logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL); #else - logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + i); + logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset); #endif } uint32_t letter; uint32_t letter_next; - lv_text_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i); + lv_text_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &next_char_offset); - /* Handle the recolor command */ + /* If recolor is enabled */ if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) { + if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) { - if(cmd_state == CMD_STATE_WAIT) { /*Start char*/ - par_start = i; - cmd_state = CMD_STATE_PAR; + /* Handle the recolor command marker depending of the current recolor state */ + + if(recolor_cmd_state == RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER) { + recolor_command_start_index = next_char_offset; + recolor_cmd_state = RECOLOR_CMD_STATE_PARAMETER; continue; } - else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/ - cmd_state = CMD_STATE_WAIT; + /*Other start char in parameter escaped cmd. char*/ + else if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) { + recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER; } - else if(cmd_state == CMD_STATE_IN) { /*Command end*/ - cmd_state = CMD_STATE_WAIT; + /* If letter is LV_TXT_COLOR_CMD and we were in the CMD_STATE_IN then the recolor close marked has been found */ + else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) { + recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER; continue; } } - /*Skip the color parameter and wait the space after it*/ - if(cmd_state == CMD_STATE_PAR) { - if(letter == ' ') { - /*Get the parameter*/ - if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) { - char buf[LABEL_RECOLOR_PAR_LENGTH + 1]; - lv_memcpy(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH); - buf[LABEL_RECOLOR_PAR_LENGTH] = '\0'; - int r, g, b; - r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]); - g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]); - b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]); + /* Find the first space (aka ' ') after the recolor command parameter, we need to skip rendering it */ + if((recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) && (letter == ' ') && (is_first_space_after_cmd == 0)) { + is_first_space_after_cmd = 1; + } + else { + is_first_space_after_cmd = 0; + } - recolor = lv_color_make(r, g, b); - } - else { - recolor.red = dsc->color.red; - recolor.blue = dsc->color.blue; - recolor.green = dsc->color.green; - } - cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/ + /* Skip the color parameter and wait the space after it + * Once we have reach the space ' ', then we will extract the color information + * and store it into the recolor variable */ + if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) { + /* Not an space? Continue with the next character */ + if(letter != ' ') { + continue; } + + /*Get the recolor parameter*/ + if((next_char_offset - recolor_command_start_index) == LABEL_RECOLOR_PAR_LENGTH + 1) { + /* Temporary buffer to hold the recolor information */ + char buf[LABEL_RECOLOR_PAR_LENGTH + 1]; + lv_memcpy(buf, &bidi_txt[recolor_command_start_index], LABEL_RECOLOR_PAR_LENGTH); + buf[LABEL_RECOLOR_PAR_LENGTH] = '\0'; + + uint8_t r, g, b; + r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]); + g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]); + b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]); + + recolor = lv_color_make(r, g, b); + } + else { + recolor.red = dsc->color.red; + recolor.blue = dsc->color.blue; + recolor.green = dsc->color.green; + } + + /*After the parameter the text is in the command*/ + recolor_cmd_state = RECOLOR_CMD_STATE_TEXT_INPUT; + } + + /* Don't draw the first space after the recolor command */ + if(is_first_space_after_cmd) { continue; } } + /* If we're in the CMD_STATE_IN state then we need to subtract the recolor command length */ + if(((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) && (recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT)) { + logical_char_pos -= (LABEL_RECOLOR_PAR_LENGTH + 1); + } + letter_w = lv_font_get_glyph_width(font, letter, letter_next); /*Always set the bg_coordinates for placeholder drawing*/ @@ -358,7 +392,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ bg_coords.x2 = pos.x + letter_w - 1; bg_coords.y2 = pos.y + line_height - 1; - if(i >= line_end - line_start) { + if(next_char_offset >= line_end - line_start) { if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) { lv_area_t fill_area; fill_area.x1 = line_start_x; @@ -381,12 +415,13 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ } } + /* Handle text selection */ if(sel_start != 0xFFFF && sel_end != 0xFFFF && logical_char_pos >= sel_start && logical_char_pos < sel_end) { draw_letter_dsc.color = dsc->sel_color; fill_dsc.color = dsc->sel_bg_color; cb(draw_unit, NULL, &fill_dsc, &bg_coords); } - else if(cmd_state == CMD_STATE_IN) { + else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) { draw_letter_dsc.color = recolor; } else { diff --git a/tests/ref_imgs/draw/label_selection_and_recolor.png b/tests/ref_imgs/draw/label_selection_and_recolor.png new file mode 100644 index 0000000000..3ff60c09bc Binary files /dev/null and b/tests/ref_imgs/draw/label_selection_and_recolor.png differ diff --git a/tests/ref_imgs_vg_lite/draw/label_selection_and_recolor.png b/tests/ref_imgs_vg_lite/draw/label_selection_and_recolor.png new file mode 100644 index 0000000000..04bca6145d Binary files /dev/null and b/tests/ref_imgs_vg_lite/draw/label_selection_and_recolor.png differ diff --git a/tests/src/test_cases/draw/test_draw_label.c b/tests/src/test_cases/draw/test_draw_label.c index 749e9fcb83..8e09f8c196 100644 --- a/tests/src/test_cases/draw/test_draw_label.c +++ b/tests/src/test_cases/draw/test_draw_label.c @@ -123,4 +123,26 @@ void test_label_decor(void) TEST_ASSERT_EQUAL_SCREENSHOT("draw/label_decor.png"); } +void test_label_selection_and_recolor(void) +{ + lv_text_decor_t decor = LV_TEXT_DECOR_NONE; + lv_color_t color = lv_palette_main(LV_PALETTE_BLUE); + lv_color_t sel_bg_color = lv_palette_lighten(LV_PALETTE_RED, 4); + lv_color_t sel_color = lv_palette_darken(LV_PALETTE_RED, 4); + + lv_obj_t * label = lv_label_create(lv_screen_active()); + lv_label_set_recolor(label, true); + lv_label_set_text(label, "Hi, Testing the #00ff00 colored labels.#"); + lv_obj_set_style_text_decor(label, decor, 0); + lv_obj_set_style_text_color(label, color, 0); + lv_obj_set_style_text_opa(label, LV_OPA_COVER, 0); + lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_style_bg_color(label, sel_bg_color, LV_PART_SELECTED); + lv_obj_set_style_text_color(label, sel_color, LV_PART_SELECTED); + lv_label_set_text_selection_start(label, 10); + lv_label_set_text_selection_end(label, 25); + + TEST_ASSERT_EQUAL_SCREENSHOT("draw/label_selection_and_recolor.png"); +} + #endif