mirror of
https://github.com/CTCaer/hekate
synced 2024-11-05 03:16:35 +00:00
a39ba2cd71
This fixes an edge case where the original label set was done with set_static_text, the next one with set_text and the text is at the same address. The incomplete check would think that the text resides on heap and it would reallocate it as such, effectively corrupting .data on the next sets.
987 lines
33 KiB
C
987 lines
33 KiB
C
/**
|
|
* @file lv_rect.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "lv_label.h"
|
|
#if USE_LV_LABEL != 0
|
|
|
|
#include "../lv_core/lv_obj.h"
|
|
#include "../lv_core/lv_group.h"
|
|
#include "../lv_core/lv_lang.h"
|
|
#include "../lv_draw/lv_draw.h"
|
|
#include "../lv_misc/lv_color.h"
|
|
#include "../lv_misc/lv_math.h"
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
/*Test configurations*/
|
|
#ifndef LV_LABEL_SCROLL_SPEED
|
|
#define LV_LABEL_SCROLL_SPEED (25) /*Hor, or ver. scroll speed (px/sec) in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
|
|
#endif
|
|
|
|
#define ANIM_WAIT_CHAR_COUNT 3
|
|
|
|
#define LV_LABEL_DOT_END_INV 0xFFFF
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param);
|
|
static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode);
|
|
static void lv_label_refr_text(lv_obj_t * label);
|
|
static void lv_label_revert_dots(lv_obj_t * label);
|
|
|
|
#if USE_LV_ANIMATION
|
|
static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x);
|
|
static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y);
|
|
#endif
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
static lv_signal_func_t ancestor_signal;
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Create a label objects
|
|
* @param par pointer to an object, it will be the parent of the new label
|
|
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
|
|
* @return pointer to the created button
|
|
*/
|
|
lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy)
|
|
{
|
|
LV_LOG_TRACE("label create started");
|
|
|
|
/*Create a basic object*/
|
|
lv_obj_t * new_label = lv_obj_create(par, copy);
|
|
lv_mem_assert(new_label);
|
|
if(new_label == NULL) return NULL;
|
|
|
|
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_label);
|
|
|
|
/*Extend the basic object to a label object*/
|
|
lv_obj_allocate_ext_attr(new_label, sizeof(lv_label_ext_t));
|
|
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(new_label);
|
|
lv_mem_assert(ext);
|
|
if(ext == NULL) return NULL;
|
|
|
|
ext->text = NULL;
|
|
ext->static_txt = 0;
|
|
ext->recolor = 0;
|
|
ext->body_draw = 0;
|
|
ext->align = LV_LABEL_ALIGN_LEFT;
|
|
ext->dot_end = LV_LABEL_DOT_END_INV;
|
|
ext->long_mode = LV_LABEL_LONG_EXPAND;
|
|
ext->anim_speed = LV_LABEL_SCROLL_SPEED;
|
|
ext->offset.x = 0;
|
|
ext->offset.y = 0;
|
|
#if USE_LV_MULTI_LANG
|
|
ext->lang_txt_id = LV_LANG_TXT_ID_NONE;
|
|
#endif
|
|
lv_obj_set_design_func(new_label, lv_label_design);
|
|
lv_obj_set_signal_func(new_label, lv_label_signal);
|
|
|
|
/*Init the new label*/
|
|
if(copy == NULL) {
|
|
lv_obj_set_click(new_label, false);
|
|
lv_label_set_long_mode(new_label, LV_LABEL_LONG_EXPAND);
|
|
lv_label_set_text(new_label, "Text");
|
|
lv_label_set_style(new_label, NULL); /*Inherit parent's style*/
|
|
}
|
|
/*Copy 'copy' if not NULL*/
|
|
else {
|
|
lv_label_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
|
|
lv_label_set_long_mode(new_label, lv_label_get_long_mode(copy));
|
|
lv_label_set_recolor(new_label, lv_label_get_recolor(copy));
|
|
lv_label_set_body_draw(new_label, lv_label_get_body_draw(copy));
|
|
lv_label_set_align(new_label, lv_label_get_align(copy));
|
|
if(copy_ext->static_txt == 0) lv_label_set_text(new_label, lv_label_get_text(copy));
|
|
else lv_label_set_static_text(new_label, lv_label_get_text(copy));
|
|
|
|
/*In DOT mode save the text byte-to-byte because a '\0' can be in the middle*/
|
|
if(copy_ext->long_mode == LV_LABEL_LONG_DOT) {
|
|
ext->text = lv_mem_realloc(ext->text, lv_mem_get_size(copy_ext->text));
|
|
lv_mem_assert(ext->text);
|
|
if(ext->text == NULL) return NULL;
|
|
memcpy(ext->text, copy_ext->text, lv_mem_get_size(copy_ext->text));
|
|
}
|
|
|
|
memcpy(ext->dot_tmp, copy_ext->dot_tmp, sizeof(ext->dot_tmp));
|
|
ext->dot_end = copy_ext->dot_end;
|
|
|
|
/*Refresh the style with new signal function*/
|
|
lv_obj_refresh_style(new_label);
|
|
}
|
|
|
|
|
|
LV_LOG_INFO("label created");
|
|
|
|
return new_label;
|
|
}
|
|
|
|
/*=====================
|
|
* Setter functions
|
|
*====================*/
|
|
|
|
/**
|
|
* Set a new text for a label. Memory will be allocated to store the text by the label.
|
|
* @param label pointer to a label object
|
|
* @param text '\0' terminated character string. NULL to refresh with the current text.
|
|
*/
|
|
void lv_label_set_text(lv_obj_t * label, const char * text)
|
|
{
|
|
lv_obj_invalidate(label);
|
|
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
/*If text is NULL then refresh */
|
|
if(text == NULL) {
|
|
lv_label_refr_text(label);
|
|
return;
|
|
}
|
|
|
|
if(ext->text == text && ext->static_txt == 0) {
|
|
/*If set its own text then reallocate it (maybe its size changed)*/
|
|
ext->text = lv_mem_realloc(ext->text, strlen(ext->text) + 1);
|
|
lv_mem_assert(ext->text);
|
|
if(ext->text == NULL) return;
|
|
} else {
|
|
/*Allocate space for the new text*/
|
|
uint32_t len = strlen(text) + 1;
|
|
if(ext->text != NULL && ext->static_txt == 0) {
|
|
lv_mem_free(ext->text);
|
|
ext->text = NULL;
|
|
}
|
|
|
|
ext->text = lv_mem_alloc(len);
|
|
lv_mem_assert(ext->text);
|
|
if(ext->text == NULL) return;
|
|
|
|
strcpy(ext->text, text);
|
|
ext->static_txt = 0; /*Now the text is dynamically allocated*/
|
|
}
|
|
|
|
lv_label_refr_text(label);
|
|
}
|
|
|
|
/**
|
|
* Set a new text for a label from a character array. The array don't has to be '\0' terminated.
|
|
* Memory will be allocated to store the array by the label.
|
|
* @param label pointer to a label object
|
|
* @param array array of characters or NULL to refresh the label
|
|
* @param size the size of 'array' in bytes
|
|
*/
|
|
void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size)
|
|
{
|
|
lv_obj_invalidate(label);
|
|
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
/*If trying to set its own text or the array is NULL then refresh */
|
|
if(array == ext->text || array == NULL) {
|
|
lv_label_refr_text(label);
|
|
return;
|
|
}
|
|
|
|
/*Allocate space for the new text*/
|
|
if(ext->text != NULL && ext->static_txt == 0) {
|
|
lv_mem_free(ext->text);
|
|
ext->text = NULL;
|
|
}
|
|
ext->text = lv_mem_alloc(size + 1);
|
|
lv_mem_assert(ext->text);
|
|
if(ext->text == NULL) return;
|
|
|
|
memcpy(ext->text, array, size);
|
|
ext->text[size] = '\0';
|
|
ext->static_txt = 0; /*Now the text is dynamically allocated*/
|
|
|
|
lv_label_refr_text(label);
|
|
}
|
|
|
|
/**
|
|
* Set a static text. It will not be saved by the label so the 'text' variable
|
|
* has to be 'alive' while the label exist.
|
|
* @param label pointer to a label object
|
|
* @param text pointer to a text. NULL to refresh with the current text.
|
|
*/
|
|
void lv_label_set_static_text(lv_obj_t * label, const char * text)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(ext->static_txt == 0 && ext->text != NULL) {
|
|
lv_mem_free(ext->text);
|
|
ext->text = NULL;
|
|
}
|
|
|
|
if(text != NULL) {
|
|
ext->static_txt = 1;
|
|
ext->text = (char *) text;
|
|
}
|
|
|
|
lv_label_refr_text(label);
|
|
}
|
|
|
|
#if USE_LV_MULTI_LANG
|
|
/**
|
|
*Set a text ID which refers a the same text but in a different languages
|
|
* @param label pointer to a label object
|
|
* @param txt_id ID of the text
|
|
*/
|
|
void lv_label_set_text_id(lv_obj_t * label, uint32_t txt_id)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
ext->lang_txt_id = txt_id;
|
|
|
|
/*Apply the new language*/
|
|
label->signal_func(label, LV_SIGNAL_LANG_CHG, NULL);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Set the behavior of the label with longer text then the object size
|
|
* @param label pointer to a label object
|
|
* @param long_mode the new mode from 'lv_label_long_mode' enum.
|
|
* In LV_LONG_BREAK/LONG/ROLL the size of the label should be set AFTER this function
|
|
*/
|
|
void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
#if USE_LV_ANIMATION
|
|
/*Delete the old animation (if exists)*/
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_x);
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_y);
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_x);
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_y);
|
|
#endif
|
|
ext->offset.x = 0;
|
|
ext->offset.y = 0;
|
|
|
|
if(long_mode == LV_LABEL_LONG_ROLL || long_mode == LV_LABEL_LONG_CROP) ext->expand = 1;
|
|
else ext->expand = 0;
|
|
|
|
/*Restore the character under the dots*/
|
|
if(ext->long_mode == LV_LABEL_LONG_DOT && ext->dot_end != LV_LABEL_DOT_END_INV) {
|
|
lv_label_revert_dots(label);
|
|
}
|
|
|
|
ext->long_mode = long_mode;
|
|
lv_label_refr_text(label);
|
|
}
|
|
|
|
/**
|
|
* Set the align of the label (left or center)
|
|
* @param label pointer to a label object
|
|
* @param align 'LV_LABEL_ALIGN_LEFT' or 'LV_LABEL_ALIGN_LEFT'
|
|
*/
|
|
void lv_label_set_align(lv_obj_t * label, lv_label_align_t align)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(ext->align == align) return;
|
|
|
|
ext->align = align;
|
|
|
|
lv_obj_invalidate(label); /*Enough to invalidate because alignment is only drawing related (lv_refr_label_text() not required)*/
|
|
}
|
|
|
|
/**
|
|
* Enable the recoloring by in-line commands
|
|
* @param label pointer to a label object
|
|
* @param en true: enable recoloring, false: disable
|
|
*/
|
|
void lv_label_set_recolor(lv_obj_t * label, bool en)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(ext->recolor == en) return;
|
|
|
|
ext->recolor = en == false ? 0 : 1;
|
|
|
|
lv_label_refr_text(label); /*Refresh the text because the potential colo codes in text needs to be hided or revealed*/
|
|
}
|
|
|
|
/**
|
|
* Set the label to draw (or not draw) background specified in its style's body
|
|
* @param label pointer to a label object
|
|
* @param en true: draw body; false: don't draw body
|
|
*/
|
|
void lv_label_set_body_draw(lv_obj_t * label, bool en)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(ext->body_draw == en) return;
|
|
|
|
ext->body_draw = en == false ? 0 : 1;
|
|
|
|
lv_obj_refresh_ext_size(label);
|
|
|
|
lv_obj_invalidate(label);
|
|
}
|
|
|
|
/**
|
|
* Set the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
|
|
* @param label pointer to a label object
|
|
* @param anim_speed speed of animation in px/sec unit
|
|
*/
|
|
void lv_label_set_anim_speed(lv_obj_t * label, uint16_t anim_speed)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(ext->anim_speed == anim_speed) return;
|
|
|
|
ext->anim_speed = anim_speed;
|
|
|
|
if(ext->long_mode == LV_LABEL_LONG_ROLL || ext->long_mode == LV_LABEL_LONG_SCROLL) {
|
|
lv_label_refr_text(label);
|
|
}
|
|
}
|
|
|
|
/*=====================
|
|
* Getter functions
|
|
*====================*/
|
|
|
|
/**
|
|
* Get the text of a label
|
|
* @param label pointer to a label object
|
|
* @return the text of the label
|
|
*/
|
|
char * lv_label_get_text(const lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
return ext->text;
|
|
}
|
|
|
|
#if USE_LV_MULTI_LANG
|
|
/**
|
|
* Get the text ID of the label. (Used by the multi-language feature)
|
|
* @param label pointer to a label object
|
|
* @return ID of the text
|
|
*/
|
|
uint16_t lv_label_get_text_id(lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
return ext->lang_txt_id;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Get the long mode of a label
|
|
* @param label pointer to a label object
|
|
* @return the long mode
|
|
*/
|
|
lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
return ext->long_mode;
|
|
}
|
|
|
|
/**
|
|
* Get the align attribute
|
|
* @param label pointer to a label object
|
|
* @return LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER
|
|
*/
|
|
lv_label_align_t lv_label_get_align(const lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
return ext->align;
|
|
}
|
|
|
|
/**
|
|
* Get the recoloring attribute
|
|
* @param label pointer to a label object
|
|
* @return true: recoloring is enabled, false: disable
|
|
*/
|
|
bool lv_label_get_recolor(const lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
return ext->recolor == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the body draw attribute
|
|
* @param label pointer to a label object
|
|
* @return true: draw body; false: don't draw body
|
|
*/
|
|
bool lv_label_get_body_draw(const lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
return ext->body_draw == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
|
|
* @param label pointer to a label object
|
|
* @return speed of animation in px/sec unit
|
|
*/
|
|
uint16_t lv_label_get_anim_speed(const lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
return ext->anim_speed;
|
|
}
|
|
|
|
/**
|
|
* Get the relative x and y coordinates of a letter
|
|
* @param label pointer to a label object
|
|
* @param index index of the letter [0 ... text length]. Expressed in character index, not byte index (different in UTF-8)
|
|
* @param pos store the result here (E.g. index = 0 gives 0;0 coordinates)
|
|
*/
|
|
void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos)
|
|
{
|
|
const char * txt = lv_label_get_text(label);
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
uint32_t line_start = 0;
|
|
uint32_t new_line_start = 0;
|
|
lv_coord_t max_w = lv_obj_get_width(label);
|
|
lv_style_t * style = lv_obj_get_style(label);
|
|
const lv_font_t * font = style->text.font;
|
|
uint8_t letter_height = lv_font_get_height(font);
|
|
lv_coord_t y = 0;
|
|
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
|
|
|
|
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
|
|
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
|
|
if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
|
|
|
|
/*If the width will be expanded the set the max length to very big */
|
|
if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) {
|
|
max_w = LV_COORD_MAX;
|
|
}
|
|
|
|
index = lv_txt_encoded_get_byte_id(txt, index);
|
|
|
|
/*Search the line of the index letter */;
|
|
while(txt[new_line_start] != '\0') {
|
|
new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
|
|
if(index < new_line_start || txt[new_line_start] == '\0') break; /*The line of 'index' letter begins at 'line_start'*/
|
|
|
|
y += letter_height + style->text.line_space;
|
|
line_start = new_line_start;
|
|
}
|
|
|
|
/*If the last character is line break then go to the next line*/
|
|
if(index > 0) {
|
|
if((txt[index - 1] == '\n' || txt[index - 1] == '\r') && txt[index] == '\0') {
|
|
y += letter_height + style->text.line_space;
|
|
line_start = index;
|
|
}
|
|
}
|
|
|
|
/*Calculate the x coordinate*/
|
|
lv_coord_t x = lv_txt_get_width(&txt[line_start], index - line_start,
|
|
font, style->text.letter_space, flag);
|
|
|
|
if(index != line_start) x += style->text.letter_space;
|
|
|
|
if(ext->align == LV_LABEL_ALIGN_CENTER) {
|
|
lv_coord_t line_w;
|
|
line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start,
|
|
font, style->text.letter_space, flag);
|
|
x += lv_obj_get_width(label) / 2 - line_w / 2;
|
|
|
|
} else if(ext->align == LV_LABEL_ALIGN_RIGHT) {
|
|
lv_coord_t line_w;
|
|
line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start,
|
|
font, style->text.letter_space, flag);
|
|
|
|
x += lv_obj_get_width(label) - line_w;
|
|
}
|
|
pos->x = x;
|
|
pos->y = y;
|
|
}
|
|
|
|
/**
|
|
* Get the index of letter on a relative point of a label
|
|
* @param label pointer to label object
|
|
* @param pos pointer to point with coordinates on a the label
|
|
* @return the index of the letter on the 'pos_p' point (E.g. on 0;0 is the 0. letter)
|
|
* Expressed in character index and not byte index (different in UTF-8)
|
|
*/
|
|
uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos)
|
|
{
|
|
const char * txt = lv_label_get_text(label);
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
uint32_t line_start = 0;
|
|
uint32_t new_line_start = 0;
|
|
lv_coord_t max_w = lv_obj_get_width(label);
|
|
lv_style_t * style = lv_obj_get_style(label);
|
|
const lv_font_t * font = style->text.font;
|
|
uint8_t letter_height = lv_font_get_height(font);
|
|
lv_coord_t y = 0;
|
|
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
|
|
|
|
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
|
|
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
|
|
if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
|
|
|
|
/*If the width will be expanded set the max length to very big */
|
|
if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) {
|
|
max_w = LV_COORD_MAX;
|
|
}
|
|
|
|
/*Search the line of the index letter */;
|
|
while(txt[line_start] != '\0') {
|
|
new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
|
|
|
|
if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/
|
|
y += letter_height + style->text.line_space;
|
|
|
|
line_start = new_line_start;
|
|
}
|
|
|
|
/*Calculate the x coordinate*/
|
|
lv_coord_t x = 0;
|
|
if(ext->align == LV_LABEL_ALIGN_CENTER) {
|
|
lv_coord_t line_w;
|
|
line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start,
|
|
font, style->text.letter_space, flag);
|
|
x += lv_obj_get_width(label) / 2 - line_w / 2;
|
|
}
|
|
|
|
lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT;
|
|
uint32_t i = line_start;
|
|
uint32_t i_current = i;
|
|
uint32_t letter;
|
|
while(i < new_line_start - 1) {
|
|
letter = lv_txt_encoded_next(txt, &i); /*Be careful 'i' already points to the next character*/
|
|
/*Handle the recolor command*/
|
|
if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
|
|
if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) {
|
|
continue; /*Skip the letter is it is part of a command*/
|
|
}
|
|
}
|
|
|
|
x += lv_font_get_width(font, letter);
|
|
if(pos->x < x) {
|
|
i = i_current;
|
|
break;
|
|
}
|
|
x += style->text.letter_space;
|
|
i_current = i;
|
|
}
|
|
|
|
return lv_encoded_get_char_id(txt, i);
|
|
}
|
|
|
|
|
|
/*=====================
|
|
* Other functions
|
|
*====================*/
|
|
|
|
/**
|
|
* Insert a text to the label. The label text can not be static.
|
|
* @param label pointer to a label object
|
|
* @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8)
|
|
* 0: before first char.
|
|
* LV_LABEL_POS_LAST: after last char.
|
|
* @param txt pointer to the text to insert
|
|
*/
|
|
void lv_label_ins_text(lv_obj_t * label, uint32_t pos, const char * txt)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
/*Can not append to static text*/
|
|
if(ext->static_txt != 0) return;
|
|
|
|
lv_obj_invalidate(label);
|
|
|
|
/*Allocate space for the new text*/
|
|
uint32_t old_len = strlen(ext->text);
|
|
uint32_t ins_len = strlen(txt);
|
|
uint32_t new_len = ins_len + old_len;
|
|
ext->text = lv_mem_realloc(ext->text, new_len + 1);
|
|
lv_mem_assert(ext->text);
|
|
if(ext->text == NULL) return;
|
|
|
|
if(pos == LV_LABEL_POS_LAST) {
|
|
#if LV_TXT_UTF8 == 0
|
|
pos = old_len;
|
|
#else
|
|
pos = lv_txt_get_encoded_length(ext->text);
|
|
#endif
|
|
}
|
|
|
|
lv_txt_ins(ext->text, pos, txt);
|
|
|
|
lv_label_refr_text(label);
|
|
}
|
|
|
|
/**
|
|
* Delete characters from a label. The label text can not be static.
|
|
* @param label pointer to a label object
|
|
* @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8)
|
|
* 0: before first char.
|
|
* @param cnt number of characters to cut
|
|
*/
|
|
void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
/*Can not append to static text*/
|
|
if(ext->static_txt != 0) return;
|
|
|
|
lv_obj_invalidate(label);
|
|
|
|
char * label_txt = lv_label_get_text(label);
|
|
/*Delete the characters*/
|
|
lv_txt_cut(label_txt, pos, cnt);
|
|
|
|
/*Refresh the label*/
|
|
lv_label_refr_text(label);
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Handle the drawing related tasks of the labels
|
|
* @param label pointer to a label object
|
|
* @param mask the object will be drawn only in this area
|
|
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
|
|
* (return 'true' if yes)
|
|
* LV_DESIGN_DRAW: draw the object (always return 'true')
|
|
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
|
|
* @param return true/false, depends on 'mode'
|
|
*/
|
|
static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode)
|
|
{
|
|
/* A label never covers an area */
|
|
if(mode == LV_DESIGN_COVER_CHK) return false;
|
|
else if(mode == LV_DESIGN_DRAW_MAIN) {
|
|
lv_area_t coords;
|
|
lv_style_t * style = lv_obj_get_style(label);
|
|
lv_opa_t opa_scale = lv_obj_get_opa_scale(label);
|
|
lv_obj_get_coords(label, &coords);
|
|
|
|
#if USE_LV_GROUP
|
|
lv_group_t * g = lv_obj_get_group(label);
|
|
if(lv_group_get_focused(g) == label) {
|
|
lv_draw_rect(&coords, mask, style, opa_scale);
|
|
}
|
|
#endif
|
|
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
if(ext->body_draw) {
|
|
lv_area_t bg;
|
|
lv_obj_get_coords(label, &bg);
|
|
bg.x1 -= style->body.padding.hor;
|
|
bg.x2 += style->body.padding.hor;
|
|
bg.y1 -= style->body.padding.ver;
|
|
bg.y2 += style->body.padding.ver;
|
|
|
|
lv_draw_rect(&bg, mask, style, lv_obj_get_opa_scale(label));
|
|
}
|
|
|
|
/*TEST: draw a background for the label*/
|
|
//lv_draw_rect(&label->coords, mask, &lv_style_plain_color, LV_OPA_COVER);
|
|
|
|
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
|
|
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
|
|
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
|
|
if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
|
|
if(ext->align == LV_LABEL_ALIGN_RIGHT) flag |= LV_TXT_FLAG_RIGHT;
|
|
|
|
/* In ROLL mode the CENTER and RIGHT are pointless so remove them.
|
|
* (In addition they will result mis-alignment is this case)*/
|
|
if((ext->long_mode == LV_LABEL_LONG_ROLL) &&
|
|
(ext->align == LV_LABEL_ALIGN_CENTER || ext->align == LV_LABEL_ALIGN_RIGHT)) {
|
|
lv_point_t size;
|
|
lv_txt_get_size(&size, ext->text, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag);
|
|
if(size.x > lv_obj_get_width(label)) {
|
|
flag &= ~LV_TXT_FLAG_RIGHT;
|
|
flag &= ~LV_TXT_FLAG_CENTER;
|
|
}
|
|
}
|
|
|
|
lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Signal function of the label
|
|
* @param label pointer to a label object
|
|
* @param sign a signal type from lv_signal_t enum
|
|
* @param param pointer to a signal specific variable
|
|
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
|
|
*/
|
|
static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param)
|
|
{
|
|
lv_res_t res;
|
|
|
|
/* Include the ancient signal function */
|
|
res = ancestor_signal(label, sign, param);
|
|
if(res != LV_RES_OK) return res;
|
|
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(sign == LV_SIGNAL_CLEANUP) {
|
|
if(ext->static_txt == 0) {
|
|
lv_mem_free(ext->text);
|
|
ext->text = NULL;
|
|
}
|
|
} else if(sign == LV_SIGNAL_STYLE_CHG) {
|
|
/*Revert dots for proper refresh*/
|
|
lv_label_revert_dots(label);
|
|
|
|
lv_label_refr_text(label);
|
|
} else if(sign == LV_SIGNAL_CORD_CHG) {
|
|
if(lv_area_get_width(&label->coords) != lv_area_get_width(param) ||
|
|
lv_area_get_height(&label->coords) != lv_area_get_height(param)) {
|
|
lv_label_revert_dots(label);
|
|
lv_label_refr_text(label);
|
|
}
|
|
} else if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
|
|
if(ext->body_draw) {
|
|
lv_style_t * style = lv_label_get_style(label);
|
|
label->ext_size = LV_MATH_MAX(label->ext_size, style->body.padding.hor);
|
|
label->ext_size = LV_MATH_MAX(label->ext_size, style->body.padding.ver);
|
|
}
|
|
} else if(sign == LV_SIGNAL_LANG_CHG) {
|
|
#if USE_LV_MULTI_LANG
|
|
if(ext->lang_txt_id != LV_LANG_TXT_ID_NONE) {
|
|
const char * lang_txt = lv_lang_get_text(ext->lang_txt_id);
|
|
if(lang_txt) {
|
|
lv_label_set_text(label, lang_txt);
|
|
} else {
|
|
LV_LOG_WARN("lv_lang_get_text return NULL for a label's text");
|
|
}
|
|
}
|
|
#endif
|
|
} else if(sign == LV_SIGNAL_GET_TYPE) {
|
|
lv_obj_type_t * buf = param;
|
|
uint8_t i;
|
|
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
|
|
if(buf->type[i] == NULL) break;
|
|
}
|
|
buf->type[i] = "lv_label";
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Refresh the label with its text stored in its extended data
|
|
* @param label pointer to a label object
|
|
*/
|
|
static void lv_label_refr_text(lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
|
|
if(ext->text == NULL) return;
|
|
|
|
lv_coord_t max_w = lv_obj_get_width(label);
|
|
lv_style_t * style = lv_obj_get_style(label);
|
|
const lv_font_t * font = style->text.font;
|
|
|
|
/*If the width will be expanded set the max length to very big */
|
|
if(ext->long_mode == LV_LABEL_LONG_EXPAND ||
|
|
ext->long_mode == LV_LABEL_LONG_SCROLL) {
|
|
max_w = LV_COORD_MAX;
|
|
}
|
|
|
|
/*Calc. the height and longest line*/
|
|
lv_point_t size;
|
|
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
|
|
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
|
|
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
|
|
lv_txt_get_size(&size, ext->text, font, style->text.letter_space, style->text.line_space, max_w, flag);
|
|
|
|
/*Set the full size in expand mode*/
|
|
if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) {
|
|
lv_obj_set_size(label, size.x, size.y);
|
|
|
|
/*Start scrolling if the label is greater then its parent*/
|
|
if(ext->long_mode == LV_LABEL_LONG_SCROLL) {
|
|
#if USE_LV_ANIMATION
|
|
lv_obj_t * parent = lv_obj_get_parent(label);
|
|
|
|
/*Delete the potential previous scroller animations*/
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_x);
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_y);
|
|
|
|
lv_anim_t anim;
|
|
anim.var = label;
|
|
anim.repeat = 1;
|
|
anim.playback = 1;
|
|
anim.start = 0;
|
|
anim.act_time = 0;
|
|
anim.end_cb = NULL;
|
|
anim.path = lv_anim_path_linear;
|
|
|
|
anim.playback_pause = (((lv_font_get_width(style->text.font, ' ') +
|
|
style->text.letter_space) * 1000) / ext->anim_speed) * ANIM_WAIT_CHAR_COUNT;
|
|
anim.repeat_pause = anim.playback_pause;
|
|
|
|
if(lv_obj_get_width(label) > lv_obj_get_width(parent)) {
|
|
anim.end = lv_obj_get_width(parent) - lv_obj_get_width(label);
|
|
anim.fp = (lv_anim_fp_t) lv_obj_set_x;
|
|
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
|
|
lv_anim_create(&anim);
|
|
} else if(lv_obj_get_height(label) > lv_obj_get_height(parent)) {
|
|
anim.end = lv_obj_get_height(parent) - lv_obj_get_height(label) - lv_font_get_height(font);
|
|
anim.fp = (lv_anim_fp_t)lv_obj_set_y;
|
|
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
|
|
lv_anim_create(&anim);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
/*In roll mode keep the size but start offset animations*/
|
|
else if(ext->long_mode == LV_LABEL_LONG_ROLL) {
|
|
#if USE_LV_ANIMATION
|
|
lv_anim_t anim;
|
|
anim.var = label;
|
|
anim.repeat = 1;
|
|
anim.playback = 1;
|
|
anim.start = 0;
|
|
anim.act_time = 0;
|
|
anim.end_cb = NULL;
|
|
anim.path = lv_anim_path_linear;
|
|
anim.playback_pause = (((lv_font_get_width(style->text.font, ' ') + style->text.letter_space) * 1000) / ext->anim_speed) * ANIM_WAIT_CHAR_COUNT;
|
|
anim.repeat_pause = anim.playback_pause;
|
|
|
|
bool hor_anim = false;
|
|
if(size.x > lv_obj_get_width(label)) {
|
|
anim.end = lv_obj_get_width(label) - size.x;
|
|
anim.fp = (lv_anim_fp_t) lv_label_set_offset_x;
|
|
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
|
|
lv_anim_create(&anim);
|
|
hor_anim = true;
|
|
} else {
|
|
/*Delete the offset animation if not required*/
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_x);
|
|
ext->offset.x = 0;
|
|
}
|
|
|
|
if(size.y > lv_obj_get_height(label) && hor_anim == false) {
|
|
anim.end = lv_obj_get_height(label) - size.y - (lv_font_get_height(font));
|
|
anim.fp = (lv_anim_fp_t)lv_label_set_offset_y;
|
|
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
|
|
lv_anim_create(&anim);
|
|
} else {
|
|
/*Delete the offset animation if not required*/
|
|
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_y);
|
|
ext->offset.y = 0;
|
|
}
|
|
#endif
|
|
} else if(ext->long_mode == LV_LABEL_LONG_DOT) {
|
|
if(size.y <= lv_obj_get_height(label)) { /*No dots are required, the text is short enough*/
|
|
ext->dot_end = LV_LABEL_DOT_END_INV;
|
|
} else if(lv_txt_get_encoded_length(ext->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/
|
|
ext->dot_end = LV_LABEL_DOT_END_INV;
|
|
} else {
|
|
lv_point_t p;
|
|
p.x = lv_obj_get_width(label) - (lv_font_get_width(style->text.font, '.') + style->text.letter_space) * LV_LABEL_DOT_NUM; /*Shrink with dots*/
|
|
p.y = lv_obj_get_height(label);
|
|
p.y -= p.y % (lv_font_get_height(style->text.font) + style->text.line_space); /*Round down to the last line*/
|
|
p.y -= style->text.line_space; /*Trim the last line space*/
|
|
uint32_t letter_id = lv_label_get_letter_on(label, &p);
|
|
|
|
|
|
#if LV_TXT_UTF8 == 0
|
|
/*Save letters under the dots and replace them with dots*/
|
|
uint8_t i;
|
|
for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
|
|
ext->dot_tmp[i] = ext->text[letter_id + i];
|
|
ext->text[letter_id + i] = '.';
|
|
}
|
|
|
|
ext->dot_tmp[LV_LABEL_DOT_NUM] = ext->text[letter_id + LV_LABEL_DOT_NUM];
|
|
ext->text[letter_id + LV_LABEL_DOT_NUM] = '\0';
|
|
|
|
ext->dot_end = letter_id + LV_LABEL_DOT_NUM;
|
|
#else
|
|
/*Save letters under the dots and replace them with dots*/
|
|
uint32_t i;
|
|
uint32_t byte_id = lv_txt_encoded_get_byte_id(ext->text, letter_id);
|
|
uint32_t byte_id_ori = byte_id;
|
|
uint8_t len = 0;
|
|
for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
|
|
len += lv_txt_encoded_size(&ext->text[byte_id]);
|
|
lv_txt_encoded_next(ext->text, &byte_id);
|
|
}
|
|
|
|
memcpy(ext->dot_tmp, &ext->text[byte_id_ori], len);
|
|
ext->dot_tmp[len] = '\0'; /*Close with a zero*/
|
|
|
|
for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
|
|
ext->text[byte_id_ori + i] = '.';
|
|
}
|
|
ext->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0';
|
|
|
|
ext->dot_end = letter_id + LV_LABEL_DOT_NUM;
|
|
#endif
|
|
|
|
}
|
|
}
|
|
/*In break mode only the height can change*/
|
|
else if(ext->long_mode == LV_LABEL_LONG_BREAK) {
|
|
lv_obj_set_height(label, size.y);
|
|
}
|
|
/*Do not set the size in Clip mode*/
|
|
else if(ext->long_mode == LV_LABEL_LONG_CROP) {
|
|
/*Do nothing*/
|
|
}
|
|
|
|
|
|
lv_obj_invalidate(label);
|
|
}
|
|
|
|
static void lv_label_revert_dots(lv_obj_t * label)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
if(ext->long_mode != LV_LABEL_LONG_DOT) return;
|
|
if(ext->dot_end == LV_LABEL_DOT_END_INV) return;
|
|
#if LV_TXT_UTF8 == 0
|
|
uint32_t i;
|
|
for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
|
|
ext->text[ext->dot_end - i] = ext->dot_tmp[LV_LABEL_DOT_NUM - i];
|
|
}
|
|
#else
|
|
uint32_t letter_i = ext->dot_end - LV_LABEL_DOT_NUM;
|
|
uint32_t byte_i = lv_txt_encoded_get_byte_id(ext->text, letter_i);
|
|
|
|
/*Restore the characters*/
|
|
uint8_t i = 0;
|
|
while(ext->dot_tmp[i] != '\0') {
|
|
ext->text[byte_i + i] = ext->dot_tmp[i];
|
|
i++;
|
|
}
|
|
#endif
|
|
|
|
ext->dot_end = LV_LABEL_DOT_END_INV;
|
|
}
|
|
|
|
#if USE_LV_ANIMATION
|
|
static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
ext->offset.x = x;
|
|
lv_obj_invalidate(label);
|
|
}
|
|
|
|
static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y)
|
|
{
|
|
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
|
|
ext->offset.y = y;
|
|
lv_obj_invalidate(label);
|
|
}
|
|
#endif
|
|
#endif
|