diff --git a/README.md b/README.md index 8f71b522d..d07a5b680 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@

-
+

+ +

@@ -130,7 +132,7 @@ indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device* indev_drv.read_cb = my_touchpad_read; /*Set your driver function*/ lv_indev_drv_register(&indev_drv); /*Finally register the driver*/ -bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data) +bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data) { static lv_coord_t last_x = 0; static lv_coord_t last_y = 0; diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md index b8bb841db..31071e94f 100644 --- a/docs/CODING_STYLE.md +++ b/docs/CODING_STYLE.md @@ -1,6 +1,6 @@ ## File format -Use [lv_misc/lv_templ.c](https://github.com/littlevgl/lvgl/blob/master/lv_misc/lv_templ.c) and [lv_misc/lv_templ.h](https://github.com/littlevgl/lvgl/blob/master/lv_misc/lv_templ.h) +Use [lv_misc/lv_templ.c](https://github.com/littlevgl/lvgl/blob/master/src/lv_misc/lv_templ.c) and [lv_misc/lv_templ.h](https://github.com/littlevgl/lvgl/blob/master/src/lv_misc/lv_templ.h) ## Naming conventions * Words are separated by '_' diff --git a/library.json b/library.json index 82f9171a0..f634590a6 100644 --- a/library.json +++ b/library.json @@ -1,5 +1,6 @@ { "name": "lvgl", + "version": "6.0.2", "keywords": "graphics, gui, embedded, littlevgl", "description": "Graphics library to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint. It offers anti-aliasing, opacity, and animations using only one frame buffer.", "repository": diff --git a/lv_conf_template.h b/lv_conf_template.h index 08e9beb9e..8ee14a1c4 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -43,6 +43,9 @@ /*Images pixels with this color will not be drawn (with chroma keying)*/ #define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/ +/* Enable chroma keying for indexed images. */ +#define LV_INDEXED_CHROMA 1 + /* Enable anti-aliasing (lines, and radiuses will be smoothed) */ #define LV_ANTIALIAS 1 @@ -297,6 +300,23 @@ typedef void * lv_font_user_data_t; /*Can break (wrap) texts on these chars*/ #define LV_TXT_BREAK_CHARS " ,.;:-_" +/* If a character is at least this long, will break wherever "prettiest" */ +#define LV_TXT_LINE_BREAK_LONG_LEN 12 + +/* Minimum number of characters of a word to put on a line before a break */ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/* Minimum number of characters of a word to put on a line after a break */ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/*Change the built in (v)snprintf functions*/ +#define LV_SPRINTF_CUSTOM 0 +#if LV_SPRINTF_CUSTOM +# define LV_SPRINTF_INCLUDE +# define lv_snprintf snprintf +# define lv_vsnprintf vsnprintf +#endif /*LV_SPRINTF_CUSTOM*/ + /*=================== * LV_OBJ SETTINGS *==================*/ diff --git a/src/lv_conf_checker.h b/src/lv_conf_checker.h index ff7ac05df..9bc2b1a3b 100644 --- a/src/lv_conf_checker.h +++ b/src/lv_conf_checker.h @@ -50,6 +50,11 @@ #define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/ #endif +/* Enable chroma keying for indexed images. */ +#ifndef LV_INDEXED_CHROMA +#define LV_INDEXED_CHROMA 1 +#endif + /* Enable anti-aliasing (lines, and radiuses will be smoothed) */ #ifndef LV_ANTIALIAS #define LV_ANTIALIAS 1 @@ -412,6 +417,37 @@ #define LV_TXT_BREAK_CHARS " ,.;:-_" #endif +/* If a character is at least this long, will break wherever "prettiest" */ +#ifndef LV_TXT_LINE_BREAK_LONG_LEN +#define LV_TXT_LINE_BREAK_LONG_LEN 12 +#endif + +/* Minimum number of characters of a word to put on a line before a break */ +#ifndef LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 +#endif + +/* Minimum number of characters of a word to put on a line after a break */ +#ifndef LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 +#endif + +/*Change the built in (v)snprintf functions*/ +#ifndef LV_SPRINTF_CUSTOM +#define LV_SPRINTF_CUSTOM 0 +#endif +#if LV_SPRINTF_CUSTOM +#ifndef LV_SPRINTF_INCLUDE +# define LV_SPRINTF_INCLUDE +#endif +#ifndef lv_snprintf +# define lv_snprintf snprintf +#endif +#ifndef lv_vsnprintf +# define lv_vsnprintf vsnprintf +#endif +#endif /*LV_SPRINTF_CUSTOM*/ + /*=================== * LV_OBJ SETTINGS *==================*/ diff --git a/src/lv_core/lv_indev.c b/src/lv_core/lv_indev.c index 86e343a60..6ec59bdac 100644 --- a/src/lv_core/lv_indev.c +++ b/src/lv_core/lv_indev.c @@ -182,7 +182,7 @@ void lv_indev_enable(lv_indev_t * indev, bool en) { if(!indev) return; - indev->proc.disabled = en ? 1 : 0; + indev->proc.disabled = en ? 0 : 1; } /** @@ -687,12 +687,14 @@ static void indev_proc_press(lv_indev_proc_t * proc) if(proc->wait_until_release != 0) return; lv_disp_t * disp = indev_act->driver.disp; + bool new_obj_searched = false; /*If there is no last object then search*/ if(indev_obj_act == NULL) { indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_sys(disp)); if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_top(disp)); if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_scr_act(disp)); + new_obj_searched = true; } /*If there is last object but it is not dragged and not protected also search*/ else if(proc->types.pointer.drag_in_prog == 0 && @@ -700,14 +702,21 @@ static void indev_proc_press(lv_indev_proc_t * proc) indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_sys(disp)); if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_top(disp)); if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_scr_act(disp)); + new_obj_searched = true; } /*If a dragable or a protected object was the last then keep it*/ else { } + /*The last object might have drag throw. Stop it manually*/ + if(new_obj_searched && proc->types.pointer.last_obj) { + proc->types.pointer.drag_throw_vect.x = 0; + proc->types.pointer.drag_throw_vect.y = 0; + indev_drag_throw(proc); + } + /*If a new object was found reset some variables and send a pressed signal*/ if(indev_obj_act != proc->types.pointer.act_obj) { - proc->types.pointer.last_point.x = proc->types.pointer.act_point.x; proc->types.pointer.last_point.y = proc->types.pointer.act_point.y; @@ -720,6 +729,7 @@ static void indev_proc_press(lv_indev_proc_t * proc) if(indev_reset_check(proc)) return; lv_event_send(last_obj, LV_EVENT_PRESS_LOST, NULL); if(indev_reset_check(proc)) return; + } proc->types.pointer.act_obj = indev_obj_act; /*Save the pressed object*/ @@ -1108,9 +1118,6 @@ static void indev_drag(lv_indev_proc_t * state) lv_obj_set_y(drag_obj, act_y + state->types.pointer.vect.y); } - - - /*If the object didn't moved then clear the invalidated areas*/ if(drag_obj->coords.x1 == prev_x && drag_obj->coords.y1 == prev_y) { // state->types.pointer.drag_in_prog = 0; diff --git a/src/lv_core/lv_obj.c b/src/lv_core/lv_obj.c index d95e18896..e8a3d8794 100644 --- a/src/lv_core/lv_obj.c +++ b/src/lv_core/lv_obj.c @@ -320,7 +320,7 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy) new_obj->realign.auto_realign = copy->realign.auto_realign; #endif - /*Only copy the `event_cb`. `signal_cb` and `design_cb` will be copied the the derived + /*Only copy the `event_cb`. `signal_cb` and `design_cb` will be copied in the derived * object type (e.g. `lv_btn`)*/ new_obj->event_cb = copy->event_cb; diff --git a/src/lv_core/lv_style.h b/src/lv_core/lv_style.h index 551443e7d..d99df4205 100644 --- a/src/lv_core/lv_style.h +++ b/src/lv_core/lv_style.h @@ -288,6 +288,18 @@ extern lv_style_t lv_style_btn_ina; * MACROS **********************/ +/** + * Create and initialize a `static` style + * Example: + * LV_STYLE_CREATE(my_style, &lv_style_plain); + * is equivalent to + * static lv_style_t my_style; + * lv_style_copy(my_style, &lv_style_plain); + * + * If the style to copy is `NULL` `lv_style_plain` will be used. + */ +#define LV_STYLE_CREATE(name, copy_p) static lv_style_t name; lv_style_copy(&name, copy_p == NULL ? &lv_style_plain : copy_p); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/lv_draw/lv_draw.c b/src/lv_draw/lv_draw.c index c264a5a46..1789a491e 100644 --- a/src/lv_draw/lv_draw.c +++ b/src/lv_draw/lv_draw.c @@ -99,17 +99,14 @@ void lv_draw_buf_release(void * p) void lv_draw_buf_free_all(void) { uint8_t i; - uint32_t s = 0; for(i = 0; i < LV_DRAW_BUF_MAX_NUM; i++) { if(_lv_draw_buf[i].p) { - s+= _lv_draw_buf[i].size; lv_mem_free(_lv_draw_buf[i].p); _lv_draw_buf[i].p = NULL; _lv_draw_buf[i].used = 0; _lv_draw_buf[i].size = 0; } } - if(s) printf("draf_buf free %d bytes\n", s); } /********************** diff --git a/src/lv_draw/lv_draw_img.c b/src/lv_draw/lv_draw_img.c index 3e58ccc59..0d3c4f55e 100644 --- a/src/lv_draw/lv_draw_img.c +++ b/src/lv_draw/lv_draw_img.c @@ -11,6 +11,7 @@ #include "../lv_hal/lv_hal_disp.h" #include "../lv_misc/lv_log.h" #include "../lv_core/lv_refr.h" +#include "../lv_misc/lv_mem.h" /********************* * DEFINES @@ -406,10 +407,14 @@ bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf) switch(cf) { case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: case LV_IMG_CF_RAW_CHROMA_KEYED: +#if LV_INDEXED_CHROMA case LV_IMG_CF_INDEXED_1BIT: case LV_IMG_CF_INDEXED_2BIT: case LV_IMG_CF_INDEXED_4BIT: - case LV_IMG_CF_INDEXED_8BIT: is_chroma_keyed = true; break; + case LV_IMG_CF_INDEXED_8BIT: +#endif + is_chroma_keyed = true; break; + default: is_chroma_keyed = false; break; } @@ -428,6 +433,10 @@ bool lv_img_color_format_has_alpha(lv_img_cf_t cf) switch(cf) { case LV_IMG_CF_TRUE_COLOR_ALPHA: case LV_IMG_CF_RAW_ALPHA: + case LV_IMG_CF_INDEXED_1BIT: + case LV_IMG_CF_INDEXED_2BIT: + case LV_IMG_CF_INDEXED_4BIT: + case LV_IMG_CF_INDEXED_8BIT: case LV_IMG_CF_ALPHA_1BIT: case LV_IMG_CF_ALPHA_2BIT: case LV_IMG_CF_ALPHA_4BIT: @@ -469,6 +478,66 @@ lv_img_src_t lv_img_src_get_type(const void * src) return img_src_type; } +lv_img_dsc_t *lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) +{ + /* Allocate image descriptor */ + lv_img_dsc_t *dsc = lv_mem_alloc(sizeof(lv_img_dsc_t)); + if(dsc == NULL) + return NULL; + + memset(dsc, 0, sizeof(lv_img_dsc_t)); + + /* Get image data size */ + dsc->data_size = lv_img_buf_get_img_size(w, h, cf); + if(dsc->data_size == 0) { + lv_mem_free(dsc); + return NULL; + } + + /* Allocate raw buffer */ + dsc->data = lv_mem_alloc(dsc->data_size); + if(dsc->data == NULL) { + lv_mem_free(dsc); + return NULL; + } + memset((uint8_t *)dsc->data, 0, dsc->data_size); + + /* Fill in header */ + dsc->header.always_zero = 0; + dsc->header.w = w; + dsc->header.h = h; + dsc->header.cf = cf; + return dsc; +} + +void lv_img_buf_free(lv_img_dsc_t *dsc) +{ + if(dsc != NULL) { + if(dsc->data != NULL) + lv_mem_free(dsc->data); + + lv_mem_free(dsc); + } +} + +uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) +{ + switch(cf) { + case LV_IMG_CF_TRUE_COLOR: return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h); + case LV_IMG_CF_TRUE_COLOR_ALPHA: return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h); + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h); + case LV_IMG_CF_ALPHA_1BIT: return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h); + case LV_IMG_CF_ALPHA_2BIT: return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h); + case LV_IMG_CF_ALPHA_4BIT: return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h); + case LV_IMG_CF_ALPHA_8BIT: return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h); + case LV_IMG_CF_INDEXED_1BIT: return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h); + case LV_IMG_CF_INDEXED_2BIT: return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h); + case LV_IMG_CF_INDEXED_4BIT: return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h); + case LV_IMG_CF_INDEXED_8BIT: return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h); + default: return 0; + } +} + /********************** * STATIC FUNCTIONS **********************/ @@ -510,7 +579,7 @@ static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mas else { lv_coord_t width = lv_area_get_width(&mask_com); - uint8_t * buf = lv_draw_buf_get(lv_area_get_width(&mask_com) * ((LV_COLOR_DEPTH >> 3) + 1)); /*+1 because of the possible alpha byte*/ + uint8_t * buf = lv_draw_buf_get(lv_area_get_width(&mask_com) * LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/ lv_area_t line; lv_area_copy(&line, &mask_com); diff --git a/src/lv_draw/lv_draw_img.h b/src/lv_draw/lv_draw_img.h index 61c604805..794dd79e6 100644 --- a/src/lv_draw/lv_draw_img.h +++ b/src/lv_draw/lv_draw_img.h @@ -20,6 +20,26 @@ extern "C" { * DEFINES *********************/ +/********************** + * MACROS + **********************/ + +#define LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h) +#define LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h) +#define LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h) + +/*+ 1: to be sure no fractional row*/ +#define LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h)) +#define LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h)) +#define LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h)) +#define LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h)) + +/*4 * X: for palette*/ +#define LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2) +#define LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4) +#define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16) +#define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256) + /********************** * TYPEDEFS **********************/ @@ -120,9 +140,30 @@ bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf); */ bool lv_img_color_format_has_alpha(lv_img_cf_t cf); -/********************** - * MACROS - **********************/ +/** + * Allocate an image buffer in RAM + * @param w width of image + * @param h height of image + * @param cf a color format (`LV_IMG_CF_...`) + * @return an allocated image, or NULL on failure + */ +lv_img_dsc_t *lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf); + +/** + * Free an allocated image buffer + * @param dsc image buffer to free + */ +void lv_img_buf_free(lv_img_dsc_t *dsc); + +/** + * Get the memory consumption of a raw bitmap, given color format and dimensions. + * @param w width + * @param h height + * @param cf color format + * @return size in bytes + */ +uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf); + #ifdef __cplusplus } /* extern "C" */ diff --git a/src/lv_draw/lv_draw_label.c b/src/lv_draw/lv_draw_label.c index 12a8626c6..93b6a2516 100644 --- a/src/lv_draw/lv_draw_label.c +++ b/src/lv_draw/lv_draw_label.c @@ -306,6 +306,8 @@ static void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area uint8_t bitmask_init; uint8_t bitmask; + if(g.bpp == 3) g.bpp = 4; + switch(g.bpp) { case 1: bpp_opa_table = bpp1_opa_table; diff --git a/src/lv_draw/lv_draw_rect.c b/src/lv_draw/lv_draw_rect.c index 7d6bacf9c..99e2607d6 100644 --- a/src/lv_draw/lv_draw_rect.c +++ b/src/lv_draw/lv_draw_rect.c @@ -112,12 +112,11 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s /*Most simple case: just a plain rectangle*/ if(simple_mode && rout == 0 && style->body.main_color.full == style->body.grad_color.full) { lv_blend_fill(clip, coords, - style->body.main_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, style->body.opa, + style->body.main_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.blend_mode); } /*More complex case: there is a radius, gradient or mask.*/ else { - lv_draw_mask_param_t mask_rout_param; if(rout > 0) { lv_draw_mask_radius_init(&mask_rout_param, coords, rout, false); @@ -184,7 +183,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s fill_area2.y2 = fill_area.y2; lv_blend_fill(clip, &fill_area2, - grad_color, mask_buf, mask_res, style->body.opa, style->body.blend_mode); + grad_color, mask_buf, mask_res, opa, style->body.blend_mode); /*Central part*/ @@ -192,7 +191,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s fill_area2.x2 = coords->x2 - rout; lv_blend_fill(clip, &fill_area2, - grad_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, style->body.opa, style->body.blend_mode); + grad_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.blend_mode); fill_area2.x1 = coords->x2 - rout + 1; fill_area2.x2 = coords->x2; @@ -200,7 +199,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s lv_coord_t mask_ofs = (coords->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); if(mask_ofs < 0) mask_ofs = 0; lv_blend_fill(clip, &fill_area2, - grad_color, mask_buf + mask_ofs, mask_res, style->body.opa, style->body.blend_mode); + grad_color, mask_buf + mask_ofs, mask_res, opa, style->body.blend_mode); } else { if(grad_map == NULL) { lv_blend_fill(clip, &fill_area, @@ -270,14 +269,14 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s fill_area2.x2 = coords->x1 + rout - 1; lv_blend_fill(clip, &fill_area2, - style->body.border.color, mask_buf, mask_res, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, mask_buf, mask_res, opa, style->body.border.blend_mode); if(fill_area2.y2 < coords->y1 + style->body.border.width) { fill_area2.x1 = coords->x1 + rout; fill_area2.x2 = coords->x2 - rout; lv_blend_fill(clip, &fill_area2, - style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.border.blend_mode); } fill_area2.x1 = coords->x2 - rout + 1; @@ -286,7 +285,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s lv_coord_t mask_ofs = (coords->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); if(mask_ofs < 0) mask_ofs = 0; lv_blend_fill(clip, &fill_area2, - style->body.border.color, mask_buf + mask_ofs, mask_res, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, mask_buf + mask_ofs, mask_res, opa, style->body.border.blend_mode); fill_area.y1++; fill_area.y2++; @@ -309,7 +308,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s fill_area2.y2 = fill_area.y2; lv_blend_fill(clip, &fill_area2, - style->body.border.color, mask_buf, mask_res, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, mask_buf, mask_res, opa, style->body.border.blend_mode); if(fill_area2.y2 > coords->y2 - style->body.border.width ) { @@ -317,7 +316,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s fill_area2.x2 = coords->x2 - rout; lv_blend_fill(clip, &fill_area2, - style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.border.blend_mode); } fill_area2.x1 = coords->x2 - rout + 1; fill_area2.x2 = coords->x2; @@ -325,7 +324,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s lv_coord_t mask_ofs = (coords->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); if(mask_ofs < 0) mask_ofs = 0; lv_blend_fill(clip, &fill_area2, - style->body.border.color, mask_buf + mask_ofs, mask_res, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, mask_buf + mask_ofs, mask_res, opa, style->body.border.blend_mode); fill_area.y1++; @@ -340,14 +339,14 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s fill_area.x1 = coords->x1; fill_area.x2 = coords->x1 + border_width - 1; lv_blend_fill(clip, &fill_area, - style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.border.blend_mode); /*Draw the right vertical border*/ fill_area.x1 = coords->x2 - border_width + 1; fill_area.x2 = coords->x2; lv_blend_fill(clip, &fill_area, - style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.border.blend_mode); } /*Process line by line if there is other mask too*/ else { @@ -360,7 +359,7 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); lv_blend_fill( clip, &fill_area, - style->body.border.color, mask_buf, mask_res, style->body.border.opa, style->body.border.blend_mode); + style->body.border.color, mask_buf, mask_res, opa, style->body.border.blend_mode); fill_area.y1++; fill_area.y2++; @@ -496,7 +495,7 @@ static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, const lv_coord_t ver_mid_dist = (a.y1 + corner_size) - (sh_area.y1 + lv_area_get_height(&sh_area) / 2); lv_coord_t ver_mid_corr = 0; - if(ver_mid_dist < 0) ver_mid_dist = 0; + if(ver_mid_dist <= 0) ver_mid_dist = 0; else { if(lv_area_get_height(&sh_area) & 0x1) ver_mid_corr = 1; } @@ -548,7 +547,7 @@ static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, const va.y1 = sh_area.y1 + corner_size; va.y2 = sh_area.y2 - corner_size; - if(va.y1 < va.y2) { + if(va.y1 <= va.y2) { for(x = a.x1; x < a.x2; x++) { if(x > coords->x2) { lv_opa_t opa_tmp = sh_buf_tmp[x - a.x1 + first_px]; @@ -602,7 +601,6 @@ static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, const } sh_buf_tmp = sh_buf ; - for(y = 0; y < corner_size - ver_mid_dist + ver_mid_corr; y++) { memcpy(mask_buf, sh_buf_tmp, corner_size); mask_res = lv_draw_mask_apply(mask_buf + first_px, a.x1, a.y1, lv_area_get_width(&a)); @@ -647,13 +645,15 @@ static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, const va.y1 = sh_area.y1 + corner_size; va.y2 = sh_area.y2 - corner_size; - for(x = a.x1; x < coords->x1; x++) { - lv_opa_t opa_tmp = sh_buf_tmp[x - a.x1 + first_px]; - if(opa_tmp != LV_OPA_COVER || opa != LV_OPA_COVER) opa_tmp = (opa * opa_tmp) >> 8; - lv_blend_fill(clip, &va, - style->body.shadow.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa_tmp, style->body.shadow.blend_mode); - va.x1++; - va.x2++; + if(va.y1 <= va.y2) { + for(x = a.x1; x < coords->x1; x++) { + lv_opa_t opa_tmp = sh_buf_tmp[x - a.x1 + first_px]; + if(opa_tmp != LV_OPA_COVER || opa != LV_OPA_COVER) opa_tmp = (opa * opa_tmp) >> 8; + lv_blend_fill(clip, &va, + style->body.shadow.color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa_tmp, style->body.shadow.blend_mode); + va.x1++; + va.x2++; + } } } else { diff --git a/src/lv_draw/lv_img_decoder.c b/src/lv_draw/lv_img_decoder.c index 82c231c2c..7b67262cf 100644 --- a/src/lv_draw/lv_img_decoder.c +++ b/src/lv_draw/lv_img_decoder.c @@ -31,6 +31,7 @@ typedef struct lv_fs_file_t * f; #endif lv_color_t * palette; + lv_opa_t * opa; } lv_img_decoder_built_in_data_t; /********************** @@ -375,7 +376,8 @@ lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder lv_img_decoder_built_in_data_t * user_data = dsc->user_data; user_data->palette = lv_mem_alloc(palette_size * sizeof(lv_color_t)); - if(user_data->palette == NULL) { + user_data->opa = lv_mem_alloc(palette_size * sizeof(lv_opa_t)); + if(user_data->palette == NULL || user_data->opa == NULL) { LV_LOG_ERROR("img_decoder_built_in_open: out of memory"); #if LV_USE_FILESYSTEM lv_mem_assert(user_data->f); @@ -386,7 +388,13 @@ lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder /*Read the palette from file*/ #if LV_USE_FILESYSTEM lv_fs_seek(user_data->f, 4); /*Skip the header*/ - lv_fs_read(user_data->f, user_data->palette, palette_size * sizeof(lv_color_t), NULL); + lv_color32_t cur_color; + uint32_t i; + for(i = 0; i < palette_size; i++) { + lv_fs_read(user_data->f, &cur_color, sizeof(lv_color32_t), NULL); + user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue); + user_data->opa[i] = cur_color.ch.alpha; + } #else LV_LOG_WARN("Image built-in decoder can read the palette because LV_USE_FILESYSTEM = 0"); return LV_RES_INV; @@ -398,6 +406,7 @@ lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder uint32_t i; for(i = 0; i < palette_size; i++) { user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue); + user_data->opa[i] = palette_p[i].ch.alpha; } } @@ -700,13 +709,24 @@ static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, #endif } - uint8_t byte_act = 0; uint8_t val_act; lv_coord_t i; - lv_color_t * cbuf = (lv_color_t *)buf; for(i = 0; i < len; i++) { - val_act = (data_tmp[byte_act] & (mask << pos)) >> pos; - cbuf[i] = user_data->palette[val_act]; + val_act = (*data_tmp & (mask << pos)) >> pos; + + lv_color_t color = user_data->palette[val_act]; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full; +#elif LV_COLOR_DEPTH == 16 + /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/ + buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF; + buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF; +#elif LV_COLOR_DEPTH == 32 + *((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full; +#else +#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h" +#endif + buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act]; pos -= px_size; if(pos < 0) { diff --git a/src/lv_font/lv_font_fmt_txt.c b/src/lv_font/lv_font_fmt_txt.c index d491da1f8..59eb3afc1 100644 --- a/src/lv_font/lv_font_fmt_txt.c +++ b/src/lv_font/lv_font_fmt_txt.c @@ -8,9 +8,11 @@ *********************/ #include "lv_font.h" #include "lv_font_fmt_txt.h" +#include "../lv_draw/lv_draw.h" #include "../lv_misc/lv_types.h" #include "../lv_misc/lv_log.h" #include "../lv_misc/lv_utils.h" +#include "../lv_misc/lv_mem.h" /********************* * DEFINES @@ -19,6 +21,11 @@ /********************** * TYPEDEFS **********************/ +typedef enum { + RLE_STATE_SINGLE = 0, + RLE_STATE_REPEATE, + RLE_STATE_COUNTER, +}rle_state_t; /********************** * STATIC PROTOTYPES @@ -29,10 +36,25 @@ static int32_t unicode_list_compare(const void * ref, const void * element); static int32_t kern_pair_8_compare(const void * ref, const void * element); static int32_t kern_pair_16_compare(const void * ref, const void * element); +static void decompress(const uint8_t * in, uint8_t * out, lv_coord_t w, lv_coord_t h, uint8_t bpp); +static void decompress_line(uint8_t * out, lv_coord_t w); +static uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len); +static void bits_write(uint8_t * out, uint32_t bit_pos, uint8_t val, uint8_t len); +static void rle_init(const uint8_t * in, uint8_t bpp); +static uint8_t rle_next(void); + + /********************** * STATIC VARIABLES **********************/ +static uint32_t rle_rdp; +static const uint8_t * rle_in; +static uint8_t rle_bpp; +static uint8_t rle_prev_v; +static uint8_t rle_cnt; +static rle_state_t rle_state; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -59,7 +81,32 @@ const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t unic const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid]; - if(gdsc) return &fdsc->glyph_bitmap[gdsc->bitmap_index]; + if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) { + if(gdsc) return &fdsc->glyph_bitmap[gdsc->bitmap_index]; + } + /*Handle compressed bitmap*/ + else + { + static uint8_t * buf = NULL; + + uint32_t gsize = gdsc->box_w * gdsc->box_h; + uint32_t buf_size = gsize; + switch(fdsc->bpp) { + case 1: buf_size = gsize >> 3; break; + case 2: buf_size = gsize >> 2; break; + case 3: buf_size = gsize >> 1; break; + case 4: buf_size = gsize >> 1; break; + } + + if(lv_mem_get_size(buf) < buf_size) { + buf = lv_mem_realloc(buf, buf_size); + lv_mem_assert(buf); + if(buf == NULL) return NULL; + } + + decompress(&fdsc->glyph_bitmap[gdsc->bitmap_index], buf, gdsc->box_w , gdsc->box_h, fdsc->bpp); + return buf; + } /*If not returned earlier then the letter is not found in this font*/ return NULL; @@ -238,6 +285,178 @@ static int32_t kern_pair_16_compare(const void * ref, const void * element) else return (int32_t) ref16_p[1] - element16_p[1]; } +/** + * The compress a glyph's bitmap + * @param in the compressed bitmap + * @param out buffer to store the result + * @param px_num number of pixels in the glyph (width * height) + * @param bpp bit per pixel (bpp = 3 will be converted to bpp = 4) + */ +static void decompress(const uint8_t * in, uint8_t * out, lv_coord_t w, lv_coord_t h, uint8_t bpp) +{ + uint32_t wrp = 0; + uint8_t wr_size = bpp; + if(bpp == 3) wr_size = 4; + + rle_init(in, bpp); + + uint8_t * line_buf1 = lv_draw_buf_get(w); + uint8_t * line_buf2 = lv_draw_buf_get(w); + + decompress_line(line_buf1, w); + + lv_coord_t y; + lv_coord_t x; + for(x = 0; x < w; x++) { + bits_write(out,wrp, line_buf1[x], bpp); + wrp += wr_size; + } + + for(y = 1; y < h; y++) { + decompress_line(line_buf2, w); + + for(x = 0; x < w; x++) { + line_buf1[x] = line_buf2[x] ^ line_buf1[x]; + bits_write(out,wrp, line_buf1[x], bpp); + wrp += wr_size; + } + } + + lv_draw_buf_release(line_buf1); + lv_draw_buf_release(line_buf2); +} + +/** + * Decompress one line. Store one pixel per byte + * @param out output buffer + * @param w width of the line in pixel count + */ +static void decompress_line(uint8_t * out, lv_coord_t w) +{ + lv_coord_t i; + for(i = 0; i < w; i++) { + out[i] = rle_next(); + } +} + +/** + * Read bits from an input buffer. The read can cross byte boundary. + * @param in the input buffer to read from. + * @param bit_pos index of teh first bit to read. + * @param len number of bits to read (must be <= 8). + * @return the read bits + */ +static uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len) +{ + uint8_t res = 0; + uint32_t byte_pos = bit_pos >> 3; + bit_pos = bit_pos & 0x7; + uint8_t bit_mask = (uint16_t)((uint16_t) 1 << len) - 1; + uint16_t in16 = (in[byte_pos] << 8) + in[byte_pos + 1]; + + res = (in16 >> (16 - bit_pos - len)) & bit_mask; + return res; +} + +/** + * Write `val` data to `bit_pos` position of `out`. The write can NOT cross byte boundary. + * @param out buffer where to write + * @param bit_pos bit index to write + * @param val value to write + * @param len length of bits to write from `val`. (Counted from the LSB). + * @note `len == 3` will be converted to `len = 4` and `val` will be upscaled too + */ +static void bits_write(uint8_t * out, uint32_t bit_pos, uint8_t val, uint8_t len) +{ + if(len == 3) { + len = 4; + switch(val) { + case 0: val = 0; break; + case 1: val = 2; break; + case 2: val = 4; break; + case 3: val = 6; break; + case 4: val = 9; break; + case 5: val = 11; break; + case 6: val = 13; break; + case 7: val = 15; break; + } + } + + uint16_t byte_pos = bit_pos >> 3; + bit_pos = bit_pos & 0x7; + bit_pos = 8 - bit_pos - len; + + uint8_t bit_mask = (uint16_t)((uint16_t) 1 << len) - 1; + out[byte_pos] &= ((~bit_mask) << bit_pos); + out[byte_pos] |= (val << bit_pos); +} + +static void rle_init(const uint8_t * in, uint8_t bpp) +{ + rle_in = in; + rle_bpp = bpp; + rle_state = RLE_STATE_SINGLE; + rle_rdp = 0; + rle_prev_v = 0; + rle_cnt = 0; +} + +static uint8_t rle_next(void) +{ + uint8_t v = 0; + uint8_t ret = 0; + + if(rle_state == RLE_STATE_SINGLE) { + ret = get_bits(rle_in, rle_rdp, rle_bpp); + if(rle_rdp != 0 && rle_prev_v == ret) { + rle_cnt = 0; + rle_state = RLE_STATE_REPEATE; + } + + rle_prev_v = ret; + rle_rdp += rle_bpp; + } + else if(rle_state == RLE_STATE_REPEATE) { + v = get_bits(rle_in, rle_rdp, 1); + rle_cnt++; + rle_rdp += 1; + if(v == 1) { + ret = rle_prev_v; + if(rle_cnt == 11) { + rle_cnt = get_bits(rle_in, rle_rdp, 6); + rle_rdp += 6; + if(rle_cnt != 0) { + rle_state = RLE_STATE_COUNTER; + } else { + ret = get_bits(rle_in, rle_rdp, rle_bpp); + rle_prev_v = ret; + rle_rdp += rle_bpp; + rle_state = RLE_STATE_SINGLE; + } + } + } else { + ret = get_bits(rle_in, rle_rdp, rle_bpp); + rle_prev_v = ret; + rle_rdp += rle_bpp; + rle_state = RLE_STATE_SINGLE; + } + + + } + else if(rle_state == RLE_STATE_COUNTER) { + ret = rle_prev_v; + rle_cnt--; + if(rle_cnt == 0) { + ret = get_bits(rle_in, rle_rdp, rle_bpp); + rle_prev_v = ret; + rle_rdp += rle_bpp; + rle_state = RLE_STATE_SINGLE; + } + } + + return ret; +} + /** Code Comparator. * * Compares the value of both input arguments. diff --git a/src/lv_font/lv_font_fmt_txt.h b/src/lv_font/lv_font_fmt_txt.h index 446a1067d..0fd41347b 100644 --- a/src/lv_font/lv_font_fmt_txt.h +++ b/src/lv_font/lv_font_fmt_txt.h @@ -180,7 +180,7 @@ typedef struct { /*Number of cmap tables*/ uint16_t cmap_num :10; - /*Bit per pixel: 1, 2, 4 or 8*/ + /*Bit per pixel: 1, 2, 3, 4*/ uint16_t bpp :3; /*Type of `kern_dsc`*/ diff --git a/src/lv_misc/lv_fs.h b/src/lv_misc/lv_fs.h index 5b86b8efd..f18242820 100644 --- a/src/lv_misc/lv_fs.h +++ b/src/lv_misc/lv_fs.h @@ -29,6 +29,7 @@ extern "C" { * DEFINES *********************/ #define LV_FS_MAX_FN_LENGTH 64 +#define LV_FS_MAX_PATH_LENGTH 256 /********************** * TYPEDEFS diff --git a/src/lv_misc/lv_math.c b/src/lv_misc/lv_math.c index d5ff06c24..6c17d94d2 100644 --- a/src/lv_misc/lv_math.c +++ b/src/lv_misc/lv_math.c @@ -26,7 +26,7 @@ /********************** * STATIC VARIABLES **********************/ -static int16_t sin0_90_table[] = { +static const int16_t sin0_90_table[] = { 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, 5690, 6252, 6813, 7371, 7927, 8481, 9032, 9580, 10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, 16383, 16876, 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964, diff --git a/src/lv_misc/lv_misc.mk b/src/lv_misc/lv_misc.mk index 41e4720f0..b9615c599 100644 --- a/src/lv_misc/lv_misc.mk +++ b/src/lv_misc/lv_misc.mk @@ -12,6 +12,7 @@ CSRCS += lv_log.c CSRCS += lv_gc.c CSRCS += lv_utils.c CSRCS += lv_async.c +CSRCS += lv_printf.c DEPPATH += --dep-path $(LVGL_DIR)/lvgl/src/lv_misc VPATH += :$(LVGL_DIR)/lvgl/src/lv_misc diff --git a/src/lv_misc/lv_printf.c b/src/lv_misc/lv_printf.c new file mode 100644 index 000000000..11a39a8ad --- /dev/null +++ b/src/lv_misc/lv_printf.c @@ -0,0 +1,861 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include "lv_printf.h" + +#if LV_SPRINTF_CUSTOM == 0 + +#include +#include + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int lv_snprintf(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int lv_vsnprintf(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +#endif /*LV_SPRINTF_CUSTOM*/ + diff --git a/src/lv_misc/lv_printf.h b/src/lv_misc/lv_printf.h new file mode 100644 index 000000000..b3b8598dd --- /dev/null +++ b/src/lv_misc/lv_printf.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _LV_PRINTF_H_ +#define _LV_PRINTF_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../../lv_conf.h" +#endif + +#if LV_SPRINTF_CUSTOM == 0 + +#include +#include + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +int lv_snprintf(char* buffer, size_t count, const char* format, ...); +int lv_vsnprintf(char* buffer, size_t count, const char* format, va_list va); + +#else +#include LV_SPRINTF_INCLUDE +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ diff --git a/src/lv_misc/lv_txt.c b/src/lv_misc/lv_txt.c index 8b35c7149..5632c5ad8 100644 --- a/src/lv_misc/lv_txt.c +++ b/src/lv_misc/lv_txt.c @@ -130,6 +130,137 @@ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * size_res->y -= line_space; } +/** + * Get the next word of text. A word is delimited by break characters. + * + * If the word cannot fit in the max_width space, obey LV_TXT_LINE_BREAK_LONG_* rules. + * + * If the next word cannot fit anything, return 0. + * + * If the first character is a break character, returns the next index. + * + * Example calls from lv_txt_get_next_line() assuming sufficent max_width and + * txt = "Test text\n" + * 0123456789 + * + * Calls would be as follows: + * 1. Return i=4, pointing at breakchar ' ', for the string "Test" + * 2. Return i=5, since i=4 was a breakchar. + * 3. Return i=9, pointing at breakchar '\n' + * 4. Parenting lv_txt_get_next_line() would detect subsequent '\0' + * + * @param txt a '\0' terminated string + * @param font pointer to a font + * @param letter_space letter space + * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks + * @param flags settings for the text from 'txt_flag_type' enum + * @param[out] word_w_ptr width (in pixels) of the parsed word. May be NULL. + * @return the index of the first char of the next word (in byte index not letter index. With UTF-8 they are different) + */ +static uint16_t lv_txt_get_next_word(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, + lv_txt_flag_t flag, uint32_t *word_w_ptr) +{ + if(txt == NULL || txt[0] == '\0') return 0; + if(font == NULL) return 0; + + if(flag & LV_TXT_FLAG_EXPAND) max_width = LV_COORD_MAX; + + uint32_t i = 0, i_next = 0, i_next_next = 0; /* Iterating index into txt */ + lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; + uint32_t letter = 0; /* Letter at i */ + uint32_t letter_next = 0; /* Letter at i_next */ + lv_coord_t letter_w; + lv_coord_t cur_w = 0; /* Pixel Width of transversed string */ + uint32_t word_len = 0; /* Number of characters in the transversed word */ + uint32_t break_index = NO_BREAK_FOUND; /* only used for "long" words */ + uint32_t break_letter_count = 0; /* Number of characters up to the long word break point */ + + letter = lv_txt_encoded_next(txt, &i_next); + i_next_next = i_next; + + while(txt[i] != '\0') { + letter_next = lv_txt_encoded_next(txt, &i_next_next); + word_len++; + + /*Handle the recolor command*/ + if((flag & LV_TXT_FLAG_RECOLOR) != 0) { + if(lv_txt_is_cmd(&cmd_state, letter) != false) { + continue; /*Skip the letter is it is part of a command*/ + } + } + + letter_w = lv_font_get_glyph_width(font, letter, letter_next); + cur_w += letter_w; + + + /* Test if this character fits within max_width */ + if( break_index == NO_BREAK_FOUND && cur_w > max_width) { + break_index = i; + if(break_index > 0) { /* zero is possible if first character doesn't fit in width */ + lv_txt_encoded_prev(txt, &break_index); + break_letter_count = word_len - 2; + } + else{ + break_letter_count = word_len - 1; + } + /* break_index is now pointing at the character that doesn't fit */ + } + + /*Check for new line chars and breakchars*/ + if(letter == '\n' || letter == '\r' || is_break_char(letter)) { + /* Update the output width on the first character if it fits. + * Must do this here incase first letter is a break character. */ + if(i == 0 && break_index == NO_BREAK_FOUND && word_w_ptr != NULL) *word_w_ptr = cur_w; + word_len--; + break; + } + + /* Update the output width */ + if( word_w_ptr != NULL && break_index == NO_BREAK_FOUND ) *word_w_ptr = cur_w; + + if(letter_w > 0) { + cur_w += letter_space; + } + + i = i_next; + i_next = i_next_next; + letter = letter_next; + } + + /* Entire Word fits in the provided space */ + if( break_index == NO_BREAK_FOUND ) { + if( word_len == 0 || (letter == '\r' && letter_next == '\n') ) i = i_next; + return i; + } + + /* Word doesn't fit in provided space, but isn't "long" */ + if(word_len < LV_TXT_LINE_BREAK_LONG_LEN) { + if(word_w_ptr != NULL) *word_w_ptr = 0; + return 0; + } + + /* Word is "long," but insufficient amounts can fit in provided space */ + if(break_letter_count < LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN) { + if(word_w_ptr != NULL) *word_w_ptr = 0; + return 0; + } + + /* Word is a "long", but letters may need to be better distributed */ + { + i = break_index; + int32_t n_move = LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN - (word_len - break_letter_count); + /* Move pointer "i" backwards */ + for(;n_move>0; n_move--){ + lv_txt_encoded_prev(txt, &i); + // todo: it would be appropriate to update the returned word width here + // However, in current usage, this doesn't impact anything. + } + } + + return i; +} + /** * Get the next line of text. Check line length and break chars too. * @param txt a '\0' terminated string @@ -139,75 +270,45 @@ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * * @param flags settings for the text from 'txt_flag_type' enum * @return the index of the first char of the new line (in byte index not letter index. With UTF-8 they are different) */ -uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font, lv_coord_t letter_space, lv_coord_t max_width, - lv_txt_flag_t flag) +uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, lv_txt_flag_t flag) { if(txt == NULL) return 0; if(font == NULL) return 0; if(flag & LV_TXT_FLAG_EXPAND) max_width = LV_COORD_MAX; - uint32_t i = 0; - uint32_t i_next = 0; - lv_coord_t cur_w = 0; - uint32_t last_break = NO_BREAK_FOUND; - lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; - uint32_t letter_w; - uint32_t letter = 0; - uint32_t letter_next = 0; + uint32_t i = 0; /* Iterating index into txt */ - letter_next = lv_txt_encoded_next(txt, &i_next); + while(txt[i] != '\0' && max_width > 0) { + uint32_t word_w = 0; + uint32_t advance = lv_txt_get_next_word(&txt[i], font, letter_space, max_width, flag, &word_w); + max_width -= word_w; - while(txt[i] != '\0') { - letter = letter_next; - i = i_next; - letter_next = lv_txt_encoded_next(txt, &i_next); - - /*Handle the recolor command*/ - if((flag & LV_TXT_FLAG_RECOLOR) != 0) { - if(lv_txt_is_cmd(&cmd_state, letter) != false) { - continue; /*Skip the letter is it is part of a command*/ - } + if( advance == 0 ){ + if(i == 0) lv_txt_encoded_next(txt, &i); // prevent inf loops + break; } - /*Check for new line chars*/ - if(letter == '\n' || letter == '\r') { - /*Return with the first letter of the next line*/ - if(letter == '\r' && letter_next == '\n') - return i_next; - else - return i; - } else { /*Check the actual length*/ - letter_w = lv_font_get_glyph_width(font, letter, letter_next); - cur_w += letter_w; + i += advance; - /*If the txt is too long then finish, this is the line end*/ - if(cur_w > max_width) { - /*If a break character was already found break there*/ - if(last_break != NO_BREAK_FOUND) { - i = last_break; - } else { - /* Now this character is out of the area so it will be first character of the next line*/ - /* But 'i' already points to the next character (because of lv_txt_utf8_next) step beck one*/ - lv_txt_encoded_prev(txt, &i); - } + if(txt[i] == '\n') break; + } - /* Do not let to return without doing nothing. - * Find at least one character (Avoid infinite loop )*/ - if(i == 0) lv_txt_encoded_next(txt, &i); + /* 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; + } - return i; - } - /*If this char still can fit to this line then check if - * txt can be broken here later */ - else if(is_break_char(letter)) { - last_break = i; /*Save the first char index after break*/ - } - } - - if(letter_w > 0) { - cur_w += letter_space; - } + /*Always step at least one to avoid infinite loops*/ + if(i == 0) { + lv_txt_encoded_next(txt, &i); } return i; diff --git a/src/lv_objx/lv_bar.c b/src/lv_objx/lv_bar.c index b9e75e262..88a6e3329 100644 --- a/src/lv_objx/lv_bar.c +++ b/src/lv_objx/lv_bar.c @@ -14,11 +14,13 @@ #include "../lv_draw/lv_draw.h" #include "../lv_themes/lv_theme.h" #include "../lv_misc/lv_anim.h" +#include "../lv_misc/lv_math.h" #include /********************* * DEFINES *********************/ +#define LV_BAR_SIZE_MIN 4 /*hor. pad and ver. pad cannot make the indicator smaller then this [px]*/ /********************** * TYPEDEFS @@ -30,6 +32,9 @@ static lv_design_res_t lv_bar_design(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode); static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param); +static void draw_bg(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode, lv_opa_t opa); +static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode, lv_opa_t opa); + #if LV_USE_ANIMATION static void lv_bar_anim(void * bar, lv_anim_value_t value); static void lv_bar_anim_ready(lv_anim_t * a); @@ -359,112 +364,9 @@ static lv_design_res_t lv_bar_design(lv_obj_t * bar, const lv_area_t * clip_area } else if(mode == LV_DESIGN_DRAW_MAIN) { lv_opa_t opa_scale = lv_obj_get_opa_scale(bar); -#if LV_USE_GROUP == 0 - ancestor_design_f(bar, mask, mode); -#else - /* Draw the borders later if the bar is focused. - * At value = 100% the indicator can cover to whole background and the focused style won't - * be visible*/ - if(lv_obj_is_focused(bar)) { - const lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG); - lv_style_t style_tmp; - lv_style_copy(&style_tmp, style_bg); - style_tmp.body.border.width = 0; - lv_draw_rect(&bar->coords, clip_area, &style_tmp, opa_scale); - } else { - ancestor_design_f(bar, clip_area, mode); - } -#endif - lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + draw_bg(bar, clip_area, mode, opa_scale); + draw_indic(bar, clip_area, mode, opa_scale); - if(ext->cur_value != ext->min_value || ext->sym -#if LV_USE_ANIMATION - || ext->anim_start != LV_BAR_ANIM_STATE_INV -#endif - ) { - const lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC); - lv_area_t indic_area; - lv_area_copy(&indic_area, &bar->coords); - indic_area.x1 += style_indic->body.padding.left; - indic_area.x2 -= style_indic->body.padding.right; - indic_area.y1 += style_indic->body.padding.top; - indic_area.y2 -= style_indic->body.padding.bottom; - - lv_coord_t w = lv_area_get_width(&indic_area); - lv_coord_t h = lv_area_get_height(&indic_area); - - if(w >= h) { - /*Horizontal*/ -#if LV_USE_ANIMATION - if(ext->anim_state != LV_BAR_ANIM_STATE_INV) { - /*Calculate the coordinates of anim. start and end*/ - lv_coord_t anim_start_x = - (int32_t)((int32_t)w * (ext->anim_start - ext->min_value)) / (ext->max_value - ext->min_value); - lv_coord_t anim_end_x = - (int32_t)((int32_t)w * (ext->anim_end - ext->min_value)) / (ext->max_value - ext->min_value); - - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - indic_area.x2 = - anim_start_x + (((anim_end_x - anim_start_x) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM); - } else -#endif - { - indic_area.x2 = - (int32_t)((int32_t)w * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value); - } - - indic_area.x2 = indic_area.x1 + indic_area.x2 - 1; - if(ext->sym && ext->min_value < 0 && ext->max_value > 0) { - /*Calculate the coordinate of the zero point*/ - lv_coord_t zero; - zero = indic_area.x1 + (-ext->min_value * w) / (ext->max_value - ext->min_value); - if(indic_area.x2 > zero) - indic_area.x1 = zero; - else { - indic_area.x1 = indic_area.x2; - indic_area.x2 = zero; - } - } - } else { -#if LV_USE_ANIMATION - if(ext->anim_state != LV_BAR_ANIM_STATE_INV) { - /*Calculate the coordinates of anim. start and end*/ - lv_coord_t anim_start_y = - (int32_t)((int32_t)h * (ext->anim_start - ext->min_value)) / (ext->max_value - ext->min_value); - lv_coord_t anim_end_y = - (int32_t)((int32_t)h * (ext->anim_end - ext->min_value)) / (ext->max_value - ext->min_value); - - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - indic_area.y1 = - anim_start_y + (((anim_end_y - anim_start_y) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM); - } else -#endif - { - indic_area.y1 = - (int32_t)((int32_t)h * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value); - } - - indic_area.y1 = indic_area.y2 - indic_area.y1 + 1; - - if(ext->sym && ext->min_value < 0 && ext->max_value > 0) { - /*Calculate the coordinate of the zero point*/ - lv_coord_t zero; - zero = indic_area.y2 - (-ext->min_value * h) / (ext->max_value - ext->min_value); - if(indic_area.y1 < zero) - indic_area.y2 = zero; - else { - indic_area.y2 = indic_area.y1; - indic_area.y1 = zero; - } - } - } - - /*Draw the indicator*/ - lv_draw_rect(&indic_area, clip_area, style_indic, opa_scale); - } - } else if(mode == LV_DESIGN_DRAW_POST) { #if LV_USE_GROUP /*Draw the border*/ if(lv_obj_is_focused(bar)) { @@ -477,10 +379,151 @@ static lv_design_res_t lv_bar_design(lv_obj_t * bar, const lv_area_t * clip_area lv_draw_rect(&bar->coords, clip_area, &style_tmp, opa_scale); } #endif + + } else if(mode == LV_DESIGN_DRAW_POST) { + } return LV_DESIGN_RES_OK; } +static void draw_bg(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode, lv_opa_t opa) +{ + +#if LV_USE_GROUP == 0 + /*Simply draw the background*/ + ancestor_design_f(bar, mask, mode); +#else + /* Draw the borders later if the bar is focused. + * At value = 100% the indicator can cover to whole background and the focused style won't + * be visible*/ + if(lv_obj_is_focused(bar)) { + const lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG); + lv_style_t style_tmp; + lv_style_copy(&style_tmp, style_bg); + style_tmp.body.border.width = 0; + lv_draw_rect(&bar->coords, clip_area, &style_tmp, opa); + } else { + ancestor_design_f(bar, clip_area, mode); + } +#endif +} + +static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode, lv_opa_t opa) +{ + (void) mode; /*Unused*/ + + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + + lv_coord_t objw = lv_obj_get_width(bar); + lv_coord_t objh = lv_obj_get_height(bar); + int32_t range = ext->max_value - ext->min_value; + bool hor = objw >= objh ? true : false; + bool sym = false; + if(ext->sym && ext->min_value < 0 && ext->max_value > 0) sym = true; + + bool anim = false; +#if LV_USE_ANIMATION + if(ext->anim_state != LV_BAR_ANIM_STATE_INV) anim = true; +#endif + + /*Calculate the indicator area*/ + lv_area_copy(&ext->indic_area, &bar->coords); + const lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC); + + /*Respect padding and minimum width/height too*/ + ext->indic_area.x1 += style_indic->body.padding.left; + ext->indic_area.x2 -= style_indic->body.padding.right; + ext->indic_area.y1 += style_indic->body.padding.top; + ext->indic_area.y2 -= style_indic->body.padding.bottom; + + if(hor && lv_area_get_height(&ext->indic_area) < LV_BAR_SIZE_MIN) { + ext->indic_area.y1 = bar->coords.y1 + (objh / 2) - (LV_BAR_SIZE_MIN / 2); + ext->indic_area.y2 = ext->indic_area.y1 + LV_BAR_SIZE_MIN; + } else if(!hor && lv_area_get_width(&ext->indic_area) < LV_BAR_SIZE_MIN) { + ext->indic_area.x1 = bar->coords.x1 + (objw / 2) - (LV_BAR_SIZE_MIN / 2); + ext->indic_area.x2 = ext->indic_area.x1 + LV_BAR_SIZE_MIN; + } + + lv_coord_t indicw = lv_area_get_width(&ext->indic_area); + lv_coord_t indich = lv_area_get_height(&ext->indic_area); + + /*Calculate the indicator length*/ + lv_coord_t indic_length; + if(anim) { +#if LV_USE_ANIMATION + /*Calculate the coordinates of anim. start and end*/ + lv_coord_t anim_start_v = (int32_t)((int32_t)(hor ? indicw : indich) * + (ext->anim_start - ext->min_value)) / range; + lv_coord_t anim_end_v = (int32_t)((int32_t)(hor ? indicw : indich) * + (ext->anim_end - ext->min_value)) / range; + + /*Calculate the real position based on `anim_state` (between `anim_start` and + * `anim_end`)*/ + indic_length = anim_start_v + (((anim_end_v - anim_start_v) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM); +#endif + } + else + { + indic_length = (int32_t)((int32_t)(hor ? indicw : indich) * (ext->cur_value - ext->min_value)) / + (ext->max_value - ext->min_value); + } + + /*Horizontal bar*/ + if(hor) { + ext->indic_area.x2 = ext->indic_area.x1 + indic_length - 1; + if(sym) { + lv_coord_t zero; + zero = ext->indic_area.x1 + (-ext->min_value * indicw) / range; + if(ext->indic_area.x2 > zero) + ext->indic_area.x1 = zero; + else { + ext->indic_area.x1 = ext->indic_area.x2; + ext->indic_area.x2 = zero; + } + } + } + /*Vertical bar*/ + else { + ext->indic_area.y1 = ext->indic_area.y2 - indic_length + 1; + if(sym) { + lv_coord_t zero; + zero = ext->indic_area.y2 - (-ext->min_value * objh) / range; + if(ext->indic_area.y1 < zero) + ext->indic_area.y2 = zero; + else { + ext->indic_area.y2 = ext->indic_area.y1; + ext->indic_area.y1 = zero; + } + } + } + + /*Draw the indicator*/ + + /*Do not draw a zero length indicator*/ + if(!sym && indic_length == 0) return; + + lv_area_t mask_area; + lv_area_copy(&mask_area, &bar->coords); + mask_area.x1 -= style_indic->body.shadow.width; + mask_area.y1 -= style_indic->body.shadow.width; + mask_area.x2 += style_indic->body.shadow.width; + mask_area.y2 += style_indic->body.shadow.width; + + if(style_indic->body.shadow.offset.x > 0) mask_area.x1 += style_indic->body.shadow.offset.x; + else mask_area.x2 -= style_indic->body.shadow.offset.x; + + if(style_indic->body.shadow.offset.y > 0) mask_area.y1 += style_indic->body.shadow.offset.y; + else mask_area.y2 -= style_indic->body.shadow.offset.y; + + lv_draw_mask_param_t mask_param; + lv_draw_mask_radius_init(&mask_param, &mask_area,style_indic->body.radius, false); + int16_t bg_mask_id = lv_draw_mask_add(&mask_param, NULL); + + lv_draw_rect(&ext->indic_area, clip_area, style_indic, opa); + + lv_draw_mask_remove_id(bg_mask_id); +} + /** * Signal function of the bar * @param bar pointer to a bar object @@ -498,6 +541,17 @@ static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param) if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) { const lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC); + const lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG); + + lv_coord_t bg_size = style_bg->body.shadow.width + style_bg->body.shadow.spread; + bg_size += LV_MATH_MAX(LV_MATH_ABS(style_bg->body.shadow.offset.x), LV_MATH_ABS(style_bg->body.shadow.offset.y)); + + lv_coord_t indic_size = style_indic->body.shadow.width + style_indic->body.shadow.spread; + indic_size += LV_MATH_MAX(LV_MATH_ABS(style_indic->body.shadow.offset.x), LV_MATH_ABS(style_indic->body.shadow.offset.y)); + + bar->ext_draw_pad = LV_MATH_MAX(bar->ext_draw_pad, bg_size); + bar->ext_draw_pad = LV_MATH_MAX(bar->ext_draw_pad, indic_size); + if(style_indic->body.shadow.width > bar->ext_draw_pad) bar->ext_draw_pad = style_indic->body.shadow.width; } else if(sign == LV_SIGNAL_GET_TYPE) { lv_obj_type_t * buf = param; diff --git a/src/lv_objx/lv_bar.h b/src/lv_objx/lv_bar.h index 14c558e7a..856014921 100644 --- a/src/lv_objx/lv_bar.h +++ b/src/lv_objx/lv_bar.h @@ -56,6 +56,7 @@ typedef struct int16_t cur_value; /*Current value of the bar*/ int16_t min_value; /*Minimum value of the bar*/ int16_t max_value; /*Maximum value of the bar*/ + lv_area_t indic_area; /*Save the indicator area. MIght be used by derived types*/ #if LV_USE_ANIMATION lv_anim_value_t anim_start; lv_anim_value_t anim_end; diff --git a/src/lv_objx/lv_btnm.c b/src/lv_objx/lv_btnm.c index fa3903fbc..bfbc5f03a 100644 --- a/src/lv_objx/lv_btnm.c +++ b/src/lv_objx/lv_btnm.c @@ -201,7 +201,7 @@ void lv_btnm_set_map(const lv_obj_t * btnm, const char * map[]) /*Make sure the last row is at the bottom of 'btnm'*/ if(map_p_tmp[btn_cnt][0] == '\0') { /*Last row?*/ - btn_h = max_h - act_y + style_bg->body.padding.bottom - 1; + btn_h = lv_obj_get_height(btnm)- act_y - style_bg->body.padding.bottom - 1; } /*Only deal with the non empty lines*/ @@ -862,7 +862,7 @@ static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param) for(area_below = ext->btn_id_pr; area_below < ext->btn_cnt; area_below++) { if(ext->button_areas[area_below].y1 > ext->button_areas[ext->btn_id_pr].y1 && pr_center >= ext->button_areas[area_below].x1 && - pr_center <= ext->button_areas[area_below].x2 + style->body.padding.left) { + pr_center <= ext->button_areas[area_below].x2 + style->body.padding.inner) { break; } } @@ -883,7 +883,7 @@ static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param) for(area_above = ext->btn_id_pr; area_above >= 0; area_above--) { if(ext->button_areas[area_above].y1 < ext->button_areas[ext->btn_id_pr].y1 && - pr_center >= ext->button_areas[area_above].x1 - style->body.padding.left && + pr_center >= ext->button_areas[area_above].x1 - style->body.padding.inner && pr_center <= ext->button_areas[area_above].x2) { break; } diff --git a/src/lv_objx/lv_calendar.c b/src/lv_objx/lv_calendar.c index b75df4955..d475c509f 100644 --- a/src/lv_objx/lv_calendar.c +++ b/src/lv_objx/lv_calendar.c @@ -787,7 +787,7 @@ static void draw_days(lv_obj_t * calendar, const lv_area_t * mask) } label_area.x1 = - calendar->coords.x1 + (w * day) / 7 + style_bg->body.padding.left + style_bg->body.padding.right; + calendar->coords.x1 + (w * day) / 7 + style_bg->body.padding.left; label_area.x2 = label_area.x1 + box_w - 1; /*Draw the "today box"*/ diff --git a/src/lv_objx/lv_canvas.c b/src/lv_objx/lv_canvas.c index 1f228e095..78def5e20 100644 --- a/src/lv_objx/lv_canvas.c +++ b/src/lv_objx/lv_canvas.c @@ -505,6 +505,8 @@ void lv_canvas_draw_rect(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord set_set_px_cb(&disp.driver, dsc->header.cf); +#if LV_ANTIALIAS + /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/ lv_color_t ctransp = LV_COLOR_TRANSP; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && @@ -513,6 +515,7 @@ void lv_canvas_draw_rect(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord { disp.driver.antialiasing = 0; } +#endif lv_disp_t * refr_ori = lv_refr_get_disp_refreshing(); lv_refr_set_disp_refreshing(&disp); @@ -671,6 +674,7 @@ void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t * points, uint32_t set_set_px_cb(&disp.driver, dsc->header.cf); +#if LV_ANTIALIAS /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/ lv_color_t ctransp = LV_COLOR_TRANSP; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && @@ -679,6 +683,7 @@ void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t * points, uint32_t { disp.driver.antialiasing = 0; } +#endif lv_disp_t * refr_ori = lv_refr_get_disp_refreshing(); lv_refr_set_disp_refreshing(&disp); @@ -725,6 +730,7 @@ void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t * points, uint32 set_set_px_cb(&disp.driver, dsc->header.cf); +#if LV_ANTIALIAS /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/ lv_color_t ctransp = LV_COLOR_TRANSP; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && @@ -733,6 +739,7 @@ void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t * points, uint32 { disp.driver.antialiasing = 0; } +#endif lv_disp_t * refr_ori = lv_refr_get_disp_refreshing(); lv_refr_set_disp_refreshing(&disp); @@ -780,6 +787,7 @@ void lv_canvas_draw_arc(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_ set_set_px_cb(&disp.driver, dsc->header.cf); +#if LV_ANTIALIAS /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/ lv_color_t ctransp = LV_COLOR_TRANSP; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && @@ -788,6 +796,7 @@ void lv_canvas_draw_arc(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_ { disp.driver.antialiasing = 0; } +#endif lv_disp_t * refr_ori = lv_refr_get_disp_refreshing(); lv_refr_set_disp_refreshing(&disp); diff --git a/src/lv_objx/lv_canvas.h b/src/lv_objx/lv_canvas.h index 1d59d44d6..9f9cf03ed 100644 --- a/src/lv_objx/lv_canvas.h +++ b/src/lv_objx/lv_canvas.h @@ -23,6 +23,7 @@ extern "C" { #include "../lv_core/lv_obj.h" #include "../lv_objx/lv_img.h" +#include "../lv_draw/lv_draw_img.h" /********************* * DEFINES @@ -239,21 +240,21 @@ void lv_canvas_draw_arc(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_ /********************** * MACROS **********************/ -#define LV_CANVAS_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h) -#define LV_CANVAS_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h) -#define LV_CANVAS_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h) +#define LV_CANVAS_BUF_SIZE_TRUE_COLOR(w, h) LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) +#define LV_CANVAS_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) +#define LV_CANVAS_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) /*+ 1: to be sure no fractional row*/ -#define LV_CANVAS_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h)) -#define LV_CANVAS_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h)) -#define LV_CANVAS_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h)) -#define LV_CANVAS_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h)) +#define LV_CANVAS_BUF_SIZE_ALPHA_1BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) +#define LV_CANVAS_BUF_SIZE_ALPHA_2BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) +#define LV_CANVAS_BUF_SIZE_ALPHA_4BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) +#define LV_CANVAS_BUF_SIZE_ALPHA_8BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) /*4 * X: for palette*/ -#define LV_CANVAS_BUF_SIZE_INDEXED_1BIT(w, h) (LV_CANVAS_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2) -#define LV_CANVAS_BUF_SIZE_INDEXED_2BIT(w, h) (LV_CANVAS_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4) -#define LV_CANVAS_BUF_SIZE_INDEXED_4BIT(w, h) (LV_CANVAS_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16) -#define LV_CANVAS_BUF_SIZE_INDEXED_8BIT(w, h) (LV_CANVAS_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256) +#define LV_CANVAS_BUF_SIZE_INDEXED_1BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) +#define LV_CANVAS_BUF_SIZE_INDEXED_2BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) +#define LV_CANVAS_BUF_SIZE_INDEXED_4BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) +#define LV_CANVAS_BUF_SIZE_INDEXED_8BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) #endif /*LV_USE_CANVAS*/ diff --git a/src/lv_objx/lv_chart.c b/src/lv_objx/lv_chart.c index 2cd341bc8..468f8aa1f 100644 --- a/src/lv_objx/lv_chart.c +++ b/src/lv_objx/lv_chart.c @@ -620,11 +620,20 @@ static lv_design_res_t lv_chart_design(lv_obj_t * chart, const lv_area_t * clip_ lv_chart_draw_div(chart, clip_area); - if(ext->type & LV_CHART_TYPE_LINE) lv_chart_draw_lines(chart, clip_area); - if(ext->type & LV_CHART_TYPE_COLUMN) lv_chart_draw_cols(chart, clip_area); - if(ext->type & LV_CHART_TYPE_POINT) lv_chart_draw_points(chart, clip_area); - if(ext->type & LV_CHART_TYPE_VERTICAL_LINE) lv_chart_draw_vertical_lines(chart, clip_area); - if((ext->type & LV_CHART_TYPE_AREA) || (ext->type & LV_CHART_TYPE_AREA_FADED)) lv_chart_draw_areas(chart, clip_area); + /* Adjust the mask to remove the margin (clips chart contents to be within background) */ + + lv_area_t mask_tmp, adjusted_mask; + lv_obj_get_coords(chart, &mask_tmp); + + bool union_ok = lv_area_intersect(&adjusted_mask, clip_area, &mask_tmp); + + if(union_ok) { + if(ext->type & LV_CHART_TYPE_LINE) lv_chart_draw_lines(chart, clip_area); + if(ext->type & LV_CHART_TYPE_COLUMN) lv_chart_draw_cols(chart, clip_area); + if(ext->type & LV_CHART_TYPE_POINT) lv_chart_draw_points(chart, clip_area); + if(ext->type & LV_CHART_TYPE_VERTICAL_LINE) lv_chart_draw_vertical_lines(chart, clip_area); + if((ext->type & LV_CHART_TYPE_AREA) || (ext->type & LV_CHART_TYPE_AREA_FADED)) lv_chart_draw_areas(chart, clip_area); + } lv_chart_draw_axes(chart, clip_area); } diff --git a/src/lv_objx/lv_ddlist.c b/src/lv_objx/lv_ddlist.c index 7a3ea414b..caadd581f 100644 --- a/src/lv_objx/lv_ddlist.c +++ b/src/lv_objx/lv_ddlist.c @@ -142,8 +142,10 @@ lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy) ext->draw_arrow = copy_ext->draw_arrow; ext->stay_open = copy_ext->stay_open; - /*Refresh the style with new signal function*/ - lv_obj_refresh_style(new_ddlist); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(copy, LV_DDLIST_STYLE_BG)); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, lv_ddlist_get_style(copy, LV_DDLIST_STYLE_SB)); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, lv_ddlist_get_style(copy, LV_DDLIST_STYLE_SEL)); + } LV_LOG_INFO("drop down list created"); @@ -230,6 +232,7 @@ void lv_ddlist_set_fix_height(lv_obj_t * ddlist, lv_coord_t h) */ void lv_ddlist_set_fix_width(lv_obj_t * ddlist, lv_coord_t w) { + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); if(w == 0) { lv_cont_set_fit2(ddlist, LV_FIT_TIGHT, lv_cont_get_fit_bottom(ddlist)); } else { @@ -237,6 +240,12 @@ void lv_ddlist_set_fix_width(lv_obj_t * ddlist, lv_coord_t w) lv_obj_set_width(ddlist, w); } + switch(lv_label_get_align(ext->label)) { + case LV_LABEL_ALIGN_LEFT: lv_obj_align(ext->label, NULL, LV_ALIGN_IN_LEFT_MID, 0, 0); break; + case LV_LABEL_ALIGN_CENTER: lv_obj_align(ext->label, NULL, LV_ALIGN_CENTER, 0, 0); break; + case LV_LABEL_ALIGN_RIGHT: lv_obj_align(ext->label, NULL, LV_ALIGN_IN_RIGHT_MID, 0, 0); break; + } + lv_ddlist_refr_size(ddlist, false); } @@ -571,9 +580,14 @@ static lv_design_res_t lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * cli new_style.text.color = sel_style->text.color; new_style.text.opa = sel_style->text.opa; lv_area_t area_arrow; - area_arrow.x2 = ddlist->coords.x2 - style->body.padding.right; - area_arrow.x1 = area_arrow.x2 - - lv_txt_get_width(LV_SYMBOL_DOWN, strlen(LV_SYMBOL_DOWN), sel_style->text.font, 0, 0); + lv_coord_t arrow_width = lv_txt_get_width(LV_SYMBOL_DOWN, strlen(LV_SYMBOL_DOWN), sel_style->text.font, 0, 0); + if(lv_label_get_align(ext->label) != LV_LABEL_ALIGN_RIGHT) { + area_arrow.x2 = ddlist->coords.x2 - style->body.padding.right; + area_arrow.x1 = area_arrow.x2 - arrow_width; + } else { + area_arrow.x1 = ddlist->coords.x1 + style->body.padding.left; + area_arrow.x2 = area_arrow.x1 + arrow_width; + } area_arrow.y1 = ddlist->coords.y1 + style->text.line_space; area_arrow.y2 = area_arrow.y1 + font_h; @@ -773,13 +787,16 @@ static lv_res_t release_handler(lv_obj_t * ddlist) uint16_t new_opt = 0; const char * txt = lv_label_get_text(ext->label); uint32_t i = 0; - uint32_t line_cnt = 0; + uint32_t i_prev = 0; + + uint32_t letter_cnt = 0; uint32_t letter; - for(line_cnt = 0; line_cnt < letter_i; line_cnt++) { + for(letter_cnt = 0; letter_cnt < letter_i; letter_cnt++) { letter = lv_txt_encoded_next(txt, &i); /*Count he lines to reach the clicked letter. But ignore the last '\n' because it * still belongs to the clicked line*/ - if(letter == '\n' && i != letter_i) new_opt++; + if(letter == '\n' && i_prev != letter_i) new_opt++; + i_prev = i; } ext->sel_opt_id = new_opt; diff --git a/src/lv_objx/lv_kb.c b/src/lv_objx/lv_kb.c index 03c1260ce..12731855a 100644 --- a/src/lv_objx/lv_kb.c +++ b/src/lv_objx/lv_kb.c @@ -73,8 +73,8 @@ static const char * kb_map_num[] = {"1", "2", "3", LV_SYMBOL_CLOSE, "\n", static const lv_btnm_ctrl_t kb_ctrl_num_map[] = { 1, 1, 1, LV_KB_CTRL_BTN_FLAGS | 2, 1, 1, 1, LV_KB_CTRL_BTN_FLAGS | 2, - 1, 1, 1, LV_KB_CTRL_BTN_FLAGS | 2, - LV_KB_CTRL_BTN_FLAGS | 1, 1, 1, LV_KB_CTRL_BTN_FLAGS | 1, LV_KB_CTRL_BTN_FLAGS | 1}; + 1, 1, 1, 2, + 1, 1, 1, 1, 1}; /* clang-format on */ /********************** @@ -323,7 +323,7 @@ const lv_style_t * lv_kb_get_style(const lv_obj_t * kb, lv_kb_style_t type) */ void lv_kb_def_event_cb(lv_obj_t * kb, lv_event_t event) { - if(event != LV_EVENT_VALUE_CHANGED && event != LV_EVENT_LONG_PRESSED_REPEAT) return; + if(event != LV_EVENT_VALUE_CHANGED) return; lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); uint16_t btn_id = lv_btnm_get_active_btn(kb); diff --git a/src/lv_objx/lv_label.c b/src/lv_objx/lv_label.c index 09e335e8b..b71020809 100644 --- a/src/lv_objx/lv_label.c +++ b/src/lv_objx/lv_label.c @@ -13,6 +13,7 @@ #include "../lv_core/lv_group.h" #include "../lv_misc/lv_color.h" #include "../lv_misc/lv_math.h" +#include "../lv_misc/lv_printf.h" /********************* * DEFINES @@ -203,6 +204,51 @@ void lv_label_set_text(lv_obj_t * label, const char * text) lv_label_refr_text(label); } +/** + * Set a new formatted text for a label. Memory will be allocated to store the text by the label. + * @param label pointer to a label object + * @param fmt `printf`-like format + */ +void lv_label_set_text_fmt(lv_obj_t * label, const char * fmt, ...) +{ + lv_obj_invalidate(label); + + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + /*If text is NULL then refresh */ + if(fmt == NULL) { + lv_label_refr_text(label); + return; + } + + if(ext->text != NULL && ext->static_txt == 0) { + lv_mem_free(ext->text); + ext->text = NULL; + } + + va_list ap, ap2; + va_start(ap, fmt); + va_copy(ap2, ap); + + /*Allocate space for the new text by using trick from C99 standard section 7.19.6.12 */ + uint32_t len = lv_vsnprintf(NULL, 0, fmt, ap); + + va_end(ap); + + + ext->text = lv_mem_alloc(len+1); + lv_mem_assert(ext->text); + if(ext->text == NULL) return; + ext->text[len-1] = 0; /* Ensure NULL termination */ + + lv_vsnprintf(ext->text, len+1, fmt, ap2); + + va_end(ap2); + ext->static_txt = 0; /*Now the text is dynamically allocated*/ + + lv_label_refr_text(label); +} + /** * Set a new text for a label from a character array. The array don't has to be '\0' terminated. * Memory will be allocated to store the array by the label. @@ -585,14 +631,13 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; uint32_t i = line_start; - uint32_t i_current = i; + uint32_t i_act = i; uint32_t letter; uint32_t letter_next; if(new_line_start > 0) { - while(i <= new_line_start - 1) { - /* Get the current letter. - * Be careful 'i' already points to the next character*/ + while(i < new_line_start) { + /* Get the current letter.*/ letter = lv_txt_encoded_next(txt, &i); /*Get the next letter too for kerning*/ @@ -606,12 +651,14 @@ 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); - if(pos->x < x) { - i = i_current; + + /*Finish if the x position or the last char of the line is reached*/ + if(pos->x < x || i == new_line_start) { + i = i_act; break; } x += style->text.letter_space; - i_current = i; + i_act = i; } } diff --git a/src/lv_objx/lv_label.h b/src/lv_objx/lv_label.h index 16b1a6e8f..2804ef700 100644 --- a/src/lv_objx/lv_label.h +++ b/src/lv_objx/lv_label.h @@ -21,6 +21,7 @@ extern "C" { #if LV_USE_LABEL != 0 +#include #include "../lv_core/lv_obj.h" #include "../lv_font/lv_font.h" #include "../lv_font/lv_symbol_def.h" @@ -124,6 +125,13 @@ lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy); */ void lv_label_set_text(lv_obj_t * label, const char * text); +/** + * Set a new formatted text for a label. Memory will be allocated to store the text by the label. + * @param label pointer to a label object + * @param fmt `printf`-like format + */ +void lv_label_set_text_fmt(lv_obj_t * label, const char * fmt, ...); + /** * Set a new text for a label from a character array. The array don't has to be '\0' terminated. * Memory will be allocated to store the array by the label. diff --git a/src/lv_objx/lv_list.c b/src/lv_objx/lv_list.c index c6e4300ca..977826e74 100644 --- a/src/lv_objx/lv_list.c +++ b/src/lv_objx/lv_list.c @@ -47,11 +47,7 @@ static lv_signal_cb_t img_signal; static lv_signal_cb_t label_signal; static lv_signal_cb_t ancestor_page_signal; static lv_signal_cb_t ancestor_btn_signal; -#if LV_USE_GROUP -/*Used to make the last clicked button pressed (selected) when the list become focused and - * `click_focus == 1`*/ -static lv_obj_t * last_clicked_btn; -#endif + /********************** * MACROS @@ -94,6 +90,7 @@ lv_obj_t * lv_list_create(lv_obj_t * par, const lv_obj_t * copy) #if LV_USE_GROUP ext->last_sel = NULL; ext->selected_btn = NULL; + ext->last_clicked_btn = NULL; #endif lv_obj_set_signal_cb(new_list, lv_list_signal); @@ -366,6 +363,36 @@ void lv_list_set_style(lv_obj_t * list, lv_list_style_t type, const lv_style_t * } } +/** + * Set layout of a list + * @param list pointer to a list object + * @param layout which layout should be used + */ + void lv_list_set_layout(lv_obj_t * list, lv_layout_t layout) + { + /* Update list layout if necessary */ + if (layout == lv_list_get_layout(list)) return; + + /* Get the first button on the list */ + lv_obj_t * btn = lv_list_get_prev_btn(list, NULL); + + /* Visit all buttons on the list and update their layout */ + while(btn != NULL) { + /*If a column layout set the buttons' width to list width*/ + if(layout == LV_LAYOUT_COL_M || layout == LV_LAYOUT_COL_L || layout == LV_LAYOUT_COL_R) { + lv_btn_set_fit2(list, LV_FIT_FLOOD, LV_FIT_TIGHT); + } + /*If a row layout set the buttons' width according to the content*/ + else if (layout == LV_LAYOUT_ROW_M || layout == LV_LAYOUT_ROW_T || layout == LV_LAYOUT_ROW_B) { + lv_btn_set_fit(list, LV_FIT_TIGHT); + } + + btn = lv_list_get_prev_btn(list, btn); + } + + lv_page_set_scrl_layout(list, layout); + } + /*===================== * Getter functions *====================*/ @@ -529,15 +556,24 @@ lv_obj_t * lv_list_get_btn_selected(const lv_obj_t * list) lv_list_ext_t * ext = lv_obj_get_ext_attr(list); return ext->selected_btn; } - #endif +/** + * Get layout of a list + * @param list pointer to a list object + * @return layout of the list object + */ +lv_layout_t lv_list_get_layout(lv_obj_t * list) +{ + return lv_page_get_scrl_layout(list); +} + /** * Get a style of a list * @param list pointer to a list object * @param type which style should be get * @return style pointer to a style - * */ + */ const lv_style_t * lv_list_get_style(const lv_obj_t * list, lv_list_style_t type) { const lv_style_t * style = NULL; @@ -558,6 +594,7 @@ const lv_style_t * lv_list_get_style(const lv_obj_t * list, lv_list_style_t type return style; } + /*===================== * Other functions *====================*/ @@ -751,10 +788,12 @@ static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param) /*Else select the clicked button*/ else { /*Mark the last clicked button (if any) as selected because it triggered the focus*/ - if(last_clicked_btn) { - lv_list_set_btn_selected(list, last_clicked_btn); + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + if(ext->last_clicked_btn) { + lv_list_set_btn_selected(list, ext->last_clicked_btn); + ext->last_clicked_btn = NULL; + } else { - lv_list_ext_t * ext = lv_obj_get_ext_attr(list); if(ext->last_sel) { /* Select the last used button */ lv_list_set_btn_selected(list, ext->last_sel); @@ -770,8 +809,8 @@ static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param) #if LV_USE_GROUP /*De-select the selected btn*/ lv_list_set_btn_selected(list, NULL); - last_clicked_btn = NULL; /*button click will be set if click happens before focus*/ lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + ext->last_clicked_btn = NULL; /*button click will be set if click happens before focus*/ ext->selected_btn = NULL; #endif } else if(sign == LV_SIGNAL_GET_EDITABLE) { @@ -861,7 +900,7 @@ static lv_res_t lv_list_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * para /* If `click_focus == 1` then LV_SIGNAL_FOCUS need to know which button triggered the focus * to mark it as selected (pressed state)*/ - last_clicked_btn = btn; + ext->last_clicked_btn = btn; #endif if(lv_indev_is_dragging(lv_indev_get_act()) == false && ext->single_mode) { lv_list_btn_single_select(btn); diff --git a/src/lv_objx/lv_list.h b/src/lv_objx/lv_list.h index 7b7927841..eef890b36 100644 --- a/src/lv_objx/lv_list.h +++ b/src/lv_objx/lv_list.h @@ -61,6 +61,9 @@ typedef struct #if LV_USE_GROUP lv_obj_t * last_sel; /* The last selected button. It will be reverted when the list is focused again */ lv_obj_t * selected_btn; /* The button is currently being selected*/ + /*Used to make the last clicked button pressed (selected) when the list become focused and + * `click_focus == 1`*/ + lv_obj_t * last_clicked_btn; #endif } lv_list_ext_t; @@ -189,6 +192,13 @@ static inline void lv_list_set_anim_time(lv_obj_t * list, uint16_t anim_time) */ void lv_list_set_style(lv_obj_t * list, lv_list_style_t type, const lv_style_t * style); +/** + * Set layout of a list + * @param list pointer to a list object + * @param layout which layout should be used + */ +void lv_list_set_layout(lv_obj_t * list, lv_layout_t layout); + /*===================== * Getter functions *====================*/ @@ -259,6 +269,13 @@ uint16_t lv_list_get_size(const lv_obj_t * list); lv_obj_t * lv_list_get_btn_selected(const lv_obj_t * list); #endif +/** + * Get layout of a list + * @param list pointer to a list object + * @return layout of the list object + */ +lv_layout_t lv_list_get_layout(lv_obj_t * list); + /** * Get the scroll bar mode of a list * @param list pointer to a list object diff --git a/src/lv_objx/lv_preload.c b/src/lv_objx/lv_preload.c index 0c23de329..8f7edbaee 100644 --- a/src/lv_objx/lv_preload.c +++ b/src/lv_objx/lv_preload.c @@ -220,9 +220,10 @@ void lv_preload_set_type(lv_obj_t * preload, lv_preload_type_t type) lv_anim_create(&b); break; } + case LV_PRELOAD_TYPE_CONSTANT_ARC: case LV_PRELOAD_TYPE_SPINNING_ARC: default: { - ext->anim_type = LV_PRELOAD_TYPE_SPINNING_ARC; + ext->anim_type = type; lv_anim_t a; a.var = preload; if(ext->anim_dir == LV_PRELOAD_DIR_FORWARD) { @@ -234,7 +235,8 @@ void lv_preload_set_type(lv_obj_t * preload, lv_preload_type_t type) a.end = 0; } a.exec_cb = (lv_anim_exec_xcb_t)lv_preload_spinner_anim; - a.path_cb = lv_anim_path_ease_in_out; + a.path_cb = (LV_PRELOAD_TYPE_CONSTANT_ARC == type ? + lv_anim_path_linear : lv_anim_path_ease_in_out); a.ready_cb = NULL; a.act_time = 0; a.time = ext->time; diff --git a/src/lv_objx/lv_preload.h b/src/lv_objx/lv_preload.h index cbc7826a6..22b87f32f 100644 --- a/src/lv_objx/lv_preload.h +++ b/src/lv_objx/lv_preload.h @@ -48,6 +48,7 @@ extern "C" { enum { LV_PRELOAD_TYPE_SPINNING_ARC, LV_PRELOAD_TYPE_FILLSPIN_ARC, + LV_PRELOAD_TYPE_CONSTANT_ARC, }; typedef uint8_t lv_preload_type_t; @@ -67,7 +68,7 @@ typedef struct /*New data for this type */ lv_anim_value_t arc_length; /*Length of the spinning indicator in degree*/ uint16_t time; /*Time of one round*/ - lv_preload_type_t anim_type : 1; /*Type of the arc animation*/ + lv_preload_type_t anim_type : 2; /*Type of the arc animation*/ lv_preload_dir_t anim_dir : 1; /*Animation Direction*/ } lv_preload_ext_t; diff --git a/src/lv_objx/lv_roller.c b/src/lv_objx/lv_roller.c index 5a460e72f..138bd8be3 100644 --- a/src/lv_objx/lv_roller.c +++ b/src/lv_objx/lv_roller.c @@ -176,9 +176,17 @@ void lv_roller_set_options(lv_obj_t * roller, const char * options, lv_roller_mo void lv_roller_set_align(lv_obj_t * roller, lv_label_align_t align) { lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); - lv_mem_assert(ext); - if(ext->ddlist.label == NULL) return; /*Probably the roller is being deleted if the label is NULL.*/ - lv_label_set_align(ext->ddlist.label, align); + + lv_obj_t * label = ext->ddlist.label; + + if(label == NULL) return; /*Probably the roller is being deleted if the label is NULL.*/ + lv_label_set_align(label, align); + + switch(lv_label_get_align(label)) { + case LV_LABEL_ALIGN_LEFT: lv_obj_align(label, NULL, LV_ALIGN_IN_LEFT_MID, 0, 0); break; + case LV_LABEL_ALIGN_CENTER: lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0); break; + case LV_LABEL_ALIGN_RIGHT: lv_obj_align(label, NULL, LV_ALIGN_IN_RIGHT_MID, 0, 0); break; + } } /** @@ -498,7 +506,7 @@ static lv_res_t lv_roller_scrl_signal(lv_obj_t * roller_scrl, lv_signal_t sign, lv_coord_t font_h = lv_font_get_line_height(font); if(sign == LV_SIGNAL_DRAG_END) { - /*If dragged then align the list to there be an element in the middle*/ + /*If dragged then align the list to have an element in the middle*/ lv_coord_t label_y1 = ext->ddlist.label->coords.y1 - roller->coords.y1; lv_coord_t label_unit = font_h + style_label->text.line_space; lv_coord_t mid = (roller->coords.y2 - roller->coords.y1) / 2; diff --git a/src/lv_objx/lv_slider.c b/src/lv_objx/lv_slider.c index 8d1be0e2e..47498d433 100644 --- a/src/lv_objx/lv_slider.c +++ b/src/lv_objx/lv_slider.c @@ -18,8 +18,6 @@ /********************* * DEFINES *********************/ -#define LV_SLIDER_SIZE_MIN 4 /*hor. pad and ver. pad cannot make the bar or indicator smaller then this [px]*/ -#define LV_SLIDER_NOT_PRESSED INT16_MIN /********************** * TYPEDEFS @@ -69,9 +67,7 @@ lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy) if(ext == NULL) return NULL; /*Initialize the allocated 'ext' */ - ext->drag_value = LV_SLIDER_NOT_PRESSED; ext->style_knob = &lv_style_pretty; - ext->knob_in = 0; /*The signal and design functions are not copied so set them here*/ lv_obj_set_signal_cb(new_slider, lv_slider_signal); @@ -96,7 +92,6 @@ lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy) else { lv_slider_ext_t * copy_ext = lv_obj_get_ext_attr(copy); ext->style_knob = copy_ext->style_knob; - ext->knob_in = copy_ext->knob_in; /*Refresh the style with new signal function*/ lv_obj_refresh_style(new_slider); } @@ -110,21 +105,6 @@ lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy) * Setter functions *====================*/ -/** - * Set the 'knob in' attribute of a slider - * @param slider pointer to slider object - * @param in true: the knob is drawn always in the slider; - * false: the knob can be out on the edges - */ -void lv_slider_set_knob_in(lv_obj_t * slider, bool in) -{ - lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); - if(ext->knob_in == in) return; - - ext->knob_in = in == false ? 0 : 1; - lv_obj_invalidate(slider); -} - /** * Set a style of a slider * @param slider pointer to a slider object @@ -156,12 +136,7 @@ void lv_slider_set_style(lv_obj_t * slider, lv_slider_style_t type, const lv_sty */ int16_t lv_slider_get_value(const lv_obj_t * slider) { - lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); - - if(ext->drag_value != LV_SLIDER_NOT_PRESSED) - return ext->drag_value; - else - return lv_bar_get_value(slider); + return lv_bar_get_value(slider); } /** @@ -172,19 +147,7 @@ int16_t lv_slider_get_value(const lv_obj_t * slider) bool lv_slider_is_dragged(const lv_obj_t * slider) { lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); - return ext->drag_value == LV_SLIDER_NOT_PRESSED ? false : true; -} - -/** - * Get the 'knob in' attribute of a slider - * @param slider pointer to slider object - * @return true: the knob is drawn always in the slider; - * false: the knob can be out on the edges - */ -bool lv_slider_get_knob_in(const lv_obj_t * slider) -{ - lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); - return ext->knob_in == 0 ? false : true; + return ext->dragging ? true : false; } /** @@ -230,218 +193,63 @@ static lv_design_res_t lv_slider_design(lv_obj_t * slider, const lv_area_t * cli } /*Draw the object*/ else if(mode == LV_DESIGN_DRAW_MAIN) { + + /*The ancestor design function will draw the background and the indicator. + * It also sets ext->bar.indic_area*/ + ancestor_design_f(slider, clip_area, mode); + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); - - const lv_style_t * style_bg = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG); const lv_style_t * style_knob = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); - const lv_style_t * style_indic = lv_slider_get_style(slider, LV_SLIDER_STYLE_INDIC); - lv_opa_t opa_scale = lv_obj_get_opa_scale(slider); - lv_coord_t slider_w = lv_area_get_width(&slider->coords); - lv_coord_t slider_h = lv_area_get_height(&slider->coords); + lv_coord_t objw = lv_obj_get_width(slider); + lv_coord_t objh = lv_obj_get_height(slider); + bool hor = objw >= objh ? true : false; + lv_coord_t knob_size = hor ? objh : objw; + bool sym = false; + if(ext->bar.sym && ext->bar.min_value < 0 && ext->bar.max_value > 0) sym = true; - /*Draw the bar*/ - lv_area_t area_bg; - lv_area_copy(&area_bg, &slider->coords); - - /*Be sure at least LV_SLIDER_SIZE_MIN size will remain*/ - lv_coord_t pad_top_bg = style_bg->body.padding.top; - lv_coord_t pad_bottom_bg = style_bg->body.padding.bottom; - lv_coord_t pad_left_bg = style_bg->body.padding.left; - lv_coord_t pad_right_bg = style_bg->body.padding.right; - if(pad_top_bg + pad_bottom_bg + LV_SLIDER_SIZE_MIN > lv_area_get_height(&area_bg)) { - pad_top_bg = (lv_area_get_height(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; - pad_bottom_bg = pad_top_bg; - } - if(pad_left_bg + pad_right_bg + LV_SLIDER_SIZE_MIN > lv_area_get_width(&area_bg)) { - pad_left_bg = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; - pad_right_bg = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; - } - - if(ext->knob_in) { /*Enable extra size if the knob is inside */ - area_bg.x1 += pad_left_bg; - area_bg.x2 -= pad_right_bg; - area_bg.y1 += pad_top_bg; - area_bg.y2 -= pad_bottom_bg; - } else { /*Let space only in the perpendicular directions*/ - area_bg.x1 += slider_w < slider_h ? pad_left_bg : 0; /*Pad only for vertical slider*/ - area_bg.x2 -= slider_w < slider_h ? pad_right_bg : 0; /*Pad only for vertical slider*/ - area_bg.y1 += slider_w > slider_h ? pad_top_bg : 0; /*Pad only for horizontal slider*/ - area_bg.y2 -= slider_w > slider_h ? pad_bottom_bg : 0; /*Pad only for horizontal slider*/ - } - -#if LV_USE_GROUP == 0 - lv_draw_rect(&area_bg, clip_area, style_bg, lv_obj_get_opa_scale(slider)); -#else - /* Draw the borders later if the slider is focused. - * At value = 100% the indicator can cover to whole background and the focused style won't - * be visible*/ - if(lv_obj_is_focused(slider)) { - lv_style_t style_tmp; - lv_style_copy(&style_tmp, style_bg); - style_tmp.body.border.width = 0; - lv_draw_rect(&area_bg, clip_area, &style_tmp, opa_scale); - } else { - lv_draw_rect(&area_bg, clip_area, style_bg, opa_scale); - } -#endif - - /*Draw the indicator*/ - lv_area_t area_indic; - lv_area_copy(&area_indic, &area_bg); - - /*Be sure at least ver pad/hor pad width indicator will remain*/ - lv_coord_t pad_top_indic = style_indic->body.padding.top; - lv_coord_t pad_bottom_indic = style_indic->body.padding.bottom; - lv_coord_t pad_left_indic = style_indic->body.padding.left; - lv_coord_t pad_right_indic = style_indic->body.padding.right; - if(pad_top_indic + pad_bottom_indic + LV_SLIDER_SIZE_MIN > lv_area_get_height(&area_bg)) { - pad_top_indic = (lv_area_get_height(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; - pad_bottom_indic = pad_top_indic; - } - if(pad_left_indic + pad_right_indic + LV_SLIDER_SIZE_MIN > lv_area_get_width(&area_bg)) { - pad_left_indic = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; - pad_right_indic = pad_left_indic; - } - - area_indic.x1 += pad_left_indic; - area_indic.x2 -= pad_right_indic; - area_indic.y1 += pad_top_indic; - area_indic.y2 -= pad_bottom_indic; - - lv_coord_t cur_value = lv_slider_get_value(slider); - lv_coord_t min_value = lv_slider_get_min_value(slider); - lv_coord_t max_value = lv_slider_get_max_value(slider); - - /*If dragged draw to the drag position*/ - if(ext->drag_value != LV_SLIDER_NOT_PRESSED) cur_value = ext->drag_value; - - if(slider_w >= slider_h) { - lv_coord_t indic_w = lv_area_get_width(&area_indic); -#if LV_USE_ANIMATION - if(ext->bar.anim_state != LV_BAR_ANIM_STATE_INV) { - /*Calculate the coordinates of anim. start and end*/ - lv_coord_t anim_start_x = - (int32_t)((int32_t)indic_w * (ext->bar.anim_start - min_value)) / (max_value - min_value); - lv_coord_t anim_end_x = - (int32_t)((int32_t)indic_w * (ext->bar.anim_end - min_value)) / (max_value - min_value); - - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - area_indic.x2 = anim_start_x + (((anim_end_x - anim_start_x) * ext->bar.anim_state) >> 8); - } else -#endif - { - area_indic.x2 = (int32_t)((int32_t)indic_w * (cur_value - min_value)) / (max_value - min_value); - } - area_indic.x2 = area_indic.x1 + area_indic.x2 - 1; - - /*Draw the indicator but don't draw an ugly 1px wide rectangle on the left on min. - * value*/ - if(area_indic.x1 != area_indic.x2) lv_draw_rect(&area_indic, clip_area, style_indic, opa_scale); - - } else { - lv_coord_t indic_h = lv_area_get_height(&area_indic); -#if LV_USE_ANIMATION - if(ext->bar.anim_state != LV_BAR_ANIM_STATE_INV) { - /*Calculate the coordinates of anim. start and end*/ - lv_coord_t anim_start_y = - (int32_t)((int32_t)indic_h * (ext->bar.anim_start - min_value)) / (max_value - min_value); - lv_coord_t anim_end_y = - (int32_t)((int32_t)indic_h * (ext->bar.anim_end - min_value)) / (max_value - min_value); - - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - area_indic.y1 = anim_start_y + (((anim_end_y - anim_start_y) * ext->bar.anim_state) >> 8); - } else -#endif - { - area_indic.y1 = (int32_t)((int32_t)indic_h * (cur_value - min_value)) / (max_value - min_value); - } - area_indic.y1 = area_indic.y2 - area_indic.y1 + 1; - - /*Draw the indicator but don't draw an ugly 1px height rectangle on the bottom on min. - * value*/ - if(area_indic.x1 != area_indic.x2) lv_draw_rect(&area_indic, clip_area, style_indic, opa_scale); - } - - /*Before the knob add the border if required*/ -#if LV_USE_GROUP - /* Draw the borders later if the bar is focused. - * At value = 100% the indicator can cover to whole background and the focused style won't - * be visible*/ - if(lv_obj_is_focused(slider)) { - lv_style_t style_tmp; - lv_style_copy(&style_tmp, style_bg); - style_tmp.body.opa = LV_OPA_TRANSP; - style_tmp.body.shadow.width = 0; - lv_draw_rect(&area_bg, clip_area, &style_tmp, opa_scale); - } -#endif - - /*Draw the knob*/ lv_area_t knob_area; - lv_area_copy(&knob_area, &slider->coords); - if(slider_w >= slider_h) { - if(ext->knob_in == 0) { - knob_area.x1 = area_indic.x2 - slider_h / 2; - knob_area.x2 = knob_area.x1 + slider_h - 1; + /*Horizontal*/ + if(hor) { + if(!sym) { + knob_area.x1 = ext->bar.indic_area.x2; } else { -#if LV_USE_ANIMATION - if(ext->bar.anim_state != LV_BAR_ANIM_STATE_INV) { - lv_coord_t w = slider_w - slider_h - 1; - lv_coord_t anim_start_x = - (int32_t)((int32_t)w * (ext->bar.anim_start - min_value)) / (max_value - min_value); - lv_coord_t anim_end_x = - (int32_t)((int32_t)w * (ext->bar.anim_end - min_value)) / (max_value - min_value); - - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - knob_area.x1 = anim_start_x + (((anim_end_x - anim_start_x) * ext->bar.anim_state) >> 8); - } else -#endif - { - knob_area.x1 = (int32_t)((int32_t)(slider_w - slider_h - 1) * (cur_value - min_value)) / - (max_value - min_value); + if(ext->bar.cur_value >= 0) { + knob_area.x1 = ext->bar.indic_area.x2; + } else { + knob_area.x1 = ext->bar.indic_area.x1; } - - knob_area.x1 += slider->coords.x1; - knob_area.x2 = knob_area.x1 + slider_h - 1; } - + knob_area.x1 -= (knob_size >> 1); + knob_area.x2 = knob_area.x1 + knob_size; knob_area.y1 = slider->coords.y1; knob_area.y2 = slider->coords.y2; - } else { - if(ext->knob_in == 0) { - knob_area.y1 = area_indic.y1 - slider_w / 2; - knob_area.y2 = knob_area.y1 + slider_w - 1; - } else { -#if LV_USE_ANIMATION - if(ext->bar.anim_state != LV_BAR_ANIM_STATE_INV) { - lv_coord_t h = slider_h - slider_w - 1; - lv_coord_t anim_start_x = - (int32_t)((int32_t)h * (ext->bar.anim_start - min_value)) / (max_value - min_value); - lv_coord_t anim_end_x = - (int32_t)((int32_t)h * (ext->bar.anim_end - min_value)) / (max_value - min_value); - - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - knob_area.y2 = anim_start_x + (((anim_end_x - anim_start_x) * ext->bar.anim_state) >> 8); - } else -#endif - { - knob_area.y2 = (int32_t)((int32_t)(slider_h - slider_w - 1) * (cur_value - min_value)) / - (max_value - min_value); - } - - knob_area.y2 = slider->coords.y2 - knob_area.y2; - knob_area.y1 = knob_area.y2 - slider_w - 1; - } - knob_area.x1 = slider->coords.x1; - knob_area.x2 = slider->coords.x2; } + /*Vertical*/ + else { + if(!sym) { + knob_area.y1 = ext->bar.indic_area.y1; + } else { + if(ext->bar.cur_value >= 0) { + knob_area.y1 = ext->bar.indic_area.y1; + } else { + knob_area.y1 = ext->bar.indic_area.y2; + } + } + knob_area.y1 -= (knob_size >> 1); + knob_area.y2 = knob_area.y1 + knob_size; + knob_area.x1 = slider->coords.x1; + knob_area.x2 = slider->coords.x2; + } + + /*Apply the paddings on the knob area*/ + knob_area.x1 -= style_knob->body.padding.left; + knob_area.x2 += style_knob->body.padding.right; + knob_area.y1 -= style_knob->body.padding.top; + knob_area.y2 += style_knob->body.padding.bottom; + lv_draw_rect(&knob_area, clip_area, style_knob, opa_scale); } /*Post draw when the children are drawn*/ @@ -468,42 +276,42 @@ static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * par lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); lv_point_t p; - lv_coord_t w = lv_obj_get_width(slider); - lv_coord_t h = lv_obj_get_height(slider); if(sign == LV_SIGNAL_PRESSED) { - ext->drag_value = lv_slider_get_value(slider); + ext->dragging = true; } else if(sign == LV_SIGNAL_PRESSING) { lv_indev_get_point(param, &p); - int16_t tmp = 0; - if(w > h) { - lv_coord_t knob_w = h; - p.x -= - slider->coords.x1 + h / 2; /*Modify the point to shift with half knob (important on the start and end)*/ - tmp = (int32_t)((int32_t)p.x * (ext->bar.max_value - ext->bar.min_value + 1)) / (w - knob_w); - tmp += ext->bar.min_value; + + lv_coord_t w = lv_obj_get_width(slider); + lv_coord_t h = lv_obj_get_height(slider); + const lv_style_t * indic_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_INDIC); + int32_t range = ext->bar.max_value - ext->bar.min_value; + int16_t new_value = 0; + if(w >= h) { + lv_coord_t indic_w = w - indic_style->body.padding.left - indic_style->body.padding.right; + int32_t range = ext->bar.max_value - ext->bar.min_value; + new_value = p.x - (slider->coords.x1 + indic_style->body.padding.left); /*Make the point relative to the indicator*/ + new_value = (new_value * range) / indic_w; + new_value += ext->bar.min_value; } else { - lv_coord_t knob_h = w; - p.y -= - slider->coords.y1 + w / 2; /*Modify the point to shift with half knob (important on the start and end)*/ - tmp = (int32_t)((int32_t)p.y * (ext->bar.max_value - ext->bar.min_value + 1)) / (h - knob_h); - tmp = ext->bar.max_value - tmp; /*Invert the value: smaller value means higher y*/ + lv_coord_t indic_h = h - indic_style->body.padding.bottom - indic_style->body.padding.top; + new_value = p.y - (slider->coords.y2 + indic_style->body.padding.bottom); /*Make the point relative to the indicator*/ + new_value = (-new_value * range) / indic_h; + new_value += ext->bar.min_value; + } - if(tmp < ext->bar.min_value) - tmp = ext->bar.min_value; - else if(tmp > ext->bar.max_value) - tmp = ext->bar.max_value; + if(new_value < ext->bar.min_value) new_value = ext->bar.min_value; + else if(new_value > ext->bar.max_value) new_value = ext->bar.max_value; - if(tmp != ext->drag_value) { - ext->drag_value = tmp; + if(new_value != ext->bar.cur_value) { + ext->bar.cur_value = new_value; lv_obj_invalidate(slider); res = lv_event_send(slider, LV_EVENT_VALUE_CHANGED, NULL); if(res != LV_RES_OK) return res; } } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) { - if(ext->drag_value != LV_SLIDER_NOT_PRESSED) lv_slider_set_value(slider, ext->drag_value, false); - ext->drag_value = LV_SLIDER_NOT_PRESSED; + ext->dragging = false; #if LV_USE_GROUP /*Leave edit mode if released. (No need to wait for LONG_PRESS) */ @@ -523,30 +331,32 @@ static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * par slider->signal_cb(slider, LV_SIGNAL_REFR_EXT_DRAW_PAD, NULL); } } else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) { - const lv_style_t * style = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG); + const lv_style_t * bg_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG); + const lv_style_t * indic_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_INDIC); const lv_style_t * knob_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); - lv_coord_t shadow_w = knob_style->body.shadow.width; - if(ext->knob_in == 0) { - /* The smaller size is the knob diameter*/ - lv_coord_t x = LV_MATH_MIN(w / 2 + 1 + shadow_w, h / 2 + 1 + shadow_w); - if(slider->ext_draw_pad < x) slider->ext_draw_pad = x; - } else { - lv_coord_t pad = 0; - pad = LV_MATH_MIN(pad, style->body.padding.top); - pad = LV_MATH_MIN(pad, style->body.padding.bottom); - pad = LV_MATH_MIN(pad, style->body.padding.left); - pad = LV_MATH_MIN(pad, style->body.padding.right); - if(pad < 0) pad = -pad; - if(slider->ext_draw_pad < pad) slider->ext_draw_pad = pad; + /* The smaller size is the knob diameter*/ + lv_coord_t knob_size = LV_MATH_MIN(lv_obj_get_width(slider), lv_obj_get_height(slider)) >> 1; + knob_size += LV_MATH_MAX( + LV_MATH_MAX(knob_style->body.padding.left, knob_style->body.padding.right), + LV_MATH_MAX(knob_style->body.padding.bottom,knob_style->body.padding.top)); + + knob_size += knob_style->body.shadow.width + knob_style->body.shadow.spread; + knob_size += LV_MATH_MAX(LV_MATH_ABS(knob_style->body.shadow.offset.x), LV_MATH_ABS(knob_style->body.shadow.offset.y)); + + lv_coord_t bg_size = bg_style->body.shadow.width + bg_style->body.shadow.spread; + bg_size += LV_MATH_MAX(LV_MATH_ABS(bg_style->body.shadow.offset.x), LV_MATH_ABS(bg_style->body.shadow.offset.y)); + + lv_coord_t indic_size = indic_style->body.shadow.width + indic_style->body.shadow.spread; + indic_size += LV_MATH_MAX(LV_MATH_ABS(indic_style->body.shadow.offset.x), LV_MATH_ABS(indic_style->body.shadow.offset.y)); + + slider->ext_draw_pad = LV_MATH_MAX(slider->ext_draw_pad, knob_size); + slider->ext_draw_pad = LV_MATH_MAX(slider->ext_draw_pad, indic_size); + slider->ext_draw_pad = LV_MATH_MAX(slider->ext_draw_pad, bg_size); - if(slider->ext_draw_pad < shadow_w) slider->ext_draw_pad = shadow_w; - } } else if(sign == LV_SIGNAL_CONTROL) { char c = *((char *)param); - ext->drag_value = LV_SLIDER_NOT_PRESSED; - if(c == LV_KEY_RIGHT || c == LV_KEY_UP) { lv_slider_set_value(slider, lv_slider_get_value(slider) + 1, true); res = lv_event_send(slider, LV_EVENT_VALUE_CHANGED, NULL); diff --git a/src/lv_objx/lv_slider.h b/src/lv_objx/lv_slider.h index 07086efe4..4872c0d36 100644 --- a/src/lv_objx/lv_slider.h +++ b/src/lv_objx/lv_slider.h @@ -42,8 +42,7 @@ typedef struct lv_bar_ext_t bar; /*Ext. of ancestor*/ /*New data for this type */ const lv_style_t * style_knob; /*Style of the knob*/ - int16_t drag_value; /*Store a temporal value during press until release (Handled by the library)*/ - uint8_t knob_in : 1; /*1: Draw the knob inside the bar*/ + uint8_t dragging :1; /*1: the slider is being dragged*/ } lv_slider_ext_t; /** Built-in styles of slider*/ @@ -93,9 +92,10 @@ static inline void lv_slider_set_range(lv_obj_t * slider, int16_t min, int16_t m } /** - * Set the animation time of the slider - * @param slider pointer to a bar object - * @param anim_time the animation time in milliseconds. + * Make the slider symmetric to zero. The indicator will grow from zero instead of the minimum + * position. + * @param slider pointer to a slider object + * @param en true: enable disable symmetric behavior; false: disable */ static inline void lv_slider_set_anim_time(lv_obj_t * slider, uint16_t anim_time) { @@ -103,12 +103,14 @@ static inline void lv_slider_set_anim_time(lv_obj_t * slider, uint16_t anim_time } /** - * Set the 'knob in' attribute of a slider - * @param slider pointer to slider object - * @param in true: the knob is drawn always in the slider; - * false: the knob can be out on the edges + * Set the animation time of the slider + * @param slider pointer to a bar object + * @param anim_time the animation time in milliseconds. */ -void lv_slider_set_knob_in(lv_obj_t * slider, bool in); +static inline void lv_slider_set_sym(lv_obj_t * slider, bool en) +{ + lv_bar_set_sym(slider, en); +} /** * Set a style of a slider @@ -157,12 +159,24 @@ static inline int16_t lv_slider_get_max_value(const lv_obj_t * slider) bool lv_slider_is_dragged(const lv_obj_t * slider); /** - * Get the 'knob in' attribute of a slider - * @param slider pointer to slider object - * @return true: the knob is drawn always in the slider; - * false: the knob can be out on the edges + * Get the animation time of the slider + * @param slider pointer to a slider object + * @return the animation time in milliseconds. */ -bool lv_slider_get_knob_in(const lv_obj_t * slider); +static inline uint16_t lv_slider_get_anim_time(lv_obj_t * slider) +{ + return lv_bar_get_anim_time(slider); +} + +/** + * Get whether the slider is symmetric or not. + * @param slider pointer to a bar object + * @return true: symmetric is enabled; false: disable + */ +static inline bool lv_slider_get_sym(lv_obj_t * slider) +{ + return lv_bar_get_sym(slider); +} /** * Get a style of a slider diff --git a/src/lv_objx/lv_sw.c b/src/lv_objx/lv_sw.c index 80854b0a0..eeb6b4441 100644 --- a/src/lv_objx/lv_sw.c +++ b/src/lv_objx/lv_sw.c @@ -29,12 +29,14 @@ /********************** * STATIC PROTOTYPES **********************/ +static lv_design_res_t lv_sw_design(lv_obj_t * slider, const lv_area_t * clip_area, lv_design_mode_t mode); static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param); /********************** * STATIC VARIABLES **********************/ static lv_signal_cb_t ancestor_signal; +static lv_design_cb_t ancestor_design; /********************** * MACROS @@ -55,11 +57,12 @@ lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy) LV_LOG_TRACE("switch create started"); /*Create the ancestor of switch*/ - lv_obj_t * new_sw = lv_slider_create(par, copy); + lv_obj_t * new_sw = lv_bar_create(par, copy); lv_mem_assert(new_sw); if(new_sw == NULL) return NULL; if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_sw); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_sw); /*Allocate the switch type specific extended data*/ lv_sw_ext_t * ext = lv_obj_allocate_ext_attr(new_sw, sizeof(lv_sw_ext_t)); @@ -71,17 +74,19 @@ lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy) #if LV_USE_ANIMATION ext->anim_time = 0; #endif - ext->style_knob_off = ext->slider.style_knob; - ext->style_knob_on = ext->slider.style_knob; + ext->style_knob_off = &lv_style_pretty; + ext->style_knob_on = &lv_style_pretty; /*The signal and design functions are not copied so set them here*/ lv_obj_set_signal_cb(new_sw, lv_sw_signal); + lv_obj_set_design_cb(new_sw, lv_sw_design); /*Init the new switch switch*/ if(copy == NULL) { + lv_obj_set_click(new_sw, true); + lv_obj_set_protect(new_sw, LV_PROTECT_PRESS_LOST); lv_obj_set_size(new_sw, 2 * LV_DPI / 3, LV_DPI / 3); - lv_slider_set_knob_in(new_sw, true); - lv_slider_set_range(new_sw, 0, LV_SW_MAX_VALUE); + lv_bar_set_range(new_sw, 0, LV_SW_MAX_VALUE); /*Set the default styles*/ lv_theme_t * th = lv_theme_get_current(); @@ -93,7 +98,6 @@ lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy) } else { /*Let the slider' style*/ } - } /*Copy an existing switch*/ else { @@ -104,11 +108,6 @@ lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy) ext->anim_time = copy_ext->anim_time; #endif - if(lv_sw_get_state(new_sw)) - lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); - else - lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); - /*Refresh the style with new signal function*/ lv_obj_refresh_style(new_sw); } @@ -133,8 +132,9 @@ void lv_sw_on(lv_obj_t * sw, lv_anim_enable_t anim) anim = LV_ANIM_OFF; #endif lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); - lv_slider_set_value(sw, LV_SW_MAX_VALUE, anim); - lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); + ext->state = 1; + lv_bar_set_value(sw, LV_SW_MAX_VALUE, anim); + lv_obj_invalidate(sw); } /** @@ -148,8 +148,9 @@ void lv_sw_off(lv_obj_t * sw, lv_anim_enable_t anim) anim = LV_ANIM_OFF; #endif lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); - lv_slider_set_value(sw, 0, anim); - lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); + ext->state = 0; + lv_bar_set_value(sw, 0, anim); + lv_obj_invalidate(sw); } /** @@ -184,15 +185,15 @@ void lv_sw_set_style(lv_obj_t * sw, lv_sw_style_t type, const lv_style_t * style lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); switch(type) { - case LV_SLIDER_STYLE_BG: lv_slider_set_style(sw, LV_SLIDER_STYLE_BG, style); break; - case LV_SLIDER_STYLE_INDIC: lv_bar_set_style(sw, LV_SLIDER_STYLE_INDIC, style); break; + case LV_SW_STYLE_BG: lv_bar_set_style(sw, LV_BAR_STYLE_BG, style); break; + case LV_SW_STYLE_INDIC: lv_bar_set_style(sw, LV_BAR_STYLE_INDIC, style); break; case LV_SW_STYLE_KNOB_OFF: ext->style_knob_off = style; - if(lv_sw_get_state(sw) == 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style); + lv_obj_invalidate(sw); break; case LV_SW_STYLE_KNOB_ON: ext->style_knob_on = style; - if(lv_sw_get_state(sw) != 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style); + lv_obj_invalidate(sw); break; } } @@ -224,8 +225,8 @@ const lv_style_t * lv_sw_get_style(const lv_obj_t * sw, lv_sw_style_t type) lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); switch(type) { - case LV_SW_STYLE_BG: style = lv_slider_get_style(sw, LV_SLIDER_STYLE_BG); break; - case LV_SW_STYLE_INDIC: style = lv_slider_get_style(sw, LV_SLIDER_STYLE_INDIC); break; + case LV_SW_STYLE_BG: style = lv_bar_get_style(sw, LV_BAR_STYLE_BG); break; + case LV_SW_STYLE_INDIC: style = lv_bar_get_style(sw, LV_BAR_STYLE_INDIC); break; case LV_SW_STYLE_KNOB_OFF: style = ext->style_knob_off; break; case LV_SW_STYLE_KNOB_ON: style = ext->style_knob_on; break; default: style = NULL; break; @@ -250,6 +251,59 @@ uint16_t lv_sw_get_anim_time(const lv_obj_t * sw) * STATIC FUNCTIONS **********************/ +/** + * Handle the drawing related tasks of the sliders + * @param sw pointer to an object + * @param clip_area 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 + * @param return an element of `lv_design_res_t` + */ +static lv_design_res_t lv_sw_design(lv_obj_t * sw, const lv_area_t * clip_area, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return LV_DESIGN_RES_NOT_COVER; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + /*The ancestor design function will draw the background and the indicator. + * It also sets ext->bar.indic_area*/ + ancestor_design(sw, clip_area, mode); + + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + lv_opa_t opa_scale = lv_obj_get_opa_scale(sw); + const lv_style_t * style_knob = lv_sw_get_style(sw, ext->state ? + LV_SW_STYLE_KNOB_ON : LV_SW_STYLE_KNOB_OFF); + + const lv_style_t * style_indic = lv_sw_get_style(sw, LV_SW_STYLE_INDIC); + + lv_coord_t objw = lv_obj_get_width(sw); + lv_coord_t objh = lv_obj_get_height(sw); + lv_coord_t indic_maxw = objw - style_indic->body.padding.left - style_indic->body.padding.right; + lv_coord_t knob_size = objh; + + lv_coord_t indic_p = (lv_area_get_width(&ext->bar.indic_area) * 256) / (indic_maxw); + lv_area_t knob_area; + knob_area.x2 = ext->bar.indic_area.x2; + knob_area.x2 += (knob_size * (256 - indic_p)) >> 8; + if(knob_area.x2 < sw->coords.x1 + knob_size) knob_area.x2 = sw->coords.x1 + knob_size; + knob_area.x1 = knob_area.x2 - knob_size; + knob_area.y1 = sw->coords.y1; + knob_area.y2 = sw->coords.y2; + + lv_draw_rect(&knob_area, clip_area, style_knob, opa_scale); + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + } + + return LV_DESIGN_RES_OK; +} + + /** * Signal function of the switch * @param sw pointer to a switch object @@ -261,26 +315,11 @@ static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) { lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); - /*Save the current (old) value before slider signal modifies it. It will be required in the - * later calculations*/ - int16_t old_val; - if(sign == LV_SIGNAL_PRESSING) - old_val = ext->slider.drag_value; - else - old_val = lv_slider_get_value(sw); - - /*Don't let the slider to call the action. Switch handles it differently*/ - lv_event_cb_t event_cb = sw->event_cb; - sw->event_cb = NULL; - lv_res_t res; /* Include the ancient signal function */ - res = ancestor_signal(sw, sign, param); if(res != LV_RES_OK) return res; - sw->event_cb = event_cb; - if(sign == LV_SIGNAL_CLEANUP) { /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ } else if(sign == LV_SIGNAL_PRESSED) { @@ -297,37 +336,42 @@ static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) } else if(sign == LV_SIGNAL_PRESSING) { /*See if the switch was slid (moved at least a little)*/ lv_indev_t * indev = lv_indev_get_act(); + lv_point_t p; if(indev) { - lv_point_t p = {0, 0}; lv_indev_get_point(indev, &p); - if(LV_MATH_ABS(p.x - ext->start_x) > LV_INDEV_DEF_DRAG_LIMIT) ext->slided = 1; - } + if(LV_MATH_ABS(p.x - ext->start_x) > indev->driver.drag_limit) ext->slided = 1; - /*If didn't slide then revert the min/max value. So click without slide won't move the - * switch as a slider*/ - if(ext->slided == 0) { - if(lv_sw_get_state(sw)) - ext->slider.drag_value = LV_SW_MAX_VALUE; - else - ext->slider.drag_value = 0; - } + /*If slid set the value accordingly*/ + if(ext->slided) { + lv_coord_t w = lv_obj_get_width(sw); + const lv_style_t * indic_style = lv_sw_get_style(sw, LV_SW_STYLE_INDIC); + int16_t new_val = 0; - /*If explicitly changed (by slide) don't need to be toggled on release*/ - int16_t threshold = LV_SW_MAX_VALUE / 2; - if((old_val < threshold && ext->slider.drag_value > threshold) || - (old_val > threshold && ext->slider.drag_value < threshold)) { - ext->changed = 1; + lv_coord_t indic_w = w - indic_style->body.padding.left - indic_style->body.padding.right; + int32_t range = ext->bar.max_value - ext->bar.min_value; + new_val = p.x - (sw->coords.x1 + indic_style->body.padding.left); /*Make the point relative to the indicator*/ + new_val = (new_val * range) / indic_w; + new_val += ext->bar.min_value; + + if(new_val < ext->bar.min_value) new_val = ext->bar.min_value; + else if(new_val > ext->bar.max_value) new_val = ext->bar.max_value; + + /*If explicitly changed (by slide) don't need to be toggled on release*/ + int16_t threshold = LV_SW_MAX_VALUE / 2; + if((new_val < threshold && ext->bar.cur_value > threshold) || + (new_val > threshold && ext->bar.cur_value < threshold)) { + ext->changed = 1; + } + + if(new_val != ext->bar.cur_value) { + ext->bar.cur_value = new_val; + lv_obj_invalidate(sw); + } + } } } else if(sign == LV_SIGNAL_PRESS_LOST) { - if(lv_sw_get_state(sw)) { - lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); - lv_slider_set_value(sw, LV_SW_MAX_VALUE, LV_ANIM_ON); - if(res != LV_RES_OK) return res; - } else { - lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); - lv_slider_set_value(sw, 0, LV_ANIM_ON); - if(res != LV_RES_OK) return res; - } + if(lv_sw_get_state(sw)) lv_sw_on(sw, LV_ANIM_ON); + else lv_sw_off(sw, LV_ANIM_ON); } else if(sign == LV_SIGNAL_RELEASED) { /*If not dragged then toggle the switch*/ if(ext->changed == 0) { @@ -345,7 +389,7 @@ static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) } /*If the switch was dragged then calculate the new state based on the current position*/ else { - int16_t v = lv_slider_get_value(sw); + int16_t v = lv_bar_get_value(sw); int32_t state; if(v > LV_SW_MAX_VALUE / 2) { lv_sw_on(sw, LV_ANIM_ON); @@ -361,17 +405,52 @@ static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) char c = *((char *)param); uint32_t state; if(c == LV_KEY_RIGHT || c == LV_KEY_UP) { - lv_slider_set_value(sw, LV_SW_MAX_VALUE, true); + lv_bar_set_value(sw, LV_SW_MAX_VALUE, LV_ANIM_ON); state = 1; res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, &state); if(res != LV_RES_OK) return res; } else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) { - lv_slider_set_value(sw, 0, true); + lv_bar_set_value(sw, 0, LV_ANIM_ON); state = 0; res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, &state); if(res != LV_RES_OK) return res; } - } else if(sign == LV_SIGNAL_GET_EDITABLE) { + } + else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) { + const lv_style_t * bg_style = lv_sw_get_style(sw, LV_SW_STYLE_BG); + const lv_style_t * indic_style = lv_sw_get_style(sw, LV_SW_STYLE_INDIC); + const lv_style_t * knob_on_style = lv_sw_get_style(sw, LV_SW_STYLE_KNOB_OFF); + const lv_style_t * knob_off_style = lv_sw_get_style(sw, LV_SW_STYLE_KNOB_ON); + /* The smaller size is the knob diameter*/ + lv_coord_t knob_on_size = LV_MATH_MIN(lv_obj_get_width(sw), lv_obj_get_height(sw)) >> 1; + knob_on_size += LV_MATH_MAX( + LV_MATH_MAX(knob_on_style->body.padding.left, knob_on_style->body.padding.right), + LV_MATH_MAX(knob_on_style->body.padding.bottom, knob_on_style->body.padding.top)); + + knob_on_size += knob_on_style->body.shadow.width + knob_on_style->body.shadow.spread; + knob_on_size += LV_MATH_MAX(LV_MATH_ABS(knob_on_style->body.shadow.offset.x), LV_MATH_ABS(knob_on_style->body.shadow.offset.y)); + + lv_coord_t knob_off_size = LV_MATH_MIN(lv_obj_get_width(sw), lv_obj_get_height(sw)) >> 1; + knob_off_size += LV_MATH_MAX( + LV_MATH_MAX(knob_off_style->body.padding.left, knob_off_style->body.padding.right), + LV_MATH_MAX(knob_off_style->body.padding.bottom, knob_off_style->body.padding.top)); + + knob_off_size += knob_off_style->body.shadow.width + knob_off_style->body.shadow.spread; + knob_off_size += LV_MATH_MAX(LV_MATH_ABS(knob_off_style->body.shadow.offset.x), LV_MATH_ABS(knob_off_style->body.shadow.offset.y)); + + lv_coord_t bg_size = bg_style->body.shadow.width + bg_style->body.shadow.spread; + bg_size += LV_MATH_MAX(LV_MATH_ABS(bg_style->body.shadow.offset.x), LV_MATH_ABS(bg_style->body.shadow.offset.y)); + + lv_coord_t indic_size = indic_style->body.shadow.width + indic_style->body.shadow.spread; + indic_size += LV_MATH_MAX(LV_MATH_ABS(indic_style->body.shadow.offset.x), LV_MATH_ABS(indic_style->body.shadow.offset.y)); + + sw->ext_draw_pad = LV_MATH_MAX(sw->ext_draw_pad, knob_on_size); + sw->ext_draw_pad = LV_MATH_MAX(sw->ext_draw_pad, knob_off_size); + sw->ext_draw_pad = LV_MATH_MAX(sw->ext_draw_pad, bg_size); + sw->ext_draw_pad = LV_MATH_MAX(sw->ext_draw_pad, indic_size); + + } + else if(sign == LV_SIGNAL_GET_EDITABLE) { bool * editable = (bool *)param; *editable = false; /*The ancestor slider is editable the switch is not*/ } else if(sign == LV_SIGNAL_GET_TYPE) { diff --git a/src/lv_objx/lv_sw.h b/src/lv_objx/lv_sw.h index f4b44aeb1..359e5deee 100644 --- a/src/lv_objx/lv_sw.h +++ b/src/lv_objx/lv_sw.h @@ -27,7 +27,7 @@ extern "C" { #endif #include "../lv_core/lv_obj.h" -#include "lv_slider.h" +#include "lv_bar.h" /********************* * DEFINES @@ -40,13 +40,14 @@ extern "C" { /*Data of switch*/ typedef struct { - lv_slider_ext_t slider; /*Ext. of ancestor*/ + lv_bar_ext_t bar; /*Ext. of ancestor*/ /*New data for this type */ const lv_style_t * style_knob_off; /**< Style of the knob when the switch is OFF*/ const lv_style_t * style_knob_on; /**< Style of the knob when the switch is ON (NULL to use the same as OFF)*/ lv_coord_t start_x; - uint8_t changed : 1; /*Indicates the switch state explicitly changed by drag*/ - uint8_t slided : 1; + uint8_t changed :1; /*Indicates the switch state explicitly changed by drag*/ + uint8_t slided :1; + uint8_t state :1; /*The current state*/ #if LV_USE_ANIMATION uint16_t anim_time; /*switch animation time */ #endif @@ -128,7 +129,8 @@ void lv_sw_set_anim_time(lv_obj_t * sw, uint16_t anim_time); */ static inline bool lv_sw_get_state(const lv_obj_t * sw) { - return lv_bar_get_value(sw) < LV_SW_MAX_VALUE / 2 ? false : true; + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + return ext->state ? true : false; } /** diff --git a/src/lv_objx/lv_table.c b/src/lv_objx/lv_table.c index 309fb9ee4..133fe4bbe 100644 --- a/src/lv_objx/lv_table.c +++ b/src/lv_objx/lv_table.c @@ -106,8 +106,8 @@ lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy) ext->cell_style[1] = copy_ext->cell_style[1]; ext->cell_style[2] = copy_ext->cell_style[2]; ext->cell_style[3] = copy_ext->cell_style[3]; - ext->col_cnt = copy_ext->col_cnt; - ext->row_cnt = copy_ext->row_cnt; + lv_table_set_row_cnt(new_table, copy_ext->row_cnt); + lv_table_set_col_cnt(new_table, copy_ext->col_cnt); /*Refresh the style with new signal function*/ lv_obj_refresh_style(new_table); @@ -752,6 +752,8 @@ static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param ext->cell_data[cell] = NULL; } } + if(ext->cell_data != NULL) + lv_mem_free(ext->cell_data); } else if(sign == LV_SIGNAL_GET_TYPE) { lv_obj_type_t * buf = param; uint8_t i; diff --git a/src/lv_themes/lv_theme_material.c b/src/lv_themes/lv_theme_material.c index 5cb57f287..935aca7cd 100644 --- a/src/lv_themes/lv_theme_material.c +++ b/src/lv_themes/lv_theme_material.c @@ -824,11 +824,10 @@ static void style_mod(lv_group_t * group, lv_style_t * style) static void style_mod_edit(lv_group_t * group, lv_style_t * style) { - - uint16_t hue2 = (_hue + 300) % 360; - (void)group; /*Unused*/ #if LV_COLOR_DEPTH != 1 + uint16_t hue2 = (_hue + 300) % 360; + /*Make the style to be a little bit orange*/ style->body.border.opa = LV_OPA_COVER; style->body.border.color = LV_COLOR_GREEN; diff --git a/src/lv_version.h b/src/lv_version.h index 745a7e595..c447d5620 100644 --- a/src/lv_version.h +++ b/src/lv_version.h @@ -16,7 +16,7 @@ extern "C" { /*Current version of LittlevGL*/ #define LVGL_VERSION_MAJOR 6 #define LVGL_VERSION_MINOR 0 -#define LVGL_VERSION_PATCH 0 +#define LVGL_VERSION_PATCH 2 #define LVGL_VERSION_INFO ""