mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-10 04:37:55 +08:00
feat(draw_label): Support simultaneous text selection and recolor (#7116)
This commit is contained in:
+77
-42
@@ -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 {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user