diff --git a/lv_conf_template.h b/lv_conf_template.h index 08e9beb9e..c616dfa08 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -297,6 +297,19 @@ typedef void * lv_font_user_data_t; /*Can break (wrap) texts on these chars*/ #define LV_TXT_BREAK_CHARS " ,.;:-_" +/* Support bidirectional texts. + * Allows mixing Left-to-Right and Right-to-Left texts. + * The direction will be processed according to the Unicode Bidirectioanl Algorithm: + * https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#define LV_USE_BIDI 1 +#if LV_USE_BIDI +/* Set the default direction. Supported values: + * `LV_BIDI_DIR_LTR` Left-to-Right + * `LV_BIDI_DIR_RTL` Right-to-Left + * `LV_BIDI_DIR_AUTO` detect texts base direction */ +#define LV_BIDI_BASE_DIR_DEF LV_BIDI_DIR_AUTO +#endif + /*=================== * LV_OBJ SETTINGS *==================*/ diff --git a/lvgl.h b/lvgl.h index b6e29b61b..586344832 100644 --- a/lvgl.h +++ b/lvgl.h @@ -33,6 +33,7 @@ extern "C" { #include "src/lv_font/lv_font.h" #include "src/lv_font/lv_font_fmt_txt.h" +#include "src/lv_misc/lv_bidi.h" #include "src/lv_objx/lv_btn.h" #include "src/lv_objx/lv_imgbtn.h" diff --git a/src/lv_conf_checker.h b/src/lv_conf_checker.h index ff7ac05df..c2700e76a 100644 --- a/src/lv_conf_checker.h +++ b/src/lv_conf_checker.h @@ -412,6 +412,23 @@ #define LV_TXT_BREAK_CHARS " ,.;:-_" #endif +/* Support bidirectional texts. + * Allows mixing Left-to-Right and Right-to-Left texts. + * The direction will be processed according to the Unicode Bidirectioanl Algorithm: + * https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#ifndef LV_USE_BIDI +#define LV_USE_BIDI 1 +#endif +#if LV_USE_BIDI +/* Set the default direction. Supported values: + * `LV_BIDI_DIR_LTR` Left-to-Right + * `LV_BIDI_DIR_RTL` Right-to-Left + * `LV_BIDI_DIR_AUTO` detect texts base direction */ +#ifndef LV_BIDI_BASE_DIR_DEF +#define LV_BIDI_BASE_DIR_DEF LV_BIDI_DIR_AUTO +#endif +#endif + /*=================== * LV_OBJ SETTINGS *==================*/ diff --git a/src/lv_core/lv_obj.c b/src/lv_core/lv_obj.c index e0ad6c42c..47ce8262d 100644 --- a/src/lv_core/lv_obj.c +++ b/src/lv_core/lv_obj.c @@ -49,6 +49,7 @@ static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coor static void report_style_mod_core(void * style_p, lv_obj_t * obj); static void refresh_children_style(lv_obj_t * obj); static void delete_children(lv_obj_t * obj); +static void base_dir_refr_children(lv_obj_t * obj); static void lv_event_mark_deleted(lv_obj_t * obj); static void lv_obj_del_async_cb(void * obj); static bool lv_obj_design(lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode); @@ -204,6 +205,16 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy) new_obj->opa_scale_en = 0; new_obj->opa_scale = LV_OPA_COVER; new_obj->parent_event = 0; +#if LV_USE_BIDI +#if LV_BIDI_BASE_DIR_DEF == LV_BIDI_DIR_LTR || LV_BIDI_BASE_DIR_DEF == LV_BIDI_DIR_RTL || LV_BIDI_BASE_DIR_DEF == LV_BIDI_DIR_AUTO + new_obj->base_dir = LV_BIDI_BASE_DIR_DEF; +#else +#error "`LV_BIDI_BASE_DIR_DEF` should be `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` (See lv_conf.h)" +#endif +#else + new_obj->base_dir = LV_BIDI_DIR_LTR; +#endif + new_obj->reserved = 0; new_obj->ext_attr = NULL; @@ -288,6 +299,12 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy) new_obj->opa_scale = LV_OPA_COVER; new_obj->opa_scale_en = 0; new_obj->parent_event = 0; +#if LV_USE_BIDI + new_obj->base_dir = LV_BIDI_DIR_INHERIT; +#else + new_obj->base_dir = LV_BIDI_DIR_LTR; +#endif + new_obj->reserved = 0; new_obj->ext_attr = NULL; } @@ -1266,6 +1283,23 @@ void lv_obj_set_parent_event(lv_obj_t * obj, bool en) obj->parent_event = (en == true ? 1 : 0); } +void lv_obj_set_base_dir(lv_obj_t * obj, lv_bidi_dir_t dir) +{ + if(dir != LV_BIDI_DIR_LTR && dir != LV_BIDI_DIR_RTL && + dir != LV_BIDI_DIR_AUTO && dir != LV_BIDI_DIR_INHERIT) { + + LV_LOG_WARN("lv_obj_set_base_dir: invalid base dir"); + return; + } + + obj->base_dir = dir; + lv_signal_send(obj, LV_SIGNAL_BASE_DIR_CHG, NULL); + + /* Notify the children about the parent base dir has changed. + * (The children might have `LV_BIDI_DIR_INHERIT`)*/ + base_dir_refr_children(obj); +} + /** * Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`) * @param obj pointer to an object @@ -1931,6 +1965,25 @@ bool lv_obj_get_parent_event(const lv_obj_t * obj) return obj->parent_event == 0 ? false : true; } + +lv_bidi_dir_t lv_obj_get_base_dir(const lv_obj_t * obj) +{ +#if LV_USE_BIDI + const lv_obj_t * parent = obj; + + while(parent) { + if(parent->base_dir != LV_BIDI_DIR_INHERIT) return parent->base_dir; + + parent = lv_obj_get_parent(parent); + } + + return LV_BIDI_BASE_DIR_DEF; +#else + return LV_BIDI_DIR_LTR; +#endif +} + + /** * Get the opa scale enable parameter * @param obj pointer to an object @@ -2326,6 +2379,22 @@ static void delete_children(lv_obj_t * obj) lv_mem_free(obj); /*Free the object itself*/ } +static void base_dir_refr_children(lv_obj_t * obj) +{ + lv_obj_t * child; + child = lv_obj_get_child(obj, NULL); + + while(child) { + if(child->base_dir == LV_BIDI_DIR_INHERIT) { + lv_signal_send(child, LV_SIGNAL_BASE_DIR_CHG, NULL); + base_dir_refr_children(child); + } + + child = lv_obj_get_child(obj, child); + } +} + + static void lv_event_mark_deleted(lv_obj_t * obj) { lv_event_temp_data_t * t = event_temp_data_head; diff --git a/src/lv_core/lv_obj.h b/src/lv_core/lv_obj.h index c2062e969..c6da4016b 100644 --- a/src/lv_core/lv_obj.h +++ b/src/lv_core/lv_obj.h @@ -28,6 +28,7 @@ extern "C" { #include "../lv_misc/lv_ll.h" #include "../lv_misc/lv_color.h" #include "../lv_misc/lv_log.h" +#include "../lv_misc/lv_bidi.h" #include "../lv_hal/lv_hal.h" /********************* @@ -111,7 +112,8 @@ enum { LV_SIGNAL_CHILD_CHG, /**< Child was removed/added */ LV_SIGNAL_CORD_CHG, /**< Object coordinates/size have changed */ LV_SIGNAL_PARENT_SIZE_CHG, /**< Parent's size has changed */ - LV_SIGNAL_STYLE_CHG, /**< Object's style has changed */ + LV_SIGNAL_STYLE_CHG, /**< Object's style has changed */ + LV_SIGNAL_BASE_DIR_CHG, /** #include "lv_txt.h" +#if LV_USE_BIDI + /********************* * DEFINES *********************/ @@ -62,7 +64,6 @@ void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir if(dir != LV_BIDI_DIR_NEUTRAL) break; } - /*if there were neutrals in the beginning apply `base_dir` on them */ if(rd && str_in[rd] != '\0') lv_txt_encoded_prev(str_in, &rd); if(rd) { @@ -110,6 +111,21 @@ void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir } +lv_bidi_dir_t lv_bidi_detect_base_dir(const char * txt) +{ + uint32_t i = 0; + uint32_t letter; + while(txt[i] != '\0') { + letter = lv_txt_encoded_next(txt, &i); + + lv_bidi_dir_t dir; + dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BIDI_DIR_RTL || dir == LV_BIDI_DIR_LTR) return dir; + } + + /*If there were no strong char earlier return with the default base dir */ + return LV_BIDI_BASE_DIR_DEF; +} lv_bidi_dir_t lv_bidi_get_letter_dir(uint32_t letter) { @@ -279,7 +295,6 @@ static uint32_t char_change_to_pair(uint32_t letter) } return letter; - - } +#endif /*LV_USE_BIDI*/ diff --git a/src/lv_misc/lv_bidi.h b/src/lv_misc/lv_bidi.h index 8adbac674..fa0d1e9ca 100644 --- a/src/lv_misc/lv_bidi.h +++ b/src/lv_misc/lv_bidi.h @@ -23,19 +23,27 @@ extern "C" { /********************** * TYPEDEFS **********************/ -typedef enum +enum { - LV_BIDI_DIR_LTR, - LV_BIDI_DIR_RTL, - LV_BIDI_DIR_NEUTRAL, - LV_BIDI_DIR_WEAK, -}lv_bidi_dir_t; + /*The first 4 values are stored in `lv_obj_t` on 2 bits*/ + LV_BIDI_DIR_LTR = 0x00, + LV_BIDI_DIR_RTL = 0x01, + LV_BIDI_DIR_AUTO = 0x02, + LV_BIDI_DIR_INHERIT = 0x03, + + LV_BIDI_DIR_NEUTRAL = 0x20, + LV_BIDI_DIR_WEAK = 0x21, +}; + +typedef uint8_t lv_bidi_dir_t; /********************** * GLOBAL PROTOTYPES **********************/ -void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir); +#if LV_USE_BIDI +void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir); +lv_bidi_dir_t lv_bidi_detect_base_dir(const char * txt); lv_bidi_dir_t lv_bidi_get_letter_dir(uint32_t letter); bool lv_bidi_letter_is_weak(uint32_t letter); bool lv_bidi_letter_is_rtl(uint32_t letter); @@ -45,6 +53,8 @@ bool lv_bidi_letter_is_neutral(uint32_t letter); * MACROS **********************/ +#endif /*LV_USE_BIDI*/ + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/lv_objx/lv_ddlist.c b/src/lv_objx/lv_ddlist.c index 3deeed174..5a3854a93 100644 --- a/src/lv_objx/lv_ddlist.c +++ b/src/lv_objx/lv_ddlist.c @@ -180,7 +180,8 @@ void lv_ddlist_set_options(lv_obj_t * ddlist, const char * options) lv_ddlist_refr_width(ddlist); - switch(lv_label_get_align(ext->label)) { + lv_label_align_t align = lv_label_get_align(ext->label); + switch(align) { case LV_LABEL_ALIGN_LEFT: lv_obj_align(ext->label, NULL, LV_ALIGN_IN_LEFT_MID, 0, 0); break; case LV_LABEL_ALIGN_CENTER: lv_obj_align(ext->label, NULL, LV_ALIGN_CENTER, 0, 0); break; case LV_LABEL_ALIGN_RIGHT: lv_obj_align(ext->label, NULL, LV_ALIGN_IN_RIGHT_MID, 0, 0); break; @@ -621,6 +622,15 @@ static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * par lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); if(sign == LV_SIGNAL_STYLE_CHG) { + lv_ddlist_refr_size(ddlist, 0); + } else if(sign == LV_SIGNAL_BASE_DIR_CHG) { + lv_label_align_t align = lv_label_get_align(ext->label); + switch(align) { + case LV_LABEL_ALIGN_LEFT: lv_obj_align(ext->label, NULL, LV_ALIGN_IN_LEFT_MID, 0, 0); break; + case LV_LABEL_ALIGN_CENTER: lv_obj_align(ext->label, NULL, LV_ALIGN_CENTER, 0, 0); break; + case LV_LABEL_ALIGN_RIGHT: lv_obj_align(ext->label, NULL, LV_ALIGN_IN_RIGHT_MID, 0, 0); break; + } + lv_ddlist_refr_size(ddlist, 0); } else if(sign == LV_SIGNAL_CLEANUP) { ext->label = NULL; diff --git a/src/lv_objx/lv_label.c b/src/lv_objx/lv_label.c index 0b6046c76..3b0417c97 100644 --- a/src/lv_objx/lv_label.c +++ b/src/lv_objx/lv_label.c @@ -13,6 +13,7 @@ #include "../lv_core/lv_group.h" #include "../lv_misc/lv_color.h" #include "../lv_misc/lv_math.h" +#include "../lv_misc/lv_bidi.h" /********************* * DEFINES @@ -88,7 +89,7 @@ lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy) ext->static_txt = 0; ext->recolor = 0; ext->body_draw = 0; - ext->align = LV_LABEL_ALIGN_LEFT; + ext->align = LV_LABEL_ALIGN_AUTO; ext->dot_end = LV_LABEL_DOT_END_INV; ext->long_mode = LV_LABEL_LONG_EXPAND; #if LV_USE_ANIMATION @@ -190,14 +191,33 @@ void lv_label_set_text(lv_obj_t * label, const char * text) if(ext->text != NULL && ext->static_txt == 0) { lv_mem_free(ext->text); ext->text = NULL; + +#if LV_USE_BIDI + lv_mem_free(ext->text_ori); + ext->text_ori = NULL; +#endif } ext->text = lv_mem_alloc(len); lv_mem_assert(ext->text); if(ext->text == NULL) return; +#if LV_USE_BIDI == 0 strcpy(ext->text, text); - ext->static_txt = 0; /*Now the text is dynamically allocated*/ +#else + ext->text_ori = lv_mem_alloc(len); + lv_mem_assert(ext->text_ori); + if(ext->text_ori == NULL) return; + + strcpy(ext->text_ori, text); + + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(label); + if(base_dir == LV_BIDI_DIR_AUTO) base_dir = lv_bidi_detect_base_dir(text); + + lv_bidi_process(ext->text_ori, ext->text, base_dir); +#endif + /*Now the text is dynamically allocated*/ + ext->static_txt = 0; } lv_label_refr_text(label); @@ -425,7 +445,22 @@ lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label) 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; + + lv_label_align_t align = ext->align; + + if(align == LV_LABEL_ALIGN_AUTO) { +#if LV_USE_BIDI + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(label); + if(base_dir == LV_BIDI_DIR_AUTO) base_dir = lv_bidi_detect_base_dir(ext->text); + + if(base_dir == LV_BIDI_DIR_LTR) align = LV_LABEL_ALIGN_LEFT; + else if (base_dir == LV_BIDI_DIR_RTL) align = LV_LABEL_ALIGN_RIGHT; +#else + align = LV_LABEL_ALIGN_LEFT; +#endif + } + + return align; } /** @@ -842,14 +877,13 @@ static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_ 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_label_align_t align = lv_label_get_align(label); 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; + if(align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + if(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)*/ @@ -947,6 +981,9 @@ static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param label->ext_draw_pad = LV_MATH_MAX(label->ext_draw_pad, style->body.padding.top); label->ext_draw_pad = LV_MATH_MAX(label->ext_draw_pad, style->body.padding.bottom); } + } + else if(sign == LV_SIGNAL_BASE_DIR_CHG) { + if(ext->static_txt == 0) lv_label_set_text(label, NULL); } else if(sign == LV_SIGNAL_GET_TYPE) { lv_obj_type_t * buf = param; uint8_t i; diff --git a/src/lv_objx/lv_label.h b/src/lv_objx/lv_label.h index 16b1a6e8f..ff7f62167 100644 --- a/src/lv_objx/lv_label.h +++ b/src/lv_objx/lv_label.h @@ -55,6 +55,7 @@ enum { LV_LABEL_ALIGN_LEFT, /**< Align text to left */ LV_LABEL_ALIGN_CENTER, /**< Align text to center */ LV_LABEL_ALIGN_RIGHT, /**< Align text to right */ + LV_LABEL_ALIGN_AUTO, /**< Use LEFT or RIGHT depending on the direction of the text (LTR/RTL)*/ }; typedef uint8_t lv_label_align_t; @@ -63,12 +64,17 @@ typedef struct { /*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/ /*New data for this type */ - char * text; /*Text of the label*/ + char * text; /*Text of the label*/ + +#if LV_USE_BIDI + char * text_ori; /*The original text. With BiDi `text` stores the characters in "bidi" ordered text*/ +#endif + union { char * tmp_ptr; /* Pointer to the allocated memory containing the character which are replaced by dots (Handled by the library)*/ - char tmp[sizeof(char *)]; /* Directly store the characters if <=4 characters */ + char tmp[LV_LABEL_DOT_NUM + 1]; /* Directly store the characters if <=4 characters */ } dot; uint16_t dot_end; /*The text end position in dot mode (Handled by the library)*/ lv_point_t offset; /*Text draw position offset*/