mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-28 13:36:27 +08:00
feat(draw_label): Support simultaneous text selection and recolor (#7116)
This commit is contained in:
+77
-42
@@ -33,9 +33,9 @@
|
|||||||
* TYPEDEFS
|
* TYPEDEFS
|
||||||
**********************/
|
**********************/
|
||||||
enum {
|
enum {
|
||||||
CMD_STATE_WAIT,
|
RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER,
|
||||||
CMD_STATE_PAR,
|
RECOLOR_CMD_STATE_PARAMETER,
|
||||||
CMD_STATE_IN,
|
RECOLOR_CMD_STATE_TEXT_INPUT,
|
||||||
};
|
};
|
||||||
typedef unsigned char cmd_state_t;
|
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;
|
fill_dsc.opa = dsc->opa;
|
||||||
int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1;
|
int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1;
|
||||||
int32_t line_start_x;
|
int32_t line_start_x;
|
||||||
uint32_t i;
|
uint32_t next_char_offset;
|
||||||
uint32_t par_start = 0;
|
uint32_t recolor_command_start_index = 0;
|
||||||
int32_t letter_w;
|
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 */
|
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*/
|
/*Write out all lines*/
|
||||||
while(dsc->text[line_start] != '\0') {
|
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;
|
line_start_x = pos.x;
|
||||||
|
|
||||||
/*Write all letter of a line*/
|
/*Write all letter of a line*/
|
||||||
cmd_state = CMD_STATE_WAIT;
|
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
|
||||||
i = 0;
|
next_char_offset = 0;
|
||||||
#if LV_USE_BIDI
|
#if LV_USE_BIDI
|
||||||
char * bidi_txt = lv_malloc(line_end - line_start + 1);
|
char * bidi_txt = lv_malloc(line_end - line_start + 1);
|
||||||
LV_ASSERT_MALLOC(bidi_txt);
|
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;
|
const char * bidi_txt = dsc->text + line_start;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while(i < line_end - line_start) {
|
while(next_char_offset < line_end - line_start) {
|
||||||
uint32_t logical_char_pos = 0;
|
uint32_t logical_char_pos = 0;
|
||||||
|
|
||||||
|
/* Check if the text selection is enabled */
|
||||||
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
|
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
|
||||||
#if LV_USE_BIDI
|
#if LV_USE_BIDI
|
||||||
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
|
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);
|
logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
|
||||||
#else
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t letter;
|
uint32_t letter;
|
||||||
uint32_t letter_next;
|
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((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
|
||||||
|
|
||||||
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
|
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
|
||||||
if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
|
/* Handle the recolor command marker depending of the current recolor state */
|
||||||
par_start = i;
|
|
||||||
cmd_state = CMD_STATE_PAR;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/
|
/*Other start char in parameter escaped cmd. char*/
|
||||||
cmd_state = CMD_STATE_WAIT;
|
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*/
|
/* If letter is LV_TXT_COLOR_CMD and we were in the CMD_STATE_IN then the recolor close marked has been found */
|
||||||
cmd_state = CMD_STATE_WAIT;
|
else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
|
||||||
|
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Skip the color parameter and wait the space after it*/
|
/* Find the first space (aka ' ') after the recolor command parameter, we need to skip rendering it */
|
||||||
if(cmd_state == CMD_STATE_PAR) {
|
if((recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) && (letter == ' ') && (is_first_space_after_cmd == 0)) {
|
||||||
if(letter == ' ') {
|
is_first_space_after_cmd = 1;
|
||||||
/*Get the parameter*/
|
}
|
||||||
if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
|
else {
|
||||||
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
|
is_first_space_after_cmd = 0;
|
||||||
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]);
|
|
||||||
|
|
||||||
recolor = lv_color_make(r, g, b);
|
/* Skip the color parameter and wait the space after it
|
||||||
}
|
* Once we have reach the space ' ', then we will extract the color information
|
||||||
else {
|
* and store it into the recolor variable */
|
||||||
recolor.red = dsc->color.red;
|
if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
|
||||||
recolor.blue = dsc->color.blue;
|
/* Not an space? Continue with the next character */
|
||||||
recolor.green = dsc->color.green;
|
if(letter != ' ') {
|
||||||
}
|
continue;
|
||||||
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*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;
|
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);
|
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
|
||||||
|
|
||||||
/*Always set the bg_coordinates for placeholder drawing*/
|
/*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.x2 = pos.x + letter_w - 1;
|
||||||
bg_coords.y2 = pos.y + line_height - 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) {
|
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
|
||||||
lv_area_t fill_area;
|
lv_area_t fill_area;
|
||||||
fill_area.x1 = line_start_x;
|
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) {
|
if(sel_start != 0xFFFF && sel_end != 0xFFFF && logical_char_pos >= sel_start && logical_char_pos < sel_end) {
|
||||||
draw_letter_dsc.color = dsc->sel_color;
|
draw_letter_dsc.color = dsc->sel_color;
|
||||||
fill_dsc.color = dsc->sel_bg_color;
|
fill_dsc.color = dsc->sel_bg_color;
|
||||||
cb(draw_unit, NULL, &fill_dsc, &bg_coords);
|
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;
|
draw_letter_dsc.color = recolor;
|
||||||
}
|
}
|
||||||
else {
|
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");
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user