diff --git a/README.md b/README.md index 2881caf85..d25abb3a4 100644 --- a/README.md +++ b/README.md @@ -188,8 +188,8 @@ Styles can be assigned to the objects to changed their appearance. A style descr You can create a new style like this: ```c -static lv_style_t style1; /*Declare a new style. Should be `static`*/ -lv_style_copy(&style1, &lv_style_plain); /*Copy a buil-in style*/ +static lv_style_t style1; /*Declare a new style. Should be `static`*/ +lv_style_copy(&style1, &lv_style_plain); /*Copy a built-in style*/ style1.body.main_color = LV_COLOR_RED; /*Main color*/ style1.body.grad_color = lv_color_hex(0xffd83c) /*Gradient color (orange)*/ style1.body.radius = 3; diff --git a/lv_conf_template.h b/lv_conf_template.h index bbbd6734e..baea0ca1f 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -438,6 +438,9 @@ typedef void * lv_obj_user_data_t; /*Container (dependencies: -*/ #define LV_USE_CONT 1 +/*Color picker (dependencies: -*/ +#define LV_USE_CPICKER 1 + /*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ #define LV_USE_DDLIST 1 #if LV_USE_DDLIST != 0 diff --git a/lvgl.h b/lvgl.h index 0c395bc87..567beb96b 100644 --- a/lvgl.h +++ b/lvgl.h @@ -47,6 +47,7 @@ extern "C" { #include "src/lv_objx/lv_chart.h" #include "src/lv_objx/lv_table.h" #include "src/lv_objx/lv_cb.h" +#include "src/lv_objx/lv_cpicker.h" #include "src/lv_objx/lv_bar.h" #include "src/lv_objx/lv_slider.h" #include "src/lv_objx/lv_led.h" diff --git a/src/lv_conf_checker.h b/src/lv_conf_checker.h index e9cf1935a..b7149d0db 100644 --- a/src/lv_conf_checker.h +++ b/src/lv_conf_checker.h @@ -612,6 +612,11 @@ #define LV_USE_CONT 1 #endif +/*Color picker (dependencies: -*/ +#ifndef LV_USE_CPICKER +#define LV_USE_CPICKER 1 +#endif + /*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ #ifndef LV_USE_DDLIST #define LV_USE_DDLIST 1 diff --git a/src/lv_core/lv_debug.c b/src/lv_core/lv_debug.c index 28d09d1da..f5b02d541 100644 --- a/src/lv_core/lv_debug.c +++ b/src/lv_core/lv_debug.c @@ -7,6 +7,7 @@ * INCLUDES *********************/ #include "lv_obj.h" +#include "lv_debug.h" #if LV_USE_DEBUG @@ -126,7 +127,7 @@ bool lv_debug_check_str(const void * str) return false; } -void lv_debug_log_error(const char * msg, unsigned long int value) +void lv_debug_log_error(const char * msg, uint64_t value) { static const char hex[] = "0123456789ABCDEF"; diff --git a/src/lv_core/lv_debug.h b/src/lv_core/lv_debug.h index f66fb65b4..f225f766a 100644 --- a/src/lv_core/lv_debug.h +++ b/src/lv_core/lv_debug.h @@ -34,7 +34,7 @@ bool lv_debug_check_obj_type(const lv_obj_t * obj, const char * obj_type); bool lv_debug_check_obj_valid(const lv_obj_t * obj); -bool lv_debug_check_style(const void * str); +bool lv_debug_check_style(const lv_style_t * style); bool lv_debug_check_str(const void * str); @@ -136,6 +136,8 @@ void lv_debug_log_error(const char * msg, uint64_t value); #else /* LV_USE_DEBUG == 0 */ +#define LV_DEBUG_ASSERT(expr, msg, value) do{}while(0) + #define LV_ASSERT_NULL(p) true #define LV_ASSERT_MEM(p) true #define LV_ASSERT_STR(p) true diff --git a/src/lv_draw/lv_draw_arc.c b/src/lv_draw/lv_draw_arc.c index 521933297..ed4192dfe 100644 --- a/src/lv_draw/lv_draw_arc.c +++ b/src/lv_draw/lv_draw_arc.c @@ -20,7 +20,6 @@ /********************** * STATIC PROTOTYPES **********************/ -static uint16_t fast_atan2(int x, int y); static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa); static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, @@ -121,7 +120,7 @@ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, cons if(r_act_sqr > r_out_sqr) continue; #endif - deg_base = fast_atan2(xi, yi) - 180; + deg_base = lv_atan2(xi, yi) - 180; #if LV_ANTIALIAS int opa = -1; diff --git a/src/lv_draw/lv_draw_img.c b/src/lv_draw/lv_draw_img.c index dcaa444a8..0f1f692d2 100644 --- a/src/lv_draw/lv_draw_img.c +++ b/src/lv_draw/lv_draw_img.c @@ -51,7 +51,7 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * if(src == NULL) { LV_LOG_WARN("Image draw: src is NULL"); lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER); - lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, -1, -1, NULL, LV_BIDI_DIR_LTR); + lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, NULL, NULL, LV_BIDI_DIR_LTR); return; } @@ -61,7 +61,7 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * if(res == LV_RES_INV) { LV_LOG_WARN("Image draw error"); lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER); - lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, -1, -1, NULL, LV_BIDI_DIR_LTR); + lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, NULL, NULL, LV_BIDI_DIR_LTR); return; } } @@ -562,8 +562,7 @@ static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mas if(cdsc->dec_dsc.error_msg != NULL) { LV_LOG_WARN("Image draw error"); lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER); - lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, cdsc->dec_dsc.error_msg, LV_TXT_FLAG_NONE, NULL, -1, - -1, NULL, LV_BIDI_DIR_LTR); + lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, cdsc->dec_dsc.error_msg, LV_TXT_FLAG_NONE, NULL, NULL, NULL, LV_BIDI_DIR_LTR); } /* The decoder open could open the image and gave the entire uncompressed image. * Just draw it!*/ diff --git a/src/lv_draw/lv_draw_label.c b/src/lv_draw/lv_draw_label.c index 86b486002..29865438f 100644 --- a/src/lv_draw/lv_draw_label.c +++ b/src/lv_draw/lv_draw_label.c @@ -52,21 +52,17 @@ static uint8_t hex_char_to_num(char hex); * @param txt 0 terminated text to write * @param flag settings for the text from 'txt_flag_t' enum * @param offset text offset in x and y direction (NULL if unused) - * @param sel_start start index of selected area (`LV_LABEL_TXT_SEL_OFF` if none) - * @param sel_end end index of selected area (`LV_LABEL_TXT_SEL_OFF` if none) + * @param sel make the text selected in the range by drawing a background there */ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale, - const char * txt, lv_txt_flag_t flag, lv_point_t * offset, uint16_t sel_start, uint16_t sel_end, + const char * txt, lv_txt_flag_t flag, lv_point_t * offset, lv_draw_label_txt_sel_t * sel, lv_draw_label_hint_t * hint, lv_bidi_dir_t bidi_dir) { const lv_font_t * font = style->text.font; lv_coord_t w; /*No need to waste processor time if string is empty*/ - if (txt[0] == '\0') - { - return; - } + if (txt[0] == '\0') return; if((flag & LV_TXT_FLAG_EXPAND) == 0) { /*Normally use the label's width as width*/ @@ -75,7 +71,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st /*If EXAPND is enabled then not limit the text's width to the object's width*/ lv_point_t p; lv_txt_get_size(&p, txt, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, - flag); + flag); w = p.x; } @@ -113,6 +109,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st pos.y += hint->y; } + uint32_t line_end = line_start + lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag); /*Go the first visible line*/ @@ -147,6 +144,18 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->text.opa : (uint16_t)((uint16_t)style->text.opa * opa_scale) >> 8; + uint16_t sel_start = 0xFFFF; + uint16_t sel_end = 0xFFFF; + if(sel) { + sel_start = sel->start; + sel_end = sel->end; + if(sel_start > sel_end) { + uint16_t tmp = sel_start; + sel_start = sel_end; + sel_end = tmp; + } + } + cmd_state_t cmd_state = CMD_STATE_WAIT; uint32_t i; uint16_t par_start = 0; @@ -166,16 +175,29 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st i = 0; uint32_t letter; uint32_t letter_next; - while(i < line_end - line_start) { #if LV_USE_BIDI - char *bidi_txt = lv_draw_get_buf(line_end - line_start + 1); - lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, bidi_dir); + char *bidi_txt = lv_draw_get_buf(line_end - line_start + 1); + lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, bidi_dir, NULL, 0); #else - const char *bidi_txt = txt + line_start; + const char *bidi_txt = txt + line_start; #endif + + while(i < line_end - line_start) { + uint16_t logical_char_pos = 0; + if(sel_start != 0xFFFF && sel_end != 0xFFFF) { +#if LV_USE_BIDI + logical_char_pos = lv_txt_encoded_get_char_id(txt, line_start); + uint16_t t = lv_txt_encoded_get_char_id(bidi_txt, i); + logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, bidi_dir, t, NULL); +#else + logical_char_pos = lv_txt_encoded_get_char_id(txt, line_start + i); +#endif + } + letter = lv_txt_encoded_next(bidi_txt, &i); letter_next = lv_txt_encoded_next(&bidi_txt[i], NULL); + /*Handle the re-color command*/ if((flag & LV_TXT_FLAG_RECOLOR) != 0) { if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) { @@ -220,9 +242,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st letter_w = lv_font_get_glyph_width(font, letter, letter_next); if(sel_start != 0xFFFF && sel_end != 0xFFFF) { - int char_ind = lv_encoded_get_char_id(bidi_txt, i); - /*Do not draw the rectangle on the character at `sel_start`.*/ - if(char_ind > sel_start && char_ind <= sel_end) { + if(logical_char_pos >= sel_start && logical_char_pos < sel_end) { lv_area_t sel_coords; sel_coords.x1 = pos.x; sel_coords.y1 = pos.y; @@ -231,6 +251,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st lv_draw_rect(&sel_coords, mask, &sel_style, opa); } } + lv_draw_letter(&pos, mask, font, letter, color, opa); if(letter_w > 0) { @@ -245,7 +266,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st /*Align to middle*/ if(flag & LV_TXT_FLAG_CENTER) { line_width = - lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); + lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); pos.x += (lv_area_get_width(coords) - line_width) / 2; @@ -253,7 +274,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st /*Align to the right*/ else if(flag & LV_TXT_FLAG_RIGHT) { line_width = - lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); + lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); pos.x += lv_area_get_width(coords) - line_width; } @@ -283,13 +304,13 @@ static uint8_t hex_char_to_num(char hex) if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/ switch(hex) { - case 'A': result = 10; break; - case 'B': result = 11; break; - case 'C': result = 12; break; - case 'D': result = 13; break; - case 'E': result = 14; break; - case 'F': result = 15; break; - default: result = 0; break; + case 'A': result = 10; break; + case 'B': result = 11; break; + case 'C': result = 12; break; + case 'D': result = 13; break; + case 'E': result = 14; break; + case 'F': result = 15; break; + default: result = 0; break; } } diff --git a/src/lv_draw/lv_draw_label.h b/src/lv_draw/lv_draw_label.h index 39ec60607..6dc4d6464 100644 --- a/src/lv_draw/lv_draw_label.h +++ b/src/lv_draw/lv_draw_label.h @@ -19,11 +19,19 @@ extern "C" { /********************* * DEFINES *********************/ +#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF) /********************** * TYPEDEFS **********************/ +typedef struct +{ + uint16_t start; + uint16_t end; +}lv_draw_label_txt_sel_t; + + /** Store some info to speed up drawing of very large texts * It takes a lot of time to get the first visible character because * all the previous characters needs to be checked to calculate the positions. @@ -55,10 +63,10 @@ typedef struct { * @param flag settings for the text from 'txt_flag_t' enum * @param offset text offset in x and y direction (NULL if unused) * @param sel_start start index of selected area (`LV_LABEL_TXT_SEL_OFF` if none) - * @param sel_end end index of selected area (`LV_LABEL_TXT_SEL_OFF` if none) + * @param bidi_dir base direction of the text */ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale, - const char * txt, lv_txt_flag_t flag, lv_point_t * offset, uint16_t sel_start, uint16_t sel_end, + const char * txt, lv_txt_flag_t flag, lv_point_t * offset, lv_draw_label_txt_sel_t * sel, lv_draw_label_hint_t * hint, lv_bidi_dir_t bidi_dir); /********************** diff --git a/src/lv_draw/lv_draw_triangle.c b/src/lv_draw/lv_draw_triangle.c index e06af2b18..1c8939ad7 100644 --- a/src/lv_draw/lv_draw_triangle.c +++ b/src/lv_draw/lv_draw_triangle.c @@ -45,8 +45,7 @@ static void point_swap(lv_point_t * p1, lv_point_t * p2); */ void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale) { - - /*Return is the triangle is degenerated*/ + /*Return if the triangle is degenerated*/ if(points[0].x == points[1].x && points[0].y == points[1].y) return; if(points[1].x == points[2].x && points[1].y == points[2].y) return; if(points[0].x == points[2].x && points[0].y == points[2].y) return; diff --git a/src/lv_misc/lv_area.c b/src/lv_misc/lv_area.c index 025e1733e..de649c5b0 100644 --- a/src/lv_misc/lv_area.c +++ b/src/lv_misc/lv_area.c @@ -192,6 +192,19 @@ bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p) return is_in; } +/** + * Increment or decrement an area's size by a single amount + * @param a_p pointer to an area to grow + * @param amount amount to increment the area, or negative to decrement + */ +void lv_area_increment(lv_area_t * a_p, const lv_coord_t amount) +{ + a_p->x1 -= amount; + a_p->y1 -= amount; + a_p->x2 += amount; + a_p->y2 += amount; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/lv_misc/lv_area.h b/src/lv_misc/lv_area.h index 149df2302..7a827c91a 100644 --- a/src/lv_misc/lv_area.h +++ b/src/lv_misc/lv_area.h @@ -168,6 +168,13 @@ bool lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p); */ bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p); +/** + * Increment or decrement an area's size by a single amount + * @param a_p pointer to an area to grow + * @param amount amount to increment the area, or negative to decrement + */ +void lv_area_increment(lv_area_t * a_p, const lv_coord_t amount); + /********************** * MACROS **********************/ diff --git a/src/lv_misc/lv_bidi.c b/src/lv_misc/lv_bidi.c index 634e1f896..bde752071 100644 --- a/src/lv_misc/lv_bidi.c +++ b/src/lv_misc/lv_bidi.c @@ -6,9 +6,10 @@ /********************* * INCLUDES *********************/ -#include "lv_bidi.h" #include +#include "lv_bidi.h" #include "lv_txt.h" +#include "../lv_draw/lv_draw.h" #if LV_USE_BIDI @@ -17,6 +18,11 @@ *********************/ #define LV_BIDI_BRACKLET_DEPTH 4 +// Highest bit of the 16-bit pos_conv value specifies whether this pos is RTL or not +#define GET_POS(x) ((x) & 0x7FFF) +#define IS_RTL_POS(x) (((x) & 0x8000) != 0) +#define SET_RTL_POS(x, is_rtl) (GET_POS(x) | ((is_rtl)? 0x8000: 0)) + /********************** * TYPEDEFS **********************/ @@ -29,10 +35,12 @@ typedef struct /********************** * STATIC PROTOTYPES **********************/ -static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t max_len, uint32_t * len); -static void rtl_reverse(char * dest, const char * src, uint32_t len); +static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t max_len, uint32_t * len, uint16_t * pos_conv_len); +static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t *pos_conv_out, uint16_t pos_conv_rd_base, uint16_t pos_conv_len); static uint32_t char_change_to_pair(uint32_t letter); static lv_bidi_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter, lv_bidi_dir_t base_dir); +static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index); +static uint32_t get_txt_len(const char * txt, uint32_t max_len); /********************** * STATIC VARIABLES @@ -64,7 +72,7 @@ void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir while(str_in[par_start] != '\0') { par_len = lv_bidi_get_next_paragraph(&str_in[par_start]); - lv_bidi_process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir); + lv_bidi_process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir, NULL, 0); par_start += par_len; while(str_in[par_start] == '\n' || str_in[par_start] == '\r') { @@ -137,23 +145,54 @@ bool lv_bidi_letter_is_neutral(uint32_t letter) return false; } +uint16_t lv_bidi_get_logical_pos(const char * str_in, char **bidi_txt, uint32_t len, lv_bidi_dir_t base_dir, uint32_t visual_pos, bool *is_rtl) +{ + uint32_t pos_conv_len = get_txt_len(str_in, len); + void *buf = lv_draw_get_buf(len + pos_conv_len * sizeof(uint16_t)); + if (bidi_txt) *bidi_txt = buf; + uint16_t *pos_conv_buf = (uint16_t*) ((char*)buf + len); + lv_bidi_process_paragraph(str_in, bidi_txt? *bidi_txt: NULL, len, base_dir, pos_conv_buf, pos_conv_len); + if (is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]); + return GET_POS(pos_conv_buf[visual_pos]); +} -/********************** - * STATIC FUNCTIONS - **********************/ +uint16_t lv_bidi_get_visual_pos(const char * str_in, char **bidi_txt, uint16_t len, lv_bidi_dir_t base_dir, uint32_t logical_pos, bool *is_rtl) +{ + uint32_t pos_conv_len = get_txt_len(str_in, len); + void *buf = lv_draw_get_buf(len + pos_conv_len * sizeof(uint16_t)); + if (bidi_txt) *bidi_txt = buf; + uint16_t *pos_conv_buf = (uint16_t*) ((char*)buf + len); + lv_bidi_process_paragraph(str_in, bidi_txt? *bidi_txt: NULL, len, base_dir, pos_conv_buf, pos_conv_len); + for (uint16_t i = 0; i < pos_conv_len; i++){ + if (GET_POS(pos_conv_buf[i]) == logical_pos){ + if (is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[i]); + return i; + } + } + return (uint16_t) -1; +} -void lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir) +void lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir, uint16_t *pos_conv_out, uint16_t pos_conv_len) { uint32_t run_len = 0; lv_bidi_dir_t run_dir; uint32_t rd = 0; uint32_t wr; + uint16_t pos_conv_run_len = 0; + uint16_t pos_conv_rd = 0; + uint16_t pos_conv_wr; if(base_dir == LV_BIDI_DIR_AUTO) base_dir = lv_bidi_detect_base_dir(str_in); - if(base_dir == LV_BIDI_DIR_RTL) wr = len; - else wr = 0; + if(base_dir == LV_BIDI_DIR_RTL) { + wr = len; + pos_conv_wr = pos_conv_len; + } + else { + wr = 0; + pos_conv_wr = 0; + } - str_out[len] = '\0'; + if (str_out) str_out[len] = '\0'; lv_bidi_dir_t dir = base_dir; @@ -163,39 +202,59 @@ void lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len /*Process neutral chars in the beginning*/ while(rd < len) { uint32_t letter = lv_txt_encoded_next(str_in, &rd); + pos_conv_rd++; dir = lv_bidi_get_letter_dir(letter); if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(str_in, rd, len, letter, base_dir); - 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 && str_in[rd] != '\0') { + lv_txt_encoded_prev(str_in, &rd); + pos_conv_rd--; + } if(rd) { if(base_dir == LV_BIDI_DIR_LTR) { - memcpy(&str_out[wr], str_in, rd); - wr += rd; + if (str_out) { + memcpy(&str_out[wr], str_in, rd); + wr += rd; + } + if (pos_conv_out) { + fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_rd, 0); + pos_conv_wr += pos_conv_rd; + } } else { wr -= rd; - rtl_reverse(&str_out[wr], str_in, rd); + pos_conv_wr -= pos_conv_rd; + rtl_reverse(str_out? &str_out[wr]: NULL, str_in, rd, pos_conv_out? &pos_conv_out[pos_conv_wr]: NULL, 0, pos_conv_rd); } } /*Get and process the runs*/ - while(rd < len) { - run_dir = get_next_run(&str_in[rd], base_dir, len - rd, &run_len); + + while(rd < len && str_in[rd]) { + run_dir = get_next_run(&str_in[rd], base_dir, len - rd, &run_len, &pos_conv_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); + if(run_dir == LV_BIDI_DIR_LTR) { + if (str_out) memcpy(&str_out[wr], &str_in[rd], run_len); + if (pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd); + } + else rtl_reverse(str_out? &str_out[wr]: NULL, &str_in[rd], run_len, pos_conv_out? &pos_conv_out[pos_conv_wr] : NULL, pos_conv_rd, pos_conv_run_len); wr += run_len; + pos_conv_wr += pos_conv_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); + pos_conv_wr -= pos_conv_run_len; + if(run_dir == LV_BIDI_DIR_LTR) { + if (str_out) memcpy(&str_out[wr], &str_in[rd], run_len); + if (pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd); + } + else rtl_reverse(str_out? &str_out[wr]: NULL, &str_in[rd], run_len, pos_conv_out? &pos_conv_out[pos_conv_wr] : NULL, pos_conv_rd, pos_conv_run_len); } rd += run_len; + pos_conv_rd += pos_conv_run_len; } } @@ -212,24 +271,53 @@ uint32_t lv_bidi_get_next_paragraph(const char * txt) return i; } -static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t max_len, uint32_t * len) +/********************** + * STATIC FUNCTIONS + **********************/ + +static uint32_t get_txt_len(const char * txt, uint32_t max_len) +{ + uint32_t len = 0; + uint32_t i = 0; + + while(i < max_len && txt[i] != '\0') { + lv_txt_encoded_next(txt, &i); + len++; + } + + return len; +} + +static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index) +{ + for (uint16_t i = 0; i < len; i++) + { + out[i] = SET_RTL_POS(index, false); + index++; + } +} + +static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t max_len, uint32_t * len, uint16_t * pos_conv_len) { uint32_t i = 0; uint32_t letter; + uint16_t pos_conv_i = 0; + letter = lv_txt_encoded_next(txt, NULL); lv_bidi_dir_t dir = lv_bidi_get_letter_dir(letter); if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, 0, max_len, letter, base_dir); - /*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); + pos_conv_i++; dir = lv_bidi_get_letter_dir(letter); if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, i, max_len, letter, base_dir); if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') { *len = i; + *pos_conv_len = pos_conv_i; return base_dir; } } @@ -238,84 +326,119 @@ static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint uint32_t i_prev = i; uint32_t i_last_strong = i; + uint16_t pos_conv_i_prev = pos_conv_i; + uint16_t pos_conv_i_last_strong = pos_conv_i; /*Find the next char which has different direction*/ lv_bidi_dir_t next_dir = base_dir; while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') { letter = lv_txt_encoded_next(txt, &i); + pos_conv_i++; next_dir = lv_bidi_get_letter_dir(letter); if(next_dir == LV_BIDI_DIR_NEUTRAL) next_dir = bracket_process(txt, i, max_len, letter, base_dir); /*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; + if(run_dir == base_dir) { + *len = i_prev; + *pos_conv_len = pos_conv_i_prev; + } /*Exclude neutrals if `run_dir != base_dir` */ - else *len = i_last_strong; + else { + *len = i_last_strong; + *pos_conv_len = pos_conv_i_last_strong; + } return run_dir; } - if(next_dir != LV_BIDI_DIR_NEUTRAL) i_last_strong = i; + if(next_dir != LV_BIDI_DIR_NEUTRAL) { + i_last_strong = i; + pos_conv_i_last_strong = pos_conv_i; + } i_prev = i; + pos_conv_i_prev = pos_conv_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; + if(run_dir == base_dir) { + *len = i_prev; + *pos_conv_len = pos_conv_i_prev; + } /*Exclude neutrals if `run_dir != base_dir` */ - else *len = i_last_strong; + else { + *len = i_last_strong; + *pos_conv_len = pos_conv_i_last_strong; + } return run_dir; - } -static void rtl_reverse(char * dest, const char * src, uint32_t len) +static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t *pos_conv_out, uint16_t pos_conv_rd_base, uint16_t pos_conv_len) { uint32_t i = len; uint32_t wr = 0; + uint16_t pos_conv_i = pos_conv_len; + uint16_t pos_conv_wr = 0; while(i) { uint32_t letter = lv_txt_encoded_prev(src, &i); + uint16_t pos_conv_letter = --pos_conv_i; /*Keep weak letters (numbers) as LTR*/ if(lv_bidi_letter_is_weak(letter)) { uint32_t last_weak = i; uint32_t first_weak = i; + uint16_t pos_conv_last_weak = pos_conv_i; + uint16_t pos_conv_first_weak = pos_conv_i; while(i) { letter = lv_txt_encoded_prev(src, &i); + pos_conv_letter = --pos_conv_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 != '$' && letter != '%') { + if (lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') { lv_txt_encoded_next(src, &i); /*Rewind one letter*/ + pos_conv_i++; first_weak = i; + pos_conv_first_weak = pos_conv_i; break; } } - if(i == 0) first_weak = 0; + if(i == 0) { + first_weak = 0; + pos_conv_first_weak = 0; + } - memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1); + if (dest) memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1); + if (pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1, pos_conv_rd_base + pos_conv_first_weak); wr += last_weak - first_weak + 1; - + pos_conv_wr += pos_conv_last_weak - pos_conv_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; + if (dest) dest[wr] = (uint8_t)new_letter; + if (pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true); + wr++; + pos_conv_wr++; } /*Just store the letter*/ else { - memcpy(&dest[wr], &src[i], letter_size); + if (dest) memcpy(&dest[wr], &src[i], letter_size); + if (pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true); wr += letter_size; + pos_conv_wr++; } } } diff --git a/src/lv_misc/lv_bidi.h b/src/lv_misc/lv_bidi.h index 9ce0fffa1..215727aa7 100644 --- a/src/lv_misc/lv_bidi.h +++ b/src/lv_misc/lv_bidi.h @@ -53,13 +53,15 @@ typedef uint8_t lv_bidi_dir_t; #if LV_USE_BIDI void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir); -void lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir); +void lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir, uint16_t *pos_conv_out, uint16_t pos_conv_len); uint32_t lv_bidi_get_next_paragraph(const char * txt); 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); +uint16_t lv_bidi_get_logical_pos(const char * str_in, char **bidi_txt, uint32_t len, lv_bidi_dir_t base_dir, uint32_t visual_pos, bool *is_rtl); +uint16_t lv_bidi_get_visual_pos(const char * str_in, char **bidi_txt, uint16_t len, lv_bidi_dir_t base_dir, uint32_t logical_pos, bool *is_rtl); /********************** * MACROS diff --git a/src/lv_misc/lv_color.c b/src/lv_misc/lv_color.c index f5bec62bb..cd4825dfe 100644 --- a/src/lv_misc/lv_color.c +++ b/src/lv_misc/lv_color.c @@ -7,6 +7,7 @@ * INCLUDES *********************/ #include "lv_color.h" +#include "lv_math.h" /********************* * DEFINES @@ -105,39 +106,66 @@ lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v) } /** - * Convert an RGB color to HSV - * @param r red - * @param g green - * @param b blue - * @return the given RGB color n HSV + * Convert a 32-bit RGB color to HSV + * @param r8 8-bit red + * @param g8 8-bit green + * @param b8 8-bit blue + * @return the given RGB color in HSV */ -lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b) +lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8) { + uint16_t r = ((uint32_t)r8 << 10) / 255; + uint16_t g = ((uint32_t)g8 << 10) / 255; + uint16_t b = ((uint32_t)b8 << 10) / 255; + + uint16_t rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b); + uint16_t rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b); + lv_color_hsv_t hsv; - uint8_t rgbMin, rgbMax; - rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b); - rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b); + // https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness + hsv.v = (100 * rgbMax) >> 10; - hsv.v = rgbMax; - if(hsv.v == 0) { + int32_t delta = rgbMax - rgbMin; + if (LV_MATH_ABS(delta) < 3) { hsv.h = 0; hsv.s = 0; return hsv; } - hsv.s = 255 * (long)(rgbMax - rgbMin) / hsv.v; - if(hsv.s == 0) { + // https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation + hsv.s = 100 * delta / rgbMax; + if(hsv.s < 3) { hsv.h = 0; return hsv; } + // https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma + int32_t h; if(rgbMax == r) - hsv.h = 0 + 43 * (g - b) / (rgbMax - rgbMin); + h = (((g - b) << 10) / delta) + (g < b ? (6 << 10) : 0); // between yellow & magenta else if(rgbMax == g) - hsv.h = 85 + 43 * (b - r) / (rgbMax - rgbMin); + h = (((b - r) << 10) / delta) + (2 << 10); // between cyan & yellow + else if(rgbMax == b) + h = (((r - g) << 10) / delta) + (4 << 10); // between magenta & cyan else - hsv.h = 171 + 43 * (r - g) / (rgbMax - rgbMin); + h = 0; + h *= 60; + h >>= 10; + if (h < 0) h += 360; + hsv.h = h; return hsv; } + +/** + * Convert a color to HSV + * @param color color + * @return the given color in HSV + */ +lv_color_hsv_t lv_color_to_hsv(lv_color_t color) +{ + lv_color32_t color32; + color32.full = lv_color_to32(color); + return lv_color_rgb_to_hsv(color32.ch.red, color32.ch.green, color32.ch.blue); +} diff --git a/src/lv_misc/lv_color.h b/src/lv_misc/lv_color.h index 4f994e2ef..95542afb6 100644 --- a/src/lv_misc/lv_color.h +++ b/src/lv_misc/lv_color.h @@ -96,10 +96,13 @@ enum { typedef union { - uint8_t blue : 1; - uint8_t green : 1; - uint8_t red : 1; - uint8_t full : 1; + struct + { + uint8_t blue : 1; + uint8_t green : 1; + uint8_t red : 1; + } ch; + uint8_t full; } lv_color1_t; typedef union @@ -389,10 +392,10 @@ static inline uint8_t lv_color_brightness(lv_color_t color) /* The most simple macro to create a color from R,G and B values */ #if LV_COLOR_DEPTH == 1 -#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){(b8 >> 7 | g8 >> 7 | r8 >> 7)}) +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){.full = (b8 >> 7 | g8 >> 7 | r8 >> 7)}) static inline lv_color_t lv_color_make(int r8, int g8, int b8) { - lv_color_t color; + lv_color_t color = { 0 }; color.full = (b8 >> 7 | g8 >> 7 | r8 >> 7); return color; } @@ -463,13 +466,20 @@ static inline lv_color_t lv_color_hex3(uint32_t c) lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v); /** - * Convert an RGB color to HSV - * @param r red - * @param g green - * @param b blue - * @return the given RGB color n HSV + * Convert a 32-bit RGB color to HSV + * @param r8 8-bit red + * @param g8 8-bit green + * @param b8 8-bit blue + * @return the given RGB color in HSV */ -lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b); +lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8); + +/** + * Convert a color to HSV + * @param color color + * @return the given color in HSV + */ +lv_color_hsv_t lv_color_to_hsv(lv_color_t color); /********************** * MACROS diff --git a/src/lv_misc/lv_log.c b/src/lv_misc/lv_log.c index 2d12da4e1..acbdfb732 100644 --- a/src/lv_misc/lv_log.c +++ b/src/lv_misc/lv_log.c @@ -12,6 +12,7 @@ #if LV_LOG_PRINTF #include #endif + /********************* * DEFINES *********************/ diff --git a/src/lv_misc/lv_math.c b/src/lv_misc/lv_math.c index cda1138a7..f015456e4 100644 --- a/src/lv_misc/lv_math.c +++ b/src/lv_misc/lv_math.c @@ -94,6 +94,87 @@ int32_t lv_bezier3(uint32_t t, int32_t u0, int32_t u1, int32_t u2, int32_t u3) return v1 + v2 + v3 + v4; } +/** + * Calculate the atan2 of a vector. + * @param x + * @param y + * @return the angle in degree calculated from the given parameters in range of [0..360] + */ +uint16_t lv_atan2(int x, int y) +{ + // Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com + // Converts any XY values including 0 to a degree value that should be + // within +/- 1 degree of the accurate value without needing + // large slow trig functions like ArcTan() or ArcCos(). + // NOTE! at least one of the X or Y values must be non-zero! + // This is the full version, for all 4 quadrants and will generate + // the angle in integer degrees from 0-360. + // Any values of X and Y are usable including negative values provided + // they are between -1456 and 1456 so the 16bit multiply does not overflow. + + unsigned char negflag; + unsigned char tempdegree; + unsigned char comp; + unsigned int degree; // this will hold the result + //signed int x; // these hold the XY vector at the start + //signed int y; // (and they will be destroyed) + unsigned int ux; + unsigned int uy; + + // Save the sign flags then remove signs and get XY as unsigned ints + negflag = 0; + if(x < 0) { + negflag += 0x01; // x flag bit + x = (0 - x); // is now + + } + ux = x; // copy to unsigned var before multiply + if(y < 0) { + negflag += 0x02; // y flag bit + y = (0 - y); // is now + + } + uy = y; // copy to unsigned var before multiply + + // 1. Calc the scaled "degrees" + if(ux > uy) { + degree = (uy * 45) / ux; // degree result will be 0-45 range + negflag += 0x10; // octant flag bit + } else { + degree = (ux * 45) / uy; // degree result will be 0-45 range + } + + // 2. Compensate for the 4 degree error curve + comp = 0; + tempdegree = degree; // use an unsigned char for speed! + if(tempdegree > 22) { // if top half of range + if(tempdegree <= 44) comp++; + if(tempdegree <= 41) comp++; + if(tempdegree <= 37) comp++; + if(tempdegree <= 32) comp++; // max is 4 degrees compensated + } else { // else is lower half of range + if(tempdegree >= 2) comp++; + if(tempdegree >= 6) comp++; + if(tempdegree >= 10) comp++; + if(tempdegree >= 15) comp++; // max is 4 degrees compensated + } + degree += comp; // degree is now accurate to +/- 1 degree! + + // Invert degree if it was X>Y octant, makes 0-45 into 90-45 + if(negflag & 0x10) degree = (90 - degree); + + // 3. Degree is now 0-90 range for this quadrant, + // need to invert it for whichever quadrant it was in + if(negflag & 0x02) { // if -Y + if(negflag & 0x01) // if -Y -X + degree = (180 + degree); + else // else is -Y +X + degree = (180 - degree); + } else { // else is +Y + if(negflag & 0x01) // if +Y -X + degree = (360 - degree); + } + return degree; +} + /** * Calculate the integer square root of a number. * @param num diff --git a/src/lv_misc/lv_math.h b/src/lv_misc/lv_math.h index 201a16bf2..dc2c547d5 100644 --- a/src/lv_misc/lv_math.h +++ b/src/lv_misc/lv_math.h @@ -54,6 +54,14 @@ int16_t lv_trigo_sin(int16_t angle); */ int32_t lv_bezier3(uint32_t t, int32_t u0, int32_t u1, int32_t u2, int32_t u3); +/** + * Calculate the atan2 of a vector. + * @param x + * @param y + * @return the angle in degree calculated from the given parameters in range of [0..360] + */ +uint16_t lv_atan2(int x, int y); + /** * Calculate the integer square root of a number. * @param num diff --git a/src/lv_misc/lv_task.c b/src/lv_misc/lv_task.c index c0656f408..af0c95aa6 100644 --- a/src/lv_misc/lv_task.c +++ b/src/lv_misc/lv_task.c @@ -68,16 +68,16 @@ LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void) LV_LOG_TRACE("lv_task_handler started"); /*Avoid concurrent running of the task handler*/ - static bool task_handler_mutex = false; - if(task_handler_mutex) return; - task_handler_mutex = true; + static bool already_running = false; + if(already_running) return; + already_running = true; static uint32_t idle_period_start = 0; static uint32_t handler_start = 0; static uint32_t busy_time = 0; if(lv_task_run == false) { - task_handler_mutex = false; /*Release mutex*/ + already_running = false; /*Release mutex*/ return; } @@ -154,7 +154,7 @@ LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void) idle_period_start = lv_tick_get(); } - task_handler_mutex = false; /*Release the mutex*/ + already_running = false; /*Release the mutex*/ LV_LOG_TRACE("lv_task_handler ready"); } diff --git a/src/lv_misc/lv_txt.c b/src/lv_misc/lv_txt.c index d18e456b0..ea2eaa81d 100644 --- a/src/lv_misc/lv_txt.c +++ b/src/lv_misc/lv_txt.c @@ -56,7 +56,7 @@ uint32_t (*lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_utf8_con uint32_t (*lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_utf8_next; uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_utf8_prev; uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_utf8_get_byte_id; -uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t) = lv_txt_utf8_get_char_id; +uint32_t (*lv_txt_encoded_get_char_id)(const char *, uint32_t) = lv_txt_utf8_get_char_id; uint32_t (*lv_txt_get_encoded_length)(const char *) = lv_txt_utf8_get_length; #elif LV_TXT_ENC == LV_TXT_ENC_ASCII uint8_t (*lv_txt_encoded_size)(const char *) = lv_txt_iso8859_1_size; @@ -65,7 +65,7 @@ uint32_t (*lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_iso8859_ uint32_t (*lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_iso8859_1_next; uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_iso8859_1_prev; uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_byte_id; -uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_char_id; +uint32_t (*lv_txt_encoded_get_char_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_char_id; uint32_t (*lv_txt_get_encoded_length)(const char *) = lv_txt_iso8859_1_get_length; #endif @@ -186,6 +186,9 @@ static uint16_t lv_txt_get_next_word(const char * txt, const lv_font_t * font, /*Handle the recolor command*/ if((flag & LV_TXT_FLAG_RECOLOR) != 0) { if(lv_txt_is_cmd(&cmd_state, letter) != false) { + i = i_next; + i_next = i_next_next; + letter = letter_next; continue; /*Skip the letter is it is part of a command*/ } } @@ -263,6 +266,9 @@ static uint16_t lv_txt_get_next_word(const char * txt, const lv_font_t * font, /** * Get the next line of text. Check line length and break chars too. + * + * A line of txt includes the \n character. + * * @param txt a '\0' terminated string * @param font pointer to a font * @param letter_space letter space @@ -292,18 +298,13 @@ uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font, i += advance; - if(txt[i] == '\n') break; - } + if(txt[0] == '\n') break; + + if(txt[i] == '\n'){ + i++; /* Include the following newline in the current line */ + break; + } - /* If this is the last of the string, make sure pointer is at NULL-terminator. - * This catches the case, for example of a string ending in "\n" */ - if(txt[i] != '\0'){ - uint32_t i_next = i; - int tmp; - uint32_t letter_next = lv_txt_encoded_next(txt, &i_next); /*Gets current character*/ - tmp = i_next; - letter_next = lv_txt_encoded_next(txt, &i_next); /*Gets subsequent character*/ - if(letter_next == '\0') i = tmp; } /*Always step at least one to avoid infinite loops*/ diff --git a/src/lv_misc/lv_txt.h b/src/lv_misc/lv_txt.h index 6fc6e4a63..865e2c97c 100644 --- a/src/lv_misc/lv_txt.h +++ b/src/lv_misc/lv_txt.h @@ -188,7 +188,7 @@ extern uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t); * @param byte_id byte index * @return character index of the letter at 'byte_id'th position */ -extern uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t); +extern uint32_t (*lv_txt_encoded_get_char_id)(const char *, uint32_t); /** * Get the number of characters (and NOT bytes) in a string. diff --git a/src/lv_objx/lv_btnm.c b/src/lv_objx/lv_btnm.c index c06419098..32869007a 100644 --- a/src/lv_objx/lv_btnm.c +++ b/src/lv_objx/lv_btnm.c @@ -739,7 +739,7 @@ static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mo area_tmp.x2 = area_tmp.x1 + txt_size.x; area_tmp.y2 = area_tmp.y1 + txt_size.y; - lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL, -1, -1, NULL, lv_obj_get_base_dir(btnm)); + lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL, NULL, NULL, lv_obj_get_base_dir(btnm)); } } diff --git a/src/lv_objx/lv_calendar.c b/src/lv_objx/lv_calendar.c index cbeca7c54..3f04e3371 100644 --- a/src/lv_objx/lv_calendar.c +++ b/src/lv_objx/lv_calendar.c @@ -670,6 +670,9 @@ static lv_coord_t get_day_names_height(lv_obj_t * calendar) static void draw_header(lv_obj_t * calendar, const lv_area_t * mask) { lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + + lv_bidi_dir_t bidi_dir = lv_obj_get_base_dir(calendar); + lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); lv_area_t header_area; @@ -687,19 +690,19 @@ static void draw_header(lv_obj_t * calendar, const lv_area_t * mask) txt_buf[5] = '\0'; strcpy(&txt_buf[5], get_month_name(calendar, ext->showed_date.month)); header_area.y1 += ext->style_header->body.padding.top; - lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL, -1, -1, NULL, lv_obj_get_base_dir(calendar)); + lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL, NULL, NULL, bidi_dir); /*Add the left arrow*/ const lv_style_t * arrow_style = ext->btn_pressing < 0 ? ext->style_header_pr : ext->style_header; header_area.x1 += ext->style_header->body.padding.left; - lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL, -1, -1, NULL, lv_obj_get_base_dir(calendar)); + lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL, NULL, NULL, bidi_dir); /*Add the right arrow*/ arrow_style = ext->btn_pressing > 0 ? ext->style_header_pr : ext->style_header; header_area.x1 = header_area.x2 - ext->style_header->body.padding.right - lv_txt_get_width(LV_SYMBOL_RIGHT, strlen(LV_SYMBOL_RIGHT), arrow_style->text.font, arrow_style->text.line_space, LV_TXT_FLAG_NONE); - lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL, -1, -1, NULL, lv_obj_get_base_dir(calendar)); + lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL, NULL, NULL, bidi_dir); } /** @@ -710,6 +713,7 @@ static void draw_header(lv_obj_t * calendar, const lv_area_t * mask) static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask) { lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + lv_bidi_dir_t bidi_dir = lv_obj_get_base_dir(calendar); lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); lv_coord_t l_pad = ext->style_day_names->body.padding.left; @@ -724,7 +728,7 @@ static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask) label_area.x1 = calendar->coords.x1 + (w * i) / 7 + l_pad; label_area.x2 = label_area.x1 + box_w - 1; lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, - NULL, -1, -1, NULL, lv_obj_get_base_dir(calendar)); + NULL, NULL, NULL, bidi_dir); } } @@ -736,6 +740,7 @@ static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask) static void draw_days(lv_obj_t * calendar, const lv_area_t * mask) { lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + lv_bidi_dir_t bidi_dir = lv_obj_get_base_dir(calendar); const lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG); lv_area_t label_area; lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); @@ -853,7 +858,7 @@ static void draw_days(lv_obj_t * calendar, const lv_area_t * mask) /*Write the day's number*/ lv_utils_num_to_str(day_cnt, buf); - lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, -1, -1, NULL, lv_obj_get_base_dir(calendar)); + lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, NULL, NULL, bidi_dir); /*Go to the next day*/ day_cnt++; diff --git a/src/lv_objx/lv_canvas.c b/src/lv_objx/lv_canvas.c index 0e02cd0fa..58cd65cb2 100644 --- a/src/lv_objx/lv_canvas.c +++ b/src/lv_objx/lv_canvas.c @@ -585,8 +585,7 @@ void lv_canvas_draw_text(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord default: flag = LV_TXT_FLAG_NONE; break; } - lv_draw_label(&coords, &mask, style, LV_OPA_COVER, txt, flag, NULL, LV_LABEL_TEXT_SEL_OFF, LV_LABEL_TEXT_SEL_OFF, - NULL, lv_obj_get_base_dir(canvas)); + lv_draw_label(&coords, &mask, style, LV_OPA_COVER, txt, flag, NULL, NULL, NULL, lv_obj_get_base_dir(canvas)); lv_refr_set_disp_refreshing(refr_ori); } diff --git a/src/lv_objx/lv_cb.h b/src/lv_objx/lv_cb.h index 5fa598b7b..a6b1e74ad 100644 --- a/src/lv_objx/lv_cb.h +++ b/src/lv_objx/lv_cb.h @@ -149,7 +149,7 @@ static inline bool lv_cb_is_checked(const lv_obj_t * cb) */ static inline bool lv_cb_is_inactive(const lv_obj_t * cb) { - return lv_btn_get_state(cb) == LV_BTN_STATE_INA ? false : true; + return lv_btn_get_state(cb) == LV_BTN_STATE_INA ? true :false; } /** diff --git a/src/lv_objx/lv_chart.c b/src/lv_objx/lv_chart.c index e82d00f7f..5da3b848d 100644 --- a/src/lv_objx/lv_chart.c +++ b/src/lv_objx/lv_chart.c @@ -1379,7 +1379,7 @@ static void lv_chart_draw_y_ticks(lv_obj_t * chart, const lv_area_t * mask, uint a.x2 = p2.x + size.x + LV_CHART_AXIS_TO_LABEL_DISTANCE; } - lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, -1, -1, NULL, lv_obj_get_base_dir(chart)); + lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, NULL, NULL, lv_obj_get_base_dir(chart)); } } @@ -1465,7 +1465,7 @@ static void lv_chart_draw_x_ticks(lv_obj_t * chart, const lv_area_t * mask) /* set the area at some distance of the major tick len under of the tick */ lv_area_t a = {(p2.x - size.x / 2), (p2.y + LV_CHART_AXIS_TO_LABEL_DISTANCE), (p2.x + size.x / 2), (p2.y + size.y + LV_CHART_AXIS_TO_LABEL_DISTANCE)}; - lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, -1, -1, NULL, lv_obj_get_base_dir(chart)); + lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, NULL, NULL, lv_obj_get_base_dir(chart)); } } } diff --git a/src/lv_objx/lv_cpicker.c b/src/lv_objx/lv_cpicker.c new file mode 100644 index 000000000..73197d374 --- /dev/null +++ b/src/lv_objx/lv_cpicker.c @@ -0,0 +1,1067 @@ +/** + * @file lv_cpicker.c + * + * From @AloyseTech and @paulpv. + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_cpicker.h" +#if LV_USE_CPICKER != 0 + +#include "../lv_core/lv_debug.h" +#include "../lv_draw/lv_draw_arc.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_core/lv_indev.h" +#include "../lv_core/lv_refr.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LV_OBJX_NAME "lv_cpicker" + +#ifndef LV_CPICKER_DEF_TYPE +#define LV_CPICKER_DEF_TYPE LV_CPICKER_TYPE_DISC +#endif + +#ifndef LV_CPICKER_DEF_HUE +#define LV_CPICKER_DEF_HUE 0 +#endif + +#ifndef LV_CPICKER_DEF_SATURATION +#define LV_CPICKER_DEF_SATURATION 100 +#endif + +#ifndef LV_CPICKER_DEF_VALUE +#define LV_CPICKER_DEF_VALUE 100 +#endif + +#ifndef LV_CPICKER_DEF_HSV +#define LV_CPICKER_DEF_HSV ((lv_color_hsv_t){LV_CPICKER_DEF_HUE, LV_CPICKER_DEF_SATURATION, LV_CPICKER_DEF_VALUE}) +#endif + +#ifndef LV_CPICKER_DEF_QF /*quantization factor*/ +#define LV_CPICKER_DEF_QF 3 +#endif + +#define TRI_OFFSET 2 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_cpicker_design(lv_obj_t * cpicker, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param); + +static void draw_rect_grad(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale); +static void draw_disc_grad(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale); +static void draw_indic(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale); +static void invalidate_indic(lv_obj_t * cpicker); +static lv_area_t get_indic_area(lv_obj_t * cpicker); + +static void next_color_mode(lv_obj_t * cpicker); +static lv_res_t double_click_reset(lv_obj_t * cpicker); +static void refr_indic_pos(lv_obj_t * cpicker); +static lv_color_t angle_to_mode_color(lv_obj_t * cpicker, uint16_t angle); +static uint16_t get_angle(lv_obj_t * cpicker); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_cb_t ancestor_signal; +static lv_design_cb_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a color_picker object + * @param par pointer to an object, it will be the parent of the new color_picker + * @param copy pointer to a color_picker object, if not NULL then the new object will be copied from it + * @return pointer to the created color_picker + */ +lv_obj_t * lv_cpicker_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("color_picker create started"); + + lv_obj_t * new_cpicker = lv_obj_create(par, copy); + LV_ASSERT_MEM(new_cpicker); + if(new_cpicker == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_cpicker); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_cpicker); + + /*Allocate the extended data*/ + lv_cpicker_ext_t * ext = lv_obj_allocate_ext_attr(new_cpicker, sizeof(lv_cpicker_ext_t)); + LV_ASSERT_MEM(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->type = LV_CPICKER_DEF_TYPE; + ext->hsv = LV_CPICKER_DEF_HSV; + ext->indic.style = &lv_style_plain; + ext->indic.colored = 0; + ext->color_mode = LV_CPICKER_COLOR_MODE_HUE; + ext->color_mode_fixed = 0; + ext->preview = 0; + ext->last_click_time = 0; + ext->last_change_time = 0; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_cb(new_cpicker, lv_cpicker_signal); + lv_obj_set_design_cb(new_cpicker, lv_cpicker_design); + + /*If no copy do the basic initialization*/ + if(copy == NULL) { + lv_obj_set_size(new_cpicker, LV_DPI * 2, LV_DPI * 2); + lv_obj_set_protect(new_cpicker, LV_PROTECT_PRESS_LOST); + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_cpicker_set_style(new_cpicker, LV_CPICKER_STYLE_MAIN, th->style.bg); + } else { + lv_cpicker_set_style(new_cpicker, LV_CPICKER_STYLE_MAIN, &lv_style_plain); + } + } + /*Copy 'copy'*/ + else { + lv_cpicker_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->type = copy_ext->type; + ext->color_mode = copy_ext->color_mode; + ext->color_mode_fixed = copy_ext->color_mode_fixed; + ext->preview = copy_ext->preview; + ext->hsv = copy_ext->hsv; + ext->indic.colored = copy_ext->indic.colored; + ext->indic.style = copy_ext->indic.style; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_cpicker); + } + refr_indic_pos(new_cpicker); + + LV_LOG_INFO("color_picker created"); + + return new_cpicker; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new type for a cpicker + * @param cpicker pointer to a cpicker object + * @param type new type of the cpicker (from 'lv_cpicker_type_t' enum) + */ +void lv_cpicker_set_type(lv_obj_t * cpicker, lv_cpicker_type_t type) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + if(ext->type == type) return; + + ext->type = type; + lv_obj_refresh_ext_draw_pad(cpicker); + refr_indic_pos(cpicker); + + lv_obj_invalidate(cpicker); +} + +/** + * Set a style of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_cpicker_set_style(lv_obj_t * cpicker, lv_cpicker_style_t type, lv_style_t * style) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + switch(type) { + case LV_CPICKER_STYLE_MAIN: + lv_obj_set_style(cpicker, style); + break; + case LV_CPICKER_STYLE_INDICATOR: + ext->indic.style = style; + lv_obj_invalidate(cpicker); + break; + } +} + +/** + * Set the current hue of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param hue current selected hue [0..360] + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_hue(lv_obj_t * cpicker, uint16_t hue) +{ + lv_color_hsv_t hsv = lv_cpicker_get_hsv(cpicker); + hsv.h = hue; + return lv_cpicker_set_hsv(cpicker, hsv); +} + +/** + * Set the current saturation of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param saturation current selected saturation [0..100] + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_saturation(lv_obj_t * cpicker, uint8_t saturation) +{ + lv_color_hsv_t hsv = lv_cpicker_get_hsv(cpicker); + hsv.s = saturation; + return lv_cpicker_set_hsv(cpicker, hsv); +} + +/** + * Set the current value of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param val current selected value [0..100] + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_value(lv_obj_t * cpicker, uint8_t val) +{ + lv_color_hsv_t hsv = lv_cpicker_get_hsv(cpicker); + hsv.v = val; + return lv_cpicker_set_hsv(cpicker, hsv); +} + +/** + * Set the current hsv of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param color current selected hsv + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_hsv(lv_obj_t * cpicker, lv_color_hsv_t hsv) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + if (hsv.h > 360) hsv.h %= 360; + if (hsv.s > 100) hsv.s = 100; + if (hsv.v > 100) hsv.v = 100; + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + if (ext->hsv.h == hsv.h && ext->hsv.s == hsv.s && ext->hsv.v == hsv.v) return false; + + ext->hsv = hsv; + + refr_indic_pos(cpicker); + + if (ext->preview && ext->type == LV_CPICKER_TYPE_DISC) { + lv_obj_invalidate(cpicker); + } + + return true; +} + +/** + * Set the current color of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param color current selected color + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_color(lv_obj_t * cpicker, lv_color_t color) +{ +#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0 + uint8_t green = (color.ch.green_h << 3) + color.ch.green_l; + return lv_cpicker_set_hsv(cpicker, lv_color_rgb_to_hsv(color.ch.red, green, color.ch.blue)); +#else + return lv_cpicker_set_hsv(cpicker, lv_color_rgb_to_hsv(color.ch.red, color.ch.green, color.ch.blue)); +#endif +} + +/** + * Set the current color mode. + * @param cpicker pointer to colorpicker object + * @param mode color mode (hue/sat/val) + */ +void lv_cpicker_set_color_mode(lv_obj_t * cpicker, lv_cpicker_color_mode_t mode) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + ext->color_mode = mode; + refr_indic_pos(cpicker); + lv_obj_invalidate(cpicker); +} + +/** + * Set if the color mode is changed on long press on center + * @param cpicker pointer to colorpicker object + * @param fixed color mode cannot be changed on long press + */ +void lv_cpicker_set_color_mode_fixed(lv_obj_t * cpicker, bool fixed) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + ext->color_mode_fixed = fixed; +} + +/** + * Make the indicator to be colored to the current color + * @param cpicker pointer to colorpicker object + * @param en true: color the indicator; false: not color the indicator + */ +void lv_cpicker_set_indic_colored(lv_obj_t * cpicker, bool en) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + ext->indic.colored = en ? 1 : 0; + invalidate_indic(cpicker); +} + +/** + * Add a color preview in the middle of the DISC type color picker + * @param cpicker pointer to colorpicker object + * @param en true: enable preview; false: disable preview + */ +void lv_cpicker_set_preview(lv_obj_t * cpicker, bool en) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + ext->preview = en ? 1 : 0; + lv_obj_invalidate(cpicker); +} +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current color mode. + * @param cpicker pointer to colorpicker object + * @return color mode (hue/sat/val) + */ +lv_cpicker_color_mode_t lv_cpicker_get_color_mode(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->color_mode; +} + +/** + * Get if the color mode is changed on long press on center + * @param cpicker pointer to colorpicker object + * @return mode cannot be changed on long press + */ +bool lv_cpicker_get_color_mode_fixed(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->color_mode_fixed; +} + +/** + * Get style of a color_picker. + * @param cpicker pointer to color_picker object + * @param type which style should be get + * @return style pointer to the style + */ +const lv_style_t * lv_cpicker_get_style(const lv_obj_t * cpicker, lv_cpicker_style_t type) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + switch(type) { + case LV_CPICKER_STYLE_MAIN: + return lv_obj_get_style(cpicker); + case LV_CPICKER_STYLE_INDICATOR: + return ext->indic.style; + default: + return NULL; + } + + /*To avoid warning*/ + return NULL; +} + +/** + * Get the current selected hue of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return hue current selected hue + */ +uint16_t lv_cpicker_get_hue(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->hsv.h; +} + +/** + * Get the current selected saturation of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected saturation + */ +uint8_t lv_cpicker_get_saturation(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->hsv.s; +} + +/** + * Get the current selected hue of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected value + */ +uint8_t lv_cpicker_get_value(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->hsv.v; +} + +/** + * Get the current selected hsv of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected hsv + */ +lv_color_hsv_t lv_cpicker_get_hsv(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->hsv; +} + +/** + * Get the current selected color of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return color current selected color + */ +lv_color_t lv_cpicker_get_color(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return lv_color_hsv_to_rgb(ext->hsv.h, ext->hsv.s, ext->hsv.v); +} + +/** + * Whether the indicator is colored to the current color or not + * @param cpicker pointer to colorpicker object + * @return true: color the indicator; false: not color the indicator + */ +bool lv_cpicker_get_indic_colored(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->indic.colored ? true : false; +} + +/** + * Whether the preview is enabled or not + * @param cpicker pointer to colorpicker object + * @return en true: preview is enabled; false: preview is disabled + */ +bool lv_cpicker_get_preview(lv_obj_t * cpicker) +{ + LV_ASSERT_OBJ(cpicker, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + return ext->preview ? true : false; +} + + +/*===================== + * Other functions + *====================*/ + +/********************** + * STATIC FUNCTIONS + **********************/ + + +/** + * Handle the drawing related tasks of the color_picker + * @param cpicker pointer to an 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 + * @return true/false, depends on 'mode' + */ +static bool lv_cpicker_design(lv_obj_t * cpicker, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + lv_opa_t opa_scale = lv_obj_get_opa_scale(cpicker); + + if(ext->type == LV_CPICKER_TYPE_DISC) { + draw_disc_grad(cpicker, mask, opa_scale); + } else if(ext->type == LV_CPICKER_TYPE_RECT) { + draw_rect_grad(cpicker, mask, opa_scale); + } + + draw_indic(cpicker, mask, opa_scale); + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + } + + return true; +} + +static void draw_disc_grad(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + int16_t start_angle = 0; /*Default*/ + int16_t end_angle = 360 - LV_CPICKER_DEF_QF; /*Default*/ + + lv_coord_t w = lv_obj_get_width(cpicker); + lv_coord_t h = lv_obj_get_height(cpicker); + lv_coord_t cx = cpicker->coords.x1 + w / 2; + lv_coord_t cy = cpicker->coords.y1 + h / 2; + lv_coord_t r = w / 2; + + /*if the mask does not include the center of the object + * redrawing all the wheel is not necessary; + * only a given angular range + */ + lv_point_t center = {cx, cy}; + if(!lv_area_is_point_on(mask, ¢er)) + { + /*get angle from center of object to each corners of the area*/ + int16_t dr, ur, ul, dl; + dr = lv_atan2(mask->x2 - cx, mask->y2 - cy); + ur = lv_atan2(mask->x2 - cx, mask->y1 - cy); + ul = lv_atan2(mask->x1 - cx, mask->y1 - cy); + dl = lv_atan2(mask->x1 - cx, mask->y2 - cy); + + /*check area position from object axis*/ + bool left = (mask->x2 < cx && mask->x1 < cx) ? true : false; + bool onYaxis = (mask->x2 > cx && mask->x1 < cx) ? true : false; + bool right = (mask->x2 > cx && mask->x1 > cx) ? true : false; + bool top = (mask->y2 < cy && mask->y1 < cy) ? true : false; + bool onXaxis = (mask->y2 > cy && mask->y1 < cy) ? true : false; + bool bottom = (mask->y2 > cy && mask->y1 > cy) ? true : false; + + /*store angular range*/ + if(right && bottom) { + start_angle = dl; + end_angle = ur; + } else if(right && onXaxis) { + start_angle = dl; + end_angle = ul; + } else if(right && top) { + start_angle = dr; + end_angle = ul; + } else if(onYaxis && top) { + start_angle = dr; + end_angle = dl; + } else if(left && top) { + start_angle = ur; + end_angle = dl; + } else if(left && onXaxis) { + start_angle = ur; + end_angle = dr; + } else if(left && bottom) { + start_angle = ul; + end_angle = dr; + } else if(onYaxis && bottom) { + start_angle = ul; + end_angle = ur; + } + + /*rollover angle*/ + if(start_angle > end_angle) end_angle += 360; + + /*round to QF factor*/ + start_angle = (start_angle/LV_CPICKER_DEF_QF) * LV_CPICKER_DEF_QF; + end_angle = (end_angle / LV_CPICKER_DEF_QF) * LV_CPICKER_DEF_QF; + + /*shift angle if necessary before adding offset*/ + if((start_angle - LV_CPICKER_DEF_QF) < 0) { + start_angle += 360; + end_angle += 360; + } + + /*ensure overlapping by adding offset*/ + start_angle -= LV_CPICKER_DEF_QF; + end_angle += LV_CPICKER_DEF_QF; + } + + lv_point_t triangle_points[3]; + lv_style_t style; + lv_style_copy(&style, &lv_style_plain); + for(uint16_t i = start_angle; i <= end_angle; i+= LV_CPICKER_DEF_QF) { + style.body.main_color = angle_to_mode_color(cpicker, i); + style.body.grad_color = style.body.main_color; + + triangle_points[0].x = cx; + triangle_points[0].y = cy; + + triangle_points[1].x = cx + (r * lv_trigo_sin(i) >> LV_TRIGO_SHIFT); + triangle_points[1].y = cy + (r * lv_trigo_sin(i + 90) >> LV_TRIGO_SHIFT); + + if(i == end_angle || i == (360 - LV_CPICKER_DEF_QF)) { + /*the last triangle is drawn without additional overlapping pixels*/ + triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF) >> LV_TRIGO_SHIFT); + triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + 90) >> LV_TRIGO_SHIFT); + } else { + triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET) >> LV_TRIGO_SHIFT); + triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET + 90) >> LV_TRIGO_SHIFT); + } + + lv_draw_triangle(triangle_points, mask, &style, LV_OPA_COVER); + } + + /*Mask out the center area*/ + const lv_style_t * style_main = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN); + lv_style_copy(&style, style_main); + style.body.radius = LV_RADIUS_CIRCLE; + lv_area_t area_mid; + lv_area_copy(&area_mid, &cpicker->coords); + lv_area_increment(&area_mid, -style_main->line.width); + + lv_draw_rect(&area_mid, mask, &style, opa_scale); + + if(ext->preview) { + lv_color_t color = lv_cpicker_get_color(cpicker); + style.body.main_color = color; + style.body.grad_color = color; + lv_area_increment(&area_mid, -style_main->line.width / 2); + + lv_draw_rect(&area_mid, mask, &style, opa_scale); + } +} + +static void draw_rect_grad(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale) +{ + lv_style_t style; + lv_style_copy(&style, lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN)); + + lv_area_t grad_area; + lv_obj_get_coords(cpicker, &grad_area); + + if(style.body.radius) { + lv_coord_t h = lv_obj_get_height(cpicker); + lv_coord_t r = style.body.radius; + if(r > h / 2) r = h / 2; + /*Make the gradient area smaller with a half circle on both ends*/ + grad_area.x1 += r; + grad_area.x2 -= r; + + /*Draw the left rounded end*/ + lv_area_t rounded_edge_area; + lv_obj_get_coords(cpicker, &rounded_edge_area); + rounded_edge_area.x2 = rounded_edge_area.x1 + 2 * r; + + style.body.main_color = angle_to_mode_color(cpicker, 0); + style.body.grad_color = style.body.main_color; + + lv_draw_rect(&rounded_edge_area, mask, &style, opa_scale); + + /*Draw the right rounded end*/ + lv_obj_get_coords(cpicker, &rounded_edge_area); + rounded_edge_area.x1 = rounded_edge_area.x2 - 2 * r; + + style.body.main_color = angle_to_mode_color(cpicker, 359); + style.body.grad_color = style.body.main_color; + + lv_draw_rect(&rounded_edge_area, mask, &style, opa_scale); + } + + lv_coord_t grad_w = lv_area_get_width(&grad_area); + uint16_t i_step = LV_MATH_MAX(LV_CPICKER_DEF_QF, 360 / grad_w); + style.body.radius = 0; + style.body.border.width = 0; + style.body.shadow.width = 0; + style.body.opa = LV_OPA_COVER; + + for(uint16_t i = 0; i < 360; i += i_step) { + style.body.main_color = angle_to_mode_color(cpicker, i); + style.body.grad_color = style.body.main_color; + + /*the following attribute might need changing between index to add border, shadow, radius etc*/ + lv_area_t rect_area; + + /*scale angle (hue/sat/val) to linear coordinate*/ + lv_coord_t xi = (i * grad_w) / 360; + + rect_area.x1 = LV_MATH_MIN(grad_area.x1 + xi, grad_area.x1 + grad_w - i_step); + rect_area.y1 = grad_area.y1; + rect_area.x2 = rect_area.x1 + i_step; + rect_area.y2 = grad_area.y2; + + lv_draw_rect(&rect_area, mask, &style, opa_scale); + } +} + +static void draw_indic(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + lv_style_t style_cir; + lv_style_copy(&style_cir, ext->indic.style); + style_cir.body.radius = LV_RADIUS_CIRCLE; + + if(ext->indic.colored) { + style_cir.body.main_color = lv_cpicker_get_color(cpicker); + style_cir.body.grad_color = style_cir.body.main_color; + } + + lv_area_t indic_area = get_indic_area(cpicker); + + lv_draw_rect(&indic_area, mask, &style_cir, opa_scale); +} + +static void invalidate_indic(lv_obj_t * cpicker) +{ + lv_area_t indic_area = get_indic_area(cpicker); + + lv_inv_area(lv_obj_get_disp(cpicker), &indic_area); +} + +static lv_area_t get_indic_area(lv_obj_t * cpicker) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + const lv_style_t * style_main = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN); + const lv_style_t * style_indic = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_INDICATOR); + + uint16_t r; + if(ext->type == LV_CPICKER_TYPE_DISC) r = style_main->line.width / 2; + else if(ext->type == LV_CPICKER_TYPE_RECT) { + lv_coord_t h = lv_obj_get_height(cpicker); + r = h / 2; + } + + lv_area_t indic_area; + indic_area.x1 = cpicker->coords.x1 + ext->indic.pos.x - r - style_indic->body.padding.left; + indic_area.y1 = cpicker->coords.y1 + ext->indic.pos.y - r - style_indic->body.padding.right; + indic_area.x2 = cpicker->coords.x1 + ext->indic.pos.x + r + style_indic->body.padding.top; + indic_area.y2 = cpicker->coords.y1 + ext->indic.pos.y + r + style_indic->body.padding.bottom; + + return indic_area; +} + +/** + * Signal function of the color_picker + * @param cpicker pointer to a color_picker 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_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param) +{ + /* Include the ancient signal function */ + lv_res_t res = ancestor_signal(cpicker, sign, param); + if(res != LV_RES_OK) return res; + if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) { + const lv_style_t * style_indic = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_INDICATOR); + lv_coord_t indic_pad = LV_MATH_MAX(style_indic->body.padding.left, style_indic->body.padding.right); + indic_pad = LV_MATH_MAX(indic_pad, style_indic->body.padding.top); + indic_pad = LV_MATH_MAX(indic_pad, style_indic->body.padding.bottom); + + if(ext->type == LV_CPICKER_TYPE_RECT) indic_pad += LV_MATH_MAX(indic_pad, lv_obj_get_height(cpicker) / 2); + + cpicker->ext_draw_pad = LV_MATH_MAX(cpicker->ext_draw_pad, indic_pad); + } else if(sign == LV_SIGNAL_CORD_CHG) { + /*Refresh extended draw area to make knob visible*/ + if(lv_obj_get_width(cpicker) != lv_area_get_width(param) || + lv_obj_get_height(cpicker) != lv_area_get_height(param)) { + lv_obj_refresh_ext_draw_pad(cpicker); + refr_indic_pos(cpicker); + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + /*Refresh extended draw area to make knob visible*/ + lv_obj_refresh_ext_draw_pad(cpicker); + refr_indic_pos(cpicker); + } + else if(sign == LV_SIGNAL_CONTROL) { + uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/ + if(c == LV_KEY_RIGHT || c == LV_KEY_UP) { + lv_color_hsv_t hsv_cur; + hsv_cur = ext->hsv; + + switch(ext->color_mode) { + case LV_CPICKER_COLOR_MODE_HUE: + hsv_cur.h = (ext->hsv.h + 1) % 360; + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + hsv_cur.s = (ext->hsv.s + 1) % 100; + break; + case LV_CPICKER_COLOR_MODE_VALUE: + hsv_cur.v = (ext->hsv.v + 1) % 100; + break; + } + + if (lv_cpicker_set_hsv(cpicker, hsv_cur)) { + res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } + else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) { + lv_color_hsv_t hsv_cur; + hsv_cur = ext->hsv; + + switch(ext->color_mode) { + case LV_CPICKER_COLOR_MODE_HUE: + hsv_cur.h = ext->hsv.h > 0?(ext->hsv.h - 1) : 360; + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + hsv_cur.s = ext->hsv.s > 0?(ext->hsv.s - 1) : 100; + break; + case LV_CPICKER_COLOR_MODE_VALUE: + hsv_cur.v = ext->hsv.v > 0?(ext->hsv.v - 1) : 100; + break; + } + + if (lv_cpicker_set_hsv(cpicker, hsv_cur)) { + res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } + } + else if(sign == LV_SIGNAL_PRESSED) { + ext->last_change_time = lv_tick_get(); + lv_indev_get_point(lv_indev_get_act(), &ext->last_press_point); + res = double_click_reset(cpicker); + if(res != LV_RES_OK) return res; + } else if(sign == LV_SIGNAL_PRESSING) { + lv_indev_t * indev = lv_indev_get_act(); + if(indev == NULL) return res; + + lv_point_t p; + lv_indev_get_point(indev, &p); + + if((LV_MATH_ABS(p.x - ext->last_press_point.x) > indev->driver.drag_limit / 2) || + (LV_MATH_ABS(p.y - ext->last_press_point.y) > indev->driver.drag_limit / 2)) { + ext->last_change_time = lv_tick_get(); + ext->last_press_point.x = p.x; + ext->last_press_point.y = p.y; + } + + p.x -= cpicker->coords.x1; + p.y -= cpicker->coords.y1; + + /*Ignore pressing in the inner area*/ + uint16_t w = lv_obj_get_width(cpicker); + + int16_t angle = 0; + + if(ext->type == LV_CPICKER_TYPE_RECT) { + /*If pressed long enough without change go to next color mode*/ + uint32_t diff = lv_tick_elaps(ext->last_change_time); + if(diff > indev->driver.long_press_time * 2 && !ext->color_mode_fixed) { + next_color_mode(cpicker); + lv_indev_wait_release(lv_indev_get_act()); + return res; + } + + angle = (p.x * 360) / w; + if(angle < 0) angle = 0; + if(angle >= 360) angle = 359; + + } else if(ext->type == LV_CPICKER_TYPE_DISC) { + const lv_style_t * style_main = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN); + lv_coord_t r_in = w / 2; + p.x -= r_in; + p.y -= r_in; + r_in -= style_main->line.width; + + if(r_in > LV_DPI / 2) { + r_in -= style_main->line.width; /* to let some sensitive space inside*/ + + if(r_in < LV_DPI / 2) r_in = LV_DPI / 2; + } + + /*If the inner area is being pressed, go to the next color mode on long press*/ + if(p.x * p.x + p.y * p.y < r_in * r_in) { + uint32_t diff = lv_tick_elaps(ext->last_change_time); + if(diff > indev->driver.long_press_time && !ext->color_mode_fixed) { + next_color_mode(cpicker); + lv_indev_wait_release(lv_indev_get_act()); + } + return res; + } + + angle = lv_atan2(p.x, p.y) % 360; + } + + lv_color_hsv_t hsv_cur; + hsv_cur = ext->hsv; + + switch(ext->color_mode) { + case LV_CPICKER_COLOR_MODE_HUE: + hsv_cur.h = angle; + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + hsv_cur.s = (angle * 100) / 360; + break; + case LV_CPICKER_COLOR_MODE_VALUE: + hsv_cur.v = (angle * 100) / 360; + break; + } + + if (lv_cpicker_set_hsv(cpicker, hsv_cur)) { + res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } + + return res; +} + +static void next_color_mode(lv_obj_t * cpicker) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + ext->color_mode = (ext->color_mode + 1) % 3; + refr_indic_pos(cpicker); + lv_obj_invalidate(cpicker); +} + +static void refr_indic_pos(lv_obj_t * cpicker) +{ + invalidate_indic(cpicker); + + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + lv_coord_t w = lv_obj_get_width(cpicker); + lv_coord_t h = lv_obj_get_height(cpicker); + + if(ext->type == LV_CPICKER_TYPE_RECT) { + lv_coord_t ind_pos = 0; + switch(ext->color_mode) { + case LV_CPICKER_COLOR_MODE_HUE: + ind_pos += (ext->hsv.h * w) / 360; + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + ind_pos += (ext->hsv.s * w) / 100; + break; + case LV_CPICKER_COLOR_MODE_VALUE: + ind_pos += (ext->hsv.v * w) / 100; + break; + } + + ext->indic.pos.x = ind_pos; + ext->indic.pos.y = h / 2; + } else if(ext->type == LV_CPICKER_TYPE_DISC) { + const lv_style_t * style_main = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN); + lv_coord_t r = w / 2 - style_main->line.width / 2; + uint16_t angle = get_angle(cpicker); + ext->indic.pos.x = (((int32_t)r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT); + ext->indic.pos.y = (((int32_t)r * lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT); + ext->indic.pos.x = ext->indic.pos.x + w / 2; + ext->indic.pos.y = ext->indic.pos.y + h / 2; + } + + invalidate_indic(cpicker); +} + +static lv_res_t double_click_reset(lv_obj_t * cpicker) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + lv_indev_t * indev = lv_indev_get_act(); + /*Double clicked? Use long press time as double click time out*/ + if(lv_tick_elaps(ext->last_click_time) < indev->driver.long_press_time) { + lv_color_hsv_t hsv_cur; + hsv_cur = ext->hsv; + + switch(ext->color_mode) { + case LV_CPICKER_COLOR_MODE_HUE: + hsv_cur.h = LV_CPICKER_DEF_HUE; + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + hsv_cur.s = LV_CPICKER_DEF_SATURATION; + break; + case LV_CPICKER_COLOR_MODE_VALUE: + hsv_cur.v = LV_CPICKER_DEF_VALUE; + break; + } + + if (lv_cpicker_set_hsv(cpicker, hsv_cur)) { + lv_res_t res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } + ext->last_click_time = lv_tick_get(); + + return LV_RES_OK; +} + +static lv_color_t angle_to_mode_color(lv_obj_t * cpicker, uint16_t angle) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + lv_color_t color; + switch(ext->color_mode) + { + default: + case LV_CPICKER_COLOR_MODE_HUE: + color = lv_color_hsv_to_rgb(angle % 360, ext->hsv.s, ext->hsv.v); + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + color = lv_color_hsv_to_rgb(ext->hsv.h, ((angle % 360) * 100) / 360, ext->hsv.v); + break; + case LV_CPICKER_COLOR_MODE_VALUE: + color = lv_color_hsv_to_rgb(ext->hsv.h, ext->hsv.s, ((angle % 360) * 100) / 360); + break; + } + return color; +} + +static uint16_t get_angle(lv_obj_t * cpicker) +{ + lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker); + uint16_t angle; + switch(ext->color_mode) + { + default: + case LV_CPICKER_COLOR_MODE_HUE: + angle = ext->hsv.h; + break; + case LV_CPICKER_COLOR_MODE_SATURATION: + angle = (ext->hsv.s * 360) / 100; + break; + case LV_CPICKER_COLOR_MODE_VALUE: + angle = (ext->hsv.v * 360) / 100 ; + break; + } + return angle; +} + +#endif /* LV_USE_CPICKER != 0 */ diff --git a/src/lv_objx/lv_cpicker.h b/src/lv_objx/lv_cpicker.h new file mode 100644 index 000000000..a9feee5ae --- /dev/null +++ b/src/lv_objx/lv_cpicker.h @@ -0,0 +1,263 @@ +/** + * @file lv_cpicker.h + * + */ + +#ifndef LV_CPICKER_H +#define LV_CPICKER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../../lv_conf.h" +#endif + +#if LV_USE_CPICKER != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +enum { + LV_CPICKER_TYPE_RECT, + LV_CPICKER_TYPE_DISC, +}; +typedef uint8_t lv_cpicker_type_t; + +enum { + LV_CPICKER_COLOR_MODE_HUE, + LV_CPICKER_COLOR_MODE_SATURATION, + LV_CPICKER_COLOR_MODE_VALUE +}; +typedef uint8_t lv_cpicker_color_mode_t; + + + +/*Data of colorpicker*/ +typedef struct { + lv_color_hsv_t hsv; + struct { + lv_style_t * style; + lv_point_t pos; + uint8_t colored :1; + + } indic; + uint32_t last_click_time; + uint32_t last_change_time; + lv_point_t last_press_point; + lv_cpicker_color_mode_t color_mode :2; + uint8_t color_mode_fixed :1; + lv_cpicker_type_t type :1; + uint8_t preview :1; +} lv_cpicker_ext_t; + +/*Styles*/ +enum { + LV_CPICKER_STYLE_MAIN, + LV_CPICKER_STYLE_INDICATOR, +}; +typedef uint8_t lv_cpicker_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a colorpicker objects + * @param par pointer to an object, it will be the parent of the new colorpicker + * @param copy pointer to a colorpicker object, if not NULL then the new object will be copied from it + * @return pointer to the created colorpicker + */ +lv_obj_t * lv_cpicker_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new type for a colorpicker + * @param cpicker pointer to a colorpicker object + * @param type new type of the colorpicker (from 'lv_cpicker_type_t' enum) + */ +void lv_cpicker_set_type(lv_obj_t * cpicker, lv_cpicker_type_t type); + +/** + * Set a style of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_cpicker_set_style(lv_obj_t * cpicker, lv_cpicker_style_t type, lv_style_t *style); + +/** + * Set the current hue of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param hue current selected hue [0..360] + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_hue(lv_obj_t * cpicker, uint16_t hue); + +/** + * Set the current saturation of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param saturation current selected saturation [0..100] + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_saturation(lv_obj_t * cpicker, uint8_t saturation); + +/** + * Set the current value of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param val current selected value [0..100] + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_value(lv_obj_t * cpicker, uint8_t val); + +/** + * Set the current hsv of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param hsv current selected hsv + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_hsv(lv_obj_t * cpicker, lv_color_hsv_t hsv); + +/** + * Set the current color of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param color current selected color + * @return true if changed, otherwise false + */ +bool lv_cpicker_set_color(lv_obj_t * cpicker, lv_color_t color); + +/** + * Set the current color mode. + * @param cpicker pointer to colorpicker object + * @param mode color mode (hue/sat/val) + */ +void lv_cpicker_set_color_mode(lv_obj_t * cpicker, lv_cpicker_color_mode_t mode); + +/** + * Set if the color mode is changed on long press on center + * @param cpicker pointer to colorpicker object + * @param fixed color mode cannot be changed on long press + */ +void lv_cpicker_set_color_mode_fixed(lv_obj_t * cpicker, bool fixed); + +/** + * Make the indicator to be colored to the current color + * @param cpicker pointer to colorpicker object + * @param en true: color the indicator; false: not color the indicator + */ +void lv_cpicker_set_indic_colored(lv_obj_t * cpicker, bool en); + +/** + * Add a color preview in the middle of the DISC type color picker + * @param cpicker pointer to colorpicker object + * @param en true: enable preview; false: disable preview + */ +void lv_cpicker_set_preview(lv_obj_t * cpicker, bool en); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current color mode. + * @param cpicker pointer to colorpicker object + * @return color mode (hue/sat/val) + */ +lv_cpicker_color_mode_t lv_cpicker_get_color_mode(lv_obj_t * cpicker); + +/** + * Get if the color mode is changed on long press on center + * @param cpicker pointer to colorpicker object + * @return mode cannot be changed on long press + */ +bool lv_cpicker_get_color_mode_fixed(lv_obj_t * cpicker); + +/** + * Get style of a colorpicker. + * @param cpicker pointer to colorpicker object + * @param type which style should be get + * @return pointer to the style + */ +const lv_style_t * lv_cpicker_get_style(const lv_obj_t * cpicker, lv_cpicker_style_t type); + +/** + * Get the current hue of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected hue + */ +uint16_t lv_cpicker_get_hue(lv_obj_t * cpicker); + +/** + * Get the current saturation of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected saturation + */ +uint8_t lv_cpicker_get_saturation(lv_obj_t * cpicker); + +/** + * Get the current hue of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected value + */ +uint8_t lv_cpicker_get_value(lv_obj_t * cpicker); + +/** + * Get the current selected hsv of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected hsv + */ +lv_color_hsv_t lv_cpicker_get_hsv(lv_obj_t * cpicker); + +/** + * Get the current selected color of a colorpicker. + * @param cpicker pointer to colorpicker object + * @return current selected color + */ +lv_color_t lv_cpicker_get_color(lv_obj_t * cpicker); + +/** + * Whether the indicator is colored to the current color or not + * @param cpicker pointer to colorpicker object + * @return true: color the indicator; false: not color the indicator + */ +bool lv_cpicker_get_indic_colored(lv_obj_t * cpicker); + +/** + * Whether the preview is enabled or not + * @param cpicker pointer to colorpicker object + * @return en true: preview is enabled; false: preview is disabled + */ +bool lv_cpicker_get_preview(lv_obj_t * cpicker); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_CPICKER*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_CPICKER_H*/ diff --git a/src/lv_objx/lv_ddlist.c b/src/lv_objx/lv_ddlist.c index 465649266..a0100a74a 100644 --- a/src/lv_objx/lv_ddlist.c +++ b/src/lv_objx/lv_ddlist.c @@ -609,7 +609,7 @@ static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_desig new_style.text.opa = sel_style->text.opa; lv_txt_flag_t flag = lv_ddlist_get_txt_flag(ddlist); lv_draw_label(&ext->label->coords, &mask_sel, &new_style, opa_scale, lv_label_get_text(ext->label), - flag, NULL, -1, -1, NULL, lv_obj_get_base_dir(ddlist)); + flag, NULL, NULL, NULL, lv_obj_get_base_dir(ddlist)); } } @@ -642,9 +642,9 @@ static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_desig bool area_ok; area_ok = lv_area_intersect(&mask_arrow, mask, &area_arrow); if(area_ok) { + /*Use a down arrow in ddlist, you can replace it with yourcustom symbol*/ lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale, LV_SYMBOL_DOWN, LV_TXT_FLAG_NONE, - NULL, -1, -1, NULL, lv_obj_get_base_dir(ddlist)); /*Use a down arrow in ddlist, you can replace it with your - custom symbol*/ + NULL, NULL, NULL, lv_obj_get_base_dir(ddlist)); } } } diff --git a/src/lv_objx/lv_gauge.c b/src/lv_objx/lv_gauge.c index 8c1ab78fe..7191d8320 100644 --- a/src/lv_objx/lv_gauge.c +++ b/src/lv_objx/lv_gauge.c @@ -389,7 +389,7 @@ static void lv_gauge_draw_scale(lv_obj_t * gauge, const lv_area_t * mask) label_cord.x2 = label_cord.x1 + label_size.x; label_cord.y2 = label_cord.y1 + label_size.y; - lv_draw_label(&label_cord, mask, style, opa_scale, scale_txt, LV_TXT_FLAG_NONE, NULL, -1, -1, NULL, lv_obj_get_base_dir(gauge)); + lv_draw_label(&label_cord, mask, style, opa_scale, scale_txt, LV_TXT_FLAG_NONE, NULL, NULL, NULL, lv_obj_get_base_dir(gauge)); } } /** diff --git a/src/lv_objx/lv_img.c b/src/lv_objx/lv_img.c index 30e994a13..c62ad0b44 100644 --- a/src/lv_objx/lv_img.c +++ b/src/lv_objx/lv_img.c @@ -384,7 +384,7 @@ static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode lv_style_t style_mod; lv_style_copy(&style_mod, style); style_mod.text.color = style->image.color; - lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL, -1, -1, NULL, lv_obj_get_base_dir(img)); + lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL, NULL, NULL, lv_obj_get_base_dir(img)); } else { /*Trigger the error handler of image drawer*/ LV_LOG_WARN("lv_img_design: image source type is unknown"); diff --git a/src/lv_objx/lv_imgbtn.c b/src/lv_objx/lv_imgbtn.c index a17fe8b50..69f97f450 100644 --- a/src/lv_objx/lv_imgbtn.c +++ b/src/lv_objx/lv_imgbtn.c @@ -304,23 +304,23 @@ static bool lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * mask, lv_desig #if LV_IMGBTN_TILED == 0 const void * src = ext->img_src[state]; if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) { - lv_draw_label(&imgbtn->coords, mask, style, opa_scale, src, LV_TXT_FLAG_NONE, NULL, LV_LABEL_TEXT_SEL_OFF, LV_LABEL_TEXT_SEL_OFF, NULL, lv_obj_get_base_dir(imgbtn)); + lv_draw_label(&imgbtn->coords, mask, style, opa_scale, src, LV_TXT_FLAG_NONE, NULL, NULL, NULL, lv_obj_get_base_dir(imgbtn)); } else { lv_draw_img(&imgbtn->coords, mask, src, style, opa_scale); } #else + const void * src; + src = ext->img_src_left[state]; if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) { LV_LOG_WARN("lv_imgbtn_design: SYMBOLS are not supported in tiled mode") - return; + return true; } - const void * src; lv_img_header_t header; lv_area_t coords; lv_coord_t left_w = 0; lv_coord_t right_w = 0; - src = ext->img_src_left[state]; if(src) { lv_img_decoder_get_info(src, &header); left_w = header.w; diff --git a/src/lv_objx/lv_kb.c b/src/lv_objx/lv_kb.c index 003bd4d33..1913316a2 100644 --- a/src/lv_objx/lv_kb.c +++ b/src/lv_objx/lv_kb.c @@ -396,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, LV_SYMBOL_NEW_LINE) == 0) + if(strcmp(txt, "Enter") == 0 || 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 46cc451c4..bd99c10c8 100644 --- a/src/lv_objx/lv_label.c +++ b/src/lv_objx/lv_label.c @@ -109,8 +109,8 @@ lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy) #endif #if LV_LABEL_TEXT_SEL - ext->txt_sel_start = LV_LABEL_TEXT_SEL_OFF; - ext->txt_sel_end = LV_LABEL_TEXT_SEL_OFF; + ext->txt_sel_start = LV_DRAW_LABEL_NO_TXT_SEL; + ext->txt_sel_end = LV_DRAW_LABEL_NO_TXT_SEL; #endif ext->dot.tmp_ptr = NULL; ext->dot_tmp_alloc = 0; @@ -578,7 +578,7 @@ uint16_t lv_label_get_anim_speed(const lv_obj_t * label) * 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) +void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t char_id, lv_point_t * pos) { LV_ASSERT_OBJ(label, LV_OBJX_NAME); LV_ASSERT_NULL(pos); @@ -606,12 +606,12 @@ void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t max_w = LV_COORD_MAX; } - index = lv_txt_encoded_get_byte_id(txt, index); + uint16_t byte_id = lv_txt_encoded_get_byte_id(txt, char_id); /*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') + if(byte_id < 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; @@ -619,26 +619,50 @@ void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t } /*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') { + if(byte_id > 0) { + if((txt[byte_id - 1] == '\n' || txt[byte_id - 1] == '\r') && txt[byte_id] == '\0') { y += letter_height + style->text.line_space; - line_start = index; + line_start = byte_id; } } - /*Calculate the x coordinate*/ - lv_coord_t x = lv_txt_get_width(&txt[line_start], index - line_start, font, style->text.letter_space, flag); + const char *bidi_txt; + uint16_t visual_byte_pos; +#if LV_USE_BIDI + /*Handle Bidi*/ + if(new_line_start == byte_id) { + visual_byte_pos = byte_id - line_start; + bidi_txt = &txt[line_start]; + } + else { + uint16_t line_char_id = lv_txt_encoded_get_char_id(&txt[line_start], byte_id - line_start); - if(index != line_start) x += style->text.letter_space; + bool is_rtl; + char *mutable_bidi_txt; + uint16_t visual_char_pos = lv_bidi_get_visual_pos(&txt[line_start], &mutable_bidi_txt, new_line_start - line_start, lv_obj_get_base_dir(label), line_char_id, &is_rtl); + bidi_txt = mutable_bidi_txt; + if (is_rtl) visual_char_pos++; + visual_byte_pos = lv_txt_encoded_get_byte_id(bidi_txt, visual_char_pos); + } +#else + bidi_txt = &txt[line_start]; + visual_byte_pos = byte_id - line_start; +#endif + + + /*Calculate the x coordinate*/ + lv_coord_t x = lv_txt_get_width(bidi_txt, visual_byte_pos, font, style->text.letter_space, flag); + + if(char_id != line_start) x += style->text.letter_space; 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); + line_w = lv_txt_get_width(bidi_txt, 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); + line_w = lv_txt_get_width(bidi_txt, new_line_start - line_start, font, style->text.letter_space, flag); x += lv_obj_get_width(label) - line_w; } @@ -668,6 +692,8 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) uint8_t letter_height = lv_font_get_line_height(font); lv_coord_t y = 0; lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + uint16_t logical_pos; + char *bidi_txt; if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR; if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND; @@ -685,43 +711,60 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) 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')*/ + if(pos->y <= y + letter_height) { + /*The line is found (stored in 'line_start')*/ + /* Include the NULL terminator in the last line */ + uint32_t tmp = new_line_start; + uint32_t letter; + letter = lv_txt_encoded_prev(txt, &tmp); + if(letter != '\n' && txt[new_line_start] == '\0' ) new_line_start++; + break; + } y += letter_height + style->text.line_space; line_start = new_line_start; } +#if LV_USE_BIDI + bidi_txt = lv_draw_get_buf(new_line_start - line_start + 1); + uint16_t txt_len = new_line_start - line_start; + if(bidi_txt[new_line_start] == '\0') txt_len--; + lv_bidi_process_paragraph(txt + line_start, bidi_txt, txt_len, lv_obj_get_base_dir(label), NULL, 0); +#else + bidi_txt = txt + line_start; +#endif + /*Calculate the x coordinate*/ lv_coord_t x = 0; 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); + line_w = lv_txt_get_width(bidi_txt, 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); + line_w = lv_txt_get_width(bidi_txt, 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; - uint32_t i = line_start; + uint32_t i = 0; uint32_t i_act = i; uint32_t letter; uint32_t letter_next; if(new_line_start > 0) { - while(i < new_line_start) { + while(i + line_start < new_line_start) { /* Get the current letter.*/ - letter = lv_txt_encoded_next(txt, &i); + letter = lv_txt_encoded_next(bidi_txt, &i); /*Get the next letter too for kerning*/ - letter_next = lv_txt_encoded_next(&txt[i], NULL); + letter_next = lv_txt_encoded_next(&bidi_txt[i], NULL); /*Handle the recolor command*/ if((flag & LV_TXT_FLAG_RECOLOR) != 0) { - if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) { + if(lv_txt_is_cmd(&cmd_state, bidi_txt[i]) != false) { continue; /*Skip the letter is it is part of a command*/ } } @@ -729,7 +772,7 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) x += lv_font_get_glyph_width(font, letter, letter_next); /*Finish if the x position or the last char of the line is reached*/ - if(pos->x < x || i == new_line_start) { + if(pos->x < x || i + line_start == new_line_start) { i = i_act; break; } @@ -738,7 +781,16 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) } } - return lv_encoded_get_char_id(txt, i); +#if LV_USE_BIDI + /*Handle Bidi*/ + bool is_rtl; + logical_pos = lv_bidi_get_logical_pos(&txt[line_start], NULL, txt_len, lv_obj_get_base_dir(label), lv_txt_encoded_get_char_id(bidi_txt, i), &is_rtl); + if (is_rtl) logical_pos++; +#else + logical_pos = lv_txt_encoded_get_char_id(bidi_txt, i); +#endif + + return logical_pos + lv_txt_encoded_get_char_id(txt, line_start); } /** @@ -799,10 +851,11 @@ bool lv_label_is_char_under_pos(const lv_obj_t * label, lv_point_t * pos) uint8_t letter_height = lv_font_get_line_height(font); lv_coord_t y = 0; lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + lv_label_align_t align = lv_label_get_align(label); 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(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) { @@ -822,11 +875,16 @@ bool lv_label_is_char_under_pos(const lv_obj_t * label, lv_point_t * pos) /*Calculate the x coordinate*/ lv_coord_t x = 0; lv_coord_t last_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; @@ -1004,8 +1062,11 @@ static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_ /*Just for compatibility*/ lv_draw_label_hint_t * hint = NULL; #endif - lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset, - lv_label_get_text_sel_start(label), lv_label_get_text_sel_end(label), hint, lv_obj_get_base_dir(label)); + lv_draw_label_txt_sel_t sel; + + sel.start = lv_label_get_text_sel_start(label); + sel.end = lv_label_get_text_sel_end(label); + lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset, &sel, hint, lv_obj_get_base_dir(label)); if(ext->long_mode == LV_LABEL_LONG_SROLL_CIRC) { @@ -1021,16 +1082,14 @@ static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_ lv_font_get_glyph_width(style->text.font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT; ofs.y = ext->offset.y; - lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ofs, - lv_label_get_text_sel_start(label), lv_label_get_text_sel_end(label), NULL, lv_obj_get_base_dir(label)); + lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ofs, &sel, NULL, lv_obj_get_base_dir(label)); } /*Draw the text again below the original to make an circular effect */ if(size.y > lv_obj_get_height(label)) { ofs.x = ext->offset.x; ofs.y = ext->offset.y + size.y + lv_font_get_line_height(style->text.font); - lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ofs, - lv_label_get_text_sel_start(label), lv_label_get_text_sel_end(label), NULL, lv_obj_get_base_dir(label)); + lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ofs, &sel, NULL, lv_obj_get_base_dir(label)); } } } diff --git a/src/lv_objx/lv_label.h b/src/lv_objx/lv_label.h index 6660c3c9d..faa21684f 100644 --- a/src/lv_objx/lv_label.h +++ b/src/lv_objx/lv_label.h @@ -33,7 +33,7 @@ extern "C" { *********************/ #define LV_LABEL_DOT_NUM 3 #define LV_LABEL_POS_LAST 0xFFFF -#define LV_LABEL_TEXT_SEL_OFF 0xFFFF +#define LV_LABEL_TEXT_SEL_OFF LV_DRAW_LABEL_NO_TXT_SEL LV_EXPORT_CONST_INT(LV_LABEL_DOT_NUM); LV_EXPORT_CONST_INT(LV_LABEL_POS_LAST); diff --git a/src/lv_objx/lv_objx.mk b/src/lv_objx/lv_objx.mk index 45b3e2ad3..fde2ac77f 100644 --- a/src/lv_objx/lv_objx.mk +++ b/src/lv_objx/lv_objx.mk @@ -1,6 +1,7 @@ CSRCS += lv_arc.c CSRCS += lv_bar.c CSRCS += lv_cb.c +CSRCS += lv_cpicker.c CSRCS += lv_ddlist.c CSRCS += lv_kb.c CSRCS += lv_line.c diff --git a/src/lv_objx/lv_roller.c b/src/lv_objx/lv_roller.c index 62c949db5..e098b18ba 100644 --- a/src/lv_objx/lv_roller.c +++ b/src/lv_objx/lv_roller.c @@ -397,7 +397,7 @@ static bool lv_roller_design(lv_obj_t * roller, const lv_area_t * mask, lv_desig new_style.text.color = sel_style->text.color; new_style.text.opa = sel_style->text.opa; lv_draw_label(&ext->ddlist.label->coords, &mask_sel, &new_style, opa_scale, - lv_label_get_text(ext->ddlist.label), txt_align, NULL, -1, -1, NULL, lv_obj_get_base_dir(ext->ddlist.label)); + lv_label_get_text(ext->ddlist.label), txt_align, NULL, NULL, NULL, lv_obj_get_base_dir(ext->ddlist.label)); } } diff --git a/src/lv_objx/lv_ta.c b/src/lv_objx/lv_ta.c index c8ffee521..4e3560de0 100644 --- a/src/lv_objx/lv_ta.c +++ b/src/lv_objx/lv_ta.c @@ -558,6 +558,9 @@ void lv_ta_set_placeholder_text(lv_obj_t * ta, const char * txt) lv_label_set_text(ext->placeholder, txt); + /*Refresh the placeholder's align*/ + lv_ta_set_text_align(ta, lv_label_get_align(ext->label)); + placeholder_update(ta); } @@ -773,12 +776,14 @@ void lv_ta_set_text_align(lv_obj_t * ta, lv_label_align_t align) lv_obj_t * label = lv_ta_get_label(ta); if(!ext->one_line) { lv_label_set_align(label, align); + if(ext->placeholder) lv_label_set_align(ext->placeholder, align); } else { /*Normal left align. Just let the text expand*/ if(align == LV_LABEL_ALIGN_LEFT) { lv_label_set_long_mode(label, LV_LABEL_LONG_EXPAND); lv_page_set_scrl_fit2(ta, LV_FIT_TIGHT, LV_FIT_FLOOD); lv_label_set_align(label, align); + if(ext->placeholder) lv_label_set_align(ext->placeholder, align); } /*Else use fix label width equal to the Text area width*/ @@ -786,6 +791,7 @@ void lv_ta_set_text_align(lv_obj_t * ta, lv_label_align_t align) lv_label_set_long_mode(label, LV_LABEL_LONG_CROP); lv_page_set_scrl_fit2(ta, LV_FIT_FLOOD, LV_FIT_FLOOD); lv_label_set_align(label, align); + if(ext->placeholder) lv_label_set_align(ext->placeholder, align); lv_obj_set_width(label, lv_page_get_fit_width(ta)); } @@ -1131,8 +1137,8 @@ bool lv_ta_text_is_selected(const lv_obj_t * ta) #if LV_LABEL_TEXT_SEL lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); - if((lv_label_get_text_sel_start(ext->label) == LV_LABEL_TEXT_SEL_OFF || - lv_label_get_text_sel_end(ext->label) == LV_LABEL_TEXT_SEL_OFF)) { + if((lv_label_get_text_sel_start(ext->label) == LV_DRAW_LABEL_NO_TXT_SEL || + lv_label_get_text_sel_end(ext->label) == LV_DRAW_LABEL_NO_TXT_SEL)) { return true; } else { return false; @@ -1203,10 +1209,10 @@ void lv_ta_clear_selection(lv_obj_t * ta) #if LV_LABEL_TEXT_SEL lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); - if(lv_label_get_text_sel_start(ext->label) != LV_LABEL_TEXT_SEL_OFF || - lv_label_get_text_sel_end(ext->label) != LV_LABEL_TEXT_SEL_OFF) { - lv_label_set_text_sel_start(ext->label, LV_LABEL_TEXT_SEL_OFF); - lv_label_set_text_sel_end(ext->label, LV_LABEL_TEXT_SEL_OFF); + if(lv_label_get_text_sel_start(ext->label) != LV_DRAW_LABEL_NO_TXT_SEL || + lv_label_get_text_sel_end(ext->label) != LV_DRAW_LABEL_NO_TXT_SEL) { + lv_label_set_text_sel_start(ext->label, LV_DRAW_LABEL_NO_TXT_SEL); + lv_label_set_text_sel_end(ext->label, LV_DRAW_LABEL_NO_TXT_SEL); } #else (void)ta; /*Unused*/ @@ -1385,8 +1391,7 @@ static bool lv_ta_scrollable_design(lv_obj_t * scrl, const lv_area_t * mask, lv_ cur_area.x1 += cur_style.body.padding.left; cur_area.y1 += cur_style.body.padding.top; - lv_draw_label(&cur_area, mask, &cur_style, opa_scale, letter_buf, LV_TXT_FLAG_NONE, 0, - LV_LABEL_TEXT_SEL_OFF, LV_LABEL_TEXT_SEL_OFF, NULL, lv_obj_get_base_dir(ta)); + lv_draw_label(&cur_area, mask, &cur_style, opa_scale, letter_buf, LV_TXT_FLAG_NONE, NULL, NULL, NULL, lv_obj_get_base_dir(ta)); } else if(ext->cursor.type == LV_CURSOR_OUTLINE) { cur_style.body.opa = LV_OPA_TRANSP; @@ -1878,13 +1883,13 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_ 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 = char_id_at_click; - ext->tmp_sel_end = LV_LABEL_TEXT_SEL_OFF; + ext->sel.start = char_id_at_click; + ext->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 = char_id_at_click; + ext->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); @@ -1897,22 +1902,22 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_ /*If the selected area has changed then update the real values and*/ /*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; - ext_label->txt_sel_end = ext->tmp_sel_start; + if(ext->sel.start > ext->sel.end) { + if(ext_label->txt_sel_start != ext->sel.end || ext_label->txt_sel_end != ext->sel.start) { + ext_label->txt_sel_start = ext->sel.end; + ext_label->txt_sel_end = ext->sel.start; lv_obj_invalidate(ta); } - } else if(ext->tmp_sel_start < ext->tmp_sel_end) { - if(ext_label->txt_sel_start != ext->tmp_sel_start || ext_label->txt_sel_end != ext->tmp_sel_end) { - ext_label->txt_sel_start = ext->tmp_sel_start; - ext_label->txt_sel_end = ext->tmp_sel_end; + } else if(ext->sel.start < ext->sel.end) { + if(ext_label->txt_sel_start != ext->sel.start || ext_label->txt_sel_end != ext->sel.end) { + ext_label->txt_sel_start = ext->sel.start; + ext_label->txt_sel_end = ext->sel.end; lv_obj_invalidate(ta); } } else { - if(ext_label->txt_sel_start != LV_LABEL_TEXT_SEL_OFF || ext_label->txt_sel_end != LV_LABEL_TEXT_SEL_OFF) { - ext_label->txt_sel_start = LV_LABEL_TEXT_SEL_OFF; - ext_label->txt_sel_end = LV_LABEL_TEXT_SEL_OFF; + if(ext_label->txt_sel_start != LV_DRAW_LABEL_NO_TXT_SEL || ext_label->txt_sel_end != LV_DRAW_LABEL_NO_TXT_SEL) { + ext_label->txt_sel_start = LV_DRAW_LABEL_NO_TXT_SEL; + ext_label->txt_sel_end = LV_DRAW_LABEL_NO_TXT_SEL; lv_obj_invalidate(ta); } } diff --git a/src/lv_objx/lv_ta.h b/src/lv_objx/lv_ta.h index ec2854e49..afbb94bc2 100644 --- a/src/lv_objx/lv_ta.h +++ b/src/lv_objx/lv_ta.h @@ -82,8 +82,7 @@ typedef struct uint8_t click_pos : 1; /*1: Enable positioning the cursor by clicking the text area*/ } cursor; #if LV_LABEL_TEXT_SEL - uint16_t tmp_sel_start; /*Temporary value*/ - uint16_t tmp_sel_end; /*Temporary value*/ + lv_draw_label_txt_sel_t sel; /*Temporary values for text selection*/ uint8_t text_sel_in_prog : 1; /*User is in process of selecting */ uint8_t text_sel_en : 1; /*Text can be selected on this text area*/ #endif diff --git a/src/lv_objx/lv_table.c b/src/lv_objx/lv_table.c index ef49559aa..79cdc777e 100644 --- a/src/lv_objx/lv_table.c +++ b/src/lv_objx/lv_table.c @@ -738,7 +738,7 @@ static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_ label_mask_ok = lv_area_intersect(&label_mask, mask, &cell_area); if(label_mask_ok) { lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1, - txt_flags, NULL, -1, -1, NULL, lv_obj_get_base_dir(table)); + txt_flags, NULL, NULL, NULL, lv_obj_get_base_dir(table)); } /*Draw lines after '\n's*/ lv_point_t p1;