diff --git a/lvgl.h b/lvgl.h index 65aa8925a4..83c296f4f8 100644 --- a/lvgl.h +++ b/lvgl.h @@ -16,7 +16,7 @@ extern "C" { #define LVGL_VERSION_MAJOR 8 #define LVGL_VERSION_MINOR 0 #define LVGL_VERSION_PATCH 0 -#define LVGL_VERSION_INFO "dev" +#define LVGL_VERSION_INFO "" /********************* * INCLUDES diff --git a/src/lv_core/lv_refr.c b/src/lv_core/lv_refr.c index 034b6a1efb..c62938f12f 100644 --- a/src/lv_core/lv_refr.c +++ b/src/lv_core/lv_refr.c @@ -818,7 +818,7 @@ static void draw_buf_rotate_90_sqr(bool is_270, lv_coord_t w, lv_color_t * color &color_p[inv_j * w + i] ); } - + } } } @@ -848,7 +848,8 @@ static void draw_buf_rotate(lv_area_t *area, lv_color_t *color_p) { if(drv->rotated == LV_DISP_ROT_90) { area->y2 = drv->ver_res - area->x1 - 1; area->y1 = area->y2 - area_w + 1; - } else { + } + else { area->y1 = area->x1; area->y2 = area->y1 + area_w - 1; } @@ -864,20 +865,23 @@ static void draw_buf_rotate(lv_area_t *area, lv_color_t *color_p) { draw_buf_rotate_90_sqr(drv->rotated == LV_DISP_ROT_270, area_w, color_p); if(drv->rotated == LV_DISP_ROT_90) { area->x1 = init_y_off; - area->x2 = init_y_off+area_w-1; - } else { + area->x2 = init_y_off + area_w - 1; + } + else { area->x2 = drv->hor_res - 1 - init_y_off; area->x1 = area->x2 - area_w + 1; } - } else { + } + else { /*Rotate other areas using a maximum buffer size*/ if(rot_buf == NULL) rot_buf = lv_mem_buf_get(LV_DISP_ROT_MAX_BUF); draw_buf_rotate_90(drv->rotated == LV_DISP_ROT_270, area_w, height, color_p, rot_buf); if(drv->rotated == LV_DISP_ROT_90) { - area->x1 = init_y_off+row; - area->x2 = init_y_off+row+height-1; - } else { + area->x1 = init_y_off + row; + area->x2 = init_y_off + row + height - 1; + } + else { area->x2 = drv->hor_res - 1 - init_y_off - row; area->x1 = area->x2 - height + 1; } diff --git a/src/lv_core/lv_style.c b/src/lv_core/lv_style.c new file mode 100644 index 0000000000..387b4da885 --- /dev/null +++ b/src/lv_core/lv_style.c @@ -0,0 +1,1134 @@ +/** + * @file lv_style.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_style.h" +#include "../lv_misc/lv_mem.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +LV_ATTRIBUTE_FAST_MEM static inline int32_t get_property_index(const lv_style_t * style, lv_style_property_t prop); +static lv_style_t * get_alloc_local_style(lv_style_list_t * list); +static inline bool style_resize(lv_style_t * style, size_t sz); +static inline lv_style_property_t get_style_prop(const lv_style_t * style, size_t idx); +static inline uint8_t get_style_prop_id(const lv_style_t * style, size_t idx); +static inline uint8_t get_style_prop_attr(const lv_style_t * style, size_t idx); +static inline size_t get_prop_size(uint8_t prop_id); +static inline size_t get_next_prop_index(uint8_t prop_id, size_t id); + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize a style + * @param style pointer to a style to initialize + */ +void lv_style_init(lv_style_t * style) +{ + _lv_memset_00(style, sizeof(lv_style_t)); +#if LV_USE_ASSERT_STYLE + style->sentinel = LV_DEBUG_STYLE_SENTINEL_VALUE; +#endif +} + +/** + * Copy a style with all its properties + * @param style_dest pointer to the destination style. (Should be initialized with `lv_style_init()`) + * @param style_src pointer to the source (to copy )style + */ +void lv_style_copy(lv_style_t * style_dest, const lv_style_t * style_src) +{ + LV_ASSERT_STYLE(style_dest); + + uint16_t size = _lv_style_get_mem_size(style_src); + if(size == 0) return; + + style_dest->map = lv_mem_alloc(size); + if(style_dest->map) + _lv_memcpy(style_dest->map, style_src->map, size); +} + +/** + * Remove a property from a style + * @param style pointer to a style + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @return true: the property was found and removed; false: the property wasn't found + */ +bool lv_style_remove_prop(lv_style_t * style, lv_style_property_t prop) +{ + int32_t id = get_property_index(style, prop); + /*The property exists but not sure it's state is the same*/ + if(id >= 0) { + lv_style_attr_t attr_found; + lv_style_attr_t attr_goal; + + attr_found = get_style_prop_attr(style, id); + attr_goal = (prop >> 8) & 0xFFU; + + if(LV_STYLE_ATTR_GET_STATE(attr_found) == LV_STYLE_ATTR_GET_STATE(attr_goal)) { + uint32_t map_size = _lv_style_get_mem_size(style); + uint8_t prop_size = get_prop_size(prop); + + /*Move the props to fill the space of the property to delete*/ + uint32_t i; + for(i = id; i < map_size - prop_size; i++) { + style->map[i] = style->map[i + prop_size]; + } + + style_resize(style, map_size - prop_size); + + return true; + } + } + + return false; +} + +/** + * Initialize a style list + * @param list a style list to initialize + */ +void lv_style_list_init(lv_style_list_t * list) +{ + _lv_memset_00(list, sizeof(lv_style_list_t)); +#if LV_USE_ASSERT_STYLE + list->sentinel = LV_DEBUG_STYLE_LIST_SENTINEL_VALUE; +#endif +} + +/** + * Copy a style list with all its styles and local style properties + * @param list_dest pointer to the destination style list. (should be initialized with `lv_style_list_init()`) + * @param list_src pointer to the source (to copy) style list. + */ +void lv_style_list_copy(lv_style_list_t * list_dest, const lv_style_list_t * list_src) +{ + LV_ASSERT_STYLE_LIST(list_dest); + LV_ASSERT_STYLE_LIST(list_src); + + _lv_style_list_reset(list_dest); + + if(list_src == NULL || list_src->style_list == NULL) return; + + /*Copy the styles but skip the transitions*/ + if(list_src->has_local == 0) { + if(list_src->has_trans) { + list_dest->style_list = lv_mem_alloc((list_src->style_cnt - 1) * sizeof(lv_style_t *)); + if(list_dest->style_list) { + _lv_memcpy(list_dest->style_list, list_src->style_list + 1, (list_src->style_cnt - 1) * sizeof(lv_style_t *)); + list_dest->style_cnt = list_src->style_cnt - 1; + } + } + else { + list_dest->style_list = lv_mem_alloc(list_src->style_cnt * sizeof(lv_style_t *)); + if(list_dest->style_list) { + _lv_memcpy(list_dest->style_list, list_src->style_list, list_src->style_cnt * sizeof(lv_style_t *)); + list_dest->style_cnt = list_src->style_cnt; + } + } + } + else { + if(list_src->has_trans) { + list_dest->style_list = lv_mem_alloc((list_src->style_cnt - 2) * sizeof(lv_style_t *)); + if(list_dest->style_list) { + _lv_memcpy(list_dest->style_list, list_src->style_list + 2, (list_src->style_cnt - 2) * sizeof(lv_style_t *)); + list_dest->style_cnt = list_src->style_cnt - 2; + } + } + else { + list_dest->style_list = lv_mem_alloc((list_src->style_cnt - 1) * sizeof(lv_style_t *)); + if(list_dest->style_list) { + _lv_memcpy(list_dest->style_list, list_src->style_list + 1, (list_src->style_cnt - 1) * sizeof(lv_style_t *)); + list_dest->style_cnt = list_src->style_cnt - 1; + } + } + + lv_style_t * local_style = get_alloc_local_style(list_dest); + if(local_style) + lv_style_copy(local_style, get_alloc_local_style((lv_style_list_t *)list_src)); + } +} + +/** + * Add a style to a style list. + * Only the style pointer will be saved so the shouldn't be a local variable. + * (It should be static, global or dynamically allocated) + * @param list pointer to a style list + * @param style pointer to a style to add + */ +void _lv_style_list_add_style(lv_style_list_t * list, lv_style_t * style) +{ + LV_ASSERT_STYLE_LIST(list); + LV_ASSERT_STYLE(style); + + if(list == NULL) return; + + /*Remove the style first if already exists*/ + _lv_style_list_remove_style(list, style); + + lv_style_t ** new_styles; + if(list->style_cnt == 0) new_styles = lv_mem_alloc(sizeof(lv_style_t *)); + else new_styles = lv_mem_realloc(list->style_list, sizeof(lv_style_t *) * (list->style_cnt + 1)); + LV_ASSERT_MEM(new_styles); + if(new_styles == NULL) { + LV_LOG_WARN("lv_style_list_add_style: couldn't add the style"); + return; + } + + /*Make space for the new style at the beginning. Leave local and trans style if exists*/ + uint8_t i; + uint8_t first_style = 0; + if(list->has_trans) first_style++; + if(list->has_local) first_style++; + for(i = list->style_cnt; i > first_style; i--) { + new_styles[i] = new_styles[i - 1]; + } + + new_styles[first_style] = style; + list->style_cnt++; + list->style_list = new_styles; +} + +/** + * Remove a style from a style list + * @param style_list pointer to a style list + * @param style pointer to a style to remove + */ +void _lv_style_list_remove_style(lv_style_list_t * list, lv_style_t * style) +{ + LV_ASSERT_STYLE_LIST(list); + LV_ASSERT_STYLE(style); + + if(list->style_cnt == 0) return; + + /*Check if the style really exists here*/ + uint8_t i; + bool found = false; + for(i = 0; i < list->style_cnt; i++) { + if(list->style_list[i] == style) { + found = true; + break; + } + } + if(found == false) return; + + if(list->style_cnt == 1) { + lv_mem_free(list->style_list); + list->style_list = NULL; + list->style_cnt = 0; + list->has_local = 0; + return; + } + + lv_style_t ** new_styles = lv_mem_alloc(sizeof(lv_style_t *) * (list->style_cnt - 1)); + LV_ASSERT_MEM(new_styles); + if(new_styles == NULL) { + LV_LOG_WARN("lv_style_list_remove_style: couldn't reallocate style list"); + return; + } + uint8_t j; + for(i = 0, j = 0; i < list->style_cnt; i++) { + if(list->style_list[i] == style) continue; + new_styles[j++] = list->style_list[i]; + } + + lv_mem_free(list->style_list); + + list->style_cnt--; + list->style_list = new_styles; +} + +/** + * Remove all styles added from style list, clear the local style, transition style and free all allocated memories. + * Leave `ignore_trans` flag as it is. + * @param list pointer to a style list. + */ +void _lv_style_list_reset(lv_style_list_t * list) +{ + LV_ASSERT_STYLE_LIST(list); + + if(list == NULL) return; + + if(list->has_local) { + lv_style_t * local = lv_style_list_get_local_style(list); + if(local) { + lv_style_reset(local); + lv_mem_free(local); + } + } + + if(list->has_trans) { + lv_style_t * trans = _lv_style_list_get_transition_style(list); + if(trans) { + lv_style_reset(trans); + lv_mem_free(trans); + } + } + + if(list->style_cnt > 0) lv_mem_free(list->style_list); + list->style_list = NULL; + list->style_cnt = 0; + list->has_local = 0; + list->has_trans = 0; + list->skip_trans = 0; + + /* Intentionally leave `ignore_trans` as it is, + * because it's independent from the styles in the list*/ +} + +/** + * Clear all properties from a style and all allocated memories. + * @param style pointer to a style + */ +void lv_style_reset(lv_style_t * style) +{ + lv_mem_free(style->map); + lv_style_init(style); +} + +/** + * Get the size of the properties in a style in bytes + * @param style pointer to a style + * @return size of the properties in bytes + */ +uint16_t _lv_style_get_mem_size(const lv_style_t * style) +{ + LV_ASSERT_STYLE(style); + + if(style == NULL || style->map == NULL) return 0; + + size_t i = 0; + uint8_t prop_id; + while((prop_id = get_style_prop_id(style, i)) != _LV_STYLE_CLOSING_PROP) { + i = get_next_prop_index(prop_id, i); + } + + return i + sizeof(lv_style_property_t); +} + +/** + * Set an integer typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_width()` + * @note for performance reasons it's not checked if the property really has integer type + */ +void _lv_style_set_int(lv_style_t * style, lv_style_property_t prop, lv_style_int_t value) +{ + int32_t id = get_property_index(style, prop); + /*The property already exists but not sure it's state is the same*/ + if(id >= 0) { + lv_style_attr_t attr_found; + lv_style_attr_t attr_goal; + + attr_found = get_style_prop_attr(style, id); + attr_goal = (prop >> 8) & 0xFFU; + + if(LV_STYLE_ATTR_GET_STATE(attr_found) == LV_STYLE_ATTR_GET_STATE(attr_goal)) { + _lv_memcpy_small(style->map + id + sizeof(lv_style_property_t), &value, sizeof(lv_style_int_t)); + return; + } + } + + /*Add new property if not exists yet*/ + uint8_t new_prop_size = sizeof(lv_style_property_t) + sizeof(lv_style_int_t); + lv_style_property_t end_mark = _LV_STYLE_CLOSING_PROP; + uint8_t end_mark_size = sizeof(end_mark); + + uint16_t size = _lv_style_get_mem_size(style); + if(size == 0) size += end_mark_size; + + size += new_prop_size; + if(!style_resize(style, size)) return; + + _lv_memcpy_small(style->map + size - new_prop_size - end_mark_size, &prop, sizeof(lv_style_property_t)); + _lv_memcpy_small(style->map + size - sizeof(lv_style_int_t) - end_mark_size, &value, sizeof(lv_style_int_t)); + _lv_memcpy_small(style->map + size - end_mark_size, &end_mark, sizeof(end_mark)); +} + +/** + * Set a color typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_color()` + * @note for performance reasons it's not checked if the property really has color type + */ +void _lv_style_set_color(lv_style_t * style, lv_style_property_t prop, lv_color_t color) +{ + int32_t id = get_property_index(style, prop); + /*The property already exists but not sure it's state is the same*/ + if(id >= 0) { + lv_style_attr_t attr_found; + lv_style_attr_t attr_goal; + + attr_found = get_style_prop_attr(style, id); + attr_goal = (prop >> 8) & 0xFFU; + + if(LV_STYLE_ATTR_GET_STATE(attr_found) == LV_STYLE_ATTR_GET_STATE(attr_goal)) { + _lv_memcpy_small(style->map + id + sizeof(lv_style_property_t), &color, sizeof(lv_color_t)); + return; + } + } + + /*Add new property if not exists yet*/ + uint8_t new_prop_size = sizeof(lv_style_property_t) + sizeof(lv_color_t); + lv_style_property_t end_mark = _LV_STYLE_CLOSING_PROP; + uint8_t end_mark_size = sizeof(end_mark); + + uint16_t size = _lv_style_get_mem_size(style); + if(size == 0) size += end_mark_size; + + size += new_prop_size; + if(!style_resize(style, size)) return; + + _lv_memcpy_small(style->map + size - new_prop_size - end_mark_size, &prop, sizeof(lv_style_property_t)); + _lv_memcpy_small(style->map + size - sizeof(lv_color_t) - end_mark_size, &color, sizeof(lv_color_t)); + _lv_memcpy_small(style->map + size - end_mark_size, &end_mark, sizeof(end_mark)); +} + +/** + * Set an opacity typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_opa()` + * @note for performance reasons it's not checked if the property really has opacity type + */ +void _lv_style_set_opa(lv_style_t * style, lv_style_property_t prop, lv_opa_t opa) +{ + int32_t id = get_property_index(style, prop); + /*The property already exists but not sure it's state is the same*/ + if(id >= 0) { + lv_style_attr_t attr_found; + lv_style_attr_t attr_goal; + + attr_found = get_style_prop_attr(style, id); + attr_goal = (prop >> 8) & 0xFFU; + + if(LV_STYLE_ATTR_GET_STATE(attr_found) == LV_STYLE_ATTR_GET_STATE(attr_goal)) { + _lv_memcpy_small(style->map + id + sizeof(lv_style_property_t), &opa, sizeof(lv_opa_t)); + return; + } + } + + /*Add new property if not exists yet*/ + uint8_t new_prop_size = sizeof(lv_style_property_t) + sizeof(lv_opa_t); + lv_style_property_t end_mark = _LV_STYLE_CLOSING_PROP; + uint8_t end_mark_size = sizeof(end_mark); + + uint16_t size = _lv_style_get_mem_size(style); + if(size == 0) size += end_mark_size; + + size += new_prop_size; + if(!style_resize(style, size)) return; + + _lv_memcpy_small(style->map + size - new_prop_size - end_mark_size, &prop, sizeof(lv_style_property_t)); + _lv_memcpy_small(style->map + size - sizeof(lv_opa_t) - end_mark_size, &opa, sizeof(lv_opa_t)); + _lv_memcpy_small(style->map + size - end_mark_size, &end_mark, sizeof(end_mark)); +} + +/** + * Set a pointer typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_POINTER | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_width()` + * @note for performance reasons it's not checked if the property is really has pointer type + */ +void _lv_style_set_ptr(lv_style_t * style, lv_style_property_t prop, const void * p) +{ + int32_t id = get_property_index(style, prop); + /*The property already exists but not sure it's state is the same*/ + if(id >= 0) { + lv_style_attr_t attr_found; + lv_style_attr_t attr_goal; + + attr_found = get_style_prop_attr(style, id); + attr_goal = (prop >> 8) & 0xFFU; + + if(LV_STYLE_ATTR_GET_STATE(attr_found) == LV_STYLE_ATTR_GET_STATE(attr_goal)) { + _lv_memcpy_small(style->map + id + sizeof(lv_style_property_t), &p, sizeof(const void *)); + return; + } + } + + /*Add new property if not exists yet*/ + uint8_t new_prop_size = sizeof(lv_style_property_t) + sizeof(const void *); + lv_style_property_t end_mark = _LV_STYLE_CLOSING_PROP; + uint8_t end_mark_size = sizeof(end_mark); + + uint16_t size = _lv_style_get_mem_size(style); + if(size == 0) size += end_mark_size; + + size += new_prop_size; + if(!style_resize(style, size)) return; + + _lv_memcpy_small(style->map + size - new_prop_size - end_mark_size, &prop, sizeof(lv_style_property_t)); + _lv_memcpy_small(style->map + size - sizeof(const void *) - end_mark_size, &p, sizeof(const void *)); + _lv_memcpy_small(style->map + size - end_mark_size, &end_mark, sizeof(end_mark)); +} + +/** + * Get an integer typed property from a style. + * Take into account the style state and return the property which matches the best. + * @param style pointer to a style where to search + * @param prop the property, might contain ORed style states too + * @param res buffer to store the result + * @return the weight of the found property (how well it fits to the style state). + * Higher number is means better fit + * -1 if the not found (`res` will be undefined) + */ +int16_t _lv_style_get_int(const lv_style_t * style, lv_style_property_t prop, lv_style_int_t * res) +{ + int32_t id = get_property_index(style, prop); + if(id < 0) { + return -1; + } + else { + _lv_memcpy_small(res, &style->map[id + sizeof(lv_style_property_t)], sizeof(lv_style_int_t)); + lv_style_attr_t attr_act; + attr_act = get_style_prop_attr(style, id); + + return LV_STYLE_ATTR_GET_STATE(attr_act); + } +} + +/** + * Get an opacity typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_border_opa()` + * @note for performance reasons it's not checked if the property really has opacity type + */ +int16_t _lv_style_get_opa(const lv_style_t * style, lv_style_property_t prop, lv_opa_t * res) +{ + int32_t id = get_property_index(style, prop); + if(id < 0) { + return -1; + } + else { + _lv_memcpy_small(res, &style->map[id + sizeof(lv_style_property_t)], sizeof(lv_opa_t)); + lv_style_attr_t attr_act; + attr_act = get_style_prop_attr(style, id); + + return LV_STYLE_ATTR_GET_STATE(attr_act); + } +} + +/** + * Get a color typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_border_color()` + * @note for performance reasons it's not checked if the property really has color type + */ +int16_t _lv_style_get_color(const lv_style_t * style, lv_style_property_t prop, lv_color_t * res) +{ + int32_t id = get_property_index(style, prop); + if(id < 0) { + return -1; + } + else { + _lv_memcpy_small(res, &style->map[id + sizeof(lv_style_property_t)], sizeof(lv_color_t)); + lv_style_attr_t attr_act; + attr_act = get_style_prop_attr(style, id); + + return LV_STYLE_ATTR_GET_STATE(attr_act); + } +} + +/** + * Get a pointer typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_text_font()` + * @note for performance reasons it's not checked if the property really has pointer type + */ +int16_t _lv_style_get_ptr(const lv_style_t * style, lv_style_property_t prop, const void ** res) +{ + int32_t id = get_property_index(style, prop); + if(id < 0) { + return -1; + } + else { + _lv_memcpy_small(res, &style->map[id + sizeof(lv_style_property_t)], sizeof(const void *)); + lv_style_attr_t attr_act; + attr_act = get_style_prop_attr(style, id); + + return LV_STYLE_ATTR_GET_STATE(attr_act); + } +} + +/** + * Get the local style of a style list + * @param list pointer to a style list where the local property should be set + * @return pointer to the local style if exists else `NULL`. + */ +lv_style_t * lv_style_list_get_local_style(lv_style_list_t * list) +{ + LV_ASSERT_STYLE_LIST(list); + + if(!list->has_local) return NULL; + if(list->has_trans) return list->style_list[1]; + else return list->style_list[0]; +} + +/** + * Get the transition style of a style list + * @param list pointer to a style list where the local property should be set + * @return pointer to the transition style if exists else `NULL`. + */ +lv_style_t * _lv_style_list_get_transition_style(lv_style_list_t * list) +{ + LV_ASSERT_STYLE_LIST(list); + + if(!list->has_trans) return NULL; + return list->style_list[0]; +} + +/** + * Allocate the transition style in a style list. If already exists simply return it. + * @param list pointer to a style list + * @return the transition style of a style list + */ +lv_style_t * _lv_style_list_add_trans_style(lv_style_list_t * list) +{ + LV_ASSERT_STYLE_LIST(list); + if(list->has_trans) return _lv_style_list_get_transition_style(list); + + lv_style_t * trans_style = lv_mem_alloc(sizeof(lv_style_t)); + LV_ASSERT_MEM(trans_style); + if(trans_style == NULL) { + LV_LOG_WARN("lv_style_list_add_trans_style: couldn't create transition style"); + return NULL; + } + + lv_style_init(trans_style); + + _lv_style_list_add_style(list, trans_style); + list->has_trans = 1; + + /*If the list has local style trans was added after it. But trans should be the first so swap them*/ + if(list->has_local) { + lv_style_t * tmp = list->style_list[0]; + list->style_list[0] = list->style_list[1]; + list->style_list[1] = tmp; + } + return trans_style; +} + +/** + * Set a local integer typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has integer type + */ +void _lv_style_list_set_local_int(lv_style_list_t * list, lv_style_property_t prop, lv_style_int_t value) +{ + LV_ASSERT_STYLE_LIST(list); + + lv_style_t * local = get_alloc_local_style(list); + _lv_style_set_int(local, prop, value); +} + +/** + * Set a local opacity typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has opacity type + */ +void _lv_style_list_set_local_opa(lv_style_list_t * list, lv_style_property_t prop, lv_opa_t value) +{ + LV_ASSERT_STYLE_LIST(list); + + lv_style_t * local = get_alloc_local_style(list); + _lv_style_set_opa(local, prop, value); +} + +/** + * Set a local color typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has color type + */ +void _lv_style_list_set_local_color(lv_style_list_t * list, lv_style_property_t prop, lv_color_t value) +{ + LV_ASSERT_STYLE_LIST(list); + + lv_style_t * local = get_alloc_local_style(list); + _lv_style_set_color(local, prop, value); +} + +/** + * Set a local pointer typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has pointer type + */ +void _lv_style_list_set_local_ptr(lv_style_list_t * list, lv_style_property_t prop, const void * value) +{ + LV_ASSERT_STYLE_LIST(list); + + lv_style_t * local = get_alloc_local_style(list); + _lv_style_set_ptr(local, prop, value); +} + +/** + * Get an integer typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has integer type + */ +lv_res_t _lv_style_list_get_int(lv_style_list_t * list, lv_style_property_t prop, lv_style_int_t * res) +{ + LV_ASSERT_STYLE_LIST(list); + + if(list == NULL) return LV_RES_INV; + if(list->style_list == NULL) return LV_RES_INV; + + lv_style_attr_t attr; + attr = prop >> 8; + int16_t weight_goal = attr; + + int16_t weight = -1; + + lv_style_int_t value_act = 0; + + int16_t ci; + for(ci = 0; ci < list->style_cnt; ci++) { + lv_style_t * style = lv_style_list_get_style(list, ci); + int16_t weight_act = _lv_style_get_int(style, prop, &value_act); + + /*On perfect match return the value immediately*/ + if(weight_act == weight_goal) { + *res = value_act; + return LV_RES_OK; + } + else if(list->has_trans && weight_act >= 0 && ci == 0 && !list->skip_trans) { + *res = value_act; + return LV_RES_OK; + } + /*If the found ID is better the current candidate then use it*/ + else if(weight_act > weight) { + weight = weight_act; + *res = value_act; + } + } + + if(weight >= 0) return LV_RES_OK; + else return LV_RES_INV; +} + +/** + * Get a color typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has color type + */ +lv_res_t _lv_style_list_get_color(lv_style_list_t * list, lv_style_property_t prop, lv_color_t * res) +{ + LV_ASSERT_STYLE_LIST(list); + + if(list == NULL) return LV_RES_INV; + if(list->style_list == NULL) return LV_RES_INV; + + lv_style_attr_t attr; + attr = prop >> 8; + int16_t weight_goal = attr; + + int16_t weight = -1; + + lv_color_t value_act; + value_act.full = 0; + + int16_t ci; + for(ci = 0; ci < list->style_cnt; ci++) { + lv_style_t * style = lv_style_list_get_style(list, ci); + int16_t weight_act = _lv_style_get_color(style, prop, &value_act); + /*On perfect match return the value immediately*/ + if(weight_act == weight_goal) { + *res = value_act; + return LV_RES_OK; + } + else if(list->has_trans && weight_act >= 0 && ci == 0 && !list->skip_trans) { + *res = value_act; + return LV_RES_OK; + } + /*If the found ID is better the current candidate then use it*/ + else if(weight_act > weight) { + weight = weight_act; + *res = value_act; + } + } + + if(weight >= 0) return LV_RES_OK; + else return LV_RES_INV; +} + +/** + * Get an opacity typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA| (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has opacity type + */ +lv_res_t _lv_style_list_get_opa(lv_style_list_t * list, lv_style_property_t prop, lv_opa_t * res) +{ + LV_ASSERT_STYLE_LIST(list); + + if(list == NULL) return LV_RES_INV; + if(list->style_list == NULL) return LV_RES_INV; + + lv_style_attr_t attr; + attr = prop >> 8; + int16_t weight_goal = attr; + + int16_t weight = -1; + + lv_opa_t value_act = LV_OPA_TRANSP; + + int16_t ci; + for(ci = 0; ci < list->style_cnt; ci++) { + lv_style_t * style = lv_style_list_get_style(list, ci); + int16_t weight_act = _lv_style_get_opa(style, prop, &value_act); + /*On perfect match return the value immediately*/ + if(weight_act == weight_goal) { + *res = value_act; + return LV_RES_OK; + } + else if(list->has_trans && weight_act >= 0 && ci == 0 && !list->skip_trans) { + *res = value_act; + return LV_RES_OK; + } + /*If the found ID is better the current candidate then use it*/ + else if(weight_act > weight) { + weight = weight_act; + *res = value_act; + } + } + + if(weight >= 0) return LV_RES_OK; + else return LV_RES_INV; +} + +/** + * Get a pointer typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has pointer type + */ +lv_res_t _lv_style_list_get_ptr(lv_style_list_t * list, lv_style_property_t prop, const void ** res) +{ + LV_ASSERT_STYLE_LIST(list); + + if(list == NULL) return LV_RES_INV; + if(list->style_list == NULL) return LV_RES_INV; + + lv_style_attr_t attr; + attr = prop >> 8; + int16_t weight_goal = attr; + + int16_t weight = -1; + + const void * value_act; + + int16_t ci; + for(ci = 0; ci < list->style_cnt; ci++) { + lv_style_t * style = lv_style_list_get_style(list, ci); + int16_t weight_act = _lv_style_get_ptr(style, prop, &value_act); + /*On perfect match return the value immediately*/ + if(weight_act == weight_goal) { + *res = value_act; + return LV_RES_OK; + } + else if(list->has_trans && weight_act >= 0 && ci == 0 && !list->skip_trans) { + *res = value_act; + return LV_RES_OK; + } + /*If the found ID is better the current candidate then use it*/ + else if(weight_act > weight) { + weight = weight_act; + *res = value_act; + } + } + + if(weight >= 0) return LV_RES_OK; + else return LV_RES_INV; +} + +/** + * Check whether a style is valid (initialized correctly) + * @param style pointer to a style + * @return true: valid + */ +bool lv_debug_check_style(const lv_style_t * style) +{ + if(style == NULL) return true; /*NULL style is still valid*/ + +#if LV_USE_ASSERT_STYLE + if(style->sentinel != LV_DEBUG_STYLE_SENTINEL_VALUE) { + LV_LOG_WARN("Invalid style (local variable or not initialized?)"); + return false; + } +#endif + + return true; +} + +/** + * Check whether a style list is valid (initialized correctly) + * @param style pointer to a style + * @return true: valid + */ +bool lv_debug_check_style_list(const lv_style_list_t * list) +{ + if(list == NULL) return true; /*NULL list is still valid*/ + +#if LV_USE_ASSERT_STYLE + if(list->sentinel != LV_DEBUG_STYLE_LIST_SENTINEL_VALUE) { + LV_LOG_WARN("Invalid style (local variable or not initialized?)"); + return false; + } +#endif + + return true; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get a property's index (byte index in `style->map`) from a style. + * Return best matching property's index considering the state of `prop` + * @param style pointer to a style + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @return + */ +LV_ATTRIBUTE_FAST_MEM static inline int32_t get_property_index(const lv_style_t * style, lv_style_property_t prop) +{ + LV_ASSERT_STYLE(style); + + if(style == NULL) return -1; + if(style->map == NULL) return -1; + + uint8_t id_to_find = prop & 0xFF; + lv_style_attr_t attr; + attr = (prop >> 8) & 0xFF; + + int16_t weight = -1; + int16_t id_guess = -1; + + size_t i = 0; + + uint8_t prop_id; + while((prop_id = get_style_prop_id(style, i)) != _LV_STYLE_CLOSING_PROP) { + if(prop_id == id_to_find) { + lv_style_attr_t attr_i; + attr_i = get_style_prop_attr(style, i); + + /*If the state perfectly matches return this property*/ + if(LV_STYLE_ATTR_GET_STATE(attr_i) == LV_STYLE_ATTR_GET_STATE(attr)) { + return i; + } + /* Be sure the property not specifies other state than the requested. + * E.g. For HOVER+PRESS, HOVER only is OK, but HOVER+FOCUS not*/ + else if((LV_STYLE_ATTR_GET_STATE(attr_i) & (~LV_STYLE_ATTR_GET_STATE(attr))) == 0) { + /* Use this property if it describes better the requested state than the current candidate. + * E.g. for HOVER+FOCUS+PRESS prefer HOVER+FOCUS over FOCUS*/ + if(LV_STYLE_ATTR_GET_STATE(attr_i) > weight) { + weight = LV_STYLE_ATTR_GET_STATE(attr_i); + id_guess = i; + } + } + } + + i = get_next_prop_index(prop_id, i); + } + + return id_guess; +} + +/** + * Get the local style from a style list. Allocate it if not exists yet. + * @param list pointer to a style list + * @return pointer to the local style + */ +static lv_style_t * get_alloc_local_style(lv_style_list_t * list) +{ + LV_ASSERT_STYLE_LIST(list); + + if(list->has_local) return lv_style_list_get_style(list, list->has_trans ? 1 : 0); + + lv_style_t * local_style = lv_mem_alloc(sizeof(lv_style_t)); + LV_ASSERT_MEM(local_style); + if(local_style == NULL) { + LV_LOG_WARN("get_local_style: couldn't create local style"); + return NULL; + } + lv_style_init(local_style); + + /*Add the local style to the first place*/ + _lv_style_list_add_style(list, local_style); + list->has_local = 1; + + return local_style; +} + +/** + * Resizes a style map. Useful entry point for debugging. + * @param style pointer to the style to be resized. + * @param size new size + */ +static inline bool style_resize(lv_style_t * style, size_t sz) +{ + uint8_t * new_map = lv_mem_realloc(style->map, sz); + if(sz && new_map == NULL) return false; + style->map = new_map; + return true; +} + +/** + * Get style property in index. + * @param style pointer to style. + * @param idx index of the style in style->map + * @return property in style->map + idx + */ +static inline lv_style_property_t get_style_prop(const lv_style_t * style, size_t idx) +{ + lv_style_property_t prop; + _lv_memcpy_small(&prop, &style->map[idx], sizeof(lv_style_property_t)); + return prop; +} + +/** + * Get style property id in index. + * @param style pointer to style. + * @param idx index of the style in style->map + * @return id of property in style->map + idx + */ +static inline uint8_t get_style_prop_id(const lv_style_t * style, size_t idx) +{ + return get_style_prop(style, idx) & 0xFF; +} + +/** + * Get style property attributes for index. + * @param style pointer to style. + * @param idx index of the style in style->map + * @return attribute of property in style->map + idx + */ +static inline uint8_t get_style_prop_attr(const lv_style_t * style, size_t idx) +{ + return ((get_style_prop(style, idx) >> 8) & 0xFFU); +} + +/** + * Get property size. + * @param prop_id property id. + * @return size of property + */ +static inline size_t get_prop_size(uint8_t prop_id) +{ + prop_id &= 0xF; + size_t size = sizeof(lv_style_property_t); + if(prop_id < LV_STYLE_ID_COLOR) size += sizeof(lv_style_int_t); + else if(prop_id < LV_STYLE_ID_OPA) size += sizeof(lv_color_t); + else if(prop_id < LV_STYLE_ID_PTR) size += sizeof(lv_opa_t); + else size += sizeof(const void *); + return size; +} + +/** + * Get next property index, given current property and index. + * @param prop_id property id. + * @param idx index of the style in style->map + * @return index of next property in style->map + */ +static inline size_t get_next_prop_index(uint8_t prop_id, size_t idx) +{ + return idx + get_prop_size(prop_id); +} diff --git a/src/lv_core/lv_style.h b/src/lv_core/lv_style.h new file mode 100644 index 0000000000..3ac80eb37a --- /dev/null +++ b/src/lv_core/lv_style.h @@ -0,0 +1,632 @@ +/** + * @file lv_style.h + * + */ + +#ifndef LV_STYLE_H +#define LV_STYLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include "../lv_font/lv_font.h" +#include "../lv_misc/lv_color.h" +#include "../lv_misc/lv_area.h" +#include "../lv_misc/lv_anim.h" +#include "../lv_misc/lv_types.h" +#include "../lv_misc/lv_debug.h" +#include "../lv_draw/lv_draw_blend.h" + +/********************* + * DEFINES + *********************/ + +#define LV_RADIUS_CIRCLE (0x7FFF) /**< A very big radius to always draw as circle*/ +LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE); + +#define LV_DEBUG_STYLE_SENTINEL_VALUE 0x2288AAEE +#define LV_DEBUG_STYLE_LIST_SENTINEL_VALUE 0x9977CCBB + +#define LV_STYLE_PROP_INIT(name, group, id, attr) name = (((group << 4) + id) | ((attr) << 8)) + +#define LV_STYLE_ID_MASK 0x00FF + +#define LV_STYLE_ATTR_NONE 0 +#define LV_STYLE_ATTR_INHERIT (1 << 7) + +#define _LV_STYLE_CLOSING_PROP 0xFF + +#define LV_STYLE_TRANS_NUM_MAX 6 + +#define LV_STYLE_PROP_ALL 0xFF + +/********************** + * TYPEDEFS + **********************/ + +/*Border types (Use 'OR'ed values)*/ +enum { + LV_BORDER_SIDE_NONE = 0x00, + LV_BORDER_SIDE_BOTTOM = 0x01, + LV_BORDER_SIDE_TOP = 0x02, + LV_BORDER_SIDE_LEFT = 0x04, + LV_BORDER_SIDE_RIGHT = 0x08, + LV_BORDER_SIDE_FULL = 0x0F, + LV_BORDER_SIDE_INTERNAL = 0x10, /**< FOR matrix-like objects (e.g. Button matrix)*/ + _LV_BORDER_SIDE_LAST +}; +typedef uint8_t lv_border_side_t; + +enum { + LV_GRAD_DIR_NONE, + LV_GRAD_DIR_VER, + LV_GRAD_DIR_HOR, + _LV_GRAD_DIR_LAST +}; + +typedef uint8_t lv_grad_dir_t; + +/*Text decorations (Use 'OR'ed values)*/ +enum { + LV_TEXT_DECOR_NONE = 0x00, + LV_TEXT_DECOR_UNDERLINE = 0x01, + LV_TEXT_DECOR_STRIKETHROUGH = 0x02, + _LV_TEXT_DECOR_LAST +}; + +typedef uint8_t lv_text_decor_t; + +typedef uint8_t lv_style_attr_t; + +#define LV_STYLE_ATTR_GET_INHERIT(f) ((f)&0x80) +#define LV_STYLE_ATTR_GET_STATE(f) ((f)&0x7F) + +#define LV_STYLE_ID_VALUE 0x0 /*max 9 pcs*/ +#define LV_STYLE_ID_COLOR 0x9 /*max 3 pcs*/ +#define LV_STYLE_ID_OPA 0xC /*max 2 pcs*/ +#define LV_STYLE_ID_PTR 0xE /*max 2 pcs*/ + +enum { + /*Skip 0th property*/ + LV_STYLE_PROP_INIT(LV_STYLE_RADIUS, 0x0, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_CLIP_CORNER, 0x0, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SIZE, 0x0, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSFORM_WIDTH, 0x0, LV_STYLE_ID_VALUE + 4, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSFORM_HEIGHT, 0x0, LV_STYLE_ID_VALUE + 5, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSFORM_ANGLE, 0x0, LV_STYLE_ID_VALUE + 6, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSFORM_ZOOM, 0x0, LV_STYLE_ID_VALUE + 7, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_OPA_SCALE, 0x0, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_INHERIT), + + LV_STYLE_PROP_INIT(LV_STYLE_PAD_TOP, 0x1, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PAD_BOTTOM, 0x1, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PAD_LEFT, 0x1, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PAD_RIGHT, 0x1, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PAD_INNER, 0x1, LV_STYLE_ID_VALUE + 4, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_MARGIN_TOP, 0x1, LV_STYLE_ID_VALUE + 5, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_MARGIN_BOTTOM, 0x1, LV_STYLE_ID_VALUE + 6, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_MARGIN_LEFT, 0x1, LV_STYLE_ID_VALUE + 7, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_MARGIN_RIGHT, 0x1, LV_STYLE_ID_VALUE + 8, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_BG_BLEND_MODE, 0x2, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BG_MAIN_STOP, 0x2, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BG_GRAD_STOP, 0x2, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BG_GRAD_DIR, 0x2, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BG_COLOR, 0x2, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BG_GRAD_COLOR, 0x2, LV_STYLE_ID_COLOR + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BG_OPA, 0x2, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_BORDER_WIDTH, 0x3, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BORDER_SIDE, 0x3, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BORDER_BLEND_MODE, 0x3, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BORDER_POST, 0x3, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BORDER_COLOR, 0x3, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_BORDER_OPA, 0x3, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_OUTLINE_WIDTH, 0x4, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_OUTLINE_PAD, 0x4, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_OUTLINE_BLEND_MODE, 0x4, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_OUTLINE_COLOR, 0x4, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_OUTLINE_OPA, 0x4, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_WIDTH, 0x5, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_OFS_X, 0x5, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_OFS_Y, 0x5, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_SPREAD, 0x5, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_BLEND_MODE, 0x5, LV_STYLE_ID_VALUE + 4, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_COLOR, 0x5, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SHADOW_OPA, 0x5, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_PATTERN_BLEND_MODE, 0x6, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PATTERN_REPEAT, 0x6, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PATTERN_RECOLOR, 0x6, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PATTERN_OPA, 0x6, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PATTERN_RECOLOR_OPA, 0x6, LV_STYLE_ID_OPA + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_PATTERN_IMAGE, 0x6, LV_STYLE_ID_PTR + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_LETTER_SPACE, 0x7, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_LINE_SPACE, 0x7, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_BLEND_MODE, 0x7, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_OFS_X, 0x7, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_OFS_Y, 0x7, LV_STYLE_ID_VALUE + 4, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_ALIGN, 0x7, LV_STYLE_ID_VALUE + 5, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_COLOR, 0x7, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_OPA, 0x7, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_FONT, 0x7, LV_STYLE_ID_PTR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_VALUE_STR, 0x7, LV_STYLE_ID_PTR + 1, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_LETTER_SPACE, 0x8, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_LINE_SPACE, 0x8, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_DECOR, 0x8, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_BLEND_MODE, 0x8, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_COLOR, 0x8, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_SEL_COLOR, 0x8, LV_STYLE_ID_COLOR + 1, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_SEL_BG_COLOR, 0x8, LV_STYLE_ID_COLOR + 2, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_OPA, 0x8, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_TEXT_FONT, 0x8, LV_STYLE_ID_PTR + 0, LV_STYLE_ATTR_INHERIT), + + LV_STYLE_PROP_INIT(LV_STYLE_LINE_WIDTH, 0x9, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_LINE_BLEND_MODE, 0x9, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_LINE_DASH_WIDTH, 0x9, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_LINE_DASH_GAP, 0x9, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_LINE_ROUNDED, 0x9, LV_STYLE_ID_VALUE + 4, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_LINE_COLOR, 0x9, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_LINE_OPA, 0x9, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_IMAGE_BLEND_MODE, 0xA, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_IMAGE_RECOLOR, 0xA, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_IMAGE_OPA, 0xA, LV_STYLE_ID_OPA + 0, LV_STYLE_ATTR_INHERIT), + LV_STYLE_PROP_INIT(LV_STYLE_IMAGE_RECOLOR_OPA, 0xA, LV_STYLE_ID_OPA + 1, LV_STYLE_ATTR_INHERIT), + + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_TIME, 0xB, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_DELAY, 0xB, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PROP_1, 0xB, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PROP_2, 0xB, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PROP_3, 0xB, LV_STYLE_ID_VALUE + 4, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PROP_4, 0xB, LV_STYLE_ID_VALUE + 5, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PROP_5, 0xB, LV_STYLE_ID_VALUE + 6, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PROP_6, 0xB, LV_STYLE_ID_VALUE + 7, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_TRANSITION_PATH, 0xB, LV_STYLE_ID_PTR + 0, LV_STYLE_ATTR_NONE), + + LV_STYLE_PROP_INIT(LV_STYLE_SCALE_WIDTH, 0xC, LV_STYLE_ID_VALUE + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SCALE_BORDER_WIDTH, 0xC, LV_STYLE_ID_VALUE + 1, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SCALE_END_BORDER_WIDTH, 0xC, LV_STYLE_ID_VALUE + 2, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SCALE_END_LINE_WIDTH, 0xC, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SCALE_GRAD_COLOR, 0xC, LV_STYLE_ID_COLOR + 0, LV_STYLE_ATTR_NONE), + LV_STYLE_PROP_INIT(LV_STYLE_SCALE_END_COLOR, 0xC, LV_STYLE_ID_COLOR + 1, LV_STYLE_ATTR_NONE), +}; + +typedef uint16_t lv_style_property_t; + +#define LV_STYLE_STATE_POS 8 +#define LV_STYLE_STATE_MASK 0x7F00 +#define LV_STYLE_INHERIT_MASK 0x8000 + +typedef uint16_t lv_style_state_t; + +typedef struct { + uint8_t * map; +#if LV_USE_ASSERT_STYLE + uint32_t sentinel; +#endif +} lv_style_t; + +typedef int16_t lv_style_int_t; + +typedef struct { + lv_style_t ** style_list; +#if LV_USE_ASSERT_STYLE + uint32_t sentinel; +#endif + uint32_t style_cnt : 6; + uint32_t has_local : 1; + uint32_t has_trans : 1; + uint32_t skip_trans : 1; /*1: Temporally skip the transition style if any*/ + uint32_t ignore_trans : 1; /*1: Mark that this style list shouldn't receive transitions at all*/ + uint32_t valid_cache : 1; /*1: The cache is valid and can be used*/ + uint32_t ignore_cache : 1; /*1: Ignore cache while getting value of properties*/ + + uint32_t radius_zero : 1; + uint32_t opa_scale_cover : 1; + uint32_t clip_corner_off : 1; + uint32_t transform_all_zero : 1; + uint32_t pad_all_zero : 1; + uint32_t margin_all_zero : 1; + uint32_t blend_mode_all_normal : 1; + uint32_t bg_opa_transp : 1; + uint32_t bg_opa_cover : 1; + + uint32_t border_width_zero : 1; + uint32_t border_side_full : 1; + uint32_t border_post_off : 1; + + uint32_t outline_width_zero : 1; + uint32_t pattern_img_null : 1; + uint32_t shadow_width_zero : 1; + uint32_t value_txt_str : 1; + uint32_t img_recolor_opa_transp : 1; + + uint32_t text_space_zero : 1; + uint32_t text_decor_none : 1; + uint32_t text_font_normal : 1; +} lv_style_list_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize a style + * @param style pointer to a style to initialize + */ +void lv_style_init(lv_style_t * style); + +/** + * Copy a style with all its properties + * @param style_dest pointer to the destination style. (Should be initialized with `lv_style_init()`) + * @param style_src pointer to the source (to copy )style + */ +void lv_style_copy(lv_style_t * style_dest, const lv_style_t * style_src); + +/** + * Initialize a style list + * @param list a style list to initialize + */ +void lv_style_list_init(lv_style_list_t * list); + +/** + * Copy a style list with all its styles and local style properties + * @param list_dest pointer to the destination style list. (should be initialized with `lv_style_list_init()`) + * @param list_src pointer to the source (to copy) style list. + */ +void lv_style_list_copy(lv_style_list_t * list_dest, const lv_style_list_t * list_src); + +/** + * Add a style to a style list. + * Only the style pointer will be saved so the shouldn't be a local variable. + * (It should be static, global or dynamically allocated) + * @param list pointer to a style list + * @param style pointer to a style to add + */ +void _lv_style_list_add_style(lv_style_list_t * list, lv_style_t * style); + +/** + * Remove a style from a style list + * @param style_list pointer to a style list + * @param style pointer to a style to remove + */ +void _lv_style_list_remove_style(lv_style_list_t * list, lv_style_t * style); + +/** + * Remove all styles added from style list, clear the local style, transition style and free all allocated memories. + * Leave `ignore_trans` flag as it is. + * @param list pointer to a style list. + */ +void _lv_style_list_reset(lv_style_list_t * style_list); + +static inline lv_style_t * lv_style_list_get_style(lv_style_list_t * list, uint8_t id) +{ + if(list->has_trans && list->skip_trans) id++; + if(list->style_cnt == 0 || id >= list->style_cnt) return NULL; + return list->style_list[id]; +} + +/** + * Clear all properties from a style and all allocated memories. + * @param style pointer to a style + */ +void lv_style_reset(lv_style_t * style); + +/** + * Get the size of the properties in a style in bytes + * @param style pointer to a style + * @return size of the properties in bytes + */ +uint16_t _lv_style_get_mem_size(const lv_style_t * style); + +/** + * Copy a style to an other + * @param dest pointer to the destination style + * @param src pointer to the source style + */ +void lv_style_copy(lv_style_t * dest, const lv_style_t * src); + +/** + * Remove a property from a style + * @param style pointer to a style + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @return true: the property was found and removed; false: the property wasn't found + */ +bool lv_style_remove_prop(lv_style_t * style, lv_style_property_t prop); + +/** + * Set an integer typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_width()` + * @note for performance reasons it's not checked if the property really has integer type + */ +void _lv_style_set_int(lv_style_t * style, lv_style_property_t prop, lv_style_int_t value); + +/** + * Set a color typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_color()` + * @note for performance reasons it's not checked if the property really has color type + */ +void _lv_style_set_color(lv_style_t * style, lv_style_property_t prop, lv_color_t color); + +/** + * Set an opacity typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_opa()` + * @note for performance reasons it's not checked if the property really has opacity type + */ +void _lv_style_set_opa(lv_style_t * style, lv_style_property_t prop, lv_opa_t opa); + +/** + * Set a pointer typed property in a style. + * @param style pointer to a style where the property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_POINTER | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note shouldn't be used directly. Use the specific property set functions instead. + * For example: `lv_style_set_border_width()` + * @note for performance reasons it's not checked if the property really has pointer type + */ +void _lv_style_set_ptr(lv_style_t * style, lv_style_property_t prop, const void * p); + +/** + * Get an integer typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_border_width()` + * @note for performance reasons it's not checked if the property really has integer type + */ +int16_t _lv_style_get_int(const lv_style_t * style, lv_style_property_t prop, lv_style_int_t * res); + +/** + * Get a color typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_border_color()` + * @note for performance reasons it's not checked if the property really has color type + */ +int16_t _lv_style_get_color(const lv_style_t * style, lv_style_property_t prop, lv_color_t * res); + +/** + * Get an opacity typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_border_opa()` + * @note for performance reasons it's not checked if the property really has opacity type + */ +int16_t _lv_style_get_opa(const lv_style_t * style, lv_style_property_t prop, lv_opa_t * res); + +/** + * Get a pointer typed property from a style. + * @param style pointer to a style from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result value + * @return -1: the property wasn't found in the style. + * The matching state bits of the desired state (in `prop`) and the best matching property's state + * Higher value means match in higher precedence state. + * @note shouldn't be used directly. Use the specific property get functions instead. + * For example: `lv_style_get_text_font()` + * @note for performance reasons it's not checked if the property really has pointer type + */ +int16_t _lv_style_get_ptr(const lv_style_t * style, lv_style_property_t prop, const void ** res); + +/** + * Get the local style of a style list + * @param list pointer to a style list where the local property should be set + * @return pointer to the local style if exists else `NULL`. + */ +lv_style_t * lv_style_list_get_local_style(lv_style_list_t * list); + +/** + * Get the transition style of a style list + * @param list pointer to a style list where the transition property should be set + * @return pointer to the transition style if exists else `NULL`. + */ +lv_style_t * _lv_style_list_get_transition_style(lv_style_list_t * list); + +/** + * Allocate the transition style in a style list. If already exists simply return it. + * @param list pointer to a style list + * @return the transition style of a style list + */ +lv_style_t * _lv_style_list_add_trans_style(lv_style_list_t * list); + +/** + * Set a local integer typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has integer type + */ +void _lv_style_list_set_local_int(lv_style_list_t * list, lv_style_property_t prop, lv_style_int_t value); + +/** + * Set a local color typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has color type + */ +void _lv_style_list_set_local_color(lv_style_list_t * list, lv_style_property_t prop, lv_color_t value); + +/** + * Set a local opacity typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has opacity type + */ +void _lv_style_list_set_local_opa(lv_style_list_t * list, lv_style_property_t prop, lv_opa_t value); + +/** + * Set a local pointer typed property in a style list. + * @param list pointer to a style list where the local property should be set + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param value the value to set + * @note for performance reasons it's not checked if the property really has pointer type + */ +void _lv_style_list_set_local_ptr(lv_style_list_t * list, lv_style_property_t prop, const void * value); + +/** + * Get an integer typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has integer type + */ +lv_res_t _lv_style_list_get_int(lv_style_list_t * list, lv_style_property_t prop, lv_style_int_t * res); + +/** + * Get a color typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has color type + */ +lv_res_t _lv_style_list_get_color(lv_style_list_t * list, lv_style_property_t prop, lv_color_t * res); + +/** + * Get an opacity typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has opacity type + */ +lv_res_t _lv_style_list_get_opa(lv_style_list_t * list, lv_style_property_t prop, lv_opa_t * res); + +/** + * Get a pointer typed property from a style list. + * It will return the property which match best with given state. + * @param list pointer to a style list from where the property should be get + * @param prop a style property ORed with a state. + * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)` + * @param res pointer to a buffer to store the result + * @return LV_RES_OK: there was a matching property in the list + * LV_RES_INV: there was NO matching property in the list + * @note for performance reasons it's not checked if the property really has pointer type + */ +lv_res_t _lv_style_list_get_ptr(lv_style_list_t * list, lv_style_property_t prop, const void ** res); + +/** + * Check whether a style is valid (initialized correctly) + * @param style pointer to a style + * @return true: valid + */ +bool lv_debug_check_style(const lv_style_t * style); + +/** + * Check whether a style list is valid (initialized correctly) + * @param list pointer to a style list + * @return true: valid + */ +bool lv_debug_check_style_list(const lv_style_list_t * list); + +/************************* + * GLOBAL VARIABLES + *************************/ + +/********************** + * MACROS + **********************/ + +/** + * Create and initialize a `static` style + * Example: + * LV_STYLE_CREATE(my_style, &style_to_copy); + * is equivalent to + * static lv_style_t my_style; + * lv_style_init(&my_style); + * lv_style_copy(&my_style, &style_to_copy); + */ +#define LV_STYLE_CREATE(name, copy_p) static lv_style_t name; lv_style_init(&name); lv_style_copy(&name, copy_p) + +#if LV_USE_DEBUG + +# ifndef LV_DEBUG_IS_STYLE +# define LV_DEBUG_IS_STYLE(style_p) (lv_debug_check_style(style_p)) +# endif + +# ifndef LV_DEBUG_IS_STYLE_LIST +# define LV_DEBUG_IS_STYLE_LIST(list_p) (lv_debug_check_style_list(list_p)) +# endif + +# if LV_USE_ASSERT_STYLE +# ifndef LV_ASSERT_STYLE +# define LV_ASSERT_STYLE(style_p) LV_DEBUG_ASSERT(LV_DEBUG_IS_STYLE(style_p), "Invalid style", style_p); +# endif +# ifndef LV_ASSERT_STYLE_LIST +# define LV_ASSERT_STYLE_LIST(list_p) LV_DEBUG_ASSERT(LV_DEBUG_IS_STYLE_LIST(list_p), "Invalid style list", list_p); +# endif +# else +# define LV_ASSERT_STYLE(style_p) +# define LV_ASSERT_STYLE_LIST(list_p) +# endif + +#else +# define LV_ASSERT_STYLE(p) +# define LV_ASSERT_STYLE_LIST(p) +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_STYLE_H*/ diff --git a/src/lv_draw/lv_img_cache.c b/src/lv_draw/lv_img_cache.c index 83c09e22f9..c5f1481fa1 100644 --- a/src/lv_draw/lv_img_cache.c +++ b/src/lv_draw/lv_img_cache.c @@ -34,7 +34,7 @@ * STATIC PROTOTYPES **********************/ #if LV_IMG_CACHE_DEF_SIZE -static bool lv_img_cache_match(const void * src1, const void * src2); + static bool lv_img_cache_match(const void * src1, const void * src2); #endif /********************** diff --git a/src/lv_misc/lv_anim.c b/src/lv_misc/lv_anim.c index 4cba2ac225..b371065a25 100644 --- a/src/lv_misc/lv_anim.c +++ b/src/lv_misc/lv_anim.c @@ -182,6 +182,15 @@ bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb) return del; } +/** + * Delete all the animations animation + */ +void lv_anim_del_all(void) +{ + _lv_ll_clear(&LV_GC_ROOT(_lv_anim_ll)); + anim_mark_list_change(); +} + /** * Get the animation of a variable and its `exec_cb`. * @param var pointer to variable diff --git a/src/lv_misc/lv_anim.h b/src/lv_misc/lv_anim.h index 8f5f06ba3b..d3a6a93daf 100644 --- a/src/lv_misc/lv_anim.h +++ b/src/lv_misc/lv_anim.h @@ -323,6 +323,11 @@ static inline uint32_t lv_anim_get_delay(lv_anim_t * a) */ bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb); +/** + * Delete all the animations animation + */ +void lv_anim_del_all(void); + /** * Get the animation of a variable and its `exec_cb`. * @param var pointer to variable diff --git a/src/lv_widgets/lv_bar.c b/src/lv_widgets/lv_bar.c index a1e1a00af5..99d5583bb7 100644 --- a/src/lv_widgets/lv_bar.c +++ b/src/lv_widgets/lv_bar.c @@ -383,7 +383,8 @@ static void draw_indic(lv_obj_t * obj, const lv_area_t * clip_area) *axis1 = *axis2; *axis2 = zero; } - } else { + } + else { zero = *axis2 - shift + 1; if(*axis1 > zero) *axis2 = zero; diff --git a/src/lv_widgets/lv_label.c b/src/lv_widgets/lv_label.c index b8d8eba3da..b43ec3c665 100644 --- a/src/lv_widgets/lv_label.c +++ b/src/lv_widgets/lv_label.c @@ -921,7 +921,7 @@ static void lv_label_refr_text(lv_obj_t * obj) if (base_dir == LV_BIDI_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(label->text); - if (base_dir == LV_BIDI_DIR_RTL) { + if(base_dir == LV_BIDI_DIR_RTL) { start = lv_area_get_width(&txt_coords) - size.x; end = 0; } @@ -1019,7 +1019,7 @@ static void lv_label_refr_text(lv_obj_t * obj) if (base_dir == LV_BIDI_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(label->text); - if (base_dir == LV_BIDI_DIR_RTL) { + if(base_dir == LV_BIDI_DIR_RTL) { start = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT; end = 0; } diff --git a/src/lv_widgets/lv_led.c b/src/lv_widgets/lv_led.c new file mode 100644 index 0000000000..1a16529a48 --- /dev/null +++ b/src/lv_widgets/lv_led.c @@ -0,0 +1,244 @@ +/** + * @file lv_led.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_led.h" +#if LV_USE_LED != 0 + +#include "../lv_misc/lv_debug.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_draw/lv_draw.h" + +/********************* + * DEFINES + *********************/ +#define LV_OBJX_NAME "lv_led" + +#define LV_LED_WIDTH_DEF (LV_DPI / 3) +#define LV_LED_HEIGHT_DEF (LV_DPI / 3) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_design_res_t lv_led_design(lv_obj_t * led, const lv_area_t * clip_area, lv_design_mode_t mode); +static lv_res_t lv_led_signal(lv_obj_t * led, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_cb_t ancestor_design; +static lv_signal_cb_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a led objects + * @param par pointer to an object, it will be the parent of the new led + * @param copy pointer to a led object, if not NULL then the new object will be copied from it + * @return pointer to the created led + */ +lv_obj_t * lv_led_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("led create started"); + + /*Create the ancestor basic object*/ + lv_obj_t * led = lv_obj_create(par, copy); + LV_ASSERT_MEM(led); + if(led == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(led); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(led); + + /*Allocate the object type specific extended data*/ + lv_led_ext_t * ext = lv_obj_allocate_ext_attr(led, sizeof(lv_led_ext_t)); + LV_ASSERT_MEM(ext); + if(ext == NULL) { + lv_obj_del(led); + return NULL; + } + + ext->bright = LV_LED_BRIGHT_MAX; + + lv_obj_set_signal_cb(led, lv_led_signal); + lv_obj_set_design_cb(led, lv_led_design); + + /*Init the new led object*/ + if(copy == NULL) { + lv_obj_set_size(led, LV_LED_WIDTH_DEF, LV_LED_HEIGHT_DEF); + + lv_theme_apply(led, LV_THEME_LED); + } + /*Copy an existing object*/ + else { + lv_led_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->bright = copy_ext->bright; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(led, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL); + } + + LV_LOG_INFO("led created"); + + return led; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the brightness of a LED object + * @param led pointer to a LED object + * @param bright LV_LED_BRIGHT_MIN (max. dark) ... LV_LED_BRIGHT_MAX (max. light) + */ +void lv_led_set_bright(lv_obj_t * led, uint8_t bright) +{ + LV_ASSERT_OBJ(led, LV_OBJX_NAME); + + /*Set the brightness*/ + lv_led_ext_t * ext = lv_obj_get_ext_attr(led); + if(ext->bright == bright) return; + + if(bright <= LV_LED_BRIGHT_MIN) bright = LV_LED_BRIGHT_MIN; + if(bright >= LV_LED_BRIGHT_MAX) bright = LV_LED_BRIGHT_MAX; + + ext->bright = bright; + + /*Invalidate the object there fore it will be redrawn*/ + lv_obj_invalidate(led); +} + +/** + * Light on a LED + * @param led pointer to a LED object + */ +void lv_led_on(lv_obj_t * led) +{ + LV_ASSERT_OBJ(led, LV_OBJX_NAME); + + lv_led_set_bright(led, LV_LED_BRIGHT_MAX); +} + +/** + * Light off a LED + * @param led pointer to a LED object + */ +void lv_led_off(lv_obj_t * led) +{ + LV_ASSERT_OBJ(led, LV_OBJX_NAME); + + lv_led_set_bright(led, LV_LED_BRIGHT_MIN); +} + +/** + * Toggle the state of a LED + * @param led pointer to a LED object + */ +void lv_led_toggle(lv_obj_t * led) +{ + LV_ASSERT_OBJ(led, LV_OBJX_NAME); + + uint8_t bright = lv_led_get_bright(led); + if(bright > (LV_LED_BRIGHT_MIN + LV_LED_BRIGHT_MAX) >> 1) + lv_led_off(led); + else + lv_led_on(led); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the brightness of a LEd object + * @param led pointer to LED object + * @return bright 0 (max. dark) ... 255 (max. light) + */ +uint8_t lv_led_get_bright(const lv_obj_t * led) +{ + LV_ASSERT_OBJ(led, LV_OBJX_NAME); + + lv_led_ext_t * ext = lv_obj_get_ext_attr(led); + return ext->bright; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the leds + * @param led 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_led_design(lv_obj_t * led, const lv_area_t * clip_area, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the clip_area area*/ + return ancestor_design(led, clip_area, mode); + } + else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Make darker colors in a temporary style according to the brightness*/ + lv_led_ext_t * ext = lv_obj_get_ext_attr(led); + + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + lv_obj_init_draw_rect_dsc(led, LV_LED_PART_MAIN, &rect_dsc); + + /*Mix. the color with black proportionally with brightness*/ + rect_dsc.bg_color = lv_color_mix(rect_dsc.bg_color, LV_COLOR_BLACK, ext->bright); + rect_dsc.bg_grad_color = lv_color_mix(rect_dsc.bg_grad_color, LV_COLOR_BLACK, ext->bright); + rect_dsc.border_color = lv_color_mix(rect_dsc.border_color, LV_COLOR_BLACK, ext->bright); + rect_dsc.shadow_color = lv_color_mix(rect_dsc.shadow_color, LV_COLOR_BLACK, ext->bright); + + /*Set the current shadow width according to brightness proportionally between LV_LED_BRIGHT_OFF + * and LV_LED_BRIGHT_ON*/ + rect_dsc.shadow_width = ((ext->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_width) / + (LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN); + rect_dsc.shadow_spread = ((ext->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_spread) / + (LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN); + + lv_draw_rect(&led->coords, clip_area, &rect_dsc); + } + return LV_DESIGN_RES_OK; +} + +/** + * Signal function of the led + * @param led pointer to a led object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_led_signal(lv_obj_t * led, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(led, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); + + return res; +} +#endif diff --git a/src/lv_widgets/lv_spinbox.c b/src/lv_widgets/lv_spinbox.c new file mode 100644 index 0000000000..10a16142d1 --- /dev/null +++ b/src/lv_widgets/lv_spinbox.c @@ -0,0 +1,593 @@ +/** + * @file lv_spinbox.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_spinbox.h" + +#if LV_USE_SPINBOX != 0 +#include "../lv_misc/lv_debug.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_math.h" +#include "../lv_misc/lv_utils.h" + +/********************* + * DEFINES + *********************/ +#define LV_OBJX_NAME "lv_spinbox" + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_spinbox_signal(lv_obj_t * spinbox, lv_signal_t sign, void * param); +static lv_style_list_t * lv_spinbox_get_style(lv_obj_t * ta, uint8_t part); +static void lv_spinbox_updatevalue(lv_obj_t * spinbox); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_cb_t ancestor_signal; +static lv_design_cb_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a spinbox object + * @param par pointer to an object, it will be the parent of the new spinbox + * @param copy pointer to a spinbox object, if not NULL then the new object will be copied from it + * @return pointer to the created spinbox + */ +lv_obj_t * lv_spinbox_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("spinbox create started"); + + /*Create the ancestor of spinbox*/ + lv_obj_t * spinbox = lv_textarea_create(par, copy); + LV_ASSERT_MEM(spinbox); + if(spinbox == NULL) return NULL; + + /*Allocate the spinbox type specific extended data*/ + lv_spinbox_ext_t * ext = lv_obj_allocate_ext_attr(spinbox, sizeof(lv_spinbox_ext_t)); + LV_ASSERT_MEM(ext); + if(ext == NULL) { + lv_obj_del(spinbox); + return NULL; + } + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(spinbox); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(spinbox); + + /*Initialize the allocated 'ext'*/ + ext->value = 0; + ext->dec_point_pos = 0; + ext->digit_count = 5; + ext->digit_padding_left = 0; + ext->step = 1; + ext->range_max = 99999; + ext->range_min = -99999; + ext->rollover = false; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_cb(spinbox, lv_spinbox_signal); + lv_obj_set_design_cb(spinbox, ancestor_design); /*Leave the Text area's design function*/ + + /*Init the new spinbox*/ + if(copy == NULL) { + /* No scrolling will happen here so make the scrollable non-clickable + * It allows to handle input events in the bg object only.*/ + lv_obj_set_click(lv_page_get_scrollable(spinbox), false); + lv_textarea_set_one_line(spinbox, true); + lv_textarea_set_cursor_click_pos(spinbox, true); + lv_obj_set_width(spinbox, LV_DPI); + lv_theme_apply(spinbox, LV_THEME_SPINBOX); + } + /*Copy an existing spinbox*/ + else { + lv_spinbox_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + + lv_spinbox_set_value(spinbox, copy_ext->value); + lv_spinbox_set_digit_format(spinbox, (uint8_t)copy_ext->digit_count, (uint8_t)copy_ext->dec_point_pos); + lv_spinbox_set_range(spinbox, copy_ext->range_min, copy_ext->range_max); + lv_spinbox_set_step(spinbox, copy_ext->step); + lv_spinbox_set_rollover(spinbox, copy_ext->rollover); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(spinbox, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL); + } + + lv_spinbox_updatevalue(spinbox); + + LV_LOG_INFO("spinbox created"); + + return spinbox; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set spinbox rollover function + * @param spinbox pointer to spinbox + * @param b true or false to enable or disable (default) + */ +void lv_spinbox_set_rollover(lv_obj_t * spinbox, bool b) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + ext->rollover = b; +} + +/** + * Set spinbox value + * @param spinbox pointer to spinbox + * @param i value to be set + */ +void lv_spinbox_set_value(lv_obj_t * spinbox, int32_t i) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) return; + + if(i > ext->range_max) i = ext->range_max; + if(i < ext->range_min) i = ext->range_min; + + ext->value = i; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Set spinbox digit format (digit count and decimal format) + * @param spinbox pointer to spinbox + * @param digit_count number of digit excluding the decimal separator and the sign + * @param separator_position number of digit before the decimal point. If 0, decimal point is not + * shown + */ +void lv_spinbox_set_digit_format(lv_obj_t * spinbox, uint8_t digit_count, uint8_t separator_position) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) return; + + if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) digit_count = LV_SPINBOX_MAX_DIGIT_COUNT; + + if(separator_position >= digit_count) separator_position = 0; + if(separator_position > LV_SPINBOX_MAX_DIGIT_COUNT) separator_position = LV_SPINBOX_MAX_DIGIT_COUNT; + + if(digit_count < LV_SPINBOX_MAX_DIGIT_COUNT) { + int64_t max_val = _lv_pow(10, digit_count); + if(ext->range_max > max_val - 1) ext->range_max = max_val - 1; + if(ext->range_min < - max_val + 1) ext->range_min = - max_val + 1; + } + + ext->digit_count = digit_count; + ext->dec_point_pos = separator_position; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Set spinbox step + * @param spinbox pointer to spinbox + * @param step steps on increment/decrement + */ +void lv_spinbox_set_step(lv_obj_t * spinbox, uint32_t step) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) return; + + ext->step = step; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Set spinbox value range + * @param spinbox pointer to spinbox + * @param range_min maximum value, inclusive + * @param range_max minimum value, inclusive + */ +void lv_spinbox_set_range(lv_obj_t * spinbox, int32_t range_min, int32_t range_max) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) return; + + ext->range_max = range_max; + ext->range_min = range_min; + + if(ext->value > ext->range_max) { + ext->value = ext->range_max; + lv_obj_invalidate(spinbox); + } + if(ext->value < ext->range_min) { + ext->value = ext->range_min; + lv_obj_invalidate(spinbox); + } + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Set spinbox left padding in digits count (added between sign and first digit) + * @param spinbox pointer to spinbox + * @param cb Callback function called on value change event + */ +void lv_spinbox_set_padding_left(lv_obj_t * spinbox, uint8_t padding) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + ext->digit_padding_left = padding; + lv_spinbox_updatevalue(spinbox); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get spinbox rollover function status + * @param spinbox pointer to spinbox + */ +bool lv_spinbox_get_rollover(lv_obj_t * spinbox) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + return ext->rollover; +} + +/** + * Get the spinbox numeral value (user has to convert to float according to its digit format) + * @param spinbox pointer to spinbox + * @return value integer value of the spinbox + */ +int32_t lv_spinbox_get_value(lv_obj_t * spinbox) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + return ext->value; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Select next lower digit for edition + * @param spinbox pointer to spinbox + */ +void lv_spinbox_step_next(lv_obj_t * spinbox) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + int32_t new_step = ext->step / 10; + if((new_step) > 0) + ext->step = new_step; + else + ext->step = 1; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Select next higher digit for edition + * @param spinbox pointer to spinbox + */ +void lv_spinbox_step_prev(lv_obj_t * spinbox) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + int32_t step_limit; + step_limit = LV_MATH_MAX(ext->range_max, (ext->range_min < 0 ? (-ext->range_min) : ext->range_min)); + int32_t new_step = ext->step * 10; + if(new_step <= step_limit) ext->step = new_step; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Increment spinbox value by one step + * @param spinbox pointer to spinbox + */ +void lv_spinbox_increment(lv_obj_t * spinbox) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + if(ext->value + ext->step <= ext->range_max) { + /*Special mode when zero crossing*/ + if((ext->value + ext->step) > 0 && ext->value < 0) ext->value = -ext->value; + ext->value += ext->step; + + } + else { + // Rollover? + if((ext->rollover) && (ext->value == ext->range_max)) + ext->value = ext->range_min; + else + ext->value = ext->range_max; + } + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Decrement spinbox value by one step + * @param spinbox pointer to spinbox + */ +void lv_spinbox_decrement(lv_obj_t * spinbox) +{ + LV_ASSERT_OBJ(spinbox, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + if(ext->value - ext->step >= ext->range_min) { + /*Special mode when zero crossing*/ + if((ext->value - ext->step) < 0 && ext->value > 0) ext->value = -ext->value; + ext->value -= ext->step; + } + else { + // Rollover? + if((ext->rollover) && (ext->value == ext->range_min)) + ext->value = ext->range_max; + else + ext->value = ext->range_min; + } + + lv_spinbox_updatevalue(spinbox); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the spinbox + * @param spinbox pointer to a spinbox object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_spinbox_signal(lv_obj_t * spinbox, lv_signal_t sign, void * param) +{ + + lv_res_t res = LV_RES_OK; + if(sign == LV_SIGNAL_GET_STYLE) { + lv_get_style_info_t * info = param; + info->result = lv_spinbox_get_style(spinbox, info->part); + if(info->result != NULL) return LV_RES_OK; + else return ancestor_signal(spinbox, sign, param); + } + + /* Include the ancient signal function */ + if(sign != LV_SIGNAL_CONTROL) { + res = ancestor_signal(spinbox, sign, param); + if(res != LV_RES_OK) return res; + } + if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } + else if(sign == LV_SIGNAL_RELEASED) { + /*If released with an ENCODER then move to the next digit*/ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + lv_indev_t * indev = lv_indev_get_act(); + if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) { +#if LV_USE_GROUP + if(lv_group_get_editing(lv_obj_get_group(spinbox))) { + if(ext->step > 1) { + lv_spinbox_step_next(spinbox); + } + else { + /*Restart from the MSB*/ + ext->step = 1; + uint32_t i; + for(i = 0; i < ext->digit_count; i++) { + int32_t new_step = ext->step * 10; + if(new_step >= ext->range_max) break; + ext->step = new_step; + } + lv_spinbox_step_prev(spinbox); + } + } +#endif + } + else { + /*The cursor has been positioned to a digit. + * Set `step` accordingly*/ + const char * txt = lv_textarea_get_text(spinbox); + size_t txt_len = strlen(txt); + + if(txt[ext->ta.cursor.pos] == '.') { + lv_textarea_cursor_left(spinbox); + } + else if(ext->ta.cursor.pos == (uint32_t)txt_len) { + lv_textarea_set_cursor_pos(spinbox, txt_len - 1); + } + else if(ext->ta.cursor.pos == 0 && ext->range_min < 0) { + lv_textarea_set_cursor_pos(spinbox, 1); + } + + size_t len = ext->digit_count - 1; + uint16_t cp = ext->ta.cursor.pos; + + if(ext->ta.cursor.pos > ext->dec_point_pos && ext->dec_point_pos != 0) cp--; + uint32_t pos = len - cp; + + if(ext->range_min < 0) pos++; + + ext->step = 1; + uint16_t i; + for(i = 0; i < pos; i++) ext->step *= 10; + + } + } + else if(sign == LV_SIGNAL_CONTROL) { +#if LV_USE_GROUP + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + + uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/ + if(c == LV_KEY_RIGHT) { + if(indev_type == LV_INDEV_TYPE_ENCODER) + lv_spinbox_increment(spinbox); + else + lv_spinbox_step_next(spinbox); + } + else if(c == LV_KEY_LEFT) { + if(indev_type == LV_INDEV_TYPE_ENCODER) + lv_spinbox_decrement(spinbox); + else + lv_spinbox_step_prev(spinbox); + } + else if(c == LV_KEY_UP) { + lv_spinbox_increment(spinbox); + } + else if(c == LV_KEY_DOWN) { + lv_spinbox_decrement(spinbox); + } + else { + lv_textarea_add_char(spinbox, c); + } +#endif + } + + return res; +} + +/** + * Get the style descriptor of a part of the object + * @param page pointer the object + * @param part the part from `lv_spinbox_part_t`. (LV_SPINBOX_PART_...) + * @return pointer to the style descriptor of the specified part + */ +static lv_style_list_t * lv_spinbox_get_style(lv_obj_t * ta, uint8_t part) +{ + LV_ASSERT_OBJ(ta, LV_OBJX_NAME); + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_style_list_t * style_dsc_p; + + switch(part) { + case LV_SPINBOX_PART_BG: + style_dsc_p = &ta->style_list; + break; + case LV_SPINBOX_PART_CURSOR: + style_dsc_p = &ext->ta.cursor.style; + break; + default: + style_dsc_p = NULL; + } + + return style_dsc_p; +} +static void lv_spinbox_updatevalue(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8]; + _lv_memset_00(buf, sizeof(buf)); + char * buf_p = buf; + uint8_t cur_shift_left = 0; + + if(ext->range_min < 0) { // hide sign if there are only positive values + /*Add the sign*/ + (*buf_p) = ext->value >= 0 ? '+' : '-'; + buf_p++; + } + else { + /*Cursor need shift to left*/ + cur_shift_left++; + } + + int32_t i; + /*padding left*/ + for(i = 0; i < ext->digit_padding_left; i++) { + (*buf_p) = ' '; + buf_p++; + } + + char digits[64]; + /*Convert the numbers to string (the sign is already handled so always covert positive number)*/ + _lv_utils_num_to_str(ext->value < 0 ? -ext->value : ext->value, digits); + + /*Add leading zeros*/ + int lz_cnt = ext->digit_count - (int)strlen(digits); + if(lz_cnt > 0) { + for(i = (uint16_t)strlen(digits); i >= 0; i--) { + digits[i + lz_cnt] = digits[i]; + } + for(i = 0; i < lz_cnt; i++) { + digits[i] = '0'; + } + } + + int32_t intDigits; + intDigits = (ext->dec_point_pos == 0) ? ext->digit_count : ext->dec_point_pos; + + /*Add the decimal part*/ + for(i = 0; i < intDigits && digits[i] != '\0'; i++) { + (*buf_p) = digits[i]; + buf_p++; + } + + if(ext->dec_point_pos != 0) { + /*Insert the decimal point*/ + (*buf_p) = '.'; + buf_p++; + + for(/*Leave i*/; i < ext->digit_count && digits[i] != '\0'; i++) { + (*buf_p) = digits[i]; + buf_p++; + } + } + + /*Refresh the text*/ + lv_textarea_set_text(spinbox, (char *)buf); + + /*Set the cursor position*/ + int32_t step = ext->step; + uint8_t cur_pos = (uint8_t)ext->digit_count; + while(step >= 10) { + step /= 10; + cur_pos--; + } + + if(cur_pos > intDigits) cur_pos++; /*Skip the decimal point*/ + + cur_pos += (ext->digit_padding_left - cur_shift_left); + + lv_textarea_set_cursor_pos(spinbox, cur_pos); +} + +#endif