1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00
lvgl/src/lv_draw/lv_draw_label.c

313 lines
10 KiB
C
Raw Normal View History

/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_label.h"
#include "../lv_misc/lv_math.h"
#include "../lv_misc/lv_bidi.h"
/*********************
* DEFINES
*********************/
2019-04-04 07:15:40 +02:00
#define LABEL_RECOLOR_PAR_LENGTH 6
2019-06-27 07:16:15 +02:00
#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
/**********************
* TYPEDEFS
**********************/
2018-09-18 13:59:40 +02:00
enum {
CMD_STATE_WAIT,
CMD_STATE_PAR,
CMD_STATE_IN,
2018-09-18 13:59:40 +02:00
};
typedef uint8_t cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param style pointer to a style
2018-06-14 13:08:19 +02:00
* @param opa_scale scale down all opacities by the factor
* @param txt 0 terminated text to write
* @param flag settings for the text from 'txt_flag_t' enum
* @param offset text offset in x and y direction (NULL if unused)
2019-04-18 06:45:45 +02:00
* @param sel_start start index of selected area (`LV_LABEL_TXT_SEL_OFF` if none)
* @param sel_end end index of selected area (`LV_LABEL_TXT_SEL_OFF` if none)
*/
2019-06-06 06:05:40 +02:00
void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale,
2019-11-11 11:10:01 +01:00
const char * txt, lv_txt_flag_t flag, lv_point_t * offset, uint16_t sel_start, uint16_t sel_end,
lv_draw_label_hint_t * hint, lv_bidi_dir_t bidi_dir)
{
const lv_font_t * font = style->text.font;
lv_coord_t w;
/*No need to waste processor time if string is empty*/
if (txt[0] == '\0')
{
return;
}
if((flag & LV_TXT_FLAG_EXPAND) == 0) {
2018-09-25 16:21:31 +02:00
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
} else {
2018-09-25 16:21:31 +02:00
/*If EXAPND is enabled then not limit the text's width to the object's width*/
lv_point_t p;
2019-06-06 06:05:40 +02:00
lv_txt_get_size(&p, txt, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX,
2019-11-11 11:10:01 +01:00
flag);
w = p.x;
}
2019-04-23 15:56:59 +02:00
lv_coord_t line_height = lv_font_get_line_height(font) + style->text.line_space;
2018-12-21 15:25:45 +01:00
/*Init variables for the first line*/
lv_coord_t line_width = 0;
lv_point_t pos;
pos.x = coords->x1;
pos.y = coords->y1;
2019-02-18 06:41:02 +01:00
lv_coord_t x_ofs = 0;
lv_coord_t y_ofs = 0;
if(offset != NULL) {
x_ofs = offset->x;
y_ofs = offset->y;
pos.y += y_ofs;
}
2019-06-27 07:16:15 +02:00
uint32_t line_start = 0;
2019-06-14 14:57:59 +02:00
int32_t last_line_start = -1;
2019-06-14 16:04:15 +02:00
/*Check the hint to use the cached info*/
2019-07-29 15:56:20 +02:00
if(hint && y_ofs == 0 && coords->y1 < 0) {
2019-06-27 07:16:15 +02:00
/*If the label changed too much recalculate the hint.*/
2019-06-14 16:04:15 +02:00
if(LV_MATH_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
2019-06-14 15:15:20 +02:00
hint->line_start = -1;
}
2019-06-14 15:20:46 +02:00
last_line_start = hint->line_start;
2019-06-14 14:57:59 +02:00
}
2019-06-14 16:04:15 +02:00
/*Use the hint if it's valid*/
if(hint && last_line_start >= 0) {
2019-06-14 14:57:59 +02:00
line_start = last_line_start;
2019-06-14 15:20:46 +02:00
pos.y += hint->y;
2019-06-14 14:57:59 +02:00
}
2019-11-10 10:52:49 +01:00
2019-06-14 14:57:59 +02:00
uint32_t line_end = line_start + lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
2018-12-21 15:25:45 +01:00
/*Go the first visible line*/
while(pos.y + line_height < mask->y1) {
/*Go to next line*/
line_start = line_end;
line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
pos.y += line_height;
2019-06-14 16:04:15 +02:00
/*Save at the threshold coordinate*/
2019-06-27 07:16:15 +02:00
if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
2019-06-14 14:57:59 +02:00
hint->line_start = line_start;
2019-06-27 07:16:15 +02:00
hint->y = pos.y - coords->y1;
hint->coord_y = coords->y1;
2019-06-14 14:57:59 +02:00
}
2018-12-21 15:25:45 +01:00
if(txt[line_start] == '\0') return;
}
/*Align to middle*/
if(flag & LV_TXT_FLAG_CENTER) {
2019-06-06 06:05:40 +02:00
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag);
2018-10-05 17:22:49 +02:00
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(flag & LV_TXT_FLAG_RIGHT) {
2019-06-06 06:05:40 +02:00
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag);
2018-10-05 17:22:49 +02:00
pos.x += lv_area_get_width(coords) - line_width;
}
2019-06-06 06:05:40 +02:00
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->text.opa : (uint16_t)((uint16_t)style->text.opa * opa_scale) >> 8;
2018-06-14 13:08:19 +02:00
2019-11-11 11:10:01 +01:00
if(sel_start > sel_end) {
uint16_t tmp = sel_start;
sel_start = sel_end;
sel_end = tmp;
}
cmd_state_t cmd_state = CMD_STATE_WAIT;
uint32_t i;
uint16_t par_start = 0;
lv_color_t recolor;
lv_coord_t letter_w;
lv_style_t sel_style;
lv_style_copy(&sel_style, &lv_style_plain_color);
sel_style.body.main_color = sel_style.body.grad_color = style->text.sel_color;
/*Write out all lines*/
while(txt[line_start] != '\0') {
if(offset != NULL) {
pos.x += x_ofs;
}
/*Write all letter of a line*/
cmd_state = CMD_STATE_WAIT;
i = 0;
uint32_t letter;
2019-05-01 16:43:32 +02:00
uint32_t letter_next;
#if LV_USE_BIDI
2019-11-11 11:10:01 +01:00
char *bidi_txt = lv_draw_get_buf(line_end - line_start + 1);
lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, bidi_dir, NULL, 0);
#else
2019-11-11 11:10:01 +01:00
const char *bidi_txt = txt + line_start;
#endif
2019-11-11 11:10:01 +01:00
while(i < line_end - line_start) {
2019-11-11 11:10:01 +01:00
uint16_t logical_char_pos = 0;
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
logical_char_pos = lv_txt_encoded_get_char_id(txt, line_start);
uint16_t t = lv_txt_encoded_get_char_id(bidi_txt, i);
logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, bidi_dir, t, NULL);
}
letter = lv_txt_encoded_next(bidi_txt, &i);
letter_next = lv_txt_encoded_next(&bidi_txt[i], NULL);
2019-11-11 11:10:01 +01:00
/*Handle the re-color command*/
if((flag & LV_TXT_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;
continue;
2019-06-06 06:05:40 +02:00
} else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char */
cmd_state = CMD_STATE_WAIT;
2018-06-19 09:49:58 +02:00
} else if(cmd_state == CMD_STATE_IN) { /*Command end */
cmd_state = CMD_STATE_WAIT;
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];
memcpy(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
2018-06-19 09:49:58 +02:00
int r, g, b;
2019-04-04 07:15:40 +02:00
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]);
2019-03-17 07:44:48 +01:00
recolor = lv_color_make(r, g, b);
} else {
recolor.full = style->text.color.full;
}
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
}
continue;
}
}
lv_color_t color = style->text.color;
if(cmd_state == CMD_STATE_IN) color = recolor;
2019-05-01 16:43:32 +02:00
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
2019-04-18 06:45:45 +02:00
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
2019-04-04 07:15:40 +02:00
/*Do not draw the rectangle on the character at `sel_start`.*/
2019-11-11 11:10:01 +01:00
if(logical_char_pos > sel_start && logical_char_pos <= sel_end) {
2019-04-04 07:15:40 +02:00
lv_area_t sel_coords;
sel_coords.x1 = pos.x;
sel_coords.y1 = pos.y;
sel_coords.x2 = pos.x + letter_w + style->text.letter_space - 1;
sel_coords.y2 = pos.y + line_height - 1;
lv_draw_rect(&sel_coords, mask, &sel_style, opa);
}
}
lv_draw_letter(&pos, mask, font, letter, color, opa);
2019-04-04 07:15:40 +02:00
if(letter_w > 0) {
pos.x += letter_w + style->text.letter_space;
}
}
/*Go to next line*/
line_start = line_end;
line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
pos.x = coords->x1;
/*Align to middle*/
if(flag & LV_TXT_FLAG_CENTER) {
2019-06-06 06:05:40 +02:00
line_width =
2019-11-11 11:10:01 +01:00
lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag);
2018-10-05 17:22:49 +02:00
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(flag & LV_TXT_FLAG_RIGHT) {
2019-06-06 06:05:40 +02:00
line_width =
2019-11-11 11:10:01 +01:00
lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag);
2018-10-05 17:22:49 +02:00
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > mask->y2) return;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
uint8_t result = 0;
if(hex >= '0' && hex <= '9') {
result = hex - '0';
2019-04-04 07:15:40 +02:00
} else {
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
2018-12-03 14:52:06 +01:00
switch(hex) {
2019-11-11 11:10:01 +01:00
case 'A': result = 10; break;
case 'B': result = 11; break;
case 'C': result = 12; break;
case 'D': result = 13; break;
case 'E': result = 14; break;
case 'F': result = 15; break;
default: result = 0; break;
2018-12-03 14:52:06 +01:00
}
}
return result;
}