feat(span): support bidi for span (#7717)
Signed-off-by: liuhongchao <liuhongchao@xiaomi.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
BIN
tests/ref_imgs/widgets/span_11.png
Normal file
BIN
tests/ref_imgs/widgets/span_11.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
BIN
tests/ref_imgs_vg_lite/widgets/span_11.png
Normal file
BIN
tests/ref_imgs_vg_lite/widgets/span_11.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user