diff --git a/lv_conf_template.h b/lv_conf_template.h index 399ec7d84..c1c1f7c86 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -196,6 +196,12 @@ typedef void * lv_img_decoder_user_data_t; * font's bitmaps */ #define LV_ATTRIBUTE_LARGE_CONST +/* Export integer constant to binding. + * This macro is used with constants in the form of LV_ that + * should also appear on lvgl binding API such as Micropython + */ +#define LV_EXPORT_CONST_INT(int_value) + /*=================== * HAL settings *==================*/ @@ -336,14 +342,27 @@ typedef void * lv_font_user_data_t; /*Can break (wrap) texts on these chars*/ #define LV_TXT_BREAK_CHARS " ,.;:-_" -/* If a character is at least this long, will break wherever "prettiest" */ -#define LV_TXT_LINE_BREAK_LONG_LEN 12 + /* If a character is at least this long, will break wherever "prettiest" */ + #define LV_TXT_LINE_BREAK_LONG_LEN 12 -/* Minimum number of characters of a word to put on a line before a break */ -#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + /* Minimum number of characters of a word to put on a line before a break */ + #define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 -/* Minimum number of characters of a word to put on a line after a break */ -#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + /* Minimum number of characters of a word to put on a line after a break */ + #define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/* 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 0 +#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 /*Change the built in (v)snprintf functions*/ #define LV_SPRINTF_CUSTOM 0 diff --git a/lvgl.h b/lvgl.h index c8855b2b3..23b413304 100644 --- a/lvgl.h +++ b/lvgl.h @@ -34,6 +34,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 d398a9b4f..b7149d0db 100644 --- a/src/lv_conf_checker.h +++ b/src/lv_conf_checker.h @@ -266,6 +266,14 @@ #define LV_ATTRIBUTE_LARGE_CONST #endif +/* Export integer constant to binding. + * This macro is used with constants in the form of LV_ that + * should also appear on lvgl binding API such as Micropython + */ +#ifndef LV_EXPORT_CONST_INT +#define LV_EXPORT_CONST_INT(int_value) +#endif + /*=================== * HAL settings *==================*/ @@ -465,19 +473,36 @@ #define LV_TXT_BREAK_CHARS " ,.;:-_" #endif -/* If a character is at least this long, will break wherever "prettiest" */ + /* If a character is at least this long, will break wherever "prettiest" */ #ifndef LV_TXT_LINE_BREAK_LONG_LEN -#define LV_TXT_LINE_BREAK_LONG_LEN 12 + #define LV_TXT_LINE_BREAK_LONG_LEN 12 #endif -/* Minimum number of characters of a word to put on a line before a break */ + /* Minimum number of characters of a word to put on a line before a break */ #ifndef LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN -#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + #define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 #endif -/* Minimum number of characters of a word to put on a line after a break */ + /* Minimum number of characters of a word to put on a line after a break */ #ifndef LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN -#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + #define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 +#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 0 +#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 /*Change the built in (v)snprintf functions*/ diff --git a/src/lv_core/lv_obj.c b/src/lv_core/lv_obj.c index 5069bee7f..4c5f6cd1e 100644 --- a/src/lv_core/lv_obj.c +++ b/src/lv_core/lv_obj.c @@ -52,6 +52,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 lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode); @@ -207,6 +208,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; @@ -225,11 +236,22 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy) new_obj->par = parent; /*Set the parent*/ lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t)); +#if LV_USE_BIDI + new_obj->base_dir = LV_BIDI_DIR_INHERIT; +#else + new_obj->base_dir = LV_BIDI_DIR_LTR; +#endif + /*Set coordinates left top corner of parent*/ - new_obj->coords.x1 = parent->coords.x1; new_obj->coords.y1 = parent->coords.y1; - new_obj->coords.x2 = parent->coords.x1 + LV_OBJ_DEF_WIDTH; new_obj->coords.y2 = parent->coords.y1 + LV_OBJ_DEF_HEIGHT; + if(lv_obj_get_base_dir(new_obj) == LV_BIDI_DIR_RTL) { + new_obj->coords.x2 = parent->coords.x2; + new_obj->coords.x1 = parent->coords.x2 - LV_OBJ_DEF_WIDTH; + } else { + new_obj->coords.x1 = parent->coords.x1; + new_obj->coords.x2 = parent->coords.x1 + LV_OBJ_DEF_WIDTH; + } new_obj->ext_draw_pad = 0; #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL @@ -292,6 +314,7 @@ 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; + new_obj->reserved = 0; new_obj->ext_attr = NULL; } @@ -725,8 +748,12 @@ void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h) lv_obj_get_coords(obj, &ori); /*Set the length and height*/ - obj->coords.x2 = obj->coords.x1 + w - 1; obj->coords.y2 = obj->coords.y1 + h - 1; + if(lv_obj_get_base_dir(obj) == LV_BIDI_DIR_RTL) { + obj->coords.x1 = obj->coords.x2 - w + 1; + } else { + obj->coords.x2 = obj->coords.x1 + w - 1; + } /*Send a signal to the object with its new coordinates*/ obj->signal_cb(obj, LV_SIGNAL_CORD_CHG, &ori); @@ -1336,6 +1363,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 @@ -1757,8 +1801,11 @@ lv_coord_t lv_obj_get_x(const lv_obj_t * obj) lv_coord_t rel_x; lv_obj_t * parent = lv_obj_get_parent(obj); - rel_x = obj->coords.x1 - parent->coords.x1; - + if(parent) { + rel_x = obj->coords.x1 - parent->coords.x1; + } else { + rel_x = obj->coords.x1; + } return rel_x; } @@ -1773,8 +1820,11 @@ lv_coord_t lv_obj_get_y(const lv_obj_t * obj) lv_coord_t rel_y; lv_obj_t * parent = lv_obj_get_parent(obj); - rel_y = obj->coords.y1 - parent->coords.y1; - + if(parent) { + rel_y = obj->coords.y1 - parent->coords.y1; + } else { + rel_y = obj->coords.y1; + } return rel_y; } @@ -2083,6 +2133,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 @@ -2549,6 +2618,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 958fb392e..1c54b72ea 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" /********************* @@ -121,7 +122,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 + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir); +static uint32_t get_next_paragraph(const char * txt); +static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t * len); +static void rtl_reverse(char * dest, const char * src, uint32_t len); +static uint32_t char_change_to_pair(uint32_t letter); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir) +{ + + if(base_dir == LV_BIDI_DIR_AUTO) base_dir = lv_bidi_detect_base_dir(str_in); + + uint32_t par_start = 0; + uint32_t par_len; + + while(str_in[par_start] == '\n' || str_in[par_start] == '\r') { + str_out[par_start] = str_in[par_start]; + par_start ++; + } + + while(str_in[par_start] != '\0') { + par_len = get_next_paragraph(&str_in[par_start]); + process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir); + par_start += par_len; + + while(str_in[par_start] == '\n' || str_in[par_start] == '\r') { + str_out[par_start] = str_in[par_start]; + par_start ++; + } + } + + str_out[par_start] = '\0'; +} + +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 */ + if(LV_BIDI_BASE_DIR_DEF == LV_BIDI_DIR_AUTO) return LV_BIDI_DIR_LTR; + else return LV_BIDI_BASE_DIR_DEF; +} + +lv_bidi_dir_t lv_bidi_get_letter_dir(uint32_t letter) +{ + if(lv_bidi_letter_is_rtl(letter)) return LV_BIDI_DIR_RTL; + if(lv_bidi_letter_is_neutral(letter)) return LV_BIDI_DIR_NEUTRAL; + if(lv_bidi_letter_is_weak(letter)) return LV_BIDI_DIR_WEAK; + + return LV_BIDI_DIR_LTR; +} + +bool lv_bidi_letter_is_weak(uint32_t letter) +{ + uint32_t i = 0; + static const char weaks[] = "0123456789"; + + do { + uint32_t x = lv_txt_encoded_next(weaks, &i); + if(letter == x) { + return true; + } + } while(weaks[i] != '\0'); + + return false; +} + +bool lv_bidi_letter_is_rtl(uint32_t letter) +{ + if(letter >= 0x5d0 && letter <= 0x5ea) return true; + if(letter == 0x202E) return true; /*Unicode of LV_BIDI_RLO*/ +// if(letter >= 'a' && letter <= 'z') return true; + + return false; +} + +bool lv_bidi_letter_is_neutral(uint32_t letter) +{ + uint16_t i; + static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\=()[]{}<>@#&$|"; + for(i = 0; neutrals[i] != '\0'; i++) { + if(letter == (uint32_t)neutrals[i]) return true; + } + + return false; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir) +{ + uint32_t run_len = 0; + lv_bidi_dir_t run_dir; + uint32_t rd = 0; + uint32_t wr; + if(base_dir == LV_BIDI_DIR_RTL) wr = len; + else wr = 0; + + str_out[len] = '\0'; + + lv_bidi_dir_t dir = base_dir; + + /*Process neutral chars in the beginning*/ + while(rd < len) { + uint32_t letter = lv_txt_encoded_next(str_in, &rd); + dir = lv_bidi_get_letter_dir(letter); + if(dir != LV_BIDI_DIR_NEUTRAL && dir != LV_BIDI_DIR_WEAK) break; + } + + if(rd && str_in[rd] != '\0') lv_txt_encoded_prev(str_in, &rd); + + if(rd) { + if(base_dir == LV_BIDI_DIR_LTR) { + memcpy(&str_out[wr], str_in, rd); + wr += rd; + } else { + wr -= rd; + rtl_reverse(&str_out[wr], str_in, rd); + } + } + + /*Get and process the runs*/ + while(rd < len) { + run_dir = get_next_run(&str_in[rd], base_dir, &run_len); + + if(base_dir == LV_BIDI_DIR_LTR) { + if(run_dir == LV_BIDI_DIR_LTR) memcpy(&str_out[wr], &str_in[rd], run_len); + else rtl_reverse(&str_out[wr], &str_in[rd], run_len); + wr += run_len; + } else { + wr -= run_len; + if(run_dir == LV_BIDI_DIR_LTR) memcpy(&str_out[wr], &str_in[rd], run_len); + else rtl_reverse(&str_out[wr], &str_in[rd], run_len); + } + + rd += run_len; + } +} + +static uint32_t get_next_paragraph(const char * txt) +{ + uint32_t i = 0; + + lv_txt_encoded_next(txt, &i); + + while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') { + lv_txt_encoded_next(txt, &i); + } + + return i; +} + +static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t * len) +{ + uint32_t i = 0; + uint32_t letter; + + letter = lv_txt_encoded_next(txt, NULL); + lv_bidi_dir_t dir = lv_bidi_get_letter_dir(letter); + + /*Find the first strong char. Skip the neutrals*/ + while(dir == LV_BIDI_DIR_NEUTRAL || dir == LV_BIDI_DIR_WEAK) { + letter = lv_txt_encoded_next(txt, &i); + dir = lv_bidi_get_letter_dir(letter); + if(txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') { + *len = i; + return base_dir; + } + } + + lv_bidi_dir_t run_dir = dir; + + uint32_t i_prev = i; + uint32_t i_last_strong = i; + + /*Find the next char which has different direction*/ + lv_bidi_dir_t next_dir = base_dir; + while(txt[i] != '\0'&& txt[i] != '\n' && txt[i] != '\r') { + letter = lv_txt_encoded_next(txt, &i); + next_dir = lv_bidi_get_letter_dir(letter); + + /*New dir found?*/ + if((next_dir == LV_BIDI_DIR_RTL || next_dir == LV_BIDI_DIR_LTR) && next_dir != run_dir) { + /*Include neutrals if `run_dir == base_dir` */ + if(run_dir == base_dir) *len = i_prev; + /*Exclude neutrals if `run_dir != base_dir` */ + else *len = i_last_strong; + + return run_dir; + } + + if(next_dir != LV_BIDI_DIR_NEUTRAL) i_last_strong = i; + + i_prev = i; + } + + + /*Handle end of of string. Apply `base_dir` on trailing neutrals*/ + + /*Include neutrals if `run_dir == base_dir` */ + if(run_dir == base_dir) *len = i_prev; + /*Exclude neutrals if `run_dir != base_dir` */ + else *len = i_last_strong; + + return run_dir; + +} + +static void rtl_reverse(char * dest, const char * src, uint32_t len) +{ + uint32_t i = len; + uint32_t wr = 0; + + while(i) { + uint32_t letter = lv_txt_encoded_prev(src, &i); + + /*Keep weak letters (numbers) as LTR*/ + if(lv_bidi_letter_is_weak(letter)) { + uint32_t last_weak = i; + uint32_t first_weak = i; + while(i) { + letter = lv_txt_encoded_prev(src, &i); + /*No need to call `char_change_to_pair` because there not such chars here*/ + + /*Finish on non-weak char */ + /*but treat number and currency related chars as weak*/ + if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$') { + lv_txt_encoded_next(src, &i); /*Rewind one letter*/ + first_weak = i; + break; + } + } + if(i == 0) first_weak = 0; + + memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1); + wr += last_weak - first_weak + 1; + + } + /*Simply store in reversed order*/ + else { + uint32_t letter_size = lv_txt_encoded_size((const char *)&src[i]); + /*Swap arithmetical symbols*/ + if(letter_size == 1) { + uint32_t new_letter = letter = char_change_to_pair(letter); + dest[wr] = (uint8_t)new_letter; + wr += 1; + } + /*Just store the letter*/ + else { + memcpy(&dest[wr], &src[i], letter_size); + wr += letter_size; + } + } + } +} + +static uint32_t char_change_to_pair(uint32_t letter) +{ + static uint8_t left[] = {"<({["}; + static uint8_t right[] = {">)}]"}; + + uint8_t i; + for(i = 0; left[i] != '\0'; i++) { + if(letter == left[i]) return right[i]; + } + + for(i = 0; right[i] != '\0'; i++) { + if(letter == right[i]) return left[i]; + } + + return letter; +} + +#endif /*LV_USE_BIDI*/ diff --git a/src/lv_misc/lv_bidi.h b/src/lv_misc/lv_bidi.h new file mode 100644 index 000000000..233765ca2 --- /dev/null +++ b/src/lv_misc/lv_bidi.h @@ -0,0 +1,72 @@ +/** + * @file lv_bifi.h + * + */ + +#ifndef LV_BIDI_H +#define LV_BIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../../lv_conf.h" +#endif + +#include +#include + +/********************* + * DEFINES + *********************/ +/* Special non printable strong characters. + * They can be inserted to texts to affect the run's direction*/ +#define LV_BIDI_LRO "\xE2\x80\xAD" /*U+202D*/ +#define LV_BIDI_RLO "\xE2\x80\xAE" /*U+202E*/ + +/********************** + * TYPEDEFS + **********************/ +enum +{ + /*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 + **********************/ +#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); +bool lv_bidi_letter_is_neutral(uint32_t letter); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_BIDI*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_BIDI_H*/ diff --git a/src/lv_misc/lv_log.h b/src/lv_misc/lv_log.h index 6a6c2d2a4..d85bcde43 100644 --- a/src/lv_misc/lv_log.h +++ b/src/lv_misc/lv_log.h @@ -26,13 +26,14 @@ extern "C" { /*Possible log level. For compatibility declare it independently from `LV_USE_LOG`*/ -#define LV_LOG_LEVEL_TRACE 0 /**< A lot of logs to give detailed information*/ -#define LV_LOG_LEVEL_INFO 1 /**< Log important events*/ -#define LV_LOG_LEVEL_WARN 2 /**< Log if something unwanted happened but didn't caused problem*/ -#define LV_LOG_LEVEL_ERROR 3 /**< Only critical issue, when the system may fail*/ -#define LV_LOG_LEVEL_NONE 4 /**< Do not log anything*/ -#define _LV_LOG_LEVEL_NUM 5 /**< Number of log levels */ - +enum { + LV_LOG_LEVEL_TRACE = 0, /**< A lot of logs to give detailed information*/ + LV_LOG_LEVEL_INFO = 1, /**< Log important events*/ + LV_LOG_LEVEL_WARN = 2, /**< Log if something unwanted happened but didn't caused problem*/ + LV_LOG_LEVEL_ERROR = 3, /**< Only critical issue, when the system may fail*/ + LV_LOG_LEVEL_NONE = 4, /**< Do not log anything*/ + _LV_LOG_LEVEL_NUM = 5 /**< Number of log levels */ +}; typedef int8_t lv_log_level_t; #if LV_USE_LOG diff --git a/src/lv_misc/lv_txt.c b/src/lv_misc/lv_txt.c index 5632c5ad8..d18e456b0 100644 --- a/src/lv_misc/lv_txt.c +++ b/src/lv_misc/lv_txt.c @@ -467,7 +467,7 @@ static uint8_t lv_txt_utf8_size(const char * str) return 3; else if((str[0] & 0xF8) == 0xF0) return 4; - return 1; /*If the char was invalid step tell it's 1 byte long*/ + return 0; /*If the char was invalid tell it's 1 byte long*/ } /** @@ -644,7 +644,8 @@ static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id) uint32_t i; uint32_t byte_cnt = 0; for(i = 0; i < utf8_id; i++) { - byte_cnt += lv_txt_encoded_size(&txt[byte_cnt]); + uint8_t c_size = lv_txt_encoded_size(&txt[byte_cnt]); + byte_cnt += c_size > 0 ? c_size : 1; } return byte_cnt; diff --git a/src/lv_objx/lv_bar.h b/src/lv_objx/lv_bar.h index 856014921..9a6466b3b 100644 --- a/src/lv_objx/lv_bar.h +++ b/src/lv_objx/lv_bar.h @@ -43,6 +43,11 @@ extern "C" { /** log2(LV_BAR_ANIM_STATE_END) used to normalize data*/ #define LV_BAR_ANIM_STATE_NORM 8 +LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_START); +LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_END); +LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_INV); +LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_NORM); + /********************** * TYPEDEFS **********************/ diff --git a/src/lv_objx/lv_btnm.c b/src/lv_objx/lv_btnm.c index 074d800ef..c321b535a 100644 --- a/src/lv_objx/lv_btnm.c +++ b/src/lv_objx/lv_btnm.c @@ -207,6 +207,8 @@ void lv_btnm_set_map(const lv_obj_t * btnm, const char * map[]) btn_h = lv_obj_get_height(btnm)- act_y - style_bg->body.padding.bottom - 1; } + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(btnm); + /*Only deal with the non empty lines*/ if(btn_cnt != 0) { /*Calculate the width of all units*/ @@ -214,7 +216,8 @@ void lv_btnm_set_map(const lv_obj_t * btnm, const char * map[]) /*Set the button size and positions and set the texts*/ uint16_t i; - lv_coord_t act_x = style_bg->body.padding.left; + lv_coord_t act_x; + lv_coord_t act_unit_w; unit_act_cnt = 0; for(i = 0; i < btn_cnt; i++) { @@ -225,9 +228,13 @@ void lv_btnm_set_map(const lv_obj_t * btnm, const char * map[]) act_unit_w--; /*-1 because e.g. width = 100 means 101 pixels (0..100)*/ /*Always recalculate act_x because of rounding errors */ - act_x = (unit_act_cnt * all_unit_w) / unit_cnt + i * style_bg->body.padding.inner + - style_bg->body.padding.left; - + if(base_dir == LV_BIDI_DIR_RTL) { + act_x = (unit_act_cnt * all_unit_w) / unit_cnt + i * style_bg->body.padding.inner; + act_x = lv_obj_get_width(btnm) - style_bg->body.padding.right - act_x - act_unit_w - 1; + } else { + act_x = (unit_act_cnt * all_unit_w) / unit_cnt + i * style_bg->body.padding.inner + + style_bg->body.padding.left; + } /* Set the button's area. * If inner padding is zero then use the prev. button x2 as x1 to avoid rounding * errors*/ @@ -643,7 +650,6 @@ static lv_design_res_t lv_btnm_design(lv_obj_t * btnm, const lv_area_t * clip_ar } /*Draw the object*/ else if(mode == LV_DESIGN_DRAW_MAIN) { - ancestor_design_f(btnm, clip_area, mode); lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); @@ -664,6 +670,10 @@ static lv_design_res_t lv_btnm_design(lv_obj_t * btnm, const lv_area_t * clip_ar lv_txt_flag_t txt_flag = LV_TXT_FLAG_NONE; if(ext->recolor) txt_flag = LV_TXT_FLAG_RECOLOR; +#if LV_USE_BIDI + char * bidi_buf = lv_mem_alloc(64); + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(btnm); +#endif for(btn_i = 0; btn_i < ext->btn_cnt; btn_i++, txt_i++) { /*Search the next valid text in the map*/ @@ -733,8 +743,22 @@ static lv_design_res_t lv_btnm_design(lv_obj_t * btnm, const lv_area_t * clip_ar area_tmp.x2 = area_tmp.x1 + txt_size.x; area_tmp.y2 = area_tmp.y1 + txt_size.y; +#if LV_USE_BIDI == 0 lv_draw_label(&area_tmp, clip_area, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL, -1, -1, NULL); +#else + uint32_t txt_len = strlen(ext->map_p[txt_i]) + 1; + if(txt_len > lv_mem_get_size(bidi_buf)) { + bidi_buf = lv_mem_realloc(bidi_buf, txt_len); + } + + lv_bidi_process(ext->map_p[txt_i], bidi_buf, base_dir); + lv_draw_label(&area_tmp, clip_area, btn_style, opa_scale, bidi_buf, txt_flag, NULL, -1, -1, NULL); +#endif } + +#if LV_USE_BIDI + lv_mem_free(bidi_buf); +#endif } return LV_DESIGN_RES_OK; } diff --git a/src/lv_objx/lv_btnm.h b/src/lv_objx/lv_btnm.h index bedcdf436..44bc4efbb 100644 --- a/src/lv_objx/lv_btnm.h +++ b/src/lv_objx/lv_btnm.h @@ -31,6 +31,8 @@ extern "C" { #define LV_BTNM_WIDTH_MASK 0x0007 #define LV_BTNM_BTN_NONE 0xFFFF +LV_EXPORT_CONST_INT(LV_BTNM_BTN_NONE); + /********************** * TYPEDEFS **********************/ diff --git a/src/lv_objx/lv_chart.h b/src/lv_objx/lv_chart.h index 216d7d2ea..fc611f811 100644 --- a/src/lv_objx/lv_chart.h +++ b/src/lv_objx/lv_chart.h @@ -34,6 +34,9 @@ extern "C" { /**Automatically calculate the tick length*/ #define LV_CHART_TICK_LENGTH_AUTO 255 +LV_EXPORT_CONST_INT(LV_CHART_POINT_DEF); +LV_EXPORT_CONST_INT(LV_CHART_TICK_LENGTH_AUTO); + /********************** * TYPEDEFS **********************/ diff --git a/src/lv_objx/lv_cont.c b/src/lv_objx/lv_cont.c index 239329a5a..9aceea02f 100644 --- a/src/lv_objx/lv_cont.c +++ b/src/lv_objx/lv_cont.c @@ -21,6 +21,7 @@ #include "../lv_misc/lv_area.h" #include "../lv_misc/lv_color.h" #include "../lv_misc/lv_math.h" +#include "../lv_misc/lv_bidi.h" /********************* * DEFINES @@ -366,23 +367,23 @@ static void lv_cont_layout_row(lv_obj_t * cont) lv_align_t align; const lv_style_t * style = lv_obj_get_style(cont); lv_coord_t vpad_corr; - + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(cont); switch(type) { case LV_LAYOUT_ROW_T: vpad_corr = style->body.padding.top; - align = LV_ALIGN_IN_TOP_LEFT; + align = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_TOP_RIGHT : LV_ALIGN_IN_TOP_LEFT; break; case LV_LAYOUT_ROW_M: vpad_corr = 0; - align = LV_ALIGN_IN_LEFT_MID; + align = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_RIGHT_MID: LV_ALIGN_IN_LEFT_MID; break; case LV_LAYOUT_ROW_B: vpad_corr = -style->body.padding.bottom; - align = LV_ALIGN_IN_BOTTOM_LEFT; + align = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_BOTTOM_RIGHT: LV_ALIGN_IN_BOTTOM_LEFT; break; default: vpad_corr = 0; - align = LV_ALIGN_IN_TOP_LEFT; + align = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_TOP_RIGHT : LV_ALIGN_IN_TOP_LEFT; break; } @@ -391,12 +392,19 @@ static void lv_cont_layout_row(lv_obj_t * cont) lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG); /* Align the children */ - lv_coord_t last_cord = style->body.padding.left; + lv_coord_t last_cord; + if(base_dir == LV_BIDI_DIR_RTL) last_cord = style->body.padding.right; + else last_cord = style->body.padding.left; + LV_LL_READ_BACK(cont->child_ll, child) { if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue; - lv_obj_align(child, cont, align, last_cord, vpad_corr); +// last_cord -= lv_obj_get_width(child); + + if(base_dir == LV_BIDI_DIR_RTL) lv_obj_align(child, cont, align, -last_cord, vpad_corr); + else lv_obj_align(child, cont, align, last_cord, vpad_corr); + last_cord += lv_obj_get_width(child) + style->body.padding.inner; } diff --git a/src/lv_objx/lv_ddlist.c b/src/lv_objx/lv_ddlist.c index 87f0c7670..d09d07938 100644 --- a/src/lv_objx/lv_ddlist.c +++ b/src/lv_objx/lv_ddlist.c @@ -113,6 +113,11 @@ lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy) lv_obj_set_drag(scrl, false); lv_page_set_scrl_fit2(new_ddlist, LV_FIT_FILL, LV_FIT_TIGHT); + /*Save (a later restore) the original X coordinate because it changes as the FITs applies*/ + lv_coord_t x; + if(lv_obj_get_base_dir(new_ddlist) == LV_BIDI_DIR_RTL) x = lv_obj_get_x(new_ddlist) + lv_obj_get_width(new_ddlist); + else x = lv_obj_get_x(new_ddlist); + ext->label = lv_label_create(new_ddlist, NULL); lv_cont_set_fit2(new_ddlist, LV_FIT_TIGHT, LV_FIT_NONE); lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_HIDE); @@ -120,6 +125,10 @@ lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy) lv_ddlist_set_options(new_ddlist, "Option 1\nOption 2\nOption 3"); + /*Restore the original X coordinate*/ + if(lv_obj_get_base_dir(new_ddlist) == LV_BIDI_DIR_RTL) lv_obj_set_x(new_ddlist, x - lv_obj_get_width(new_ddlist)); + else lv_obj_set_x(new_ddlist, x); + /*Set the default styles*/ lv_theme_t * th = lv_theme_get_current(); if(th) { @@ -131,6 +140,8 @@ lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy) lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, &lv_style_plain_color); lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, &lv_style_pretty_color); } + + } /*Copy an existing drop down list*/ else { @@ -186,7 +197,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; @@ -661,6 +673,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; @@ -980,11 +1001,20 @@ static void lv_ddlist_pos_current_option(lv_obj_t * ddlist) */ static void lv_ddlist_refr_width(lv_obj_t * ddlist) { + + /*Save the current x coordinate because it should be kept after the refrsh*/ + lv_coord_t x; + if(lv_obj_get_base_dir(ddlist) == LV_BIDI_DIR_RTL) x = lv_obj_get_x(ddlist) + lv_obj_get_width(ddlist); + else x = lv_obj_get_x(ddlist); + /*Set the TIGHT fit horizontally the set the width to the content*/ lv_page_set_scrl_fit2(ddlist, LV_FIT_TIGHT, lv_page_get_scrl_fit_bottom(ddlist)); /*Revert FILL fit to fill the parent with the options area. It allows to RIGHT/CENTER align the text*/ lv_page_set_scrl_fit2(ddlist, LV_FIT_FILL, lv_page_get_scrl_fit_bottom(ddlist)); + + if(lv_obj_get_base_dir(ddlist) == LV_BIDI_DIR_RTL) lv_obj_set_x(ddlist, x - lv_obj_get_width(ddlist)); + else lv_obj_set_x(ddlist, x); } #endif diff --git a/src/lv_objx/lv_kb.c b/src/lv_objx/lv_kb.c index 1229ffa8b..003bd4d33 100644 --- a/src/lv_objx/lv_kb.c +++ b/src/lv_objx/lv_kb.c @@ -131,6 +131,7 @@ lv_obj_t * lv_kb_create(lv_obj_t * par, const lv_obj_t * copy) lv_obj_set_event_cb(new_kb, lv_kb_def_event_cb); lv_btnm_set_map(new_kb, kb_map_lc); lv_btnm_set_ctrl_map(new_kb, kb_ctrl_lc_map); + lv_obj_set_base_dir(new_kb, LV_BIDI_DIR_LTR); /*Set the default styles*/ lv_theme_t * th = lv_theme_get_current(); @@ -395,7 +396,7 @@ void lv_kb_def_event_cb(lv_obj_t * kb, lv_event_t event) /*Add the characters to the text area if set*/ if(ext->ta == NULL) return; - if(strcmp(txt, "Enter") == 0) + if(strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) lv_ta_add_char(ext->ta, '\n'); else if(strcmp(txt, LV_SYMBOL_LEFT) == 0) lv_ta_cursor_left(ext->ta); diff --git a/src/lv_objx/lv_label.c b/src/lv_objx/lv_label.c index 4a56347ae..a52d3fe65 100644 --- a/src/lv_objx/lv_label.c +++ b/src/lv_objx/lv_label.c @@ -14,6 +14,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" #include "../lv_misc/lv_printf.h" /********************* @@ -92,7 +93,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 @@ -204,8 +205,14 @@ void lv_label_set_text(lv_obj_t * label, const char * text) LV_ASSERT_MEM(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 + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(label); + lv_bidi_process(text, ext->text, base_dir); +#endif + /*Now the text is dynamically allocated*/ + ext->static_txt = 0; } lv_label_refr_text(label); @@ -506,7 +513,22 @@ lv_label_align_t lv_label_get_align(const lv_obj_t * label) LV_ASSERT_OBJ(label, LV_OBJX_NAME); 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; } /** @@ -578,7 +600,10 @@ void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t 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; + + lv_label_align_t align = lv_label_get_align(label); + if(align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + if(align == LV_LABEL_ALIGN_RIGHT) flag |= LV_TXT_FLAG_RIGHT; /*If the width will be expanded the set the max length to very big */ if(ext->long_mode == LV_LABEL_LONG_EXPAND) { @@ -610,12 +635,12 @@ void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t if(index != line_start) x += style->text.letter_space; - if(ext->align == LV_LABEL_ALIGN_CENTER) { + if(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) { + } else if(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); @@ -650,7 +675,10 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) 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; + + lv_label_align_t align = lv_label_get_align(label); + if(align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + if(align == LV_LABEL_ALIGN_RIGHT) flag |= LV_TXT_FLAG_RIGHT; /*If the width will be expanded set the max length to very big */ if(ext->long_mode == LV_LABEL_LONG_EXPAND) { @@ -669,11 +697,16 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) /*Calculate the x coordinate*/ lv_coord_t x = 0; - if(ext->align == LV_LABEL_ALIGN_CENTER) { + if(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(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; + } lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; @@ -871,7 +904,18 @@ void lv_label_ins_text(lv_obj_t * label, uint32_t pos, const char * txt) pos = lv_txt_get_encoded_length(ext->text); } +#if LV_USE_BIDI + char * bidi_buf = lv_mem_alloc(ins_len) + 1; + LV_ASSERT_MEM(bidi_buf); + if(bidi_buf == NULL) return; + + lv_bidi_process(txt, bidi_buf, lv_obj_get_base_dir(label)); + lv_txt_ins(ext->text, pos, bidi_buf); + + lv_mem_free(bidi_buf); +#else lv_txt_ins(ext->text, pos, txt); +#endif lv_label_refr_text(label); } @@ -947,14 +991,13 @@ static lv_design_res_t lv_label_design(lv_obj_t * label, const lv_area_t * clip_ lv_draw_rect(&bg, clip_area, 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)*/ @@ -1054,6 +1097,18 @@ 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.bottom); } } + else if(sign == LV_SIGNAL_BASE_DIR_CHG) { +#if LV_USE_BIDI + if(ext->static_txt == 0) lv_label_set_text(label, NULL); +#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; } @@ -1137,11 +1192,12 @@ static void lv_label_refr_text(lv_obj_t * label) /*In roll inf. mode keep the size but start offset animations*/ else if(ext->long_mode == LV_LABEL_LONG_SROLL_CIRC) { #if LV_USE_ANIMATION + lv_label_align_t align = lv_label_get_align(label); + lv_anim_t anim; anim.var = label; anim.repeat = 1; anim.playback = 0; - anim.start = 0; anim.act_time = -(((lv_font_get_glyph_width(style->text.font, ' ', ' ') + style->text.letter_space) * 1000) / ext->anim_speed) * LV_LABEL_WAIT_CHAR_COUNT; @@ -1152,7 +1208,14 @@ static void lv_label_refr_text(lv_obj_t * label) bool hor_anim = false; if(size.x > lv_obj_get_width(label)) { - anim.end = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT; + if(align == LV_LABEL_ALIGN_RIGHT) { + anim.end = 0; + anim.start = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT; + } else { + anim.start = 0; + anim.end = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT; + } + anim.exec_cb = (lv_anim_exec_xcb_t)lv_label_set_offset_x; anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end); lv_anim_create(&anim); @@ -1164,7 +1227,14 @@ static void lv_label_refr_text(lv_obj_t * label) } if(size.y > lv_obj_get_height(label) && hor_anim == false) { - anim.end = -size.y - (lv_font_get_line_height(font)); + if(align == LV_LABEL_ALIGN_RIGHT) { + anim.end = 0; + anim.start = -size.y - (lv_font_get_line_height(font)); + } else { + anim.start = 0; + anim.end = -size.y - (lv_font_get_line_height(font)); + } + anim.exec_cb = (lv_anim_exec_xcb_t)lv_label_set_offset_y; anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end); lv_anim_create(&anim); diff --git a/src/lv_objx/lv_label.h b/src/lv_objx/lv_label.h index 2804ef700..6660c3c9d 100644 --- a/src/lv_objx/lv_label.h +++ b/src/lv_objx/lv_label.h @@ -35,6 +35,10 @@ extern "C" { #define LV_LABEL_POS_LAST 0xFFFF #define LV_LABEL_TEXT_SEL_OFF 0xFFFF +LV_EXPORT_CONST_INT(LV_LABEL_DOT_NUM); +LV_EXPORT_CONST_INT(LV_LABEL_POS_LAST); +LV_EXPORT_CONST_INT(LV_LABEL_TEXT_SEL_OFF); + /********************** * TYPEDEFS **********************/ @@ -56,6 +60,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; @@ -64,12 +69,13 @@ 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*/ + 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*/ diff --git a/src/lv_objx/lv_list.c b/src/lv_objx/lv_list.c index 74ac6e34e..eae4e7b95 100644 --- a/src/lv_objx/lv_list.c +++ b/src/lv_objx/lv_list.c @@ -218,7 +218,8 @@ lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * img_src, const char * t lv_label_set_text(label, txt); lv_obj_set_click(label, false); lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC); - lv_obj_set_width(label, liste->coords.x2 - label->coords.x1 - btn_hor_pad); + if(lv_obj_get_base_dir(liste) == LV_BIDI_DIR_RTL) lv_obj_set_width(label, label->coords.x2 - liste->coords.x1 - btn_hor_pad); + else lv_obj_set_width(label, liste->coords.x2 - label->coords.x1 - btn_hor_pad); if(label_signal == NULL) label_signal = lv_obj_get_signal_cb(label); } #if LV_USE_GROUP diff --git a/src/lv_objx/lv_page.c b/src/lv_objx/lv_page.c index 4ce6142ad..1e06d8bb5 100644 --- a/src/lv_objx/lv_page.c +++ b/src/lv_objx/lv_page.c @@ -871,8 +871,10 @@ static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param) /*Automatically move children to the scrollable object*/ else if(sign == LV_SIGNAL_CHILD_CHG) { lv_obj_t * child; - const lv_style_t * style = lv_page_get_style(page, LV_PAGE_STYLE_SCRL); + const lv_style_t * style_bg = lv_page_get_style(page, LV_PAGE_STYLE_BG); + const lv_style_t * style_scrl = lv_page_get_style(page, LV_PAGE_STYLE_SCRL); lv_fit_t fit_left = lv_page_get_scrl_fit_left(page); + lv_fit_t fit_right = lv_page_get_scrl_fit_right(page); lv_fit_t fit_top = lv_page_get_scrl_fit_top(page); child = lv_obj_get_child(page, NULL); while(child != NULL) { @@ -880,15 +882,19 @@ static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param) lv_obj_t * tmp = child; child = lv_obj_get_child(page, child); /*Get the next child before move this*/ - /* Reposition the child to take padding into account (Only if it's on (0;0) now) + /* Reposition the child to take padding into account (Only if it's on (0;0) or (widht;height) coordinates now) * It's required to keep new the object on the same coordinate if FIT is enabled.*/ if((tmp->coords.x1 == page->coords.x1) && (fit_left == LV_FIT_TIGHT || fit_left == LV_FIT_FILL)) { - tmp->coords.x1 += style->body.padding.left; - tmp->coords.x2 += style->body.padding.left; + tmp->coords.x1 += style_scrl->body.padding.left; + tmp->coords.x2 += style_scrl->body.padding.left; + } + else if((tmp->coords.x2 == page->coords.x2) && (fit_right == LV_FIT_TIGHT || fit_right == LV_FIT_FILL)) { + tmp->coords.x1 -= style_scrl->body.padding.right + style_bg->body.padding.right; + tmp->coords.x2 -= style_scrl->body.padding.right + style_bg->body.padding.right; } if((tmp->coords.y1 == page->coords.y1) && (fit_top == LV_FIT_TIGHT || fit_top == LV_FIT_FILL)) { - tmp->coords.y1 += style->body.padding.top; - tmp->coords.y2 += style->body.padding.top; + tmp->coords.y1 += style_scrl->body.padding.top; + tmp->coords.y2 += style_scrl->body.padding.top; } lv_obj_set_parent(tmp, ext->scrl); } else { diff --git a/src/lv_objx/lv_ta.c b/src/lv_objx/lv_ta.c index 0ad55dbe1..bfae7bc0d 100644 --- a/src/lv_objx/lv_ta.c +++ b/src/lv_objx/lv_ta.c @@ -1850,53 +1850,53 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_ lv_indev_get_vect(click_source, &vect_act); if(point_act.x < 0 || point_act.y < 0) return; /*Ignore event from keypad*/ - lv_point_t relative_position; - relative_position.x = point_act.x - label_coords.x1; - relative_position.y = point_act.y - label_coords.y1; + lv_point_t rel_pos; + rel_pos.x = point_act.x - label_coords.x1; + rel_pos.y = point_act.y - label_coords.y1; lv_coord_t label_width = lv_obj_get_width(ext->label); - uint16_t index_of_char_at_position; + uint16_t char_id_at_click; #if LV_LABEL_TEXT_SEL lv_label_ext_t * ext_label = lv_obj_get_ext_attr(ext->label); bool click_outside_label; /*Check if the click happened on the left side of the area outside the label*/ - if(relative_position.x < 0) { - index_of_char_at_position = 0; + if(rel_pos.x < 0) { + char_id_at_click = 0; click_outside_label = true; } /*Check if the click happened on the right side of the area outside the label*/ - else if(relative_position.x >= label_width) { - index_of_char_at_position = LV_TA_CURSOR_LAST; + else if(rel_pos.x >= label_width) { + char_id_at_click = LV_TA_CURSOR_LAST; click_outside_label = true; } else { - index_of_char_at_position = lv_label_get_letter_on(ext->label, &relative_position); - click_outside_label = !lv_label_is_char_under_pos(ext->label, &relative_position); + char_id_at_click = lv_label_get_letter_on(ext->label, &rel_pos); + click_outside_label = !lv_label_is_char_under_pos(ext->label, &rel_pos); } if(ext->text_sel_en) { if(!ext->text_sel_in_prog && !click_outside_label && sign == LV_SIGNAL_PRESSED) { /*Input device just went down. Store the selection start position*/ - ext->tmp_sel_start = index_of_char_at_position; + ext->tmp_sel_start = char_id_at_click; ext->tmp_sel_end = LV_LABEL_TEXT_SEL_OFF; ext->text_sel_in_prog = 1; lv_obj_set_drag(lv_page_get_scrl(ta), false); } else if(ext->text_sel_in_prog && sign == LV_SIGNAL_PRESSING) { /*Input device may be moving. Store the end position */ - ext->tmp_sel_end = index_of_char_at_position; + ext->tmp_sel_end = char_id_at_click; } else if(ext->text_sel_in_prog && (sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED)) { /*Input device is released. Check if anything was selected.*/ lv_obj_set_drag(lv_page_get_scrl(ta), true); } } - if(ext->text_sel_in_prog || sign == LV_SIGNAL_PRESSED) lv_ta_set_cursor_pos(ta, index_of_char_at_position); + if(ext->text_sel_in_prog || sign == LV_SIGNAL_PRESSED) lv_ta_set_cursor_pos(ta, char_id_at_click); if(ext->text_sel_in_prog) { /*If the selected area has changed then update the real values and*/ - /*invalidate the text area.*/ + /*Invalidate the text area.*/ if(ext->tmp_sel_start > ext->tmp_sel_end) { if(ext_label->txt_sel_start != ext->tmp_sel_end || ext_label->txt_sel_end != ext->tmp_sel_start) { ext_label->txt_sel_start = ext->tmp_sel_end; @@ -1923,17 +1923,17 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_ } #else /*Check if the click happened on the left side of the area outside the label*/ - if(relative_position.x < 0) { - index_of_char_at_position = 0; + if(rel_pos.x < 0) { + char_id_at_click = 0; } /*Check if the click happened on the right side of the area outside the label*/ - else if(relative_position.x >= label_width) { - index_of_char_at_position = LV_TA_CURSOR_LAST; + else if(rel_pos.x >= label_width) { + char_id_at_click = LV_TA_CURSOR_LAST; } else { - index_of_char_at_position = lv_label_get_letter_on(ext->label, &relative_position); + char_id_at_click = lv_label_get_letter_on(ext->label, &rel_pos); } - if(sign == LV_SIGNAL_PRESSED) lv_ta_set_cursor_pos(ta, index_of_char_at_position); + if(sign == LV_SIGNAL_PRESSED) lv_ta_set_cursor_pos(ta, char_id_at_click); #endif } diff --git a/src/lv_objx/lv_ta.h b/src/lv_objx/lv_ta.h index 4d3a4a4e3..ec2854e49 100644 --- a/src/lv_objx/lv_ta.h +++ b/src/lv_objx/lv_ta.h @@ -39,6 +39,8 @@ extern "C" { *********************/ #define LV_TA_CURSOR_LAST (0x7FFF) /*Put the cursor after the last character*/ +LV_EXPORT_CONST_INT(LV_TA_CURSOR_LAST); + /********************** * TYPEDEFS **********************/ diff --git a/src/lv_objx/lv_table.c b/src/lv_objx/lv_table.c index 64065d3aa..7a00c3aa4 100644 --- a/src/lv_objx/lv_table.c +++ b/src/lv_objx/lv_table.c @@ -145,20 +145,31 @@ void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const uint32_t cell = row * ext->col_cnt + col; lv_table_cell_format_t format; + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(table); + /*Save the format byte*/ if(ext->cell_data[cell]) { format.format_byte = ext->cell_data[cell][0]; } /*Initialize the format byte*/ else { - format.s.align = LV_LABEL_ALIGN_LEFT; + if(base_dir == LV_BIDI_DIR_LTR) format.s.align = LV_LABEL_ALIGN_LEFT; + else if(base_dir == LV_BIDI_DIR_RTL) format.s.align = LV_LABEL_ALIGN_RIGHT; + else if(base_dir == LV_BIDI_DIR_AUTO) format.s.align = lv_bidi_detect_base_dir(txt); + format.s.right_merge = 0; format.s.type = 0; format.s.crop = 0; } ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/ - strcpy(ext->cell_data[cell] + 1, txt); /*Leave the format byte*/ + +#if LV_USE_BIDI == 0 + strcpy(ext->cell_data[cell] + 1, txt); /*+1 to skip the format byte*/ +#else + lv_bidi_process(txt, ext->cell_data[cell] + 1, base_dir); +#endif + ext->cell_data[cell][0] = format.format_byte; refr_size(table); } diff --git a/src/lv_objx/lv_tabview.c b/src/lv_objx/lv_tabview.c index de28d3afb..5a97fe1d2 100644 --- a/src/lv_objx/lv_tabview.c +++ b/src/lv_objx/lv_tabview.c @@ -143,7 +143,7 @@ lv_obj_t * lv_tabview_create(lv_obj_t * par, const lv_obj_t * copy) lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_PR, th->style.tabview.btn.tgl_pr); } else { lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BG, &lv_style_plain); - lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, &lv_style_transp); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, &lv_style_pretty);//transp); lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_INDIC, &lv_style_plain_color); } } @@ -319,6 +319,10 @@ void lv_tabview_set_tab_act(lv_obj_t * tabview, uint16_t id, lv_anim_enable_t an ext->tab_cur = id; + if(lv_obj_get_base_dir(tabview) == LV_BIDI_DIR_RTL) { + id = (ext->tab_cnt - (id + 1)); + } + lv_coord_t cont_x; switch(ext->btns_pos) { @@ -358,6 +362,11 @@ void lv_tabview_set_tab_act(lv_obj_t * tabview, uint16_t id, lv_anim_enable_t an } #endif + lv_bidi_dir_t base_dir = lv_obj_get_base_dir(tabview); + + if(base_dir == LV_BIDI_DIR_RTL) id = ext->tab_cnt - id - 1; + + /*Move the indicator*/ const lv_style_t * tabs_style = lv_obj_get_style(ext->btns); lv_coord_t indic_size; @@ -741,7 +750,6 @@ static lv_res_t tabview_scrl_signal(lv_obj_t * tabview_scrl, lv_signal_t sign, v if(res != LV_RES_OK) return res; } - return res; }