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 ""