feat(span): support bidi for span (#7717)

Signed-off-by: liuhongchao <liuhongchao@xiaomi.com>
This commit is contained in:
红桃六
2025-02-20 10:18:17 +08:00
committed by GitHub
parent 377cc5aa80
commit 28f902d702
6 changed files with 200 additions and 26 deletions

View File

@@ -336,9 +336,19 @@ void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_ds
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);
size_t bidi_size = line_end - line_start;
char * bidi_txt = lv_malloc(bidi_size + 1);
LV_ASSERT_MALLOC(bidi_txt);
lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
/**
* has_bided = 1: already executed lv_bidi_process_paragraph.
* has_bided = 0: has not been executed lv_bidi_process_paragraph.*/
if(dsc->has_bided) {
lv_memcpy(bidi_txt, &dsc->text[line_start], bidi_size);
}
else {
lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, bidi_size, base_dir, NULL, 0);
}
#else
const char * bidi_txt = dsc->text + line_start;
#endif
@@ -349,9 +359,14 @@ void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_ds
/* Check if the text selection is enabled */
if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
#if LV_USE_BIDI
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
uint32_t c_idx = 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, c_idx, NULL);
if(dsc->has_bided) {
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
}
else {
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
uint32_t c_idx = 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, c_idx, NULL);
}
#else
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
#endif

View File

@@ -60,6 +60,11 @@ typedef struct {
* Indicate that the text is constant and its pointer can be safely saved e.g. in a cache.
*/
uint8_t text_static : 1;
/**
* 1: already executed lv_bidi_process_paragraph.
* 0: has not been executed lv_bidi_process_paragraph.*/
uint8_t has_bided : 1;
lv_draw_label_hint_t * hint;
} lv_draw_label_dsc_t;

View File

@@ -15,6 +15,8 @@
#include "../../misc/lv_assert.h"
#include "../../misc/lv_text_private.h"
#include "../../misc/lv_bidi_private.h"
#include "../../misc/lv_text_ap.h"
#include "../../core/lv_global.h"
/*********************
@@ -170,7 +172,13 @@ void lv_span_set_text(lv_span_t * span, const char * text)
return;
}
size_t text_alloc_len = lv_strlen(text) + 1;
size_t text_alloc_len = 0;
#if LV_USE_ARABIC_PERSIAN_CHARS
text_alloc_len = lv_text_ap_calc_bytes_count(text);
#else
text_alloc_len = lv_strlen(text) + 1;
#endif
if(span->txt == NULL || span->static_flag == 1) {
span->txt = lv_malloc(text_alloc_len);
@@ -184,7 +192,12 @@ void lv_span_set_text(lv_span_t * span, const char * text)
if(span->txt == NULL) return;
span->static_flag = 0;
#if LV_USE_ARABIC_PERSIAN_CHARS
lv_text_ap_proc(text, span->txt);
#else
lv_memcpy(span->txt, text, text_alloc_len);
#endif
}
void lv_spangroup_set_span_text(lv_obj_t * obj, lv_span_t * span, const char * text)
@@ -204,7 +217,16 @@ void lv_span_set_text_static(lv_span_t * span, const char * text)
span->txt = NULL;
}
span->static_flag = 1;
#if LV_USE_ARABIC_PERSIAN_CHARS
size_t text_alloc_len = lv_text_ap_calc_bytes_count(text);
span->txt = lv_malloc(text_alloc_len);
LV_ASSERT_MALLOC(span->txt)
lv_text_ap_proc(text, span->txt);
span->static_flag = 0;
#else
span->txt = (char *)text;
#endif
}
void lv_spangroup_set_span_text_static(lv_obj_t * obj, lv_span_t * span, const char * text)
@@ -952,6 +974,28 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
lv_span_t * cur_span = lv_ll_get_head(&spans->child_ll);
const char * cur_txt = cur_span->txt;
span_text_check(&cur_txt);
lv_text_align_t align = lv_obj_get_style_text_align(obj, LV_PART_MAIN);
#if LV_USE_BIDI
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
if(base_dir == LV_BASE_DIR_AUTO) {
base_dir = lv_bidi_detect_base_dir(cur_txt) == LV_BASE_DIR_RTL ? LV_BASE_DIR_RTL : LV_BASE_DIR_AUTO;
}
while(cur_span) {
cur_span = lv_ll_get_next(&spans->child_ll, cur_span);
if(cur_span == NULL) break;
cur_txt = cur_span->txt;
span_text_check(&cur_txt);
if(base_dir == LV_BASE_DIR_AUTO) {
base_dir = lv_bidi_detect_base_dir(cur_txt) == LV_BASE_DIR_RTL ? LV_BASE_DIR_RTL : LV_BASE_DIR_AUTO;
}
}
cur_span = lv_ll_get_head(&spans->child_ll);
cur_txt = cur_span->txt;
#endif
uint32_t cur_txt_ofs = 0;
lv_snippet_t snippet; /* use to save cur_span info and push it to stack */
lv_memzero(&snippet, sizeof(snippet));
@@ -1064,36 +1108,74 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
}
/* align deal with */
lv_text_align_t align = lv_obj_get_style_text_align(obj, LV_PART_MAIN);
#if LV_USE_BIDI
if(base_dir == LV_BASE_DIR_AUTO) {
base_dir = LV_BASE_DIR_LTR;
}
if(align == LV_TEXT_ALIGN_AUTO) {
if(base_dir == LV_BASE_DIR_RTL) align = LV_TEXT_ALIGN_RIGHT;
else align = LV_TEXT_ALIGN_LEFT;
}
#endif
int32_t align_ofs = 0;
int32_t txts_w = is_first_line ? indent : 0;
uint32_t i_item;
for(i_item = 0; i_item < item_cnt; i_item++) {
lv_snippet_t * pinfo = lv_get_snippet(i_item);
if(ellipsis_valid && i_item == item_cnt - 1) {
uint32_t n_ofs = 0;
lv_text_get_snippet(pinfo->txt, pinfo->font, pinfo->letter_space, max_width - txts_w,
LV_TEXT_FLAG_BREAK_ALL, &pinfo->txt_w, &n_ofs);
pinfo->bytes = n_ofs;
}
txts_w = txts_w + pinfo->txt_w;
}
txts_w -= lv_get_snippet(item_cnt - 1)->letter_space;
align_ofs = max_width > txts_w ? max_width - txts_w : 0;
if(align == LV_TEXT_ALIGN_CENTER) {
align_ofs = align_ofs >> 1;
}
if(align == LV_TEXT_ALIGN_CENTER || align == LV_TEXT_ALIGN_RIGHT) {
int32_t align_ofs = 0;
int32_t txts_w = is_first_line ? indent : 0;
uint32_t i;
for(i = 0; i < item_cnt; i++) {
lv_snippet_t * pinfo = lv_get_snippet(i);
if(ellipsis_valid && i == item_cnt - 1) {
uint32_t n_ofs = 0;
lv_text_get_snippet(pinfo->txt, pinfo->font, pinfo->letter_space, max_width - txts_w,
LV_TEXT_FLAG_BREAK_ALL, &pinfo->txt_w, &n_ofs);
pinfo->bytes = n_ofs;
}
txts_w = txts_w + pinfo->txt_w;
}
txts_w -= lv_get_snippet(item_cnt - 1)->letter_space;
align_ofs = max_width > txts_w ? max_width - txts_w : 0;
if(align == LV_TEXT_ALIGN_CENTER) {
align_ofs = align_ofs >> 1;
}
txt_pos.x += align_ofs;
}
#if LV_USE_BIDI
int32_t first_txt_pos_x = txt_pos.x;
bool is_draw_rtl = false;
lv_snippet_t * pinfo0 = lv_get_snippet(0);
lv_base_dir_t bidi_dir = lv_bidi_detect_base_dir(pinfo0->txt);
if(bidi_dir == LV_BASE_DIR_RTL && base_dir == LV_BASE_DIR_RTL) {
is_draw_rtl = true;
if(align == LV_TEXT_ALIGN_LEFT || align == LV_TEXT_ALIGN_CENTER) {
txt_pos.x = coords.x2 - align_ofs;
}
else if(align == LV_TEXT_ALIGN_RIGHT) {
txt_pos.x = coords.x2;
}
}
#endif
/* draw line letters */
uint32_t i;
for(i = 0; i < item_cnt; i++) {
lv_snippet_t * pinfo = lv_get_snippet(i);
/* bidi deal with:todo */
#if LV_USE_BIDI
char * bidi_txt;
if(base_dir == LV_BASE_DIR_RTL) {
bidi_txt = lv_malloc(pinfo->bytes + 1);
lv_memcpy(bidi_txt, pinfo->txt, (size_t)pinfo->bytes);
label_draw_dsc.bidi_dir = base_dir;
label_draw_dsc.has_bided = true;
label_draw_dsc.text_local = true;
lv_bidi_process_paragraph(pinfo->txt, bidi_txt, pinfo->bytes, label_draw_dsc.bidi_dir, NULL, 0);
}
else {
bidi_txt = (char *)pinfo->txt;
}
#else
const char * bidi_txt = pinfo->txt;
#endif
lv_point_t pos;
pos.x = txt_pos.x;
@@ -1121,6 +1203,13 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
a.x2 = a.x1 + pinfo->txt_w;
a.y2 = a.y1 + pinfo->line_h;
#if LV_USE_BIDI
if(is_draw_rtl) {
a.x1 = pos.x - pinfo->txt_w;
a.x2 = pos.x;
}
#endif
bool need_draw_ellipsis = false;
uint32_t dot_width = 0;
/* deal overflow */
@@ -1134,18 +1223,64 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
label_draw_dsc.flag, &pinfo->txt_w, &next_ofs);
a.x2 = a.x1 + pinfo->txt_w;
label_draw_dsc.text_length = next_ofs + 1;
#if LV_USE_BIDI
if(base_dir == LV_BASE_DIR_RTL) {
if(txt_bytes > label_draw_dsc.text_length) {
char * tmp_txt = lv_malloc(label_draw_dsc.text_length + 1);
if(lv_bidi_detect_base_dir(bidi_txt) == LV_BASE_DIR_RTL) {
lv_memcpy(tmp_txt, bidi_txt + (txt_bytes - label_draw_dsc.text_length), (size_t)label_draw_dsc.text_length);
}
else {
lv_memcpy(tmp_txt, bidi_txt, (size_t)label_draw_dsc.text_length);
}
label_draw_dsc.text = tmp_txt;
lv_free(bidi_txt);
}
if(i == 0) {
a.x1 = a.x1 + dot_width;
a.x2 = a.x2 + dot_width;
}
}
#endif
}
lv_draw_label(layer, &label_draw_dsc, &a);
#if LV_USE_BIDI
if(label_draw_dsc.has_bided) {
lv_free((void *)label_draw_dsc.text);
}
#endif
if(need_draw_ellipsis) {
label_draw_dsc.text = "...";
#if LV_USE_BIDI
if(label_draw_dsc.bidi_dir == LV_BASE_DIR_RTL) {
a.x1 = first_txt_pos_x;
a.x2 = a.x1 + dot_width;
}
else {
a.x1 = a.x2;
a.x2 = a.x1 + dot_width;
}
label_draw_dsc.text_local = false;
#else
a.x1 = a.x2;
a.x2 = a.x1 + dot_width;
#endif
lv_draw_label(layer, &label_draw_dsc, &a);
}
txt_pos.x = a.x2;
#if LV_USE_BIDI
if(is_draw_rtl) {
txt_pos.x = a.x1;
}
#endif
}
Next_line_init:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -498,4 +498,23 @@ void test_spangroup_set_right_align_on_overflow(void)
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_10.png");
}
void test_spangroup_rtl_mode(void)
{
const char * message = "مرحبا بكم في LVGL.";
active_screen = lv_screen_active();
spangroup = lv_spangroup_create(active_screen);
lv_obj_set_style_outline_width(spangroup, 1, 0);
lv_obj_set_style_text_font(spangroup, &lv_font_dejavu_16_persian_hebrew, 0);
lv_obj_set_style_base_dir(spangroup, LV_BASE_DIR_RTL, 0);
lv_obj_set_size(spangroup, 300, lv_font_dejavu_16_persian_hebrew.line_height);
lv_spangroup_set_align(spangroup, LV_TEXT_ALIGN_LEFT);
lv_span_t * span = lv_spangroup_new_span(spangroup);
lv_span_set_text_static(span, message);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_11.png");
}
#endif