A39模拟器

This commit is contained in:
liulin
2024-03-07 11:03:18 +08:00
parent cb2cc0d66f
commit 33e6eb45b3
1008 changed files with 2222717 additions and 0 deletions

View File

@ -0,0 +1,20 @@
CSRCS += lv_disp.c
CSRCS += lv_group.c
CSRCS += lv_indev.c
CSRCS += lv_indev_scroll.c
CSRCS += lv_obj.c
CSRCS += lv_obj_class.c
CSRCS += lv_obj_draw.c
CSRCS += lv_obj_pos.c
CSRCS += lv_obj_scroll.c
CSRCS += lv_obj_style.c
CSRCS += lv_obj_style_gen.c
CSRCS += lv_obj_tree.c
CSRCS += lv_event.c
CSRCS += lv_refr.c
CSRCS += lv_theme.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core"

View File

@ -0,0 +1,434 @@
/**
* @file lv_disp.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_disp.h"
#include "../misc/lv_math.h"
#include "../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scr_load_anim_start(lv_anim_t * a);
static void opa_scale_anim(void * obj, int32_t v);
static void set_x_anim(void * obj, int32_t v);
static void set_y_anim(void * obj, int32_t v);
static void scr_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Return with a pointer to the active screen
* @param disp pointer to display which active screen should be get. (NULL to use the default
* screen)
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered to get its active screen");
return NULL;
}
return disp->act_scr;
}
/**
* Return with a pointer to the previous screen. Only used during screen transitions.
* @param disp pointer to display which previous screen should be get. (NULL to use the default
* screen)
* @return pointer to the previous screen object or NULL if not used now
*/
lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered to get its previous screen");
return NULL;
}
return disp->prev_scr;
}
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_load_scr(lv_obj_t * scr)
{
lv_disp_t * d = lv_obj_get_disp(scr);
if(!d) return; /*Shouldn't happen, just to be sure*/
d->act_scr = scr;
lv_obj_invalidate(scr);
}
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
return NULL;
}
return disp->top_layer;
}
/**
* Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
* layer)
* @param disp pointer to display which sys. layer should be get. (NULL to use the default screen)
* @return pointer to the sys layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer");
return NULL;
}
return disp->sys_layer;
}
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th)
{
if(disp == NULL) disp = lv_disp_get_default();
disp->theme = th;
if(disp->screen_cnt == 3 &&
lv_obj_get_child_cnt(disp->screens[0]) == 0 &&
lv_obj_get_child_cnt(disp->screens[1]) == 0 &&
lv_obj_get_child_cnt(disp->screens[2]) == 0)
{
lv_theme_apply(disp->screens[0]);
}
}
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
lv_theme_t * lv_disp_get_theme(lv_disp_t * disp)
{
if(disp == NULL) disp = lv_disp_get_default();
return disp->theme;
}
/**
* Set the background color of a display
* @param disp pointer to a display
* @param color color of the background
*/
void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_color = color;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Set the background image of a display
* @param disp pointer to a display
* @param img_src path to file or pointer to an `lv_img_dsc_t` variable
*/
void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_img = img_src;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Opacity of the background
* @param disp pointer to a display
* @param opa opacity (0..255)
*/
void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_opa = opa;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
* @param time time of the animation
* @param delay delay before the transition
* @param auto_del true: automatically delete the old screen
*/
void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del)
{
lv_disp_t * d = lv_obj_get_disp(new_scr);
lv_obj_t * act_scr = lv_scr_act();
if(d->del_prev && act_scr != d->scr_to_load && d->scr_to_load) {
lv_obj_del(act_scr);
lv_disp_load_scr(d->scr_to_load);
lv_anim_del(d->scr_to_load, NULL);
lv_obj_set_pos(d->scr_to_load, 0, 0);
lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0);
act_scr = d->scr_to_load;
}
d->scr_to_load = new_scr;
if(d->prev_scr && d->del_prev) {
lv_obj_del(d->prev_scr);
d->prev_scr = NULL;
}
d->del_prev = auto_del;
/*Be sure there is no other animation on the screens*/
lv_anim_del(new_scr, NULL);
lv_anim_del(lv_scr_act(), NULL);
/*Be sure both screens are in a normal position*/
lv_obj_set_pos(new_scr, 0, 0);
lv_obj_set_pos(lv_scr_act(), 0, 0);
lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0);
lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0);
lv_anim_t a_new;
lv_anim_init(&a_new);
lv_anim_set_var(&a_new, new_scr);
lv_anim_set_start_cb(&a_new, scr_load_anim_start);
lv_anim_set_ready_cb(&a_new, scr_anim_ready);
lv_anim_set_time(&a_new, time);
lv_anim_set_delay(&a_new, delay);
lv_anim_t a_old;
lv_anim_init(&a_old);
lv_anim_set_var(&a_old, d->act_scr);
lv_anim_set_time(&a_old, time);
lv_anim_set_delay(&a_old, delay);
switch(anim_type) {
case LV_SCR_LOAD_ANIM_NONE:
/*Create a dummy animation to apply the delay*/
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, 0, 0);
break;
case LV_SCR_LOAD_ANIM_OVER_LEFT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_RIGHT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_TOP:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_BOTTOM:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_MOVE_LEFT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_TOP:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_BOTTOM:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_FADE_ON:
lv_anim_set_exec_cb(&a_new, opa_scale_anim);
lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER);
break;
}
lv_anim_start(&a_new);
lv_anim_start(&a_old);
}
/**
* Get elapsed time since last user activity on a display (e.g. click)
* @param disp pointer to an display (NULL to get the overall smallest inactivity)
* @return elapsed ticks (milliseconds) since the last activity
*/
uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp)
{
if(disp) return lv_tick_elaps(disp->last_activity_time);
lv_disp_t * d;
uint32_t t = UINT32_MAX;
d = lv_disp_get_next(NULL);
while(d) {
uint32_t elaps = lv_tick_elaps(d->last_activity_time);
t = LV_MIN(t, elaps);
d = lv_disp_get_next(d);
}
return t;
}
/**
* Manually trigger an activity on a display
* @param disp pointer to an display (NULL to use the default display)
*/
void lv_disp_trig_activity(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_trig_activity: no display registered");
return;
}
disp->last_activity_time = lv_tick_get();
}
/**
* Clean any CPU cache that is related to the display.
* @param disp pointer to an display (NULL to use the default display)
*/
void lv_disp_clean_dcache(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_clean_dcache: no display registered");
return;
}
if(disp->driver->clean_dcache_cb)
disp->driver->clean_dcache_cb(disp->driver);
}
/**
* Get a pointer to the screen refresher timer to
* modify its parameters with `lv_timer_...` functions.
* @param disp pointer to a display
* @return pointer to the display refresher timer. (NULL on error)
*/
lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_get_refr_timer: no display registered");
return NULL;
}
return disp->refr_timer;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scr_load_anim_start(lv_anim_t * a)
{
lv_disp_t * d = lv_obj_get_disp(a->var);
d->prev_scr = lv_scr_act();
lv_disp_load_scr(a->var);
}
static void opa_scale_anim(void * obj, int32_t v)
{
lv_obj_set_style_opa(obj, v, 0);
}
static void set_x_anim(void * obj, int32_t v)
{
lv_obj_set_x(obj, v);
}
static void set_y_anim(void * obj, int32_t v)
{
lv_obj_set_y(obj, v);
}
static void scr_anim_ready(lv_anim_t * a)
{
lv_disp_t * d = lv_obj_get_disp(a->var);
if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr);
d->prev_scr = NULL;
d->scr_to_load = NULL;
lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
}

View File

@ -0,0 +1,245 @@
/**
* @file lv_disp.h
*
*/
#ifndef LV_DISP_H
#define LV_DISP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../hal/lv_hal.h"
#include "lv_obj.h"
#include "lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_SCR_LOAD_ANIM_NONE,
LV_SCR_LOAD_ANIM_OVER_LEFT,
LV_SCR_LOAD_ANIM_OVER_RIGHT,
LV_SCR_LOAD_ANIM_OVER_TOP,
LV_SCR_LOAD_ANIM_OVER_BOTTOM,
LV_SCR_LOAD_ANIM_MOVE_LEFT,
LV_SCR_LOAD_ANIM_MOVE_RIGHT,
LV_SCR_LOAD_ANIM_MOVE_TOP,
LV_SCR_LOAD_ANIM_MOVE_BOTTOM,
LV_SCR_LOAD_ANIM_FADE_ON,
} lv_scr_load_anim_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Return with a pointer to the active screen
* @param disp pointer to display which active screen should be get. (NULL to use the default
* screen)
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp);
/**
* Return with a pointer to the previous screen. Only used during screen transitions.
* @param disp pointer to display which previous screen should be get. (NULL to use the default
* screen)
* @return pointer to the previous screen object or NULL if not used now
*/
lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp);
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_load_scr(lv_obj_t * scr);
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp);
/**
* Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
* layer)
* @param disp pointer to display which sys. layer should be get. (NULL to use the default screen)
* @return pointer to the sys layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp);
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th);
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
lv_theme_t * lv_disp_get_theme(lv_disp_t * disp);
/**
* Set the background color of a display
* @param disp pointer to a display
* @param color color of the background
*/
void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color);
/**
* Set the background image of a display
* @param disp pointer to a display
* @param img_src path to file or pointer to an `lv_img_dsc_t` variable
*/
void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src);
/**
* Opacity of the background
* @param disp pointer to a display
* @param opa opacity (0..255)
*/
void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa);
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
* @param time time of the animation
* @param delay delay before the transition
* @param auto_del true: automatically delete the old screen
*/
void lv_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del);
/**
* Get elapsed time since last user activity on a display (e.g. click)
* @param disp pointer to an display (NULL to get the overall smallest inactivity)
* @return elapsed ticks (milliseconds) since the last activity
*/
uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp);
/**
* Manually trigger an activity on a display
* @param disp pointer to an display (NULL to use the default display)
*/
void lv_disp_trig_activity(lv_disp_t * disp);
/**
* Clean any CPU cache that is related to the display.
* @param disp pointer to an display (NULL to use the default display)
*/
void lv_disp_clean_dcache(lv_disp_t * disp);
/**
* Get a pointer to the screen refresher timer to
* modify its parameters with `lv_timer_...` functions.
* @param disp pointer to a display
* @return pointer to the display refresher timer. (NULL on error)
*/
lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp);
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
/**
* Get the active screen of the default display
* @return pointer to the active screen
*/
static inline lv_obj_t * lv_scr_act(void)
{
return lv_disp_get_scr_act(lv_disp_get_default());
}
/**
* Get the top layer of the default display
* @return pointer to the top layer
*/
static inline lv_obj_t * lv_layer_top(void)
{
return lv_disp_get_layer_top(lv_disp_get_default());
}
/**
* Get the active screen of the default display
* @return pointer to the sys layer
*/
static inline lv_obj_t * lv_layer_sys(void)
{
return lv_disp_get_layer_sys(lv_disp_get_default());
}
static inline void lv_scr_load(lv_obj_t * scr)
{
lv_disp_load_scr(scr);
}
/**********************
* MACROS
**********************/
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
#ifndef LV_HOR_RES
/**
* The horizontal resolution of the currently active display.
*/
#define LV_HOR_RES lv_disp_get_hor_res(lv_disp_get_default())
#endif
#ifndef LV_VER_RES
/**
* The vertical resolution of the currently active display.
*/
#define LV_VER_RES lv_disp_get_ver_res(lv_disp_get_default())
#endif
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the default display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_dpx(lv_coord_t n)
{
return LV_DPX(n);
}
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the given display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param obj an display whose dpi should be considered
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_disp_dpx(const lv_disp_t * disp, lv_coord_t n)
{
return _LV_DPX_CALC(lv_disp_get_dpi(disp), n);
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DISP_H*/

View File

@ -0,0 +1,419 @@
/**
* @file lv_event.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_event_dsc_t{
lv_event_cb_t cb;
void * user_data;
lv_event_code_t filter :8;
}lv_event_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id);
static lv_res_t event_send_core(lv_event_t * e);
static bool event_is_bubbled(lv_event_code_t e);
/**********************
* STATIC VARIABLES
**********************/
static lv_event_t * event_head;
/**********************
* MACROS
**********************/
#if LV_LOG_TRACE_EVENT
# define EVENT_TRACE(...) LV_LOG_TRACE( __VA_ARGS__)
#else
# define EVENT_TRACE(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param)
{
if(obj == NULL) return LV_RES_OK;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_event_t e;
e.target = obj;
e.current_target = obj;
e.code = event_code;
e.user_data = NULL;
e.param = param;
e.deleted = 0;
/*Build a simple linked list from the objects used in the events
*It's important to know if an this object was deleted by a nested event
*called from this `event_cb`.*/
e.prev = event_head;
event_head = &e;
/*Send the event*/
lv_res_t res = event_send_core(&e);
/*Remove this element from the list*/
event_head = e.prev;
return res;
}
lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e)
{
const lv_obj_class_t * base;
if(class_p == NULL) base = e->current_target->class_p;
else base = class_p->base_class;
/*Find a base in which Call the ancestor's event handler_cb is set*/
while(base && base->event_cb == NULL) base = base->base_class;
if(base == NULL) return LV_RES_OK;
if(base->event_cb == NULL) return LV_RES_OK;
/*Call the actual event callback*/
e->user_data = NULL;
base->event_cb(base, e);
lv_res_t res = LV_RES_OK;
/*Stop if the object is deleted*/
if(e->deleted) res = LV_RES_INV;
return res;
}
lv_obj_t * lv_event_get_target(lv_event_t * e)
{
return e->target;
}
lv_obj_t * lv_event_get_current_target(lv_event_t * e)
{
return e->current_target;
}
lv_event_code_t lv_event_get_code(lv_event_t * e)
{
return e->code;
}
void * lv_event_get_param(lv_event_t * e)
{
return e->param;
}
void * lv_event_get_user_data(lv_event_t * e)
{
return e->user_data;
}
uint32_t lv_event_register_id(void)
{
static uint32_t last_id = _LV_EVENT_LAST;
last_id ++;
return last_id;
}
void _lv_event_mark_deleted(lv_obj_t * obj)
{
lv_event_t * e = event_head;
while(e) {
if(e->current_target == obj || e->target == obj) e->deleted = 1;
e = e->prev;
}
}
struct _lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->event_dsc_cnt++;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb;
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].filter = filter;
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data;
return &obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1];
}
bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(obj->spec_attr->event_dsc[i].cb == event_cb) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt-1); i++) {
obj->spec_attr->event_dsc[i].cb = obj->spec_attr->event_dsc[i+1].cb;
obj->spec_attr->event_dsc[i].user_data = obj->spec_attr->event_dsc[i+1].user_data;
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
bool lv_obj_remove_event_dsc(lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(&obj->spec_attr->event_dsc[i] == event_dsc) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt-1); i++) {
obj->spec_attr->event_dsc[i].cb = obj->spec_attr->event_dsc[i+1].cb;
obj->spec_attr->event_dsc[i].user_data = obj->spec_attr->event_dsc[i+1].user_data;
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
lv_indev_t * lv_event_get_indev(lv_event_t * e)
{
if(e->code == LV_EVENT_PRESSED ||
e->code == LV_EVENT_PRESSING ||
e->code == LV_EVENT_PRESS_LOST ||
e->code == LV_EVENT_SHORT_CLICKED ||
e->code == LV_EVENT_LONG_PRESSED ||
e->code == LV_EVENT_LONG_PRESSED_REPEAT ||
e->code == LV_EVENT_CLICKED ||
e->code == LV_EVENT_RELEASED ||
e->code == LV_EVENT_SCROLL_BEGIN ||
e->code == LV_EVENT_SCROLL_END ||
e->code == LV_EVENT_SCROLL ||
e->code == LV_EVENT_GESTURE ||
e->code == LV_EVENT_KEY ||
e->code == LV_EVENT_FOCUSED ||
e->code == LV_EVENT_DEFOCUSED ||
e->code == LV_EVENT_LEAVE)
{
return lv_event_get_param(e);
} else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_PART_BEGIN||
e->code == LV_EVENT_DRAW_PART_END)
{
return lv_event_get_param(e);
} else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_clip_area(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_MAIN ||
e->code == LV_EVENT_DRAW_MAIN_BEGIN ||
e->code == LV_EVENT_DRAW_MAIN_END ||
e->code == LV_EVENT_DRAW_POST ||
e->code == LV_EVENT_DRAW_POST_BEGIN ||
e->code == LV_EVENT_DRAW_POST_END)
{
return lv_event_get_param(e);
} else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_old_size(lv_event_t * e)
{
if(e->code == LV_EVENT_SIZE_CHANGED) {
return lv_event_get_param(e);
} else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
uint32_t lv_event_get_key(lv_event_t * e)
{
if(e->code == LV_EVENT_KEY) {
uint32_t * k = lv_event_get_param(e);
if(k) return *k;
else return 0;
} else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size)
{
if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * cur_size = lv_event_get_param(e);
*cur_size = LV_MAX(*cur_size, size);
} else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_point_t * lv_event_get_self_size_info(lv_event_t * e)
{
if(e->code == LV_EVENT_GET_SELF_SIZE) {
return lv_event_get_param(e);
} else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e)
{
if(e->code == LV_EVENT_HIT_TEST) {
return lv_event_get_param(e);
} else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
const lv_area_t * lv_event_get_cover_area(lv_event_t * e)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
return p->area;
} else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
if(res > p->res) p->res = res; /*Save only "stronger" results*/
} else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!obj->spec_attr) return NULL;
if(id >= obj->spec_attr->event_dsc_cnt) return NULL;
return &obj->spec_attr->event_dsc[id];
}
static lv_res_t event_send_core(lv_event_t * e)
{
EVENT_TRACE("Sending event %d to %p with %p param", e->code, e->current_target, e->param);
/*Call the input device's feedback callback if set*/
lv_indev_t * indev_act = lv_indev_get_act();
if(indev_act) {
if(indev_act->driver->feedback_cb) indev_act->driver->feedback_cb(indev_act->driver, e->code);
}
lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(e->current_target, 0);
lv_res_t res = LV_RES_OK;
res = lv_obj_event_base(NULL, e);
uint32_t i = 0;
while(event_dsc && res == LV_RES_OK) {
if(event_dsc->cb && (event_dsc->filter == LV_EVENT_ALL || event_dsc->filter == e->code)) {
e->user_data = event_dsc->user_data;
event_dsc->cb(e);
/*Stop if the object is deleted*/
if(e->deleted) return LV_RES_INV;
}
i++;
event_dsc = lv_obj_get_event_dsc(e->current_target, i);
}
if(res == LV_RES_OK && event_is_bubbled(e->code)) {
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) && e->current_target->parent) {
e->current_target = e->current_target->parent;
res = event_send_core(e);
if(res != LV_RES_OK) return LV_RES_INV;
}
}
return res;
}
static bool event_is_bubbled(lv_event_code_t e)
{
switch(e) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_PART_BEGIN:
case LV_EVENT_DRAW_PART_END:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}

View File

@ -0,0 +1,319 @@
/**
* @file lv_templ.h
*
*/
#ifndef LV_EVENT_H
#define LV_EVENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_event_dsc_t;
/**
* Type of event being sent to the object.
*/
typedef enum {
LV_EVENT_ALL = 0,
/** Input device events*/
LV_EVENT_PRESSED, /**< The object has been pressed*/
LV_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/
LV_EVENT_PRESS_LOST, /**< The object is still being pressed but slid cursor/finger off of the object */
LV_EVENT_SHORT_CLICKED, /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `long_press_time`. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.*/
LV_EVENT_CLICKED, /**< Called on release if not scrolled (regardless to long press)*/
LV_EVENT_RELEASED, /**< Called in every cases when the object has been released*/
LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins*/
LV_EVENT_SCROLL_END, /**< Scrolling ends*/
LV_EVENT_SCROLL, /**< Scrolling*/
LV_EVENT_GESTURE, /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */
LV_EVENT_KEY, /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/
LV_EVENT_FOCUSED, /**< The object is focused*/
LV_EVENT_DEFOCUSED, /**< The object is defocused*/
LV_EVENT_LEAVE, /**< The object is defocused but still selected*/
LV_EVENT_HIT_TEST, /**< Perform advanced hit-testing*/
/** Drawing events*/
LV_EVENT_COVER_CHECK, /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/
LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get the required extra draw area around the object (e.g. for shadow). The event parameter is `lv_coord_t *` to store the size.*/
LV_EVENT_DRAW_MAIN_BEGIN, /**< Starting the main drawing phase*/
LV_EVENT_DRAW_MAIN, /**< Perform the main drawing*/
LV_EVENT_DRAW_MAIN_END, /**< Finishing the main drawing phase*/
LV_EVENT_DRAW_POST_BEGIN, /**< Starting the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_POST, /**< Perform the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_POST_END, /**< Finishing the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_PART_BEGIN, /**< Starting to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
LV_EVENT_DRAW_PART_END, /**< Finishing to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
/** Special events*/
LV_EVENT_VALUE_CHANGED, /**< The object's value has changed (i.e. slider moved)*/
LV_EVENT_INSERT, /**< A text is inserted to the object. The event data is `char *` being inserted.*/
LV_EVENT_REFRESH, /**< Notify the object to refresh something on it (for the user)*/
LV_EVENT_READY, /**< A process has finished*/
LV_EVENT_CANCEL, /**< A process has been cancelled */
/** Other events*/
LV_EVENT_DELETE, /**< Object is being deleted*/
LV_EVENT_CHILD_CHANGED, /**< Child was removed/added*/
LV_EVENT_SIZE_CHANGED, /**< Object coordinates/size have changed*/
LV_EVENT_STYLE_CHANGED, /**< Object's style has changed*/
LV_EVENT_LAYOUT_CHANGED, /**< The children position has changed due to a layout recalculation*/
LV_EVENT_GET_SELF_SIZE, /**< Get the internal size of a widget*/
LV_EVENT_CALL_IN,
LV_EVENT_CALL_REJECT,
LV_EVENT_CALL_OK,
LV_EVENT_SCROLL_PREDICT,
LV_EVENT_ANCS_MSG_IN,
LV_EVENT_MSUIC_CONTEXT_IN,
_LV_EVENT_LAST /** Number of default events*/
}lv_event_code_t;
typedef struct _lv_event_t {
struct _lv_obj_t * target;
struct _lv_obj_t * current_target;
lv_event_code_t code;
void * user_data;
void * param;
struct _lv_event_t * prev;
uint8_t deleted :1;
}lv_event_t;
/**
* @brief Event callback.
* Events are used to notify the user of some action being taken on the object.
* For details, see ::lv_event_t.
*/
typedef void (*lv_event_cb_t)(lv_event_t * e);
/**
* Used as the event parameter of ::LV_EVENT_HIT_TEST to check if an `point` can click the object or not.
* `res` should be set like this:
* - If already set to `false` an other event wants that point non clickable. If you want to respect it leave it as `false` or set `true` to overwrite it.
* - If already set `true` and `point` shouldn't be clickable set to `false`
* - If already set to `true` you agree that `point` can click the object leave it as `true`
*/
typedef struct {
const lv_point_t * point; /**< A point relative to screen to check if it can click the object or not*/
bool res; /**< true: `point` can click the object; false: it cannot*/
} lv_hit_test_info_t;
/**
* Used as the event parameter of ::LV_EVENT_COVER_CHECK to check if an area is covered by the object or not.
* In the event use `const lv_area_t * area = lv_event_get_cover_area(e)` to get the area to check
* and `lv_event_set_cover_res(e, res)` to set the result.
*/
typedef struct {
lv_cover_res_t res;
const lv_area_t * area;
} lv_cover_check_info_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Send an event to the object
* @param obj pointer to an object
* @param event_code the type of the event from `lv_event_t`
* @param param arbitrary data depending on the widget type and the event. (Usually `NULL`)
* @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event_code
*/
lv_res_t lv_event_send(struct _lv_obj_t * obj, lv_event_code_t event_code, void * param);
/**
* Used by the widgets internally to call the ancestor widget types's event handler
* @param class_p pointer to the class of the widget (NOT the ancestor class)
* @param e pointer to the event descriptor
* @return LV_RES_OK: the taget object was not deleted in the event; LV_RES_INV: it was deleted in the event_code
*/
lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e);
/**
* Get the object originally targeted by the event. It's the same even if the event is bubbled.
* @param e pointer to the event descriptor
* @return the target of the event_code
*/
struct _lv_obj_t * lv_event_get_target(lv_event_t * e);
/**
* Get the current target of the event. It's the object which event handler being called.
* If the event is not bubbled it's the same as "normal" target.
* @param e pointer to the event descriptor
* @return pointer to the current target of the event_code
*/
struct _lv_obj_t * lv_event_get_current_target(lv_event_t * e);
/**
* Get the event code of an event
* @param e pointer to the event descriptor
* @return the event code. (E.g. `LV_EVENT_CLICKED`, `LV_EVENT_FOCUSED`, etc)
*/
lv_event_code_t lv_event_get_code(lv_event_t * e);
/**
* Get the parameter passed when the event was sent
* @param e pointer to the event descriptor
* @return pointer to the parameter
*/
void * lv_event_get_param(lv_event_t * e);
/**
* Get the user_data passed when the event was registered on the object
* @param e pointer to the event descriptor
* @return pointer to the user_data
*/
void * lv_event_get_user_data(lv_event_t * e);
/**
* Register a new, custom event ID.
* It can be used the same way as e.g. `LV_EVENT_CLICKED` to send custom events
* @return the new event id
* @example
* uint32_t LV_EVENT_MINE = 0;
* ...
* e = lv_event_register_id();
* ...
* lv_event_send(obj, LV_EVENT_MINE, &some_data);
*/
uint32_t lv_event_register_id(void);
/**
* Nested events can be called and one of them might belong to an object that is being deleted.
* Mark this object's `event_temp_data` deleted to know that it's `lv_event_send` should return `LV_RES_INV`
* @param obj pointer to an obejct to mark as deleted
*/
void _lv_event_mark_deleted(struct _lv_obj_t * obj);
/**
* Add an event handler function for an object.
* Used by the user to react on event which happens with the object.
* An object can have multiple event handler. They will be called in the same order as they were added.
* @param obj pointer to an object
* @param filter and event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be sued the receive all the events.
* @param event_cb the new event function
* @param user_data custom data data will be available in `event_cb`
* @return a pointer the event descriptor. Can be used in ::lv_obj_remove_event_dsc
*/
struct _lv_event_dsc_t * lv_obj_add_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
/**
* Remove an event handler function for an object.
* @param obj pointer to an object
* @param event_cb the event function to remove
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
/**
* Remove an event handler function for an object.
* @param obj pointer to an object
* @param event_dsc pointer to an event descriptor to remove (returned by ::lv_obj_add_event_cb)
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_dsc(struct _lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc);
/**
* Get the input device passed as parameter to indev related events.
* @param e pointer to an event
* @return the indev that triggered the event or NULL if called on a not indev related event
*/
lv_indev_t * lv_event_get_indev(lv_event_t * e);
/**
* Get the part draw descriptor passed as parameter to `LV_EVENT_DRAW_PART_BEGIN/END`.
* @param e pointer to an event
* @return the part draw descriptor to hook the drawing or NULL if called on an unrelated event
*/
lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e);
/**
* Get the clip area passed as parameter to draw events events.
* Namely: `LV_EVENT_DRAW_MAIN/POST`, `LV_EVENT_DRAW_MAIN/POST_BEGIN`, `LV_EVENT_DRAW_MAIN/POST_END`
* @param e pointer to an event
* @return the clip area to use during drawing or NULL if called on an unrelated event
*/
const lv_area_t * lv_event_get_clip_area(lv_event_t * e);
/**
* Get the old area of the object before its size was changed. Can be used in `LV_EVENT_SIZE_CHANGED`
* @param e pointer to an event
* @return the old absolute area of the object or NULL if called on an unrelated event
*/
const lv_area_t * lv_event_get_old_size(lv_event_t * e);
/**
* Get the key passed as parameter to an event. Can be used in `LV_EVENT_KEY`
* @param e pointer to an event
* @return the triggering key or NULL if called on an unrelated event
*/
uint32_t lv_event_get_key(lv_event_t * e);
/**
* Set the new extra draw size. Can be used in `LV_EVENT_REFR_EXT_DRAW_SIZE`
* @param e pointer to an event
* @param size The new extra draw size
*/
void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size);
/**
* Get a pointer to an `lv_point_t` variable in which the self size should be saved (width in `point->x` and height `point->y`).
* Can be used in `LV_EVENT_GET_SELF_SIZE`
* @param e pointer to an event
* @return pointer to `lv_point_t` or NULL if called on an unrelated event
*/
lv_point_t * lv_event_get_self_size_info(lv_event_t * e);
/**
* Get a pointer to an `lv_hit_test_info_t` variable in which the hit test result should be saved. Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return pointer to `lv_hit_test_info_t` or NULL if called on an unrelated event
*/
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e);
/**
* Get a pointer to an area which should be examined whether the object fully covers it or not.
* Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return an area with absolute coordinates to check
*/
const lv_area_t * lv_event_get_cover_area(lv_event_t * e);
/**
* Set the result of cover checking. Can be used in `LV_EVENT_COVER_CHECK`
* @param e pointer to an event
* @param res an element of ::lv_cover_check_info_t
*/
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EVENT_H*/

View File

@ -0,0 +1,438 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_group.h"
#include "../misc/lv_gc.h"
#include "../core/lv_obj.h"
#include "../core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *));
static void lv_group_refocus(lv_group_t * g);
static lv_indev_t * get_indev(const lv_group_t * g);
/**********************
* STATIC VARIABLES
**********************/
static lv_group_t * default_group;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_group_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_group_ll), sizeof(lv_group_t));
}
lv_group_t * lv_group_create(void)
{
lv_group_t * group = _lv_ll_ins_head(&LV_GC_ROOT(_lv_group_ll));
LV_ASSERT_MALLOC(group);
if(group == NULL) return NULL;
_lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
group->obj_focus = NULL;
group->frozen = 0;
group->focus_cb = NULL;
group->editing = 0;
group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
group->wrap = 1;
#if LV_USE_USER_DATA
group->user_data = NULL;
#endif
return group;
}
void lv_group_del(lv_group_t * group)
{
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
_LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr) (*obj)->spec_attr->group_p = NULL;
}
_lv_ll_clear(&(group->obj_ll));
_lv_ll_remove(&LV_GC_ROOT(_lv_group_ll), group);
lv_mem_free(group);
}
void lv_group_set_default(lv_group_t * group)
{
default_group = group;
}
lv_group_t * lv_group_get_default(void)
{
return default_group;
}
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
if(group == NULL) return;
LV_LOG_TRACE("begin");
/*Do not add the object twice*/
lv_obj_t ** obj_i;
_LV_LL_READ(&group->obj_ll, obj_i) {
if((*obj_i) == obj) {
LV_LOG_INFO("the object is already added to this group");
return;
}
}
/*If the object is already in a group and focused then refocus it*/
lv_group_t * group_cur = lv_obj_get_group(obj);
if(group_cur) {
if(obj->spec_attr->group_p && *(obj->spec_attr->group_p->obj_focus) == obj) {
lv_group_refocus(group_cur);
LV_LOG_INFO("changing object's group");
}
}
if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
obj->spec_attr->group_p = group;
lv_obj_t ** next = _lv_ll_ins_tail(&group->obj_ll);
LV_ASSERT_MALLOC(next);
if(next == NULL) return;
*next = obj;
/*If the head and the tail is equal then there is only one object in the linked list.
*In this case automatically activate it*/
if(_lv_ll_get_head(&group->obj_ll) == next) {
lv_group_refocus(group);
}
LV_LOG_TRACE("finished");
}
void lv_group_remove_obj(lv_obj_t * obj)
{
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
LV_LOG_TRACE("begin");
/*Focus on the next object*/
if(g->obj_focus && *g->obj_focus == obj) {
if(g->frozen) g->frozen = 0;
/*If this is the only object in the group then focus to nothing.*/
if(_lv_ll_get_head(&g->obj_ll) == g->obj_focus && _lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
}
/*If there more objects in the group then focus to the next/prev object*/
else {
lv_group_refocus(g);
}
}
/*If the focuses object is still the same then it was the only object in the group but it will
*be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
*zero objects*/
if(g->obj_focus && *g->obj_focus == obj) {
g->obj_focus = NULL;
}
/*Search the object and remove it from its group*/
lv_obj_t ** i;
_LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
_lv_ll_remove(&g->obj_ll, i);
lv_mem_free(i);
if(obj->spec_attr) obj->spec_attr->group_p = NULL;
break;
}
}
LV_LOG_TRACE("finished");
}
void lv_group_remove_all_objs(lv_group_t * group)
{
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
group->obj_focus = NULL;
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
_LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr) (*obj)->spec_attr->group_p = NULL;
}
_lv_ll_clear(&(group->obj_ll));
}
void lv_group_focus_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
if(g->frozen != 0) return;
// if(g->obj_focus != NULL && obj == *g->obj_focus) return;
/*On defocus edit mode must be leaved*/
lv_group_set_editing(g, false);
lv_obj_t ** i;
_LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
if(g->obj_focus != NULL) {
lv_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
if(g->obj_focus != NULL) {
if(g->focus_cb) g->focus_cb(g);
lv_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
break;
}
}
}
void lv_group_focus_next(lv_group_t * group)
{
focus_next_core(group, _lv_ll_get_head, _lv_ll_get_next);
}
void lv_group_focus_prev(lv_group_t * group)
{
focus_next_core(group, _lv_ll_get_tail, _lv_ll_get_prev);
}
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RES_OK;
lv_res_t res;
res = lv_event_send(act, LV_EVENT_KEY, &c);
if(res != LV_RES_OK) return res;
return res;
}
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
group->focus_cb = focus_cb;
}
void lv_group_set_editing(lv_group_t * group, bool edit)
{
if(group == NULL) return;
uint8_t en_val = edit ? 1 : 0;
if(en_val == group->editing) return; /*Do not set the same mode again*/
group->editing = en_val;
lv_obj_t * focused = lv_group_get_focused(group);
if(focused) {
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(focused);
}
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
{
group->refocus_policy = policy & 0x01;
}
void lv_group_set_wrap(lv_group_t * group, bool en)
{
group->wrap = en ? 1 : 0;
}
lv_obj_t * lv_group_get_focused(const lv_group_t * group)
{
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
{
if(!group) return NULL;
return group->focus_cb;
}
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing ? true : false;
}
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap ? true : false;
}
uint32_t lv_group_get_obj_count(lv_group_t * group)
{
return _lv_ll_get_len(&group->obj_ll);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_group_refocus(lv_group_t * g)
{
/*Refocus must temporarily allow wrapping to work correctly*/
uint8_t temp_wrap = g->wrap;
g->wrap = 1;
if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
lv_group_focus_next(g);
else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
lv_group_focus_prev(g);
/*Restore wrap property*/
g->wrap = temp_wrap;
}
static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *))
{
if(group->frozen) return;
lv_obj_t ** obj_next = group->obj_focus;
lv_obj_t ** obj_sentinel = NULL;
bool can_move = true;
bool can_begin = true;
for(;;) {
if(obj_next == NULL) {
if(group->wrap || obj_sentinel == NULL) {
if(!can_begin) return;
obj_next = begin(&group->obj_ll);
can_move = false;
can_begin = false;
}
else {
/*Currently focused object is the last/first in the group, keep it that way*/
return;
}
}
if(obj_sentinel == NULL) {
obj_sentinel = obj_next;
if(obj_sentinel == NULL) return; /*Group is empty*/
}
if(can_move) {
obj_next = move(&group->obj_ll, obj_next);
/*Give up if we walked the entire list and haven't found another visible object*/
if(obj_next == obj_sentinel) return;
}
can_move = true;
if(obj_next == NULL) continue;
if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
/*Hidden objects don't receive focus*/
if(lv_obj_has_flag(*obj_next, LV_OBJ_FLAG_HIDDEN) == false) break;
}
if(obj_next == group->obj_focus) return; /*There's only one visible object and it's already focused*/
if(group->obj_focus) {
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
}
group->obj_focus = obj_next;
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
}
/**
* Find an indev preferably with KEYPAD or ENCOEDR type that uses the given group.
* In other words, find an indev, that is related to the given group.
* In the worst case simply return the latest indev
* @param g a group the find in the indevs
* @return the suggested indev
*/
static lv_indev_t * get_indev(const lv_group_t * g)
{
lv_indev_t * indev_encoder = NULL;
lv_indev_t * indev_group = NULL;
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev->group == g) {
/*Prefer KEYPAD*/
if(indev_type == LV_INDEV_TYPE_KEYPAD) return indev;
if(indev_type == LV_INDEV_TYPE_ENCODER) indev_encoder = indev;
indev_group = indev;
}
indev = lv_indev_get_next(indev);
}
if(indev_encoder) return indev_encoder;
if(indev_group) return indev_group;
/*In lack of a better option use the first input device. (It can be NULL if there is no input device)*/
return lv_indev_get_next(NULL);
}

View File

@ -0,0 +1,239 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include <stdbool.h>
#include "../misc/lv_ll.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/*Predefined keys to control the focused object via lv_group_send(group, c)*/
enum {
LV_KEY_UP = 17, /*0x11*/
LV_KEY_DOWN = 18, /*0x12*/
LV_KEY_RIGHT = 19, /*0x13*/
LV_KEY_LEFT = 20, /*0x14*/
LV_KEY_ESC = 27, /*0x1B*/
LV_KEY_DEL = 127, /*0x7F*/
LV_KEY_BACKSPACE = 8, /*0x08*/
LV_KEY_ENTER = 10, /*0x0A, '\n'*/
LV_KEY_NEXT = 9, /*0x09, '\t'*/
LV_KEY_PREV = 11, /*0x0B, '*/
LV_KEY_HOME = 2, /*0x02, STX*/
LV_KEY_END = 3, /*0x03, ETX*/
};
typedef uint8_t lv_key_t;
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_group_t;
typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
/**
* Groups can be used to logically hold objects so that they can be individually focused.
* They are NOT for laying out objects on a screen (try `lv_cont` for that).
*/
typedef struct _lv_group_t {
lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/
struct _lv_obj_t ** obj_focus; /**< The object in focus*/
lv_group_focus_cb_t focus_cb; /**< A function to call when a new object is focused (optional)*/
#if LV_USE_USER_DATA
void * user_data;
#endif
uint8_t frozen : 1; /**< 1: can't focus to new object*/
uint8_t editing : 1; /**< 1: Edit mode, 0: Navigate mode*/
uint8_t refocus_policy : 1; /**< 1: Focus prev if focused on deletion. 0: Focus next if focused on
deletion.*/
uint8_t wrap : 1; /**< 1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end
of list.*/
} lv_group_t;
typedef enum {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
}lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the group module
* @remarks Internal function, do not call directly.
*/
void _lv_group_init(void);
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void);
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group);
/**
* Set a default group. New object are added to this group if it's enabled in their class with `add_to_def_group = true`
* @param group pointer to a group (can be `NULL`)
*/
void lv_group_set_default(lv_group_t * group);
/**
* Get the default group
* @return pointer to the default group
*/
lv_group_t * lv_group_get_default(void);
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, struct _lv_obj_t * obj);
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(struct _lv_obj_t * obj);
/**
* Remove all objects from a group
* @param group pointer to a group
*/
void lv_group_remove_all_objs(lv_group_t * group);
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(struct _lv_obj_t * obj);
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en);
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c);
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
/**
* Set whether the next or previous item in a group is focused if the currently focused obj is
* deleted.
* @param group pointer to a group
* @param policy new refocus policy enum
*/
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit);
/**
* Set whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
void lv_group_set_wrap(lv_group_t * group, bool en);
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
struct _lv_obj_t * lv_group_get_focused(const lv_group_t * group);
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group);
/**
* Get whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group);
/**
* Get the number of object in the group
* @param group pointer to a group
* @return number of objects in the group
*/
uint32_t lv_group_get_obj_count(lv_group_t * group);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
/**
* @file lv_indev.h
*
*/
#ifndef LV_INDEV_H
#define LV_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../hal/lv_hal_indev.h"
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Called periodically to read the input devices
* @param param pointer to and input device to read
*/
void lv_indev_read_timer_cb(lv_timer_t * timer);
void lv_indev_enable(lv_indev_t * indev, bool en);
/**
* Get the currently processed input device. Can be used in action functions too.
* @return pointer to the currently processed input device or NULL if no input device processing
* right now
*/
lv_indev_t * lv_indev_get_act(void);
/**
* Get the type of an input device
* @param indev pointer to an input device
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
*/
lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
/**
* Reset one or all input devices
* @param indev pointer to an input device to reset or NULL to reset all of them
* @param obj pointer to an object which triggers the reset.
*/
void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj);
/**
* Reset the long press state of an input device
* @param indev pointer to an input device
*/
void lv_indev_reset_long_press(lv_indev_t * indev);
/**
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
* @param indev pointer to an input device
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj);
/**
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group);
/**
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
* These points will be assigned to the buttons to press a specific point on the screen
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[]);
/**
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
/**
* Get the current gesture direct
* @param indev pointer to an input device
* @return current gesture direct
*/
lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev);
/**
* Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @return the last pressed key (0 on error)
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev);
/**
* Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return LV_DIR_NONE: no scrolling now
* LV_DIR_HOR/VER
*/
lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev);
/**
* Get the currently scrolled object (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return pointer to the currently scrolled object or NULL if no scrolling by this indev
*/
lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev);
/**
* Get the movement vector of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the types.pointer.vector
*/
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
/**
* Do nothing until the next release
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev);
/**
* Gets a pointer to the currently active object in the currently processed input device.
* @return pointer to currently active object or NULL if no active object
*/
lv_obj_t * lv_indev_get_obj_act(void);
/**
* Get a pointer to the indev read timer to
* modify its parameters with `lv_timer_...` functions.
* @param indev pointer to an input device
* @return pointer to the indev read refresher timer. (NULL on error)
*/
lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev);
/**
* Search the most top, clickable object by a point
* @param obj pointer to a start object, typically the screen
* @param point pointer to a point for searching the most top child
* @return pointer to the found object or NULL if there was no suitable object
*/
lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_INDEV_H*/

View File

@ -0,0 +1,660 @@
/**
* @file lv_indev_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_indev.h"
#include "lv_indev_scroll.h"
/*********************
* DEFINES
*********************/
#define ELASTIC_SLOWNESS_FACTOR 4 /*Scrolling on elastic parts are slower by this factor*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc);
static void init_scroll_limits(_lv_indev_proc_t * proc);
static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y);
static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc);
static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc);
static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end, lv_dir_t dir);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_indev_scroll_handler(_lv_indev_proc_t * proc)
{
lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
/*If there is no scroll object yet try to find one*/
if(scroll_obj == NULL) {
proc->types.pointer.scroll_sum.x += proc->types.pointer.vect.x;
proc->types.pointer.scroll_sum.y += proc->types.pointer.vect.y;
lv_point_t scroll_sum;
scroll_sum.x = proc->types.pointer.scroll_sum.x;
scroll_sum.y = proc->types.pointer.scroll_sum.y;
scroll_obj = find_scroll_obj(proc);
if(scroll_obj == NULL) return;
init_scroll_limits(proc);
lv_indev_t * indev_act = lv_indev_get_act();
lv_event_send(scroll_obj, LV_EVENT_SCROLL_BEGIN, indev_act);
if(proc->reset_query) return;
}
/*Set new position or scroll if the vector is not zero*/
if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
lv_coord_t diff_x = 0;
lv_coord_t diff_y = 0;
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
if(scroll_dir == LV_DIR_CUSTOMER_STARTSKY)
{
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
}
else
{
if(proc->types.pointer.scroll_dir == LV_DIR_HOR)
{
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
}
else
{
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
}
}
//lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
if((scroll_dir & LV_DIR_LEFT) == 0 && diff_x > 0) diff_x = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && diff_x < 0) diff_x = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && diff_y > 0) diff_y = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
/*Respect the scroll limit area*/
scroll_limit_diff(proc, &diff_x, &diff_y);
lv_obj_scroll_by(scroll_obj, diff_x, diff_y, LV_ANIM_OFF);
proc->types.pointer.scroll_sum.x += diff_x;
proc->types.pointer.scroll_sum.y += diff_y;
}
}
void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)
{
lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
if(scroll_obj == NULL) return;
if(proc->types.pointer.scroll_dir == LV_DIR_NONE) return;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
proc->types.pointer.scroll_throw_vect.y = 0;
proc->types.pointer.scroll_throw_vect.x = 0;
}
lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
if(proc->types.pointer.scroll_dir == LV_DIR_VER) {
proc->types.pointer.scroll_throw_vect.x = 0;
/*If no snapping "throw"*/
if(align_y == LV_SCROLL_SNAP_NONE) {
proc->types.pointer.scroll_throw_vect.y =
proc->types.pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
proc->types.pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.y, st, sb, LV_DIR_VER);
lv_obj_scroll_by(scroll_obj, 0, proc->types.pointer.scroll_throw_vect.y, LV_ANIM_OFF);
}
/*With snapping find the nearest snap point and scroll there*/
else {
lv_coord_t diff_y = scroll_throw_predict_y(proc);
proc->types.pointer.scroll_throw_vect.y = 0;
scroll_limit_diff(proc, NULL, &diff_y);
lv_coord_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
}
}
else if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
proc->types.pointer.scroll_throw_vect.y = 0;
/*If no snapping "throw"*/
if(align_x == LV_SCROLL_SNAP_NONE) {
proc->types.pointer.scroll_throw_vect.x =
proc->types.pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
proc->types.pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.x, sl ,sr, LV_DIR_HOR);
lv_obj_scroll_by(scroll_obj, proc->types.pointer.scroll_throw_vect.x, 0, LV_ANIM_OFF);
}
/*With snapping find the nearest snap point and scroll there*/
else {
lv_coord_t diff_x = scroll_throw_predict_x(proc);
proc->types.pointer.scroll_throw_vect.x = 0;
scroll_limit_diff(proc, &diff_x, NULL);
lv_coord_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
}
}
/*Check if the scroll has finished*/
if(proc->types.pointer.scroll_throw_vect.x == 0 && proc->types.pointer.scroll_throw_vect.y == 0) {
/*Revert if scrolled in*/
/*If vertically scrollable and not controlled by snap*/
if(align_y == LV_SCROLL_SNAP_NONE) {
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
if(st > 0 || sb > 0) {
if(st < 0) {
lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
}
else if(sb < 0) {
lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
}
}
}
/*If horizontally scrollable and not controlled by snap*/
if(align_x == LV_SCROLL_SNAP_NONE) {
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
if (sl > 0 || sr > 0) {
if(sl < 0) {
lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
}
else if(sr < 0) {
lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
}
}
}
lv_event_send(scroll_obj, LV_EVENT_SCROLL_END, indev_act);
if(proc->reset_query) return;
proc->types.pointer.scroll_dir = LV_DIR_NONE;
proc->types.pointer.scroll_obj = NULL;
}
}
/**
* Predict where would a scroll throw end
* @param indev pointer to an input device
* @param dir `LV_DIR_VER` or `LV_DIR_HOR`
* @return the difference compared to the current position when the throw would be finished
*/
lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
{
if(indev == NULL) return 0;
lv_coord_t v;
switch(dir) {
case LV_DIR_VER:
v = indev->proc.types.pointer.scroll_throw_vect_ori.y;
break;
case LV_DIR_HOR:
v = indev->proc.types.pointer.scroll_throw_vect_ori.x;
break;
default:
return 0;
}
lv_coord_t scroll_throw = indev->driver->scroll_throw;
lv_coord_t sum = 0;
while(v) {
sum += v;
v = v * (100 - scroll_throw) / 100;
}
return sum;
}
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
{
p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc)
{
lv_obj_t * obj_candidate = NULL;
lv_dir_t dir_candidate = LV_DIR_NONE;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_limit = indev_act->driver->scroll_limit;
/*Go until find an scrollable object in the current direction
*More precisely:
* 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
* 2. Scrollable means it has some content out of it's area
* 3. If an object can be scrolled into the current direction then use it ("real match"")
* 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
* 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
lv_obj_t * obj_act = proc->types.pointer.act_obj;
while(obj_act) {
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
/*If this object don't want to chain the scroll ot the parent stop searching*/
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN) == false) break;
obj_act = lv_obj_get_parent(obj_act);
continue;
}
/*Decide if it's a horizontal or vertical scroll*/
bool hor_en = false;
bool ver_en = false;
if(LV_ABS(proc->types.pointer.scroll_sum.x) > LV_ABS(proc->types.pointer.scroll_sum.y)) {
hor_en = true;
}
else {
ver_en = true;
}
/*Consider both up-down or left/right scrollable according to the current direction*/
bool up_en = ver_en;
bool down_en = ver_en;
bool left_en = hor_en;
bool right_en = hor_en;
/*The object might have disabled some directions.*/
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
/*The object is scrollable to a direction if its content overflow in that direction.*/
lv_coord_t st = lv_obj_get_scroll_top(obj_act);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj_act);
lv_coord_t sl = lv_obj_get_scroll_left(obj_act);
lv_coord_t sr = lv_obj_get_scroll_right(obj_act);
/*If this object is scrollable into the current scroll direction then save it as a candidate.
*It's important only to be scrollable on the current axis (hor/ver) because if the scroll
*is propagated to this object it can show at least elastic scroll effect.
*But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
if((st > 0 || sb > 0) &&
((up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
(down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit)))
{
obj_candidate = obj_act;
dir_candidate = LV_DIR_VER;
}
if((sl > 0 || sr > 0) &&
((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
(right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit)))
{
obj_candidate = obj_act;
dir_candidate = LV_DIR_HOR;
}
if(st <= 0) up_en = false;
if(sb <= 0) down_en = false;
if(sl <= 0) left_en = false;
if(sr <= 0) right_en = false;
/*If the object really can be scrolled into the current direction the use it.*/
if((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
(right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit) ||
(up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
(down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit))
{
proc->types.pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
break;
}
/*If this object don't want to chain the scroll ot the parent stop searching*/
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN) == false) break;
/*Try the parent*/
obj_act = lv_obj_get_parent(obj_act);
}
/*Use the last candidate*/
if(obj_candidate) {
proc->types.pointer.scroll_dir = dir_candidate;
proc->types.pointer.scroll_obj = obj_candidate;
proc->types.pointer.scroll_sum.x = 0;
proc->types.pointer.scroll_sum.y = 0;
}
return obj_candidate;
}
static void init_scroll_limits(_lv_indev_proc_t * proc)
{
lv_obj_t * obj = proc->types.pointer.scroll_obj;
/*If there no STOP allow scrolling anywhere*/
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
lv_area_set(&proc->types.pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
}
/*With STOP limit the scrolling to the perv and next snap point*/
else {
switch(lv_obj_get_scroll_snap_y(obj)) {
case LV_SCROLL_SNAP_START:
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
break;
case LV_SCROLL_SNAP_END:
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
break;
case LV_SCROLL_SNAP_CENTER: {
lv_coord_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
break;
}
default:
proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
break;
}
switch(lv_obj_get_scroll_snap_x(obj)) {
case LV_SCROLL_SNAP_START:
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
break;
case LV_SCROLL_SNAP_END:
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
break;
case LV_SCROLL_SNAP_CENTER: {
lv_coord_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
break;
}
default:
proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
break;
}
}
/*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
if(proc->types.pointer.scroll_area.x1 == 0) proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
if(proc->types.pointer.scroll_area.x2 == 0) proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
if(proc->types.pointer.scroll_area.y1 == 0) proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
if(proc->types.pointer.scroll_area.y2 == 0) proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
}
/**
* Search for snap point in the `min` - `max` range.
* @param obj the object on which snap point should be found
* @param min ignore snap points smaller then this. (Absolute coordinate)
* @param max ignore snap points greater then this. (Absolute coordinate)
* @param ofs offset to snap points. Useful the get a snap point in an imagined case
* what if children are already moved by this value
* @return the distance of the snap point.
*/
static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
{
lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
if(align == LV_SCROLL_SNAP_NONE) return 0;
lv_coord_t dist = LV_COORD_MAX;
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPABLE)) {
lv_coord_t x_child = 0;
lv_coord_t x_parent = 0;
switch(align) {
case LV_SCROLL_SNAP_START:
x_child = child->coords.x1;
x_parent = obj->coords.x1 + pad_left;
break;
case LV_SCROLL_SNAP_END:
x_child = child->coords.x2;
x_parent = obj->coords.x2 - pad_right;
break;
case LV_SCROLL_SNAP_CENTER:
x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
break;
default:
continue;
}
x_child += ofs;
if(x_child >= min && x_child <= max) {
lv_coord_t x = x_child - x_parent;
if(LV_ABS(x) < LV_ABS(dist)) dist = x;
}
}
}
return dist == LV_COORD_MAX ? 0: -dist;
}
/**
* Search for snap point in the `min` - `max` range.
* @param obj the object on which snap point should be found
* @param min ignore snap points smaller then this. (Absolute coordinate)
* @param max ignore snap points greater then this. (Absolute coordinate)
* @param ofs offset to snap points. Useful the get a snap point in an imagined case
* what if children are already moved by this value
* @return the distance of the snap point.
*/
static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
{
lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
if(align == LV_SCROLL_SNAP_NONE) return 0;
lv_coord_t dist = LV_COORD_MAX;
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPABLE)) {
lv_coord_t y_child = 0;
lv_coord_t y_parent = 0;
switch(align) {
case LV_SCROLL_SNAP_START:
y_child = child->coords.y1;
y_parent = obj->coords.y1 + pad_top;
break;
case LV_SCROLL_SNAP_END:
y_child = child->coords.y2;
y_parent = obj->coords.y2 - pad_bottom;
break;
case LV_SCROLL_SNAP_CENTER:
y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
break;
default:
continue;
}
y_child += ofs;
if(y_child >= min && y_child <= max) {
lv_coord_t y = y_child - y_parent;
if(LV_ABS(y) < LV_ABS(dist)) dist = y;
}
}
}
return dist == LV_COORD_MAX ? 0 : -dist;
}
static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y)
{
if(diff_y) {
if(proc->types.pointer.scroll_sum.y + *diff_y < proc->types.pointer.scroll_area.y1) {
*diff_y = proc->types.pointer.scroll_area.y1 - proc->types.pointer.scroll_sum.y;
}
if(proc->types.pointer.scroll_sum.y + *diff_y > proc->types.pointer.scroll_area.y2) {
*diff_y = proc->types.pointer.scroll_area.y2 - proc->types.pointer.scroll_sum.y;
}
}
if(diff_x) {
if(proc->types.pointer.scroll_sum.x + *diff_x < proc->types.pointer.scroll_area.x1) {
*diff_x = proc->types.pointer.scroll_area.x1 - proc->types.pointer.scroll_sum.x;
}
if(proc->types.pointer.scroll_sum.x + *diff_x > proc->types.pointer.scroll_area.x2) {
*diff_x = proc->types.pointer.scroll_area.x2 - proc->types.pointer.scroll_sum.x;
}
}
}
static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc)
{
lv_coord_t y = proc->types.pointer.scroll_throw_vect.y;
lv_coord_t move = 0;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
while(y) {
move += y;
y = y * (100 - scroll_throw) / 100;
}
return move;
}
static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc)
{
lv_coord_t x = proc->types.pointer.scroll_throw_vect.x;
lv_coord_t move = 0;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
while(x) {
move += x;
x = x * (100 - scroll_throw) / 100;
}
return move;
}
static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end, lv_dir_t dir)
{
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
/*If there is snapping in the current direction don't use the elastic factor because
*it's natural that the first and last items are scrolled (snapped) in.*/
lv_scroll_snap_t snap;
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
lv_obj_t * act_obj = lv_indev_get_obj_act();
lv_coord_t snap_point = 0;
lv_coord_t act_obj_point = 0;
if(dir == LV_DIR_HOR) {
lv_coord_t pad_left = lv_obj_get_style_pad_left(scroll_obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(scroll_obj, LV_PART_MAIN);
switch(snap) {
case LV_SCROLL_SNAP_CENTER:
snap_point = pad_left + (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2 + scroll_obj->coords.x1;
act_obj_point = lv_area_get_width(&act_obj->coords) / 2 + act_obj->coords.x1;
break;
case LV_SCROLL_SNAP_START:
snap_point = scroll_obj->coords.x1 + pad_left;
act_obj_point = act_obj->coords.x1;
break;
case LV_SCROLL_SNAP_END:
snap_point = scroll_obj->coords.x2 - pad_right;
act_obj_point = act_obj->coords.x2;
break;
}
} else {
lv_coord_t pad_top = lv_obj_get_style_pad_top(scroll_obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, LV_PART_MAIN);
switch(snap) {
case LV_SCROLL_SNAP_CENTER:
snap_point = pad_top + (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2 + scroll_obj->coords.y1;
act_obj_point = lv_area_get_height(&act_obj->coords) / 2 + act_obj->coords.y1;
break;
case LV_SCROLL_SNAP_START:
snap_point = scroll_obj->coords.y1 + pad_top;
act_obj_point = act_obj->coords.y1;
break;
case LV_SCROLL_SNAP_END:
snap_point = scroll_obj->coords.y2 - pad_bottom;
act_obj_point = act_obj->coords.y2;
break;
}
}
if(scroll_end < 0) {
if(snap != LV_SCROLL_SNAP_NONE && act_obj_point > snap_point) return diff;
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
else if(scroll_start < 0) {
if(snap != LV_SCROLL_SNAP_NONE && act_obj_point < snap_point) return diff;
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
} else {
/*Scroll back to the boundary if required*/
if(scroll_end + diff < 0) diff = - scroll_end;
if(scroll_start - diff < 0) diff = scroll_start;
}
return diff;
}

View File

@ -0,0 +1,65 @@
/**
* @file lv_indev_scroll.h
*
*/
#ifndef LV_INDEV_SCROLL_H
#define LV_INDEV_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Handle scrolling. Called by LVGL during input device processing
* @param proc pointer to an input device's proc field
*/
void _lv_indev_scroll_handler(_lv_indev_proc_t * proc);
/**
* Handle throwing after scrolling. Called by LVGL during input device processing
* @param proc pointer to an input device's proc field
*/
void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc);
/**
* Predict where would a scroll throw end
* @param indev pointer to an input device
* @param dir ` LV_DIR_VER` or `LV_DIR_HOR`
* @return the difference compared to the current position when the throw would be finished
*/
lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir);
/**
* Get the distance of the nearest snap point
* @param obj the object on which snap points should be found
* @param p save the distance of the found snap point there
*/
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_INDEV_SCROLL_H*/

View File

@ -0,0 +1,790 @@
/**
* @file lv_obj.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
#include "lv_refr.h"
#include "lv_group.h"
#include "lv_disp.h"
#include "lv_theme.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_async.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_gc.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../hal/lv_hal.h"
#include "../extra/lv_extra.h"
#include <stdint.h>
#include <string.h>
#if LV_USE_GPU_STM32_DMA2D
#include "../gpu/lv_gpu_stm32_dma2d.h"
#endif
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
#define LV_OBJ_DEF_WIDTH (LV_DPX(100))
#define LV_OBJ_DEF_HEIGHT (LV_DPX(50))
#define GRID_DEBUG 0 /*Draw rectangles on grid cells*/
#define STYLE_TRANSITION_MAX 32
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_obj_draw(lv_event_t * e);
static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_scrollbar(lv_obj_t * obj, const lv_area_t * clip_area);
static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state);
/**********************
* STATIC VARIABLES
**********************/
static bool lv_initialized = false;
const lv_obj_class_t lv_obj_class = {
.constructor_cb = lv_obj_constructor,
.destructor_cb = lv_obj_destructor,
.event_cb = lv_obj_event,
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
.editable = LV_OBJ_CLASS_EDITABLE_FALSE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
.instance_size = (sizeof(lv_obj_t)),
.base_class = NULL,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_init(void)
{
/*Do nothing if already initialized*/
if(lv_initialized) {
LV_LOG_WARN("lv_init: already inited");
return;
}
LV_LOG_INFO("begin");
/*Initialize the misc modules*/
lv_mem_init();
_lv_timer_core_init();
_lv_fs_init();
_lv_anim_core_init();
_lv_group_init();
#if LV_USE_GPU_STM32_DMA2D
/*Initialize DMA2D GPU*/
lv_gpu_stm32_dma2d_init();
#endif
_lv_obj_style_init();
_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
_lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
/*Initialize the screen refresh system*/
_lv_refr_init();
_lv_img_decoder_init();
#if LV_IMG_CACHE_DEF_SIZE
lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
#endif
/*Test if the IDE has UTF-8 encoding*/
char * txt = "Á";
uint8_t * txt_u8 = (uint8_t *)txt;
if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
LV_LOG_WARN("The strings has no UTF-8 encoding. Non-ASCII characters won't be displayed.")
}
uint32_t endianess_test = 0x11223344;
uint8_t * endianess_test_p = (uint8_t*) &endianess_test;
bool big_endian = endianess_test_p[0] == 0x11 ? true : false;
if(big_endian) {
LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1, "It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h");
} else {
LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0, "It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h");
}
#if LV_USE_ASSERT_MEM_INTEGRITY
LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower")
#endif
#if LV_USE_ASSERT_OBJ
LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower")
#endif
#if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE
LV_LOG_WARN("Log level is set the Trace which makes LVGL much slower")
#endif
lv_extra_init();
lv_initialized = true;
LV_LOG_TRACE("finished");
}
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
void lv_deinit(void)
{
_lv_gc_clear_roots();
lv_disp_set_default(NULL);
lv_mem_deinit();
lv_initialized = false;
LV_LOG_INFO("lv_deinit done");
#if LV_USE_LOG
lv_log_register_print_cb(NULL);
#endif
}
#endif
lv_obj_t * lv_obj_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/*-----------------
* Attribute set
*----------------*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
bool was_on_layout = lv_obj_is_layout_positioned(obj);
if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
obj->flags |= f;
if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj);
}
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
}
void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
bool was_on_layout = lv_obj_is_layout_positioned(obj);
obj->flags &= (~f);
if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj);
if(lv_obj_is_layout_positioned(obj)) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
}
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
}
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t new_state = obj->state | state;
if(obj->state != new_state) {
lv_obj_set_state(obj, new_state);
}
}
void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t new_state = obj->state & (~state);
if(obj->state != new_state) {
lv_obj_set_state(obj, new_state);
}
}
/*=======================
* Getter functions
*======================*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return (obj->flags & f) == f ? true : false;
}
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return (obj->flags & f) ? true : false;
}
lv_state_t lv_obj_get_state(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->state;
}
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->state & state ? true : false;
}
void * lv_obj_get_group(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr) return obj->spec_attr->group_p;
else return NULL;
}
/*-------------------
* OTHER FUNCTIONS
*------------------*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) {
static uint32_t x = 0;
x++;
obj->spec_attr = lv_mem_alloc(sizeof(_lv_obj_spec_attr_t));
LV_ASSERT_MALLOC(obj->spec_attr);
if(obj->spec_attr == NULL) return;
lv_memset_00(obj->spec_attr, sizeof(_lv_obj_spec_attr_t));
obj->spec_attr->scroll_dir = LV_DIR_ALL;
obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
}
}
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
if(obj == NULL) return false;
return obj->class_p == class_p ? true : false;
}
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
const lv_obj_class_t * obj_class = obj->class_p;
while(obj_class) {
if(obj_class == class_p) return true;
obj_class = obj_class->base_class;
}
return false;
}
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
{
return obj->class_p;
}
bool lv_obj_is_valid(const lv_obj_t * obj)
{
lv_disp_t * disp = lv_disp_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) return true;
bool found = obj_valid_child(disp->screens[i], obj);
if(found) return true;
}
disp = lv_disp_get_next(disp);
}
return false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_obj_t * parent = obj->parent;
if(parent) {
lv_coord_t sl = lv_obj_get_scroll_left(parent);
lv_coord_t st = lv_obj_get_scroll_top(parent);
obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
obj->coords.y2 = obj->coords.y1 - 1;
obj->coords.x1 = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
obj->coords.x2 = obj->coords.x1 - 1;
}
/*Set attributes*/
obj->flags = LV_OBJ_FLAG_CLICKABLE;
obj->flags |= LV_OBJ_FLAG_SNAPABLE;
if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
if(obj->spec_attr) {
if(obj->spec_attr->children) {
lv_mem_free(obj->spec_attr->children);
obj->spec_attr->children = NULL;
}
if(obj->spec_attr->event_dsc) {
lv_mem_free(obj->spec_attr->event_dsc);
obj->spec_attr->event_dsc = NULL;
}
lv_mem_free(obj->spec_attr);
obj->spec_attr = NULL;
}
}
static void lv_obj_draw(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res == LV_COVER_RES_MASKED) return;
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
info->res = LV_COVER_RES_MASKED;
return;
}
/*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
if(_lv_area_is_in(info->area, &coords, r) == false) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_blend_mode(obj, LV_PART_MAIN) != LV_BLEND_MODE_NORMAL) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
#endif
if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
info->res = LV_COVER_RES_COVER;
}
else if(code == LV_EVENT_DRAW_MAIN) {
const lv_area_t * clip_area = lv_event_get_param(e);
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
/*If the border is drawn later disable loading its properties*/
if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
draw_dsc.border_post = 1;
}
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
lv_draw_rect(&coords, clip_area, &draw_dsc);
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
lv_draw_mask_radius_init(mp, &obj->coords, r, false);
/*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
lv_draw_mask_add(mp, obj + 8);
}
#endif
}
else if(code == LV_EVENT_DRAW_POST) {
const lv_area_t * clip_area = lv_event_get_param(e);
draw_scrollbar(obj, clip_area);
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
lv_mem_buf_release(param);
}
#endif
/*If the border is drawn later disable loading other properties*/
if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
draw_dsc.bg_opa = LV_OPA_TRANSP;
draw_dsc.outline_opa = LV_OPA_TRANSP;
draw_dsc.shadow_opa = LV_OPA_TRANSP;
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
lv_draw_rect(&coords, clip_area, &draw_dsc);
}
}
}
static void draw_scrollbar(lv_obj_t * obj, const lv_area_t * clip_area)
{
lv_area_t hor_area;
lv_area_t ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
lv_draw_rect_dsc_t draw_dsc;
lv_res_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
if(sb_res != LV_RES_OK) return;
if(lv_area_get_size(&hor_area) > 0) lv_draw_rect(&hor_area, clip_area, &draw_dsc);
if(lv_area_get_size(&ver_area) > 0) lv_draw_rect(&ver_area, clip_area, &draw_dsc);
}
/**
* Initialize the draw descriptor for the scrollbar
* @param obj pointer to an object
* @param dsc the draw descriptor to initialize
* @return LV_RES_OK: the scrollbar is visible; LV_RES_INV: the scrollbar is not visible
*/
static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
{
lv_draw_rect_dsc_init(dsc);
dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
if(dsc->bg_opa > LV_OPA_MIN) {
dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
}
dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
if(dsc->border_opa > LV_OPA_MIN) {
dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
if(dsc->border_width > 0) {
dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
} else {
dsc->border_opa = LV_OPA_TRANSP;
}
}
#if LV_DRAW_COMPLEX
dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
if(dsc->shadow_opa > LV_OPA_MIN) {
dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
if(dsc->shadow_width > 0) {
dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
} else {
dsc->shadow_opa = LV_OPA_TRANSP;
}
}
lv_opa_t opa = lv_obj_get_style_opa(obj, LV_PART_SCROLLBAR);
if(opa < LV_OPA_MAX) {
dsc->bg_opa = (dsc->bg_opa * opa) >> 8;
dsc->border_opa = (dsc->bg_opa * opa) >> 8;
dsc->shadow_opa = (dsc->bg_opa * opa) >> 8;
}
if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP ) {
dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
return LV_RES_OK;
} else {
return LV_RES_INV;
}
#else
if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP) return LV_RES_OK;
else return LV_RES_INV;
#endif
}
static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED) {
lv_obj_add_state(obj, LV_STATE_PRESSED);
}
else if(code == LV_EVENT_RELEASED) {
lv_obj_clear_state(obj, LV_STATE_PRESSED);
void * param = lv_event_get_param(e);
/*Go the checked state if enabled*/
if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
else lv_obj_clear_state(obj, LV_STATE_CHECKED);
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_PRESS_LOST) {
lv_obj_clear_state(obj, LV_STATE_PRESSED);
}
else if(code == LV_EVENT_KEY) {
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_obj_add_state(obj, LV_STATE_CHECKED);
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_obj_clear_state(obj, LV_STATE_CHECKED);
}
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_FOCUSED) {
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
}
bool editing = false;
editing = lv_group_get_editing(lv_obj_get_group(obj));
lv_state_t state = LV_STATE_FOCUSED;
lv_indev_t * indev = lv_indev_get_act();
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
if(editing) {
state |= LV_STATE_EDITED;
lv_obj_add_state(obj, state);
}
else {
lv_obj_add_state(obj, state);
lv_obj_clear_state(obj, LV_STATE_EDITED);
}
}
else if(code == LV_EVENT_SCROLL_BEGIN) {
lv_obj_add_state(obj, LV_STATE_SCROLLED);
}
else if(code == LV_EVENT_SCROLL_END) {
lv_obj_clear_state(obj, LV_STATE_SCROLLED);
}
else if(code == LV_EVENT_DEFOCUSED) {
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout || align) {
lv_obj_mark_layout_as_dirty(obj);
}
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
lv_obj_mark_layout_as_dirty(child);
}
}
else if(code == LV_EVENT_CHILD_CHANGED) {
lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
lv_obj_mark_layout_as_dirty(obj);
}
}
else if(code == LV_EVENT_SCROLL_END) {
if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
lv_obj_invalidate(obj);
}
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
*s = LV_MAX(*s, d);
}
else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
lv_obj_draw(e);
}
}
/**
* Set the state (fully overwrite) of an object.
* If specified in the styles, transition animations will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the new state
*/
static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
{
if(obj->state == new_state) return;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t prev_state = obj->state;
obj->state = new_state;
_lv_style_state_cmp_t cmp_res = _lv_obj_style_state_compare(obj, prev_state, new_state);
/*If there is no difference in styles there is nothing else to do*/
if(cmp_res == _LV_STYLE_STATE_CMP_SAME) return;
_lv_obj_style_transition_dsc_t * ts = lv_mem_buf_get(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
lv_memset_00(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
uint32_t tsi = 0;
uint32_t i;
for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
if(obj_style->is_trans) continue;
lv_style_value_t v;
if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) == false) continue;
const lv_style_transition_dsc_t * tr = v.ptr;
/*Add the props to the set if not added yet or added but with smaller weight*/
uint32_t j;
for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
uint32_t t;
for(t = 0; t < tsi; t++) {
lv_style_selector_t selector = ts[t].selector;
lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
}
/*If not found add it*/
if(t == tsi) {
ts[tsi].time = tr->time;
ts[tsi].delay = tr->delay;
ts[tsi].path_cb = tr->path_xcb;
ts[tsi].prop = tr->props[j];
#if LV_USE_USER_DATA
ts[tsi].user_data = tr->user_data;
#endif
ts[tsi].selector = obj_style->selector;
tsi++;
}
}
}
for(i = 0;i < tsi; i++) {
lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
_lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
}
lv_mem_buf_release(ts);
if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_REDRAW) {
lv_obj_invalidate(obj);
}
else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
}
else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
lv_obj_refresh_ext_draw_size(obj);
}
}
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
{
/*Check all children of `parent`*/
uint32_t child_cnt = 0;
if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
if(child == obj_to_find) {
return true;
}
/*Check the children*/
bool found = obj_valid_child(child, obj_to_find);
if(found) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,388 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stddef.h>
#include <stdbool.h>
#include "../misc/lv_style.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_assert.h"
#include "../hal/lv_hal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
/**
* Possible states of a widget.
* OR-ed values are possible
*/
enum {
LV_STATE_DEFAULT = 0x0000,
LV_STATE_CHECKED = 0x0001,
LV_STATE_FOCUSED = 0x0002,
LV_STATE_FOCUS_KEY = 0x0004,
LV_STATE_EDITED = 0x0008,
LV_STATE_HOVERED = 0x0010,
LV_STATE_PRESSED = 0x0020,
LV_STATE_SCROLLED = 0x0040,
LV_STATE_DISABLED = 0x0080,
LV_STATE_USER_1 = 0x1000,
LV_STATE_USER_2 = 0x2000,
LV_STATE_USER_3 = 0x4000,
LV_STATE_USER_4 = 0x8000,
LV_STATE_ANY = 0xFFFF, /**< Special value can be used in some functions to target all states*/
};
typedef uint16_t lv_state_t;
/**
* The possible parts of widgets.
* The parts can be considered as the internal building block of the widgets.
* E.g. slider = background + indicator + knob
* Note every part is used by every widget
*/
enum {
LV_PART_MAIN = 0x000000, /**< A background like rectangle*/
LV_PART_SCROLLBAR = 0x010000, /**< The scrollbar(s)*/
LV_PART_INDICATOR = 0x020000, /**< Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox*/
LV_PART_KNOB = 0x030000, /**< Like handle to grab to adjust the value*/
LV_PART_SELECTED = 0x040000, /**< Indicate the currently selected option or section*/
LV_PART_ITEMS = 0x050000, /**< Used if the widget has multiple similar elements (e.g. tabel cells)*/
LV_PART_TICKS = 0x060000, /**< Ticks on scale e.g. for a chart or meter*/
LV_PART_CURSOR = 0x070000, /**< Mark a specific place e.g. for text area's cursor or on a chart*/
LV_PART_CUSTOM_FIRST = 0x080000, /**< Extension point for custom widgets*/
LV_PART_ANY = 0x0F0000, /**< Special value can be used in some functions to target all parts*/
};
typedef uint32_t lv_part_t;
/**
* On/Off features controlling the object's behavior.
* OR-ed values are possible
*/
enum {
LV_OBJ_FLAG_HIDDEN = (1 << 0), /**< Make the object hidden. (Like it wasn't there at all)*/
LV_OBJ_FLAG_CLICKABLE = (1 << 1), /**< Make the object clickable by the input devices*/
LV_OBJ_FLAG_CLICK_FOCUSABLE = (1 << 2), /**< Add focused state to the object when clicked*/
LV_OBJ_FLAG_CHECKABLE = (1 << 3), /**< Toggle checked state when the object is clicked*/
LV_OBJ_FLAG_SCROLLABLE = (1 << 4), /**< Make the object scrollable*/
LV_OBJ_FLAG_SCROLL_ELASTIC = (1 << 5), /**< Allow scrolling inside but with slower speed*/
LV_OBJ_FLAG_SCROLL_MOMENTUM = (1 << 6), /**< Make the object scroll further when "thrown"*/
LV_OBJ_FLAG_SCROLL_ONE = (1 << 7), /**< Allow scrolling only one snapable children*/
LV_OBJ_FLAG_SCROLL_CHAIN = (1 << 8), /**< Allow propagating the scroll to a parent*/
LV_OBJ_FLAG_SCROLL_ON_FOCUS = (1 << 9), /**< Automatically scroll object to make it visible when focused*/
LV_OBJ_FLAG_SNAPABLE = (1 << 10), /**< If scroll snap is enabled on the parent it can snap to this object*/
LV_OBJ_FLAG_PRESS_LOCK = (1 << 11), /**< Keep the object pressed even if the press slid from the object*/
LV_OBJ_FLAG_EVENT_BUBBLE = (1 << 12), /**< Propagate the events to the parent too*/
LV_OBJ_FLAG_GESTURE_BUBBLE = (1 << 13), /**< Propagate the gestures to the parent*/
LV_OBJ_FLAG_ADV_HITTEST = (1 << 14), /**< Allow performing more accurate hit (click) test. E.g. consider rounded corners.*/
LV_OBJ_FLAG_IGNORE_LAYOUT = (1 << 15), /**< Make the object position-able by the layouts*/
LV_OBJ_FLAG_FLOATING = (1 << 16), /**< Do not scroll the object when the parent scrolls and ignore layout*/
LV_OBJ_FLAG_LAYOUT_1 = (1 << 23), /** Custom flag, free to use by layouts*/
LV_OBJ_FLAG_LAYOUT_2 = (1 << 24), /** Custom flag, free to use by layouts*/
LV_OBJ_FLAG_WIDGET_1 = (1 << 25), /** Custom flag, free to use by widget*/
LV_OBJ_FLAG_WIDGET_2 = (1 << 26), /** Custom flag, free to use by widget*/
LV_OBJ_FLAG_USER_1 = (1 << 27), /** Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_2 = (1 << 28), /** Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_3 = (1 << 29), /** Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_4 = (1 << 30), /** Custom flag, free to use by user*/
};
typedef uint32_t lv_obj_flag_t;
#include "lv_obj_tree.h"
#include "lv_obj_pos.h"
#include "lv_obj_scroll.h"
#include "lv_obj_style.h"
#include "lv_obj_draw.h"
#include "lv_obj_class.h"
#include "lv_event.h"
#include "lv_group.h"
/**
* Make the base object's class publicly available.
*/
extern const lv_obj_class_t lv_obj_class;
/**
* Special, rarely used attributes.
* They are allocated automatically if any elements is set.
*/
typedef struct {
struct _lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
uint32_t child_cnt; /**< Number of children*/
lv_group_t * group_p;
struct _lv_event_dsc_t * event_dsc; /**< Dynamically allocated event callback and user data array*/
lv_point_t scroll; /**< The current X/Y scroll offset*/
lv_coord_t ext_click_pad; /**< Extra click padding in all direction*/
lv_coord_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
lv_scrollbar_mode_t scrollbar_mode :2; /**< How to display scrollbars*/
lv_scroll_snap_t scroll_snap_x : 2; /**< Where to align the snapable children horizontally*/
lv_scroll_snap_t scroll_snap_y : 2; /**< Where to align the snapable children horizontally*/
//lv_dir_t scroll_dir :4; /**< The allowed scroll direction(s)*/
lv_dir_t scroll_dir; /**< The allowed scroll direction(s)*/
uint8_t event_dsc_cnt; /**< Number of event callabcks stored in `event_cb` array*/
}_lv_obj_spec_attr_t;
typedef struct _lv_obj_t {
const lv_obj_class_t * class_p;
struct _lv_obj_t * parent;
_lv_obj_spec_attr_t * spec_attr;
_lv_obj_style_t * styles;
#if LV_USE_USER_DATA
void * user_data;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
lv_state_t state;
uint16_t layout_inv :1;
uint16_t scr_layout_inv :1;
uint16_t skip_trans :1;
uint16_t style_cnt :6;
uint16_t h_layout :1;
uint16_t w_layout :1;
}lv_obj_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize LVGL library.
* Should be called before any other LVGL related function.
*/
void lv_init(void);
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
/**
* Deinit the 'lv' library
* Currently only implemented when not using custom allocators, or GC is enabled.
*/
void lv_deinit(void);
#endif
/**
* Create a base object (a rectangle)
* @param parent pointer to a parent object. If NULL then a screen will be created.
* @return pointer to the new object
*/
lv_obj_t * lv_obj_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set one or more flags
* @param obj pointer to an object
* @param f R-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Clear one or more flags
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Add one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state);
/**
* Remove one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state);
/**
* Set the user_data field of the object
* @param obj pointer to an object
* @param user_data pointer to the new user_data.
*/
#if LV_USE_USER_DATA
static inline void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
{
obj->user_data = user_data;
}
#endif
/*=======================
* Getter functions
*======================*/
/**
* Check if a given flag or all the given flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: all flags are set; false: not all flags are set
*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Check if a given flag or any of the flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: at lest one flag flag is set; false: none of the flags are set
*/
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Get the state of an object
* @param obj pointer to an object
* @return the state (OR-ed values from `lv_state_t`)
*/
lv_state_t lv_obj_get_state(const lv_obj_t * obj);
/**
* Check if the object is in a given state or not.
* @param obj pointer to an object
* @param state a state or combination of states to check
* @return true: `obj` is in `state`; false: `obj` is not in `state`
*/
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state);
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
void * lv_obj_get_group(const lv_obj_t * obj);
/**
* Get the user_data field of the object
* @param obj pointer to an object
* @return the pointer to the user_data of the object
*/
#if LV_USE_USER_DATA
static inline void * lv_obj_get_user_data(lv_obj_t * obj)
{
return obj->user_data;
}
#endif
/*=======================
* Other functions
*======================*/
/**
* Allocate special data for an object if not allocated yet.
* @param obj pointer to an object
*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj);
/**
* Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
* E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
* @param obj pointer to an object which type should be get
* @param buf pointer to an `lv_obj_type_t` buffer to store the types
*/
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Check if any object has a given class (type).
* It checks the ancestor classes too.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `obj` has the given class
*/
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Get the class (type) of the object
* @param obj pointer to an object
* @return the class (type) of the object
*/
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj);
/**
* Check if any object is still "alive", and part of the hierarchy
* @param obj pointer to an object
* @param obj_type type of the object. (e.g. "lv_btn")
* @return true: valid
*/
bool lv_obj_is_valid(const lv_obj_t * obj);
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the `obj`'s display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param obj an object whose display's dpi should be considered
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_obj_dpx(const lv_obj_t * obj, lv_coord_t n)
{
return _LV_DPX_CALC(lv_disp_get_dpi(lv_obj_get_disp(obj)), n);
}
/**********************
* MACROS
**********************/
#if LV_USE_ASSERT_OBJ
# define LV_ASSERT_OBJ(obj_p, obj_class) \
LV_ASSERT_MSG(obj_p != NULL, "The object is NULL"); \
LV_ASSERT_MSG(lv_obj_has_class(obj_p, obj_class) == true, "Incompatible object type."); \
LV_ASSERT_MSG(lv_obj_is_valid(obj_p) == true, "The object is invalid, deleted or corrupted?");
# else
# define LV_ASSERT_OBJ(obj_p, obj_class) do{}while(0)
#endif
#if LV_USE_LOG && LV_LOG_TRACE_OBJ_CREATE
# define LV_TRACE_OBJ_CREATE(...) LV_LOG_TRACE( __VA_ARGS__)
#else
# define LV_TRACE_OBJ_CREATE(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_H*/

View File

@ -0,0 +1,197 @@
/**
* @file lv_obj_class.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_theme.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_construct(lv_obj_t * obj);
static uint32_t get_instance_size(const lv_obj_class_t * class_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
{
LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", class_p, parent);
uint32_t s = get_instance_size(class_p);
lv_obj_t * obj = lv_mem_alloc(s);
if(obj == NULL) return NULL;
lv_memset_00(obj, s);
obj->class_p = class_p;
obj->parent = parent;
/*Create a screen*/
if(parent == NULL) {
LV_TRACE_OBJ_CREATE("creating a screen");
lv_disp_t * disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("No display created to so far. No place to assign the new screen");
return NULL;
}
if(disp->screens == NULL) {
disp->screens = lv_mem_alloc(sizeof(lv_obj_t *));
disp->screens[0] = obj;
disp->screen_cnt = 1;
} else {
disp->screen_cnt++;
disp->screens = lv_mem_realloc(disp->screens, sizeof(lv_obj_t *) * disp->screen_cnt);
disp->screens[disp->screen_cnt - 1] = obj;
}
/*Set coordinates to full screen size*/
obj->coords.x1 = 0;
obj->coords.y1 = 0;
obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1;
obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1;
}
/*Create a normal object*/
else {
LV_TRACE_OBJ_CREATE("creating normal object");
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent->spec_attr == NULL) {
lv_obj_allocate_spec_attr(parent);
}
if(parent->spec_attr->children == NULL) {
parent->spec_attr->children = lv_mem_alloc(sizeof(lv_obj_t *));
parent->spec_attr->children[0] = obj;
parent->spec_attr->child_cnt = 1;
} else {
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children, sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
}
}
return obj;
}
void lv_obj_class_init_obj(lv_obj_t * obj)
{
lv_obj_mark_layout_as_dirty(obj);
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj);
lv_obj_construct(obj);
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
lv_obj_refresh_self_size(obj);
lv_group_t * def_group = lv_group_get_default();
if(def_group && lv_obj_is_group_def(obj)) {
lv_group_add_obj(def_group, obj);
}
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
/*Call the ancestor's event handler to the parent to notify it about the new child.
*Also triggers layout update*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
/*Invalidate the area if not screen created*/
lv_obj_invalidate(obj);
}
}
void _lv_obj_destructor(lv_obj_t * obj)
{
if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj);
if(obj->class_p->base_class) {
/*Don't let the descendant methods run during destructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Call the base class's destructor too*/
_lv_obj_destructor(obj);
}
}
bool lv_obj_is_editable(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which editable is set*/
while(class_p && class_p->editable == LV_OBJ_CLASS_EDITABLE_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->editable == LV_OBJ_CLASS_EDITABLE_TRUE ? true : false;
}
bool lv_obj_is_group_def(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which group_def is set*/
while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE ? true : false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_construct(lv_obj_t * obj)
{
const lv_obj_class_t * original_class_p = obj->class_p;
if(obj->class_p->base_class) {
/*Don't let the descendant methods run during constructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Construct the base first*/
lv_obj_construct(obj);
}
/*Restore the original class*/
obj->class_p = original_class_p;
if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(obj->class_p, obj);
}
static uint32_t get_instance_size(const lv_obj_class_t * class_p)
{
/*Find a base in which instance size is set*/
const lv_obj_class_t * base = class_p;
while(base && base->instance_size == 0) base = base->base_class;
if(base == NULL) return 0; /*Never happens: set at least in `lv_obj` class*/
return base->instance_size;
}

View File

@ -0,0 +1,93 @@
/**
* @file lv_obj_class.h
*
*/
#ifndef LV_OBJ_CLASS_H
#define LV_OBJ_CLASS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
struct _lv_event_t;
typedef enum {
LV_OBJ_CLASS_EDITABLE_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_EDITABLE_TRUE,
LV_OBJ_CLASS_EDITABLE_FALSE,
}lv_obj_class_editable_t;
typedef enum {
LV_OBJ_CLASS_GROUP_DEF_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_GROUP_DEF_TRUE,
LV_OBJ_CLASS_GROUP_DEF_FALSE,
}lv_obj_class_group_def_t;
typedef void (*lv_obj_class_event_cb_t)(struct _lv_obj_class_t * class_p, struct _lv_event_t * e);
/**
* Describe the common methods of every object.
* Similar to a C++ class.
*/
typedef struct _lv_obj_class_t {
const struct _lv_obj_class_t * base_class;
void (*constructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
void (*destructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
#if LV_USE_USER_DATA
void * user_data;
#endif
void (*event_cb)(const struct _lv_obj_class_t * class_p, struct _lv_event_t * e); /**< Widget type specific event function*/
lv_coord_t width_def;
lv_coord_t height_def;
uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
uint32_t instance_size : 16;
}lv_obj_class_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an object form a class descriptor
* @param class_p pointer to a class
* @param parent pointer to an object where the new object should be created
* @return pointer to the created object
*/
struct _lv_obj_t * lv_obj_class_create_obj(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * parent);
void lv_obj_class_init_obj(struct _lv_obj_t * obj);
void _lv_obj_destructor(struct _lv_obj_t * obj);
bool lv_obj_is_editable(struct _lv_obj_t * obj);
bool lv_obj_is_group_def(struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_H*/

View File

@ -0,0 +1,365 @@
/**
* @file lv_obj_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw.h"
#include "lv_obj.h"
#include "lv_disp.h"
#include "lv_indev.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
draw_dsc->radius = lv_obj_get_style_radius(obj, part);
lv_opa_t main_opa = part != LV_PART_MAIN ? lv_obj_get_style_opa(obj, part) : LV_OPA_COVER;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa <= LV_OPA_MIN || main_opa <= LV_OPA_MIN) {
draw_dsc->bg_opa = LV_OPA_TRANSP;
draw_dsc->border_opa = LV_OPA_TRANSP;
draw_dsc->shadow_opa = LV_OPA_TRANSP;
draw_dsc->outline_opa = LV_OPA_TRANSP;
return;
}
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
if(draw_dsc->bg_opa > LV_OPA_MIN) {
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
draw_dsc->bg_grad_dir = lv_obj_get_style_bg_grad_dir(obj, part);
if(draw_dsc->bg_grad_dir != LV_GRAD_DIR_NONE) {
draw_dsc->bg_grad_color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
draw_dsc->bg_main_color_stop = lv_obj_get_style_bg_main_stop(obj, part);
draw_dsc->bg_grad_color_stop = lv_obj_get_style_bg_grad_stop(obj, part);
}
}
}
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
if(draw_dsc->border_opa > LV_OPA_MIN) {
draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
}
}
}
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(draw_dsc->outline_opa > LV_OPA_MIN) {
draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
draw_dsc->outline_color = lv_obj_get_style_outline_color(obj, part);
}
}
}
if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
if(draw_dsc->bg_img_src) {
draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
draw_dsc->bg_img_symbol_font= lv_obj_get_style_text_font(obj, part);
draw_dsc->bg_img_recolor = lv_obj_get_style_text_color(obj, part);
} else {
draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor(obj, part);
draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
}
}
}
}
if(draw_dsc->shadow_opa) {
draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
if(draw_dsc->shadow_width) {
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
draw_dsc->shadow_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
draw_dsc->shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
}
}
}
}
if(main_opa < LV_OPA_MAX) {
opa = (uint16_t)((uint16_t) main_opa * opa) >> 8;
}
if(opa < LV_OPA_MAX) {
draw_dsc->bg_opa = (uint16_t)((uint16_t)draw_dsc->bg_opa * opa) >> 8;
draw_dsc->border_opa = (uint16_t)((uint16_t)draw_dsc->border_opa * opa) >> 8;
draw_dsc->shadow_opa = (uint16_t)((uint16_t)draw_dsc->shadow_opa * opa) >> 8;
draw_dsc->outline_opa = (uint16_t)((uint16_t)draw_dsc->outline_opa * opa) >> 8;
}
#else /*LV_DRAW_COMPLEX*/
if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
if(draw_dsc->bg_opa > LV_OPA_MIN) {
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
}
}
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
if(draw_dsc->border_opa > LV_OPA_MIN) {
draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
}
}
}
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(draw_dsc->outline_opa > LV_OPA_MIN) {
draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
draw_dsc->outline_color = lv_obj_get_style_outline_color(obj, part);
}
}
}
if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
if(draw_dsc->bg_img_src) {
draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
draw_dsc->bg_img_symbol_font= lv_obj_get_style_text_font(obj, part);
draw_dsc->bg_img_recolor = lv_obj_get_style_text_color(obj, part);
} else {
draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor(obj, part);
draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
}
}
}
}
#endif
}
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc)
{
draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_text_color_filtered(obj, part);
draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part);
draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part);
draw_dsc->decor = lv_obj_get_style_text_decor(obj, part);
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
draw_dsc->font = lv_obj_get_style_text_font(obj, part);
#if LV_USE_BIDI
draw_dsc->bidi_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
#endif
draw_dsc->align = lv_obj_get_style_text_align(obj, part);
if(draw_dsc->align == LV_TEXT_ALIGN_AUTO) {
if(draw_dsc->bidi_dir == LV_BASE_DIR_RTL) draw_dsc->align = LV_TEXT_ALIGN_RIGHT;
else draw_dsc->align = LV_TEXT_ALIGN_LEFT;
}
}
void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc)
{
draw_dsc->opa = lv_obj_get_style_img_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa_scale = lv_obj_get_style_opa(obj, part);
if(opa_scale < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->angle = 0;
draw_dsc->zoom = LV_IMG_ZOOM_NONE;
draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
draw_dsc->recolor_opa = lv_obj_get_style_img_recolor_opa(obj, part);
if(draw_dsc->recolor_opa > 0) {
draw_dsc->recolor = lv_obj_get_style_img_recolor(obj, part);
}
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc)
{
draw_dsc->width = lv_obj_get_style_line_width(obj, part);
if(draw_dsc->width == 0) return;
draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_line_color(obj, part);
draw_dsc->dash_width = lv_obj_get_style_line_dash_width(obj, part);
if(draw_dsc->dash_width) {
draw_dsc->dash_gap = lv_obj_get_style_line_dash_gap(obj, part);
}
draw_dsc->round_start = lv_obj_get_style_line_rounded(obj, part);
draw_dsc->round_end = draw_dsc->round_start;
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc)
{
draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
if(draw_dsc->width == 0) return;
draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_arc_color(obj, part);
draw_dsc->img_src = lv_obj_get_style_arc_img_src(obj, part);
draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
lv_coord_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, uint32_t part)
{
lv_coord_t s = 0;
lv_coord_t sh_width = lv_obj_get_style_shadow_width(obj, part);
if(sh_width) {
lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
if(sh_opa > LV_OPA_MIN) {
sh_width = sh_width / 2 + 1; /*The blur adds only half width*/
sh_width += lv_obj_get_style_shadow_spread(obj, part);
lv_coord_t sh_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
lv_coord_t sh_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
s = LV_MAX(s, sh_width);
}
}
lv_coord_t outline_width = lv_obj_get_style_outline_width(obj, part);
if(outline_width) {
lv_opa_t outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(outline_opa > LV_OPA_MIN) {
lv_coord_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
s = LV_MAX(s, outline_pad + outline_width);
}
}
lv_coord_t w = lv_obj_get_style_transform_width(obj, part);
lv_coord_t h = lv_obj_get_style_transform_height(obj, part);
lv_coord_t wh = LV_MAX(w, h);
if(wh > 0) s += wh;
return s;
}
void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, const lv_area_t * clip_area)
{
lv_memset_00(dsc, sizeof(lv_obj_draw_part_dsc_t));
dsc->clip_area = clip_area;
}
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t s_old = _lv_obj_get_ext_draw_size(obj);
lv_coord_t s_new = 0;
lv_event_send(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
if(s_new != s_old) lv_obj_invalidate(obj);
/*Store the result if the special attrs already allocated*/
if(obj->spec_attr) {
obj->spec_attr->ext_draw_size = s_new;
}
/*Allocate spec. attrs. only if the result is not zero.
*Zero is the default value if the spec. attr. are not defined.*/
else if(s_new != 0) {
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->ext_draw_size = s_new;
}
if(s_new != s_old) lv_obj_invalidate(obj);
}
lv_coord_t _lv_obj_get_ext_draw_size(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->ext_draw_size;
else return 0;
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,145 @@
/**
* @file lv_obj_draw.h
*
*/
#ifndef LV_OBJ_DRAW_H
#define LV_OBJ_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../draw/lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
/** Cover check results.*/
typedef enum {
LV_COVER_RES_COVER = 0,
LV_COVER_RES_NOT_COVER = 1,
LV_COVER_RES_MASKED = 2,
}lv_cover_res_t;
typedef struct
{
const lv_area_t * clip_area; /**< The current clip area, required if you need to draw something in the event*/
lv_area_t * draw_area; /**< The area of the part being drawn*/
lv_draw_rect_dsc_t * rect_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for rectangle-like parts*/
lv_draw_label_dsc_t * label_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for text-like parts*/
lv_draw_line_dsc_t * line_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for line-like parts*/
lv_draw_img_dsc_t * img_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for image-like parts*/
lv_draw_arc_dsc_t * arc_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for arc-like parts*/
const lv_point_t * p1; /**< A point calculated during drawing. E.g. a point of chart or the center of an arc.*/
const lv_point_t * p2; /**< A point calculated during drawing. E.g. a point of chart.*/
char text[16]; /**< A text calculated during drawing. Can be modified. E.g. tick labels on a chart axis.*/
uint32_t part; /**< The current part for which the event is sent*/
uint32_t id; /**< The index of the part. E.g. a button's index on button matrix or table cell index.*/
lv_coord_t radius; /**< E.g. the radius of an arc (not the corner radius).*/
int32_t value; /**< A value calculated during drawing. E.g. Chart's tick line value.*/
const void * sub_part_ptr; /**< A pointer the identifies something in the part. E.g. chart series. */
}lv_obj_draw_part_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a rectangle draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor the initialize.
* If an `..._opa` filed is set to `LV_OPA_TRANSP` the related properties won't be initialized.
* Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`.
* @note Only the relevant fields will be set.
* E.g. if `border width == 0` the other border properties won't be evaluated.
*/
void lv_obj_init_draw_rect_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc);
/**
* Initialize a label draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor the initialize.
* If the `opa` filed is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized.
* Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_label_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc);
/**
* Initialize an image draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor the initialize.
* Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_img_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc);
/**
* Initialize a line draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor the initialize.
* Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_line_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc);
/**
* Initialize an arc draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor the initialize.
* Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_arc_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc);
/**
* Get the required extra size (around the object's part) to draw shadow, outline, value etc.
* @param obj pointer to an object
* @param part part of the object
* @return the extra size required around the object
*/
lv_coord_t lv_obj_calculate_ext_draw_size(struct _lv_obj_t * obj, uint32_t part);
/**
* Initialize a draw descriptor used in events.
* @param dsc pointer to a descriptor. Later it should be passed as parameter to an `LV_EEVNT_DRAW_PART_BEGIN/END` event.
* @param clip_area the current clip area of the drawing
*/
void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, const lv_area_t * clip_area);
/**
* Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size.
* The result will be saved in `obj`.
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_draw_size(struct _lv_obj_t * obj);
/**
* Get the extended draw area of an object.
* @param obj pointer to an object
* @return the size extended draw area around the real coordinates
*/
lv_coord_t _lv_obj_get_ext_draw_size(const struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_H*/

View File

@ -0,0 +1,980 @@
/**
* @file lv_obj_pos.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_disp.h"
#include "lv_refr.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void calc_auto_size(lv_obj_t * obj, lv_coord_t * w_out, lv_coord_t * h_out);
static void layout_update_core(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
static uint32_t layout_cnt;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_x(obj, x);
lv_obj_set_y(obj, y);
}
void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(lv_obj_is_layout_positioned(obj)) {
return;
}
lv_res_t res_x;
lv_style_value_t v_x;
res_x = lv_obj_get_local_style_prop(obj, LV_STYLE_X, &v_x, 0);
if((res_x == LV_RES_OK && v_x.num != x) || res_x == LV_RES_INV) {
lv_obj_set_style_x(obj, x, 0);
}
}
void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
{
if(lv_obj_is_layout_positioned(obj)) {
return;
}
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_res_t res_y;
lv_style_value_t v_y;
res_y = lv_obj_get_local_style_prop(obj, LV_STYLE_Y, &v_y, 0);
if((res_y == LV_RES_OK && v_y.num != y) || res_y == LV_RES_INV) {
lv_obj_set_style_y(obj, y, 0);
}
}
bool lv_obj_refr_size(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*If the width or height is set by a layout do not modify them*/
if(obj->w_layout && obj->h_layout) return false;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return false;
lv_coord_t w;
lv_coord_t sl_ori = lv_obj_get_scroll_left(obj);
bool w_content = false;
if(obj->w_layout) {
w = lv_obj_get_width(obj);
} else {
w = lv_obj_get_style_width(obj, LV_PART_MAIN);
w_content = w == LV_SIZE_CONTENT ? true : false;
/*Be sure the object is not scrolled when it has auto size*/
if(w_content) {
lv_obj_scroll_to_x(obj, 0, LV_ANIM_OFF);
calc_auto_size(obj, &w, NULL);
}
/*Calculate the sizes in percentage*/
bool pct_w = LV_COORD_IS_PCT(w) ? true : false;
lv_coord_t parent_w = lv_obj_get_content_width(parent);
if(pct_w) w = (LV_COORD_GET_PCT(w) * parent_w) / 100;
lv_coord_t minw = lv_obj_get_style_min_width(obj, LV_PART_MAIN);
lv_coord_t maxw = lv_obj_get_style_max_width(obj, LV_PART_MAIN);
w = lv_clamp_width(w, minw, maxw, parent_w);
}
lv_coord_t h;
lv_coord_t st_ori = lv_obj_get_scroll_top(obj);
bool h_content = false;
if(obj->h_layout) {
h = lv_obj_get_height(obj);
} else {
h = lv_obj_get_style_height(obj, LV_PART_MAIN);
h_content = h == LV_SIZE_CONTENT ? true : false;
/*Be sure the object is not scrolled when it has auto size*/
if(h_content) {
lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF);
calc_auto_size(obj, NULL, &h);
}
/*Calculate the sizes in percentage*/
bool pct_h = LV_COORD_IS_PCT(h) ? true : false;
lv_coord_t parent_h = lv_obj_get_content_height(parent);
if(pct_h) h = (LV_COORD_GET_PCT(h) * parent_h) / 100;
lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_MAIN);
lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_MAIN);
h = lv_clamp_height(h, minh, maxh, parent_h);
}
/*calc_auto_size set the scroll x/y to 0 so revert the original value*/
if(w_content || h_content) {
lv_obj_scroll_to(obj, sl_ori, st_ori, LV_ANIM_OFF);
}
/*Do nothing if the size is not changed*/
/*It is very important else recursive resizing can occur without size change*/
if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) return false;
/*Invalidate the original area*/
lv_obj_invalidate(obj);
/*Save the original coordinates*/
lv_area_t ori;
lv_obj_get_coords(obj, &ori);
/*Check if the object inside the parent or not*/
lv_area_t parent_fit_area;
lv_obj_get_content_coords(parent, &parent_fit_area);
/*If the object is already out of the parent and its position is changes
*surely the scrollbars also changes so invalidate them*/
bool on1 = _lv_area_is_in(&ori, &parent_fit_area, 0);
if(!on1) lv_obj_scrollbar_invalidate(parent);
/*Set the length and height
*Be sure the content is not scrolled in an invalid position on the new size*/
obj->coords.y2 = obj->coords.y1 + h - 1;
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
obj->coords.x1 = obj->coords.x2 - w + 1;
}
else {
obj->coords.x2 = obj->coords.x1 + w - 1;
}
/*Call the ancestor's event handler to the object with its new coordinates*/
lv_event_send(obj, LV_EVENT_SIZE_CHANGED, &ori);
/*Call the ancestor's event handler to the parent too*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
/*Invalidate the new area*/
lv_obj_invalidate(obj);
/*Be sure the bottom side is not remains scrolled in*/
/*With snapping the content can't be scrolled in*/
if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
lv_coord_t st = lv_obj_get_scroll_top(obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
if(sb < 0 && st > 0) {
sb = LV_MIN(st, -sb);
lv_obj_scroll_by(obj, 0, sb, LV_ANIM_OFF);
}
}
if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_t sr = lv_obj_get_scroll_right(obj);
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
/*Be sure the left side is not remains scrolled in*/
if(sr < 0 && sl > 0) {
sr = LV_MIN(sl, -sr);
lv_obj_scroll_by(obj, sr, 0, LV_ANIM_OFF);
}
} else {
/*Be sure the right side is not remains scrolled in*/
if(sl < 0 && sr > 0) {
sr = LV_MIN(sr, -sl);
lv_obj_scroll_by(obj, sl, 0, LV_ANIM_OFF);
}
}
}
/*If the object was out of the parent invalidate the new scrollbar area too.
*If it wasn't out of the parent but out now, also invalidate the srollbars*/
bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
return true;
}
void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_width(obj, w);
lv_obj_set_height(obj, h);
}
void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_res_t res_w;
lv_style_value_t v_w;
res_w = lv_obj_get_local_style_prop(obj, LV_STYLE_WIDTH, &v_w, 0);
if((res_w == LV_RES_OK && v_w.num != w) || res_w == LV_RES_INV) {
lv_obj_set_style_width(obj, w, 0);
}
}
void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_res_t res_h;
lv_style_value_t v_h;
res_h = lv_obj_get_local_style_prop(obj, LV_STYLE_HEIGHT, &v_h, 0);
if((res_h == LV_RES_OK && v_h.num != h) || res_h == LV_RES_INV) {
lv_obj_set_style_height(obj, h, 0);
}
}
void lv_obj_set_content_width(lv_obj_t * obj, lv_coord_t w)
{
lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_obj_set_width(obj, w + pleft + pright + 2 * border_width);
}
void lv_obj_set_content_height(lv_obj_t * obj, lv_coord_t h)
{
lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_obj_set_height(obj, h + ptop + pbottom + 2 * border_width);
}
void lv_obj_set_layout(lv_obj_t * obj, uint32_t layout)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_style_layout(obj, layout, 0);
lv_obj_mark_layout_as_dirty(obj);
}
bool lv_obj_is_layout_positioned(const lv_obj_t * obj)
{
if(lv_obj_has_flag_any(obj, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_FLOATING)) return false;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return false;
uint32_t layout = lv_obj_get_style_layout(parent, LV_PART_MAIN);
if(layout) return true;
else return false;
}
void lv_obj_mark_layout_as_dirty(lv_obj_t * obj)
{
obj->layout_inv = 1;
/*Mark the screen as dirty too to mark that there is an something to do on this screen*/
lv_obj_t * scr = lv_obj_get_screen(obj);
scr->scr_layout_inv = 1;
/*Make the display refreshing*/
lv_disp_t * disp = lv_obj_get_disp(scr);
lv_timer_resume(disp->refr_timer);
}
void lv_obj_update_layout(const lv_obj_t * obj)
{
static bool mutex = false;
if(mutex) {
LV_LOG_TRACE("Already running, returning")
return;
}
mutex = true;
lv_obj_t * scr = lv_obj_get_screen(obj);
/*Repeat until there where layout invalidations*/
while(scr->scr_layout_inv) {
LV_LOG_INFO("Layout update begin")
scr->scr_layout_inv = 0;
layout_update_core(scr);
LV_LOG_TRACE("Layout update end")
}
mutex = false;
}
uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data)
{
layout_cnt++;
LV_GC_ROOT(_lv_layout_list) = lv_mem_realloc(LV_GC_ROOT(_lv_layout_list), layout_cnt * sizeof(lv_layout_dsc_t));
LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_layout_list));
LV_GC_ROOT(_lv_layout_list)[layout_cnt - 1].cb = cb;
LV_GC_ROOT(_lv_layout_list)[layout_cnt - 1].user_data = user_data;
return layout_cnt; /*No -1 to skip 0th index*/
}
void lv_obj_set_align(lv_obj_t * obj, lv_align_t align)
{
lv_obj_set_style_align(obj, align, 0);
}
void lv_obj_align(lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
{
lv_obj_set_style_align(obj, align, 0);
lv_obj_set_pos(obj, x_ofs, y_ofs);
}
void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_update_layout(obj);
if(base == NULL) base = lv_obj_get_parent(obj);
LV_ASSERT_OBJ(base, MY_CLASS);
lv_coord_t x = 0;
lv_coord_t y = 0;
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN) + border_width;
lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN) + border_width;
if(align == LV_ALIGN_DEFAULT) {
if(lv_obj_get_style_base_dir(parent, LV_PART_MAIN) == LV_BASE_DIR_RTL) align = LV_ALIGN_TOP_RIGHT;
else align = LV_ALIGN_TOP_LEFT;
}
switch(align) {
case LV_ALIGN_CENTER:
x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2;
y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2;
break;
case LV_ALIGN_TOP_LEFT:
x = 0;
y = 0;
break;
case LV_ALIGN_TOP_MID:
x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2;
y = 0;
break;
case LV_ALIGN_TOP_RIGHT:
x = lv_obj_get_content_width(base) - lv_obj_get_width(obj);
y = 0;
break;
case LV_ALIGN_BOTTOM_LEFT:
x = 0;
y = lv_obj_get_content_height(base) - lv_obj_get_height(obj);
break;
case LV_ALIGN_BOTTOM_MID:
x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2;
y = lv_obj_get_content_height(base) - lv_obj_get_height(obj);
break;
case LV_ALIGN_BOTTOM_RIGHT:
x = lv_obj_get_content_width(base) - lv_obj_get_width(obj);
y = lv_obj_get_content_height(base) - lv_obj_get_height(obj);
break;
case LV_ALIGN_LEFT_MID:
x = 0;
y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2;
break;
case LV_ALIGN_RIGHT_MID:
x = lv_obj_get_content_width(base) - lv_obj_get_width(obj);
y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2;
break;
case LV_ALIGN_OUT_TOP_LEFT:
x = -pleft;
y = -lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_TOP_MID:
x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2 - pleft;
y = -lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_TOP_RIGHT:
x = lv_obj_get_width(base) - lv_obj_get_width(obj) - pleft;
y = -lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_BOTTOM_LEFT:
x = - pleft;
y = lv_obj_get_height(base) - ptop;
break;
case LV_ALIGN_OUT_BOTTOM_MID:
x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2 - pleft;
y = lv_obj_get_height(base) - ptop;
break;
case LV_ALIGN_OUT_BOTTOM_RIGHT:
x = lv_obj_get_width(base) - lv_obj_get_width(obj) - pleft;
y = lv_obj_get_height(base) - ptop;
break;
case LV_ALIGN_OUT_LEFT_TOP:
x = -lv_obj_get_width(obj) - pleft;
y = - ptop;
break;
case LV_ALIGN_OUT_LEFT_MID:
x = -lv_obj_get_width(obj) - pleft;
y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2 - ptop;
break;
case LV_ALIGN_OUT_LEFT_BOTTOM:
x = -lv_obj_get_width(obj) - pleft;
y = lv_obj_get_height(base) - lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_RIGHT_TOP:
x = lv_obj_get_width(base) - pleft;
y = - ptop;
break;
case LV_ALIGN_OUT_RIGHT_MID:
x = lv_obj_get_width(base) - pleft;
y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2 - ptop;
break;
case LV_ALIGN_OUT_RIGHT_BOTTOM:
x = lv_obj_get_width(base) - pleft;
y = lv_obj_get_height(base) - lv_obj_get_height(obj) - ptop;
break;
}
x += x_ofs + base->coords.x1 - parent->coords.x1 + lv_obj_get_scroll_left(parent);
y += y_ofs + base->coords.y1 - parent->coords.y1 + lv_obj_get_scroll_top(parent);
lv_obj_set_style_align(obj, LV_ALIGN_TOP_LEFT, 0);
lv_obj_set_pos(obj, x, y);
}
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_area_copy(coords, &obj->coords);
}
lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t rel_x;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
rel_x = obj->coords.x1 - parent->coords.x1;
rel_x += lv_obj_get_scroll_x(parent);
rel_x -= lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
rel_x -= lv_obj_get_style_border_width(parent, LV_PART_MAIN);
}
else {
rel_x = obj->coords.x1;
}
return rel_x;
}
lv_coord_t lv_obj_get_x2(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_obj_get_x(obj) + lv_obj_get_width(obj);
}
lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t rel_y;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
rel_y = obj->coords.y1 - parent->coords.y1;
rel_y += lv_obj_get_scroll_y(parent);
rel_y -= lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
rel_y -= lv_obj_get_style_border_width(parent, LV_PART_MAIN);
}
else {
rel_y = obj->coords.y1;
}
return rel_y;
}
lv_coord_t lv_obj_get_y2(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_obj_get_y(obj) + lv_obj_get_height(obj);
}
lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_area_get_width(&obj->coords);
}
lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_area_get_height(&obj->coords);
}
lv_coord_t lv_obj_get_content_width(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
return lv_obj_get_width(obj) - left - right - 2 * border_width;
}
lv_coord_t lv_obj_get_content_height(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t top = lv_obj_get_style_pad_top((lv_obj_t *)obj, LV_PART_MAIN);
lv_coord_t bottom = lv_obj_get_style_pad_bottom((lv_obj_t *)obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
return lv_obj_get_height(obj) - top - bottom - 2 * border_width;
}
void lv_obj_get_content_coords(const lv_obj_t * obj, lv_area_t * area)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_obj_get_coords(obj, area);
lv_area_increase(area, -border_width, -border_width);
area->x1 += lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
area->x2 -= lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
area->y1 += lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
area->y2 -= lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
}
lv_coord_t lv_obj_get_self_width(const lv_obj_t * obj)
{
lv_point_t p = {0, LV_COORD_MIN};
lv_event_send((lv_obj_t * )obj, LV_EVENT_GET_SELF_SIZE, &p);
return p.x;
}
lv_coord_t lv_obj_get_self_height(const lv_obj_t * obj)
{
lv_point_t p = {LV_COORD_MIN, 0};
lv_event_send((lv_obj_t * )obj, LV_EVENT_GET_SELF_SIZE, &p);
return p.y;
}
bool lv_obj_refresh_self_size(lv_obj_t * obj)
{
lv_coord_t w_set = lv_obj_get_style_width(obj, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(obj, LV_PART_MAIN);
if(w_set != LV_SIZE_CONTENT && h_set != LV_SIZE_CONTENT) return false;
lv_obj_mark_layout_as_dirty(obj);
return true;
}
void lv_obj_refr_pos(lv_obj_t * obj)
{
if(lv_obj_is_layout_positioned(obj)) return;
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_coord_t x = lv_obj_get_style_x(obj, LV_PART_MAIN);
lv_coord_t y = lv_obj_get_style_y(obj, LV_PART_MAIN);
if(parent == NULL) {
lv_obj_move_to(obj, x, y);
return;
}
/*Handle percentage value*/
lv_coord_t pw = lv_obj_get_content_width(parent);
lv_coord_t ph = lv_obj_get_content_height(parent);
if(LV_COORD_IS_PCT(x)) x = (pw * LV_COORD_GET_PCT(x)) / 100;
if(LV_COORD_IS_PCT(y)) y = (ph * LV_COORD_GET_PCT(y)) / 100;
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(obj, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(obj, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
/*Use the translation*/
x += tr_x;
y += tr_y;
lv_align_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
if(align == LV_ALIGN_DEFAULT) {
if(lv_obj_get_style_base_dir(parent, LV_PART_MAIN) == LV_BASE_DIR_RTL) align = LV_ALIGN_TOP_RIGHT;
else align = LV_ALIGN_TOP_LEFT;
}
if(align == LV_ALIGN_TOP_LEFT) {
lv_obj_move_to(obj, x, y);
}
else {
switch(align) {
case LV_ALIGN_TOP_MID:
x += pw / 2 - w / 2;
break;
case LV_ALIGN_TOP_RIGHT:
x += pw - w;
break;
case LV_ALIGN_LEFT_MID:
y += ph / 2 - h / 2;
break;
case LV_ALIGN_BOTTOM_LEFT:
y += ph - h;
break;
case LV_ALIGN_BOTTOM_MID:
x += pw / 2 - w / 2;
y += ph - h;
break;
case LV_ALIGN_BOTTOM_RIGHT:
x += pw - w;
y += ph - h;
break;
case LV_ALIGN_RIGHT_MID:
x += pw - w;
y += ph / 2 - h / 2;
break;
case LV_ALIGN_CENTER:
x += pw / 2 - w / 2;
y += ph / 2 - h / 2;
break;
default:
break;
}
lv_obj_move_to(obj, x, y);
}
}
void lv_obj_move_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
/*Convert x and y to absolute coordinates*/
lv_obj_t * parent = obj->parent;
if(parent) {
lv_coord_t pad_left = lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
lv_coord_t pad_top = lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_FLOATING)) {
x += pad_left + parent->coords.x1;
y += pad_top + parent->coords.y1;
} else {
x += pad_left + parent->coords.x1 - lv_obj_get_scroll_x(parent);
y += pad_top + parent->coords.y1 - lv_obj_get_scroll_y(parent);
}
lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
x += border_width;
y += border_width;
}
/*Calculate and set the movement*/
lv_point_t diff;
diff.x = x - obj->coords.x1;
diff.y = y - obj->coords.y1;
/*Do nothing if the position is not changed*/
/*It is very important else recursive positioning can
*occur without position change*/
if(diff.x == 0 && diff.y == 0) return;
/*Invalidate the original area*/
lv_obj_invalidate(obj);
/*Save the original coordinates*/
lv_area_t ori;
lv_obj_get_coords(obj, &ori);
/*Check if the object inside the parent or not*/
lv_area_t parent_fit_area;
bool on1 = false;
if(parent) {
lv_obj_get_content_coords(parent, &parent_fit_area);
/*If the object is already out of the parent and its position is changes
*surely the scrollbars also changes so invalidate them*/
on1 = _lv_area_is_in(&ori, &parent_fit_area, 0);
if(!on1) lv_obj_scrollbar_invalidate(parent);
}
obj->coords.x1 += diff.x;
obj->coords.y1 += diff.y;
obj->coords.x2 += diff.x;
obj->coords.y2 += diff.y;
lv_obj_move_children_by(obj, diff.x, diff.y, false);
/*Call the ancestor's event handler to the parent too*/
if(parent) lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
/*Invalidate the new area*/
lv_obj_invalidate(obj);
/*If the object was out of the parent invalidate the new scrollbar area too.
*If it wasn't out of the parent but out now, also invalidate the srollbars*/
if(parent) {
bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
}
}
void lv_obj_move_children_by(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating)
{
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(ignore_floating && lv_obj_has_flag(child, LV_OBJ_FLAG_FLOATING)) continue;
child->coords.x1 += x_diff;
child->coords.y1 += y_diff;
child->coords.x2 += x_diff;
child->coords.y2 += y_diff;
lv_obj_move_children_by(child, x_diff, y_diff, false);
}
}
void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_area_t area_tmp;
lv_area_copy(&area_tmp, area);
bool visible = lv_obj_area_is_visible(obj, &area_tmp);
if(visible) _lv_inv_area(lv_obj_get_disp(obj), &area_tmp);
}
void lv_obj_invalidate(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Truncate the area to the object*/
lv_area_t obj_coords;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
lv_obj_invalidate_area(obj, &obj_coords);
}
bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area)
{
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
/*Invalidate the object only if it belongs to the current or previous'*/
lv_obj_t * obj_scr = lv_obj_get_screen(obj);
lv_disp_t * disp = lv_obj_get_disp(obj_scr);
if(obj_scr != lv_disp_get_scr_act(disp) &&
obj_scr != lv_disp_get_scr_prev(disp) &&
obj_scr != lv_disp_get_layer_top(disp) &&
obj_scr != lv_disp_get_layer_sys(disp))
{
return false;
}
/*Truncate the area to the object*/
lv_area_t obj_coords;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
bool is_common;
is_common = _lv_area_intersect(area, area, &obj_coords);
if(is_common == false) return false; /*The area is not on the object*/
/*Truncate recursively to the parents*/
lv_obj_t * par = lv_obj_get_parent(obj);
while(par != NULL) {
is_common = _lv_area_intersect(area, area, &par->coords);
if(is_common == false) return false; /*If no common parts with parent break;*/
if(lv_obj_has_flag(par, LV_OBJ_FLAG_HIDDEN)) return false; /*If the parent is hidden then the child is hidden and won't be drawn*/
par = lv_obj_get_parent(par);
}
return true;
}
bool lv_obj_is_visible(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_area_t obj_coords;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
return lv_obj_area_is_visible(obj, &obj_coords);
}
void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t size)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->ext_click_pad = size;
}
void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area)
{
lv_area_copy(area, &obj->coords);
if(obj->spec_attr) {
area->x1 -= obj->spec_attr->ext_click_pad;
area->x2 += obj->spec_attr->ext_click_pad;
area->y1 -= obj->spec_attr->ext_click_pad;
area->y2 += obj->spec_attr->ext_click_pad;
}
}
bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point)
{
lv_area_t a;
lv_obj_get_click_area(obj, &a);
bool res = _lv_area_is_point_on(&a, point, 0);
if(res == false) return false;
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_ADV_HITTEST)) {
lv_hit_test_info_t hit_info;
hit_info.point = point;
hit_info.res = true;
lv_event_send(obj, LV_EVENT_HIT_TEST, &hit_info);
return hit_info.res;
}
return res;
}
lv_coord_t lv_clamp_width(lv_coord_t width, lv_coord_t min_width, lv_coord_t max_width, lv_coord_t ref_width)
{
if(LV_COORD_IS_PCT(min_width)) min_width = (ref_width * LV_COORD_GET_PCT(min_width)) / 100;
if(LV_COORD_IS_PCT(max_width)) max_width = (ref_width * LV_COORD_GET_PCT(max_width)) / 100;
return LV_CLAMP(min_width, width, max_width);
}
lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t max_height, lv_coord_t ref_height)
{
if(LV_COORD_IS_PCT(min_height)) min_height = (ref_height * LV_COORD_GET_PCT(min_height)) / 100;
if(LV_COORD_IS_PCT(max_height)) max_height = (ref_height * LV_COORD_GET_PCT(max_height)) / 100;
return LV_CLAMP(min_height, height, max_height);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Calculate the "auto size". It's `auto_size = max(children_size, self_size)`
* @param obj pointer to an object
* @param w_out store the width here. NULL to not calculate width
* @param h_out store the height here. NULL to not calculate height
*/
static void calc_auto_size(lv_obj_t * obj, lv_coord_t * w_out, lv_coord_t * h_out)
{
if(!w_out && !h_out) return;
/*Get the bounding box of the children*/
if(w_out) {
lv_coord_t scroll_right = lv_obj_get_scroll_right(obj);
lv_coord_t scroll_left = lv_obj_get_scroll_left(obj);
*w_out = lv_obj_get_width(obj) + scroll_right + scroll_left;
}
if(h_out) {
lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(obj);
lv_coord_t scroll_top = lv_obj_get_scroll_top(obj);
*h_out = lv_obj_get_height(obj) + scroll_bottom + scroll_top;
}
}
static void layout_update_core(lv_obj_t * obj)
{
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
layout_update_core(child);
}
if(obj->layout_inv == 0) return;
obj->layout_inv = 0;
lv_obj_refr_size(obj);
lv_obj_refr_pos(obj);
if(lv_obj_get_child_cnt(obj) > 0) {
uint32_t layout_id = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout_id > 0 && layout_id <= layout_cnt) {
void * user_data = LV_GC_ROOT(_lv_layout_list)[layout_id -1].user_data;
LV_GC_ROOT(_lv_layout_list)[layout_id -1].cb(obj, user_data);
}
}
}

View File

@ -0,0 +1,386 @@
/**
* @file lv_obj_pos.h
*
*/
#ifndef LV_OBJ_POS_H
#define LV_OBJ_POS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
typedef void (*lv_layout_update_cb_t)(struct _lv_obj_t *, void * user_data);
typedef struct {
lv_layout_update_cb_t cb;
void * user_data;
}lv_layout_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Set the relative the position of an object (relative to the parent's top left corner)
* @param obj pointer to an object
* @param x new distance from the left side of the parent plus the parent's left padding
* @param y new distance from the top side of the parent plus the parent's right padding
*/
void lv_obj_set_pos(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
/**
* Set the x coordinate of a object
* @param obj pointer to an object
* @param x new distance from the left side from the parent plus the parent's left padding
*/
void lv_obj_set_x(struct _lv_obj_t * obj, lv_coord_t x);
/**
* Set the y coordinate of a object
* @param obj pointer to an object
* @param y new distance from the top of the parent plus the parent's top padding
*/
void lv_obj_set_y(struct _lv_obj_t * obj, lv_coord_t y);
/**
* Set the size of an object.
* @param obj pointer to an object
* @param w the new width
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* LV_SIZE_PCT(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_size(struct _lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
/**
* Recalculate the size of the object
* @param obj pointer to an object
* @return true: the size has been changed
*/
bool lv_obj_refr_size(struct _lv_obj_t * obj);
/**
* Set the width of an object
* @param obj pointer to an object
* @param w the new width
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_width(struct _lv_obj_t * obj, lv_coord_t w);
/**
* Set the height of an object
* @param obj pointer to an object
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_height(struct _lv_obj_t * obj, lv_coord_t h);
/**
* Set the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @param w the width without paddings in pixels
*/
void lv_obj_set_content_width(struct _lv_obj_t * obj, lv_coord_t w);
/**
* Set the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @param h the height without paddings in pixels
*/
void lv_obj_set_content_height(struct _lv_obj_t * obj, lv_coord_t h);
/**
* Set a layout for an object
* @param obj pointer to an object
* @param layout pointer to a layout descriptor to set
*/
void lv_obj_set_layout(struct _lv_obj_t * obj, uint32_t layout);
/**
* Test whether the and object is positioned by a layout or not
* @param obj pointer to an object to test
* @return true: positioned by a layout; false: not positioned by a layout
*/
bool lv_obj_is_layout_positioned(const struct _lv_obj_t * obj);
/**
* Mark the object for layout update.
* @param obj pointer to an object whose children needs to be updated
*/
void lv_obj_mark_layout_as_dirty(struct _lv_obj_t * obj);
/**
* Update the layout of an object.
* @param obj pointer to an object whose children needs to be updated
*/
void lv_obj_update_layout(const struct _lv_obj_t * obj);
/**
* Regsiter a new layout
* @param cb the layout update callback
* @param user_data custom data that will be passed to `cb`
* @return the ID of the new layout
*/
uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data);
/**
* Change the alignment of an object.
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
*/
void lv_obj_set_align(struct _lv_obj_t * obj, lv_align_t align);
/**
* Change the alignment of an object and set new coordinates.
* Equivalent to:
* lv_obj_set_align(obj, align);
* lv_obj_set_pos(obj, x_ofs, y_ofs);
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
*/
void lv_obj_align(struct _lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an other object (if NULL `obj`s parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
* @note if the position or size of `base` changes `obj` needs to be aligned manually again
*/
void lv_obj_align_to(struct _lv_obj_t * obj, const struct _lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs);
/**
* Align an object to the center on its parent.
* @param obj pointer to an object to align
* @note if the parent size changes `obj` needs to be aligned manually again
*/
static inline void lv_obj_center(struct _lv_obj_t * obj)
{
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
}
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
* @param coords pointer to an area to store the coordinates
*/
void lv_obj_get_coords(const struct _lv_obj_t * obj, lv_area_t * coords);
/**
* Get the x coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the left side of its parent plus the parent's left padding
* @note Zero return value means the object is on the left padding of the parent, and not on the left edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_x(const struct _lv_obj_t * obj);
/**
* Get the x2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the right side of its parent plus the parent's right padding
* @note Zero return value means the object is on the right padding of the parent, and not on the right edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_x2(const struct _lv_obj_t * obj);
/**
* Get the y coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the top side of its parent plus the parent's top padding
* @note Zero return value means the object is on the top padding of the parent, and not on the top edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_y(const struct _lv_obj_t * obj);
/**
* Get the y2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the bottom side of its parent plus the parent's bottom padding
* @note Zero return value means the object is on the bottom padding of the parent, and not on the bottom edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_y2(const struct _lv_obj_t * obj);
/**
* Get the width of an object
* @param obj pointer to an object
* @return the width in pixels
*/
lv_coord_t lv_obj_get_width(const struct _lv_obj_t * obj);
/**
* Get the height of an object
* @param obj pointer to an object
* @return the height in pixels
*/
lv_coord_t lv_obj_get_height(const struct _lv_obj_t * obj);
/**
* Get the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @return the width which still fits into its parent without causing overflow (making the parent scrollable)
*/
lv_coord_t lv_obj_get_content_width(const struct _lv_obj_t * obj);
/**
* Get the height reduced by the top an bottom padding and the border width.
* @param obj pointer to an object
* @return the height which still fits into the parent without causing overflow (making the parent scrollable)
*/
lv_coord_t lv_obj_get_content_height(const struct _lv_obj_t * obj);
/**
* Get the area reduced by the paddings and the border width.
* @param obj pointer to an object
* @param area the area which still fits into the parent without causing overflow (making the parent scrollable)
*/
void lv_obj_get_content_coords(const struct _lv_obj_t * obj, lv_area_t * area);
/**
* Get the width occupied by the "parts" of the widget. E.g. the width of all columns of a table.
* @param obj pointer to an objects
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
lv_coord_t lv_obj_get_self_width(const struct _lv_obj_t * obj);
/**
* Get the height occupied by the "parts" of the widget. E.g. the height of all rows of a table.
* @param obj pointer to an objects
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
lv_coord_t lv_obj_get_self_height(const struct _lv_obj_t * obj);
/**
* Handle if the size of the internal ("virtual") content of an object has changed.
* @param obj pointer to an object
* @return false: nothing happened; true: refresh happened
*/
bool lv_obj_refresh_self_size(struct _lv_obj_t * obj);
void lv_obj_refr_pos(struct _lv_obj_t * obj);
void lv_obj_move_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
void lv_obj_move_children_by(struct _lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating);
/**
* Mark an area of an object as invalid.
* The area will be truncated to the object's area and marked for redraw.
* @param obj pointer to an object
* @param area the area to redraw
*/
void lv_obj_invalidate_area(const struct _lv_obj_t * obj, const lv_area_t * area);
/**
* Mark the object as invalid to redrawn its area
* @param obj pointer to an object
*/
void lv_obj_invalidate(const struct _lv_obj_t * obj);
/**
* Tell whether an area of an object is visible (even partially) now or not
* @param obj pointer to an object
* @param area the are to check. The visible part of the area will be written back here.
* @return true visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_area_is_visible(const struct _lv_obj_t * obj, lv_area_t * area);
/**
* Tell whether an object is visible (even partially) now or not
* @param obj pointer to an object
* @return true: visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_is_visible(const struct _lv_obj_t * obj);
/**
* Set the size of an extended clickable area
* @param obj pointer to an object
* @param size extended clickable area in all 4 directions [px]
*/
void lv_obj_set_ext_click_area(struct _lv_obj_t * obj, lv_coord_t size);
/**
* Get the an area where to object can be clicked.
* It's the object's normal area plus the extended click area.
* @param obj pointer to an object
* @param area store the result area here
*/
void lv_obj_get_click_area(const struct _lv_obj_t * obj, lv_area_t * area);
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point (absolute coordinate)
* @return true: if the object is considered under the point
*/
bool lv_obj_hit_test(struct _lv_obj_t * obj, const lv_point_t * point);
/**
* Clamp a width between min and max width. If the min/max width is in percentage value use the ref_width
* @param width width to clamp
* @param min_width the minimal width
* @param max_width the maximal width
* @param ref_width the reference width used when min/max width is in percentage
* @return the clampled width
*/
lv_coord_t lv_clamp_width(lv_coord_t width, lv_coord_t min_width, lv_coord_t max_width, lv_coord_t ref_width);
/**
* Clamp a height between min and max height. If the min/max height is in percentage value use the ref_height
* @param height height to clamp
* @param min_height the minimal height
* @param max_height the maximal height
* @param ref_height the reference height used when min/max height is in percentage
* @return the clampled height
*/
lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t max_height, lv_coord_t ref_height);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_POS_H*/

View File

@ -0,0 +1,678 @@
/**
* @file lv_obj_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll.h"
#include "lv_obj.h"
#include "lv_indev.h"
#include "lv_disp.h"
#include "lv_indev_scroll.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
#define SCROLL_ANIM_TIME_MIN 200 /*ms*/
#define SCROLL_ANIM_TIME_MAX 400 /*ms*/
#define SCROLLBAR_MIN_SIZE (LV_DPX(10))
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
static void scroll_x_anim(void * obj, int32_t v);
static void scroll_y_anim(void * obj, int32_t v);
static void scroll_anim_ready_cb(lv_anim_t * a);
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value, lv_anim_enable_t anim_en);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(obj->spec_attr->scrollbar_mode == mode) return;
obj->spec_attr->scrollbar_mode = mode;
lv_obj_invalidate(obj);
}
void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
{
lv_obj_allocate_spec_attr(obj);
if(dir != obj->spec_attr->scroll_dir) {
obj->spec_attr->scroll_dir = dir;
}
}
void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_x = align;
}
void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_y = align;
}
/*=====================
* Getter functions
*====================*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scrollbar_mode;
else return LV_SCROLLBAR_MODE_AUTO;
}
lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scroll_dir;
else return LV_DIR_ALL;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scroll_snap_x;
else return LV_SCROLL_SNAP_NONE;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scroll_snap_y;
else return LV_SCROLL_SNAP_NONE;
}
lv_coord_t lv_obj_get_scroll_x(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
lv_coord_t lv_obj_get_scroll_y(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
lv_coord_t lv_obj_get_scroll_top(lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
lv_coord_t lv_obj_get_scroll_bottom(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
child_res = LV_MAX(child_res, child->coords.y2);
}
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.y2 - pad_bottom - border_width);
}
lv_coord_t self_h = lv_obj_get_self_height(obj);
self_h = self_h - (lv_obj_get_height(obj) - pad_top - pad_bottom - 2 * border_width);
self_h -= lv_obj_get_scroll_y(obj);
return LV_MAX(child_res, self_h);
}
lv_coord_t lv_obj_get_scroll_left(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Normally can't scroll the object out on the left.
*So simply use the current scroll position as "left size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
/*With RTL base direction scrolling the left is normal so find the left most coordinate*/
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t child_res = 0;
uint32_t i;
lv_coord_t x1 = LV_COORD_MAX;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
x1 = LV_MIN(x1, child->coords.x1);
}
if(x1 != LV_COORD_MAX) {
child_res = x1;
child_res = (obj->coords.x1 + pad_left + border_width) - child_res;
} else {
child_res = LV_COORD_MIN;
}
lv_coord_t self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
self_w += lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
lv_coord_t lv_obj_get_scroll_right(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*With RTL base dir can't scroll to the object out on the right.
*So simply use the current scroll position as "right size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->scroll.x;
}
/*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
child_res = LV_MAX(child_res, child->coords.x2);
}
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
child_res -= (obj->coords.x2 - pad_right - border_width);
lv_coord_t self_w;
self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
self_w -= lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end)
{
lv_anim_t * a;
a = lv_anim_get(obj, scroll_x_anim);
end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
a = lv_anim_get(obj, scroll_y_anim);
end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
}
/*=====================
* Other functions
*====================*/
void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
{
if(x == 0 && y == 0) return;
if(anim_en == LV_ANIM_ON) {
lv_disp_t * d = lv_obj_get_disp(obj);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_ready_cb(&a, scroll_anim_ready_cb);
if(x) {
lv_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(res != LV_RES_OK) return;
uint32_t t = lv_anim_speed_to_time((lv_disp_get_hor_res(d) * 2) >> 2, 0, x);
if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
lv_anim_set_time(&a, t);
lv_coord_t sx = lv_obj_get_scroll_x(obj);
lv_anim_set_values(&a, -sx, -sx + x);
lv_anim_set_exec_cb(&a, scroll_x_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_anim_start(&a);
}
if(y) {
lv_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(res != LV_RES_OK) return;
uint32_t t = lv_anim_speed_to_time((lv_disp_get_ver_res(d) * 2) >> 2, 0, y);
if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
lv_anim_set_time(&a, t);
lv_coord_t sy = lv_obj_get_scroll_y(obj);
lv_anim_set_values(&a, -sy, -sy + y);
lv_anim_set_exec_cb(&a, scroll_y_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_anim_start(&a);
}
} else {
/*Remove pending animations*/
lv_anim_del(obj, scroll_y_anim);
lv_anim_del(obj, scroll_x_anim);
scroll_by_raw(obj, x, y);
}
}
void lv_obj_scroll_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
{
lv_obj_scroll_to_x(obj, x, anim_en);
lv_obj_scroll_to_y(obj, y, anim_en);
}
void lv_obj_scroll_to_x(lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en)
{
lv_anim_del(obj, scroll_x_anim);
/*Don't let scroll more then naturally possible by the size of the content*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(x < 0) x = 0;
if(x > 0) {
lv_coord_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x > scroll_max) x = scroll_max;
}
} else {
if(x > 0) x = 0;
if(x < 0) {
lv_coord_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x < -scroll_max) x = -scroll_max;
}
}
lv_coord_t scroll_x = lv_obj_get_scroll_x(obj);
lv_coord_t diff = -x + scroll_x;
lv_obj_scroll_by(obj, diff, 0, anim_en);
}
void lv_obj_scroll_to_y(lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en)
{
lv_anim_del(obj, scroll_y_anim);
/*Don't let scroll more then naturally possible by the size of the content*/
if(y < 0) y = 0;
if(y > 0) {
lv_coord_t scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
if(scroll_max < 0) scroll_max = 0;
if(y > scroll_max) y = scroll_max;
}
lv_coord_t scroll_y = lv_obj_get_scroll_y(obj);
lv_coord_t diff = -y + scroll_y;
lv_obj_scroll_by(obj, 0, diff, anim_en);
}
void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
scroll_area_into_view(&obj->coords, obj, &p, anim_en);
}
void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
lv_obj_t * child = obj;
lv_obj_t * parent = lv_obj_get_parent(child);
while(parent) {
scroll_area_into_view(&obj->coords, child, &p, anim_en);
child = parent;
parent = lv_obj_get_parent(parent);
}
}
bool lv_obj_is_scrolling(const lv_obj_t * obj)
{
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) return true;
indev = lv_indev_get_next(indev);
}
return false;
}
void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
lv_obj_update_layout(obj);
lv_point_t p;
lv_indev_scroll_get_snap_dist(obj, &p);
lv_obj_scroll_by(obj, p.x, p.y, anim_en);
}
void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
{
lv_area_set(hor_area, 0, 0, -1, -1);
lv_area_set(ver_area, 0, 0, -1, -1);
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
lv_dir_t sm = lv_obj_get_scrollbar_mode(obj);
if(sm == LV_SCROLLBAR_MODE_OFF) return;
/*If there is no indev scrolling this object but `mode==active` return*/
lv_indev_t * indev = lv_indev_get_next(NULL);
if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) break;
indev = lv_indev_get_next(indev);
}
if(indev == NULL) return;
}
lv_coord_t st = lv_obj_get_scroll_top(obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_t sr = lv_obj_get_scroll_right(obj);
lv_dir_t dir = lv_obj_get_scroll_dir(obj);
bool ver_draw = false;
if((dir & LV_DIR_VER) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
ver_draw = true;
}
bool hor_draw = false;
if((dir & LV_DIR_HOR) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
hor_draw = true;
}
if(!hor_draw && !ver_draw) return;
lv_coord_t end_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
lv_coord_t side_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
lv_coord_t tickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
lv_coord_t obj_h = lv_obj_get_height(obj);
lv_coord_t obj_w = lv_obj_get_width(obj);
lv_coord_t ver_reg_space = ver_draw ? tickness + side_space : 0;
lv_coord_t hor_req_space = hor_draw ? tickness + side_space : 0;
lv_coord_t rem;
if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN &&
lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN) {
return;
}
/*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
lv_coord_t content_h = obj_h + st + sb;
if(ver_draw && content_h) {
ver_area->y1 = obj->coords.y1;
ver_area->y2 = obj->coords.y2;
ver_area->x2 = obj->coords.x2 - side_space;
ver_area->x1 =ver_area->x2 - tickness;
lv_coord_t sb_h = ((obj_h - end_space * 2 - hor_req_space) * obj_h) / content_h;
sb_h = LV_MAX(sb_h, SCROLLBAR_MIN_SIZE);
rem = (obj_h - end_space * 2 - hor_req_space) - sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
lv_coord_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
if(scroll_h <= 0) {
ver_area->y1 = obj->coords.y1 + end_space;
ver_area->y2 = obj->coords.y2 - end_space - hor_req_space - 1;
ver_area->x2 = obj->coords.x2 - side_space;
ver_area->x1 =ver_area->x2 - tickness + 1;
} else {
lv_coord_t sb_y = (rem * sb) / scroll_h;
sb_y = rem - sb_y;
ver_area->y1 = obj->coords.y1 + sb_y + end_space;
ver_area->y2 =ver_area->y1 + sb_h - 1;
ver_area->x2 = obj->coords.x2 - side_space;
ver_area->x1 =ver_area->x2 - tickness;
if(ver_area->y1 < obj->coords.y1 + end_space) {
ver_area->y1 = obj->coords.y1 + end_space;
if(ver_area->y1 + SCROLLBAR_MIN_SIZE >ver_area->y2)ver_area->y2 =ver_area->y1 + SCROLLBAR_MIN_SIZE;
}
if(ver_area->y2 > obj->coords.y2 - hor_req_space - end_space) {
ver_area->y2 = obj->coords.y2 - hor_req_space - end_space;
if(ver_area->y2 - SCROLLBAR_MIN_SIZE <ver_area->y1)ver_area->y1 =ver_area->y2 - SCROLLBAR_MIN_SIZE;
}
}
}
/*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
lv_coord_t content_w = obj_w + sl + sr;
if(hor_draw && content_w) {
hor_area->y2 = obj->coords.y2 - side_space;
hor_area->y1 = hor_area->y2 - tickness;
hor_area->x1 = obj->coords.x1;
hor_area->x2 = obj->coords.x2;
lv_coord_t sb_w = ((obj_w - end_space * 2 - ver_reg_space) * obj_w) / content_w;
sb_w = LV_MAX(sb_w, SCROLLBAR_MIN_SIZE);
rem = (obj_w - end_space * 2 - ver_reg_space) - sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
lv_coord_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
if(scroll_w <= 0) {
hor_area->y2 = obj->coords.y2 - side_space;
hor_area->y1 = hor_area->y2 - tickness + 1;
hor_area->x1 = obj->coords.x1 + end_space;
hor_area->x2 = obj->coords.x2 - end_space - ver_reg_space - 1;
} else {
lv_coord_t sb_x = (rem * sr) / scroll_w;
sb_x = rem - sb_x;
hor_area->x1 = obj->coords.x1 + sb_x + end_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
hor_area->y2 = obj->coords.y2 - side_space;
hor_area->y1 = hor_area->y2 - tickness;
if(hor_area->x1 < obj->coords.x1 + end_space) {
hor_area->x1 = obj->coords.x1 + end_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
if(hor_area->x2 > obj->coords.x2 - ver_reg_space - end_space) {
hor_area->x2 = obj->coords.x2 - ver_reg_space - end_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
}
}
void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
{
lv_area_t hor_area;
lv_area_t ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
if(x == 0 && y == 0) return;
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll.x += x;
obj->spec_attr->scroll.y += y;
lv_obj_move_children_by(obj, x, y, true);
lv_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL);
if(res != LV_RES_OK) return;
lv_obj_invalidate(obj);
}
static void scroll_x_anim(void * obj, int32_t v)
{
scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
}
static void scroll_y_anim(void * obj, int32_t v)
{
scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
}
static void scroll_anim_ready_cb(lv_anim_t * a)
{
lv_event_send(a->var, LV_EVENT_SCROLL_END, NULL);
}
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value, lv_anim_enable_t anim_en)
{
lv_obj_t * parent = lv_obj_get_parent(child);
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
lv_coord_t snap_goal = 0;
lv_coord_t act = 0;
const lv_area_t * area_tmp;
lv_coord_t y_scroll = 0;
lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
lv_coord_t pbottom = lv_obj_get_style_pad_bottom(parent, LV_PART_MAIN);
lv_coord_t top_diff = parent->coords.y1 + ptop - area_tmp->y1 - scroll_value->y;
lv_coord_t bottom_diff = -(parent->coords.y2 - pbottom - area_tmp->y2 - scroll_value->y);
lv_coord_t parent_h = lv_obj_get_height(parent) - ptop - pbottom;
if((top_diff > 0 && bottom_diff > 0)) y_scroll = 0;
else if(top_diff > 0) {
y_scroll = top_diff;
/*Do not let scrolling in*/
lv_coord_t st = lv_obj_get_scroll_top(parent);
if(st - y_scroll < 0) y_scroll = 0;
}
else if(bottom_diff > 0) {
y_scroll = -bottom_diff;
/*Do not let scrolling in*/
lv_coord_t sb = lv_obj_get_scroll_bottom(parent);
if(sb + y_scroll < 0) y_scroll = 0;
}
switch(snap_y) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.y1 + ptop;
act = area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.y2 - pbottom;
act = area_tmp->y2 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.y1 + ptop + parent_h / 2;
act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
}
lv_coord_t x_scroll = 0;
lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
lv_coord_t pright = lv_obj_get_style_pad_right(parent, LV_PART_MAIN);
lv_coord_t left_diff = parent->coords.x1 + pleft - area_tmp->x1 - scroll_value->x;
lv_coord_t right_diff = -(parent->coords.x2 - pright - area_tmp->x2- scroll_value->x);
if((left_diff > 0 && right_diff > 0)) x_scroll = 0;
else if(left_diff > 0) {
x_scroll = left_diff;
/*Do not let scrolling in*/
lv_coord_t sl = lv_obj_get_scroll_left(parent);
if(sl + x_scroll > 0) x_scroll = 0;
}
else if(right_diff > 0) {
x_scroll = -right_diff;
/*Do not let scrolling in*/
lv_coord_t sr = lv_obj_get_scroll_right(parent);
if(sr + x_scroll < 0) x_scroll = 0;
}
lv_coord_t parent_w = lv_obj_get_width(parent) - pleft - pright;
switch(snap_x) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.x1 + pleft;
act = area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.x2 - pright;
act = area_tmp->x2 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.x1 + pleft + parent_w / 2;
act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
}
/*Remove any pending scroll animations.*/
lv_anim_del(parent, scroll_x_anim);
lv_anim_del(parent, scroll_y_anim);
if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
scroll_value->x += anim_en == LV_ANIM_OFF ? 0 : x_scroll;
scroll_value->y += anim_en == LV_ANIM_OFF ? 0 : y_scroll;
lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
}

View File

@ -0,0 +1,277 @@
/**
* @file lv_obj_scroll.h
*
*/
#ifndef LV_OBJ_SCROLL_H
#define LV_OBJ_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
#include "../misc/lv_anim.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
/** Scrollbar modes: shows when should the scrollbars be visible*/
enum {
LV_SCROLLBAR_MODE_OFF, /**< Never show scrollbars*/
LV_SCROLLBAR_MODE_ON, /**< Always show scrollbars*/
LV_SCROLLBAR_MODE_ACTIVE, /**< Show scroll bars when object is being scrolled*/
LV_SCROLLBAR_MODE_AUTO, /**< Show scroll bars when the content is large enough to be scrolled*/
};
typedef uint8_t lv_scrollbar_mode_t;
/** Scroll span align options. Tells where to align the snapable children when scroll stops.*/
enum {
LV_SCROLL_SNAP_NONE, /**< Do not align, leave where it is*/
LV_SCROLL_SNAP_START, /**< Align to to the left/top*/
LV_SCROLL_SNAP_END, /**< Align to to the right/bottom*/
LV_SCROLL_SNAP_CENTER /**< Align to to the center*/
};
typedef uint8_t lv_scroll_snap_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set how the scrollbars should behave.
* @param obj pointer to an object
* @param mode LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
*/
void lv_obj_set_scrollbar_mode(struct _lv_obj_t * obj, lv_scrollbar_mode_t mode);
/**
* Set the object in which directions can be scrolled
* @param obj pointer to an object
* @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
*/
void lv_obj_set_scroll_dir(struct _lv_obj_t * obj, lv_dir_t dir);
/**
* Set where to snap the children when scrolling ends horizontally
* @param obj pointer to an object
* @param align the snap align to set from `lv_snap_align_t`
*/
void lv_obj_set_scroll_snap_x(struct _lv_obj_t * obj, lv_scroll_snap_t align);
/**
* Set where to snap the children when scrolling ends vertically
* @param obj pointer to an object
* @param align the snap align to set from `lv_snap_align_t`
*/
void lv_obj_set_scroll_snap_y(struct _lv_obj_t * obj, lv_scroll_snap_t align);
/*=====================
* Getter functions
*====================*/
/**
* Get the current scroll mode (when to hide the scrollbars)
* @param obj pointer to an object
* @return the current scroll mode from `lv_scroll_mode_t`
*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const struct _lv_obj_t * obj);
/**
* Get the object in which directions can be scrolled
* @param obj pointer to an object
* @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
*/
lv_dir_t lv_obj_get_scroll_dir(const struct _lv_obj_t * obj);
/**
* Get where to snap the children when scrolling ends horizontally
* @param obj pointer to an object
* @return the current snap align from `lv_snap_align_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const struct _lv_obj_t * obj);
/**
* Get where to snap the children when scrolling ends vertically
* @param obj pointer to an object
* @return the current snap align from `lv_snap_align_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const struct _lv_obj_t * obj);
/**
* Get current X scroll position.
* @param obj pointer to an object
* @return the current scroll position from the left edge.
* If the object is not scrolled return 0
* If scrolled return > 0
* If scrolled in (elastic scroll) return < 0
*/
lv_coord_t lv_obj_get_scroll_x(const struct _lv_obj_t * obj);
/**
* Get current Y scroll position.
* @param obj pointer to an object
* @return the current scroll position from the top edge.
* If the object is not scrolled return 0
* If scrolled return > 0
* If scrolled inside return < 0
*/
lv_coord_t lv_obj_get_scroll_y(const struct _lv_obj_t * obj);
/**
* Return the height of the area above the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area above the object in pixels
*/
lv_coord_t lv_obj_get_scroll_top(struct _lv_obj_t * obj);
/**
* Return the height of the area below the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area below the object in pixels
*/
lv_coord_t lv_obj_get_scroll_bottom(struct _lv_obj_t * obj);
/**
* Return the width of the area on the left the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area on the left the object in pixels
*/
lv_coord_t lv_obj_get_scroll_left(struct _lv_obj_t * obj);
/**
* Return the width of the area on the right the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area on the right the object in pixels
*/
lv_coord_t lv_obj_get_scroll_right(struct _lv_obj_t * obj);
/**
* Get the X and Y coordinates where the scrolling will end for this object if a scrolling animation is in progress.
* In no scrolling animation give the current `x` or `y` scroll position.
* @param obj pointer to an object
* @param end poinr to point to store the result
*/
void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end);
/*=====================
* Other functions
*====================*/
/**
*
* Scroll by a given amount of pixels
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
* @note > 0 value means scroll right/bottom (show the more content on the right/bottom)
* @note
*/
void lv_obj_scroll_by(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to a given coordinate on an object.
* `x` and `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to a given X coordinate on an object.
* `x` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_x(struct _lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en);
/**
* Scroll to a given Y coordinate on an object
* `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object to scroll
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_y(struct _lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to an object until it becomes visible on its parent
* @param obj pointer to an object to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Scroll to an object until it becomes visible on its parent.
* Do the same on the parent's parent, and so on.
* Therefore the object will be scrolled into view even it has nested scrollable parents
* @param obj pointer to an object to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view_recursive(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Tell whether an object is being scrolled or not at this moment
* @param obj pointer to an object
* @return true: `obj` is being scrolled
*/
bool lv_obj_is_scrolling(const struct _lv_obj_t * obj);
/**
* Check the children of `obj` and scroll `obj` to fulfill the scroll_snap settings
* @param obj an object whose children needs to checked and snapped
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_update_snap(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Get the area of the scrollbars
* @param obj pointer to an object
* @param hor_area pointer to store the area of the horizontal scrollbar
* @param ver_area pointer to store the area of the vertical scrollbar
*/
void lv_obj_get_scrollbar_area(struct _lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
/**
* Invalidate the area of the scrollbars
* @param obj pointer to an object
*/
void lv_obj_scrollbar_invalidate(struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_H*/

View File

@ -0,0 +1,811 @@
/**
* @file lv_obj_style.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_disp.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t * obj;
lv_style_prop_t prop;
lv_style_selector_t selector;
lv_style_value_t start_value;
lv_style_value_t end_value;
} trans_t;
typedef enum {
CACHE_ZERO = 0,
CACHE_TRUE = 1,
CACHE_UNSET = 2,
CACHE_255 = 3,
CACHE_NEED_CHECK = 4,
}cache_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector);
static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, uint32_t part);
static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v);
static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v);
static void report_style_change_core(void * style, lv_obj_t * obj);
static void refresh_children_style(lv_obj_t * obj);
static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
static void trans_anim_cb(void * _tr, int32_t v);
static void trans_anim_start_cb(lv_anim_t * a);
static void trans_anim_ready_cb(lv_anim_t * a);
static void fade_anim_cb(void * obj, int32_t v);
static void fade_in_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
static bool style_refr = true;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_obj_style_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(trans_t));
}
void lv_obj_add_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
trans_del(obj, selector, LV_STYLE_PROP_ANY, NULL);
uint32_t i;
/*Go after the transition and local styles*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans) continue;
if(obj->styles[i].is_local) continue;
break;
}
/*Now `i` is at the first normal style. Insert the new style before this*/
/*Allocate space for the new style and shift the rest of the style to the end*/
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
uint32_t j;
for(j = obj->style_cnt - 1; j > i ; j--) {
obj->styles[j] = obj->styles[j - 1];
}
lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
obj->styles[i].style = style;
obj->styles[i].selector = selector;
lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
}
void lv_obj_remove_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
lv_state_t state = lv_obj_style_get_selector_state(selector);
lv_part_t part = lv_obj_style_get_selector_part(selector);
lv_style_prop_t prop = LV_STYLE_PROP_ANY;
if(style && style->prop_cnt == 0) prop = LV_STYLE_PROP_INV;
uint32_t i = 0;
bool deleted = false;
while(i < obj->style_cnt) {
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if((state != LV_STATE_ANY && state_act != state) ||
(part != LV_PART_ANY && part_act != part) ||
(style != NULL && style != obj->styles[i].style))
{
i++;
continue;
}
if(obj->styles[i].is_trans) {
trans_del(obj, part, LV_STYLE_PROP_ANY, NULL);
}
if(obj->styles[i].is_local || obj->styles[i].is_trans) {
lv_style_reset(obj->styles[i].style);
lv_mem_free(obj->styles[i].style);
obj->styles[i].style = NULL;
}
/*Shift the styles after `i` by one*/
uint32_t j;
for(j = i; j < (uint32_t)obj->style_cnt - 1 ; j++) {
obj->styles[j] = obj->styles[j + 1];
}
obj->style_cnt--;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
deleted = true;
/*The style from the current `i` index is removed, so `i` points to the next style.
*Therefore it doesn't needs to be incremented*/
}
if(deleted && prop != LV_STYLE_PROP_INV) {
lv_obj_refresh_style(obj, part, prop);
}
}
void lv_obj_report_style_change(lv_style_t * style)
{
if(!style_refr) return;
lv_disp_t * d = lv_disp_get_next(NULL);
while(d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
report_style_change_core(style, d->screens[i]);
}
d = lv_disp_get_next(d);
}
}
void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!style_refr) return;
lv_obj_invalidate(obj);
lv_part_t part = lv_obj_style_get_selector_part(selector);
if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || (prop & LV_STYLE_PROP_LAYOUT_REFR))) {
lv_event_send(obj, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_mark_layout_as_dirty(obj);
}
if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || (prop & LV_STYLE_PROP_PARENT_LAYOUT_REFR))) {
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) lv_obj_mark_layout_as_dirty(parent);
}
if(prop == LV_STYLE_PROP_ANY || (prop & LV_STYLE_PROP_EXT_DRAW)) {
lv_obj_refresh_ext_draw_size(obj);
}
lv_obj_invalidate(obj);
if(prop == LV_STYLE_PROP_ANY ||
((prop & LV_STYLE_PROP_INHERIT) && ((prop & LV_STYLE_PROP_EXT_DRAW) || (prop & LV_STYLE_PROP_LAYOUT_REFR))))
{
if(part != LV_PART_SCROLLBAR) {
refresh_children_style(obj);
}
}
}
void lv_obj_enable_style_refresh(bool en)
{
style_refr = en;
}
lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
{
lv_style_value_t value_act;
bool inherit = prop & LV_STYLE_PROP_INHERIT ? true : false;
bool filter = prop & LV_STYLE_PROP_FILTER ? true : false;
if(filter) {
prop &= ~LV_STYLE_PROP_FILTER;
}
bool found = false;
while(obj) {
found = get_prop_core(obj, part, prop, &value_act);
if(found) break;
if(!inherit) break;
/*If not found, check the `MAIN` style first*/
if(part != LV_PART_MAIN) {
part = LV_PART_MAIN;
continue;
}
/*Check the parent too.*/
obj = lv_obj_get_parent(obj);
}
if(!found) {
if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
const lv_obj_class_t * cls = obj->class_p;
while(cls) {
if(prop == LV_STYLE_WIDTH) {
if(cls->width_def != 0) break;
} else {
if(cls->height_def != 0) break;
}
cls = cls->base_class;
}
value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def;
} else {
value_act = lv_style_prop_get_default(prop);
}
}
if(filter) value_act = apply_color_filter(obj, part, value_act);
return value_act;
}
void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value, lv_style_selector_t selector)
{
lv_style_t * style = get_local_style(obj, selector);
lv_style_set_prop(style, prop, value);
lv_obj_refresh_style(obj, selector, prop);
}
lv_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector)
{
return lv_style_get_prop(obj->styles[i].style, prop, value);
}
}
return LV_RES_INV;
}
bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
uint32_t i;
/*Find the style*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
break;
}
}
/*The style is not found*/
if(i == obj->style_cnt) return false;
return lv_style_remove_prop(obj->styles[i].style, prop);
}
void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state, const _lv_obj_style_transition_dsc_t * tr_dsc)
{
trans_t * tr;
/*Get the previous and current values*/
obj->skip_trans = 1;
obj->state = prev_state;
lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->state = new_state;
lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->skip_trans = 0;
if(v1.ptr == v2.ptr && v1.num == v2.num && v1.color.full == v2.color.full) return;
obj->state = prev_state;
v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->state = new_state;
_lv_obj_style_t * style_trans = get_trans_style(obj, part);
lv_style_set_prop(style_trans->style, tr_dsc->prop, v1); /*Be sure `trans_style` has a valid value*/
if(tr_dsc->prop == LV_STYLE_RADIUS) {
if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
lv_coord_t whalf = lv_obj_get_width(obj) / 2;
lv_coord_t hhalf = lv_obj_get_width(obj) / 2;
if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
}
}
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
LV_ASSERT_MALLOC(tr);
if(tr == NULL) return;
tr->start_value = v1;
tr->end_value = v2;
if(tr) {
tr->obj = obj;
tr->prop = tr_dsc->prop;
tr->selector = part;
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, tr);
lv_anim_set_exec_cb(&a, trans_anim_cb);
lv_anim_set_start_cb(&a, trans_anim_start_cb);
lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
lv_anim_set_values(&a, 0x00, 0xFF);
lv_anim_set_time(&a, tr_dsc->time);
lv_anim_set_delay(&a, tr_dsc->delay);
lv_anim_set_path_cb(&a, tr_dsc->path_cb);
lv_anim_set_early_apply(&a, false);
#if LV_USE_USER_DATA
a.user_data = tr_dsc->user_data;
#endif
lv_anim_start(&a);
}
}
_lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
{
_lv_style_state_cmp_t res = _LV_STYLE_STATE_CMP_SAME;
/*Are there any new styles for the new state?*/
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans) continue;
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
/*The style is valid for a stat but not the other*/
bool valid1 = state_act & (~state1) ? false : true;
bool valid2 = state_act & (~state2) ? false : true;
if(valid1 != valid2) {
lv_style_t * style = obj->styles[i].style;
lv_style_value_t v;
/*If there is layout difference on the main part, return immediately. There is no more serious difference*/
bool layout_diff = false;
if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
if(layout_diff) {
if(part_act == LV_PART_MAIN) {
return _LV_STYLE_STATE_CMP_DIFF_LAYOUT;
}
else {
res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
continue;
}
}
/*Check for draw pad changes*/
if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_X, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_Y, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(res == _LV_STYLE_STATE_CMP_SAME) res = _LV_STYLE_STATE_CMP_DIFF_REDRAW;
}
}
return res;
}
void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER);
lv_anim_set_exec_cb(&a, fade_anim_cb);
lv_anim_set_ready_cb(&a, fade_in_anim_ready);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_start(&a);
}
void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, LV_OPA_COVER, LV_OPA_TRANSP);
lv_anim_set_exec_cb(&a, fade_anim_cb);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_start(&a);
}
lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
{
return selector & 0xFFFF;
}
lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
{
return selector & 0xFF0000;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get the local style of an object for a given part and for a given state.
* If the local style for the part-state pair doesn't exist allocate and return it.
* @param obj pointer to an object
* @param part the part in whose local style to get
* @param state the state in whose local style to get
* @return pointer to the local style
*/
static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector)
{
return obj->styles[i].style;
}
}
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
LV_ASSERT_MALLOC(obj->styles);
for(i = obj->style_cnt - 1; i > 0 ; i--) {
/*Copy only normal styles (not local and transition).
*The new local style will be added as the last local style*/
if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
obj->styles[i] = obj->styles[i - 1];
}
lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
obj->styles[i].style = lv_mem_alloc(sizeof(lv_style_t));
lv_style_init(obj->styles[i].style);
obj->styles[i].is_local = 1;
obj->styles[i].selector = selector;
return obj->styles[i].style;
}
/**
* Get the transition style of an object for a given part and for a given state.
* If the transition style for the part-state pair doesn't exist allocate and return it.
* @param obj pointer to an object
* @param part the part in whose local style to get
* @param state the state in whose local style to get
* @return pointer to the transition style
*/
static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
}
/*Already have a transition style for it*/
if(i != obj->style_cnt) return &obj->styles[i];
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
for(i = obj->style_cnt - 1; i > 0 ; i--) {
obj->styles[i] = obj->styles[i - 1];
}
lv_memset_00(&obj->styles[0], sizeof(_lv_obj_style_t));
obj->styles[0].style = lv_mem_alloc(sizeof(lv_style_t));
lv_style_init(obj->styles[0].style);
obj->styles[0].is_trans = 1;
obj->styles[0].selector = selector;
return &obj->styles[0];
}
static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v)
{
uint8_t group = 1 << _lv_style_get_prop_group(prop);
int32_t weight = -1;
lv_state_t state = obj->state;
lv_state_t state_inv = ~state;
lv_style_value_t value_tmp;
bool skip_trans = obj->skip_trans;
uint32_t i;
bool found;
for(i = 0; i < obj->style_cnt; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
if(obj_style->is_trans == false) break;
if(skip_trans) continue;
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if(part_act != part) continue;
if((obj_style->style->has_group & group) == 0) continue;
found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
if(found) {
*v = value_tmp;
return true;
}
}
for(; i < obj->style_cnt; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
if(part_act != part) continue;
if((obj_style->style->has_group & group) == 0) continue;
/*Be sure the style not specifies other state than the requested.
*E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
if((state_act & state_inv)) continue;
/*Check only better candidates*/
if(state_act <= weight) continue;
found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
if(found) {
if(state_act == state) {
*v = value_tmp;
return true;
}
if(weight < state_act) {
weight = state_act;
*v = value_tmp;
}
}
}
if(weight >= 0) {
*v = value_tmp;
return true;
}
else return false;
}
static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v)
{
if(obj == NULL) return v;
const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
if(f && f->filter_cb) {
lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
}
return v;
}
/**
* Refresh the style of all children of an object. (Called recursively)
* @param style refresh objects only with this
* @param obj pointer to an object
*/
static void report_style_change_core(void * style, lv_obj_t * obj)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(style == NULL || obj->styles[i].style == style) {
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
break;
}
}
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
report_style_change_core(style, lv_obj_get_child(obj, i));
}
}
/**
* Recursively refresh the style of the children. Go deeper until a not NULL style is found
* because the NULL styles are inherited from the parent
* @param obj pointer to an object
*/
static void refresh_children_style(lv_obj_t * obj)
{
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
lv_obj_invalidate(child);
lv_event_send(child, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_invalidate(child);
refresh_children_style(child); /*Check children too*/
}
}
/**
* Remove the transition from object's part's property.
* - Remove the transition from `_lv_obj_style_trans_ll` and free it
* - Delete pending transitions
* @param obj pointer to an object which transition(s) should be removed
* @param part a part of object or 0xFF to remove from all parts
* @param prop a property or 0xFF to remove all properties
* @param tr_limit delete transitions only "older" than this. `NULL` if not used
*/
static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
{
trans_t * tr;
trans_t * tr_prev;
bool removed = false;
tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
while(tr != NULL) {
if(tr == tr_limit) break;
/*'tr' might be deleted, so get the next object while 'tr' is valid*/
tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
/*Remove the transitioned property from trans. style
*to allow changing it by normal styles*/
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
lv_style_remove_prop(obj->styles[i].style, tr->prop);
lv_anim_del(tr, NULL);
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
lv_mem_free(tr);
removed = true;
}
}
}
tr = tr_prev;
}
return removed;
}
static void trans_anim_cb(void * _tr, int32_t v)
{
trans_t * tr = _tr;
lv_obj_t * obj = tr->obj;
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
lv_style_value_t value_final;
switch (tr->prop) {
case LV_STYLE_BORDER_SIDE:
case LV_STYLE_BORDER_POST:
case LV_STYLE_BLEND_MODE:
if(v < 255) value_final.num = tr->start_value.num;
else value_final.num = tr->end_value.num;
break;
case LV_STYLE_TRANSITION:
case LV_STYLE_TEXT_FONT:
if(v < 255) value_final.ptr = tr->start_value.ptr;
else value_final.ptr = tr->end_value.ptr;
break;
case LV_STYLE_COLOR_FILTER_DSC:
if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
else if(v < 128) value_final.ptr = tr->start_value.ptr;
else value_final.ptr = tr->end_value.ptr;
break;
case LV_STYLE_BG_COLOR:
case LV_STYLE_BORDER_COLOR:
case LV_STYLE_TEXT_COLOR:
case LV_STYLE_SHADOW_COLOR:
case LV_STYLE_OUTLINE_COLOR:
case LV_STYLE_IMG_RECOLOR:
if(v <= 0) value_final.color = tr->start_value.color;
else if(v >= 255) value_final.color = tr->end_value.color;
else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
break;
default:
if(v == 0) value_final.num = tr->start_value.num;
else if(v == 255) value_final.num = tr->end_value.num;
else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
break;
}
lv_style_value_t old_value;
bool refr = true;
if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
if(value_final.ptr == old_value.ptr && value_final.color.full == old_value.color.full && value_final.num == old_value.num) {
refr = false;
}
}
lv_style_set_prop(obj->styles[i].style, tr->prop, value_final);
if (refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
break;
}
}
static void trans_anim_start_cb(lv_anim_t * a)
{
trans_t * tr = a->var;
lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
/*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
lv_style_prop_t prop_tmp = tr->prop;
tr->prop = LV_STYLE_PROP_INV;
/*Delete the related transitions if any*/
trans_del(tr->obj, part, prop_tmp, tr);
tr->prop = prop_tmp;
_lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
lv_style_set_prop(style_trans->style, tr->prop, tr->start_value); /*Be sure `trans_style` has a valid value*/
}
static void trans_anim_ready_cb(lv_anim_t * a)
{
trans_t * tr = a->var;
lv_obj_t * obj = tr->obj;
lv_style_prop_t prop = tr->prop;
/*Remove the transitioned property from trans. style
*if there no more transitions for this property
*It allows changing it by normal styles*/
bool running = false;
trans_t * tr_i;
_LV_LL_READ(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
running = true;
break;
}
}
if(!running) {
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
lv_mem_free(tr);
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_style_remove_prop(obj_style->style, prop);
if(lv_style_is_empty(obj->styles[i].style)) {
lv_obj_remove_style(obj, obj_style->style, obj_style->selector);
}
break;
}
}
}
}
static void fade_anim_cb(void * obj, int32_t v)
{
lv_obj_set_style_opa(obj, v, 0);
}
static void fade_in_anim_ready(lv_anim_t * a)
{
lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
}

View File

@ -0,0 +1,232 @@
/**
* @file lv_obj_style.h
*
*/
#ifndef LV_OBJ_STYLE_H
#define LV_OBJ_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include "../misc/lv_bidi.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
_LV_STYLE_STATE_CMP_SAME, /*The style properties in the 2 states are identical*/
_LV_STYLE_STATE_CMP_DIFF_REDRAW, /*The differences can be shown with a simple redraw*/
_LV_STYLE_STATE_CMP_DIFF_DRAW_PAD, /*The differences can be shown with a simple redraw*/
_LV_STYLE_STATE_CMP_DIFF_LAYOUT, /*The differences can be shown with a simple redraw*/
} _lv_style_state_cmp_t;
typedef uint32_t lv_style_selector_t;
typedef struct {
lv_style_t * style;
uint32_t selector :24;
uint32_t is_local :1;
uint32_t is_trans :1;
}_lv_obj_style_t;
typedef struct {
uint16_t time;
uint16_t delay;
lv_style_selector_t selector;
lv_style_prop_t prop;
lv_anim_path_cb_t path_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
}_lv_obj_style_transition_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the object related style manager module.
* Called by LVGL in `lv_init()`
*/
void _lv_obj_style_init(void);
/**
* Add a style to an object.
* @param obj pointer to an object
* @param part a part of the object to which the style should be added E.g. `LV_PART_MAIN` or `LV_PART_KNOB`
* @param state a state or combination of states to which the style should be assigned
* @param style pointer to a style to add
* @example lv_obj_add_style_no_refresh(slider, LV_PART_KNOB, LV_STATE_PRESSED, &style1);
*/
void lv_obj_add_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
/**
* Add a style to an object.
* @param obj pointer to an object
* @param style pointer to a style to remove. Can be NULL to check only the selector
* @param selector OR-ed values of states and a part to remove only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
* @example lv_obj_remove_style(obj, LV_PART_ANY, LV_STATE_ANY, &style); //Remove a specific style
* @example lv_obj_remove_style(obj, LV_PART_MAIN, LV_STATE_ANY, &style); //Remove all styles from the main part
* @example lv_obj_remove_style(obj, LV_PART_ANY, LV_STATE_ANY, NULL); //Remove all styles
*/
void lv_obj_remove_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
/**
* Remove all styles from an object
* @param obj pointer to an object
*/
static inline void lv_obj_remove_style_all(struct _lv_obj_t * obj)
{
lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY);
}
/**
* Notify all object if a style is modified
* @param style pointer to a style. Only the objects with this style will be notified
* (NULL to notify all objects)
*/
void lv_obj_report_style_change(lv_style_t * style);
/**
* Notify an object and its children about its style is modified.
* @param obj pointer to an object
* @param part the part whose style was changed. E.g. `LV_PART_ANY`, `LV_PART_MAIN`
* @param prop `LV_STYLE_PROP_ANY` or an `LV_STYLE_...` property.
* It is used to optimize what needs to be refreshed.
* `LV_STYLE_PROP_INV` to perform only a style cache update
*/
void lv_obj_refresh_style(struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Enable or disable automatic style refreshing when a new style is added/removed to/from an object
* or any other style change happens.
* @param en true: enable refreshing; false: disable refreshing
*/
void lv_obj_enable_style_refresh(bool en);
/**
* Get the value of a style property. The current state of the object will be considered.
* Inherited properties will be inherited.
* If a property is not set a default value will be returned.
* @param obj pointer to an object
* @param part a part from which the property should be get
* @param prop the property to get
* @return the value of the property.
* Should be read from the correct field of the `lv_style_value_t` according to the type of the property.
*/
lv_style_value_t lv_obj_get_style_prop(const struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Set local style property on an object's part and state.
* @param obj pointer to an object
* @param part a part to which the property should be added
* @param state a state to which the property should be added
* @param prop the property
* @param value value of the property. The correct element should be set according to the type of the property
*/
void lv_obj_set_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value, lv_style_selector_t selector);
lv_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, lv_style_selector_t selector);
/**
* Remove a local style property from a part of an object with a given state.
* @param obj pointer to an object
* @param part the part of the object which style property should be removed.
* @param state the state from which the property should be removed.
* @param prop a style property to remove.
* @return true the property was found and removed; false: the property was not found
*/
bool lv_obj_remove_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector);
/**
* Used internally to create a style tarnsition
* @param obj
* @param part
* @param prev_state
* @param new_state
* @param tr
*/
void _lv_obj_style_create_transition(struct _lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state, const _lv_obj_style_transition_dsc_t * tr);
/**
* Used internally to compare the appearance of an object in 2 states
* @param obj
* @param state1
* @param state2
* @return
*/
_lv_style_state_cmp_t _lv_obj_style_state_compare(struct _lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
/**
* Fade in an an object and all its children.
* @param obj the object to fade in
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_in(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
/**
* Fade out an an object and all its children.
* @param obj the object to fade out
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_out(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector);
lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector);
#include "lv_obj_style_gen.h"
static inline void lv_obj_set_style_pad_all(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) {
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_hor(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) {
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
}
static inline void lv_obj_set_style_pad_ver(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) {
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) {
lv_obj_set_style_pad_row(obj, value, selector);
lv_obj_set_style_pad_column(obj, value, selector);
}
static inline void lv_obj_set_style_size(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) {
lv_obj_set_style_width(obj, value, selector);
lv_obj_set_style_height(obj, value, selector);
}
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEMPL_H*/

View File

@ -0,0 +1,713 @@
#include "lv_obj.h"
void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_WIDTH, v, selector);
}
void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_WIDTH, v, selector);
}
void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_WIDTH, v, selector);
}
void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_HEIGHT, v, selector);
}
void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_HEIGHT, v, selector);
}
void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_HEIGHT, v, selector);
}
void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_X, v, selector);
}
void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_Y, v, selector);
}
void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ALIGN, v, selector);
}
void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_WIDTH, v, selector);
}
void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_HEIGHT, v, selector);
}
void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_X, v, selector);
}
void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_Y, v, selector);
}
void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ZOOM, v, selector);
}
void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ANGLE, v, selector);
}
void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_TOP, v, selector);
}
void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_BOTTOM, v, selector);
}
void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_LEFT, v, selector);
}
void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RIGHT, v, selector);
}
void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_ROW, v, selector);
}
void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_COLUMN, v, selector);
}
void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_RADIUS, v, selector);
}
void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_CLIP_CORNER, v, selector);
}
void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OPA, v, selector);
}
void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_DSC, v, selector);
}
void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_OPA, v, selector);
}
void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_TIME, v, selector);
}
void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_SPEED, v, selector);
}
void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSITION, v, selector);
}
void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BLEND_MODE, v, selector);
}
void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LAYOUT, v, selector);
}
void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BASE_DIR, v, selector);
}
void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR, v, selector);
}
void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_OPA, v, selector);
}
void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR, v, selector);
}
void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_DIR, v, selector);
}
void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_STOP, v, selector);
}
void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
}
void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_SRC, v, selector);
}
void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_OPA, v, selector);
}
void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR, v, selector);
}
void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR_FILTERED, v, selector);
}
void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_TILED, v, selector);
}
void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR, v, selector);
}
void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_OPA, v, selector);
}
void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_WIDTH, v, selector);
}
void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_SIDE, v, selector);
}
void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_POST, v, selector);
}
void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR, v, selector);
}
void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OPA, v, selector);
}
void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_FONT, v, selector);
}
void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LETTER_SPACE, v, selector);
}
void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LINE_SPACE, v, selector);
}
void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_DECOR, v, selector);
}
void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_ALIGN, v, selector);
}
void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_OPA, v, selector);
}
void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR, v, selector);
}
void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR_FILTERED, v, selector);
}
void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_WIDTH, v, selector);
}
void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR, v, selector);
}
void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_OPA, v, selector);
}
void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_PAD, v, selector);
}
void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_WIDTH, v, selector);
}
void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFS_X, v, selector);
}
void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFS_Y, v, selector);
}
void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_SPREAD, v, selector);
}
void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR, v, selector);
}
void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OPA, v, selector);
}
void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_WIDTH, v, selector);
}
void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_WIDTH, v, selector);
}
void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_GAP, v, selector);
}
void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_ROUNDED, v, selector);
}
void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR, v, selector);
}
void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_OPA, v, selector);
}
void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_WIDTH, v, selector);
}
void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_ROUNDED, v, selector);
}
void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR, v, selector);
}
void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_OPA, v, selector);
}
void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_IMG_SRC, v, selector);
}

View File

@ -0,0 +1,623 @@
static inline lv_coord_t lv_obj_get_style_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_min_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_max_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_min_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_max_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_Y);
return (lv_coord_t)v.num;
}
static inline lv_align_t lv_obj_get_style_align(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ALIGN);
return (lv_align_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_translate_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_translate_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_Y);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_zoom(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ZOOM);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_angle(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ANGLE);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_top(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_bottom(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_BOTTOM);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_left(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_LEFT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_right(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_row(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_ROW);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_column(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_COLUMN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_radius(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_clip_corner(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_CLIP_CORNER);
return (bool)v.num;
}
static inline lv_opa_t lv_obj_get_style_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_DSC);
return (const lv_color_filter_dsc_t *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_color_filter_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_OPA);
return (lv_opa_t)v.num;
}
static inline uint32_t lv_obj_get_style_anim_time(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_TIME);
return (uint32_t)v.num;
}
static inline uint32_t lv_obj_get_style_anim_speed(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_SPEED);
return (uint32_t)v.num;
}
static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSITION);
return (const lv_style_transition_dsc_t *)v.ptr;
}
static inline lv_blend_mode_t lv_obj_get_style_blend_mode(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLEND_MODE);
return (lv_blend_mode_t)v.num;
}
static inline uint16_t lv_obj_get_style_layout(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LAYOUT);
return (uint16_t)v.num;
}
static inline lv_base_dir_t lv_obj_get_style_base_dir(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BASE_DIR);
return (lv_base_dir_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_grad_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR_FILTERED);
return v.color;
}
static inline lv_grad_dir_t lv_obj_get_style_bg_grad_dir(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_DIR);
return (lv_grad_dir_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_bg_main_stop(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_STOP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_bg_grad_stop(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_STOP);
return (lv_coord_t)v.num;
}
static inline const void * lv_obj_get_style_bg_img_src(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_SRC);
return (const void *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_bg_img_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline bool lv_obj_get_style_bg_img_tiled(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_TILED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_border_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_border_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_border_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_border_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_border_side_t lv_obj_get_style_border_side(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_SIDE);
return (lv_border_side_t)v.num;
}
static inline bool lv_obj_get_style_border_post(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_POST);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_text_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_text_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_text_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_font_t * lv_obj_get_style_text_font(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_FONT);
return (const lv_font_t *)v.ptr;
}
static inline lv_coord_t lv_obj_get_style_text_letter_space(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LETTER_SPACE);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_text_line_space(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LINE_SPACE);
return (lv_coord_t)v.num;
}
static inline lv_text_decor_t lv_obj_get_style_text_decor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_DECOR);
return (lv_text_decor_t)v.num;
}
static inline lv_text_align_t lv_obj_get_style_text_align(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_ALIGN);
return (lv_text_align_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_img_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_outline_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_outline_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_outline_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_outline_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_outline_pad(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_PAD);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_ofs_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_ofs_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_Y);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_spread(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_SPREAD);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_shadow_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_shadow_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_dash_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_dash_gap(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_GAP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_rounded(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_ROUNDED);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_line_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_line_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_line_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_arc_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_arc_rounded(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_ROUNDED);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_arc_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_arc_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_arc_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_OPA);
return (lv_opa_t)v.num;
}
static inline const void * lv_obj_get_style_arc_img_src(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_IMG_SRC);
return (const void *)v.ptr;
}
void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector);
void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector);
void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector);
void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);

View File

@ -0,0 +1,438 @@
/**
* @file lv_obj_tree.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_gc.h"
#include "../misc/lv_async.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
#if defined(LV_USER_DATA_FREE_INCLUDE)
#include LV_USER_DATA_FREE_INCLUDE
#endif /*LV_USE_USER_DATA_FREE*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_del_async_cb(void * obj);
static void obj_del_core(lv_obj_t * obj);
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_del(lv_obj_t * obj)
{
LV_LOG_TRACE("begin (delete %p)", obj)
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * par = lv_obj_get_parent(obj);
if(par) {
lv_obj_scrollbar_invalidate(par);
}
lv_disp_t * disp = NULL;
bool act_scr_del = false;
if(par == NULL) {
disp = lv_obj_get_disp(obj);
if(!disp) return; /*Shouldn't happen*/
if(disp->act_scr == obj) act_scr_del = true;
}
obj_del_core(obj);
/*Call the ancestor's event handler to the parent to notify it about the child delete*/
if(par) {
/*Just to remove scroll animations if any*/
lv_obj_scroll_to(par, 0, 0, LV_ANIM_OFF);
if(par->spec_attr) {
par->spec_attr->scroll.x = 0;
par->spec_attr->scroll.y = 0;
}
lv_event_send(par, LV_EVENT_CHILD_CHANGED, NULL);
}
/*Handle if the active screen was deleted*/
if(act_scr_del) {
LV_LOG_WARN("the active screen was deleted")
disp->act_scr = NULL;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", obj)
}
void lv_obj_clean(lv_obj_t * obj)
{
LV_LOG_TRACE("begin (delete %p)", obj)
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_del_core(child);
child = lv_obj_get_child(obj, 0);
}
/*Just to remove scroll animations if any*/
lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
if(obj->spec_attr) {
obj->spec_attr->scroll.x = 0;
obj->spec_attr->scroll.y = 0;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", obj)
}
void lv_obj_del_anim_ready_cb(lv_anim_t * a)
{
lv_obj_del(a->var);
}
void lv_obj_del_async(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_async_call(lv_obj_del_async_cb, obj);
}
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_OBJ(parent, MY_CLASS);
if(obj->parent == NULL) {
LV_LOG_WARN("Can't set the parent of a screen");
return;
}
if(parent == NULL) {
LV_LOG_WARN("Can't set parent == NULL to an object");
return;
}
lv_obj_invalidate(obj);
lv_obj_allocate_spec_attr(parent);
lv_obj_t * old_parent = obj->parent;
lv_point_t old_pos;
old_pos.y = lv_obj_get_y(obj);
lv_base_dir_t new_base_dir = lv_obj_get_style_base_dir(parent, LV_PART_MAIN);
if(new_base_dir != LV_BASE_DIR_RTL) old_pos.x = lv_obj_get_x(obj);
else old_pos.x = old_parent->coords.x2 - obj->coords.x2;
/*Remove the object from the old parent's child list*/
int32_t i;
for(i = lv_obj_get_child_id(obj); i <= (int32_t)lv_obj_get_child_cnt(old_parent) - 2; i++) {
old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i+1];
}
old_parent->spec_attr->child_cnt--;
if(old_parent->spec_attr->child_cnt) {
old_parent->spec_attr->children = lv_mem_realloc(old_parent->spec_attr->children, old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
} else {
lv_mem_free(old_parent->spec_attr->children);
old_parent->spec_attr->children = NULL;
}
/*Add the child to the new parent as the last (newest child)*/
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children, parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
parent->spec_attr->children[lv_obj_get_child_cnt(parent) - 1] = obj;
obj->parent = parent;
if(new_base_dir != LV_BASE_DIR_RTL) {
lv_obj_set_pos(obj, old_pos.x, old_pos.y);
}
else {
/*Align to the right in case of RTL base dir*/
lv_coord_t new_x = lv_obj_get_width(parent) - old_pos.x - lv_obj_get_width(obj);
lv_obj_set_pos(obj, new_x, old_pos.y);
}
/*Notify the original parent because one of its children is lost*/
lv_event_send(old_parent, LV_EVENT_CHILD_CHANGED, obj);
/*Notify the new parent about the child*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_invalidate(obj);
}
void lv_obj_move_foreground(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_obj_invalidate(parent);
uint32_t i;
for(i = lv_obj_get_child_id(obj); i < lv_obj_get_child_cnt(parent) - 1; i++) {
parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
}
parent->spec_attr->children[lv_obj_get_child_cnt(parent) - 1] = obj;
/*Notify the new parent about the child*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_invalidate(parent);
}
void lv_obj_move_background(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_obj_invalidate(parent);
int32_t i;
for(i = lv_obj_get_child_id(obj); i > 0; i--) {
parent->spec_attr->children[i] = parent->spec_attr->children[i-1];
}
parent->spec_attr->children[0] = obj;
/*Notify the new parent about the child*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_invalidate(parent);
}
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * par = obj;
const lv_obj_t * act_par;
do {
act_par = par;
par = lv_obj_get_parent(act_par);
} while(par != NULL);
return (lv_obj_t*)act_par;
}
lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * scr;
if(obj->parent == NULL) scr = obj; /*`obj` is a screen*/
else scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
lv_disp_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_disp_ll), d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
if(d->screens[i] == scr) return d;
}
}
LV_LOG_WARN("No screen found")
return NULL;
}
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
{
if(obj == NULL) return NULL;
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->parent;
}
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t id)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
uint32_t idu;
if(id < 0) {
id = obj->spec_attr->child_cnt + id;
if(id < 0) return NULL;
idu = (uint32_t) id;
} else {
idu = id;
}
if(idu >= obj->spec_attr->child_cnt) return NULL;
else return obj->spec_attr->children[id];
}
uint32_t lv_obj_get_child_cnt(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->child_cnt;
}
uint32_t lv_obj_get_child_id(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return 0;
uint32_t i = 0;
for(i = 0; i < lv_obj_get_child_cnt(parent); i++) {
if(lv_obj_get_child(parent, i) == obj) return i;
}
return 0xFFFFFFFF; /*Shouldn't happen*/
}
void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
walk_core(start_obj, cb, user_data);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_del_async_cb(void * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_del(obj);
}
static void obj_del_core(lv_obj_t * obj)
{
/*Let the user free the resources used in `LV_EVENT_DELETE`*/
lv_res_t res = lv_event_send(obj, LV_EVENT_DELETE, NULL);
if(res == LV_RES_INV) return;
/*Delete from the group*/
lv_group_t * group = lv_obj_get_group(obj);
if(group) lv_group_remove_obj(obj);
/*Remove the animations from this object*/
lv_anim_del(obj, NULL);
/*Recursively delete the children*/
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_del_core(child);
child = lv_obj_get_child(obj, 0);
}
_lv_event_mark_deleted(obj);
/*Remove all style*/
lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
lv_obj_remove_style_all(obj);
lv_obj_enable_style_refresh(true);
/*Reset all input devices if the object to delete is used*/
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
lv_indev_reset(indev, obj);
}
if(indev->proc.types.pointer.last_pressed == obj) {
indev->proc.types.pointer.last_pressed = NULL;
}
if(indev->group == group && obj == lv_indev_get_obj_act()) {
lv_indev_reset(indev, obj);
}
indev = lv_indev_get_next(indev);
}
/*All children deleted. Now clean up the object specific data*/
_lv_obj_destructor(obj);
/*Remove the screen for the screen list*/
if(obj->parent == NULL) {
lv_disp_t * disp = lv_obj_get_disp(obj);
uint32_t i;
/*Find the screen in the list*/
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) break;
}
uint32_t id = i;
for(i = id; i < disp->screen_cnt - 1; i++) {
disp->screens[i] = disp->screens[i + 1];
}
disp->screen_cnt--;
disp->screens = lv_mem_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
}
/*Remove the object from the child list of its parent*/
else {
uint32_t id = lv_obj_get_child_id(obj);
uint32_t i;
for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
}
obj->parent->spec_attr->child_cnt--;
obj->parent->spec_attr->children = lv_mem_realloc(obj->parent->spec_attr->children, obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
}
/*Free the object itself*/
lv_mem_free(obj);
}
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
if(obj == NULL) {
lv_disp_t * disp = lv_disp_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
walk_core(disp->screens[i], cb, user_data);
}
disp = lv_disp_get_next(disp);
}
return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
}
res = cb(obj, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
}
}
return LV_OBJ_TREE_WALK_NEXT;
}

View File

@ -0,0 +1,163 @@
/**
* @file struct _lv_obj_tree.h
*
*/
#ifndef LV_OBJ_TREE_H
#define LV_OBJ_TREE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
typedef enum {
LV_OBJ_TREE_WALK_NEXT,
LV_OBJ_TREE_WALK_SKIP_CHILDREN,
LV_OBJ_TREE_WALK_END,
} lv_obj_tree_walk_res_t;
typedef lv_obj_tree_walk_res_t (*lv_obj_tree_walk_cb_t)(struct _lv_obj_t *, void *);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Delete an object and all of it's children.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETED` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_del(struct _lv_obj_t * obj);
/**
* Delete all children of an object.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETED` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_clean(struct _lv_obj_t * obj);
/**
* A function to be easily used in animation ready callback to delete an object when the animation is ready
* @param a pointer to the animation
*/
void lv_obj_del_anim_ready_cb(lv_anim_t * a);
/**
* Helper function for asynchronously deleting objects.
* Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
* @param obj object to delete
* @see lv_async_call
*/
void lv_obj_del_async(struct _lv_obj_t * obj);
/**
* Move the parent of an object. The relative coordinates will be kept.
*
* @param obj pointer to an object whose parent needs to be changed
* @param parent pointer to the new parent
*/
void lv_obj_set_parent(struct _lv_obj_t * obj, struct _lv_obj_t * parent);
/**
* Move the object to the foreground.
* It will look like if it was created as the last child of its parent.
* It also means it can cover any of the siblings.
* @param obj pointer to an object
*/
void lv_obj_move_foreground(struct _lv_obj_t * obj);
/**
* Move the object to the background.
* It will look like if it was created as the first child of its parent.
* It also means any of the siblings can cover the object.
* @param obj pointer to an object
*/
void lv_obj_move_background(struct _lv_obj_t * obj);
/**
* Get the screen of an object
* @param obj pointer to an object
* @return pointer to the obejct's screen
*/
struct _lv_obj_t * lv_obj_get_screen(const struct _lv_obj_t * obj);
/**
* Get the display of the object
* @param obj pointer to an object
* @return pointer to the obejct's display
*/
lv_disp_t * lv_obj_get_disp(const struct _lv_obj_t * obj);
/**
* Get the parent of an object
* @param obj pointer to an object
* @return the parent of the object. (NULL if `obj` was a screen)
*/
struct _lv_obj_t * lv_obj_get_parent(const struct _lv_obj_t * obj);
/**
* Get the child of an object by the child's index.
* @param obj pointer to an object whose child should be get
* @param id the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @return pointer to the child or NULL if the index was invalid
*/
struct _lv_obj_t * lv_obj_get_child(const struct _lv_obj_t * obj, int32_t id);
/**
* Get the number of children
* @param obj pointer to an object
* @return the number of children
*/
uint32_t lv_obj_get_child_cnt(const struct _lv_obj_t * obj);
/**
* Get the index of a child.
* @param obj pointer to an obejct
* @return the child index of the object.
* E.g. 0: the oldest (firstly created child)
*/
uint32_t lv_obj_get_child_id(const struct _lv_obj_t * obj);
/**
* Iterate through all children of any object.
* @param start_obj start integrating from this object
* @param cb call this callback on the objects
* @param user_data pointer to any user related data (will be passed to `cb`)
*/
void lv_obj_tree_walk(struct _lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_TREE_H*/

View File

@ -0,0 +1,940 @@
/**
* @file lv_refr.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_refr.h"
#include "lv_disp.h"
#include "../hal/lv_hal_tick.h"
#include "../hal/lv_hal_disp.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_math.h"
#include "../misc/lv_gc.h"
#include "../draw/lv_draw.h"
#include "../font/lv_font_fmt_txt.h"
#if LV_USE_PERF_MONITOR || LV_USE_MEM_MONITOR
#include "../widgets/lv_label.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_refr_join_area(void);
static void lv_refr_areas(void);
static void lv_refr_area(const lv_area_t * area_p);
static void lv_refr_area_part(const lv_area_t * area_p);
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p);
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p);
static void draw_buf_flush(void);
static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
/**********************
* STATIC VARIABLES
**********************/
static uint32_t px_num;
static lv_disp_t * disp_refr; /*Display being refreshed*/
#if LV_USE_PERF_MONITOR
static uint32_t fps_sum_cnt;
static uint32_t fps_sum_all;
#endif
/**********************
* MACROS
**********************/
#if LV_LOG_TRACE_DISP_REFR
# define TRACE_REFR(...) LV_LOG_TRACE( __VA_ARGS__)
#else
# define TRACE_REFR(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void _lv_refr_init(void)
{
/*Nothing to do*/
}
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
* can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
* (e.g. progress bar) this function can be called when the screen should be updated.
* @param disp pointer to display to refresh. NULL to refresh all displays.
*/
void lv_refr_now(lv_disp_t * disp)
{
lv_anim_refr_now();
if(disp) {
_lv_disp_refr_timer(disp->refr_timer);
}
else {
lv_disp_t * d;
d = lv_disp_get_next(NULL);
while(d) {
_lv_disp_refr_timer(d->refr_timer);
d = lv_disp_get_next(d);
}
}
}
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
*/
void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) return;
/*Clear the invalidate buffer if the parameter is NULL*/
if(area_p == NULL) {
disp->inv_p = 0;
return;
}
lv_area_t scr_area;
scr_area.x1 = 0;
scr_area.y1 = 0;
scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
lv_area_t com_area;
bool suc;
suc = _lv_area_intersect(&com_area, area_p, &scr_area);
if(suc == false) return; /*Out of the screen*/
/*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
if(disp->driver->full_refresh) {
disp->inv_areas[0] = scr_area;
disp->inv_p = 1;
lv_timer_resume(disp->refr_timer);
return;
}
if(disp->driver->rounder_cb) disp->driver->rounder_cb(disp->driver, &com_area);
/*Save only if this area is not in one of the saved areas*/
uint16_t i;
for(i = 0; i < disp->inv_p; i++) {
if(_lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
}
/*Save the area*/
if(disp->inv_p < LV_INV_BUF_SIZE) {
lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
}
else { /*If no place for the area add the screen*/
disp->inv_p = 0;
lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
}
disp->inv_p++;
lv_timer_resume(disp->refr_timer);
}
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * _lv_refr_get_disp_refreshing(void)
{
return disp_refr;
}
/**
* Set the display which is being refreshed.
* It shouldn't be used directly by the user.
* It can be used to trick the drawing functions about there is an active display.
* @param the display being refreshed
*/
void _lv_refr_set_disp_refreshing(lv_disp_t * disp)
{
disp_refr = disp;
}
/**
* Called periodically to handle the refreshing
* @param tmr pointer to the timer itself
*/
void _lv_disp_refr_timer(lv_timer_t * tmr)
{
TRACE_REFR("begin");
uint32_t start = lv_tick_get();
uint32_t elaps = 0;
disp_refr = tmr->user_data;
#if LV_USE_PERF_MONITOR == 0 && LV_USE_MEM_MONITOR == 0
/**
* Ensure the timer does not run again automatically.
* This is done before refreshing in case refreshing invalidates something else.
*/
lv_timer_pause(tmr);
#endif
/*Refresh the screen's layout if required*/
lv_obj_update_layout(disp_refr->act_scr);
if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
lv_obj_update_layout(disp_refr->top_layer);
lv_obj_update_layout(disp_refr->sys_layer);
/*Do nothing if there is no active screen*/
if(disp_refr->act_scr == NULL) {
disp_refr->inv_p = 0;
LV_LOG_WARN("there is no active screen");
TRACE_REFR("finished");
return;
}
lv_refr_join_area();
lv_refr_areas();
/*If refresh happened ...*/
if(disp_refr->inv_p != 0) {
if(disp_refr->driver->full_refresh) {
draw_buf_flush();
}
/*Clean up*/
lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
disp_refr->inv_p = 0;
elaps = lv_tick_elaps(start);
/*Call monitor cb if present*/
if(disp_refr->driver->monitor_cb) {
disp_refr->driver->monitor_cb(disp_refr->driver, elaps, px_num);
}
}
lv_mem_buf_free_all();
_lv_font_clean_up_fmt_txt();
#if LV_USE_PERF_MONITOR && LV_USE_LABEL
static lv_obj_t * perf_label = NULL;
if(perf_label == NULL) {
perf_label = lv_label_create(lv_layer_sys());
lv_obj_set_style_bg_opa(perf_label, LV_OPA_10, 0);
lv_obj_set_style_bg_color(perf_label, lv_color_black(), 0);
lv_obj_set_style_text_color(perf_label, lv_color_white(), 0);
lv_obj_set_style_pad_top(perf_label, 3, 0);
lv_obj_set_style_pad_bottom(perf_label, 3, 0);
lv_obj_set_style_pad_left(perf_label, 3, 0);
lv_obj_set_style_pad_right(perf_label, 3, 0);
lv_obj_set_style_text_align(perf_label, LV_TEXT_ALIGN_RIGHT, 0);
lv_label_set_text(perf_label, "?");
lv_obj_align(perf_label, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
}
static uint32_t perf_last_time = 0;
static uint32_t elaps_sum = 0;
static uint32_t frame_cnt = 0;
if(lv_tick_elaps(perf_last_time) < 300) {
if(px_num > 5000) {
elaps_sum += elaps;
frame_cnt ++;
}
}
else {
perf_last_time = lv_tick_get();
uint32_t fps_limit = 1000 / disp_refr->refr_timer->period;
uint32_t fps;
if(elaps_sum == 0) elaps_sum = 1;
if(frame_cnt == 0) fps = fps_limit;
else fps = (1000 * frame_cnt) / elaps_sum;
elaps_sum = 0;
frame_cnt = 0;
if(fps > fps_limit) fps = fps_limit;
fps_sum_all += fps;
fps_sum_cnt ++;
uint32_t cpu = 100 - lv_timer_get_idle();
lv_label_set_text_fmt(perf_label, "%d FPS\n%d%% CPU", fps, cpu);
}
#endif
#if LV_USE_MEM_MONITOR && LV_MEM_CUSTOM == 0 && LV_USE_LABEL
static lv_obj_t * mem_label = NULL;
if(mem_label == NULL) {
mem_label = lv_label_create(lv_layer_sys());
lv_obj_set_style_bg_opa(mem_label, LV_OPA_50, 0);
lv_obj_set_style_bg_color(mem_label, lv_color_black(), 0);
lv_obj_set_style_text_color(mem_label, lv_color_white(), 0);
lv_obj_set_style_pad_top(mem_label, 3, 0);
lv_obj_set_style_pad_bottom(mem_label, 3, 0);
lv_obj_set_style_pad_left(mem_label, 3, 0);
lv_obj_set_style_pad_right(mem_label, 3, 0);
lv_label_set_text(mem_label, "?");
lv_obj_align(mem_label, LV_ALIGN_BOTTOM_LEFT, 0, 0);
}
static uint32_t mem_last_time = 0;
if(lv_tick_elaps(mem_last_time) > 300) {
mem_last_time = lv_tick_get();
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
uint32_t used_size = mon.total_size - mon.free_size;;
uint32_t used_kb = used_size / 1024;
uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
lv_label_set_text_fmt(mem_label, "%d.%d kB used (%d %%)\n%d%% frag.", used_kb, used_kb_tenth, mon.used_pct, mon.frag_pct);
}
#endif
TRACE_REFR("finished");
}
#if LV_USE_PERF_MONITOR
uint32_t lv_refr_get_fps_avg(void)
{
return fps_sum_all / fps_sum_cnt;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Join the areas which has got common parts
*/
static void lv_refr_join_area(void)
{
uint32_t join_from;
uint32_t join_in;
lv_area_t joined_area;
for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
if(disp_refr->inv_area_joined[join_in] != 0) continue;
/*Check all areas to join them in 'join_in'*/
for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
/*Handle only unjoined areas and ignore itself*/
if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
continue;
}
/*Check if the areas are on each other*/
if(_lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
continue;
}
_lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
/*Join two area only if the joined area size is smaller*/
if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
/*Mark 'join_form' is joined into 'join_in'*/
disp_refr->inv_area_joined[join_from] = 1;
}
}
}
}
/**
* Refresh the joined areas
*/
static void lv_refr_areas(void)
{
px_num = 0;
if(disp_refr->inv_p == 0) return;
/*Find the last area which will be drawn*/
int32_t i;
int32_t last_i = 0;
for(i = disp_refr->inv_p - 1; i >= 0; i--) {
if(disp_refr->inv_area_joined[i] == 0) {
last_i = i;
break;
}
}
disp_refr->driver->draw_buf->last_area = 0;
disp_refr->driver->draw_buf->last_part = 0;
for(i = 0; i < disp_refr->inv_p; i++) {
/*Refresh the unjoined areas*/
if(disp_refr->inv_area_joined[i] == 0) {
if(i == last_i) disp_refr->driver->draw_buf->last_area = 1;
disp_refr->driver->draw_buf->last_part = 0;
lv_refr_area(&disp_refr->inv_areas[i]);
px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
}
}
}
/**
* Refresh an area if there is Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area(const lv_area_t * area_p)
{
/*With full refresh just redraw directly into the buffer*/
if(disp_refr->driver->full_refresh) {
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
draw_buf->area.x1 = 0;
draw_buf->area.x2 = lv_disp_get_hor_res(disp_refr) - 1;
draw_buf->area.y1 = 0;
draw_buf->area.y2 = lv_disp_get_ver_res(disp_refr) - 1;
disp_refr->driver->draw_buf->last_part = 1;
lv_refr_area_part(area_p);
}
/*Normal refresh: draw the area in parts*/
else {
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
/*Calculate the max row num*/
lv_coord_t w = lv_area_get_width(area_p);
lv_coord_t h = lv_area_get_height(area_p);
lv_coord_t y2 =
area_p->y2 >= lv_disp_get_ver_res(disp_refr) ? lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
int32_t max_row = (uint32_t)draw_buf->size / w;
if(max_row > h) max_row = h;
//printf("1 max_row %d \n",max_row);
/*Round down the lines of draw_buf if rounding is added*/
if(disp_refr->driver->rounder_cb) {
lv_area_t tmp;
tmp.x1 = 0;
tmp.x2 = 0;
tmp.y1 = 0;
lv_coord_t h_tmp = max_row;
do {
tmp.y2 = h_tmp - 1;
disp_refr->driver->rounder_cb(disp_refr->driver, &tmp);
/*If this height fits into `max_row` then fine*/
if(lv_area_get_height(&tmp) <= max_row) break;
/*Decrement the height of the area until it fits into `max_row` after rounding*/
h_tmp--;
} while(h_tmp > 0);
if(h_tmp <= 0) {
LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or to "
"small draw_buf)");
return;
}
else {
max_row = tmp.y2 + 1;
}
}
//printf("2 max_row %d \n",max_row);
/*Always use the full row*/
lv_coord_t row;
lv_coord_t row_last = 0;
for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
/*Calc. the next y coordinates of draw_buf*/
draw_buf->area.x1 = area_p->x1;
draw_buf->area.x2 = area_p->x2;
draw_buf->area.y1 = row;
draw_buf->area.y2 = row + max_row - 1;
if(draw_buf->area.y2 > y2) draw_buf->area.y2 = y2;
row_last = draw_buf->area.y2;
if(y2 == row_last) disp_refr->driver->draw_buf->last_part = 1;
//printf("max_row %d \n",draw_buf->area.y2 - draw_buf->area.y1);
lv_refr_area_part(area_p);
}
/*If the last y coordinates are not handled yet ...*/
if(y2 != row_last) {
/*Calc. the next y coordinates of draw_buf*/
draw_buf->area.x1 = area_p->x1;
draw_buf->area.x2 = area_p->x2;
draw_buf->area.y1 = row;
draw_buf->area.y2 = y2;
disp_refr->driver->draw_buf->last_part = 1;
lv_refr_area_part(area_p);
}
}
}
/**
* Refresh a part of an area which is on the actual Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area_part(const lv_area_t * area_p)
{
//printf("1 flash area : x1 %d, x2 %d, y1 %d, y2 %d \n",area_p->x1,area_p->x2,area_p->y1,area_p->y2);
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
while(draw_buf->flushing) {
if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
}
lv_obj_t * top_act_scr = NULL;
lv_obj_t * top_prev_scr = NULL;
/*Get the new mask from the original area and the act. draw_buf
It will be a part of 'area_p'*/
lv_area_t start_mask;
_lv_area_intersect(&start_mask, area_p, &draw_buf->area);
/*Get the most top object which is not covered by others*/
top_act_scr = lv_refr_get_top_obj(&start_mask, lv_disp_get_scr_act(disp_refr));
if(disp_refr->prev_scr) {
top_prev_scr = lv_refr_get_top_obj(&start_mask, disp_refr->prev_scr);
}
/*Draw a display background if there is no top object*/
if(top_act_scr == NULL && top_prev_scr == NULL) {
if(disp_refr->bg_img) {
lv_draw_img_dsc_t dsc;
lv_draw_img_dsc_init(&dsc);
dsc.opa = disp_refr->bg_opa;
lv_img_header_t header;
lv_res_t res;
res = lv_img_decoder_get_info(disp_refr->bg_img, &header);
if(res == LV_RES_OK) {
lv_area_t a;
lv_area_set(&a, 0, 0, header.w - 1, header.h - 1);
lv_draw_img(&a, &start_mask, disp_refr->bg_img, &dsc);
}
else {
LV_LOG_WARN("Can't draw the background image")
}
}
else {
lv_draw_rect_dsc_t dsc;
lv_draw_rect_dsc_init(&dsc);
dsc.bg_color = disp_refr->bg_color;
dsc.bg_opa = disp_refr->bg_opa;
lv_draw_rect(&start_mask, &start_mask, &dsc);
}
}
/*Refresh the previous screen if any*/
if(disp_refr->prev_scr) {
/*Get the most top object which is not covered by others*/
if(top_prev_scr == NULL) {
top_prev_scr = disp_refr->prev_scr;
}
/*Do the refreshing from the top object*/
lv_refr_obj_and_children(top_prev_scr, &start_mask);
}
if(top_act_scr == NULL) {
top_act_scr = disp_refr->act_scr;
}
/*Do the refreshing from the top object*/
lv_refr_obj_and_children(top_act_scr, &start_mask);
/*Also refresh top and sys layer unconditionally*/
lv_refr_obj_and_children(lv_disp_get_layer_top(disp_refr), &start_mask);
lv_refr_obj_and_children(lv_disp_get_layer_sys(disp_refr), &start_mask);
/*In true double buffered mode flush only once when all areas were rendered.
*In normal mode flush after every area*/
if(disp_refr->driver->full_refresh == false) {
//printf("2 flash area : x1 %d, x2 %d, y1 %d, y2 %d \n",area_p->x1,area_p->x2,area_p->y1,area_p->y2);
draw_buf_flush();
}
}
/**
* Search the most top object which fully covers an area
* @param area_p pointer to an area
* @param obj the first object to start the searching (typically a screen)
* @return
*/
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
{
lv_obj_t * found_p = NULL;
/*If this object is fully cover the draw area check the children too*/
if(_lv_area_is_in(area_p, &obj->coords, 0) && lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN) == false) {
lv_cover_check_info_t info;
info.res = LV_COVER_RES_COVER;
info.area = area_p;
lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
if(info.res == LV_COVER_RES_MASKED) return NULL;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
found_p = lv_refr_get_top_obj(area_p, child);
/*If a children is ok then break*/
if(found_p != NULL) {
break;
}
}
/*If no better children use this object*/
if(found_p == NULL) {
if(info.res == LV_COVER_RES_COVER) {
found_p = obj;
}
}
}
return found_p;
}
/**
* Make the refreshing from an object. Draw all its children and the youngers too.
* @param top_p pointer to an objects. Start the drawing from it.
* @param mask_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
{
/*Normally always will be a top_obj (at least the screen)
*but in special cases (e.g. if the screen has alpha) it won't.
*In this case use the screen directly*/
if(top_p == NULL) top_p = lv_disp_get_scr_act(disp_refr);
if(top_p == NULL) return; /*Shouldn't happen*/
/*Refresh the top object and its children*/
lv_refr_obj(top_p, mask_p);
/*Draw the 'younger' sibling objects because they can be on top_obj*/
lv_obj_t * par;
lv_obj_t * border_p = top_p;
par = lv_obj_get_parent(top_p);
/*Do until not reach the screen*/
while(par != NULL) {
bool go = false;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(par); i++) {
lv_obj_t * child = lv_obj_get_child(par, i);
if(!go) {
if(child == border_p) go = true;
} else {
/*Refresh the objects*/
lv_refr_obj(child, mask_p);
}
}
/*Call the post draw draw function of the parents of the to object*/
lv_event_send(par, LV_EVENT_DRAW_POST_BEGIN, (void*)mask_p);
lv_event_send(par, LV_EVENT_DRAW_POST, (void*)mask_p);
lv_event_send(par, LV_EVENT_DRAW_POST_END, (void*)mask_p);
/*The new border will be the last parents,
*so the 'younger' brothers of parent will be refreshed*/
border_p = par;
/*Go a level deeper*/
par = lv_obj_get_parent(par);
}
}
/**
* Refresh an object an all of its children. (Called recursively)
* @param obj pointer to an object to refresh
* @param mask_ori_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
{
/*Do not refresh hidden objects*/
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
bool union_ok; /*Store the return value of area_union*/
/*Truncate the original mask to the coordinates of the parent
*because the parent and its children are visible only here*/
lv_area_t obj_mask;
lv_area_t obj_ext_mask;
lv_area_t obj_area;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_obj_get_coords(obj, &obj_area);
obj_area.x1 -= ext_size;
obj_area.y1 -= ext_size;
obj_area.x2 += ext_size;
obj_area.y2 += ext_size;
union_ok = _lv_area_intersect(&obj_ext_mask, mask_ori_p, &obj_area);
/*Draw the parent and its children only if they ore on 'mask_parent'*/
if(union_ok != false) {
/*Redraw the object*/
lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_MAIN, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, &obj_ext_mask);
#if LV_USE_REFR_DEBUG
lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
draw_dsc.bg_color.full = debug_color.full;
draw_dsc.bg_opa = LV_OPA_20;
draw_dsc.border_width = 1;
draw_dsc.border_opa = LV_OPA_30;
draw_dsc.border_color = debug_color;
lv_draw_rect(&obj_ext_mask, &obj_ext_mask, &draw_dsc);
#endif
/*Create a new 'obj_mask' without 'ext_size' because the children can't be visible there*/
lv_obj_get_coords(obj, &obj_area);
union_ok = _lv_area_intersect(&obj_mask, mask_ori_p, &obj_area);
if(union_ok != false) {
lv_area_t mask_child; /*Mask from obj and its child*/
lv_area_t child_area;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
lv_obj_get_coords(child, &child_area);
ext_size = _lv_obj_get_ext_draw_size(child);
child_area.x1 -= ext_size;
child_area.y1 -= ext_size;
child_area.x2 += ext_size;
child_area.y2 += ext_size;
/*Get the union (common parts) of original mask (from obj)
*and its child*/
union_ok = _lv_area_intersect(&mask_child, &obj_mask, &child_area);
/*If the parent and the child has common area then refresh the child*/
if(union_ok) {
/*Refresh the next children*/
lv_refr_obj(child, &mask_child);
}
}
}
/*If all the children are redrawn make 'post draw' draw*/
lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_POST, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_POST_END, &obj_ext_mask);
}
}
static void draw_buf_rotate_180(lv_disp_drv_t *drv, lv_area_t *area, lv_color_t *color_p) {
lv_coord_t area_w = lv_area_get_width(area);
lv_coord_t area_h = lv_area_get_height(area);
uint32_t total = area_w * area_h;
/*Swap the beginning and end values*/
lv_color_t tmp;
uint32_t i = total - 1, j = 0;
while(i > j) {
tmp = color_p[i];
color_p[i] = color_p[j];
color_p[j] = tmp;
i--;
j++;
}
lv_coord_t tmp_coord;
tmp_coord = area->y2;
area->y2 = drv->ver_res - area->y1 - 1;
area->y1 = drv->ver_res - tmp_coord - 1;
tmp_coord = area->x2;
area->x2 = drv->hor_res - area->x1 - 1;
area->x1 = drv->hor_res - tmp_coord - 1;
}
static LV_ATTRIBUTE_FAST_MEM void draw_buf_rotate_90(bool invert_i, lv_coord_t area_w, lv_coord_t area_h, lv_color_t *orig_color_p, lv_color_t *rot_buf) {
uint32_t invert = (area_w * area_h) - 1;
uint32_t initial_i = ((area_w - 1) * area_h);
for(lv_coord_t y = 0; y < area_h; y++) {
uint32_t i = initial_i + y;
if(invert_i)
i = invert - i;
for(lv_coord_t x = 0; x < area_w; x++) {
rot_buf[i] = *(orig_color_p++);
if(invert_i)
i += area_h;
else
i -= area_h;
}
}
}
/**
* Helper function for draw_buf_rotate_90_sqr. Given a list of four numbers, rotate the entire list to the left.
*/
static inline void draw_buf_rotate4(lv_color_t *a, lv_color_t *b, lv_color_t * c, lv_color_t * d) {
lv_color_t tmp;
tmp = *a;
*a = *b;
*b = *c;
*c = *d;
*d = tmp;
}
/**
* Rotate a square image 90/270 degrees in place.
* @note inspired by https://stackoverflow.com/a/43694906
*/
static void draw_buf_rotate_90_sqr(bool is_270, lv_coord_t w, lv_color_t * color_p) {
for(lv_coord_t i = 0; i < w/2; i++) {
for(lv_coord_t j = 0; j < (w + 1)/2; j++) {
lv_coord_t inv_i = (w - 1) - i;
lv_coord_t inv_j = (w - 1) - j;
if(is_270) {
draw_buf_rotate4(
&color_p[i * w + j],
&color_p[inv_j * w + i],
&color_p[inv_i * w + inv_j],
&color_p[j * w + inv_i]
);
} else {
draw_buf_rotate4(
&color_p[i * w + j],
&color_p[j * w + inv_i],
&color_p[inv_i * w + inv_j],
&color_p[inv_j * w + i]
);
}
}
}
}
/**
* Rotate the draw_buf to the display's native orientation.
*/
static void draw_buf_rotate(lv_area_t *area, lv_color_t *color_p) {
lv_disp_drv_t * drv = disp_refr->driver;
if(disp_refr->driver->full_refresh && drv->sw_rotate) {
LV_LOG_ERROR("cannot rotate a full refreshed display!");
return;
}
if(drv->rotated == LV_DISP_ROT_180) {
draw_buf_rotate_180(drv, area, color_p);
call_flush_cb(drv, area, color_p);
} else if(drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
/*Allocate a temporary buffer to store rotated image*/
lv_color_t * rot_buf = NULL;
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
lv_coord_t area_w = lv_area_get_width(area);
lv_coord_t area_h = lv_area_get_height(area);
/*Determine the maximum number of rows that can be rotated at a time*/
lv_coord_t max_row = LV_MIN((lv_coord_t)((LV_DISP_ROT_MAX_BUF/sizeof(lv_color_t)) / area_w), area_h);
lv_coord_t init_y_off;
init_y_off = area->y1;
if(drv->rotated == LV_DISP_ROT_90) {
area->y2 = drv->ver_res - area->x1 - 1;
area->y1 = area->y2 - area_w + 1;
}
else {
area->y1 = area->x1;
area->y2 = area->y1 + area_w - 1;
}
draw_buf->flushing = 0;
/*Rotate the screen in chunks, flushing after each one*/
lv_coord_t row = 0;
while(row < area_h) {
lv_coord_t height = LV_MIN(max_row, area_h-row);
draw_buf->flushing = 1;
if((row == 0) && (area_h >= area_w)) {
/*Rotate the initial area as a square*/
height = area_w;
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 = drv->hor_res - 1 - init_y_off;
area->x1 = area->x2 - area_w + 1;
}
}
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->x2 = drv->hor_res - 1 - init_y_off - row;
area->x1 = area->x2 - height + 1;
}
}
/*Flush the completed area to the display*/
call_flush_cb(drv, area, rot_buf == NULL ? color_p : rot_buf);
/*FIXME: Rotation forces legacy behavior where rendering and flushing are done serially*/
while(draw_buf->flushing) {
if(drv->wait_cb) drv->wait_cb(drv);
}
color_p += area_w * height;
row += height;
}
/*Free the allocated buffer at the end if necessary*/
if(rot_buf != NULL) lv_mem_buf_release(rot_buf);
}
}
/**
* Flush the content of the draw buffer
*/
static void draw_buf_flush(void)
{
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
lv_color_t * color_p = draw_buf->buf_act;
draw_buf->flushing = 1;
if(disp_refr->driver->draw_buf->last_area && disp_refr->driver->draw_buf->last_part) draw_buf->flushing_last = 1;
else draw_buf->flushing_last = 0;
/*Flush the rendered content to the display*/
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->gpu_wait_cb) disp->driver->gpu_wait_cb(disp->driver);
if(disp->driver->flush_cb) {
/*Rotate the buffer to the display's native orientation if necessary*/
if(disp->driver->rotated != LV_DISP_ROT_NONE && disp->driver->sw_rotate) {
draw_buf_rotate(&draw_buf->area, draw_buf->buf_act);
} else {
//lv_area_t *area_p = &draw_buf->area;
//printf("2 flash area : x1 %d, x2 %d, y1 %d, y2 %d \n",area_p->x1,area_p->x2,area_p->y1,area_p->y2);
call_flush_cb(disp->driver, &draw_buf->area, color_p);
}
}
if(draw_buf->buf1 && draw_buf->buf2) {
if(draw_buf->buf_act == draw_buf->buf1)
draw_buf->buf_act = draw_buf->buf2;
else
draw_buf->buf_act = draw_buf->buf1;
}
}
static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
{
TRACE_REFR("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer", area->x1, area->y1, area->x2, area->y2, color_p);
drv->flush_cb(drv, area, color_p);
}

View File

@ -0,0 +1,103 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include <stdbool.h>
/*********************
* DEFINES
*********************/
#define LV_REFR_TASK_PRIO LV_TASK_PRIO_MID
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void _lv_refr_init(void);
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
* can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
* (e.g. progress bar) this function can be called when the screen should be updated.
* @param disp pointer to display to refresh. NULL to refresh all displays.
*/
void lv_refr_now(lv_disp_t * disp);
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
*/
void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p);
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * _lv_refr_get_disp_refreshing(void);
/**
* Set the display which is being refreshed.
* It shouldn't be used directly by the user.
* It can be used to trick the drawing functions about there is an active display.
* @param the display being refreshed
*/
void _lv_refr_set_disp_refreshing(lv_disp_t * disp);
#if LV_USE_PERF_MONITOR
/**
* Get the average FPS since start up
* @return the average FPS
*/
uint32_t lv_refr_get_fps_avg(void);
#endif
/**
* Called periodically to handle the refreshing
* @param timer pointer to the timer itself
*/
void _lv_disp_refr_timer(lv_timer_t * timer);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_H*/

View File

@ -0,0 +1,118 @@
/**
* @file lv_theme.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void apply_theme(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj)
{
lv_disp_t * disp = obj ? lv_obj_get_disp(obj) : lv_disp_get_default();
return lv_disp_get_theme(disp);
}
/**
* Apply the active theme on an object
* @param obj pointer to an object
* @param name the name of the theme element to apply. E.g. `LV_THEME_BTN`
*/
void lv_theme_apply(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
if(th == NULL) return;
lv_obj_remove_style_all(obj);
apply_theme(th, obj); /*Apply the theme including the base theme(s)*/
}
/**
* Set a base theme for a theme.
* The styles from the base them will be added before the styles of the current theme.
* Arbitrary long chain of themes can be created by setting base themes.
* @param new_theme pointer to theme which base should be set
* @param base pointer to the base theme
*/
void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * base)
{
new_theme->parent = base;
}
/**
* Set a callback for a theme.
* The callback is used to add styles to different objects
* @param theme pointer to theme which callback should be set
* @param cb pointer to the callback
*/
void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb)
{
theme->apply_cb = apply_cb;
}
const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_small : LV_FONT_DEFAULT;
}
const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_normal : LV_FONT_DEFAULT;
}
const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_large : LV_FONT_DEFAULT;
}
lv_color_t lv_theme_get_color_primary(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->color_primary : lv_palette_main(LV_PALETTE_BLUE_GREY);
}
lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->color_secondary : lv_palette_main(LV_PALETTE_BLUE);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void apply_theme(lv_theme_t * th, lv_obj_t * obj)
{
if(th->parent) apply_theme(th->parent, obj);
if(th->apply_cb) th->apply_cb(th, obj);
}

View File

@ -0,0 +1,115 @@
/**
*@file lv_theme.h
*
*/
#ifndef LV_THEME_H
#define LV_THEME_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_theme_t;
struct _lv_disp_t;
typedef void (*lv_theme_apply_cb_t)(struct _lv_theme_t *, lv_obj_t *);
typedef struct _lv_theme_t {
lv_theme_apply_cb_t apply_cb;
struct _lv_theme_t * parent; /**< Apply the current theme's style on top of this theme.*/
void * user_data;
struct _lv_disp_t * disp;
lv_color_t color_primary;
lv_color_t color_secondary;
const lv_font_t * font_small;
const lv_font_t * font_normal;
const lv_font_t * font_large;
uint32_t flags; /*Any custom flag used by the theme*/
} lv_theme_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the theme assigned to the display of the object
* @param obj pointer to object
* @return the theme of the object's display (can be NULL)
*/
lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj);
/**
* Apply the active theme on an object
* @param obj pointer to an object
*/
void lv_theme_apply(lv_obj_t * obj);
/**
* Set a base theme for a theme.
* The styles from the base them will be added before the styles of the current theme.
* Arbitrary long chain of themes can be created by setting base themes.
* @param new_theme pointer to theme which base should be set
* @param parent pointer to the base theme
*/
void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * parent);
/**
* Set an apply callback for a theme.
* The apply callback is used to add styles to different objects
* @param theme pointer to theme which callback should be set
* @param apply_cb pointer to the callback
*/
void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb);
/**
* Get the small font of the theme
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj);
/**
* Get the normal font of the theme
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj);
/**
* Get the subtitle font of the theme
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj);
/**
* Get the primary color of the theme
* @return the color
*/
lv_color_t lv_theme_get_color_primary(lv_obj_t * obj);
/**
* Get the secondary color of the theme
* @return the color
*/
lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_H*/

View File

@ -0,0 +1,59 @@
/**
* @file lv_draw.h
*
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_style.h"
#include "../misc/lv_txt.h"
#include "lv_img_decoder.h"
#include "lv_draw_rect.h"
#include "lv_draw_label.h"
#include "lv_draw_img.h"
#include "lv_draw_line.h"
#include "lv_draw_triangle.h"
#include "lv_draw_arc.h"
#include "lv_draw_blend.h"
#include "lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* POST INCLUDES
*********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_H*/

View File

@ -0,0 +1,16 @@
CSRCS += lv_draw_arc.c
CSRCS += lv_draw_blend.c
CSRCS += lv_draw_img.c
CSRCS += lv_draw_label.c
CSRCS += lv_draw_line.c
CSRCS += lv_draw_mask.c
CSRCS += lv_draw_rect.c
CSRCS += lv_draw_triangle.c
CSRCS += lv_img_buf.c
CSRCS += lv_img_cache.c
CSRCS += lv_img_decoder.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw"

View File

@ -0,0 +1,542 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_arc.h"
#include "lv_draw_rect.h"
#include "lv_draw_mask.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
#define SPLIT_RADIUS_LIMIT 10 /*With radius greater then this the arc will drawn in quarters. A quarter is drawn only if there is arc in it*/
#define SPLIT_ANGLE_GAP_LIMIT 60 /*With small gaps in the arc don't bother with splitting because there is nothing to skip.*/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_coord_t center_x;
lv_coord_t center_y;
lv_coord_t radius;
uint16_t start_angle;
uint16_t end_angle;
uint16_t start_quarter;
uint16_t end_quarter;
lv_coord_t width;
lv_draw_rect_dsc_t * draw_dsc;
const lv_area_t * draw_area;
const lv_area_t * clip_area;
} quarter_draw_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q);
static void draw_quarter_1(quarter_draw_dsc_t * q);
static void draw_quarter_2(quarter_draw_dsc_t * q);
static void draw_quarter_3(quarter_draw_dsc_t * q);
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc)
{
#if LV_DRAW_COMPLEX
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(start_angle == end_angle) return;
lv_coord_t width = dsc->width;
if(width > radius) width = radius;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
cir_dsc.blend_mode = dsc->blend_mode;
if(dsc->img_src) {
cir_dsc.bg_opa = LV_OPA_TRANSP;
cir_dsc.bg_img_src = dsc->img_src;
cir_dsc.bg_img_opa = dsc->opa;
} else {
cir_dsc.bg_opa = dsc->opa;
cir_dsc.bg_color = dsc->color;
}
lv_area_t area_out;
area_out.x1 = center_x - radius;
area_out.y1 = center_y - radius;
area_out.x2 = center_x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/
area_out.y2 = center_y + radius - 1;
lv_area_t area_in;
lv_area_copy(&area_in, &area_out);
area_in.x1 += dsc->width;
area_in.y1 += dsc->width;
area_in.x2 -= dsc->width;
area_in.y2 -= dsc->width;
/*Draw a full ring*/
if(start_angle + 360 == end_angle || start_angle == end_angle + 360) {
cir_dsc.border_width = dsc->width;
cir_dsc.border_color = dsc->color;
cir_dsc.border_opa = dsc->opa;
cir_dsc.bg_opa = LV_OPA_TRANSP;
cir_dsc.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&area_out, clip_area, &cir_dsc);
return;
}
while(start_angle >= 360) start_angle -= 360;
while(end_angle >= 360) end_angle -= 360;
lv_draw_mask_angle_param_t mask_angle_param;
lv_draw_mask_angle_init(&mask_angle_param, center_x, center_y, start_angle, end_angle);
int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL);
/*Create inner the mask*/
lv_draw_mask_radius_param_t mask_in_param;
lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
int16_t mask_in_id = lv_draw_mask_add(&mask_in_param, NULL);
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL);
int32_t angle_gap;
if(end_angle > start_angle) {
angle_gap = 360 - (end_angle - start_angle);
}
else {
angle_gap = start_angle - end_angle;
}
if(angle_gap > SPLIT_ANGLE_GAP_LIMIT && radius > SPLIT_RADIUS_LIMIT) {
/*Handle each quarter individually and skip which is empty*/
quarter_draw_dsc_t q_dsc;
q_dsc.center_x = center_x;
q_dsc.center_y = center_y;
q_dsc.radius = radius;
q_dsc.start_angle = start_angle;
q_dsc.end_angle = end_angle;
q_dsc.start_quarter = (start_angle / 90) & 0x3;
q_dsc.end_quarter = (end_angle / 90) & 0x3;
q_dsc.width = width;
q_dsc.draw_dsc = &cir_dsc;
q_dsc.draw_area = &area_out;
q_dsc.clip_area = clip_area;
draw_quarter_0(&q_dsc);
draw_quarter_1(&q_dsc);
draw_quarter_2(&q_dsc);
draw_quarter_3(&q_dsc);
}
else {
lv_draw_rect(&area_out, clip_area, &cir_dsc);
}
lv_draw_mask_remove_id(mask_angle_id);
lv_draw_mask_remove_id(mask_out_id);
lv_draw_mask_remove_id(mask_in_id);
if(dsc->rounded) {
lv_draw_mask_radius_param_t mask_end_param;
lv_area_t round_area;
get_rounded_area(start_angle, radius, width, &round_area);
round_area.x1 += center_x;
round_area.x2 += center_x;
round_area.y1 += center_y;
round_area.y2 += center_y;
lv_area_t clip_area2;
if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
lv_draw_rect(&area_out, &clip_area2, &cir_dsc);
lv_draw_mask_remove_id(mask_end_id);
}
get_rounded_area(end_angle, radius, width, &round_area);
round_area.x1 += center_x;
round_area.x2 += center_x;
round_area.y1 += center_y;
round_area.y2 += center_y;
if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
lv_draw_rect(&area_out, &clip_area2, &cir_dsc);
lv_draw_mask_remove_id(mask_end_id);
}
}
#else
LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0");
LV_UNUSED(center_x);
LV_UNUSED(center_y);
LV_UNUSED(radius);
LV_UNUSED(start_angle);
LV_UNUSED(end_angle);
LV_UNUSED(clip_area);
LV_UNUSED(dsc);
#endif /*LV_DRAW_COMPLEX*/
}
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, lv_coord_t w, bool rounded, lv_area_t * area)
{
lv_coord_t rout = radius;
lv_coord_t rin = radius - w;
lv_coord_t extra_area = rounded ? w : 0;
uint8_t start_quarter = start_angle / 90;
uint8_t end_quarter = end_angle / 90;
if(start_quarter == end_quarter && start_angle <= end_angle) {
if(start_quarter == 0) {
area->y1 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 1) {
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 2) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
}
else if(start_quarter == 0 && end_quarter == 1) {
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle),
lv_trigo_sin(start_angle)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + rout + extra_area;
}
else if(start_quarter == 1 && end_quarter == 2) {
area->x1 = x - rout - extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle + 90),
lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 2 && end_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y - rout - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle) * rin,
lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3 && end_quarter == 0) {
area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle + 90),
lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + rout + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 0 && q->end_quarter == 0 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 0 || q->end_quarter == 0) {
/*Start and/or end arcs here*/
if(q->start_quarter == 0) {
quarter_area.x1 = q->center_x;
quarter_area.y2 = q->center_y + q->radius;
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 0) {
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y1 = q->center_y;
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 0 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 1) ||
(q->start_quarter == 3 && q->end_quarter == 2) ||
(q->start_quarter == 3 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x;
quarter_area.y1 = q->center_y;
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y2 = q->center_y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void draw_quarter_1(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 1 && q->end_quarter == 1 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 1 || q->end_quarter == 1) {
/*Start and/or end arcs here*/
if(q->start_quarter == 1) {
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y1 = q->center_y;
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 1) {
quarter_area.x2 = q->center_x - 1;
quarter_area.y2 = q->center_y + q->radius;
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 1 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 2) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 3 && q->end_quarter == 2)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y1 = q->center_y;
quarter_area.x2 = q->center_x - 1;
quarter_area.y2 = q->center_y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void draw_quarter_2(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 2 && q->end_quarter == 2 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 2 || q->end_quarter == 2) {
/*Start and/or end arcs here*/
if(q->start_quarter == 2) {
quarter_area.x2 = q->center_x - 1;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 2) {
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y2 = q->center_y - 1;
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 2 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 0)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x2 = q->center_x - 1;
quarter_area.y2 = q->center_y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void draw_quarter_3(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 3 && q->end_quarter == 3 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 3 || q->end_quarter == 3) {
/*Start and/or end arcs here*/
if(q->start_quarter == 3) {
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y2 = q->center_y - 1;
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 3) {
quarter_area.x1 = q->center_x;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 3 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 0) ||
(q->start_quarter == 1 && q->end_quarter == 0) ||
(q->start_quarter == 2 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y2 = q->center_y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area)
{
const uint8_t ps = 8;
const uint8_t pa = 127;
int32_t thick_half = thickness / 2;
uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
int32_t cir_x;
int32_t cir_y;
cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps);
cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
/*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
if(cir_x > 0) {
cir_x = (cir_x - pa) >> ps;
res_area->x1 = cir_x - thick_half + thick_corr;
res_area->x2 = cir_x + thick_half;
}
else {
cir_x = (cir_x + pa) >> ps;
res_area->x1 = cir_x - thick_half;
res_area->x2 = cir_x + thick_half - thick_corr;
}
if(cir_y > 0) {
cir_y = (cir_y - pa) >> ps;
res_area->y1 = cir_y - thick_half + thick_corr;
res_area->y2 = cir_y + thick_half;
}
else {
cir_y = (cir_y + pa) >> ps;
res_area->y1 = cir_y - thick_half;
res_area->y2 = cir_y + thick_half - thick_corr;
}
}
#endif /*LV_DRAW_COMPLEX*/

View File

@ -0,0 +1,75 @@
/**
* @file lv_draw_arc.h
*
*/
#ifndef LV_DRAW_ARC_H
#define LV_DRAW_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_line.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t color;
lv_coord_t width;
const void * img_src;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t rounded : 1;
} lv_draw_arc_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc);
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param mask the arc will be drawn only in this mask
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param clip_area the arc will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc);
/**
* Get an area the should be invalidated when the arcs angle changed between start_angle and end_ange
* @param x the x coordinate of the center of the arc
* @param y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param w width of the arc
* @param rounded true: the arc is rounded
* @param area store the area to invalidate here
*/
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, lv_coord_t w, bool rounded, lv_area_t * area);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_ARC*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
/**
* @file lv_draw_blend.h
*
*/
#ifndef LV_DRAW_BLEND_H
#define LV_DRAW_BLEND_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
#include "lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
//! @cond Doxygen_Suppress
LV_ATTRIBUTE_FAST_MEM void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color,
lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode);
LV_ATTRIBUTE_FAST_MEM void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area,
const lv_color_t * map_buf,
lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode);
//! @endcond
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_BLEND_H*/

View File

@ -0,0 +1,660 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_img.h"
#include "lv_img_cache.h"
#include "../hal/lv_hal_disp.h"
#include "../misc/lv_log.h"
#include "../core/lv_refr.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_math.h"
#if LV_USE_GPU_STM32_DMA2D
#include "../gpu/lv_gpu_stm32_dma2d.h"
#elif LV_USE_GPU_NXP_PXP
#include "../gpu/lv_gpu_nxp_pxp.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area,
const void * src,
const lv_draw_img_dsc_t * draw_dsc);
LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const lv_area_t * clip_area,
const uint8_t * map_p,
const lv_draw_img_dsc_t * draw_dsc,
bool chroma_key, bool alpha_byte);
static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg);
static void draw_cleanup(_lv_img_cache_entry_t * cache);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
dsc->recolor = lv_color_black();
dsc->opa = LV_OPA_COVER;
dsc->zoom = LV_IMG_ZOOM_NONE;
dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
}
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * src, const lv_draw_img_dsc_t * dsc)
{
if(src == NULL) {
LV_LOG_WARN("Image draw: src is NULL");
show_error(coords, mask, "No\ndata");
return;
}
if(dsc->opa <= LV_OPA_MIN) return;
lv_res_t res;
res = lv_img_draw_core(coords, mask, src, dsc);
if(res == LV_RES_INV) {
LV_LOG_WARN("Image draw error");
show_error(coords, mask, "No\ndata");
return;
}
}
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
{
uint8_t px_size = 0;
switch(cf) {
case LV_IMG_CF_UNKNOWN:
case LV_IMG_CF_RAW:
px_size = 0;
break;
case LV_IMG_CF_TRUE_COLOR:
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
px_size = LV_COLOR_SIZE;
break;
case LV_IMG_CF_TRUE_COLOR_ALPHA:
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
break;
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_ALPHA_1BIT:
px_size = 1;
break;
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_ALPHA_2BIT:
px_size = 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_ALPHA_4BIT:
px_size = 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_8BIT:
px_size = 8;
break;
default:
px_size = 0;
break;
}
return px_size;
}
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
{
bool is_chroma_keyed = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
case LV_IMG_CF_RAW_CHROMA_KEYED:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
is_chroma_keyed = true;
break;
default:
is_chroma_keyed = false;
break;
}
return is_chroma_keyed;
}
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf)
{
bool has_alpha = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_RAW_ALPHA:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
has_alpha = true;
break;
default:
has_alpha = false;
break;
}
return has_alpha;
}
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src)
{
lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
if(src == NULL) return img_src_type;
const uint8_t * u8_p = src;
/*The first byte shows the type of the image source*/
if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
}
else if(u8_p[0] >= 0x80) {
img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
}
else {
img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
}
if(LV_IMG_SRC_UNKNOWN == img_src_type) {
LV_LOG_WARN("lv_img_src_get_type: unknown image type");
}
return img_src_type;
}
/**********************
* STATIC FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area,
const void * src,
const lv_draw_img_dsc_t * draw_dsc)
{
if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
if(cdsc == NULL) return LV_RES_INV;
bool chroma_keyed = lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf);
bool alpha_byte = lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf);
if(cdsc->dec_dsc.error_msg != NULL) {
LV_LOG_WARN("Image draw error");
show_error(coords, clip_area, cdsc->dec_dsc.error_msg);
}
/*The decoder could open the image and gave the entire uncompressed image.
*Just draw it!*/
else if(cdsc->dec_dsc.img_data) {
lv_area_t map_area_rot;
lv_area_copy(&map_area_rot, coords);
if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
_lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
map_area_rot.x1 += coords->x1;
map_area_rot.y1 += coords->y1;
map_area_rot.x2 += coords->x1;
map_area_rot.y2 += coords->y1;
}
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&mask_com, clip_area, &map_area_rot);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
lv_draw_map(coords, &mask_com, cdsc->dec_dsc.img_data, draw_dsc, chroma_keyed, alpha_byte);
}
/*The whole uncompressed image is not available. Try to read it line-by-line*/
else {
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&mask_com, clip_area, coords);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
int32_t width = lv_area_get_width(&mask_com);
uint8_t * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/
lv_area_t line;
lv_area_copy(&line, &mask_com);
lv_area_set_height(&line, 1);
int32_t x = mask_com.x1 - coords->x1;
int32_t y = mask_com.y1 - coords->y1;
int32_t row;
lv_res_t read_res;
for(row = mask_com.y1; row <= mask_com.y2; row++) {
lv_area_t mask_line;
union_ok = _lv_area_intersect(&mask_line, clip_area, &line);
if(union_ok == false) continue;
read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
if(read_res != LV_RES_OK) {
lv_img_decoder_close(&cdsc->dec_dsc);
LV_LOG_WARN("Image draw can't read the line");
lv_mem_buf_release(buf);
draw_cleanup(cdsc);
return LV_RES_INV;
}
lv_draw_map(&line, &mask_line, buf, draw_dsc, chroma_keyed, alpha_byte);
line.y1++;
line.y2++;
y++;
}
lv_mem_buf_release(buf);
}
draw_cleanup(cdsc);
return LV_RES_OK;
}
/**
* Draw a color map to the display (image)
* @param cords_p coordinates the color map
* @param mask_p the map will drawn only on this area (truncated to draw_buf area)
* @param map_p pointer to a lv_color_t array
* @param draw_dsc pointer to an initialized `lv_draw_img_dsc_t` variable
* @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
* @param alpha_byte true: extra alpha byte is inserted for every pixel
*/
LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const lv_area_t * clip_area,
const uint8_t * map_p,
const lv_draw_img_dsc_t * draw_dsc,
bool chroma_key, bool alpha_byte)
{
/*Use the clip area as draw area*/
lv_area_t draw_area;
lv_area_copy(&draw_area, clip_area);
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Now `draw_area` has absolute coordinates.
*Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
uint8_t other_mask_cnt = lv_draw_mask_get_cnt();
/*The simplest case just copy the pixels into the draw_buf*/
if(other_mask_cnt == 0 && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE &&
chroma_key == false && alpha_byte == false && draw_dsc->recolor_opa == LV_OPA_TRANSP) {
_lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa,
draw_dsc->blend_mode);
}
#if LV_USE_GPU_NXP_PXP
/*Simple case without masking and transformations*/
else if(other_mask_cnt == 0 && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && alpha_byte == false &&
chroma_key == true && draw_dsc->recolor_opa == LV_OPA_TRANSP) { /*copy with color keying (+ alpha)*/
lv_gpu_nxp_pxp_enable_color_key();
_lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa,
draw_dsc->blend_mode);
lv_gpu_nxp_pxp_disable_color_key();
}
else if(other_mask_cnt == 0 && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && alpha_byte == false &&
chroma_key == false && draw_dsc->recolor_opa != LV_OPA_TRANSP) { /*copy with recolor (+ alpha)*/
lv_gpu_nxp_pxp_enable_recolor(draw_dsc->recolor, draw_dsc->recolor_opa);
_lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa,
draw_dsc->blend_mode);
lv_gpu_nxp_pxp_disable_recolor();
}
#endif
/*In the other cases every pixel need to be checked one-by-one*/
else {
//#if LV_DRAW_COMPLEX
/*The pixel size in byte is different if an alpha byte is added too*/
uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
/*Go to the first displayed pixel of the map*/
int32_t map_w = lv_area_get_width(map_area);
const uint8_t * map_buf_tmp = map_p;
map_buf_tmp += map_w * (draw_area.y1 - (map_area->y1 - disp_area->y1)) * px_size_byte;
map_buf_tmp += (draw_area.x1 - (map_area->x1 - disp_area->x1)) * px_size_byte;
lv_color_t c;
lv_color_t chroma_keyed_color = LV_COLOR_CHROMA_KEY;
uint32_t px_i = 0;
const uint8_t * map_px;
lv_area_t blend_area;
blend_area.x1 = draw_area.x1 + disp_area->x1;
blend_area.x2 = blend_area.x1 + lv_area_get_width(&draw_area) - 1;
blend_area.y1 = disp_area->y1 + draw_area.y1;
blend_area.y2 = blend_area.y1;
lv_coord_t draw_area_h = lv_area_get_height(&draw_area);
lv_coord_t draw_area_w = lv_area_get_width(&draw_area);
bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false;
/*Simple ARGB image. Handle it as special case because it's very common*/
if(other_mask_cnt == 0 && !transform && !chroma_key && draw_dsc->recolor_opa == LV_OPA_TRANSP && alpha_byte) {
#if LV_USE_GPU_STM32_DMA2D && LV_COLOR_DEPTH == 32
/*Blend ARGB images directly*/
if(lv_area_get_size(&draw_area) > 240) {
int32_t disp_w = lv_area_get_width(disp_area);
lv_color_t * disp_buf = draw_buf->buf_act;
lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area.y1 + draw_area.x1;
lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, (const lv_color_t *)map_buf_tmp, draw_dsc->opa, map_w, draw_area_w,
draw_area_h);
return;
}
#endif
uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp);
uint32_t mask_buf_size = lv_area_get_size(&draw_area) > (uint32_t) hor_res ? hor_res : lv_area_get_size(&draw_area);
lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
int32_t x;
int32_t y;
for(y = 0; y < draw_area_h; y++) {
map_px = map_buf_tmp;
for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) {
lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
mask_buf[px_i] = px_opa;
if(px_opa) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
map2[px_i].full = map_px[0];
#elif LV_COLOR_DEPTH == 16
map2[px_i].full = map_px[0] + (map_px[1] << 8);
#elif LV_COLOR_DEPTH == 32
map2[px_i].full = *((uint32_t *)map_px);
#endif
}
#if LV_COLOR_DEPTH == 32
map2[px_i].ch.alpha = 0xFF;
#endif
}
map_buf_tmp += map_w * px_size_byte;
if(px_i + lv_area_get_width(&draw_area) < mask_buf_size) {
blend_area.y2 ++;
}
else {
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, draw_dsc->blend_mode);
blend_area.y1 = blend_area.y2 + 1;
blend_area.y2 = blend_area.y1;
px_i = 0;
}
}
/*Flush the last part*/
if(blend_area.y1 != blend_area.y2) {
blend_area.y2--;
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, draw_dsc->blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(map2);
}
/*Most complicated case: transform or other mask or chroma keyed*/
else {
/*Build the image and a mask line-by-line*/
uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp);
uint32_t mask_buf_size = lv_area_get_size(&draw_area) > hor_res ? hor_res : lv_area_get_size(&draw_area);
lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
#if LV_DRAW_COMPLEX
lv_img_transform_dsc_t trans_dsc;
lv_memset_00(&trans_dsc, sizeof(lv_img_transform_dsc_t));
if(transform) {
lv_img_cf_t cf = LV_IMG_CF_TRUE_COLOR;
if(alpha_byte) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
else if(chroma_key) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
trans_dsc.cfg.angle = draw_dsc->angle;
trans_dsc.cfg.zoom = draw_dsc->zoom;
trans_dsc.cfg.src = map_p;
trans_dsc.cfg.src_w = map_w;
trans_dsc.cfg.src_h = lv_area_get_height(map_area);;
trans_dsc.cfg.cf = cf;
trans_dsc.cfg.pivot_x = draw_dsc->pivot.x;
trans_dsc.cfg.pivot_y = draw_dsc->pivot.y;
trans_dsc.cfg.color = draw_dsc->recolor;
trans_dsc.cfg.antialias = draw_dsc->antialias;
_lv_img_buf_transform_init(&trans_dsc);
}
#endif
uint16_t recolor_premult[3] = {0};
lv_opa_t recolor_opa_inv = 255 - draw_dsc->recolor_opa;
if(draw_dsc->recolor_opa != 0) {
lv_color_premult(draw_dsc->recolor, draw_dsc->recolor_opa, recolor_premult);
}
lv_draw_mask_res_t mask_res;
mask_res = (alpha_byte || chroma_key || draw_dsc->angle ||
draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
/*Prepare the `mask_buf`if there are other masks*/
if(other_mask_cnt) {
lv_memset_ff(mask_buf, mask_buf_size);
}
int32_t x;
int32_t y;
#if LV_DRAW_COMPLEX
int32_t rot_y = disp_area->y1 + draw_area.y1 - map_area->y1;
#endif
for(y = 0; y < draw_area_h; y++) {
map_px = map_buf_tmp;
#if LV_DRAW_COMPLEX
uint32_t px_i_start = px_i;
int32_t rot_x = disp_area->x1 + draw_area.x1 - map_area->x1;
#endif
for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) {
#if LV_DRAW_COMPLEX
if(transform) {
/*Transform*/
bool ret;
ret = _lv_img_buf_transform(&trans_dsc, rot_x + x, rot_y + y);
if(ret == false) {
mask_buf[px_i] = LV_OPA_TRANSP;
continue;
}
else {
mask_buf[px_i] = trans_dsc.res.opa;
c.full = trans_dsc.res.color.full;
}
}
/*No transform*/
else
#endif
{
if(alpha_byte) {
lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
mask_buf[px_i] = px_opa;
if(px_opa == 0) {
#if LV_COLOR_DEPTH == 32
map2[px_i].full = 0;
#endif
continue;
}
}
else {
mask_buf[px_i] = 0xFF;
}
#if LV_COLOR_DEPTH == 1
c.full = map_px[0];
#elif LV_COLOR_DEPTH == 8
c.full = map_px[0];
#elif LV_COLOR_DEPTH == 16
c.full = map_px[0] + (map_px[1] << 8);
#elif LV_COLOR_DEPTH == 32
c.full = *((uint32_t *)map_px);
c.ch.alpha = 0xFF;
#endif
if(chroma_key) {
if(c.full == chroma_keyed_color.full) {
mask_buf[px_i] = LV_OPA_TRANSP;
#if LV_COLOR_DEPTH == 32
map2[px_i].full = 0;
#endif
continue;
}
}
}
if(draw_dsc->recolor_opa != 0) {
c = lv_color_mix_premult(recolor_premult, c, recolor_opa_inv);
}
map2[px_i].full = c.full;
}
#if LV_DRAW_COMPLEX
/*Apply the masks if any*/
if(other_mask_cnt) {
lv_draw_mask_res_t mask_res_sub;
mask_res_sub = lv_draw_mask_apply(mask_buf + px_i_start, draw_area.x1 + draw_buf->area.x1, y + draw_area.y1 + draw_buf->area.y1,
lv_area_get_width(&draw_area));
if(mask_res_sub == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + px_i_start, lv_area_get_width(&draw_area));
mask_res = LV_DRAW_MASK_RES_CHANGED;
}
else if(mask_res_sub == LV_DRAW_MASK_RES_CHANGED) {
mask_res = LV_DRAW_MASK_RES_CHANGED;
}
}
#endif
map_buf_tmp += map_w * px_size_byte;
if(px_i + lv_area_get_width(&draw_area) < mask_buf_size) {
blend_area.y2 ++;
}
else {
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode);
blend_area.y1 = blend_area.y2 + 1;
blend_area.y2 = blend_area.y1;
px_i = 0;
mask_res = (alpha_byte || chroma_key || draw_dsc->angle ||
draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
/*Prepare the `mask_buf`if there are other masks*/
if(other_mask_cnt) {
lv_memset_ff(mask_buf, mask_buf_size);
}
}
}
/*Flush the last part*/
if(blend_area.y1 != blend_area.y2) {
blend_area.y2--;
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(map2);
}
}
}
static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg)
{
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_white();
lv_draw_rect(coords, clip_area, &rect_dsc);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_draw_label(coords, clip_area, &label_dsc, msg, NULL);
}
static void draw_cleanup(_lv_img_cache_entry_t * cache)
{
/*Automatically close images with no caching*/
#if LV_IMG_CACHE_DEF_SIZE == 0
lv_img_decoder_close(&cache->dec_dsc);
#else
LV_UNUSED(cache);
#endif
}

View File

@ -0,0 +1,97 @@
/**
* @file lv_draw_img.h
*
*/
#ifndef LV_DRAW_IMG_H
#define LV_DRAW_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "lv_img_buf.h"
#include "lv_draw_blend.h"
/*********************
* DEFINES
*********************/
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint16_t angle;
uint16_t zoom;
lv_point_t pivot;
lv_color_t recolor;
lv_opa_t recolor_opa;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 4;
int32_t frame_id;
uint8_t antialias : 1;
} lv_draw_img_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc);
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * src, const lv_draw_img_dsc_t * dsc);
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src);
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf);
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf);
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_IMG_H*/

View File

@ -0,0 +1,860 @@
/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_label.h"
#include "../misc/lv_math.h"
#include "../hal/lv_hal_disp.h"
#include "../core/lv_refr.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define LABEL_RECOLOR_PAR_LENGTH 6
#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
/**********************
* TYPEDEFS
**********************/
enum {
CMD_STATE_WAIT,
CMD_STATE_PAR,
CMD_STATE_IN,
};
typedef uint8_t cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g,
const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
#endif
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
const uint8_t _lv_bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const uint8_t _lv_bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const uint8_t _lv_bpp3_opa_table[8] = {0, 36, 73, 109, /*Opacity mapping with bpp = 3*/
146, 182, 219, 255
};
const uint8_t _lv_bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119,
136, 153, 170, 187,
204, 221, 238, 255
};
const uint8_t _lv_bpp8_opa_table[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->font = LV_FONT_DEFAULT;
dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_color = lv_color_black();
dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE);
dsc->bidi_dir = LV_BASE_DIR_LTR;
}
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask,
const lv_draw_label_dsc_t * dsc,
const char * txt,
lv_draw_label_hint_t * hint)
{
if(dsc->opa <= LV_OPA_MIN) return;
const lv_font_t * font = dsc->font;
int32_t w;
/*No need to waste processor time if string is empty*/
if (txt == NULL || txt[0] == '\0')
return;
lv_area_t clipped_area;
bool clip_ok = _lv_area_intersect(&clipped_area, coords, mask);
if(!clip_ok) return;
if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
}
else {
/*If EXAPND is enabled then not limit the text's width to the object's width*/
lv_point_t p;
lv_txt_get_size(&p, txt, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
dsc->flag);
w = p.x;
}
int32_t line_height_font = lv_font_get_line_height(font);
int32_t line_height = line_height_font + dsc->line_space;
/*Init variables for the first line*/
int32_t line_width = 0;
lv_point_t pos;
pos.x = coords->x1;
pos.y = coords->y1;
int32_t x_ofs = 0;
int32_t y_ofs = 0;
x_ofs = dsc->ofs_x;
y_ofs = dsc->ofs_y;
pos.y += y_ofs;
uint32_t line_start = 0;
int32_t last_line_start = -1;
/*Check the hint to use the cached info*/
if(hint && y_ofs == 0 && coords->y1 < 0) {
/*If the label changed too much recalculate the hint.*/
if(LV_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
hint->line_start = -1;
}
last_line_start = hint->line_start;
}
/*Use the hint if it's valid*/
if(hint && last_line_start >= 0) {
line_start = last_line_start;
pos.y += hint->y;
}
uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
/*Go the first visible line*/
while(pos.y + line_height_font < mask->y1) {
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
pos.y += line_height;
/*Save at the threshold coordinate*/
if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
hint->line_start = line_start;
hint->y = pos.y - coords->y1;
hint->coord_y = coords->y1;
}
if(txt[line_start] == '\0') return;
}
/*Align to middle*/
if(dsc->align == LV_TEXT_ALIGN_CENTER) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(dsc->align == LV_TEXT_ALIGN_RIGHT) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
lv_opa_t opa = dsc->opa;
uint32_t sel_start = dsc->sel_start;
uint32_t sel_end = dsc->sel_end;
if(sel_start > sel_end) {
uint32_t tmp = sel_start;
sel_start = sel_end;
sel_end = tmp;
}
lv_draw_line_dsc_t line_dsc;
if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) {
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = dsc->color;
line_dsc.width = font->underline_thickness ? font->underline_thickness : 1;
line_dsc.opa = dsc->opa;
line_dsc.blend_mode = dsc->blend_mode;
}
cmd_state_t cmd_state = CMD_STATE_WAIT;
uint32_t i;
uint32_t par_start = 0;
lv_color_t recolor;
int32_t letter_w;
lv_draw_rect_dsc_t draw_dsc_sel;
lv_draw_rect_dsc_init(&draw_dsc_sel);
draw_dsc_sel.bg_color = dsc->sel_bg_color;
int32_t pos_x_start = pos.x;
/*Write out all lines*/
while(txt[line_start] != '\0') {
pos.x += x_ofs;
/*Write all letter of a line*/
cmd_state = CMD_STATE_WAIT;
i = 0;
#if LV_USE_BIDI
char * bidi_txt = lv_mem_buf_get(line_end - line_start + 1);
_lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, dsc->bidi_dir, NULL, 0);
#else
const char * bidi_txt = txt + line_start;
#endif
while(i < line_end - line_start) {
uint32_t logical_char_pos = 0;
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
#if LV_USE_BIDI
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start);
uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i);
logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, dsc->bidi_dir, t, NULL);
#else
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i);
#endif
}
uint32_t letter;
uint32_t letter_next;
_lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i);
/*Handle the re-color command*/
if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
par_start = i;
cmd_state = CMD_STATE_PAR;
continue;
}
else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/
cmd_state = CMD_STATE_WAIT;
}
else if(cmd_state == CMD_STATE_IN) { /*Command end*/
cmd_state = CMD_STATE_WAIT;
continue;
}
}
/*Skip the color parameter and wait the space after it*/
if(cmd_state == CMD_STATE_PAR) {
if(letter == ' ') {
/*Get the parameter*/
if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
int r, g, b;
r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
recolor = lv_color_make(r, g, b);
}
else {
recolor.full = dsc->color.full;
}
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
}
continue;
}
}
lv_color_t color = dsc->color;
if(cmd_state == CMD_STATE_IN) color = recolor;
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
if(logical_char_pos >= sel_start && logical_char_pos < sel_end) {
lv_area_t sel_coords;
sel_coords.x1 = pos.x;
sel_coords.y1 = pos.y;
sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1;
sel_coords.y2 = pos.y + line_height - 1;
lv_draw_rect(&sel_coords, mask, &draw_dsc_sel);
color = dsc->sel_color;
}
}
lv_draw_letter(&pos, mask, font, letter, color, opa, dsc->blend_mode);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
}
}
if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + (dsc->font->line_height / 2) + line_dsc.width / 2;
p2.x = pos.x;
p2.y = p1.y;
lv_draw_line(&p1, &p2, mask, &line_dsc);
}
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position;
p2.x = pos.x;
p2.y = p1.y;
lv_draw_line(&p1, &p2, mask, &line_dsc);
}
#if LV_USE_BIDI
lv_mem_buf_release(bidi_txt);
bidi_txt = NULL;
#endif
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
pos.x = coords->x1;
/*Align to middle*/
if(dsc->align == LV_TEXT_ALIGN_CENTER) {
line_width =
lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(dsc->align == LV_TEXT_ALIGN_RIGHT) {
line_width =
lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > mask->y2) return;
}
LV_ASSERT_MEM_INTEGRITY();
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Draw a letter in the Virtual Display Buffer
* @param pos_p left-top coordinate of the latter
* @param mask_p the letter will be drawn only on this area (truncated to draw_buf area)
* @param font_p pointer to font
* @param letter a letter to draw
* @param color color of letter
* @param opa opacity of letter (0..255)
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
const lv_font_t * font_p,
uint32_t letter,
lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
{
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
if(font_p == NULL) {
LV_LOG_WARN("lv_draw_letter: font is NULL");
return;
}
lv_font_glyph_dsc_t g;
bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found
*but do not print warning for non printable ASCII chars (e.g. '\n')*/
if(letter >= 0x20 &&
letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter);
}
return;
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) return;
int32_t pos_x = pos_p->x + g.ofs_x;
int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
/*If the letter is completely out of mask don't draw it*/
if(pos_x + g.box_w < clip_area->x1 ||
pos_x > clip_area->x2 ||
pos_y + g.box_h < clip_area->y1 ||
pos_y > clip_area->y2) {
return;
}
const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
if(map_p == NULL) {
LV_LOG_WARN("lv_draw_letter: character's bitmap not found");
return;
}
if(font_p->subpx) {
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
draw_letter_subpx(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
#else
LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h");
#endif
} else {
draw_letter_normal(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
}
}
LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g,
const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
{
const uint8_t * bpp_opa_table_p;
uint32_t bitmask_init;
uint32_t bitmask;
uint32_t bpp = g->bpp;
uint32_t shades;
if(bpp == 3) bpp = 4;
switch(bpp) {
case 1:
bpp_opa_table_p = _lv_bpp1_opa_table;
bitmask_init = 0x80;
shades = 2;
break;
case 2:
bpp_opa_table_p = _lv_bpp2_opa_table;
bitmask_init = 0xC0;
shades = 4;
break;
case 4:
bpp_opa_table_p = _lv_bpp4_opa_table;
bitmask_init = 0xF0;
shades = 16;
break;
case 8:
bpp_opa_table_p = _lv_bpp8_opa_table;
bitmask_init = 0xFF;
shades = 256;
break; /*No opa table, pixel value will be used directly*/
default:
LV_LOG_WARN("lv_draw_letter: invalid bpp");
return; /*Invalid bpp. Can't render the letter*/
}
static lv_opa_t opa_table[256];
static lv_opa_t prev_opa = LV_OPA_TRANSP;
static uint32_t prev_bpp = 0;
if(opa < LV_OPA_MAX) {
if(prev_opa != opa || prev_bpp != bpp) {
uint32_t i;
for(i = 0; i < shades; i++) {
opa_table[i] = bpp_opa_table_p[i] == LV_OPA_COVER ? opa : ((bpp_opa_table_p[i] * opa) >> 8);
}
}
bpp_opa_table_p = opa_table;
prev_opa = opa;
prev_bpp = bpp;
}
int32_t col, row;
int32_t box_w = g->box_w;
int32_t box_h = g->box_h;
int32_t width_bit = box_w * bpp; /*Letter width in bits*/
/*Calculate the col/row start/end on the map*/
int32_t col_start = pos_x >= clip_area->x1 ? 0 : clip_area->x1 - pos_x;
int32_t col_end = pos_x + box_w <= clip_area->x2 ? box_w : clip_area->x2 - pos_x + 1;
int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y;
int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1;
/*Move on the map too*/
uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
map_p += bit_ofs >> 3;
uint8_t letter_px;
uint32_t col_bit;
col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/
lv_coord_t hor_res = lv_disp_get_hor_res(_lv_refr_get_disp_refreshing());
uint32_t mask_buf_size = box_w * box_h > hor_res ? hor_res : box_w * box_h;
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
int32_t mask_p = 0;
lv_area_t fill_area;
fill_area.x1 = col_start + pos_x;
fill_area.x2 = col_end + pos_x - 1;
fill_area.y1 = row_start + pos_y;
fill_area.y2 = fill_area.y1;
#if LV_DRAW_COMPLEX
uint8_t other_mask_cnt = lv_draw_mask_get_cnt();
#endif
uint32_t col_bit_max = 8 - bpp;
uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp;
for(row = row_start ; row < row_end; row++) {
#if LV_DRAW_COMPLEX
int32_t mask_p_start = mask_p;
#endif
bitmask = bitmask_init >> col_bit;
for(col = col_start; col < col_end; col++) {
/*Load the pixel's opacity into the mask*/
letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit);
if(letter_px) {
mask_buf[mask_p] = bpp_opa_table_p[letter_px];
}
else {
mask_buf[mask_p] = 0;
}
/*Go to the next column*/
if(col_bit < col_bit_max) {
col_bit += bpp;
bitmask = bitmask >> bpp;
}
else {
col_bit = 0;
bitmask = bitmask_init;
map_p++;
}
/*Next mask byte*/
mask_p++;
}
#if LV_DRAW_COMPLEX
/*Apply masks if any*/
if(other_mask_cnt) {
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2,
lv_area_get_width(&fill_area));
if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&fill_area));
}
}
#endif
if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) {
fill_area.y2 ++;
}
else {
_lv_blend_fill(clip_area, &fill_area,
color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER,
blend_mode);
fill_area.y1 = fill_area.y2 + 1;
fill_area.y2 = fill_area.y1;
mask_p = 0;
}
col_bit += col_bit_row_ofs;
map_p += (col_bit >> 3);
col_bit = col_bit & 0x7;
}
/*Flush the last part*/
if(fill_area.y1 != fill_area.y2) {
fill_area.y2--;
_lv_blend_fill(clip_area, &fill_area,
color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER,
blend_mode);
mask_p = 0;
}
lv_mem_buf_release(mask_buf);
}
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
{
const uint8_t * bpp_opa_table;
uint32_t bitmask_init;
uint32_t bitmask;
uint32_t bpp = g->bpp;
if(bpp == 3) bpp = 4;
switch(bpp) {
case 1:
bpp_opa_table = _lv_bpp1_opa_table;
bitmask_init = 0x80;
break;
case 2:
bpp_opa_table = _lv_bpp2_opa_table;
bitmask_init = 0xC0;
break;
case 4:
bpp_opa_table = _lv_bpp4_opa_table;
bitmask_init = 0xF0;
break;
case 8:
bpp_opa_table = _lv_bpp8_opa_table;
bitmask_init = 0xFF;
break; /*No opa table, pixel value will be used directly*/
default:
LV_LOG_WARN("lv_draw_letter: invalid bpp not found");
return; /*Invalid bpp. Can't render the letter*/
}
int32_t col, row;
int32_t box_w = g->box_w;
int32_t box_h = g->box_h;
int32_t width_bit = box_w * bpp; /*Letter width in bits*/
/*Calculate the col/row start/end on the map*/
int32_t col_start = pos_x >= clip_area->x1 ? 0 : (clip_area->x1 - pos_x) * 3;
int32_t col_end = pos_x + box_w / 3 <= clip_area->x2 ? box_w : (clip_area->x2 - pos_x + 1) * 3;
int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y;
int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1;
/*Move on the map too*/
int32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
map_p += bit_ofs >> 3;
uint8_t letter_px;
lv_opa_t px_opa;
int32_t col_bit;
col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/
int32_t mask_buf_size = box_w * box_h > _LV_MASK_BUF_MAX_SIZE ? _LV_MASK_BUF_MAX_SIZE : g->box_w * g->box_h;
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
int32_t mask_p = 0;
lv_color_t * color_buf = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
int32_t disp_buf_width = lv_area_get_width(&draw_buf->area);
lv_color_t * disp_buf_buf_tmp = draw_buf->buf_act;
/*Set a pointer on draw_buf to the first pixel of the letter*/
disp_buf_buf_tmp += ((pos_y - draw_buf->area.y1) * disp_buf_width) + pos_x - draw_buf->area.x1;
/*If the letter is partially out of mask the move there on draw_buf*/
disp_buf_buf_tmp += (row_start * disp_buf_width) + col_start / 3;
lv_area_t map_area;
map_area.x1 = col_start / 3 + pos_x;
map_area.x2 = col_end / 3 + pos_x - 1;
map_area.y1 = row_start + pos_y;
map_area.y2 = map_area.y1;
uint8_t other_mask_cnt = lv_draw_mask_get_cnt();
uint8_t font_rgb[3];
#if LV_COLOR_16_SWAP == 0
uint8_t txt_rgb[3] = {color.ch.red, color.ch.green, color.ch.blue};
#else
uint8_t txt_rgb[3] = {color.ch.red, (color.ch.green_h << 3) + color.ch.green_l, color.ch.blue};
#endif
for(row = row_start ; row < row_end; row++) {
uint32_t subpx_cnt = 0;
bitmask = bitmask_init >> col_bit;
int32_t mask_p_start = mask_p;
for(col = col_start; col < col_end; col++) {
/*Load the pixel's opacity into the mask*/
letter_px = (*map_p & bitmask) >> (8 - col_bit - bpp);
if(letter_px != 0) {
if(opa == LV_OPA_COVER) {
px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px];
}
else {
px_opa = bpp == 8 ? (uint32_t)((uint32_t)letter_px * opa) >> 8
: (uint32_t)((uint32_t)bpp_opa_table[letter_px] * opa) >> 8;
}
}
else {
px_opa = 0;
}
font_rgb[subpx_cnt] = px_opa;
subpx_cnt ++;
if(subpx_cnt == 3) {
subpx_cnt = 0;
lv_color_t res_color;
#if LV_COLOR_16_SWAP == 0
uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red, disp_buf_buf_tmp->ch.green, disp_buf_buf_tmp->ch.blue};
#else
uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red,
(disp_buf_buf_tmp->ch.green_h << 3) + disp_buf_buf_tmp->ch.green_l,
disp_buf_buf_tmp->ch.blue
};
#endif
#if LV_FONT_SUBPX_BGR
res_color.ch.blue = (uint32_t)((uint32_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8;
res_color.ch.red = (uint32_t)((uint32_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8;
#else
res_color.ch.red = (uint32_t)((uint16_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8;
res_color.ch.blue = (uint32_t)((uint16_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8;
#endif
#if LV_COLOR_16_SWAP == 0
res_color.ch.green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8;
#else
uint8_t green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8;
res_color.ch.green_h = green >> 3;
res_color.ch.green_l = green & 0x7;
#endif
#if LV_COLOR_DEPTH == 32
res_color.ch.alpha = 0xff;
#endif
if(font_rgb[0] == 0 && font_rgb[1] == 0 && font_rgb[2] == 0) mask_buf[mask_p] = LV_OPA_TRANSP;
else mask_buf[mask_p] = LV_OPA_COVER;
color_buf[mask_p] = res_color;
/*Next mask byte*/
mask_p++;
disp_buf_buf_tmp++;
}
/*Go to the next column*/
if(col_bit < (int32_t)(8 - bpp)) {
col_bit += bpp;
bitmask = bitmask >> bpp;
}
else {
col_bit = 0;
bitmask = bitmask_init;
map_p++;
}
}
/*Apply masks if any*/
if(other_mask_cnt) {
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, map_area.x1, map_area.y2,
lv_area_get_width(&map_area));
if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&map_area));
}
}
if((int32_t) mask_p + (col_end - col_start) < mask_buf_size) {
map_area.y2 ++;
}
else {
_lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode);
map_area.y1 = map_area.y2 + 1;
map_area.y2 = map_area.y1;
mask_p = 0;
}
col_bit += ((box_w - col_end) + col_start) * bpp;
map_p += (col_bit >> 3);
col_bit = col_bit & 0x7;
/*Next row in draw_buf*/
disp_buf_buf_tmp += disp_buf_width - (col_end - col_start) / 3;
}
/*Flush the last part*/
if(map_area.y1 != map_area.y2) {
map_area.y2--;
_lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(color_buf);
}
#endif
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
uint8_t result = 0;
if(hex >= '0' && hex <= '9') {
result = hex - '0';
}
else {
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
switch(hex) {
case 'A':
result = 10;
break;
case 'B':
result = 11;
break;
case 'C':
result = 12;
break;
case 'D':
result = 13;
break;
case 'E':
result = 14;
break;
case 'F':
result = 15;
break;
default:
result = 0;
break;
}
}
return result;
}

View File

@ -0,0 +1,108 @@
/**
* @file lv_draw_label.h
*
*/
#ifndef LV_DRAW_LABEL_H
#define LV_DRAW_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_txt.h"
#include "../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF)
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_font_t * font;
uint32_t sel_start;
uint32_t sel_end;
lv_color_t color;
lv_color_t sel_color;
lv_color_t sel_bg_color;
lv_coord_t line_space;
lv_coord_t letter_space;
lv_coord_t ofs_x;
lv_coord_t ofs_y;
lv_opa_t opa;
lv_base_dir_t bidi_dir;
lv_text_flag_t flag;
lv_text_align_t align :2;
lv_text_decor_t decor : 3;
lv_blend_mode_t blend_mode: 3;
} lv_draw_label_dsc_t;
/** Store some info to speed up drawing of very large texts
* It takes a lot of time to get the first visible character because
* all the previous characters needs to be checked to calculate the positions.
* This structure stores an earlier (e.g. at -1000 px) coordinate and the index of that line.
* Therefore the calculations can start from here.*/
typedef struct _lv_draw_label_hint_t {
/** Index of the line at `y` coordinate*/
int32_t line_start;
/** Give the `y` coordinate of the first letter at `line start` index. Relative to the label's coordinates*/
int32_t y;
/** The 'y1' coordinate of the label when the hint was saved.
* Used to invalidate the hint if the label has moved too much.*/
int32_t coord_y;
} lv_draw_label_hint_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
//! @cond Doxygen_Suppress
LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc);
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask,
const lv_draw_label_dsc_t * dsc,
const char * txt, lv_draw_label_hint_t * hint);
LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
const lv_font_t * font_p,
uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
//! @endcond
/***********************
* GLOBAL VARIABLES
***********************/
extern const uint8_t _lv_bpp2_opa_table[];
extern const uint8_t _lv_bpp3_opa_table[];
extern const uint8_t _lv_bpp1_opa_table[];
extern const uint8_t _lv_bpp4_opa_table[];
extern const uint8_t _lv_bpp8_opa_table[];
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LABEL_H*/

View File

@ -0,0 +1,489 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "lv_draw_mask.h"
#include "lv_draw_blend.h"
#include "../core/lv_refr.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_line_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param clip the line will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
if(dsc->width == 0) return;
if(dsc->opa <= LV_OPA_MIN) return;
if(point1->x == point2->x && point1->y == point2->y) return;
lv_area_t clip_line;
clip_line.x1 = LV_MIN(point1->x, point2->x) - dsc->width / 2;
clip_line.x2 = LV_MAX(point1->x, point2->x) + dsc->width / 2;
clip_line.y1 = LV_MIN(point1->y, point2->y) - dsc->width / 2;
clip_line.y2 = LV_MAX(point1->y, point2->y) + dsc->width / 2;
bool is_common;
is_common = _lv_area_intersect(&clip_line, &clip_line, clip);
if(!is_common) return;
if(point1->y == point2->y) draw_line_hor(point1, point2, &clip_line, dsc);
else if(point1->x == point2->x) draw_line_ver(point1, point2, &clip_line, dsc);
else draw_line_skew(point1, point2, &clip_line, dsc);
if(dsc->round_end || dsc->round_start) {
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
cir_dsc.bg_color = dsc->color;
cir_dsc.radius = LV_RADIUS_CIRCLE;
cir_dsc.bg_opa = dsc->opa;
int32_t r = (dsc->width >> 1);
int32_t r_corr = (dsc->width & 1) ? 0 : 1;
lv_area_t cir_area;
if(dsc->round_start) {
cir_area.x1 = point1->x - r;
cir_area.y1 = point1->y - r;
cir_area.x2 = point1->x + r - r_corr;
cir_area.y2 = point1->y + r - r_corr ;
lv_draw_rect(&cir_area, clip, &cir_dsc);
}
if(dsc->round_end) {
cir_area.x1 = point2->x - r;
cir_area.y1 = point2->y - r;
cir_area.x2 = point2->x + r - r_corr;
cir_area.y2 = point2->y + r - r_corr ;
lv_draw_rect(&cir_area, clip, &cir_dsc);
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
lv_opa_t opa = dsc->opa;
int32_t w = dsc->width - 1;
int32_t w_half0 = w >> 1;
int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
bool dashed = dsc->dash_gap && dsc->dash_width ? true : false;
bool simple_mode = true;
if(lv_draw_mask_get_cnt()) simple_mode = false;
else if(dashed) simple_mode = false;
lv_area_t draw_area;
draw_area.x1 = LV_MIN(point1->x, point2->x);
draw_area.x2 = LV_MAX(point1->x, point2->x) - 1;
draw_area.y1 = point1->y - w_half1;
draw_area.y2 = point1->y + w_half0;
/*If there is no mask then simply draw a rectangle*/
if(simple_mode) {
_lv_blend_fill(clip, &draw_area,
dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa,
dsc->blend_mode);
}
#if LV_DRAW_COMPLEX
/*If there other mask apply it*/
else {
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Get clipped fill area which is the real draw area.
*It is always the same or inside `fill_area`*/
bool is_common;
is_common = _lv_area_intersect(&draw_area, clip, &draw_area);
if(!is_common) return;
/*Now `draw_area` has absolute coordinates.
*Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
int32_t draw_area_w = lv_area_get_width(&draw_area);
lv_area_t fill_area;
fill_area.x1 = draw_area.x1 + disp_area->x1;
fill_area.x2 = draw_area.x2 + disp_area->x1;
fill_area.y1 = draw_area.y1 + disp_area->y1;
fill_area.y2 = fill_area.y1;
lv_coord_t dash_start = 0;
if(dashed) {
dash_start = (draw_buf->area.x1 + draw_area.x1) % (dsc->dash_gap + dsc->dash_width);
}
lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w);
int32_t h;
for(h = draw_area.y1; h <= draw_area.y2; h++) {
lv_memset_ff(mask_buf, draw_area_w);
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, draw_area_w);
if(dashed) {
if(mask_res != LV_DRAW_MASK_RES_TRANSP) {
lv_coord_t dash_cnt = dash_start;
lv_coord_t i;
for(i = 0; i < draw_area_w; i++, dash_cnt++) {
if(dash_cnt <= dsc->dash_width) {
int16_t diff = dsc->dash_width - dash_cnt;
i += diff;
dash_cnt += diff;
}
else if(dash_cnt >= dsc->dash_gap + dsc->dash_width) {
dash_cnt = 0;
}
else {
mask_buf[i] = 0x00;
}
}
mask_res = LV_DRAW_MASK_RES_CHANGED;
}
}
_lv_blend_fill(clip, &fill_area,
dsc->color, mask_buf, mask_res, dsc->opa,
dsc->blend_mode);
fill_area.y1++;
fill_area.y2++;
}
lv_mem_buf_release(mask_buf);
}
#endif /*LV_DRAW_COMPLEX*/
}
LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
lv_opa_t opa = dsc->opa;
int32_t w = dsc->width - 1;
int32_t w_half0 = w >> 1;
int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
bool dashed = dsc->dash_gap && dsc->dash_width ? true : false;
bool simple_mode = true;
if(lv_draw_mask_get_cnt()) simple_mode = false;
else if(dashed) simple_mode = false;
lv_area_t draw_area;
draw_area.x1 = point1->x - w_half1;
draw_area.x2 = point1->x + w_half0;
draw_area.y1 = LV_MIN(point1->y, point2->y);
draw_area.y2 = LV_MAX(point1->y, point2->y) - 1;
/*If there is no mask then simply draw a rectangle*/
if(simple_mode) {
_lv_blend_fill(clip, &draw_area,
dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa,
dsc->blend_mode);
}
#if LV_DRAW_COMPLEX
/*If there other mask apply it*/
else {
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Get clipped fill area which is the real draw area.
*It is always the same or inside `fill_area`*/
bool is_common;
is_common = _lv_area_intersect(&draw_area, clip, &draw_area);
if(!is_common) return;
/*Now `draw_area` has absolute coordinates.
*Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= draw_buf->area.x1;
draw_area.y1 -= draw_buf->area.y1;
draw_area.x2 -= draw_buf->area.x1;
draw_area.y2 -= draw_buf->area.y1;
int32_t draw_area_w = lv_area_get_width(&draw_area);
lv_area_t fill_area;
fill_area.x1 = draw_area.x1 + disp_area->x1;
fill_area.x2 = draw_area.x2 + disp_area->x1;
fill_area.y1 = draw_area.y1 + disp_area->y1;
fill_area.y2 = fill_area.y1;
lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w);
lv_coord_t dash_start = 0;
if(dashed) {
dash_start = (draw_buf->area.y1 + draw_area.y1) % (dsc->dash_gap + dsc->dash_width);
}
lv_coord_t dash_cnt = dash_start;
int32_t h;
for(h = draw_area.y1; h <= draw_area.y2; h++) {
lv_memset_ff(mask_buf, draw_area_w);
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, draw_area_w);
if(dashed) {
if(mask_res != LV_DRAW_MASK_RES_TRANSP) {
if(dash_cnt > dsc->dash_width) {
mask_res = LV_DRAW_MASK_RES_TRANSP;
}
if(dash_cnt >= dsc->dash_gap + dsc->dash_width) {
dash_cnt = 0;
}
}
dash_cnt ++;
}
_lv_blend_fill(clip, &fill_area,
dsc->color, mask_buf, mask_res, dsc->opa,
LV_BLEND_MODE_NORMAL);
fill_area.y1++;
fill_area.y2++;
}
lv_mem_buf_release(mask_buf);
}
#endif /*LV_DRAW_COMPLEX*/
}
LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
#if LV_DRAW_COMPLEX
/*Keep the great y in p1*/
lv_point_t p1;
lv_point_t p2;
if(point1->y < point2->y) {
p1.y = point1->y;
p2.y = point2->y;
p1.x = point1->x;
p2.x = point2->x;
}
else {
p1.y = point2->y;
p2.y = point1->y;
p1.x = point2->x;
p2.x = point1->x;
}
int32_t xdiff = p2.x - p1.x;
int32_t ydiff = p2.y - p1.y;
bool flat = LV_ABS(xdiff) > LV_ABS(ydiff) ? true : false;
static const uint8_t wcorr[] = {
128, 128, 128, 129, 129, 130, 130, 131,
132, 133, 134, 135, 137, 138, 140, 141,
143, 145, 147, 149, 151, 153, 155, 158,
160, 162, 165, 167, 170, 173, 175, 178,
181,
};
int32_t w = dsc->width;
int32_t wcorr_i = 0;
if(flat) wcorr_i = (LV_ABS(ydiff) << 5) / LV_ABS(xdiff);
else wcorr_i = (LV_ABS(xdiff) << 5) / LV_ABS(ydiff);
w = (w * wcorr[wcorr_i] + 63) >> 7; /*+ 63 for rounding*/
int32_t w_half0 = w >> 1;
int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
lv_area_t draw_area;
draw_area.x1 = LV_MIN(p1.x, p2.x) - w;
draw_area.x2 = LV_MAX(p1.x, p2.x) + w;
draw_area.y1 = LV_MIN(p1.y, p2.y) - w;
draw_area.y2 = LV_MAX(p1.y, p2.y) + w;
/*Get the union of `coords` and `clip`*/
/*`clip` is already truncated to the `draw_buf` size
*in 'lv_refr_area' function*/
bool is_common = _lv_area_intersect(&draw_area, &draw_area, clip);
if(is_common == false) return;
lv_draw_mask_line_param_t mask_left_param;
lv_draw_mask_line_param_t mask_right_param;
lv_draw_mask_line_param_t mask_top_param;
lv_draw_mask_line_param_t mask_bottom_param;
if(flat) {
if(xdiff > 0) {
lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
}
else {
lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
}
}
else {
lv_draw_mask_line_points_init(&mask_left_param, p1.x + w_half1, p1.y, p2.x + w_half1, p2.y,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_line_points_init(&mask_right_param, p1.x - w_half0, p1.y, p2.x - w_half0, p2.y,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
}
/*Use the normal vector for the endings*/
int16_t mask_left_id = lv_draw_mask_add(&mask_left_param, NULL);
int16_t mask_right_id = lv_draw_mask_add(&mask_right_param, NULL);
int16_t mask_top_id = LV_MASK_ID_INV;
int16_t mask_bottom_id = LV_MASK_ID_INV;
if(!dsc->raw_end) {
lv_draw_mask_line_points_init(&mask_top_param, p1.x, p1.y, p1.x - ydiff, p1.y + xdiff, LV_DRAW_MASK_LINE_SIDE_BOTTOM);
lv_draw_mask_line_points_init(&mask_bottom_param, p2.x, p2.y, p2.x - ydiff, p2.y + xdiff, LV_DRAW_MASK_LINE_SIDE_TOP);
mask_top_id = lv_draw_mask_add(&mask_top_param, NULL);
mask_bottom_id = lv_draw_mask_add(&mask_bottom_param, NULL);
}
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Store the coordinates of the `draw_a` relative to the draw_buf*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
/*The real draw area is around the line.
*It's easy to calculate with steep lines, but the area can be very wide with very flat lines.
*So deal with it only with steep lines.*/
int32_t draw_area_w = lv_area_get_width(&draw_area);
/*Draw the background line by line*/
int32_t h;
uint32_t hor_res = (uint32_t)lv_disp_get_hor_res(disp);
size_t mask_buf_size = LV_MIN(lv_area_get_size(&draw_area), hor_res);
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
lv_area_t fill_area;
fill_area.x1 = draw_area.x1 + disp_area->x1;
fill_area.x2 = draw_area.x2 + disp_area->x1;
fill_area.y1 = draw_area.y1 + disp_area->y1;
fill_area.y2 = fill_area.y1;
int32_t x = draw_buf->area.x1 + draw_area.x1;
uint32_t mask_p = 0;
lv_memset_ff(mask_buf, mask_buf_size);
/*Fill the first row with 'color'*/
for(h = draw_area.y1 + disp_area->y1; h <= draw_area.y2 + disp_area->y1; h++) {
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(&mask_buf[mask_p], x, h, draw_area_w);
if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&mask_buf[mask_p], draw_area_w);
}
mask_p += draw_area_w;
if((uint32_t) mask_p + draw_area_w < mask_buf_size) {
fill_area.y2 ++;
}
else {
_lv_blend_fill(&fill_area, clip,
dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa,
dsc->blend_mode);
fill_area.y1 = fill_area.y2 + 1;
fill_area.y2 = fill_area.y1;
mask_p = 0;
lv_memset_ff(mask_buf, mask_buf_size);
}
}
/*Flush the last part*/
if(fill_area.y1 != fill_area.y2) {
fill_area.y2--;
_lv_blend_fill(&fill_area, clip,
dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa,
dsc->blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_draw_mask_remove_id(mask_left_id);
lv_draw_mask_remove_id(mask_right_id);
lv_draw_mask_remove_id(mask_top_id);
lv_draw_mask_remove_id(mask_bottom_id);
#else
LV_UNUSED(point1);
LV_UNUSED(point2);
LV_UNUSED(clip);
LV_UNUSED(dsc);
LV_LOG_WARN("Can't draw skewed line with LV_DRAW_COMPLEX == 0");
#endif /*LV_DRAW_COMPLEX*/
}

View File

@ -0,0 +1,64 @@
/**
* @file lv_draw_line.h
*
*/
#ifndef LV_DRAW_LINE_H
#define LV_DRAW_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t color;
lv_coord_t width;
lv_coord_t dash_width;
lv_coord_t dash_gap;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t round_start : 1;
uint8_t round_end : 1;
uint8_t raw_end : 1; /*Do not bother with perpendicular line ending is it's not visible for any reason*/
} lv_draw_line_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
//! @cond Doxygen_Suppress
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param clip the line will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
//! @endcond
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LINE_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,326 @@
/**
* @file lv_draw_mask.h
*
*/
#ifndef LV_DRAW_MASK_H
#define LV_DRAW_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_MASK_ID_INV (-1)
#if LV_DRAW_COMPLEX
# define _LV_MASK_MAX_NUM 16
# ifndef _LV_MASK_BUF_MAX_SIZE
# define _LV_MASK_BUF_MAX_SIZE 2048 /*Should be >= than the max hor res*/
# endif
#else
# define _LV_MASK_MAX_NUM 1
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_DRAW_MASK_RES_TRANSP,
LV_DRAW_MASK_RES_FULL_COVER,
LV_DRAW_MASK_RES_CHANGED,
LV_DRAW_MASK_RES_UNKNOWN
};
typedef uint8_t lv_draw_mask_res_t;
typedef struct {
void * param;
void * custom_id;
} _lv_draw_mask_saved_t;
typedef _lv_draw_mask_saved_t _lv_draw_mask_saved_arr_t[_LV_MASK_MAX_NUM];
#if LV_DRAW_COMPLEX == 0
static inline uint8_t lv_draw_mask_get_cnt(void) {
return 0;
}
#endif
#if LV_DRAW_COMPLEX
enum {
LV_DRAW_MASK_TYPE_LINE,
LV_DRAW_MASK_TYPE_ANGLE,
LV_DRAW_MASK_TYPE_RADIUS,
LV_DRAW_MASK_TYPE_FADE,
LV_DRAW_MASK_TYPE_MAP,
};
typedef uint8_t lv_draw_mask_type_t;
enum {
LV_DRAW_MASK_LINE_SIDE_LEFT = 0,
LV_DRAW_MASK_LINE_SIDE_RIGHT,
LV_DRAW_MASK_LINE_SIDE_TOP,
LV_DRAW_MASK_LINE_SIDE_BOTTOM,
};
/**
* A common callback type for every mask type.
* Used internally by the library.
*/
typedef lv_draw_mask_res_t (*lv_draw_mask_xcb_t)(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len,
void * p);
typedef uint8_t lv_draw_mask_line_side_t;
typedef struct {
lv_draw_mask_xcb_t cb;
lv_draw_mask_type_t type;
} _lv_draw_mask_common_dsc_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
/*First point*/
lv_point_t p1;
/*Second point*/
lv_point_t p2;
/*Which side to keep?*/
lv_draw_mask_line_side_t side : 2;
} cfg;
/*A point of the line*/
lv_point_t origo;
/*X / (1024*Y) steepness (X is 0..1023 range). What is the change of X in 1024 Y?*/
int32_t xy_steep;
/*Y / (1024*X) steepness (Y is 0..1023 range). What is the change of Y in 1024 X?*/
int32_t yx_steep;
/*Helper which stores yx_steep for flat lines and xy_steep for steep (non flat) lines*/
int32_t steep;
/*Steepness in 1 px in 0..255 range. Used only by flat lines.*/
int32_t spx;
/*1: It's a flat line? (Near to horizontal)*/
uint8_t flat : 1;
/*Invert the mask. The default is: Keep the left part.
*It is used to select left/right/top/bottom*/
uint8_t inv: 1;
} lv_draw_mask_line_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_point_t vertex_p;
lv_coord_t start_angle;
lv_coord_t end_angle;
} cfg;
lv_draw_mask_line_param_t start_line;
lv_draw_mask_line_param_t end_line;
uint16_t delta_deg;
} lv_draw_mask_angle_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t rect;
lv_coord_t radius;
/*Invert the mask. 0: Keep the pixels inside.*/
uint8_t outer: 1;
} cfg;
int32_t y_prev;
lv_sqrt_res_t y_prev_x;
} lv_draw_mask_radius_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
lv_coord_t y_top;
lv_coord_t y_bottom;
lv_opa_t opa_top;
lv_opa_t opa_bottom;
} cfg;
} lv_draw_mask_fade_param_t;
typedef struct _lv_draw_mask_map_param_t {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
const lv_opa_t * map;
} cfg;
} lv_draw_mask_map_param_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
* @param param an initialized mask parameter. Only the pointer is saved.
* @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
* @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
*/
int16_t lv_draw_mask_add(void * param, void * custom_id);
//! @cond Doxygen_Suppress
/**
* Apply the added buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len);
//! @endcond
/**
* Remove a mask with a given ID
* @param id the ID of the mask. Returned by `lv_draw_mask_add`
* @return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_id(int16_t id);
/**
* Remove all mask with a given custom ID
* @param custom_id a pointer used in `lv_draw_mask_add`
* @return return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_custom(void * custom_id);
//! @cond Doxygen_Suppress
/**
* Count the currently added masks
* @return number of active masks
*/
LV_ATTRIBUTE_FAST_MEM uint8_t lv_draw_mask_get_cnt(void);
//! @endcond
/**
*Initialize a line mask from two points.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param p1x X coordinate of the first point of the line
* @param p1y Y coordinate of the first point of the line
* @param p2x X coordinate of the second point of the line
* @param p2y y coordinate of the second point of the line
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
lv_coord_t p2y, lv_draw_mask_line_side_t side);
/**
*Initialize a line mask from a point and an angle.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param px X coordinate of a point of the line
* @param py X coordinate of a point of the line
* @param angle right 0 deg, bottom: 90
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
lv_draw_mask_line_side_t side);
/**
* Initialize an angle mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param vertex_x X coordinate of the angle vertex (absolute coordinates)
* @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
* @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
* @param end_angle end angle
*/
void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
lv_coord_t start_angle, lv_coord_t end_angle);
/**
* Initialize a fade mask.
* @param param param pointer to a `lv_draw_mask_param_t` to initialize
* @param rect coordinates of the rectangle to affect (absolute coordinates)
* @param radius radius of the rectangle
* @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
*/
void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv);
/**
* Initialize a fade mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the area to affect (absolute coordinates)
* @param opa_top opacity on the top
* @param y_top at which coordinate start to change to opacity to `opa_bottom`
* @param opa_bottom opacity at the bottom
* @param y_bottom at which coordinate reach `opa_bottom`.
*/
void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
lv_coord_t y_top,
lv_opa_t opa_bottom, lv_coord_t y_bottom);
/**
* Initialize a map mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the map (absolute coordinates)
* @param map array of bytes with the mask values
*/
void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_MASK_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
/**
* @file lv_draw_rect.h
*
*/
#ifndef LV_DRAW_RECT_H
#define LV_DRAW_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
#include "../font/lv_font.h"
/*********************
* DEFINES
*********************/
#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/
LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE);
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_coord_t radius;
lv_blend_mode_t blend_mode;
/*Background*/
lv_color_t bg_color;
lv_color_t bg_grad_color;
uint8_t bg_main_color_stop;
uint8_t bg_grad_color_stop;
lv_opa_t bg_opa;
lv_grad_dir_t bg_grad_dir :3;
/*Background img*/
const void * bg_img_src;
const void * bg_img_symbol_font;
lv_color_t bg_img_recolor;
lv_opa_t bg_img_opa;
lv_opa_t bg_img_recolor_opa;
uint8_t bg_img_tiled;
/*Border*/
lv_color_t border_color;
lv_coord_t border_width;
lv_opa_t border_opa;
uint8_t border_post : 1; /*There is a border it will be drawn later.*/
lv_border_side_t border_side :5;
/*Outline*/
lv_color_t outline_color;
lv_coord_t outline_width;
lv_coord_t outline_pad;
lv_opa_t outline_opa;
/*Shadow*/
lv_color_t shadow_color;
lv_coord_t shadow_width;
lv_coord_t shadow_ofs_x;
lv_coord_t shadow_ofs_y;
lv_coord_t shadow_spread;
lv_opa_t shadow_opa;
} lv_draw_rect_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
//! @endcond
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param mask the rectangle will be drawn only in this mask
* @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_rect(const lv_area_t * coords, const lv_area_t * mask, const lv_draw_rect_dsc_t * dsc);
/**
* Draw a pixel
* @param point the coordinates of the point to draw
* @param mask the pixel will be drawn only in this mask
* @param style pointer to a style
*/
//void lv_draw_px(const lv_point_t * point, const lv_area_t * clip_area, const lv_style_t * style);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_RECT_H*/

View File

@ -0,0 +1,216 @@
/**
* @file lv_draw_triangle.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_triangle.h"
#include "../misc/lv_math.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Draw a triangle
* @param points pointer to an array with 3 points
* @param clip_area the triangle will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area, const lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
lv_draw_polygon(points, 3, clip_area, draw_dsc);
#else
LV_UNUSED(points);
LV_UNUSED(clip_area);
LV_UNUSED(draw_dsc);
LV_LOG_WARN("Can't draw triangle with LV_DRAW_COMPLEX == 0");
#endif /*LV_DRAW_COMPLEX*/
}
/**
* Draw a polygon. Only convex polygons are supported
* @param points an array of points
* @param point_cnt number of points
* @param clip_area polygon will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area,
const lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
if(point_cnt < 3) return;
if(points == NULL) return;
/*Join adjacent points if they are on the same coordinate*/
lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t));
if(p == NULL) return;
uint16_t i;
uint16_t pcnt = 0;
p[0] = points[0];
for(i = 0; i < point_cnt - 1; i++) {
if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) {
p[pcnt] = points[i];
pcnt++;
}
}
/*The first and the last points are also adjacent*/
if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) {
p[pcnt] = points[point_cnt - 1];
pcnt++;
}
point_cnt = pcnt;
if(point_cnt < 3) {
lv_mem_buf_release(p);
return;
}
lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
for(i = 0; i < point_cnt; i++) {
poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x);
poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y);
poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x);
poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y);
}
bool is_common;
lv_area_t poly_mask;
is_common = _lv_area_intersect(&poly_mask, &poly_coords, clip_area);
if(!is_common) {
lv_mem_buf_release(p);
return;
}
/*Find the lowest point*/
lv_coord_t y_min = p[0].y;
int16_t y_min_i = 0;
for(i = 1; i < point_cnt; i++) {
if(p[i].y < y_min) {
y_min = p[i].y;
y_min_i = i;
}
}
lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt);
lv_draw_mask_line_param_t * mp_next = mp;
int32_t i_prev_left = y_min_i;
int32_t i_prev_right = y_min_i;
int32_t i_next_left;
int32_t i_next_right;
uint32_t mask_cnt = 0;
/*Get the index of the left and right points*/
i_next_left = y_min_i - 1;
if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
i_next_right = y_min_i + 1;
if(i_next_right > point_cnt - 1) i_next_right = 0;
/**
* Check if the order of points is inverted or not.
* The normal case is when the left point is on `y_min_i - 1`
* Explanation:
* if angle(p_left) < angle(p_right) -> inverted
* dy_left/dx_left < dy_right/dx_right
* dy_left * dx_right < dy_right * dx_left
*/
lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x;
lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x;
lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y;
lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y;
bool inv = false;
if(dyl * dxr < dyr * dxl) inv = true;
do {
if(!inv) {
i_next_left = i_prev_left - 1;
if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
i_next_right = i_prev_right + 1;
if(i_next_right > point_cnt - 1) i_next_right = 0;
}
else {
i_next_left = i_prev_left + 1;
if(i_next_left > point_cnt - 1) i_next_left = 0;
i_next_right = i_prev_right - 1;
if(i_next_right < 0) i_next_right = point_cnt + i_next_right;
}
if(p[i_next_left].y >= p[i_prev_left].y) {
if(p[i_next_left].y != p[i_prev_left].y &&
p[i_next_left].x != p[i_prev_left].x) {
lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y,
p[i_next_left].x, p[i_next_left].y,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
lv_draw_mask_add(mp_next, mp);
mp_next++;
}
mask_cnt++;
i_prev_left = i_next_left;
}
if(mask_cnt == point_cnt) break;
if(p[i_next_right].y >= p[i_prev_right].y) {
if(p[i_next_right].y != p[i_prev_right].y &&
p[i_next_right].x != p[i_prev_right].x) {
lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y,
p[i_next_right].x, p[i_next_right].y,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_add(mp_next, mp);
mp_next++;
}
mask_cnt++;
i_prev_right = i_next_right;
}
} while(mask_cnt < point_cnt);
lv_draw_rect(&poly_coords, clip_area, draw_dsc);
lv_draw_mask_remove_custom(mp);
lv_mem_buf_release(mp);
lv_mem_buf_release(p);
#else
LV_UNUSED(points);
LV_UNUSED(point_cnt);
LV_UNUSED(clip_area);
LV_UNUSED(draw_dsc);
LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0");
#endif /*LV_DRAW_COMPLEX*/
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,56 @@
/**
* @file lv_draw_triangle.h
*
*/
#ifndef LV_DRAW_TRIANGLE_H
#define LV_DRAW_TRIANGLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw a triangle
* @param points pointer to an array with 3 points
* @param clip_area the triangle will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip, const lv_draw_rect_dsc_t * draw_dsc);
/**
* Draw a polygon. Only convex polygons are supported.
* @param points an array of points
* @param point_cnt number of points
* @param clip_area polygon will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * mask,
const lv_draw_rect_dsc_t * draw_dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRIANGLE_H*/

View File

@ -0,0 +1,772 @@
/**
* @file lv_img_buf.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <string.h>
#include "lv_img_buf.h"
#include "lv_draw_img.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color)
{
lv_color_t p_color = lv_color_black();
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
#if LV_COLOR_SIZE == 32
p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
#endif
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += 4 * 2;
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8, 16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += 4 * 4;
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += 4 * 16;
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2, 4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += 4 * 256;
uint32_t px = dsc->header.w * y + x;
p_color.full = buf_u8[px];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
p_color = color;
}
return p_color;
}
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
const uint8_t opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
return buf_u8[px];
}
return LV_OPA_COVER;
}
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
buf_u8[px + px_size - 1] = opa;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
opa = opa >> 7; /*opa -> [0,1]*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
opa = opa >> 6; /*opa -> [0,3]*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
opa = opa >> 4; /*opa -> [0,15]*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = opa;
}
}
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size);
}
else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8 ,12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = c.full;
}
}
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
{
if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
return;
}
lv_color32_t c32;
c32.full = lv_color_to32(c);
uint8_t * buf = (uint8_t *)dsc->data;
lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
}
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
/*Allocate image descriptor*/
lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
if(dsc == NULL)
return NULL;
lv_memset_00(dsc, sizeof(lv_img_dsc_t));
/*Get image data size*/
dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
if(dsc->data_size == 0) {
lv_mem_free(dsc);
return NULL;
}
/*Allocate raw buffer*/
dsc->data = lv_mem_alloc(dsc->data_size);
if(dsc->data == NULL) {
lv_mem_free(dsc);
return NULL;
}
lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
/*Fill in header*/
dsc->header.always_zero = 0;
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
return dsc;
}
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc)
{
if(dsc != NULL) {
if(dsc->data != NULL)
lv_mem_free((void*)dsc->data);
lv_mem_free(dsc);
}
}
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
switch(cf) {
case LV_IMG_CF_TRUE_COLOR:
return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
case LV_IMG_CF_TRUE_COLOR_ALPHA:
return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
case LV_IMG_CF_ALPHA_1BIT:
return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
case LV_IMG_CF_ALPHA_2BIT:
return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
case LV_IMG_CF_ALPHA_4BIT:
return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
case LV_IMG_CF_ALPHA_8BIT:
return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
case LV_IMG_CF_INDEXED_1BIT:
return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
case LV_IMG_CF_INDEXED_2BIT:
return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
case LV_IMG_CF_INDEXED_4BIT:
return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
case LV_IMG_CF_INDEXED_8BIT:
return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
default:
return 0;
}
}
#if LV_DRAW_COMPLEX
/**
* Initialize a descriptor to transform an image
* @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized
*/
void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc)
{
dsc->tmp.pivot_x_256 = dsc->cfg.pivot_x * 256;
dsc->tmp.pivot_y_256 = dsc->cfg.pivot_y * 256;
int32_t angle_low = dsc->cfg.angle / 10;
int32_t angle_high = angle_low + 1;
int32_t angle_rem = dsc->cfg.angle - (angle_low * 10);
int32_t s1 = lv_trigo_sin(-angle_low);
int32_t s2 = lv_trigo_sin(-angle_high);
int32_t c1 = lv_trigo_sin(-angle_low + 90);
int32_t c2 = lv_trigo_sin(-angle_high + 90);
dsc->tmp.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
dsc->tmp.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
/*Use smaller value to avoid overflow*/
dsc->tmp.sinma = dsc->tmp.sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
dsc->tmp.cosma = dsc->tmp.cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
dsc->tmp.chroma_keyed = lv_img_cf_is_chroma_keyed(dsc->cfg.cf) ? 1 : 0;
dsc->tmp.has_alpha = lv_img_cf_has_alpha(dsc->cfg.cf) ? 1 : 0;
if(dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR || dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
dsc->tmp.native_color = 1;
}
else {
dsc->tmp.native_color = 0;
}
dsc->tmp.img_dsc.data = dsc->cfg.src;
dsc->tmp.img_dsc.header.always_zero = 0;
dsc->tmp.img_dsc.header.cf = dsc->cfg.cf;
dsc->tmp.img_dsc.header.w = dsc->cfg.src_w;
dsc->tmp.img_dsc.header.h = dsc->cfg.src_h;
/*The inverse of the zoom will be sued during the transformation
* + dsc->cfg.zoom / 2 for rounding*/
dsc->tmp.zoom_inv = (((256 * 256) << _LV_ZOOM_INV_UPSCALE) + dsc->cfg.zoom / 2) / dsc->cfg.zoom;
dsc->res.opa = LV_OPA_COVER;
dsc->res.color = dsc->cfg.color;
}
#endif
/**
* Get the area of a rectangle if its rotated and scaled
* @param res store the coordinates here
* @param w width of the rectangle to transform
* @param h height of the rectangle to transform
* @param angle angle of rotation
* @param zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot)
{
#if LV_DRAW_COMPLEX
if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
return;
}
res->x1 = (((-pivot->x) * zoom) >> 8) - 1;
res->y1 = (((-pivot->y) * zoom) >> 8) - 1;
res->x2 = (((w - pivot->x) * zoom) >> 8) + 2;
res->y2 = (((h - pivot->y) * zoom) >> 8) + 2;
if(angle == 0) {
res->x1 += pivot->x;
res->y1 += pivot->y;
res->x2 += pivot->x;
res->y2 += pivot->y;
return;
}
int32_t angle_low = angle / 10;
int32_t angle_high = angle_low + 1;
int32_t angle_rem = angle - (angle_low * 10);
int32_t s1 = lv_trigo_sin(angle_low);
int32_t s2 = lv_trigo_sin(angle_high);
int32_t c1 = lv_trigo_sin(angle_low + 90);
int32_t c2 = lv_trigo_sin(angle_high + 90);
int32_t sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
int32_t cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
/*Use smaller value to avoid overflow*/
sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
lv_point_t lt;
lv_point_t rt;
lv_point_t lb;
lv_point_t rb;
lv_coord_t xt;
lv_coord_t yt;
xt = res->x1;
yt = res->y1;
lt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
lt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x2;
yt = res->y1;
rt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
rt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x1;
yt = res->y2;
lb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
lb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x2;
yt = res->y2;
rb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
rb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
res->x1 = LV_MIN4(lb.x, lt.x, rb.x, rt.x);
res->x2 = LV_MAX4(lb.x, lt.x, rb.x, rt.x);
res->y1 = LV_MIN4(lb.y, lt.y, rb.y, rt.y);
res->y2 = LV_MAX4(lb.y, lt.y, rb.y, rt.y);
#else
LV_UNUSED(angle);
LV_UNUSED(zoom);
LV_UNUSED(pivot);
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
#endif
}
#if LV_DRAW_COMPLEX
/**
* Get which color and opa would come to a pixel if it were rotated
* @param dsc a descriptor initialized by `lv_img_buf_rotate_init`
* @param x the coordinate which color and opa should be get
* @param y the coordinate which color and opa should be get
* @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image
* @note the result is written back to `dsc->res_color` and `dsc->res_opa`
*/
bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
const uint8_t * src_u8 = (const uint8_t *)dsc->cfg.src;
/*Get the target point relative coordinates to the pivot*/
int32_t xt = x - dsc->cfg.pivot_x;
int32_t yt = y - dsc->cfg.pivot_y;
int32_t xs;
int32_t ys;
if(dsc->cfg.zoom == LV_IMG_ZOOM_NONE) {
/*Get the source pixel from the upscaled image*/
xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_x_256;
ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_y_256;
}
else if(dsc->cfg.angle == 0) {
xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
xs = xt + dsc->tmp.pivot_x_256;
ys = yt + dsc->tmp.pivot_y_256;
}
else {
xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_x_256;
ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_y_256;
}
/*Get the integer part of the source pixel*/
int32_t xs_int = xs >> 8;
int32_t ys_int = ys >> 8;
if(xs_int >= dsc->cfg.src_w) return false;
else if(xs_int < 0) return false;
if(ys_int >= dsc->cfg.src_h) return false;
else if(ys_int < 0) return false;
uint8_t px_size;
uint32_t pxi;
if(dsc->tmp.native_color) {
if(dsc->tmp.has_alpha == 0) {
px_size = LV_COLOR_SIZE >> 3;
pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size;
lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size);
}
else {
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE;
pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size;
lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size - 1);
dsc->res.opa = src_u8[pxi + px_size - 1];
}
}
else {
pxi = 0; /*unused*/
px_size = 0; /*unused*/
dsc->res.color = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, xs_int, ys_int, dsc->cfg.color);
dsc->res.opa = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, xs_int, ys_int);
}
if(dsc->tmp.chroma_keyed) {
lv_color_t ct = LV_COLOR_CHROMA_KEY;
if(dsc->res.color.full == ct.full) return false;
}
if(dsc->cfg.antialias == false) return true;
dsc->tmp.xs = xs;
dsc->tmp.ys = ys;
dsc->tmp.xs_int = xs_int;
dsc->tmp.ys_int = ys_int;
dsc->tmp.pxi = pxi;
dsc->tmp.px_size = px_size;
bool ret;
ret = _lv_img_buf_transform_anti_alias(dsc);
return ret;
}
/**
* Continue transformation by taking the neighbors into account
* @param dsc pointer to the transformation descriptor
*/
bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc)
{
const uint8_t * src_u8 = dsc->cfg.src;
/*Get the fractional part of the source pixel*/
int xs_fract = dsc->tmp.xs & 0xff;
int ys_fract = dsc->tmp.ys & 0xff;
int32_t xn; /*x neighbor*/
lv_opa_t xr; /*x mix ratio*/
if(xs_fract < 0x70) {
xn = - 1;
if(dsc->tmp.xs_int + xn < 0) xn = 0;
xr = xs_fract + 0x80;
}
else if(xs_fract > 0x90) {
xn = 1;
if(dsc->tmp.xs_int + xn >= dsc->cfg.src_w) xn = 0;
xr = (0xFF - xs_fract) + 0x80;
}
else {
xn = 0;
xr = 0xFF;
}
int32_t yn; /*x neighbor*/
lv_opa_t yr; /*x mix ratio*/
if(ys_fract < 0x70) {
yn = - 1;
if(dsc->tmp.ys_int + yn < 0) yn = 0;
yr = ys_fract + 0x80;
}
else if(ys_fract > 0x90) {
yn = 1;
if(dsc->tmp.ys_int + yn >= dsc->cfg.src_h) yn = 0;
yr = (0xFF - ys_fract) + 0x80;
}
else {
yn = 0;
yr = 0xFF;
}
lv_color_t c00 = dsc->res.color;
lv_color_t c01;
lv_color_t c10;
lv_color_t c11;
lv_opa_t a00 = dsc->res.opa;
lv_opa_t a10 = 0;
lv_opa_t a01 = 0;
lv_opa_t a11 = 0;
if(dsc->tmp.native_color) {
lv_memcpy_small(&c01, &src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn], sizeof(lv_color_t));
lv_memcpy_small(&c10, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn], sizeof(lv_color_t));
lv_memcpy_small(&c11, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn],
sizeof(lv_color_t));
if(dsc->tmp.has_alpha) {
a10 = src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1];
a01 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size - 1];
a11 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1];
}
}
else {
c01 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int, dsc->cfg.color);
c10 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn, dsc->cfg.color);
c11 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn, dsc->cfg.color);
if(dsc->tmp.has_alpha) {
a10 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int);
a01 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn);
a11 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn);
}
}
lv_opa_t xr0 = xr;
lv_opa_t xr1 = xr;
if(dsc->tmp.has_alpha) {
lv_opa_t a0 = (a00 * xr + (a10 * (255 - xr))) >> 8;
lv_opa_t a1 = (a01 * xr + (a11 * (255 - xr))) >> 8;
dsc->res.opa = (a0 * yr + (a1 * (255 - yr))) >> 8;
if(a0 <= LV_OPA_MIN && a1 <= LV_OPA_MIN) return false;
if(a0 <= LV_OPA_MIN) yr = LV_OPA_TRANSP;
if(a1 <= LV_OPA_MIN) yr = LV_OPA_COVER;
if(a00 <= LV_OPA_MIN) xr0 = LV_OPA_TRANSP;
if(a10 <= LV_OPA_MIN) xr0 = LV_OPA_COVER;
if(a01 <= LV_OPA_MIN) xr1 = LV_OPA_TRANSP;
if(a11 <= LV_OPA_MIN) xr1 = LV_OPA_COVER;
}
else {
xr0 = xr;
xr1 = xr;
dsc->res.opa = LV_OPA_COVER;
}
lv_color_t c0;
if(xr0 == LV_OPA_TRANSP) c0 = c01;
else if(xr0 == LV_OPA_COVER) c0 = c00;
else c0 = lv_color_mix(c00, c01, xr0);
lv_color_t c1;
if(xr1 == LV_OPA_TRANSP) c1 = c11;
else if(xr1 == LV_OPA_COVER) c1 = c10;
else c1 = lv_color_mix(c10, c11, xr1);
if(yr == LV_OPA_TRANSP) dsc->res.color = c1;
else if(yr == LV_OPA_COVER) dsc->res.color = c0;
else dsc->res.color = lv_color_mix(c0, c1, yr);
return true;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,310 @@
/**
* @file lv_img_buf.h
*
*/
#ifndef LV_IMG_BUF_H
#define LV_IMG_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/*If image pixels contains alpha we need to know how much byte is a pixel*/
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
#define LV_IMG_PX_SIZE_ALPHA_BYTE 2
#elif LV_COLOR_DEPTH == 16
#define LV_IMG_PX_SIZE_ALPHA_BYTE 3
#elif LV_COLOR_DEPTH == 32
#define LV_IMG_PX_SIZE_ALPHA_BYTE 4
#endif
#define LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h)
/*+ 1: to be sure no fractional row*/
#define LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h))
/*4 * X: for palette*/
#define LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2)
#define LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4)
#define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16)
#define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256)
#define _LV_TRANSFORM_TRIGO_SHIFT 10
#define _LV_ZOOM_INV_UPSCALE 5
/**********************
* TYPEDEFS
**********************/
/*Image color format*/
enum {
LV_IMG_CF_UNKNOWN = 0,
LV_IMG_CF_RAW, /**< Contains the file as it is. Needs custom decoder function*/
LV_IMG_CF_RAW_ALPHA, /**< Contains the file as it is. The image has alpha. Needs custom decoder
function*/
LV_IMG_CF_RAW_CHROMA_KEYED, /**< Contains the file as it is. The image is chroma keyed. Needs
custom decoder function*/
LV_IMG_CF_TRUE_COLOR, /**< Color format and depth should match with LV_COLOR settings*/
LV_IMG_CF_TRUE_COLOR_ALPHA, /**< Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /**< Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels
will be transparent*/
LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_ALPHA_1BIT, /**< Can have one color and it can be drawn or not*/
LV_IMG_CF_ALPHA_2BIT, /**< Can have one color but 4 different alpha value*/
LV_IMG_CF_ALPHA_4BIT, /**< Can have one color but 16 different alpha value*/
LV_IMG_CF_ALPHA_8BIT, /**< Can have one color but 256 different alpha value*/
LV_IMG_CF_RESERVED_15, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_16, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_17, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_18, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_19, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_20, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_21, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_22, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_23, /**< Reserved for further use.*/
LV_IMG_CF_USER_ENCODED_0, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_1, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_2, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_3, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_4, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_5, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_6, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_7, /**< User holder encoding format.*/
};
typedef uint8_t lv_img_cf_t;
/**
* The first 8 bit is very important to distinguish the different source types.
* For more info see `lv_img_get_src_type()` in lv_img.c
* On big endian systems the order is reversed so cf and always_zero must be at
* the end of the struct.
*/
#if LV_BIG_ENDIAN_SYSTEM
typedef struct {
uint32_t h : 11; /*Height of the image map*/
uint32_t w : 11; /*Width of the image map*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
} lv_img_header_t;
#else
typedef struct {
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t w : 11; /*Width of the image map*/
uint32_t h : 11; /*Height of the image map*/
} lv_img_header_t;
#endif
/** Image header it is compatible with
* the result from image converter utility*/
typedef struct {
lv_img_header_t header; /**< A header describing the basics of the image*/
uint32_t data_size; /**< Size of the image in bytes*/
const uint8_t * data; /**< Pointer to the data of the image*/
} lv_img_dsc_t;
typedef struct {
struct {
const void * src; /*image source (array of pixels)*/
lv_coord_t src_w; /*width of the image source*/
lv_coord_t src_h; /*height of the image source*/
lv_coord_t pivot_x; /*pivot x*/
lv_coord_t pivot_y; /*pivot y*/
int16_t angle; /*angle to rotate*/
uint16_t zoom; /*256 no zoom, 128 half size, 512 double size*/
lv_color_t color; /*a color used for `LV_IMG_CF_INDEXED_1/2/4/8BIT` color formats*/
lv_img_cf_t cf; /*color format of the image to rotate*/
bool antialias;
} cfg;
struct {
lv_color_t color;
lv_opa_t opa;
} res;
struct {
lv_img_dsc_t img_dsc;
int32_t pivot_x_256;
int32_t pivot_y_256;
int32_t sinma;
int32_t cosma;
uint8_t chroma_keyed : 1;
uint8_t has_alpha : 1;
uint8_t native_color : 1;
uint32_t zoom_inv;
/*Runtime data*/
lv_coord_t xs;
lv_coord_t ys;
lv_coord_t xs_int;
lv_coord_t ys_int;
uint32_t pxi;
uint8_t px_size;
} tmp;
} lv_img_transform_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color);
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c);
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa);
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c);
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc);
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
#if LV_DRAW_COMPLEX
/**
* Initialize a descriptor to rotate an image
* @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized
*/
void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc);
/**
* Continue transformation by taking the neighbors into account
* @param dsc pointer to the transformation descriptor
*/
bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc);
/**
* Get which color and opa would come to a pixel if it were rotated
* @param dsc a descriptor initialized by `lv_img_buf_rotate_init`
* @param x the coordinate which color and opa should be get
* @param y the coordinate which color and opa should be get
* @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image
* @note the result is written back to `dsc->res_color` and `dsc->res_opa`
*/
bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
#endif
/**
* Get the area of a rectangle if its rotated and scaled
* @param res store the coordinates here
* @param w width of the rectangle to transform
* @param h height of the rectangle to transform
* @param angle angle of rotation
* @param zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_BUF_H*/

View File

@ -0,0 +1,215 @@
/**
* @file lv_img_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_assert.h"
#include "lv_img_cache.h"
#include "lv_img_decoder.h"
#include "lv_draw_img.h"
#include "../hal/lv_hal_tick.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/*Decrement life with this value on every open*/
#define LV_IMG_CACHE_AGING 1
/*Boost life by this factor (multiply time_to_open with this value)*/
#define LV_IMG_CACHE_LIFE_GAIN 1
/*Don't let life to be greater than this limit because it would require a lot of time to
* "die" from very high values*/
#define LV_IMG_CACHE_LIFE_LIMIT 1000
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static uint16_t entry_cnt;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color color The color of the image with `LV_IMG_CF_ALPHA_...`
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
{
/*Is the image cached?*/
_lv_img_cache_entry_t * cached_src = NULL;
#if LV_IMG_CACHE_DEF_SIZE
if(entry_cnt == 0) {
LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
return NULL;
}
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
/*Decrement all lifes. Make the entries older*/
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
cache[i].life -= LV_IMG_CACHE_AGING;
}
}
for(i = 0; i < entry_cnt; i++) {
if(color.full == cache[i].dec_dsc.color.full &&
frame_id == cache[i].dec_dsc.frame_id &&
lv_img_cache_match(src, cache[i].dec_dsc.src)) {
/*If opened increment its life.
*Image difficult to open should live longer to keep avoid frequent their recaching.
*Therefore increase `life` with `time_to_open`*/
cached_src = &cache[i];
cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;
if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
LV_LOG_TRACE("image source found in the cache");
break;
}
}
/*The image is not cached then cache it now*/
if(cached_src) return cached_src;
/*Find an entry to reuse. Select the entry with the least life*/
cached_src = &cache[0];
for(i = 1; i < entry_cnt; i++) {
if(cache[i].life < cached_src->life) {
cached_src = &cache[i];
}
}
/*Close the decoder to reuse if it was opened (has a valid source)*/
if(cached_src->dec_dsc.src) {
lv_img_decoder_close(&cached_src->dec_dsc);
LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
}
else {
LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
}
#else
cached_src = &LV_GC_ROOT(_lv_img_cache_single);
#endif
/*Open the image and measure the time to open*/
uint32_t t_start = lv_tick_get();
lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color, frame_id);
if(open_res == LV_RES_INV) {
LV_LOG_WARN("Image draw cannot open the image resource");
lv_memset_00(cached_src, sizeof(_lv_img_cache_entry_t));
cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its us*/
return NULL;
}
cached_src->life = 0;
/*If `time_to_open` was not set in the open function set it here*/
if(cached_src->dec_dsc.time_to_open == 0) {
cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
}
if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
return cached_src;
}
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_entry_cnt)
{
#if LV_IMG_CACHE_DEF_SIZE == 0
LV_UNUSED(new_entry_cnt);
LV_LOG_WARN("Can't change cache size because it's disabled by LV_IMG_CACHE_DEF_SIZE = 0");
#else
if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
/*Clean the cache before free it*/
lv_img_cache_invalidate_src(NULL);
lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
}
/*Reallocate the cache*/
LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(_lv_img_cache_entry_t) * new_entry_cnt);
LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_img_cache_array));
if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
entry_cnt = 0;
return;
}
entry_cnt = new_entry_cnt;
/*Clean the cache*/
lv_memset_00(LV_GC_ROOT(_lv_img_cache_array), entry_cnt * sizeof(_lv_img_cache_entry_t));
#endif
}
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src)
{
LV_UNUSED(src);
#if LV_IMG_CACHE_DEF_SIZE
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(src == NULL || lv_img_cache_match(src, cache[i].dec_dsc.src)) {
if(cache[i].dec_dsc.src != NULL) {
lv_img_decoder_close(&cache[i].dec_dsc);
}
lv_memset_00(&cache[i], sizeof(_lv_img_cache_entry_t));
}
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2)
{
lv_img_src_t src_type = lv_img_src_get_type(src1);
if(src_type == LV_IMG_SRC_VARIABLE)
return src1 == src2;
if(src_type != LV_IMG_SRC_FILE)
return false;
if(lv_img_src_get_type(src2) != LV_IMG_SRC_FILE)
return false;
return strcmp(src1, src2) == 0;
}
#endif

View File

@ -0,0 +1,78 @@
/**
* @file lv_img_cache.h
*
*/
#ifndef LV_IMG_CACHE_H
#define LV_IMG_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* When loading images from the network it can take a long time to download and decode the image.
*
* To avoid repeating this heavy load images can be cached.
*/
typedef struct {
lv_img_decoder_dsc_t dec_dsc; /**< Image information*/
/** Count the cache entries's life. Add `time_to_open` to `life` when the entry is used.
* Decrement all lifes by one every in every ::lv_img_cache_open.
* If life == 0 the entry can be reused*/
int32_t life;
} _lv_img_cache_entry_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id);
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_slot_num);
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_CACHE_H*/

View File

@ -0,0 +1,689 @@
/**
* @file lv_img_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw_img.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR
#define CF_BUILT_IN_LAST LV_IMG_CF_ALPHA_8BIT
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t f;
lv_color_t * palette;
lv_opa_t * opa;
} lv_img_decoder_built_in_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_img_decoder_ll), sizeof(lv_img_decoder_t));
lv_img_decoder_t * decoder;
/*Create a decoder for the built in color format*/
decoder = lv_img_decoder_create();
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) {
LV_LOG_WARN("lv_img_decoder_init: out of memory");
return;
}
lv_img_decoder_set_info_cb(decoder, lv_img_decoder_built_in_info);
lv_img_decoder_set_open_cb(decoder, lv_img_decoder_built_in_open);
lv_img_decoder_set_read_line_cb(decoder, lv_img_decoder_built_in_read_line);
lv_img_decoder_set_close_cb(decoder, lv_img_decoder_built_in_close);
}
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. E.g. file name or variable.
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
{
lv_memset_00(header, sizeof(lv_img_header_t));
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) {
if(d->info_cb) {
res = d->info_cb(d, src, header);
if(res == LV_RES_OK) break;
}
}
return res;
}
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
{
lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));
dsc->color = color;
dsc->src_type = lv_img_src_get_type(src);
dsc->frame_id = frame_id;
if(dsc->src_type == LV_IMG_SRC_FILE) {
size_t fnlen = strlen(src);
dsc->src = lv_mem_alloc(fnlen + 1);
LV_ASSERT_MALLOC(dsc->src);
if(dsc->src == NULL) {
LV_LOG_WARN("lv_img_decoder_open: out of memory");
return LV_RES_INV;
}
strcpy((char *)dsc->src, src);
}
else {
dsc->src = src;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * decoder;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) {
/*Info and Open callbacks are required*/
if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;
res = decoder->info_cb(decoder, src, &dsc->header);
if(res != LV_RES_OK) continue;
dsc->decoder = decoder;
res = decoder->open_cb(decoder, dsc);
/*Opened successfully. It is a good decoder to for this image source*/
if(res == LV_RES_OK) return res;
/*Prepare for the next loop*/
lv_memset_00(&dsc->header, sizeof(lv_img_header_t));
dsc->error_msg = NULL;
dsc->img_data = NULL;
dsc->user_data = NULL;
dsc->time_to_open = 0;
}
if(dsc->src_type == LV_IMG_SRC_FILE)
lv_mem_free((void*)dsc->src);
return res;
}
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
lv_res_t res = LV_RES_INV;
if(dsc->decoder->read_line_cb) res = dsc->decoder->read_line_cb(dsc->decoder, dsc, x, y, len, buf);
return res;
}
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc)
{
if(dsc->decoder) {
if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_mem_free((void*)dsc->src);
dsc->src = NULL;
}
}
}
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void)
{
lv_img_decoder_t * decoder;
decoder = _lv_ll_ins_head(&LV_GC_ROOT(_lv_img_decoder_ll));
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) return NULL;
lv_memset_00(decoder, sizeof(lv_img_decoder_t));
return decoder;
}
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder)
{
_lv_ll_remove(&LV_GC_ROOT(_lv_img_decoder_ll), decoder);
lv_mem_free(decoder);
}
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb)
{
decoder->info_cb = info_cb;
}
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb)
{
decoder->open_cb = open_cb;
}
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb)
{
decoder->read_line_cb = read_line_cb;
}
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb)
{
decoder->close_cb = close_cb;
}
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
(void)decoder; /*Unused*/
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
lv_img_cf_t cf = ((lv_img_dsc_t *)src)->header.cf;
if(cf < CF_BUILT_IN_FIRST || cf > CF_BUILT_IN_LAST) return LV_RES_INV;
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
}
else if(src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
if(res == LV_FS_RES_OK) {
uint32_t rn;
res = lv_fs_read(&f, header, sizeof(lv_img_header_t), &rn);
lv_fs_close(&f);
if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
LV_LOG_WARN("Image get info get read file header");
return LV_RES_INV;
}
}
if(header->cf < CF_BUILT_IN_FIRST || header->cf > CF_BUILT_IN_LAST) return LV_RES_INV;
}
else if(src_type == LV_IMG_SRC_SYMBOL) {
/*The size depend on the font but it is unknown here. It should be handled outside of the
*function*/
header->w = 1;
header->h = 1;
/*Symbols always have transparent parts. Important because of cover check in the draw
*function. The actual value doesn't matter because lv_draw_label will draw it*/
header->cf = LV_IMG_CF_ALPHA_1BIT;
}
else {
LV_LOG_WARN("Image get info found unknown src type");
return LV_RES_INV;
}
return LV_RES_OK;
}
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
/*Open the file if it's a file*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder can't open the file");
return LV_RES_INV;
}
/*If the file was open successfully save the file descriptor*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_fs_close(&f);
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_memcpy_small(&user_data->f, &f, sizeof(f));
}
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*The variables should have valid data*/
if(((lv_img_dsc_t *)dsc->src)->data == NULL) {
return LV_RES_INV;
}
}
lv_img_cf_t cf = dsc->header.cf;
/*Process true color formats*/
if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
return LV_RES_OK;
}
else {
/*If it's a file it need to be read line by line later*/
return LV_RES_OK;
}
}
/*Process indexed images. Build a palette*/
else if(cf == LV_IMG_CF_INDEXED_1BIT || cf == LV_IMG_CF_INDEXED_2BIT || cf == LV_IMG_CF_INDEXED_4BIT ||
cf == LV_IMG_CF_INDEXED_8BIT) {
uint8_t px_size = lv_img_cf_get_px_size(cf);
uint32_t palette_size = 1 << px_size;
/*Allocate the palette*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
user_data->palette = lv_mem_alloc(palette_size * sizeof(lv_color_t));
LV_ASSERT_MALLOC(user_data->palette);
user_data->opa = lv_mem_alloc(palette_size * sizeof(lv_opa_t));
LV_ASSERT_MALLOC(user_data->opa);
if(user_data->palette == NULL || user_data->opa == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_img_decoder_built_in_close(decoder, dsc);
return LV_RES_INV;
}
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Read the palette from file*/
lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*Skip the header*/
lv_color32_t cur_color;
uint32_t i;
for(i = 0; i < palette_size; i++) {
lv_fs_read(&user_data->f, &cur_color, sizeof(lv_color32_t), NULL);
user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue);
user_data->opa[i] = cur_color.ch.alpha;
}
}
else {
/*The palette begins in the beginning of the image data. Just point to it.*/
lv_color32_t * palette_p = (lv_color32_t *)((lv_img_dsc_t *)dsc->src)->data;
uint32_t i;
for(i = 0; i < palette_size; i++) {
user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
user_data->opa[i] = palette_p[i].ch.alpha;
}
}
return LV_RES_OK;
}
/*Alpha indexed images.*/
else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT ||
cf == LV_IMG_CF_ALPHA_8BIT) {
return LV_RES_OK; /*Nothing to process*/
}
/*Unknown format. Can't decode it.*/
else {
/*Free the potentially allocated memories*/
lv_img_decoder_built_in_close(decoder, dsc);
LV_LOG_WARN("Image decoder open: unknown color format")
return LV_RES_INV;
}
}
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
(void)decoder; /*Unused*/
lv_res_t res = LV_RES_INV;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
/*For TRUE_COLOR images read line required only for files.
*For variables the image data was returned in `open`*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
res = lv_img_decoder_built_in_line_true_color(dsc, x, y, len, buf);
}
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
res = lv_img_decoder_built_in_line_alpha(dsc, x, y, len, buf);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT || dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
dsc->header.cf == LV_IMG_CF_INDEXED_4BIT || dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
res = lv_img_decoder_built_in_line_indexed(dsc, x, y, len, buf);
}
else {
LV_LOG_WARN("Built-in image decoder read not supports the color format");
return LV_RES_INV;
}
return res;
}
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
(void)decoder; /*Unused*/
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
if(user_data) {
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_fs_close(&user_data->f);
}
if(user_data->palette) lv_mem_free(user_data->palette);
if(user_data->opa) lv_mem_free(user_data->opa);
lv_mem_free(user_data);
dsc->user_data = NULL;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_fs_res_t res;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint32_t pos = ((y * dsc->header.w + x) * px_size) >> 3;
pos += 4; /*Skip the header*/
res = lv_fs_seek(&user_data->f, pos, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder seek failed");
return LV_RES_INV;
}
uint32_t btr = len * (px_size >> 3);
uint32_t br = 0;
res = lv_fs_read(&user_data->f, buf, btr, &br);
if(res != LV_FS_RES_OK || btr != br) {
LV_LOG_WARN("Built-in image decoder read failed");
return LV_RES_INV;
}
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
/*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
lv_color_t bg_color = dsc->color;
lv_coord_t i;
for(i = 0; i < len; i++) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
}
const lv_opa_t * opa_table = NULL;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
uint32_t ofs = 0;
int8_t pos = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_ALPHA_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
pos = 7 - (x & 0x7);
opa_table = alpha1_opa_table;
break;
case LV_IMG_CF_ALPHA_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
pos = 6 - (x & 0x3) * 2;
opa_table = alpha2_opa_table;
break;
case LV_IMG_CF_ALPHA_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
pos = 4 - (x & 0x1) * 4;
opa_table = alpha4_opa_table;
break;
case LV_IMG_CF_ALPHA_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if (fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
dsc->header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
int8_t pos = 0;
uint32_t ofs = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_INDEXED_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
ofs += 8; /*Skip the palette*/
pos = 7 - (x & 0x7);
break;
case LV_IMG_CF_INDEXED_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
ofs += 16; /*Skip the palette*/
pos = 6 - (x & 0x3) * 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
ofs += 64; /*Skip the palette*/
pos = 4 - (x & 0x1) * 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
ofs += 1024; /*Skip the palette*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if (fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
lv_coord_t i;
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
lv_color_t color = user_data->palette[val_act];
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}

View File

@ -0,0 +1,274 @@
/**
* @file lv_img_decoder.h
*
*/
#ifndef LV_IMG_DECODER_H
#define LV_IMG_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include "lv_img_buf.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Source of image.*/
enum {
LV_IMG_SRC_VARIABLE, /** Binary/C variable*/
LV_IMG_SRC_FILE, /** File in filesystem*/
LV_IMG_SRC_SYMBOL, /** Symbol (@ref lv_symbol_def.h)*/
LV_IMG_SRC_UNKNOWN, /** Unknown source*/
};
typedef uint8_t lv_img_src_t;
/*Decoder function definitions*/
struct _lv_img_decoder_dsc_t;
struct _lv_img_decoder_t;
/**
* Get info from an image and store in the `header`
* @param src the image source. Can be a pointer to a C array or a file name (Use
* `lv_img_src_get_type` to determine the type)
* @param header store the info here
* @return LV_RES_OK: info written correctly; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_info_f_t)(struct _lv_img_decoder_t * decoder, const void * src,
lv_img_header_t * header);
/**
* Open an image for decoding. Prepare it as it is required to read it later
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
*/
typedef lv_res_t (*lv_img_decoder_open_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_read_line_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc,
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
typedef void (*lv_img_decoder_close_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
typedef struct _lv_img_decoder_t {
lv_img_decoder_info_f_t info_cb;
lv_img_decoder_open_f_t open_cb;
lv_img_decoder_read_line_f_t read_line_cb;
lv_img_decoder_close_f_t close_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_img_decoder_t;
/**Describe an image decoding session. Stores data about the decoding*/
typedef struct _lv_img_decoder_dsc_t {
/**The decoder which was able to open the image source*/
lv_img_decoder_t * decoder;
/**The image source. A file path like "S:my_img.png" or pointer to an `lv_img_dsc_t` variable*/
const void * src;
/**Color to draw the image. USed when the image has alpha channel only*/
lv_color_t color;
/**Frame of the image, using with animated images*/
int32_t frame_id;
/**Type of the source: file or variable. Can be set in `open` function if required*/
lv_img_src_t src_type;
/**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
lv_img_header_t header;
/** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format.
* MUST be set in `open` function*/
const uint8_t * img_data;
/** How much time did it take to open the image. [ms]
* If not set `lv_img_cache` will measure and set the time to open*/
uint32_t time_to_open;
/**A text to display instead of the image when the image can't be opened.
* Can be set in `open` function or set NULL.*/
const char * error_msg;
/**Store any custom data here is required*/
void * user_data;
} lv_img_decoder_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void);
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_add_drv()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header);
/**
* Open an image.
* Try the created image decoder one by one. Once one is able to open the image that decoder is save in `dsc`
* @param dsc describe a decoding session. Simply a pointer to an `lv_img_decoder_dsc_t` variable.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_add_drv()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return LV_RES_OK: opened the image. `dsc->img_data` and `dsc->header` are set.
* LV_RES_INV: none of the registered image decoders were able to open the image.
*/
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id);
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len,
uint8_t * buf);
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc);
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void);
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder);
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb);
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb);
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb);
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb);
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_DECODER_H*/

View File

@ -0,0 +1 @@
CSRCS += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra -name \*.c)

View File

@ -0,0 +1,577 @@
/**
* @file lv_flex.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_layouts.h"
#if LV_USE_FLEX
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_flex_align_t main_place;
lv_flex_align_t cross_place;
lv_flex_align_t track_place;
uint8_t row :1;
uint8_t wrap :1;
uint8_t rev :1;
}flex_t;
typedef struct {
lv_obj_t * item;
lv_coord_t min_size;
lv_coord_t max_size;
lv_coord_t final_size;
uint32_t grow_value;
uint32_t clamped :1;
}grow_dsc_t;
typedef struct {
lv_coord_t track_cross_size;
lv_coord_t track_main_size; /*For all items*/
lv_coord_t track_fix_main_size; /*For non grow items*/
uint32_t item_cnt;
grow_dsc_t * grow_dsc;
uint32_t grow_item_cnt;
uint32_t grow_dsc_calc :1;
}track_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void flex_update(lv_obj_t * cont, void * user_data);
static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, lv_coord_t item_gap, lv_coord_t max_main_size, track_t * t);
static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t);
static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt, lv_coord_t * start_pos, lv_coord_t * gap);
static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id);
/**********************
* GLOBAL VARIABLES
**********************/
uint32_t LV_LAYOUT_FLEX;
lv_style_prop_t LV_STYLE_FLEX_FLOW;
lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE;
lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE;
lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE;
lv_style_prop_t LV_STYLE_FLEX_GROW;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_flex_init(void)
{
LV_LAYOUT_FLEX = lv_layout_register(flex_update, NULL);
LV_STYLE_FLEX_FLOW = lv_style_register_prop();
LV_STYLE_FLEX_MAIN_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_FLEX_CROSS_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_FLEX_TRACK_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
}
void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow)
{
lv_obj_set_style_flex_flow(obj, flow, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
}
void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place, lv_flex_align_t track_place)
{
lv_obj_set_style_flex_main_place(obj, main_place, 0);
lv_obj_set_style_flex_cross_place(obj, cross_place, 0);
lv_obj_set_style_flex_track_place(obj, track_place, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
}
void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow)
{
lv_obj_set_style_flex_grow(obj, grow, 0);
}
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_FLOW, v);
}
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_MAIN_PLACE, v);
}
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_CROSS_PLACE, v);
}
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_TRACK_PLACE, v);
}
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_GROW, v);
}
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_FLOW, v, selector);
}
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_MAIN_PLACE, v, selector);
}
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_CROSS_PLACE, v, selector);
}
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_TRACK_PLACE, v, selector);
}
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_GROW, v, selector);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void flex_update(lv_obj_t * cont, void * user_data)
{
LV_LOG_INFO("update %p container", cont);
LV_UNUSED(user_data);
flex_t f;
lv_flex_flow_t flow = lv_obj_get_style_flex_flow(cont, LV_PART_MAIN);
f.row = flow & _LV_FLEX_COLUMN ? 0 : 1;
f.wrap = flow & _LV_FLEX_WRAP ? 1 : 0;
f.rev = flow & _LV_FLEX_REVERSE ? 1 : 0;
f.main_place = lv_obj_get_style_flex_main_place(cont, LV_PART_MAIN);
f.cross_place = lv_obj_get_style_flex_cross_place(cont, LV_PART_MAIN);
f.track_place = lv_obj_get_style_flex_track_place(cont, LV_PART_MAIN);
bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t track_gap = !f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t item_gap = f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t max_main_size = (f.row ? lv_obj_get_content_width(cont) : lv_obj_get_content_height(cont));
lv_coord_t border_widt = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
lv_coord_t abs_y = cont->coords.y1 + lv_obj_get_style_pad_top(cont, LV_PART_MAIN) + border_widt - lv_obj_get_scroll_y(cont);
lv_coord_t abs_x = cont->coords.x1 + lv_obj_get_style_pad_left(cont, LV_PART_MAIN) + border_widt - lv_obj_get_scroll_x(cont);
lv_flex_align_t track_cross_place = f.track_place;
lv_coord_t * cross_pos = (f.row ? &abs_y : &abs_x);
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
/*Content sized objects should squeezed the gap between the children, therefore any alignment will look like `START`*/
if((f.row && h_set == LV_SIZE_CONTENT && cont->h_layout == 0) ||
(!f.row && w_set == LV_SIZE_CONTENT && cont->w_layout == 0))
{
track_cross_place = LV_FLEX_ALIGN_START;
}
if(rtl && !f.row) {
if(track_cross_place == LV_FLEX_ALIGN_START) track_cross_place = LV_FLEX_ALIGN_END;
else if(track_cross_place == LV_FLEX_ALIGN_END) track_cross_place = LV_FLEX_ALIGN_START;
}
lv_coord_t total_track_cross_size = 0;
lv_coord_t gap = 0;
uint32_t track_cnt = 0;
int32_t track_first_item;
int32_t next_track_first_item;
if(track_cross_place != LV_FLEX_ALIGN_START) {
track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
track_t t;
while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
/*Search the first item of the next row*/
t.grow_dsc_calc = 0;
next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
total_track_cross_size += t.track_cross_size + track_gap;
track_cnt++;
track_first_item = next_track_first_item;
}
if(track_cnt) total_track_cross_size -= track_gap; /*No gap after the last track*/
/*Place the tracks to get the start position*/
lv_coord_t max_cross_size = (f.row ? lv_obj_get_content_height(cont) : lv_obj_get_content_width(cont));
place_content(track_cross_place, max_cross_size, total_track_cross_size, track_cnt, cross_pos, &gap);
}
track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
if(rtl && !f.row) {
*cross_pos += total_track_cross_size;
}
while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
track_t t;
t.grow_dsc_calc = 1;
/*Search the first item of the next row*/
next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
if(rtl && !f.row) {
*cross_pos -= t.track_cross_size;
}
children_repos(cont, &f, track_first_item, next_track_first_item, abs_x, abs_y, max_main_size, item_gap, &t);
track_first_item = next_track_first_item;
lv_mem_buf_release(t.grow_dsc);
t.grow_dsc = NULL;
if(rtl && !f.row) {
*cross_pos -= gap + track_gap;
} else {
*cross_pos += t.track_cross_size + gap + track_gap;
}
}
LV_ASSERT_MEM_INTEGRITY();
if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
lv_obj_refr_size(cont);
}
lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
LV_TRACE_LAYOUT("finished");
}
/**
* Find the last item of a track
*/
static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t)
{
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
/*Can't wrap if the size if auto (i.e. the size depends on the children)*/
if(f->wrap && ((f->row && w_set == LV_SIZE_CONTENT) || (!f->row && h_set == LV_SIZE_CONTENT))) {
f->wrap = false;
}
lv_coord_t(*get_main_size)(const lv_obj_t *) = (f->row ? lv_obj_get_width : lv_obj_get_height);
lv_coord_t(*get_cross_size)(const lv_obj_t *) = (!f->row ? lv_obj_get_width : lv_obj_get_height);
lv_coord_t grow_sum = 0;
t->track_main_size = 0;
t->track_fix_main_size = 0;
t->grow_item_cnt = 0;
t->track_cross_size = 0;
t->item_cnt = 0;
t->grow_dsc = NULL;
int32_t item_id = item_start_id;
lv_obj_t * item = lv_obj_get_child(cont, item_id);
while(item) {
if(item_id != item_start_id && lv_obj_has_flag(item, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)) break;
if(!lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
uint8_t grow_value = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
if(grow_value) {
grow_sum += grow_value;
t->grow_item_cnt++;
t->track_fix_main_size += item_gap;
if(t->grow_dsc_calc) {
grow_dsc_t * new_dsc = lv_mem_buf_get(sizeof(grow_dsc_t) * (t->grow_item_cnt));
LV_ASSERT_MALLOC(new_dsc);
if(new_dsc == NULL) return item_id;
lv_memcpy(new_dsc, t->grow_dsc, sizeof(grow_dsc_t) * (t->grow_item_cnt - 1));
lv_mem_buf_release(t->grow_dsc);
new_dsc[t->grow_item_cnt - 1].item = item;
new_dsc[t->grow_item_cnt - 1].min_size = f->row ? lv_obj_get_style_min_width(item, LV_PART_MAIN) : lv_obj_get_style_min_height(item, LV_PART_MAIN);
new_dsc[t->grow_item_cnt - 1].max_size = f->row ? lv_obj_get_style_max_width(item, LV_PART_MAIN) : lv_obj_get_style_max_height(item, LV_PART_MAIN);
new_dsc[t->grow_item_cnt - 1].grow_value = grow_value;
new_dsc[t->grow_item_cnt - 1].clamped = 0;
t->grow_dsc = new_dsc;
}
} else {
lv_coord_t item_size = get_main_size(item);
if(f->wrap && t->track_fix_main_size + item_size > max_main_size) break;
t->track_fix_main_size += item_size + item_gap;
}
t->track_cross_size = LV_MAX(get_cross_size(item), t->track_cross_size);
t->item_cnt++;
}
item_id += f->rev ? -1 : +1;
if(item_id < 0) break;
item = lv_obj_get_child(cont, item_id);
}
if(t->track_fix_main_size > 0) t->track_fix_main_size -= item_gap; /*There is no gap after the last item*/
/*If there is at least one "grow item" the track takes the full space*/
t->track_main_size = t->grow_item_cnt ? max_main_size : t->track_fix_main_size;
/*Have at least one item in a row*/
if(item && item_id == item_start_id) {
item = cont->spec_attr->children[item_id];
get_next_item(cont, f->rev, &item_id);
if(item) {
t->track_cross_size = get_cross_size(item);
t->track_main_size = get_main_size(item);
t->item_cnt = 1;
}
}
return item_id;
}
/**
* Position the children in the same track
*/
static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t)
{
void (*area_set_main_size)(lv_area_t *, lv_coord_t) = (f->row ? lv_area_set_width : lv_area_set_height);
lv_coord_t (*area_get_main_size)(const lv_area_t *) = (f->row ? lv_area_get_width : lv_area_get_height);
lv_coord_t (*area_get_cross_size)(const lv_area_t *) = (!f->row ? lv_area_get_width : lv_area_get_height);
/*Calculate the size of grow items first*/
uint32_t i;
bool grow_reiterate = true;
while(grow_reiterate) {
grow_reiterate = false;
lv_coord_t grow_value_sum = 0;
lv_coord_t grow_max_size = t->track_main_size - t->track_fix_main_size;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].clamped == 0) {
grow_value_sum += t->grow_dsc[i].grow_value;
} else {
grow_max_size -= t->grow_dsc[i].final_size;
}
}
lv_coord_t grow_unit;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].clamped == 0) {
grow_unit = grow_max_size / grow_value_sum;
lv_coord_t size = grow_unit * t->grow_dsc[i].grow_value;
lv_coord_t size_clamp = LV_CLAMP(t->grow_dsc[i].min_size, size, t->grow_dsc[i].max_size);
if(size_clamp != size) {
t->grow_dsc[i].clamped = 1;
grow_reiterate = true;
}
t->grow_dsc[i].final_size = size_clamp;
grow_value_sum -= t->grow_dsc[i].grow_value;
grow_max_size -= t->grow_dsc[i].final_size;
}
}
}
bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t main_pos = 0;
lv_coord_t place_gap = 0;
place_content(f->main_place, max_main_size, t->track_main_size, t->item_cnt, &main_pos, &place_gap);
if(f->row && rtl) main_pos += lv_obj_get_content_width(cont);
lv_obj_t * item = lv_obj_get_child(cont, item_first_id);
/*Reposition the children*/
while(item && item_first_id != item_last_id) {
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
item = get_next_item(cont, f->rev, &item_first_id);
continue;
}
lv_coord_t grow_size = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
if(grow_size) {
lv_coord_t s = 0;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].item == item) {
s = t->grow_dsc[i].final_size;
break;
}
}
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
area_set_main_size(&item->coords, s);
if(f->row) item->w_layout = 1;
else item->h_layout = 1;
if(area_get_main_size(&old_coords) != area_get_main_size(&item->coords)) {
lv_obj_invalidate(item);
lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
lv_obj_invalidate(item);
}
} else {
item->w_layout = 0;
item->h_layout = 0;
}
lv_coord_t cross_pos = 0;
switch(f->cross_place) {
case LV_FLEX_ALIGN_CENTER:
/*Round up the cross size to avoid rounding error when dividing by 2
*The issue comes up e,g, with column direction with center cross direction if an element's width changes*/
cross_pos = (((t->track_cross_size + 1) & (~1)) - area_get_cross_size(&item->coords)) / 2;
break;
case LV_FLEX_ALIGN_END:
cross_pos = t->track_cross_size - area_get_cross_size(&item->coords);
break;
default:
break;
}
if(f->row && rtl) main_pos -= area_get_main_size(&item->coords);
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(item);
lv_coord_t h = lv_obj_get_height(item);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
lv_coord_t diff_x = abs_x - item->coords.x1 + tr_x;
lv_coord_t diff_y = abs_y - item->coords.y1 + tr_y;
diff_x += f->row ? main_pos : cross_pos;
diff_y += f->row ? cross_pos : main_pos;
if(diff_x || diff_y) {
lv_obj_invalidate(item);
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
lv_obj_invalidate(item);
lv_obj_move_children_by(item, diff_x, diff_y, true);
}
if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap;
else main_pos -= item_gap + place_gap;
item = get_next_item(cont, f->rev, &item_first_id);
}
}
/**
* Tell a start coordinate and gap for a placement type.
*/
static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt, lv_coord_t * start_pos, lv_coord_t * gap)
{
if(item_cnt <= 1) {
switch(place) {
case LV_FLEX_ALIGN_SPACE_BETWEEN:
case LV_FLEX_ALIGN_SPACE_AROUND:
case LV_FLEX_ALIGN_SPACE_EVENLY:
place = LV_FLEX_ALIGN_CENTER;
break;
default:
break;
}
}
switch(place) {
case LV_FLEX_ALIGN_CENTER:
*gap = 0;
*start_pos += (max_size - content_size) / 2;
break;
case LV_FLEX_ALIGN_END:
*gap = 0;
*start_pos += max_size - content_size;
break;
case LV_FLEX_ALIGN_SPACE_BETWEEN:
*gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt - 1);
break;
case LV_FLEX_ALIGN_SPACE_AROUND:
*gap += (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt);
*start_pos += *gap / 2;
break;
case LV_FLEX_ALIGN_SPACE_EVENLY:
*gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt + 1);
*start_pos += *gap;
break;
default:
*gap = 0;
}
}
static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id)
{
if(rev) {
(*item_id)--;
if(*item_id >= 0) return cont->spec_attr->children[*item_id];
else return NULL;
} else {
(*item_id)++;
if((*item_id) < (int32_t)cont->spec_attr->child_cnt) return cont->spec_attr->children[*item_id];
else return NULL;
}
}
#endif /*LV_USE_FLEX*/

View File

@ -0,0 +1,152 @@
/**
* @file lv_flex.h
*
*/
#ifndef LV_FLEX_H
#define LV_FLEX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_FLEX
/*********************
* DEFINES
*********************/
#define LV_OBJ_FLAG_FLEX_IN_NEW_TRACK LV_OBJ_FLAG_LAYOUT_1
LV_EXPORT_CONST_INT(LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
#define _LV_FLEX_COLUMN (1 << 0)
#define _LV_FLEX_WRAP (1 << 2)
#define _LV_FLEX_REVERSE (1 << 3)
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_END,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_SPACE_AROUND,
LV_FLEX_ALIGN_SPACE_BETWEEN,
}lv_flex_align_t;
typedef enum {
LV_FLEX_FLOW_ROW = 0x00,
LV_FLEX_FLOW_COLUMN = _LV_FLEX_COLUMN,
LV_FLEX_FLOW_ROW_WRAP = LV_FLEX_FLOW_ROW | _LV_FLEX_WRAP,
LV_FLEX_FLOW_ROW_REVERSE = LV_FLEX_FLOW_ROW | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_ROW_WRAP_REVERSE = LV_FLEX_FLOW_ROW | _LV_FLEX_WRAP | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_COLUMN_WRAP = LV_FLEX_FLOW_COLUMN | _LV_FLEX_WRAP,
LV_FLEX_FLOW_COLUMN_REVERSE = LV_FLEX_FLOW_COLUMN | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_COLUMN_WRAP_REVERSE = LV_FLEX_FLOW_COLUMN | _LV_FLEX_WRAP | _LV_FLEX_REVERSE,
}lv_flex_flow_t;
/**********************
* GLOBAL VARIABLES
**********************/
extern uint32_t LV_LAYOUT_FLEX;
extern lv_style_prop_t LV_STYLE_FLEX_FLOW;
extern lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_GROW;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a felx layout the default values
* @param flex pointer to a flex layout descriptor
*/
void lv_flex_init(void);
/**
* Set hot the item should flow
* @param flex pointer to a flex layout descriptor
* @param flow an element of `lv_flex_flow_t`.
*/
void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow);
/**
* Set how to place (where to align) the items an tracks
* @param flex pointer: to a flex layout descriptor
* @param main_place where to place the items on main axis (in their track). Any value of `lv_flex_align_t`.
* @param cross_place where to place the item in their track on the cross axis. `LV_FLEX_ALIGN_START/END/CENTER`
* @param track_place where to place the tracks in the cross direction. Any value of `lv_flex_align_t`.
*/
void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place, lv_flex_align_t track_cross_place);
/**
* Sets the width or height (on main axis) to grow the object in order fill the free space
* @param obj pointer to an object. The parent must have flex layout else nothing will happen.
* @param grow a value to set how much free space to take proportionally to other growing items.
*/
void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow);
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value);
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value);
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector);
static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_FLOW);
return (lv_flex_flow_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_main_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_MAIN_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_cross_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_CROSS_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_track_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_TRACK_PLACE);
return (lv_flex_align_t)v.num;
}
static inline uint8_t lv_obj_get_style_flex_grow(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_GROW);
return (uint8_t)v.num;
}
/**********************
* MACROS
**********************/
#endif /*LV_USE_FLEX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FLEX_H*/

View File

@ -0,0 +1,743 @@
/**
* @file lv_grid.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_layouts.h"
#if LV_USE_GRID
/*********************
* DEFINES
*********************/
/**
* Some helper defines
*/
#define IS_FR(x) (x >= LV_COORD_MAX - 100)
#define IS_CONTENT(x) (x == LV_COORD_MAX - 101)
#define GET_FR(x) (x - (LV_COORD_MAX - 100))
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint32_t col;
uint32_t row;
lv_point_t grid_abs;
}item_repos_hint_t;
typedef struct {
lv_coord_t * x;
lv_coord_t * y;
lv_coord_t * w;
lv_coord_t * h;
uint32_t col_num;
uint32_t row_num;
lv_coord_t grid_w;
lv_coord_t grid_h;
}_lv_grid_calc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void grid_update(lv_obj_t * cont, void * user_data);
static void calc(lv_obj_t * obj, _lv_grid_calc_t * calc);
static void calc_free(_lv_grid_calc_t * calc);
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c);
static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c);
static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint);
static lv_coord_t grid_align(lv_coord_t cont_size, bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num, lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse);
static uint32_t count_tracks(const lv_coord_t * templ);
static inline const lv_coord_t * get_col_dsc(lv_obj_t * obj) {return lv_obj_get_style_grid_column_dsc_array(obj, 0); }
static inline const lv_coord_t * get_row_dsc(lv_obj_t * obj) {return lv_obj_get_style_grid_row_dsc_array(obj, 0); }
static inline uint8_t get_col_pos(lv_obj_t * obj) {return lv_obj_get_style_grid_cell_column_pos(obj, 0); }
static inline uint8_t get_row_pos(lv_obj_t * obj) {return lv_obj_get_style_grid_cell_row_pos(obj, 0); }
static inline uint8_t get_col_span(lv_obj_t * obj) {return lv_obj_get_style_grid_cell_column_span(obj, 0); }
static inline uint8_t get_row_span(lv_obj_t * obj) {return lv_obj_get_style_grid_cell_row_span(obj, 0); }
static inline uint8_t get_cell_col_align(lv_obj_t * obj) {return lv_obj_get_style_grid_cell_x_align(obj, 0); }
static inline uint8_t get_cell_row_align(lv_obj_t * obj) {return lv_obj_get_style_grid_cell_y_align(obj, 0); }
static inline uint8_t get_grid_col_align(lv_obj_t * obj) {return lv_obj_get_style_grid_column_align(obj, 0); }
static inline uint8_t get_grid_row_align(lv_obj_t * obj) {return lv_obj_get_style_grid_row_align(obj, 0); }
/**********************
* GLOBAL VARIABLES
**********************/
uint32_t LV_LAYOUT_GRID;
lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_grid_init(void)
{
LV_LAYOUT_GRID = lv_layout_register(grid_update, NULL);
LV_STYLE_GRID_COLUMN_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_ROW_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_COLUMN_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_ROW_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_ROW_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_ROW_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_COLUMN_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_COLUMN_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_X_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_Y_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
}
void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[])
{
lv_obj_set_style_grid_column_dsc_array(obj, col_dsc, 0);
lv_obj_set_style_grid_row_dsc_array(obj, row_dsc, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_GRID, 0);
}
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align)
{
lv_obj_set_style_grid_column_align(obj, column_align, 0);
lv_obj_set_style_grid_row_align(obj, row_align, 0);
}
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t x_align, uint8_t col_pos, uint8_t col_span,
lv_grid_align_t y_align, uint8_t row_pos, uint8_t row_span)
{
lv_obj_set_style_grid_cell_column_pos(obj, col_pos, 0);
lv_obj_set_style_grid_cell_row_pos(obj, row_pos, 0);
lv_obj_set_style_grid_cell_x_align(obj, x_align, 0);
lv_obj_set_style_grid_cell_column_span(obj, col_span, 0);
lv_obj_set_style_grid_cell_row_span(obj, row_span, 0);
lv_obj_set_style_grid_cell_y_align(obj, y_align, 0);
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[])
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_style_set_prop(style, LV_STYLE_GRID_ROW_DSC_ARRAY, v);
}
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[])
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v);
}
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value)
{
lv_style_value_t v = {
.num = (lv_grid_align_t)value
};
lv_style_set_prop(style, LV_STYLE_GRID_ROW_ALIGN, v);
}
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value)
{
lv_style_value_t v = {
.num = (lv_grid_align_t)value
};
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_ALIGN, v);
}
void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_POS, v);
}
void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_SPAN, v);
}
void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_POS, v);
}
void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_SPAN, v);
}
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_X_ALIGN, v);
}
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_Y_ALIGN, v);
}
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_obj_set_local_style_prop(obj,LV_STYLE_GRID_ROW_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_ALIGN, v, selector);
}
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj,LV_STYLE_GRID_CELL_COLUMN_POS, v, selector);
}
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj,LV_STYLE_GRID_CELL_COLUMN_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj,LV_STYLE_GRID_CELL_ROW_POS, v, selector);
}
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_X_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_Y_ALIGN, v, selector);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void grid_update(lv_obj_t * cont, void * user_data)
{
LV_LOG_INFO("update %p container", cont);
LV_UNUSED(user_data);
const lv_coord_t * col_templ = get_col_dsc(cont);
const lv_coord_t * row_templ = get_row_dsc(cont);
if(col_templ == NULL || row_templ == NULL) return;
_lv_grid_calc_t c;
calc(cont, &c);
item_repos_hint_t hint;
lv_memset_00(&hint, sizeof(hint));
/*Calculate the grids absolute x and y coordinates.
*It will be used as helper during item repositioning to avoid calculating this value for every children*/
lv_coord_t border_widt = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(cont, LV_PART_MAIN) + border_widt;
lv_coord_t pad_top = lv_obj_get_style_pad_top(cont, LV_PART_MAIN) + border_widt;
hint.grid_abs.x = pad_left + cont->coords.x1 - lv_obj_get_scroll_x(cont);
hint.grid_abs.y = pad_top + cont->coords.y1 - lv_obj_get_scroll_y(cont);
uint32_t i;
for(i = 0; i < cont->spec_attr->child_cnt; i++) {
lv_obj_t * item = cont->spec_attr->children[i];
item_repos(item, &c, &hint);
}
calc_free(&c);
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
lv_obj_refr_size(cont);
}
lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
LV_TRACE_LAYOUT("finished");
}
/**
* Calculate the grid cells coordinates
* @param cont an object that has a grid
* @param calc store the calculated cells sizes here
* @note `_lv_grid_calc_free(calc_out)` needs to be called when `calc_out` is not needed anymore
*/
static void calc(lv_obj_t * cont, _lv_grid_calc_t * calc_out)
{
if(lv_obj_get_child(cont, 0) == NULL) {
lv_memset_00(calc_out, sizeof(_lv_grid_calc_t));
return;
}
calc_rows(cont, calc_out);
calc_cols(cont, calc_out);
lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
bool rev = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
bool auto_w = (w_set == LV_SIZE_CONTENT && !cont->w_layout) ? true : false;
lv_coord_t cont_w = lv_obj_get_content_width(cont);
calc_out->grid_w = grid_align(cont_w, auto_w, get_grid_col_align(cont), col_gap, calc_out->col_num, calc_out->w, calc_out->x, rev);
bool auto_h = (h_set == LV_SIZE_CONTENT && !cont->h_layout) ? true : false;
lv_coord_t cont_h = lv_obj_get_content_height(cont);
calc_out->grid_h = grid_align(cont_h, auto_h, get_grid_row_align(cont), row_gap, calc_out->row_num, calc_out->h, calc_out->y, false);
LV_ASSERT_MEM_INTEGRITY();
}
/**
* Free the a grid calculation's data
* @param calc pointer to the calculated gtrid cell coordinates
*/
static void calc_free(_lv_grid_calc_t * calc)
{
lv_mem_buf_release(calc->x);
lv_mem_buf_release(calc->y);
lv_mem_buf_release(calc->w);
lv_mem_buf_release(calc->h);
}
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c)
{
const lv_coord_t * col_templ = get_col_dsc(cont);
lv_coord_t cont_w = lv_obj_get_content_width(cont);
c->col_num = count_tracks(col_templ);
c->x = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
c->w = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
/*Set sizes for CONTENT cells*/
uint32_t i;
for(i = 0; i < c->col_num; i++) {
lv_coord_t size = LV_COORD_MIN;
if(IS_CONTENT(col_templ[i])) {
/*Check the size of children of this cell*/
uint32_t ci;
for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
lv_obj_t * item = lv_obj_get_child(cont, ci);
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
uint32_t col_span = get_col_span(item);
if(col_span != 1) continue;
uint32_t col_pos = get_col_pos(item);
if(col_pos != i) continue;
size = LV_MAX(size, lv_obj_get_width(item));
}
if(size >= 0) c->w[i] = size;
else c->w[i] = 0;
}
}
uint32_t col_fr_cnt = 0;
lv_coord_t grid_w = 0;
for(i = 0; i < c->col_num; i++) {
lv_coord_t x = col_templ[i];
if(IS_FR(x)) {
col_fr_cnt += GET_FR(x);
}
else if (IS_CONTENT(x)) {
grid_w += c->w[i];
}
else {
c->w[i] = x;
grid_w += x;
}
}
lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
cont_w -= col_gap * (c->col_num - 1);
lv_coord_t free_w = cont_w - grid_w;
if(free_w < 0) free_w = 0;
int32_t last_fr_i = -1;
int32_t last_fr_x = 0;
for(i = 0; i < c->col_num; i++) {
lv_coord_t x = col_templ[i];
if(IS_FR(x)) {
lv_coord_t f = GET_FR(x);
c->w[i] = (free_w * f) / col_fr_cnt;
last_fr_i = i;
last_fr_x = f;
}
}
/*To avoid rounding errors set the last FR track to the remaining size */
if(last_fr_i >= 0) {
c->w[last_fr_i] = free_w - ((free_w * (col_fr_cnt - last_fr_x)) / col_fr_cnt);
}
}
static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c)
{
uint32_t i;
const lv_coord_t * row_templ = get_row_dsc(cont);
c->row_num = count_tracks(row_templ);
c->y = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
c->h = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
/*Set sizes for CONTENT cells*/
for(i = 0; i < c->row_num; i++) {
lv_coord_t size = LV_COORD_MIN;
if(IS_CONTENT(row_templ[i])) {
/*Check the size of children of this cell*/
uint32_t ci;
for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
lv_obj_t * item = lv_obj_get_child(cont, ci);
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
uint32_t row_span = get_row_span(item);
if(row_span != 1) continue;
uint32_t row_pos = get_row_pos(item);
if(row_pos != i) continue;
size = LV_MAX(size, lv_obj_get_height(item));
}
if(size >= 0) c->h[i] = size;
else c->h[i] = 0;
}
}
uint32_t row_fr_cnt = 0;
lv_coord_t grid_h = 0;
for(i = 0; i < c->row_num; i++) {
lv_coord_t x = row_templ[i];
if(IS_FR(x)) {
row_fr_cnt += GET_FR(x);
} else if (IS_CONTENT(x)) {
grid_h += c->h[i];
} else {
c->h[i] = x;
grid_h += x;
}
}
lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t cont_h = lv_obj_get_content_height(cont) - row_gap * (c->row_num - 1);
lv_coord_t free_h = cont_h - grid_h;
if(free_h < 0) free_h = 0;
int32_t last_fr_i = -1;
int32_t last_fr_x = 0;
for(i = 0; i < c->row_num; i++) {
lv_coord_t x = row_templ[i];
if(IS_FR(x)) {
lv_coord_t f = GET_FR(x);
c->h[i] = (free_h * f) / row_fr_cnt;
}
}
/*To avoid rounding errors set the last FR track to the remaining size */
if(last_fr_i >= 0) {
c->h[last_fr_i] = free_h - ((free_h * (row_fr_cnt - last_fr_x)) / row_fr_cnt);
}
}
/**
* Reposition a grid item in its cell
* @param item a grid item to reposition
* @param calc the calculated grid of `cont`
* @param child_id_ext helper value if the ID of the child is know (order from the oldest) else -1
* @param grid_abs helper value, the absolute position of the grid, NULL if unknown
*/
static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint)
{
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) return;
uint32_t col_span = get_col_span(item);
uint32_t row_span = get_row_span(item);
if(row_span == 0 || col_span == 0) return;
uint32_t col_pos = get_col_pos(item);
uint32_t row_pos = get_row_pos(item);
lv_grid_align_t col_align = get_cell_col_align(item);
lv_grid_align_t row_align = get_cell_row_align(item);
lv_coord_t col_x1 = c->x[col_pos];
lv_coord_t col_x2 = c->x[col_pos + col_span - 1] + c->w[col_pos + col_span - 1];
lv_coord_t col_w = col_x2 - col_x1;
lv_coord_t row_y1 = c->y[row_pos];
lv_coord_t row_y2 = c->y[row_pos + row_span - 1] + c->h[row_pos + row_span - 1];
lv_coord_t row_h = row_y2 - row_y1;
/*If the item has RTL base dir switch start and end*/
if(lv_obj_get_style_base_dir(item, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(col_align == LV_GRID_ALIGN_START) col_align = LV_GRID_ALIGN_END;
else if(col_align == LV_GRID_ALIGN_END) col_align = LV_GRID_ALIGN_START;
}
lv_coord_t x;
lv_coord_t y;
lv_coord_t item_w = lv_area_get_width(&item->coords);
lv_coord_t item_h = lv_area_get_height(&item->coords);
switch(col_align) {
default:
case LV_GRID_ALIGN_START:
x = c->x[col_pos];
item->w_layout = 0;
break;
case LV_GRID_ALIGN_STRETCH:
x = c->x[col_pos];
item_w = col_w;
item->w_layout = 1;
break;
case LV_GRID_ALIGN_CENTER:
x = c->x[col_pos] + (col_w - item_w) / 2;
item->w_layout = 0;
break;
case LV_GRID_ALIGN_END:
x = c->x[col_pos] + col_w - lv_obj_get_width(item);
item->w_layout = 0;
break;
}
switch(row_align) {
default:
case LV_GRID_ALIGN_START:
y = c->y[row_pos];
item->h_layout = 0;
break;
case LV_GRID_ALIGN_STRETCH:
y = c->y[row_pos];
item_h = row_h;
item->h_layout = 1;
break;
case LV_GRID_ALIGN_CENTER:
y = c->y[row_pos] + (row_h - item_h) / 2;
item->h_layout = 0;
break;
case LV_GRID_ALIGN_END:
y = c->y[row_pos] + row_h - lv_obj_get_height(item);
item->h_layout = 0;
break;
}
/*Set a new size if required*/
if(lv_obj_get_width(item) != item_w || lv_obj_get_height(item) != item_h) {
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
lv_obj_invalidate(item);
lv_area_set_width(&item->coords, item_w);
lv_area_set_height(&item->coords, item_h);
lv_obj_invalidate(item);
lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
}
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(item);
lv_coord_t h = lv_obj_get_height(item);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
x += tr_x;
y += tr_y;
lv_coord_t diff_x = hint->grid_abs.x + x - item->coords.x1;
lv_coord_t diff_y = hint->grid_abs.y + y - item->coords.y1;
if(diff_x || diff_y) {
lv_obj_invalidate(item);
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
lv_obj_invalidate(item);
lv_obj_move_children_by(item, diff_x, diff_y, true);
}
}
/**
* Place the grid track according to align methods. It keeps the track sizes but sets their position.
* It can process both columns or rows according to the passed parameters.
* @param cont_size size of the containers content area (width/height)
* @param auto_size true: the container has auto size in the current direction
* @param align align method
* @param gap grid gap
* @param track_num number of tracks
* @param size_array array with the track sizes
* @param pos_array write the positions of the tracks here
* @return the total size of the grid
*/
static lv_coord_t grid_align(lv_coord_t cont_size, bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num, lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse)
{
lv_coord_t grid_size = 0;
uint32_t i;
if(auto_size) {
pos_array[0] = 0;
} else {
/*With spaced alignment gap will be calculated from the remaining space*/
if(align == LV_GRID_ALIGN_SPACE_AROUND || align == LV_GRID_ALIGN_SPACE_BETWEEN || align == LV_GRID_ALIGN_SPACE_EVENLY) {
gap = 0;
if(track_num == 1) align = LV_GRID_ALIGN_CENTER;
}
/*Get the full grid size with gap*/
for(i = 0; i < track_num; i++) {
grid_size += size_array[i] + gap;
}
grid_size -= gap;
/*Calculate the position of the first item and set gap is necessary*/
switch(align) {
case LV_GRID_ALIGN_START:
pos_array[0] = 0;
break;
case LV_GRID_ALIGN_CENTER:
pos_array[0] = (cont_size - grid_size) / 2;
break;
case LV_GRID_ALIGN_END:
pos_array[0] = cont_size - grid_size;
break;
case LV_GRID_ALIGN_SPACE_BETWEEN:
pos_array[0] = 0;
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num - 1);
break;
case LV_GRID_ALIGN_SPACE_AROUND:
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num);
pos_array[0] = gap / 2;
break;
case LV_GRID_ALIGN_SPACE_EVENLY:
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num + 1);
pos_array[0] = gap;
break;
}
}
/*Set the position of all tracks from the start position, gaps and track sizes*/
for(i = 0; i < track_num - 1; i++) {
pos_array[i + 1] = pos_array[i] + size_array[i] + gap;
}
lv_coord_t total_gird_size = pos_array[track_num - 1] + size_array[track_num - 1] - pos_array[0];
if(reverse) {
for(i = 0; i < track_num; i++) {
pos_array[i] = cont_size - pos_array[i] - size_array[i];
}
}
/*Return the full size of the grid*/
return total_gird_size;
}
static uint32_t count_tracks(const lv_coord_t * templ)
{
uint32_t i;
for(i = 0; templ[i] != LV_COORD_MAX; i++);
return i;
}
#endif /*LV_USE_GRID*/

View File

@ -0,0 +1,194 @@
/**
* @file lv_grid.h
*
*/
#ifndef LV_GRID_H
#define LV_GRID_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_GRID
/*********************
* DEFINES
*********************/
/**
* Can be used track size to make the track fill the free space.
* @param x how much space to take proportionally to other FR tracks
* @return a special track size
*/
#define LV_GRID_FR(x) (LV_COORD_MAX - 100 + x)
#define LV_GRID_CONTENT (LV_COORD_MAX - 101)
LV_EXPORT_CONST_INT(LV_GRID_CONTENT);
#define LV_GRID_TEMPLATE_LAST (LV_COORD_MAX)
LV_EXPORT_CONST_INT(LV_GRID_TEMPLATE_LAST);
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
LV_GRID_ALIGN_START,
LV_GRID_ALIGN_CENTER,
LV_GRID_ALIGN_END,
LV_GRID_ALIGN_STRETCH,
LV_GRID_ALIGN_SPACE_EVENLY,
LV_GRID_ALIGN_SPACE_AROUND,
LV_GRID_ALIGN_SPACE_BETWEEN,
}lv_grid_align_t;
/**********************
* GLOBAL VARIABLES
**********************/
extern uint32_t LV_LAYOUT_GRID;
extern lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
extern lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
extern lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
extern lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
extern lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_grid_init(void);
void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[]);
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align);
/**
* Set the cell of an object. The object's parent needs to have grid layout, else nothing will happen
* @param obj pointer to an object
* @param hor_place the vertical alignment in the cell. `LV_GRID_START/END/CENTER/STRETCH`
* @param col_pos column ID
* @param col_span number of columns to take (>= 1)
* @param ver_place the horizontal alignment in the cell. `LV_GRID_START/END/CENTER/STRETCH`
* @param row_pos row ID
* @param row_span number of rows to take (>= 1)
*/
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t column_align, uint8_t col_pos, uint8_t col_span,
lv_grid_align_t row_align, uint8_t row_pos, uint8_t row_span);
/**
* Just a wrapper to `LV_GRID_FR` for bindings.
*/
static inline lv_coord_t lv_grid_fr(uint8_t x)
{
return LV_GRID_FR(x);
}
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[]);
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[]);
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value);
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value);
void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value);
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector);
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector);
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
static inline const lv_coord_t * lv_obj_get_style_grid_row_dsc_array(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_DSC_ARRAY);
return (const lv_coord_t *)v.ptr;
}
static inline const lv_coord_t * lv_obj_get_style_grid_column_dsc_array(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_DSC_ARRAY);
return (const lv_coord_t *)v.ptr;
}
static inline lv_grid_align_t lv_obj_get_style_grid_row_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline lv_grid_align_t lv_obj_get_style_grid_column_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_column_pos(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_POS);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_column_span(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_SPAN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_row_pos(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_POS);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_row_span(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_SPAN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_x_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_X_ALIGN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_y_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_Y_ALIGN);
return (lv_coord_t)v.num;
}
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GRID*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GRID_H*/

View File

@ -0,0 +1,44 @@
/**
* @file lv_layouts.h
*
*/
#ifndef LV_LAYOUTS_H
#define LV_LAYOUTS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "flex/lv_flex.h"
#include "grid/lv_grid.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_LAYOUT
# define LV_TRACE_LAYOUT(...) LV_LOG_TRACE( __VA_ARGS__)
#else
# define LV_TRACE_LAYOUT(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LAYOUTS_H*/

View File

@ -0,0 +1,48 @@
/**
* @file lv_extra.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_extra_init(void)
{
#if LV_USE_FLEX
lv_flex_init();
#endif
#if LV_USE_GRID
lv_grid_init();
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,42 @@
/**
* @file lv_extra.h
*
*/
#ifndef LV_EXTRA_H
#define LV_EXTRA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the extra components
*/
void lv_extra_init(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EXTRA_H*/

View File

@ -0,0 +1,424 @@
/**
* @file lv_theme_basic.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h" /*To see all the widgets*/
#if LV_USE_THEME_BASIC
#include "lv_theme_basic.h"
#include "../../../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define COLOR_WHITE lv_color_white()
#define COLOR_LIGHT lv_palette_lighten(LV_PALETTE_GREY, 3)
#define COLOR_MID lv_palette_lighten(LV_PALETTE_GREY, 1)
#define COLOR_DARK lv_palette_main(LV_PALETTE_GREY)
#define COLOR_DIM lv_palette_darken(LV_PALETTE_GREY, 2)
#define PAD_DEF LV_DPX(5)
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_style_t scr;
lv_style_t light;
lv_style_t dark;
lv_style_t scrollbar;
lv_style_t pressed;
lv_style_t disabled;
lv_style_t pad_zero;
#if LV_USE_TEXTAREA
lv_style_t ta_cursor;
#endif
} my_theme_styles_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void style_init_reset(lv_style_t * style);
static void theme_apply(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
static my_theme_styles_t * styles;
static lv_theme_t theme;
static bool inited;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static lv_color_t dark_color_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t c, lv_opa_t opa)
{
LV_UNUSED(f);
return lv_color_darken(c, opa);
}
static lv_color_t grey_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t color, lv_opa_t opa)
{
LV_UNUSED(f);
return lv_color_mix(lv_color_white(), color, opa);
}
static void style_init(void)
{
style_init_reset(&styles->scrollbar);
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scrollbar, lv_palette_darken(LV_PALETTE_GREY, 2));
lv_style_set_width(&styles->scrollbar, PAD_DEF);
style_init_reset(&styles->scr);
lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scr, COLOR_WHITE);
lv_style_set_text_color(&styles->scr, COLOR_DIM);
lv_style_set_pad_row(&styles->scr, PAD_DEF / 2);
lv_style_set_pad_column(&styles->scr, PAD_DEF / 2);
style_init_reset(&styles->light);
lv_style_set_bg_opa(&styles->light, LV_OPA_COVER);
lv_style_set_bg_color(&styles->light, COLOR_LIGHT);
lv_style_set_border_color(&styles->light, COLOR_MID);
lv_style_set_border_width(&styles->light, 1);
lv_style_set_pad_all(&styles->light, PAD_DEF);
lv_style_set_pad_gap(&styles->light, PAD_DEF / 2);
lv_style_set_line_width(&styles->light, LV_DPX(2));
lv_style_set_line_color(&styles->light, COLOR_MID);
lv_style_set_arc_width(&styles->light, LV_DPX(2));
lv_style_set_arc_color(&styles->light, COLOR_MID);
style_init_reset(&styles->dark);
lv_style_set_bg_opa(&styles->dark, LV_OPA_COVER);
lv_style_set_bg_color(&styles->dark, COLOR_DARK);
lv_style_set_border_color(&styles->dark, COLOR_DIM);
lv_style_set_border_width(&styles->dark, 1);
lv_style_set_pad_all(&styles->dark, PAD_DEF);
lv_style_set_pad_gap(&styles->dark, PAD_DEF / 2);
lv_style_set_line_width(&styles->dark, LV_DPX(2));
lv_style_set_line_color(&styles->dark, COLOR_DIM);
lv_style_set_arc_width(&styles->dark, LV_DPX(2));
lv_style_set_arc_color(&styles->dark, COLOR_DIM);
static lv_color_filter_dsc_t dark_filter;
lv_color_filter_dsc_init(&dark_filter, dark_color_filter_cb);
style_init_reset(&styles->pressed);
lv_style_set_color_filter_dsc(&styles->pressed, &dark_filter);
lv_style_set_color_filter_opa(&styles->pressed, 35);
static lv_color_filter_dsc_t grey_filter;
lv_color_filter_dsc_init(&grey_filter, grey_filter_cb);
style_init_reset(&styles->disabled);
lv_style_set_color_filter_dsc(&styles->disabled, &grey_filter);
lv_style_set_color_filter_opa(&styles->disabled, LV_OPA_70);
style_init_reset(&styles->pad_zero);
lv_style_set_pad_all(&styles->pad_zero, 0);
lv_style_set_pad_gap(&styles->pad_zero, 0);
#if LV_USE_TEXTAREA
style_init_reset(&styles->ta_cursor);
lv_style_set_border_side(&styles->ta_cursor, LV_BORDER_SIDE_LEFT);
lv_style_set_border_color(&styles->ta_cursor, COLOR_DIM);
lv_style_set_border_width(&styles->ta_cursor, 2);
lv_style_set_bg_opa(&styles->ta_cursor, LV_OPA_TRANSP);
lv_style_set_anim_time(&styles->ta_cursor, 500);
#endif
}
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_basic_init(lv_disp_t * disp)
{
/*This trick is required only to avoid the garbage collection of
*styles' data if LVGL is used in a binding (e.g. Micropython)
*In a general case styles could be in simple `static lv_style_t my_style...` variables*/
if(!inited) {
LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles);
}
theme.disp = disp;
theme.font_small = LV_FONT_DEFAULT;
theme.font_normal = LV_FONT_DEFAULT;
theme.font_large = LV_FONT_DEFAULT;
theme.apply_cb = theme_apply;
style_init();
inited = true;
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
return (lv_theme_t *)&theme;
}
static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);
if(lv_obj_get_parent(obj) == NULL) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
if(lv_obj_check_type(obj, &lv_obj_class)) {
#if LV_USE_TABVIEW
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Tabview content area*/
if(lv_obj_check_type(parent, &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->light, 0);
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
#if LV_USE_WIN
/*Header*/
if(lv_obj_get_child_id(obj) == 0 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->light, 0);
return;
}
/*Content*/
else if(lv_obj_get_child_id(obj) == 1 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#if LV_USE_BTN
else if(lv_obj_check_type(obj, &lv_btn_class)) {
lv_obj_add_style(obj, &styles->dark, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
}
#endif
#if LV_USE_BTNMATRIX
else if(lv_obj_check_type(obj, &lv_btnmatrix_class)) {
#if LV_USE_MSGBOX
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
return;
}
#endif
#if LV_USE_TABVIEW
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
return;
}
#endif
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
}
#endif
#if LV_USE_BAR
else if(lv_obj_check_type(obj, &lv_bar_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
}
#endif
#if LV_USE_SLIDER
else if(lv_obj_check_type(obj, &lv_slider_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB);
}
#endif
#if LV_USE_TABLE
else if(lv_obj_check_type(obj, &lv_table_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
}
#endif
#if LV_USE_CHECKBOX
else if(lv_obj_check_type(obj, &lv_checkbox_class)) {
lv_obj_add_style(obj, &styles->light, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_INDICATOR | LV_STATE_PRESSED);
}
#endif
#if LV_USE_SWITCH
else if(lv_obj_check_type(obj, &lv_switch_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_KNOB);
}
#endif
#if LV_USE_CHART
else if(lv_obj_check_type(obj, &lv_chart_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->light, LV_PART_TICKS);
lv_obj_add_style(obj, &styles->light, LV_PART_CURSOR);
}
#endif
#if LV_USE_ROLLER
else if(lv_obj_check_type(obj, &lv_roller_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_SELECTED);
}
#endif
#if LV_USE_DROPDOWN
else if(lv_obj_check_type(obj, &lv_dropdown_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
}
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->light, LV_PART_SELECTED);
lv_obj_add_style(obj, &styles->dark, LV_PART_SELECTED | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_SELECTED | LV_STATE_PRESSED);
}
#endif
#if LV_USE_ARC
else if(lv_obj_check_type(obj, &lv_arc_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB);
}
#endif
#if LV_USE_METER
else if(lv_obj_check_type(obj, &lv_meter_class)) {
lv_obj_add_style(obj, &styles->light, 0);
}
#endif
#if LV_USE_TEXTAREA
else if(lv_obj_check_type(obj, &lv_textarea_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR);
lv_obj_add_style(obj, &styles->light, LV_PART_TEXTAREA_PLACEHOLDER);
}
#endif
#if LV_USE_CALENDAR
else if(lv_obj_check_type(obj, &lv_calendar_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
}
#endif
#if LV_USE_KEYBOARD
else if(lv_obj_check_type(obj, &lv_keyboard_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
}
#endif
#if LV_USE_LIST
else if(lv_obj_check_type(obj, &lv_list_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
else if(lv_obj_check_type(obj, &lv_list_text_class)) {
}
else if(lv_obj_check_type(obj, &lv_list_btn_class)) {
lv_obj_add_style(obj, &styles->dark, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
}
#endif
#if LV_USE_MSGBOX
else if(lv_obj_check_type(obj, &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->light, 0);
return;
}
#endif
#if LV_USE_SPINBOX
else if(lv_obj_check_type(obj, &lv_spinbox_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_CURSOR);
}
#endif
#if LV_USE_TILEVIEW
else if(lv_obj_check_type(obj, &lv_tileview_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
else if(lv_obj_check_type(obj, &lv_tileview_tile_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#endif
#if LV_USE_COLORWHEEL
else if(lv_obj_check_type(obj, &lv_colorwheel_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_KNOB);
}
#endif
#if LV_USE_LED
else if(lv_obj_check_type(obj, &lv_led_class)) {
lv_obj_add_style(obj, &styles->light, 0);
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init_reset(lv_style_t * style)
{
if(inited) lv_style_reset(style);
else lv_style_init(style);
}
#endif

View File

@ -0,0 +1,49 @@
/**
* @file lv_theme_basic.h
*
*/
#ifndef LV_THEME_BASIC_H
#define LV_THEME_BASIC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_THEME_BASIC
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the theme
* @param disp pointer to display to attach the theme
* @return a pointer to reference this theme later
*/
lv_theme_t * lv_theme_basic_init(lv_disp_t * disp);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_BASIC_H*/

View File

@ -0,0 +1,979 @@
/**
* @file lv_theme_defau.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h" /*To see all the widgets*/
#if LV_USE_THEME_DEFAULT
#include "lv_theme_default.h"
#include "../../../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define MODE_DARK 1
#define RADIUS_DEFAULT (disp_size == DISP_LARGE ? lv_disp_dpx(theme.disp, 12) : lv_disp_dpx(theme.disp, 8))
/*SCREEN*/
#define LIGHT_COLOR_SCR lv_palette_lighten(LV_PALETTE_GREY, 4)
#define LIGHT_COLOR_CARD lv_color_white()
#define LIGHT_COLOR_TEXT lv_palette_darken(LV_PALETTE_GREY, 4)
#define LIGHT_COLOR_GREY lv_palette_lighten(LV_PALETTE_GREY, 2)
#define DARK_COLOR_SCR lv_color_hex(0x15171A)
#define DARK_COLOR_CARD lv_color_hex(0x282b30)
#define DARK_COLOR_TEXT lv_palette_lighten(LV_PALETTE_GREY, 5)
#define DARK_COLOR_GREY lv_color_hex(0x2f3237)
#define TRANSITION_TIME LV_THEME_DEFAULT_TRANSITON_TIME
#define BORDER_WIDTH lv_disp_dpx(theme.disp, 2)
#define OUTLINE_WIDTH lv_disp_dpx(theme.disp, 3)
#define PAD_DEF (disp_size == DISP_LARGE ? lv_disp_dpx(theme.disp, 24) : disp_size == DISP_MEDIUM ? lv_disp_dpx(theme.disp, 20) : lv_disp_dpx(theme.disp, 16))
#define PAD_SMALL (disp_size == DISP_LARGE ? lv_disp_dpx(theme.disp, 14) : disp_size == DISP_MEDIUM ? lv_disp_dpx(theme.disp, 12) : lv_disp_dpx(theme.disp, 10))
#define PAD_TINY (disp_size == DISP_LARGE ? lv_disp_dpx(theme.disp, 8) : disp_size == DISP_MEDIUM ? lv_disp_dpx(theme.disp, 6) : lv_disp_dpx(theme.disp, 2))
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_style_t scr;
lv_style_t scrollbar;
lv_style_t scrollbar_scrolled;
lv_style_t card;
lv_style_t btn;
/*Utility*/
lv_style_t bg_color_primary;
lv_style_t bg_color_primary_muted;
lv_style_t bg_color_secondary;
lv_style_t bg_color_secondary_muted;
lv_style_t bg_color_grey;
lv_style_t bg_color_white;
lv_style_t pressed;
lv_style_t disabled;
lv_style_t pad_zero;
lv_style_t pad_tiny;
lv_style_t pad_small;
lv_style_t pad_normal;
lv_style_t pad_gap;
lv_style_t line_space_large;
lv_style_t text_align_center;
lv_style_t outline_primary;
lv_style_t outline_secondary;
lv_style_t circle;
lv_style_t no_radius;
lv_style_t clip_corner;
#if LV_THEME_DEFAULT_GROW
lv_style_t grow;
#endif
lv_style_t transition_delayed;
lv_style_t transition_normal;
lv_style_t anim;
/*Parts*/
lv_style_t knob;
lv_style_t indic;
#if LV_USE_ARC
lv_style_t arc_indic;
lv_style_t arc_indic_primary;
#endif
#if LV_USE_CHART
lv_style_t chart_series, chart_indic, chart_ticks, chart_bg;
#endif
#if LV_USE_DROPDOWN
lv_style_t dropdown_list;
#endif
#if LV_USE_CHECKBOX
lv_style_t cb_marker, cb_marker_checked;
#endif
#if LV_USE_SWITCH
lv_style_t switch_knob;
#endif
#if LV_USE_LINE
lv_style_t line;
#endif
#if LV_USE_TABLE
lv_style_t table_cell;
#endif
#if LV_USE_METER
lv_style_t meter_marker, meter_indic;
#endif
#if LV_USE_TEXTAREA
lv_style_t ta_cursor, ta_placeholder;
#endif
#if LV_USE_CALENDAR
lv_style_t calendar_bg, calendar_day;
#endif
#if LV_USE_COLORWHEEL
lv_style_t colorwheel_main;
#endif
#if LV_USE_MSGBOX
lv_style_t msgbox_bg, msgbox_btn_bg;
#endif
#if LV_USE_KEYBOARD
lv_style_t keyboard_btn_bg;
#endif
#if LV_USE_LIST
lv_style_t list_bg, list_btn, list_item_grow, list_label;
#endif
#if LV_USE_TABVIEW
lv_style_t tab_bg_focus, tab_btn;
#endif
#if LV_USE_LED
lv_style_t led;
#endif
} my_theme_styles_t;
typedef struct {
lv_theme_t base;
uint8_t light :1;
}my_theme_t;
typedef enum {
DISP_SMALL = 3,
DISP_MEDIUM = 2,
DISP_LARGE = 1,
}disp_size_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void theme_apply(lv_theme_t * th, lv_obj_t * obj);
static void style_init_reset(lv_style_t * style);
/**********************
* STATIC VARIABLES
**********************/
static my_theme_styles_t * styles;
static lv_theme_t theme;
static disp_size_t disp_size;
static bool inited;
static lv_color_t color_scr;
static lv_color_t color_text;
static lv_color_t color_card;
static lv_color_t color_grey;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static lv_color_t dark_color_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t c, lv_opa_t opa)
{
LV_UNUSED(f);
return lv_color_darken(c, opa);
}
static lv_color_t grey_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t color, lv_opa_t opa)
{
LV_UNUSED(f);
if(theme.flags & MODE_DARK) return lv_color_mix(lv_palette_darken(LV_PALETTE_GREY, 2), color, opa);
else return lv_color_mix(lv_palette_lighten(LV_PALETTE_GREY, 2), color, opa);
}
static void style_init(void)
{
static const lv_style_prop_t trans_props[] = {
LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,
LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_HEIGHT,
LV_STYLE_TRANSLATE_Y, LV_STYLE_TRANSLATE_X,
LV_STYLE_TRANSFORM_ZOOM, LV_STYLE_TRANSFORM_ANGLE,
LV_STYLE_COLOR_FILTER_OPA, LV_STYLE_COLOR_FILTER_DSC,
0
};
color_scr = theme.flags & MODE_DARK ? DARK_COLOR_SCR : LIGHT_COLOR_SCR;
color_text = theme.flags & MODE_DARK ? DARK_COLOR_TEXT : LIGHT_COLOR_TEXT;
color_card = theme.flags & MODE_DARK ? DARK_COLOR_CARD : LIGHT_COLOR_CARD;
color_grey = theme.flags & MODE_DARK ? DARK_COLOR_GREY : LIGHT_COLOR_GREY;
static lv_style_transition_dsc_t trans_delayed;
lv_style_transition_dsc_init(&trans_delayed, trans_props, lv_anim_path_linear, TRANSITION_TIME, 70, NULL);
static lv_style_transition_dsc_t trans_normal;
lv_style_transition_dsc_init(&trans_normal, trans_props, lv_anim_path_linear, TRANSITION_TIME, 0, NULL);
style_init_reset(&styles->transition_delayed);
lv_style_set_transition(&styles->transition_delayed, &trans_delayed); /*Go back to default state with delay*/
style_init_reset(&styles->transition_normal);
lv_style_set_transition(&styles->transition_normal, &trans_normal); /*Go back to default state with delay*/
style_init_reset(&styles->scrollbar);
lv_style_set_bg_color(&styles->scrollbar, (theme.flags & MODE_DARK) ? lv_palette_darken(LV_PALETTE_GREY, 2) : lv_palette_main(LV_PALETTE_GREY));
lv_style_set_radius(&styles->scrollbar, LV_RADIUS_CIRCLE);
lv_style_set_pad_right(&styles->scrollbar, lv_disp_dpx(theme.disp, 7));
lv_style_set_pad_top(&styles->scrollbar, lv_disp_dpx(theme.disp, 7));
lv_style_set_size(&styles->scrollbar, lv_disp_dpx(theme.disp, 5));
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_40);
lv_style_set_transition(&styles->scrollbar, &trans_normal);
style_init_reset(&styles->scrollbar_scrolled);
lv_style_set_bg_opa(&styles->scrollbar_scrolled, LV_OPA_COVER);
style_init_reset(&styles->scr);
lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scr, color_scr);
lv_style_set_text_color(&styles->scr, color_text);
lv_style_set_pad_row(&styles->scr, PAD_SMALL);
lv_style_set_pad_column(&styles->scr, PAD_SMALL);
style_init_reset(&styles->card);
lv_style_set_radius(&styles->card, RADIUS_DEFAULT);
lv_style_set_bg_opa(&styles->card, LV_OPA_COVER);
lv_style_set_bg_color(&styles->card, color_card);
lv_style_set_border_color(&styles->card, color_grey);
lv_style_set_border_width(&styles->card, BORDER_WIDTH);
lv_style_set_border_post(&styles->card, true);
lv_style_set_text_color(&styles->card, color_text);
lv_style_set_pad_all(&styles->card, PAD_DEF);
lv_style_set_pad_row(&styles->card, PAD_SMALL);
lv_style_set_pad_column(&styles->card, PAD_SMALL);
lv_style_set_line_color(&styles->card, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_line_width(&styles->card, lv_disp_dpx(theme.disp, 1));
style_init_reset(&styles->outline_primary);
lv_style_set_outline_color(&styles->outline_primary, theme.color_primary);
lv_style_set_outline_color(&styles->outline_primary, lv_color_hex(0x00FF1E));
lv_style_set_outline_width(&styles->outline_primary, OUTLINE_WIDTH);
lv_style_set_outline_pad(&styles->outline_primary, OUTLINE_WIDTH);
lv_style_set_outline_opa(&styles->outline_primary, LV_OPA_50);
style_init_reset(&styles->outline_secondary);
lv_style_set_outline_color(&styles->outline_secondary, theme.color_secondary);
lv_style_set_outline_width(&styles->outline_secondary, OUTLINE_WIDTH);
lv_style_set_outline_opa(&styles->outline_secondary, LV_OPA_50);
style_init_reset(&styles->btn);
lv_style_set_radius(&styles->btn, (disp_size == DISP_LARGE ? lv_disp_dpx(theme.disp, 16) : disp_size == DISP_MEDIUM ? lv_disp_dpx(theme.disp, 12) : lv_disp_dpx(theme.disp, 8)));
lv_style_set_bg_opa(&styles->btn, LV_OPA_COVER);
lv_style_set_bg_color(&styles->btn, color_grey);
if(!(theme.flags & MODE_DARK)) {
lv_style_set_shadow_color(&styles->btn, lv_palette_lighten(LV_PALETTE_GREY, 3));
lv_style_set_shadow_width(&styles->btn, 1);
lv_style_set_shadow_ofs_y(&styles->btn, lv_disp_dpx(theme.disp, 4));
}
lv_style_set_text_color(&styles->btn, color_text);
lv_style_set_pad_hor(&styles->btn, PAD_DEF);
lv_style_set_pad_ver(&styles->btn, PAD_SMALL);
lv_style_set_pad_column(&styles->btn, lv_disp_dpx(theme.disp, 5));
lv_style_set_pad_row(&styles->btn, lv_disp_dpx(theme.disp, 5));
static lv_color_filter_dsc_t dark_filter;
lv_color_filter_dsc_init(&dark_filter, dark_color_filter_cb);
static lv_color_filter_dsc_t grey_filter;
lv_color_filter_dsc_init(&grey_filter, grey_filter_cb);
style_init_reset(&styles->pressed);
lv_style_set_color_filter_dsc(&styles->pressed, &dark_filter);
lv_style_set_color_filter_opa(&styles->pressed, 35);
style_init_reset(&styles->disabled);
lv_style_set_color_filter_dsc(&styles->disabled, &grey_filter);
lv_style_set_color_filter_opa(&styles->disabled, LV_OPA_50);
style_init_reset(&styles->clip_corner);
lv_style_set_clip_corner(&styles->clip_corner, true);
style_init_reset(&styles->pad_normal);
lv_style_set_pad_all(&styles->pad_normal, PAD_DEF);
lv_style_set_pad_row(&styles->pad_normal, PAD_DEF);
lv_style_set_pad_column(&styles->pad_normal, PAD_DEF);
style_init_reset(&styles->pad_small);
lv_style_set_pad_all(&styles->pad_small, PAD_SMALL);
lv_style_set_pad_gap(&styles->pad_small, PAD_SMALL);
style_init_reset(&styles->pad_gap);
lv_style_set_pad_row(&styles->pad_gap, lv_disp_dpx(theme.disp, 10));
lv_style_set_pad_column(&styles->pad_gap, lv_disp_dpx(theme.disp, 10));
style_init_reset(&styles->line_space_large);
lv_style_set_text_line_space(&styles->line_space_large, lv_disp_dpx(theme.disp, 20));
style_init_reset(&styles->text_align_center);
lv_style_set_text_align(&styles->text_align_center, LV_TEXT_ALIGN_CENTER);
style_init_reset(&styles->pad_zero);
lv_style_set_pad_all(&styles->pad_zero, 0);
lv_style_set_pad_row(&styles->pad_zero, 0);
lv_style_set_pad_column(&styles->pad_zero, 0);
style_init_reset(&styles->pad_tiny);
lv_style_set_pad_all(&styles->pad_tiny, PAD_TINY);
lv_style_set_pad_row(&styles->pad_tiny, PAD_TINY);
lv_style_set_pad_column(&styles->pad_tiny, PAD_TINY);
style_init_reset(&styles->bg_color_primary);
lv_style_set_bg_color(&styles->bg_color_primary, theme.color_primary);
lv_style_set_text_color(&styles->bg_color_primary, lv_color_white());
lv_style_set_bg_opa(&styles->bg_color_primary, LV_OPA_COVER);
style_init_reset(&styles->bg_color_primary_muted);
lv_style_set_bg_color(&styles->bg_color_primary_muted, theme.color_primary);
lv_style_set_text_color(&styles->bg_color_primary_muted, theme.color_primary);
lv_style_set_bg_opa(&styles->bg_color_primary_muted, LV_OPA_20);
style_init_reset(&styles->bg_color_secondary);
lv_style_set_bg_color(&styles->bg_color_secondary, theme.color_secondary);
lv_style_set_text_color(&styles->bg_color_secondary, lv_color_white());
lv_style_set_bg_opa(&styles->bg_color_secondary, LV_OPA_COVER);
style_init_reset(&styles->bg_color_secondary_muted);
lv_style_set_bg_color(&styles->bg_color_secondary_muted, theme.color_secondary);
lv_style_set_text_color(&styles->bg_color_secondary_muted, theme.color_secondary);
lv_style_set_bg_opa(&styles->bg_color_secondary_muted, LV_OPA_20);
style_init_reset(&styles->bg_color_grey);
lv_style_set_bg_color(&styles->bg_color_grey, color_grey);
lv_style_set_bg_opa(&styles->bg_color_grey, LV_OPA_COVER);
lv_style_set_text_color(&styles->bg_color_grey, color_text);
style_init_reset(&styles->bg_color_white);
lv_style_set_bg_color(&styles->bg_color_white, color_card);
lv_style_set_bg_opa(&styles->bg_color_white, LV_OPA_COVER);
lv_style_set_text_color(&styles->bg_color_white, color_text);
style_init_reset(&styles->circle);
lv_style_set_radius(&styles->circle, LV_RADIUS_CIRCLE);
style_init_reset(&styles->no_radius);
lv_style_set_radius(&styles->no_radius, 0);
#if LV_THEME_DEFAULT_GROW
style_init_reset(&styles->grow);
lv_style_set_transform_width(&styles->grow, lv_disp_dpx(theme.disp, 3));
lv_style_set_transform_height(&styles->grow, lv_disp_dpx(theme.disp, 3));
#endif
style_init_reset(&styles->knob);
lv_style_set_bg_color(&styles->knob, theme.color_primary);
lv_style_set_bg_opa(&styles->knob, LV_OPA_COVER);
lv_style_set_pad_all(&styles->knob, lv_disp_dpx(theme.disp, 6));
lv_style_set_radius(&styles->knob, LV_RADIUS_CIRCLE);
style_init_reset(&styles->anim);
lv_style_set_anim_time(&styles->anim, 200);
#if LV_USE_ARC
style_init_reset(&styles->arc_indic);
lv_style_set_arc_color(&styles->arc_indic, color_grey);
lv_style_set_arc_width(&styles->arc_indic, lv_disp_dpx(theme.disp, 15));
lv_style_set_arc_rounded(&styles->arc_indic, true);
style_init_reset(&styles->arc_indic_primary);
lv_style_set_arc_color(&styles->arc_indic_primary, theme.color_primary);
#endif
#if LV_USE_DROPDOWN
style_init_reset(&styles->dropdown_list);
lv_style_set_max_height(&styles->dropdown_list, LV_DPI_DEF * 2);
#endif
#if LV_USE_CHECKBOX
style_init_reset(&styles->cb_marker);
lv_style_set_pad_all(&styles->cb_marker, lv_disp_dpx(theme.disp, 3));
lv_style_set_border_width(&styles->cb_marker, BORDER_WIDTH);
lv_style_set_border_color(&styles->cb_marker, theme.color_primary);
lv_style_set_bg_color(&styles->cb_marker, color_card);
lv_style_set_bg_opa(&styles->cb_marker, LV_OPA_COVER);
lv_style_set_radius(&styles->cb_marker, RADIUS_DEFAULT / 2);
style_init_reset(&styles->cb_marker_checked);
lv_style_set_bg_img_src(&styles->cb_marker_checked, LV_SYMBOL_OK);
lv_style_set_text_color(&styles->cb_marker_checked, lv_color_white());
lv_style_set_text_font(&styles->cb_marker_checked, theme.font_small);
#endif
#if LV_USE_SWITCH
style_init_reset(&styles->switch_knob);
lv_style_set_pad_all(&styles->switch_knob, - lv_disp_dpx(theme.disp, 4));
lv_style_set_bg_color(&styles->switch_knob, lv_color_white());
#endif
#if LV_USE_LINE
style_init_reset(&styles->line);
lv_style_set_line_width(&styles->line, 1);
lv_style_set_line_color(&styles->line, color_text);
#endif
#if LV_USE_CHART
style_init_reset(&styles->chart_bg);
lv_style_set_border_post(&styles->chart_bg, false);
lv_style_set_pad_column(&styles->chart_bg, lv_disp_dpx(theme.disp, 10));
lv_style_set_line_color(&styles->chart_bg, color_grey);
style_init_reset(&styles->chart_series);
lv_style_set_line_width(&styles->chart_series, lv_disp_dpx(theme.disp, 3));
lv_style_set_radius(&styles->chart_series, lv_disp_dpx(theme.disp, 3));
lv_style_set_size(&styles->chart_series, lv_disp_dpx(theme.disp, 8));
lv_style_set_pad_column(&styles->chart_series, lv_disp_dpx(theme.disp, 2));
style_init_reset(&styles->chart_indic);
lv_style_set_radius(&styles->chart_indic,LV_RADIUS_CIRCLE);
lv_style_set_size(&styles->chart_indic, lv_disp_dpx(theme.disp, 8));
lv_style_set_bg_color(&styles->chart_indic, theme.color_primary);
lv_style_set_bg_opa(&styles->chart_indic, LV_OPA_COVER);
style_init_reset(&styles->chart_ticks);
lv_style_set_line_width(&styles->chart_ticks, lv_disp_dpx(theme.disp, 1));
lv_style_set_line_color(&styles->chart_ticks, color_text);
lv_style_set_pad_all(&styles->chart_ticks, lv_disp_dpx(theme.disp, 2));
lv_style_set_text_color(&styles->chart_ticks, lv_palette_main(LV_PALETTE_GREY));
#endif
#if LV_USE_METER
style_init_reset(&styles->meter_marker);
lv_style_set_line_width(&styles->meter_marker, lv_disp_dpx(theme.disp, 5));
lv_style_set_line_color(&styles->meter_marker, color_text);
lv_style_set_size(&styles->meter_marker, lv_disp_dpx(theme.disp, 20));
lv_style_set_pad_left(&styles->meter_marker, lv_disp_dpx(theme.disp, 15));
style_init_reset(&styles->meter_indic);
lv_style_set_radius(&styles->meter_indic, LV_RADIUS_CIRCLE);
lv_style_set_bg_color(&styles->meter_indic, color_text);
lv_style_set_bg_opa(&styles->meter_indic, LV_OPA_COVER);
lv_style_set_size(&styles->meter_indic, lv_disp_dpx(theme.disp, 15));
#endif
#if LV_USE_TABLE
style_init_reset(&styles->table_cell);
lv_style_set_border_width(&styles->table_cell, lv_disp_dpx(theme.disp, 1));
lv_style_set_border_color(&styles->table_cell, color_grey);
lv_style_set_border_side(&styles->table_cell, LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_BOTTOM );
#endif
#if LV_USE_TEXTAREA
style_init_reset(&styles->ta_cursor);
lv_style_set_border_color(&styles->ta_cursor, color_text);
lv_style_set_border_width(&styles->ta_cursor, lv_disp_dpx(theme.disp, 2));
lv_style_set_pad_left(&styles->ta_cursor, lv_disp_dpx(theme.disp, 1));
lv_style_set_border_side(&styles->ta_cursor, LV_BORDER_SIDE_LEFT);
lv_style_set_anim_time(&styles->ta_cursor, 400);
style_init_reset(&styles->ta_placeholder);
lv_style_set_text_color(&styles->ta_placeholder, (theme.flags & MODE_DARK) ? lv_palette_darken(LV_PALETTE_GREY, 2) : lv_palette_lighten(LV_PALETTE_GREY, 1));
#endif
#if LV_USE_CALENDAR
style_init_reset(&styles->calendar_bg);
lv_style_set_pad_all(&styles->calendar_bg, PAD_SMALL);
lv_style_set_pad_gap(&styles->calendar_bg, PAD_SMALL / 2);
lv_style_set_radius(&styles->calendar_bg, 0);
style_init_reset(&styles->calendar_day);
lv_style_set_border_width(&styles->calendar_day, lv_disp_dpx(theme.disp, 1));
lv_style_set_border_color(&styles->calendar_day, color_grey);
lv_style_set_bg_color(&styles->calendar_day, color_card);
lv_style_set_bg_opa(&styles->calendar_day, LV_OPA_20);
#endif
#if LV_USE_COLORWHEEL
style_init_reset(&styles->colorwheel_main);
lv_style_set_arc_width(&styles->colorwheel_main, lv_disp_dpx(theme.disp, 10));
#endif
#if LV_USE_MSGBOX
/*To add space for for the button shadow*/
style_init_reset(&styles->msgbox_btn_bg);
lv_style_set_pad_all(&styles->msgbox_btn_bg, lv_disp_dpx(theme.disp, 4));
style_init_reset(&styles->msgbox_bg);
lv_style_set_max_width(&styles->msgbox_bg, lv_pct(100));
#endif
#if LV_USE_KEYBOARD
style_init_reset(&styles->keyboard_btn_bg);
lv_style_set_shadow_width(&styles->keyboard_btn_bg, 0);
lv_style_set_radius(&styles->keyboard_btn_bg, disp_size == DISP_SMALL ? RADIUS_DEFAULT / 2 : RADIUS_DEFAULT);
#endif
#if LV_USE_TABVIEW
style_init_reset(&styles->tab_btn);
lv_style_set_border_color(&styles->tab_btn, theme.color_primary);
lv_style_set_border_width(&styles->tab_btn, BORDER_WIDTH * 2);
lv_style_set_border_side(&styles->tab_btn, LV_BORDER_SIDE_BOTTOM);
style_init_reset(&styles->tab_bg_focus);
lv_style_set_outline_pad(&styles->tab_bg_focus, -BORDER_WIDTH);
#endif
#if LV_USE_LIST
style_init_reset(&styles->list_bg);
lv_style_set_pad_hor(&styles->list_bg, PAD_DEF);
lv_style_set_pad_ver(&styles->list_bg, 0);
lv_style_set_pad_gap(&styles->list_bg, 0);
lv_style_set_clip_corner(&styles->list_bg, true);
style_init_reset(&styles->list_btn);
lv_style_set_border_width(&styles->list_btn, lv_disp_dpx(theme.disp, 1));
lv_style_set_border_color(&styles->list_btn, color_grey);
lv_style_set_border_side(&styles->list_btn, LV_BORDER_SIDE_BOTTOM);
lv_style_set_pad_all(&styles->list_btn, PAD_SMALL);
lv_style_set_pad_column(&styles->list_btn, PAD_SMALL);
style_init_reset(&styles->list_item_grow);
lv_style_set_transform_width(&styles->list_item_grow, PAD_DEF);
#endif
#if LV_USE_LED
style_init_reset(&styles->led);
lv_style_set_bg_opa(&styles->led, LV_OPA_COVER);
lv_style_set_bg_color(&styles->led, lv_color_white());
lv_style_set_bg_grad_color(&styles->led, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_radius(&styles->led, LV_RADIUS_CIRCLE);
lv_style_set_shadow_width(&styles->led, lv_disp_dpx(theme.disp, 15));
lv_style_set_shadow_color(&styles->led, lv_color_white());
lv_style_set_shadow_spread(&styles->led, lv_disp_dpx(theme.disp, 5));
#endif
}
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, lv_color_t color_secondary, bool dark, const lv_font_t * font)
{
/*This trick is required only to avoid the garbage collection of
*styles' data if LVGL is used in a binding (e.g. Micropython)
*In a general case styles could be in simple `static lv_style_t my_style...` variables*/
if(!inited) {
LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles);
}
if(LV_HOR_RES <= 320) disp_size = DISP_SMALL;
else if(LV_HOR_RES < 720) disp_size = DISP_MEDIUM;
else disp_size = DISP_LARGE;
theme.disp = disp;
theme.color_primary = color_primary;
theme.color_secondary = color_secondary;
theme.font_small = font;
theme.font_normal = font;
theme.font_large = font;
theme.apply_cb = theme_apply;
theme.flags = dark ? MODE_DARK : 0;
style_init();
inited = true;
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
return (lv_theme_t *)&theme;
}
bool lv_theme_default_is_inited(void)
{
return inited;
}
static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);
if(lv_obj_get_parent(obj) == NULL) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
return;
}
if(lv_obj_check_type(obj, &lv_obj_class)) {
#if LV_USE_TABVIEW
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Tabview content area*/
if(lv_obj_check_type(parent, &lv_tabview_class)) {
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->pad_normal, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
return;
}
#endif
#if LV_USE_WIN
/*Header*/
if(lv_obj_get_child_id(obj) == 0 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->bg_color_grey, 0);
lv_obj_add_style(obj, &styles->pad_tiny, 0);
return;
}
/*Content*/
else if(lv_obj_get_child_id(obj) == 1 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->pad_normal, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
return;
}
#endif
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
}
#if LV_USE_BTN
else if(lv_obj_check_type(obj, &lv_btn_class)) {
lv_obj_add_style(obj, &styles->btn, 0);
lv_obj_add_style(obj, &styles->bg_color_primary, 0);
lv_obj_add_style(obj, &styles->transition_delayed, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->transition_normal, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
#if LV_THEME_DEFAULT_GROW
lv_obj_add_style(obj, &styles->grow, LV_STATE_PRESSED);
#endif
lv_obj_add_style(obj, &styles->bg_color_secondary, LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
}
#endif
#if LV_USE_LINE
else if(lv_obj_check_type(obj, &lv_line_class)) {
lv_obj_add_style(obj, &styles->line, 0);
}
#endif
#if LV_USE_BTNMATRIX
else if(lv_obj_check_type(obj, &lv_btnmatrix_class)) {
#if LV_USE_MSGBOX
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->msgbox_btn_bg, 0);
lv_obj_add_style(obj, &styles->pad_gap, 0);
lv_obj_add_style(obj, &styles->btn, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->bg_color_primary_muted, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->bg_color_secondary_muted, LV_PART_ITEMS | LV_STATE_EDITED);
return;
}
#endif
#if LV_USE_TABVIEW
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->bg_color_white, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->tab_bg_focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->bg_color_primary_muted, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->tab_btn, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->outline_primary, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_PART_ITEMS | LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->tab_bg_focus, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
return;
}
#endif
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->btn, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->outline_primary, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_PART_ITEMS | LV_STATE_EDITED);
}
#endif
#if LV_USE_BAR
else if(lv_obj_check_type(obj, &lv_bar_class)) {
lv_obj_add_style(obj, &styles->bg_color_primary_muted, 0);
lv_obj_add_style(obj, &styles->circle, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->circle, LV_PART_INDICATOR);
}
#endif
#if LV_USE_SLIDER
else if(lv_obj_check_type(obj, &lv_slider_class)) {
lv_obj_add_style(obj, &styles->bg_color_primary_muted, 0);
lv_obj_add_style(obj, &styles->circle, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->circle, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->knob, LV_PART_KNOB);
#if LV_THEME_DEFAULT_GROW
lv_obj_add_style(obj, &styles->grow, LV_PART_KNOB | LV_STATE_PRESSED);
#endif
lv_obj_add_style(obj, &styles->transition_delayed, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->transition_normal, LV_PART_KNOB | LV_STATE_PRESSED);
}
#endif
#if LV_USE_TABLE
else if(lv_obj_check_type(obj, &lv_table_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
lv_obj_add_style(obj, &styles->bg_color_white, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->table_cell, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pad_normal, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->bg_color_secondary, LV_PART_ITEMS | LV_STATE_EDITED);
}
#endif
#if LV_USE_CHECKBOX
else if(lv_obj_check_type(obj, &lv_checkbox_class)) {
lv_obj_add_style(obj, &styles->pad_gap, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->cb_marker, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->cb_marker_checked, LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_INDICATOR | LV_STATE_PRESSED);
#if LV_THEME_DEFAULT_GROW
lv_obj_add_style(obj, &styles->grow, LV_PART_INDICATOR | LV_STATE_PRESSED);
#endif
lv_obj_add_style(obj, &styles->transition_normal, LV_PART_INDICATOR | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->transition_delayed, LV_PART_INDICATOR);
}
#endif
#if LV_USE_SWITCH
else if(lv_obj_check_type(obj, &lv_switch_class)) {
lv_obj_add_style(obj, &styles->bg_color_grey, 0);
lv_obj_add_style(obj, &styles->circle, 0);
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->circle, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->knob, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->bg_color_white, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->switch_knob, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->disabled, LV_PART_KNOB | LV_STATE_DISABLED);
}
#endif
#if LV_USE_CHART
else if(lv_obj_check_type(obj, &lv_chart_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_small, 0);
lv_obj_add_style(obj, &styles->chart_bg, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
lv_obj_add_style(obj, &styles->chart_series, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->chart_indic, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->chart_ticks, LV_PART_TICKS);
lv_obj_add_style(obj, &styles->chart_series, LV_PART_CURSOR);
}
#endif
#if LV_USE_ROLLER
else if(lv_obj_check_type(obj, &lv_roller_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->anim, 0);
lv_obj_add_style(obj, &styles->line_space_large, 0);
lv_obj_add_style(obj, &styles->text_align_center, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_SELECTED);
}
#endif
#if LV_USE_DROPDOWN
else if(lv_obj_check_type(obj, &lv_dropdown_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_small, 0);
lv_obj_add_style(obj, &styles->transition_delayed, 0);
lv_obj_add_style(obj, &styles->transition_normal, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->transition_normal, LV_PART_INDICATOR);
}
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->clip_corner, 0);
lv_obj_add_style(obj, &styles->line_space_large, 0);
lv_obj_add_style(obj, &styles->dropdown_list, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
lv_obj_add_style(obj, &styles->bg_color_white, LV_PART_SELECTED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_SELECTED | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_SELECTED | LV_STATE_PRESSED);
}
#endif
#if LV_USE_ARC
else if(lv_obj_check_type(obj, &lv_arc_class)) {
lv_obj_add_style(obj, &styles->arc_indic, 0);
lv_obj_add_style(obj, &styles->arc_indic, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->arc_indic_primary, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->knob, LV_PART_KNOB);
}
#endif
#if LV_USE_METER
else if(lv_obj_check_type(obj, &lv_meter_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->circle, 0);
lv_obj_add_style(obj, &styles->meter_indic, LV_PART_INDICATOR);
}
#endif
#if LV_USE_TEXTAREA
else if(lv_obj_check_type(obj, &lv_textarea_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_small, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR | LV_STATE_FOCUSED);
lv_obj_add_style(obj, &styles->ta_placeholder, LV_PART_TEXTAREA_PLACEHOLDER);
}
#endif
#if LV_USE_CALENDAR
else if(lv_obj_check_type(obj, &lv_calendar_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->calendar_bg, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->calendar_day, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->outline_primary, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_PART_ITEMS | LV_STATE_EDITED);
}
#endif
#if LV_USE_KEYBOARD
else if(lv_obj_check_type(obj, &lv_keyboard_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, disp_size == DISP_LARGE ? &styles->pad_small : &styles->pad_tiny, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->btn, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->bg_color_white, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->keyboard_btn_bg, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->bg_color_grey, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->outline_primary, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_PART_ITEMS | LV_STATE_EDITED);
}
#endif
#if LV_USE_LIST
else if(lv_obj_check_type(obj, &lv_list_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->list_bg, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
return;
}
else if(lv_obj_check_type(obj, &lv_list_text_class)) {
lv_obj_add_style(obj, &styles->bg_color_grey, 0);
lv_obj_add_style(obj, &styles->list_item_grow, 0);
}
else if(lv_obj_check_type(obj, &lv_list_btn_class)) {
lv_obj_add_style(obj, &styles->bg_color_white, 0);
lv_obj_add_style(obj, &styles->list_btn, 0);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->list_item_grow, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->list_item_grow, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
}
#endif
#if LV_USE_MSGBOX
else if(lv_obj_check_type(obj, &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->msgbox_bg, 0);
return;
}
#endif
#if LV_USE_SPINBOX
else if(lv_obj_check_type(obj, &lv_spinbox_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_small, 0);
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_CURSOR);
}
#endif
#if LV_USE_TILEVIEW
else if(lv_obj_check_type(obj, &lv_tileview_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
}
else if(lv_obj_check_type(obj, &lv_tileview_tile_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
}
#endif
#if LV_USE_TABVIEW
if(lv_obj_check_type(obj, &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
return;
}
#endif
#if LV_USE_COLORWHEEL
else if(lv_obj_check_type(obj, &lv_colorwheel_class)) {
lv_obj_add_style(obj, &styles->colorwheel_main, 0);
lv_obj_add_style(obj, &styles->pad_normal, 0);
lv_obj_add_style(obj, &styles->bg_color_white, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->pad_normal, LV_PART_KNOB);
}
#endif
#if LV_USE_LED
else if(lv_obj_check_type(obj, &lv_led_class)) {
lv_obj_add_style(obj, &styles->led, 0);
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init_reset(lv_style_t * style)
{
if(inited) lv_style_reset(style);
else lv_style_init(style);
}
#endif

View File

@ -0,0 +1,54 @@
/**
* @file lv_theme_default.h
*
*/
#ifndef LV_THEME_DEFAULT_H
#define LV_THEME_DEFAULT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_THEME_DEFAULT
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the theme
* @param color_primary the primary color of the theme
* @param color_secondary the secondary color for the theme
* @param font pointer to a font to use.
* @return a pointer to reference this theme later
*/
lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, lv_color_t color_secondary, bool dark, const lv_font_t * font);
bool lv_theme_default_is_inited(void);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_DEFAULT_H*/

View File

@ -0,0 +1,40 @@
/**
* @file lv_themes.h
*
*/
#ifndef LV_THEMES_H
#define LV_THEMES_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "default/lv_theme_default.h"
#include "mono/lv_theme_mono.h"
#include "basic/lv_theme_basic.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEMES_H*/

View File

@ -0,0 +1,494 @@
/**
* @file lv_theme_mono.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_THEME_MONO
#include "lv_theme_mono.h"
#include "../../../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define COLOR_FG dark_bg ? lv_color_white() : lv_color_black()
#define COLOR_BG dark_bg ? lv_color_black() : lv_color_white()
#define BORDER_W_NORMAL 1
#define BORDER_W_PR 3
#define BORDER_W_DIS 0
#define BORDER_W_FOCUS 1
#define BORDER_W_EDIT 2
#define PAD_DEF 4
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_style_t scr;
lv_style_t card;
lv_style_t scrollbar;
lv_style_t btn;
lv_style_t pr;
lv_style_t inv;
lv_style_t disabled;
lv_style_t focus;
lv_style_t edit;
lv_style_t pad_gap;
lv_style_t pad_zero;
lv_style_t no_radius;
lv_style_t radius_circle;
lv_style_t large_border;
lv_style_t large_line_space;
lv_style_t underline;
#if LV_USE_TEXTAREA
lv_style_t ta_cursor;
#endif
} my_theme_styles_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void style_init_reset(lv_style_t * style);
static void theme_apply(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
static my_theme_styles_t * styles;
static lv_theme_t theme;
static bool inited;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init(bool dark_bg, const lv_font_t * font)
{
style_init_reset(&styles->scrollbar);
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scrollbar,COLOR_FG);
lv_style_set_width(&styles->scrollbar, PAD_DEF);
style_init_reset(&styles->scr);
lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scr, COLOR_BG);
lv_style_set_text_color(&styles->scr, COLOR_FG);
lv_style_set_pad_row(&styles->scr, PAD_DEF);
lv_style_set_pad_column(&styles->scr, PAD_DEF);
lv_style_set_text_font(&styles->scr, font);
style_init_reset(&styles->card);
lv_style_set_bg_opa(&styles->card, LV_OPA_COVER);
lv_style_set_bg_color(&styles->card, COLOR_BG);
lv_style_set_border_color(&styles->card, COLOR_FG);
lv_style_set_radius(&styles->card, 2);
lv_style_set_border_width(&styles->card, BORDER_W_NORMAL);
lv_style_set_pad_all(&styles->card, PAD_DEF);
lv_style_set_pad_gap(&styles->card, PAD_DEF);
lv_style_set_text_color(&styles->card, COLOR_FG);
lv_style_set_line_width(&styles->card, 2);
lv_style_set_line_color(&styles->card, COLOR_FG);
lv_style_set_arc_width(&styles->card, 2);
lv_style_set_arc_color(&styles->card, COLOR_FG);
lv_style_set_outline_color(&styles->card, COLOR_FG);
lv_style_set_anim_time(&styles->card, 300);
style_init_reset(&styles->pr);
lv_style_set_border_width(&styles->pr, BORDER_W_PR);
style_init_reset(&styles->inv);
lv_style_set_bg_opa(&styles->inv, LV_OPA_COVER);
lv_style_set_bg_color(&styles->inv, COLOR_FG);
lv_style_set_border_color(&styles->inv, COLOR_BG);
lv_style_set_line_color(&styles->inv, COLOR_BG);
lv_style_set_arc_color(&styles->inv, COLOR_BG);
lv_style_set_text_color(&styles->inv, COLOR_BG);
lv_style_set_outline_color(&styles->inv, COLOR_BG);
style_init_reset(&styles->disabled);
lv_style_set_border_width(&styles->disabled, BORDER_W_DIS);
style_init_reset(&styles->focus);
lv_style_set_outline_width(&styles->focus, 1);
lv_style_set_outline_pad(&styles->focus, BORDER_W_FOCUS);
style_init_reset(&styles->edit);
lv_style_set_outline_width(&styles->edit, BORDER_W_EDIT);
style_init_reset(&styles->large_border);
lv_style_set_border_width(&styles->large_border, BORDER_W_EDIT);
style_init_reset(&styles->pad_gap);
lv_style_set_pad_gap(&styles->pad_gap, PAD_DEF);
style_init_reset(&styles->pad_zero);
lv_style_set_pad_all(&styles->pad_zero, 0);
lv_style_set_pad_gap(&styles->pad_zero, 0);
style_init_reset(&styles->no_radius);
lv_style_set_radius(&styles->no_radius, 0);
style_init_reset(&styles->radius_circle);
lv_style_set_radius(&styles->radius_circle, LV_RADIUS_CIRCLE);
style_init_reset(&styles->large_line_space);
lv_style_set_text_line_space(&styles->large_line_space, 6);
style_init_reset(&styles->underline);
lv_style_set_text_decor(&styles->underline, LV_TEXT_DECOR_UNDERLINE);
#if LV_USE_TEXTAREA
style_init_reset(&styles->ta_cursor);
lv_style_set_border_side(&styles->ta_cursor, LV_BORDER_SIDE_LEFT);
lv_style_set_border_color(&styles->ta_cursor, COLOR_FG);
lv_style_set_border_width(&styles->ta_cursor, 2);
lv_style_set_bg_opa(&styles->ta_cursor, LV_OPA_TRANSP);
lv_style_set_anim_time(&styles->ta_cursor, 500);
#endif
}
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font)
{
/*This trick is required only to avoid the garbage collection of
*styles' data if LVGL is used in a binding (e.g. Micropython)
*In a general case styles could be in simple `static lv_style_t my_style...` variables*/
if(!inited) {
LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles);
}
theme.disp = disp;
theme.font_small = LV_FONT_DEFAULT;
theme.font_normal = LV_FONT_DEFAULT;
theme.font_large = LV_FONT_DEFAULT;
theme.apply_cb = theme_apply;
style_init(dark_bg, font);
inited = true;
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
return (lv_theme_t *)&theme;
}
static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);
if(lv_obj_get_parent(obj) == NULL) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
if(lv_obj_check_type(obj, &lv_obj_class)) {
#if LV_USE_TABVIEW
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Tabview content area*/
if(lv_obj_check_type(parent, &lv_tabview_class)) {
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
#if LV_USE_WIN
/*Header*/
if(lv_obj_get_child_id(obj) == 0 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
return;
}
/*Content*/
else if(lv_obj_get_child_id(obj) == 1 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#if LV_USE_BTN
else if(lv_obj_check_type(obj, &lv_btn_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pr, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_BTNMATRIX
else if(lv_obj_check_type(obj, &lv_btnmatrix_class)) {
#if LV_USE_MSGBOX
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->pad_gap, 0);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->underline, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
return;
}
#endif
#if LV_USE_TABVIEW
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->pad_gap, 0);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->underline, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
return;
}
#endif
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->underline, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_BAR
else if(lv_obj_check_type(obj, &lv_bar_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_SLIDER
else if(lv_obj_check_type(obj, &lv_slider_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->card, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_TABLE
else if(lv_obj_check_type(obj, &lv_table_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->no_radius, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_CHECKBOX
else if(lv_obj_check_type(obj, &lv_checkbox_class)) {
lv_obj_add_style(obj, &styles->pad_gap, LV_PART_MAIN);
lv_obj_add_style(obj, &styles->card, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pr, LV_PART_INDICATOR | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_SWITCH
else if(lv_obj_check_type(obj, &lv_switch_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->radius_circle, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->card, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_CHART
else if(lv_obj_check_type(obj, &lv_chart_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->card, LV_PART_TICKS);
lv_obj_add_style(obj, &styles->card, LV_PART_CURSOR);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_ROLLER
else if(lv_obj_check_type(obj, &lv_roller_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->large_line_space, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_SELECTED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_DROPDOWN
else if(lv_obj_check_type(obj, &lv_dropdown_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pr, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->large_line_space, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->inv, LV_PART_SELECTED | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pr, LV_PART_SELECTED | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_ARC
else if(lv_obj_check_type(obj, &lv_arc_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->card, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_METER
else if(lv_obj_check_type(obj, &lv_meter_class)) {
lv_obj_add_style(obj, &styles->card, 0);
}
#endif
#if LV_USE_TEXTAREA
else if(lv_obj_check_type(obj, &lv_textarea_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR | LV_STATE_FOCUSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUSED);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_CALENDAR
else if(lv_obj_check_type(obj, &lv_calendar_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_KEYBOARD
else if(lv_obj_check_type(obj, &lv_keyboard_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_EDITED);
}
#endif
#if LV_USE_LIST
else if(lv_obj_check_type(obj, &lv_list_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
else if(lv_obj_check_type(obj, &lv_list_text_class)) {
}
else if(lv_obj_check_type(obj, &lv_list_btn_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pr, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_STATE_EDITED);
}
#endif
#if LV_USE_MSGBOX
else if(lv_obj_check_type(obj, &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->card, 0);
return;
}
#endif
#if LV_USE_SPINBOX
else if(lv_obj_check_type(obj, &lv_spinbox_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_CURSOR);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_TILEVIEW
else if(lv_obj_check_type(obj, &lv_tileview_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
else if(lv_obj_check_type(obj, &lv_tileview_tile_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#endif
#if LV_USE_LED
else if(lv_obj_check_type(obj, &lv_led_class)) {
lv_obj_add_style(obj, &styles->card, 0);
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init_reset(lv_style_t * style)
{
if(inited) lv_style_reset(style);
else lv_style_init(style);
}
#endif

View File

@ -0,0 +1,51 @@
/**
* @file lv_theme_mono.h
*
*/
#ifndef LV_USE_THEME_MONO_H
#define LV_USE_THEME_MONO_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_THEME_MONO
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the theme
* @param color_primary the primary color of the theme
* @param color_secondary the secondary color for the theme
* @param font pointer to a font to use.
* @return a pointer to reference this theme later
*/
lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_DEFAULT_H*/

View File

@ -0,0 +1,143 @@
/**
* @file lv_animimg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_animimg.h"
#if LV_USE_ANIMIMG != 0
/*Testing of dependencies*/
#if LV_USE_IMG == 0
#error "lv_animimg: lv_img is required. Enable it in lv_conf.h (LV_USE_IMG 1) "
#endif
#include "../../../misc/lv_assert.h"
#include "../../../draw/lv_img_decoder.h"
#include "../../../misc/lv_fs.h"
#include "../../../misc/lv_txt.h"
#include "../../../misc/lv_math.h"
#include "../../../misc/lv_log.h"
#include "../../../misc/lv_anim.h"
/*********************
* DEFINES
*********************/
#define LV_OBJX_NAME "lv_animimg"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void index_change(lv_obj_t * obj, int32_t index);
static void lv_animimg_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static uint8_t animimg_end_flag=0;
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_animimg_class = {
.constructor_cb = lv_animimg_constructor,
.instance_size = sizeof(lv_animimg_t),
.base_class = &lv_img_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_animimg_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_animimg_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_animimg_set_src(lv_obj_t * obj, lv_img_dsc_t * dsc[], uint8_t num)
{
lv_animimg_t * animimg = (lv_animimg_t *)obj;
animimg->dsc = dsc;
animimg->pic_count = num;
lv_anim_set_values(&animimg->anim, 0 , num);
}
void lv_animimg_start(lv_obj_t * obj)
{
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_start(&animimg->anim);
}
/*=====================
* Setter functions
*====================*/
void lv_animimg_set_duration(lv_obj_t * obj, uint32_t duration)
{
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_set_time(&animimg->anim, duration);
lv_anim_set_playback_delay(&animimg->anim, duration);
}
void lv_animimg_set_repeat_count(lv_obj_t * obj, uint16_t count)
{
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_set_repeat_count(&animimg->anim, count);
}
/*=====================
* Getter functions
*====================*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_animimg_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
animimg->dsc = NULL;
animimg->pic_count = -1;
//initial animation
lv_anim_init(&animimg->anim);
lv_anim_set_var(&animimg->anim, obj);
lv_anim_set_time(&animimg->anim, 30);
lv_anim_set_exec_cb(&animimg->anim, (lv_anim_exec_xcb_t)index_change);
lv_anim_set_values(&animimg->anim, 0 , 1);
lv_anim_set_repeat_count(&animimg->anim, LV_ANIM_REPEAT_INFINITE);
}
static void index_change(lv_obj_t * obj, int32_t index)
{
lv_coord_t idx;
lv_animimg_t * animimg = (lv_animimg_t *)obj;
idx = index % animimg->pic_count;
lv_img_set_src(obj, animimg->dsc[idx]);
if(idx==0)
{
animimg_end_flag++;
}
}
uint8_t get_animimg_status(void)
{
return animimg_end_flag;
}
#endif

View File

@ -0,0 +1,100 @@
/**
* @file lv_animimg.h
*
*/
#ifndef LV_ANIM_IMG_H
#define LV_ANIM_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_ANIMIMG != 0
/*Testing of dependencies*/
#if LV_USE_IMG == 0
#error "lv_animing: lv_img is required. Enable it in lv_conf.h (LV_USE_IMG 1)"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of image*/
typedef struct {
lv_img_t img;
lv_anim_t anim;
/*picture sequence */
lv_img_dsc_t **dsc;
int8_t pic_count;
} lv_animimg_t;
/*Image parts*/
enum {
LV_ANIM_IMG_PART_MAIN,
};
typedef uint8_t lv_animimg_part_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an animation image objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a image object, if not NULL then the new object will be copied from it
* @return pointer to the created animation image object
*/
lv_obj_t * lv_animimg_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the image animation images source.
* @param img pointer to an animation image object
* @param dsc pointer to a series images
* @param num images' number
*/
void lv_animimg_set_src(lv_obj_t * img, lv_img_dsc_t * dsc[], uint8_t num);
/**
* Startup the image animation.
* @param img pointer to an animation image object
*/
void lv_animimg_start(lv_obj_t * obj);
/**
* Set the image animation duration time. unit:ms
* @param img pointer to an animation image object
*/
void lv_animimg_set_duration(lv_obj_t * img, uint32_t duration);
/**
* Set the image animation reapeatly play times.
* @param img pointer to an animation image object
*/
void lv_animimg_set_repeat_count(lv_obj_t * img, uint16_t count);
/*=====================
* Getter functions
*====================*/
#endif /*LV_USE_ANIMIMG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ANIM_IMG_H*/

View File

@ -0,0 +1,367 @@
/**
* @file lv_calendar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar.h"
#include "../../../lvgl.h"
#if LV_USE_CALENDAR
/*********************
* DEFINES
*********************/
#define LV_CALENDAR_CTRL_TODAY LV_BTNMATRIX_CTRL_CUSTOM_1
#define LV_CALENDAR_CTRL_HIGHLIGHT LV_BTNMATRIX_CTRL_CUSTOM_2
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_part_begin_event_cb(lv_event_t * e);
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
static uint8_t get_month_length(int32_t year, int32_t month);
static uint8_t is_leap_year(uint32_t year);
static void highlight_update(lv_obj_t * calendar);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_class = {
.constructor_cb = lv_calendar_constructor,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def =(LV_DPI_DEF * 3) / 2,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_calendar_t),
.base_class = &lv_btnmatrix_class
};
static const char * day_names_def[7] = LV_CALENDAR_DEFAULT_DAY_NAMES;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
void lv_calendar_set_day_names(lv_obj_t * obj, const char * day_names[])
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint32_t i;
for(i = 0; i < 7; i++) {
calendar->map[i] = day_names[i];
}
}
void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->today.year = year;
calendar->today.month = month;
calendar->today.day = day;
highlight_update(obj);
}
void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num)
{
LV_ASSERT_NULL(highlighted);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->highlighted_dates = highlighted;
calendar->highlighted_dates_num = date_num;
highlight_update(obj);
}
void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->showed_date.year = year;
calendar->showed_date.month = month;
calendar->showed_date.day = 1;
lv_calendar_date_t d;
d.year = calendar->showed_date.year;
d.month = calendar->showed_date.month;
d.day = calendar->showed_date.day;
uint8_t i;
/*Remove the disabled state but revert it for day names*/
lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_DISABLED);
for(i = 0; i < 7; i++) {
lv_btnmatrix_set_btn_ctrl(obj, i, LV_BTNMATRIX_CTRL_DISABLED);
}
uint8_t act_mo_len = get_month_length(d.year, d.month);
uint8_t day_first = get_day_of_week(d.year, d.month, 1);
uint8_t c;
for(i = day_first, c = 1; i < act_mo_len + day_first; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
}
uint8_t prev_mo_len = get_month_length(d.year, d.month - 1);
for(i = 0, c = prev_mo_len - day_first + 1; i < day_first; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
lv_btnmatrix_set_btn_ctrl(obj, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
}
for(i = day_first + act_mo_len, c = 1; i < 6 * 7; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
lv_btnmatrix_set_btn_ctrl(obj, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
}
highlight_update(obj);
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
const lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * obj)
{
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return &calendar->today;
}
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
const lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * obj)
{
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return &calendar->showed_date;
}
/**
* Get the the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * obj)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->highlighted_dates;
}
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * obj)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->highlighted_dates_num;
}
lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * obj, lv_calendar_date_t * date)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint16_t d = lv_btnmatrix_get_selected_btn(obj);
if(d == LV_BTNMATRIX_BTN_NONE) {
date->year = 0;
date->month = 0;
date->day = 0;
return LV_RES_INV;
}
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));
if(txt[1] == 0) date->day = txt[0] - '0';
else date->day = (txt[0] - '0') * 10 + (txt[1] - '0');
date->year = calendar->showed_date.year;
date->month = calendar->showed_date.month;
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
/*Initialize the allocated 'ext'*/
calendar->today.year = 2020;
calendar->today.month = 1;
calendar->today.day = 1;
calendar->showed_date.year = 2020;
calendar->showed_date.month = 1;
calendar->showed_date.day = 1;
calendar->highlighted_dates = NULL;
calendar->highlighted_dates_num = 0;
lv_memset_00(calendar->nums, sizeof(calendar->nums));
uint8_t i;
uint8_t j = 0;
for(i = 0; i < 8 * 7; i++) {
/*Every 8th string is "\n"*/
if(i != 0 && (i + 1) % 8 == 0) {
calendar->map[i] = "\n";
} else if(i < 8){
calendar->map[i] = day_names_def[i];
} else {
calendar->nums[j][0] = 'x';
calendar->map[i] = calendar->nums[j];
j++;
}
}
calendar->map[8 * 7 - 1] = "";
lv_btnmatrix_set_map(obj, calendar->map);
lv_btnmatrix_set_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
lv_calendar_set_showed_date(obj, calendar->showed_date.year, calendar->showed_date.month);
lv_calendar_set_today_date(obj, calendar->today.year, calendar->today.month, calendar->today.day);
lv_obj_add_event_cb(obj, draw_part_begin_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
}
static void draw_part_begin_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
if(dsc->part == LV_PART_ITEMS) {
/*Day name styles*/
if(dsc->id < 7) {
dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
}
else if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED)) {
dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
dsc->label_dsc->color = lv_palette_main(LV_PALETTE_GREY);
}
if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_HIGHLIGHT)) {
dsc->rect_dsc->bg_opa = LV_OPA_40;
dsc->rect_dsc->bg_color = lv_theme_get_color_primary(obj);
if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) {
dsc->rect_dsc->bg_opa = LV_OPA_70;
}
}
if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_TODAY)) {
dsc->rect_dsc->border_opa = LV_OPA_COVER;
dsc->rect_dsc->border_color = lv_theme_get_color_primary(obj);
dsc->rect_dsc->border_width += 1;
}
}
}
/**
* Get the number of days in a month
* @param year a year
* @param month a month. The range is basically [1..12] but [-11..0] or [13..24] is also
* supported to handle next/prev. year
* @return [28..31]
*/
static uint8_t get_month_length(int32_t year, int32_t month)
{
month--;
if(month < 0) {
year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/
month = 12 + month; /*`month` is negative, the result will be < 12*/
}
if(month >= 12) {
year++;
month -= 12;
}
/*month == 1 is february*/
return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
}
/**
* Tells whether a year is leap year or not
* @param year a year
* @return 0: not leap year; 1: leap year
*/
static uint8_t is_leap_year(uint32_t year)
{
return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
}
/**
* Get the day of the week
* @param year a year
* @param month a month [1..12]
* @param day a day [1..32]
* @return [0..6] which means [Sun..Sat] or [Mon..Sun] depending on LV_CALENDAR_WEEK_STARTS_MONDAY
*/
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
{
uint32_t a = month < 3 ? 1 : 0;
uint32_t b = year - a;
#if LV_CALENDAR_WEEK_STARTS_MONDAY
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400) - 1) % 7;
#else
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400)) % 7;
#endif
return day_of_week;
}
static void highlight_update(lv_obj_t * obj)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint16_t i;
/*Clear all kind of selection*/
lv_btnmatrix_clear_btn_ctrl_all(obj, LV_CALENDAR_CTRL_TODAY | LV_CALENDAR_CTRL_HIGHLIGHT);
if(calendar->highlighted_dates) {
for(i = 0; i < calendar->highlighted_dates_num; i++) {
if(calendar->highlighted_dates[i].year == calendar->today.year && calendar->highlighted_dates[i].month == calendar->showed_date.month) {
lv_btnmatrix_set_btn_ctrl(obj, calendar->highlighted_dates[i].day + 7, LV_CALENDAR_CTRL_HIGHLIGHT);
}
}
}
if(calendar->showed_date.year == calendar->today.year && calendar->showed_date.month == calendar->today.month) {
uint8_t day_first = get_day_of_week(calendar->today.year, calendar->today.month, calendar->today.day - 1);
lv_btnmatrix_set_btn_ctrl(obj, calendar->today.day + day_first + 7, LV_CALENDAR_CTRL_TODAY);
}
}
#endif /*LV_USE_CALENDAR*/

View File

@ -0,0 +1,154 @@
/**
* @file lv_calendar.h
*
*/
#ifndef LV_CALENDAR_H
#define LV_CALENDAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../widgets/lv_btnmatrix.h"
#if LV_USE_CALENDAR
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Represents a date on the calendar object (platform-agnostic).
*/
typedef struct {
uint16_t year;
int8_t month; /** 1..12*/
int8_t day; /** 1..31*/
} lv_calendar_date_t;
/*Data of calendar*/
typedef struct {
lv_btnmatrix_t btnm;
/*New data for this type*/
lv_calendar_date_t today; /*Date of today*/
lv_calendar_date_t showed_date; /*Currently visible month (day is ignored)*/
lv_calendar_date_t * highlighted_dates; /*Apply different style on these days (pointer to an array defined by the user)*/
uint16_t highlighted_dates_num; /*Number of elements in `highlighted_days`*/
const char * map[8 * 7];
char nums [7 * 6][4];
} lv_calendar_t;
extern const lv_obj_class_t lv_calendar_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_calendar_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the today's date
* @param obj pointer to a calendar object
* @param year today's year
* @param month today's month [1..12]
* @param day today's day [1..31]
*/
void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day);
/**
* Set the currently showed
* @param obj pointer to a calendar object
* @param year today's year
* @param month today's month [1..12]
*/
void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month);
/**
* Set the the highlighted dates
* @param obj pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates.
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num);
/**
* Set the name of the days
* @param obj pointer to a calendar object
* @param day_names pointer to an array with the names.
* E.g. `const char * days[7] = {"Sun", "Mon", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_day_names(lv_obj_t * obj, const char ** day_names);
/*=====================
* Getter functions
*====================*/
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
const lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar);
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
const lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar);
/**
* Get the the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar);
/**
* Get the number of the highlighted dates
* @param calendar pointer to a calendar object
* @return number of highlighted days
*/
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar);
/**
* Get the currently pressed day
* @param calendar pointer to a calendar object
* @param date store the pressed date here
* @return LV_RES_OK: there is a valid pressed date; LV_RES_INV: there is no pressed data
*/
lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * calendar, lv_calendar_date_t * date);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/

View File

@ -0,0 +1,125 @@
/**
* @file lv_calendar_header_arrow.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar_header_arrow.h"
#if LV_USE_CALENDAR_HEADER_ARROW
#include "lv_calendar.h"
#include "../../../widgets/lv_btn.h"
#include "../../../widgets/lv_label.h"
#include "../../layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void month_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
static const char * month_names_def[12] = LV_CALENDAR_DEFAULT_MONTH_NAMES;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent, lv_obj_t * calendar, lv_coord_t btn_size)
{
lv_obj_t * header = lv_obj_create(parent);
/*Use the same paddings as the calendar*/
lv_obj_set_style_pad_left(header, lv_obj_get_style_pad_left(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_right(header, lv_obj_get_style_pad_right(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_top(header, lv_obj_get_style_pad_top(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_bottom(header, lv_obj_get_style_pad_bottom(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_column(header, lv_obj_get_style_pad_column(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_radius(header, lv_obj_get_style_radius(calendar, LV_PART_MAIN), 0);
const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
lv_obj_update_layout(calendar);
lv_coord_t w = lv_obj_get_width(calendar);
lv_obj_set_size(header, w, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_t * mo_prev = lv_btn_create(header);
lv_obj_set_style_bg_img_src(mo_prev, LV_SYMBOL_LEFT, 0);
lv_obj_set_size(mo_prev, btn_size, btn_size);
lv_obj_add_event_cb(mo_prev, month_event_cb, LV_EVENT_CLICKED, calendar);
lv_obj_clear_flag(mo_prev, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_t * label = lv_label_create(header);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_flex_grow(label, 1);
lv_label_set_text_fmt(label, "%d %s", cur_date->year, month_names_def[cur_date->month - 1]);
lv_obj_t * mo_next = lv_btn_create(header);
lv_obj_set_style_bg_img_src(mo_next, LV_SYMBOL_RIGHT, 0);
lv_obj_set_size(mo_next, btn_size, btn_size);
lv_obj_add_event_cb(mo_next, month_event_cb, LV_EVENT_CLICKED, calendar);
lv_obj_clear_flag(mo_next, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_align_to(header, calendar, LV_ALIGN_OUT_TOP_MID, 0, 0);
return header;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void month_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * header = lv_obj_get_parent(btn);
lv_obj_t * calendar = lv_event_get_user_data(e);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
/*The last child is the right button*/
if(lv_obj_get_child(header, 0) == btn) {
if(newd.month == 1) {
newd.month = 12;
newd.year --;
} else {
newd.month --;
}
} else {
if(newd.month == 12) {
newd.month = 1;
newd.year ++;
} else {
newd.month ++;
}
}
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
lv_obj_t * label = lv_obj_get_child(header, 1);
lv_label_set_text_fmt(label, "%d %s", newd.year, month_names_def[newd.month - 1]);
}
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/

View File

@ -0,0 +1,48 @@
/**
* @file lv_calendar_header_arrow.h
*
*/
#ifndef LV_CALENDAR_HEADER_ARROW_H
#define LV_CALENDAR_HEADER_ARROW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_CALENDAR_HEADER_ARROW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar objects
* @param par pointer to an object, it will be the parent of the new calendar
* @return pointer to the created calendar
*/
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent, lv_obj_t * calendar, lv_coord_t btn_size);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/

View File

@ -0,0 +1,123 @@
/**
* @file lv_calendar_header_dropdown.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar_header_dropdown.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN
#include "lv_calendar.h"
#include "../../../widgets/lv_dropdown.h"
#include "../../layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void year_event_cb(lv_event_t * e);
static void month_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
static const char * month_list = "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12";
static const char * year_list = {
"2023\n2022\n2021\n"
"2020\n2019\n2018\n2017\n2016\n2015\n2014\n2013\n2012\n2011\n2010\n2009\n2008\n2007\n2006\n2005\n2004\n2003\n2002\n2001\n"
"2000\n1999\n1998\n1997\n1996\n1995\n1994\n1993\n1992\n1991\n1990\n1989\n1988\n1987\n1986\n1985\n1984\n1983\n1982\n1981\n"
"1980\n1979\n1978\n1977\n1976\n1975\n1974\n1973\n1972\n1971\n1970\n1969\n1968\n1967\n1966\n1965\n1964\n1963\n1962\n1961\n"
"1960\n1959\n1958\n1957\n1956\n1955\n1954\n1953\n1952\n1951\n1950\n1949\n1948\n1947\n1946\n1945\n1944\n1943\n1942\n1941\n"
"1940\n1939\n1938\n1937\n1936\n1935\n1934\n1933\n1932\n1931\n1930\n1929\n1928\n1927\n1926\n1925\n1924\n1923\n1922\n1921\n"
"1920\n1919\n1918\n1917\n1916\n1915\n1914\n1913\n1912\n1911\n1910\n1909\n1908\n1907\n1906\n1905\n1904\n1903\n1902\n1901"
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_header_dropdown_create(lv_obj_t * parent, lv_obj_t * calendar)
{
lv_obj_t * header = lv_obj_create(parent);
/*Use the same paddings as the calendar*/
lv_obj_set_style_pad_left(header,lv_obj_get_style_pad_left(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_right(header,lv_obj_get_style_pad_right(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_top(header,lv_obj_get_style_pad_top(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_bottom(header,lv_obj_get_style_pad_bottom(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_pad_column(header,lv_obj_get_style_pad_column(calendar, LV_PART_MAIN), 0);
lv_obj_set_style_radius(header,lv_obj_get_style_radius(calendar, LV_PART_MAIN), 0);
const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
lv_obj_update_layout(calendar);
lv_coord_t w = lv_obj_get_width(calendar);
lv_obj_set_size(header, w, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_t * year_dd = lv_dropdown_create(header);
lv_dropdown_set_options(year_dd, year_list);
lv_dropdown_set_selected(year_dd, 2023 - cur_date->year);
lv_obj_add_event_cb(year_dd, year_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
lv_obj_set_flex_grow(year_dd, 1);
lv_obj_t * month_dd = lv_dropdown_create(header);
lv_dropdown_set_options(month_dd, month_list);
lv_dropdown_set_selected(month_dd, cur_date->month - 1);
lv_obj_add_event_cb(month_dd, month_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
lv_obj_set_flex_grow(month_dd, 1);
lv_obj_align_to(header, calendar, LV_ALIGN_OUT_TOP_MID, 0, 0);
return header;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void month_event_cb(lv_event_t * e)
{
lv_obj_t * dropdown = lv_event_get_target(e);
lv_obj_t * calendar = lv_event_get_user_data(e);
uint16_t sel = lv_dropdown_get_selected(dropdown);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
newd.month = sel + 1;
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}
static void year_event_cb(lv_event_t * e)
{
lv_obj_t * dropdown = lv_event_get_target(e);
lv_obj_t * calendar = lv_event_get_user_data(e);
uint16_t sel = lv_dropdown_get_selected(dropdown);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
newd.year = 2023 - sel;
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/

View File

@ -0,0 +1,47 @@
/**
* @file lv_calendar_header_dropdown.h
*
*/
#ifndef LV_CALENDAR_HEADER_DROPDOWN_H
#define LV_CALENDAR_HEADER_DROPDOWN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar objects
* @param par pointer to an object, it will be the parent of the new calendar
* @return pointer to the created calendar
*/
lv_obj_t * lv_calendar_header_dropdown_create(lv_obj_t * parent, lv_obj_t * calendar);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,439 @@
/**
* @file lv_chart.h
*
*/
#ifndef LV_CHART_H
#define LV_CHART_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_CHART != 0
/*********************
* DEFINES
*********************/
/**Default value of points. Can be used to not draw a point*/
#define LV_CHART_POINT_NONE (INT16_MAX)
LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE);
/**********************
* TYPEDEFS
**********************/
/**
* Chart types
*/
enum {
LV_CHART_TYPE_NONE, /**< Don't draw the series*/
LV_CHART_TYPE_LINE, /**< Connect the points with lines*/
LV_CHART_TYPE_BAR, /**< Draw columns*/
LV_CHART_TYPE_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
};
typedef uint8_t lv_chart_type_t;
/**
* Chart update mode for `lv_chart_set_next`
*/
enum {
LV_CHART_UPDATE_MODE_SHIFT, /**< Shift old data to the left and add the new one the right*/
LV_CHART_UPDATE_MODE_CIRCULAR, /**< Add the new data in a circular way*/
};
typedef uint8_t lv_chart_update_mode_t;
/**
* Enumeration of the axis'
*/
enum {
LV_CHART_AXIS_PRIMARY_Y = 0x00,
LV_CHART_AXIS_SECONDARY_Y = 0x01,
LV_CHART_AXIS_PRIMARY_X = 0x02,
LV_CHART_AXIS_SECONDARY_X = 0x04,
_LV_CHART_AXIS_LAST
};
typedef uint8_t lv_chart_axis_t;
/**
* Descriptor a chart series
*/
typedef struct {
lv_coord_t * x_points;
lv_coord_t * y_points;
lv_color_t color;
uint16_t start_point;
uint8_t hidden : 1;
uint8_t x_ext_buf_assigned : 1;
uint8_t y_ext_buf_assigned : 1;
uint8_t x_axis_sec : 1;
uint8_t y_axis_sec : 1;
} lv_chart_series_t;
typedef struct {
lv_point_t pos;
uint16_t point_id;
lv_color_t color;
lv_chart_series_t * ser;
lv_dir_t dir;
uint8_t pos_set:1; /*1: pos is set; 0: point_id is set*/
} lv_chart_cursor_t;
typedef struct {
lv_coord_t major_len;
lv_coord_t minor_len;
lv_coord_t draw_size;
uint32_t minor_cnt :15;
uint32_t major_cnt :15;
uint32_t label_en :1;
}lv_chart_tick_dsc_t;
typedef struct {
lv_obj_t obj;
lv_ll_t series_ll; /**< Linked list for the series (stores lv_chart_series_t)*/
lv_ll_t cursor_ll; /**< Linked list for the cursors (stores lv_chart_cursor_t)*/
lv_chart_tick_dsc_t tick[4];
lv_coord_t ymin[2];
lv_coord_t ymax[2];
lv_coord_t xmin[2];
lv_coord_t xmax[2];
uint16_t pressed_point_id;
uint16_t hdiv_cnt; /**< Number of horizontal division lines*/
uint16_t vdiv_cnt; /**< Number of vertical division lines*/
uint16_t point_cnt; /**< Point number in a data line*/
uint16_t zoom_x;
uint16_t zoom_y;
lv_chart_type_t type :3; /**< Line or column chart*/
lv_chart_update_mode_t update_mode : 1;
}lv_chart_t;
extern const lv_obj_class_t lv_chart_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a chart objects
* @param parent pointer to an object, it will be the parent of the new chart
* @return pointer to the created chart
*/
lv_obj_t * lv_chart_create(lv_obj_t * parent);
/**
* Set a new type for a chart
* @param obj pointer to a chart object
* @param type new type of the chart (from 'lv_chart_type_t' enum)
*/
void lv_chart_set_type(lv_obj_t * obj, lv_chart_type_t type);
/**
* Set the number of points on a data line on a chart
* @param obj pointer to a chart object
* @param cnt new number of points on the data lines
*/
void lv_chart_set_point_count(lv_obj_t * obj, uint16_t cnt);
/**
* Set the minimal and maximal y values on an axis
* @param obj pointer to a chart object
* @param axis `LV_CHART_AXIS_PRIMARY_Y` or `LV_CHART_AXIS_SECONDARY_Y`
* @param min minimum value of the y axis
* @param max maximum value of the y axis
*/
void lv_chart_set_range(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t min, lv_coord_t max);
/**
* Set update mode of the chart object. Affects
* @param obj pointer to a chart object
* @param mode the update mode
*/
void lv_chart_set_update_mode(lv_obj_t * obj, lv_chart_update_mode_t update_mode);
/**
* Set the number of horizontal and vertical division lines
* @param obj pointer to a chart object
* @param hdiv number of horizontal division lines
* @param vdiv number of vertical division lines
*/
void lv_chart_set_div_line_count(lv_obj_t * obj, uint8_t hdiv, uint8_t vdiv);
/**
* Zoom into the chart in X direction
* @param obj pointer to a chart object
* @param zoom_x zoom in x direction. LV_ZOOM_NONE or 256 for no zoom, 512 double zoom
*/
void lv_chart_set_zoom_x(lv_obj_t * obj, uint16_t zoom_x);
/**
* Zoom into the chart in Y direction
* @param obj pointer to a chart object
* @param zoom_y zoom in y direction. LV_ZOOM_NONE or 256 for no zoom, 512 double zoom
*/
void lv_chart_set_zoom_y(lv_obj_t * obj, uint16_t zoom_y);
/**
* Get X zoom of a chart
* @param obj pointer to a chart object
* @return the X zoom value
*/
uint16_t lv_chart_get_zoom_x(const lv_obj_t * obj);
/**
* Get Y zoom of a chart
* @param obj pointer to a chart object
* @return the Y zoom value
*/
uint16_t lv_chart_get_zoom_y(const lv_obj_t * obj);
/**
* Set the number of tick lines on an axis
* @param obj pointer to a chart object
* @param axis an axis which ticks count should be set
* @param major_len length of major ticks
* @param minor_len length of minor ticks
* @param major_cnt number of major ticks on the axis
* @param minor_cnt number of minor ticks between two major ticks
* @param label_en true: enable label drawing on major ticks
* @param draw_size extra size required to draw the tick and labels
* (start with 20 px and increase if the ticks/labels are clipped)
*/
void lv_chart_set_axis_tick(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t major_len, lv_coord_t minor_len, lv_coord_t major_cnt, lv_coord_t minor_cnt, bool label_en, lv_coord_t draw_size);
/**
* Get the type of a chart
* @param obj pointer to chart object
* @return type of the chart (from 'lv_chart_t' enum)
*/
lv_chart_type_t lv_chart_get_type(const lv_obj_t * obj);
/**
* Get the data point number per data line on chart
* @param chart pointer to chart object
* @return point number on each data line
*/
uint16_t lv_chart_get_point_count(const lv_obj_t * obj);
/**
* Get the current index of the x-axis start point in the data array
* @param chart pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the index of the current x start point in the data array
*/
uint16_t lv_chart_get_x_start_point(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the position of a point to the chart.
* @param chart pointer to a chart object
* @param ser pointer to series
* @param id the index.
* @param p_out store the result position here
*/
void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_point_t * p_out);
/**
* Refresh a chart if its data line has changed
* @param chart pointer to chart object
*/
void lv_chart_refresh(lv_obj_t * obj);
/*======================
* Series
*=====================*/
/**
* Allocate and add a data series to the chart
* @param obj pointer to a chart object
* @param color color of the data series
* @param axis the y axis to which the series should be attached (::LV_CHART_AXIS_PRIMARY_Y or ::LV_CHART_AXIS_SECONDARY_Y)
* @return pointer to the allocated data series
*/
lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_chart_axis_t axis);
/**
* Deallocate and remove a data series from a chart
* @param chart pointer to a chart object
* @param series pointer to a data series on 'chart'
*/
void lv_chart_remove_series(lv_obj_t * obj, lv_chart_series_t * series);
/**
* Hide/Unhide a single series of a chart.
* @param obj pointer to a chart object.
* @param series pointer to a series object
* @param hide true: hide the series
*/
void lv_chart_hide_series(lv_obj_t * chart, lv_chart_series_t * series, bool hide);
/**
* Change the color of a series
* @param obj pointer to a chart object.
* @param series pointer to a series object
* @param color the new color of the series
*/
void lv_chart_set_series_color(lv_obj_t * chart, lv_chart_series_t * series, lv_color_t color);
/**
* Set the index of the x-axis start point in the data array.
* This point will be considers the first (left) point and the other points will be drawn after it.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the data array
*/
void lv_chart_set_x_start_point(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id);
/**
* Get the next series.
* @param chart pointer to a chart
* @param ser the previous series or NULL to get the first
* @return the next series or NULL if thre is no more.
*/
lv_chart_series_t * lv_chart_get_series_next(const lv_obj_t * chart, const lv_chart_series_t * ser);
/*=====================
* Cursor
*====================*/
/**
* Add a cursor with a given color
* @param obj pointer to chart object
* @param color color of the cursor
* @param dir direction of the cursor. `LV_DIR_RIGHT/LEFT/TOP/DOWN/HOR/VER/ALL`. OR-ed values are possible
* @return pointer to the created cursor
*/
lv_chart_cursor_t * lv_chart_add_cursor(lv_obj_t * obj, lv_color_t color, lv_dir_t dir);
/**
* Set the coordinate of the cursor with respect to the paddings
* @param obj pointer to a chart object
* @param cursor pointer to the cursor
* @param pos the new coordinate of cursor relative the the chart
*/
void lv_chart_set_cursor_pos(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_point_t * pos);
/**
* Stick the cursor to a point
* @param obj pointer to a chart object
* @param cursor pointer to the cursor
* @param ser pointer to a series
* @param point_id the point's index or `LV_CHART_POINT_NONE` to not assign to any points.
*/
void lv_chart_set_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_chart_series_t * ser, uint16_t point_id);
/**
* Get the coordinate of the cursor with respect to the paddings
* @param obj pointer to a chart object
* @param cursor pointer to cursor
* @return coordinate of the cursor as lv_point_t
*/
lv_point_t lv_chart_get_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor);
/*=====================
* Set/Get value(s)
*====================*/
/**
* Initialize all data points of a series with a value
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param value the new value for all points. `LV_CHART_POINT_DEF` can be used to hide the points.
*/
void lv_chart_set_all_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value);
/**
* Set the next point's Y value according to the update mode policy.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param value the new value of the next data
*/
void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value);
/**
* Set the next point's X and Y value according to the update mode policy.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param x_value the new X value of the next data
* @param y_value the new Y value of the next data
*/
void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t x_value, lv_coord_t y_value);
/**
* Set an individual point's y value of a chart's series directly based on its index
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @param value value to assign to array point
*/
void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t value);
/**
* Set an individual point's x and y value of a chart's series directly based on its index
* Can be used only with `LV_CHART_TYPE_SCATTER`.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @param x_value the new X value of the next data
* @param y_value the new Y value of the next data
*/
void lv_chart_set_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t x_value, lv_coord_t y_value);
/**
* Set an external array for the y data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_y_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Set an external array for the x data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_x_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Get the array of y values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_y_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the array of x values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_x_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the index of the currently pressed point. It's the same for every series.
* @param obj pointer to a chart object
* @return the index of the point [0 .. point count] or LV_CHART_POINT_ID_NONE if no point is being pressed
*/
uint32_t lv_chart_get_pressed_point(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CHART*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CHART_H*/

View File

@ -0,0 +1,689 @@
/**
* @file lv_colorwheel.c
*
* Based on the work of @AloyseTech and @paulpv.
*/
/*********************
* INCLUDES
*********************/
#include "lv_colorwheel.h"
#if LV_USE_COLORWHEEL
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_colorwheel_class
#define LV_CPICKER_DEF_QF 3
/**
* The OUTER_MASK_WIDTH define is required to assist with the placing of a mask over the outer ring of the widget as when the
* multicoloured radial lines are calculated for the outer ring of the widget their lengths are jittering because of the
* integer based arithmetic. From tests the maximum delta was found to be 2 so the current value is set to 3 to achieve
* appropriate masking.
*/
#define OUTER_MASK_WIDTH 3
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_colorwheel_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_colorwheel_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_disc_grad(lv_event_t * e);
static void draw_knob(lv_event_t * e);
static void invalidate_knob(lv_obj_t * obj);
static lv_area_t get_knob_area(lv_obj_t * obj);
static void next_color_mode(lv_obj_t * obj);
static lv_res_t double_click_reset(lv_obj_t * obj);
static void refr_knob_pos(lv_obj_t * obj);
static lv_color_t angle_to_mode_color_fast(lv_obj_t * obj, uint16_t angle);
static uint16_t get_angle(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_colorwheel_class = {.instance_size = sizeof(lv_colorwheel_t), .base_class = &lv_obj_class,
.constructor_cb = lv_colorwheel_constructor,
.event_cb = lv_colorwheel_event,
.width_def = LV_DPI_DEF * 2,
.height_def = LV_DPI_DEF * 2,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
};
static bool create_knob_recolor;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a color_picker object
* @param par pointer to an object, it will be the parent of the new color_picker
* @return pointer to the created color_picker
*/
lv_obj_t * lv_colorwheel_create(lv_obj_t * parent, bool knob_recolor)
{
LV_LOG_INFO("begin")
create_knob_recolor = knob_recolor;
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the current hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected hsv
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv)
{
if(hsv.h > 360) hsv.h %= 360;
if(hsv.s > 100) hsv.s = 100;
if(hsv.v > 100) hsv.v = 100;
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(colorwheel->hsv.h == hsv.h && colorwheel->hsv.s == hsv.s && colorwheel->hsv.v == hsv.v) return false;
colorwheel->hsv = hsv;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
return true;
}
/**
* Set the current color of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected color
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color)
{
lv_color32_t c32;
c32.full = lv_color_to32(color);
return lv_colorwheel_set_hsv(obj, lv_color_rgb_to_hsv(c32.ch.red, c32.ch.green, c32.ch.blue));
}
/**
* Set the current color mode.
* @param colorwheel pointer to color wheel object
* @param mode color mode (hue/sat/val)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = mode;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
}
/**
* Set if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @param fixed color mode cannot be changed on long press
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode_fixed = fixed;
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current selected hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @return current selected hsv
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->hsv;
}
/**
* Get the current selected color of a color wheel.
* @param colorwheel pointer to color wheel object
* @return color current selected color
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return lv_color_hsv_to_rgb(colorwheel->hsv.h, colorwheel->hsv.s, colorwheel->hsv.v);
}
/**
* Get the current color mode.
* @param colorwheel pointer to color wheel object
* @return color mode (hue/sat/val)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode;
}
/**
* Get if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @return mode cannot be changed on long press
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode_fixed;
}
/*=====================
* Other functions
*====================*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_colorwheel_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->hsv.h = 0;
colorwheel->hsv.s = 100;
colorwheel->hsv.v = 100;
colorwheel->mode = LV_COLORWHEEL_MODE_HUE;
colorwheel->mode_fixed = 0;
colorwheel->last_click_time = 0;
colorwheel->last_change_time = 0;
colorwheel->knob.recolor = create_knob_recolor;
lv_obj_add_flag(obj, LV_OBJ_FLAG_ADV_HITTEST);
refr_knob_pos(obj);
}
static void draw_disc_grad(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t cx = obj->coords.x1 + w / 2;
lv_coord_t cy = obj->coords.y1 + h / 2;
lv_coord_t r = w / 2;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
line_dsc.width = (r * 628 / (256 / LV_CPICKER_DEF_QF)) / 100;
line_dsc.width += 2;
uint16_t i;
uint32_t a = 0;
lv_coord_t cir_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
#if LV_DRAW_COMPLEX
/*Mask outer and inner ring of widget to tidy up ragged edges of lines while drawing outer ring*/
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &obj->coords, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, 0);
lv_area_t mask_area;
lv_area_copy(&mask_area, &obj->coords);
mask_area.x1 += cir_w;
mask_area.x2 -= cir_w;
mask_area.y1 += cir_w;
mask_area.y2 -= cir_w;
lv_draw_mask_radius_param_t mask_in_param;
lv_draw_mask_radius_init(&mask_in_param, &mask_area, LV_RADIUS_CIRCLE, true);
int16_t mask_in_id = lv_draw_mask_add(&mask_in_param, 0);
/*The inner and outer line ends will be masked out.
*So make lines a little bit longer because the masking makes a more even result*/
lv_coord_t cir_w_extra = line_dsc.width;
#else
lv_coord_t cir_w_extra = 0;
#endif
for(i = 0; i <= 256; i += LV_CPICKER_DEF_QF, a += 360 * LV_CPICKER_DEF_QF) {
line_dsc.color = angle_to_mode_color_fast(obj, i);
uint16_t angle_trigo = (uint16_t)(a >> 8); /*i * 360 / 256 is the scale to apply, but we can skip multiplication here*/
lv_point_t p[2];
p[0].x = cx + ((r + cir_w_extra) * lv_trigo_sin(angle_trigo) >> LV_TRIGO_SHIFT);
p[0].y = cy + ((r + cir_w_extra) * lv_trigo_cos(angle_trigo) >> LV_TRIGO_SHIFT);
p[1].x = cx + ((r - cir_w - cir_w_extra) * lv_trigo_sin(angle_trigo) >> LV_TRIGO_SHIFT);
p[1].y = cy + ((r - cir_w - cir_w_extra) * lv_trigo_cos(angle_trigo) >> LV_TRIGO_SHIFT);
lv_draw_line(&p[0], &p[1], clip_area, &line_dsc);
}
#if LV_DRAW_COMPLEX
lv_draw_mask_remove_id(mask_out_id);
lv_draw_mask_remove_id(mask_in_id);
#endif
}
static void draw_knob(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &cir_dsc);
cir_dsc.radius = LV_RADIUS_CIRCLE;
if(colorwheel->knob.recolor) {
cir_dsc.bg_color = lv_colorwheel_get_rgb(obj);
}
lv_area_t knob_area = get_knob_area(obj);
lv_draw_rect(&knob_area, clip_area, &cir_dsc);
}
static void invalidate_knob(lv_obj_t * obj)
{
lv_area_t knob_area = get_knob_area(obj);
lv_obj_invalidate_area(obj, &knob_area);
}
static lv_area_t get_knob_area(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
/*Get knob's radius*/
uint16_t r = 0;
r = lv_obj_get_style_arc_width(obj, LV_PART_MAIN) / 2;
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_area_t knob_area;
knob_area.x1 = obj->coords.x1 + colorwheel->knob.pos.x - r - left;
knob_area.y1 = obj->coords.y1 + colorwheel->knob.pos.y - r - right;
knob_area.x2 = obj->coords.x1 + colorwheel->knob.pos.x + r + top;
knob_area.y2 = obj->coords.y1 + colorwheel->knob.pos.y + r + bottom;
return knob_area;
}
static void lv_colorwheel_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_coord_t knob_pad = LV_MAX4(left, right, top, bottom) + 2;
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, knob_pad);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
void * param = lv_event_get_param(e);
/*Refresh extended draw area to make knob visible*/
if(lv_obj_get_width(obj) != lv_area_get_width(param) ||
lv_obj_get_height(obj) != lv_area_get_height(param)) {
refr_knob_pos(obj);
}
}
else if(code == LV_EVENT_STYLE_CHANGED) {
/*Refresh extended draw area to make knob visible*/
refr_knob_pos(obj);
}
else if(code == LV_EVENT_KEY) {
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = (colorwheel->hsv.h + 1) % 360;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = (colorwheel->hsv.s + 1) % 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = (colorwheel->hsv.v + 1) % 100;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = colorwheel->hsv.h > 0 ? (colorwheel->hsv.h - 1) : 360;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = colorwheel->hsv.s > 0 ? (colorwheel->hsv.s - 1) : 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = colorwheel->hsv.v > 0 ? (colorwheel->hsv.v - 1) : 100;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
}
else if(code == LV_EVENT_PRESSED) {
colorwheel->last_change_time = lv_tick_get();
lv_indev_get_point(lv_indev_get_act(), &colorwheel->last_press_point);
res = double_click_reset(obj);
if(res != LV_RES_OK) return;
}
else if(code == LV_EVENT_PRESSING) {
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) return;
lv_indev_type_t indev_type = lv_indev_get_type(indev);
lv_point_t p;
if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
p.x = obj->coords.x1 + lv_obj_get_width(obj) / 2;
p.y = obj->coords.y1 + lv_obj_get_height(obj) / 2;
}
else {
lv_indev_get_point(indev, &p);
}
lv_coord_t drag_limit = indev->driver->scroll_limit;
if((LV_ABS(p.x - colorwheel->last_press_point.x) > drag_limit) ||
(LV_ABS(p.y - colorwheel->last_press_point.y) > drag_limit)) {
colorwheel->last_change_time = lv_tick_get();
colorwheel->last_press_point.x = p.x;
colorwheel->last_press_point.y = p.y;
}
p.x -= obj->coords.x1;
p.y -= obj->coords.y1;
/*Ignore pressing in the inner area*/
uint16_t w = lv_obj_get_width(obj);
int16_t angle = 0;
lv_coord_t cir_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
lv_coord_t r_in = w / 2;
p.x -= r_in;
p.y -= r_in;
bool on_ring = true;
r_in -= cir_w;
if(r_in > LV_DPI_DEF / 2) {
lv_coord_t inner = cir_w / 2;
r_in -= inner;
if(r_in < LV_DPI_DEF / 2) r_in = LV_DPI_DEF / 2;
}
if(p.x * p.x + p.y * p.y < r_in * r_in) {
on_ring = false;
}
/*If the inner area is being pressed, go to the next color mode on long press*/
uint32_t diff = lv_tick_elaps(colorwheel->last_change_time);
if(!on_ring && diff > indev->driver->long_press_time && !colorwheel->mode_fixed) {
next_color_mode(obj);
lv_indev_wait_release(lv_indev_get_act());
return;
}
/*Set the angle only if pressed on the ring*/
if(!on_ring) return;
angle = lv_atan2(p.x, p.y) % 360;
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = angle;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = (angle * 100) / 360;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = (angle * 100) / 360;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_HIT_TEST) {
lv_hit_test_info_t * info = lv_event_get_param(e);;
/*Valid clicks can be only in the circle*/
info->res = _lv_area_is_point_on(&obj->coords, info->point, LV_RADIUS_CIRCLE);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_disc_grad(e);
draw_knob(e);
}
else if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
}
}
static void next_color_mode(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = (colorwheel->mode + 1) % 3;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
}
static void refr_knob_pos(lv_obj_t * obj)
{
invalidate_knob(obj);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t scale_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
lv_coord_t r = (w - scale_w) / 2;
uint16_t angle = get_angle(obj);
colorwheel->knob.pos.x = (((int32_t)r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT);
colorwheel->knob.pos.y = (((int32_t)r * lv_trigo_cos(angle)) >> LV_TRIGO_SHIFT);
colorwheel->knob.pos.x = colorwheel->knob.pos.x + w / 2;
colorwheel->knob.pos.y = colorwheel->knob.pos.y + w / 2;
invalidate_knob(obj);
}
static lv_res_t double_click_reset(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_indev_t * indev = lv_indev_get_act();
/*Double clicked? Use long press time as double click time out*/
if(lv_tick_elaps(colorwheel->last_click_time) < indev->driver->long_press_time) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = 0;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = 100;
break;
}
lv_indev_wait_release(indev);
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
}
colorwheel->last_click_time = lv_tick_get();
return LV_RES_OK;
}
#define SWAPPTR(A, B) do { uint8_t * t = A; A = B; B = t; } while(0)
#define HSV_PTR_SWAP(sextant,r,g,b) if((sextant) & 2) { SWAPPTR((r), (b)); } if((sextant) & 4) { SWAPPTR((g), (b)); } if(!((sextant) & 6)) { \
if(!((sextant) & 1)) { SWAPPTR((r), (g)); } } else { if((sextant) & 1) { SWAPPTR((r), (g)); } }
/**
* Based on the idea from https://www.vagrearg.org/content/hsvrgb
* Here we want to compute an approximate RGB value from a HSV input color space. We don't want to be accurate
* (for that, there's lv_color_hsv_to_rgb), but we want to be fast.
*
* Few tricks are used here: Hue is in range [0; 6 * 256] (so that the sextant is in the high byte and the fractional part is in the low byte)
* both s and v are in [0; 255] range (very convenient to avoid divisions).
*
* We fold all symmetry by swapping the R, G, B pointers so that the code is the same for all sextants.
* We replace division by 255 by a division by 256, a.k.a a shift right by 8 bits.
* This is wrong, but since this is only used to compute the pixels on the screen and not the final color, it's ok.
*/
static void fast_hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b);
static void fast_hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b)
{
if (!s) { *r = *g = *b = v; return; }
uint8_t sextant = h >> 8;
HSV_PTR_SWAP(sextant, r, g, b); /*Swap pointers so the conversion code is the same*/
*g = v;
uint8_t bb = ~s;
uint16_t ww = v * bb; /*Don't try to be precise, but instead, be fast*/
*b = ww >> 8;
uint8_t h_frac = h & 0xff;
if(!(sextant & 1)) {
/*Up slope*/
ww = !h_frac ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_frac)); /*Skip multiply if not required*/
} else {
/*Down slope*/
ww = s * h_frac;
}
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
*r = ww >> 8;
}
static lv_color_t angle_to_mode_color_fast(lv_obj_t * obj, uint16_t angle)
{
lv_colorwheel_t * ext = (lv_colorwheel_t*)obj;
uint8_t r = 0, g = 0, b = 0;
static uint16_t h = 0;
static uint8_t s = 0, v = 0, m = 255;
switch(ext->mode) {
default:
case LV_COLORWHEEL_MODE_HUE:
/*Don't recompute costly scaling if it does not change*/
if (m != ext->mode) {
s = (uint8_t)(((uint16_t)ext->hsv.s * 51) / 20); v = (uint8_t)(((uint16_t)ext->hsv.v * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(angle * 6, s, v, &r, &g, &b); /*A smart compiler will replace x * 6 by (x << 2) + (x << 1) if it's more efficient*/
break;
case LV_COLORWHEEL_MODE_SATURATION:
/*Don't recompute costly scaling if it does not change*/
if (m != ext->mode) {
h = (uint16_t)(((uint32_t)ext->hsv.h * 6 * 256) / 360); v = (uint8_t)(((uint16_t)ext->hsv.v * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(h, angle, v, &r, &g, &b);
break;
case LV_COLORWHEEL_MODE_VALUE:
/*Don't recompute costly scaling if it does not change*/
if (m != ext->mode) {
h = (uint16_t)(((uint32_t)ext->hsv.h * 6 * 256) / 360); s = (uint8_t)(((uint16_t)ext->hsv.s * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(h, s, angle, &r, &g, &b);
break;
}
return lv_color_make(r, g, b);
}
static uint16_t get_angle(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
uint16_t angle;
switch(colorwheel->mode) {
default:
case LV_COLORWHEEL_MODE_HUE:
angle = colorwheel->hsv.h;
break;
case LV_COLORWHEEL_MODE_SATURATION:
angle = (colorwheel->hsv.s * 360) / 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
angle = (colorwheel->hsv.v * 360) / 100 ;
break;
}
return angle;
}
#endif /*LV_USE_COLORWHEEL*/

View File

@ -0,0 +1,142 @@
/**
* @file lv_colorwheel.h
*
*/
#ifndef LV_COLORWHEEL_H
#define LV_COLORWHEEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_COLORWHEEL
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_COLORWHEEL_MODE_HUE,
LV_COLORWHEEL_MODE_SATURATION,
LV_COLORWHEEL_MODE_VALUE
};
typedef uint8_t lv_colorwheel_mode_t;
/*Data of color picker*/
typedef struct {
lv_obj_t obj;
lv_color_hsv_t hsv;
struct {
lv_point_t pos;
uint8_t recolor : 1;
} knob;
uint32_t last_click_time;
uint32_t last_change_time;
lv_point_t last_press_point;
lv_colorwheel_mode_t mode : 2;
uint8_t mode_fixed : 1;
} lv_colorwheel_t;
extern const lv_obj_class_t lv_colorwheel_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a color picker objects with disc shape
* @param parent pointer to an object, it will be the parent of the new color picker
* @param knob_recolor true: set the knob's color to the current color
* @return pointer to the created color picker
*/
lv_obj_t * lv_colorwheel_create(lv_obj_t * parent, bool knob_recolor);
/*=====================
* Setter functions
*====================*/
/**
* Set the current hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected hsv
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv);
/**
* Set the current color of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected color
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color);
/**
* Set the current color mode.
* @param colorwheel pointer to color wheel object
* @param mode color mode (hue/sat/val)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode);
/**
* Set if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @param fixed color mode cannot be changed on long press
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed);
/*=====================
* Getter functions
*====================*/
/**
* Get the current selected hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @return current selected hsv
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj);
/**
* Get the current selected color of a color wheel.
* @param colorwheel pointer to color wheel object
* @return color current selected color
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj);
/**
* Get the current color mode.
* @param colorwheel pointer to color wheel object
* @return color mode (hue/sat/val)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj);
/**
* Get if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @return mode cannot be changed on long press
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_COLORWHEEL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_COLORWHEEL_H*/

View File

@ -0,0 +1,342 @@
/**
* @file lv_imgbtn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_imgbtn.h"
#if LV_USE_IMGBTN != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_imgbtn_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_main(lv_event_t * e);
static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void refr_img(lv_obj_t * imgbtn);
static lv_imgbtn_state_t suggest_state(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_imgbtn_class = {
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_imgbtn_t),
.constructor_cb = lv_imgbtn_constructor,
.event_cb = lv_imgbtn_event,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a image button object
* @param par pointer to an object, it will be the parent of the new image button
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image
* @param src_left pointer to an image source for the left side of the button (a C array or path to
* a file)
* @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C
* array or path to a file)
* @param src_right pointer to an image source for the right side of the button (a C array or path
* to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * obj, lv_imgbtn_state_t state, const void * src_left, const void * src_mid,
const void * src_right)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
imgbtn->img_src_left[state] = src_left;
imgbtn->img_src_mid[state] = src_mid;
imgbtn->img_src_right[state] = src_right;
refr_img(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the left image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_left(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_left[state];
}
/**
* Get the middle image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the middle image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_middle(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_mid[state];
}
/**
* Get the right image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_right(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_right[state];
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
/*Initialize the allocated 'ext'*/
lv_memset_00((void *)imgbtn->img_src_mid, sizeof(imgbtn->img_src_mid));
lv_memset_00(imgbtn->img_src_left, sizeof(imgbtn->img_src_left));
lv_memset_00(imgbtn->img_src_right, sizeof(imgbtn->img_src_right));
imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
}
static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(&lv_imgbtn_class, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
refr_img(obj);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_main(e);
}
else if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
}
}
static void draw_main(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
const lv_area_t * clip_area = lv_event_get_param(e);
/*Just draw_main an image*/
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
/*Simply draw the middle src if no tiled*/
const void * src = imgbtn->img_src_left[state];
lv_coord_t tw = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t th = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= tw;
coords.x2 += tw;
coords.y1 -= th;
coords.y2 += th;
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc);
lv_img_header_t header;
lv_area_t coords_part;
lv_coord_t left_w = 0;
lv_coord_t right_w = 0;
if(src) {
lv_img_decoder_get_info(src, &header);
left_w = header.w;
coords_part.x1 = coords.x1;
coords_part.y1 = coords.y1;
coords_part.x2 = coords.x1 + header.w - 1;
coords_part.y2 = coords.y1 + header.h - 1;
lv_draw_img(&coords_part, clip_area, src, &img_dsc);
}
src = imgbtn->img_src_right[state];
if(src) {
lv_img_decoder_get_info(src, &header);
right_w = header.w;
coords_part.x1 = coords.x2 - header.w + 1;
coords_part.y1 = coords.y1;
coords_part.x2 = coords.x2;
coords_part.y2 = coords.y1 + header.h - 1;
lv_draw_img(&coords_part, clip_area, src, &img_dsc);
}
src = imgbtn->img_src_mid[state];
if(src) {
lv_area_t clip_center_area;
clip_center_area.x1 = coords.x1 + left_w;
clip_center_area.x2 = coords.x2 - right_w;
clip_center_area.y1 = coords.y1;
clip_center_area.y2 = coords.y2;
bool comm_res;
comm_res = _lv_area_intersect(&clip_center_area, &clip_center_area, clip_area);
if(comm_res) {
lv_coord_t i;
lv_img_decoder_get_info(src, &header);
coords_part.x1 = coords.x1 + left_w;
coords_part.y1 = coords.y1;
coords_part.x2 = coords_part.x1 + header.w - 1;
coords_part.y2 = coords_part.y1 + header.h - 1;
for(i = 0; i < clip_center_area.x2 + header.w - 1; i += header.w) {
lv_draw_img(&coords_part, &clip_center_area, src, &img_dsc);
coords_part.x1 = coords_part.x2 + 1;
coords_part.x2 += header.w;
}
}
}
}
static void refr_img(lv_obj_t * obj)
{
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
lv_img_header_t header;
const void * src = imgbtn->img_src_mid[state];
if(src == NULL) return;
lv_res_t info_res = LV_RES_OK;
info_res = lv_img_decoder_get_info(src, &header);
if(info_res == LV_RES_OK) {
imgbtn->act_cf = header.cf;
lv_obj_set_height(obj, header.h); /*Keep the user defined width*/
}
else {
imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
}
lv_obj_invalidate(obj);
}
/**
* If `src` is not defined for the current state try to get a state which is related to the current but has `src`.
* E.g. if the PRESSED src is not set but the RELEASED does, use the RELEASED.
* @param imgbtn pointer to an image button
* @param state the state to convert
* @return the suggested state
*/
static lv_imgbtn_state_t suggest_state(lv_obj_t * obj, lv_imgbtn_state_t state)
{
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
if(imgbtn->img_src_mid[state] == NULL) {
switch(state) {
case LV_IMGBTN_STATE_PRESSED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_RELEASED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_PRESSED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_PRESSED]) return LV_IMGBTN_STATE_PRESSED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_DISABLED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_DISABLED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
default:
break;
}
}
return state;
}
lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn)
{
LV_ASSERT_OBJ(imgbtn, MY_CLASS);
lv_state_t obj_state = lv_obj_get_state(imgbtn);
if(obj_state & LV_STATE_DISABLED) {
if(obj_state & LV_STATE_CHECKED) return LV_IMGBTN_STATE_CHECKED_DISABLED;
else return LV_IMGBTN_STATE_DISABLED;
}
if(obj_state & LV_STATE_CHECKED) {
if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_CHECKED_PRESSED;
else return LV_IMGBTN_STATE_CHECKED_RELEASED;
}
else {
if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_PRESSED;
else return LV_IMGBTN_STATE_RELEASED;
}
}
#endif

View File

@ -0,0 +1,124 @@
/**
* @file lv_imgbtn.h
*
*/
#ifndef LV_IMGBTN_H
#define LV_IMGBTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_IMGBTN != 0
/*********************
* DEFINES
*********************/
typedef enum {
LV_IMGBTN_STATE_RELEASED,
LV_IMGBTN_STATE_PRESSED,
LV_IMGBTN_STATE_DISABLED,
LV_IMGBTN_STATE_CHECKED_RELEASED,
LV_IMGBTN_STATE_CHECKED_PRESSED,
LV_IMGBTN_STATE_CHECKED_DISABLED,
_LV_IMGBTN_STATE_NUM,
}lv_imgbtn_state_t;
/**********************
* TYPEDEFS
**********************/
/*Data of image button*/
typedef struct {
lv_obj_t obj;
const void * img_src_mid[_LV_IMGBTN_STATE_NUM]; /*Store center images to each state*/
const void * img_src_left[_LV_IMGBTN_STATE_NUM]; /*Store left side images to each state*/
const void * img_src_right[_LV_IMGBTN_STATE_NUM]; /*Store right side images to each state*/
lv_img_cf_t act_cf; /*Color format of the currently active image*/
} lv_imgbtn_t;
extern const lv_obj_class_t lv_imgbtn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a image button objects
* @param par pointer to an object, it will be the parent of the new image button
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image
* @param src_left pointer to an image source for the left side of the button (a C array or path to
* a file)
* @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C
* array or path to a file)
* @param src_right pointer to an image source for the right side of the button (a C array or path
* to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_imgbtn_state_t state, const void * src_left, const void * src_mid,
const void * src_right);
/*=====================
* Getter functions
*====================*/
/**
* Get the left image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/**
* Get the middle image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the middle image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/**
* Get the right image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_IMGBTN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMGBTN_H*/

View File

@ -0,0 +1,349 @@
/**
* @file lv_keyboard.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_keyboard.h"
#if LV_USE_KEYBOARD
#include "../../../widgets/lv_textarea.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_keyboard_update_map(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_keyboard_class = {
.constructor_cb = lv_keyboard_constructor,
.width_def = LV_PCT(100),
.height_def = LV_PCT(50),
.instance_size = sizeof(lv_keyboard_t),
.editable = 1,
.base_class = &lv_btnmatrix_class
};
static const char * const default_kb_map_lc[] = {"1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n",
"ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_lc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_uc[] = {"1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n",
"abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_uc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_spec[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n",
"abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n",
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_spec_map[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 2,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_num[] = {"1", "2", "3", LV_SYMBOL_KEYBOARD, "\n",
"4", "5", "6", LV_SYMBOL_OK, "\n",
"7", "8", "9", LV_SYMBOL_BACKSPACE, "\n",
"+/-", "0", ".", LV_SYMBOL_LEFT, LV_SYMBOL_RIGHT, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_num_map[] = {
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, 2,
1, 1, 1, 1, 1
};
static const char * * kb_map[5] = {
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_uc,
(const char * *)default_kb_map_spec,
(const char * *)default_kb_map_num,
(const char * *)NULL,
};
static const lv_btnmatrix_ctrl_t * kb_ctrl[5] = {
default_kb_ctrl_lc_map,
default_kb_ctrl_uc_map,
default_kb_ctrl_spec_map,
default_kb_ctrl_num_map,
NULL,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @return pointer to the created keyboard
*/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_keyboard_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_keyboard_set_textarea(lv_obj_t * obj, lv_obj_t * ta)
{
if(ta) {
LV_ASSERT_OBJ(ta, &lv_textarea_class);
}
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
/*Hide the cursor of the old Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_clear_state(obj, LV_STATE_FOCUSED);
}
keyboard->ta = ta;
/*Show the cursor of the new Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_add_flag(obj, LV_STATE_FOCUSED);
}
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t * obj, lv_keyboard_mode_t mode)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->mode == mode) return;
keyboard->mode = mode;
lv_btnmatrix_set_map(obj, kb_map[mode]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[mode]);
}
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t * obj, lv_keyboard_mode_t mode, const char * map[], const lv_btnmatrix_ctrl_t ctrl_map[])
{
kb_map[mode] = map;
kb_ctrl[mode] = ctrl_map;
lv_keyboard_update_map(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->ta;
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->mode;
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
uint16_t btn_id = lv_btnmatrix_get_selected_btn(obj);
if(btn_id == LV_BTNMATRIX_BTN_NONE) return;
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));
if(txt == NULL) return;
if(strcmp(txt, "abc") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_TEXT_LOWER]);
return;
}
else if(strcmp(txt, "ABC") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_UPPER;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_TEXT_UPPER]);
return;
}
else if(strcmp(txt, "1#") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_SPECIAL;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_SPECIAL]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_SPECIAL]);
return;
}
else if(strcmp(txt, LV_SYMBOL_CLOSE) == 0 || strcmp(txt, LV_SYMBOL_KEYBOARD) == 0) {
lv_res_t res = lv_event_send(obj, LV_EVENT_CANCEL, NULL);
if(res != LV_RES_OK) return;
if(keyboard->ta) {
res = lv_event_send(keyboard->ta, LV_EVENT_CANCEL, NULL);
if(res != LV_RES_OK) return;
}
return;
}
else if(strcmp(txt, LV_SYMBOL_OK) == 0) {
lv_res_t res = lv_event_send(obj, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
if(keyboard->ta) {
res = lv_event_send(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
}
return;
}
/*Add the characters to the text area if set*/
if(keyboard->ta == NULL) return;
if(strcmp(txt, "Enter") == 0 || strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) {
lv_textarea_add_char(keyboard->ta, '\n');
if(lv_textarea_get_one_line(keyboard->ta)) {
lv_res_t res = lv_event_send(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
}
} else if(strcmp(txt, LV_SYMBOL_LEFT) == 0) {
lv_textarea_cursor_left(keyboard->ta);
} else if(strcmp(txt, LV_SYMBOL_RIGHT) == 0) {
lv_textarea_cursor_right(keyboard->ta);
} else if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
lv_textarea_del_char(keyboard->ta);
} else if(strcmp(txt, "+/-") == 0) {
uint16_t cur = lv_textarea_get_cursor_pos(keyboard->ta);
const char * ta_txt = lv_textarea_get_text(keyboard->ta);
if(ta_txt[0] == '-') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_del_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '+');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else if(ta_txt[0] == '+') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_del_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else {
lv_textarea_set_cursor_pos(keyboard->ta, 0);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur + 1);
}
}
else {
lv_textarea_add_text(keyboard->ta, txt);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
keyboard->ta = NULL;
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
lv_obj_align(obj, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_event_cb(obj, lv_keyboard_def_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_LTR, 0);
lv_btnmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
}
/**
* Update the key map for the current mode
* @param kb pointer to a keyboard object
*/
static void lv_keyboard_update_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
lv_btnmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
}
#endif /*LV_USE_KEYBOARD*/

View File

@ -0,0 +1,145 @@
/**
* @file lv_keyboard.h
*
*/
#ifndef LV_KEYBOARD_H
#define LV_KEYBOARD_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../widgets/lv_btnmatrix.h"
#if LV_USE_KEYBOARD
/*Testing of dependencies*/
#if LV_USE_BTNMATRIX == 0
#error "lv_kb: lv_btnm is required. Enable it in lv_conf.h (LV_USE_BTNMATRIX 1) "
#endif
#if LV_USE_TEXTAREA == 0
#error "lv_kb: lv_ta is required. Enable it in lv_conf.h (LV_USE_TEXTAREA 1) "
#endif
/*********************
* DEFINES
*********************/
#define LV_KEYBOARD_CTRL_BTN_FLAGS (LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_CHECKED)
/**********************
* TYPEDEFS
**********************/
/** Current keyboard mode.*/
enum {
LV_KEYBOARD_MODE_TEXT_LOWER,
LV_KEYBOARD_MODE_TEXT_UPPER,
LV_KEYBOARD_MODE_SPECIAL,
LV_KEYBOARD_MODE_NUMBER,
};
typedef uint8_t lv_keyboard_mode_t;
/*Data of keyboard*/
typedef struct {
lv_btnmatrix_t btnm;
lv_obj_t * ta; /*Pointer to the assigned text area*/
lv_keyboard_mode_t mode; /*Key map type*/
} lv_keyboard_t;
extern const lv_obj_class_t lv_keyboard_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @return pointer to the created keyboard
*/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_keyboard_set_textarea(lv_obj_t * kb, lv_obj_t * ta);
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t * kb, lv_keyboard_mode_t mode);
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const char * map[], const lv_btnmatrix_ctrl_t ctrl_map[]);
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * kb);
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * kb);
/**
* Get the current map of a keyboard
* @param kb pointer to a keyboard object
* @return the current map
*/
static inline const char ** lv_keyboard_get_map_array(const lv_obj_t * kb)
{
return lv_btnmatrix_get_map(kb);
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_event_t * e);
/**********************
* MACROS
**********************/
#endif /*LV_USE_KEYBOARD*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_KEYBOARD_H*/

View File

@ -0,0 +1,207 @@
/**
* @file lv_led.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_led.h"
#if LV_USE_LED
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_led_class
#ifndef LV_LED_BRIGHT_MIN
# define LV_LED_BRIGHT_MIN 80
#endif
#ifndef LV_LED_BRIGHT_MAX
# define LV_LED_BRIGHT_MAX 255
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_led_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_led_class = {
.base_class = &lv_obj_class,
.constructor_cb = lv_led_constructor,
.width_def = LV_DPI_DEF / 5,
.height_def = LV_DPI_DEF / 5,
.event_cb = lv_led_event,
.instance_size = sizeof(lv_led_t),
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a led objects
* @param par pointer to an object, it will be the parent of the new led
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the color of the LED
* @param led pointer to a LED object
* @param color the color of the the LED
*/
void lv_led_set_color(lv_obj_t * obj, lv_color_t color)
{
lv_led_t * led = (lv_led_t *)obj;
led->color = color;
lv_obj_invalidate(obj);
}
/**
* 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_brightness(lv_obj_t * obj, uint8_t bright)
{
lv_led_t * led = (lv_led_t *)obj;
if(led->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;
led->bright = bright;
/*Invalidate the object there fore it will be redrawn*/
lv_obj_invalidate(obj);
}
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led)
{
lv_led_set_brightness(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_led_set_brightness(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 * obj)
{
uint8_t bright = lv_led_get_brightness(obj);
if(bright > (LV_LED_BRIGHT_MIN + LV_LED_BRIGHT_MAX) >> 1)
lv_led_off(obj);
else
lv_led_on(obj);
}
/*=====================
* 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_brightness(const lv_obj_t * obj)
{
lv_led_t * led = (lv_led_t *)obj;
return led->bright;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_led_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_led_t * led = (lv_led_t *)obj;
led->color = lv_theme_get_color_primary(obj);
led->bright = LV_LED_BRIGHT_MAX;
}
static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/* Call the ancestor's event handler */
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
/*Make darker colors in a temporary style according to the brightness*/
lv_led_t * led = (lv_led_t *)obj;
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &rect_dsc);
/*Use the original colors brightness to modify color->led*/
rect_dsc.bg_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_color));
rect_dsc.bg_grad_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_grad_color));
rect_dsc.shadow_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.shadow_color));
rect_dsc.border_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.border_color));
rect_dsc.outline_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.outline_color));
/*Mix. the color with black proportionally with brightness*/
rect_dsc.bg_color = lv_color_mix(rect_dsc.bg_color, lv_color_black(), led->bright);
rect_dsc.bg_grad_color = lv_color_mix(rect_dsc.bg_grad_color, lv_color_black(), led->bright);
rect_dsc.border_color = lv_color_mix(rect_dsc.border_color, lv_color_black(), led->bright);
rect_dsc.shadow_color = lv_color_mix(rect_dsc.shadow_color, lv_color_black(), led->bright);
rect_dsc.outline_color = lv_color_mix(rect_dsc.outline_color, lv_color_black(), led->bright);
/*Set the current shadow width according to brightness proportionally between LV_LED_BRIGHT_OFF
* and LV_LED_BRIGHT_ON*/
rect_dsc.shadow_width = ((led->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_width) /
(LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN);
rect_dsc.shadow_spread = ((led->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_spread) /
(LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_draw_rect(&obj->coords, clip_area, &rect_dsc);
}
}
#endif

View File

@ -0,0 +1,99 @@
/**
* @file lv_led.h
*
*/
#ifndef LV_LED_H
#define LV_LED_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_LED
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of led*/
typedef struct {
lv_obj_t obj;
lv_color_t color;
uint8_t bright; /**< Current brightness of the LED (0..255)*/
} lv_led_t;
extern const lv_obj_class_t lv_led_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a led objects
* @param par pointer to an object, it will be the parent of the new led
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * parent);
/**
* Set the color of the LED
* @param led pointer to a LED object
* @param color the color of the the LED
*/
void lv_led_set_color(lv_obj_t * led, lv_color_t color);
/**
* 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_brightness(lv_obj_t * led, uint8_t bright);
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led);
/**
* Light off a LED
* @param led pointer to a LED object
*/
void lv_led_off(lv_obj_t * led);
/**
* Toggle the state of a LED
* @param led pointer to a LED object
*/
void lv_led_toggle(lv_obj_t * led);
/**
* 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_brightness(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LED*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LED_H*/

View File

@ -0,0 +1,203 @@
/**
* @file lv_list.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_list.h"
#include "../../../core/lv_disp.h"
#include "../../../widgets/lv_label.h"
#include "../../../widgets/lv_img.h"
#include "../../../widgets/lv_btn.h"
#if LV_USE_LIST
/*********************
* DEFINES
*********************/
#define MV_CLASS &lv_list
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
const lv_obj_class_t lv_list_class = {
.base_class = &lv_obj_class,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = LV_DPI_DEF * 2
};
const lv_obj_class_t lv_list_btn_class = {
.base_class = &lv_btn_class,
};
const lv_obj_class_t lv_list_text_class = {
.base_class = &lv_label_class,
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_list_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_class, parent);
lv_obj_class_init_obj(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
return obj;
}
lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_text_class, list);
lv_obj_class_init_obj(obj);
lv_label_set_text(obj, txt);
lv_label_set_long_mode(obj, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(obj, LV_PCT(100));
return obj;
}
lv_obj_t * lv_list_add_btn(lv_obj_t * list, const char * icon, const char * txt)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_btn_class, list);
lv_obj_class_init_obj(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
if(icon) {
lv_obj_t * img = lv_img_create(obj);
lv_img_set_src(img, icon);
}
if(txt) {
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label, txt);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_flex_grow(label, 1);
}
return obj;
}
lv_obj_t * lv_list_msg_add_btn(lv_obj_t * list, const char * icon, const char * txt, lv_event_cb_t event_cb)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_btn_class, list);
lv_obj_class_init_obj(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(33));
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_add_event_cb(obj, event_cb, LV_EVENT_ALL, NULL);
lv_obj_set_style_shadow_width(obj, 0, 0);
lv_obj_set_style_radius( obj, 5*5, 0);
lv_obj_set_style_text_color(obj, lv_color_white(), 0);
lv_obj_set_style_bg_color(obj, lv_color_make(0x21,0x21,0x21),0);
lv_obj_set_style_border_width(obj, 5, 0);
lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM|LV_BORDER_SIDE_RIGHT, 0);
lv_obj_set_style_border_opa(obj, LV_OPA_0, 0);
if(icon) {
lv_obj_t * img = lv_img_create(obj);
lv_img_set_src(img, icon);
}
if(txt) {
lv_obj_t * label = lv_label_create(obj);
lv_obj_align_to(label, obj, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label, txt);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_flex_grow(label, 1);
}
return obj;
}
lv_obj_t * lv_list_new_msg_add_btn(lv_obj_t * list, const char * icon, const char * title, const char * context,lv_event_cb_t event_cb)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_btn_class, list);
lv_obj_class_init_obj(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(50));
lv_obj_add_event_cb(obj, event_cb, LV_EVENT_ALL, NULL);
lv_obj_set_style_shadow_width(obj, 0, 0);
lv_obj_set_style_radius( obj, 5*5, 0);
lv_obj_set_style_text_color(obj, lv_color_white(), 0);
lv_obj_set_style_bg_color(obj, lv_color_make(0x29,0x29,0x29),0);
lv_obj_set_style_border_width(obj, 5, 0);
lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM|LV_BORDER_SIDE_RIGHT, 0);
lv_obj_set_style_border_opa(obj, LV_OPA_0, 0);
if(icon) {
lv_obj_t * img = lv_img_create(obj);
lv_img_set_src(img, icon);
lv_obj_align(img,LV_ALIGN_TOP_LEFT,0,0);
lv_obj_t * label_title;
if(title) {
label_title = lv_label_create(obj);
lv_obj_align_to(label_title, img, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
lv_obj_set_size(label_title,LV_PCT(80),LV_PCT(50));
lv_label_set_long_mode(label_title, LV_LABEL_LONG_DOT);
lv_label_set_text(label_title, title);
}
if(context) {
lv_obj_t * label_context = lv_label_create(obj);
lv_obj_align_to(label_context, img, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
lv_obj_set_size(label_context,LV_PCT(100),LV_PCT(50));
lv_label_set_long_mode(label_context, LV_LABEL_LONG_DOT);
lv_label_set_text(label_context, context);
}
}
return obj;
}
const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn)
{
LV_UNUSED(list);
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(btn); i++) {
lv_obj_t * child = lv_obj_get_child(btn, i);
if(lv_obj_check_type(child, &lv_label_class)) {
return lv_label_get_text(child);
}
}
return "";
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_LIST*/

View File

@ -0,0 +1,58 @@
/**
* @file lv_win.h
*
*/
#ifndef LV_LIST_H
#define LV_LIST_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#include "../../layouts/flex/lv_flex.h"
#if LV_USE_LIST
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_list_class;
extern const lv_obj_class_t lv_list_text_class;
extern const lv_obj_class_t lv_list_btn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_list_create(lv_obj_t * parent);
lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt);
lv_obj_t * lv_list_add_btn(lv_obj_t * list, const char * icon, const char * txt);
lv_obj_t * lv_list_msg_add_btn(lv_obj_t * list, const char * icon, const char * txt, lv_event_cb_t event_cb);
lv_obj_t * lv_list_new_msg_add_btn(lv_obj_t * list, const char * icon, const char * title, const char * context,lv_event_cb_t event_cb);
const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LIST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LIST_H*/

View File

@ -0,0 +1,55 @@
/**
* @file lv_widgets.h
*
*/
#ifndef LV_WIDGETS_H
#define LV_WIDGETS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "animimg/lv_animimg.h"
#include "calendar/lv_calendar.h"
#include "calendar/lv_calendar_header_arrow.h"
#include "calendar/lv_calendar_header_dropdown.h"
#include "chart/lv_chart.h"
#include "keyboard/lv_keyboard.h"
#include "list/lv_list.h"
#include "msgbox/lv_msgbox.h"
#include "meter/lv_meter.h"
#include "spinbox/lv_spinbox.h"
#include "spinner/lv_spinner.h"
#include "tabview/lv_tabview.h"
#include "tileview/lv_tileview.h"
#include "win/lv_win.h"
#include "colorwheel/lv_colorwheel.h"
#include "led/lv_led.h"
#include "imgbtn/lv_imgbtn.h"
#include "span/lv_span.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WIDGETS_H*/

View File

@ -0,0 +1,659 @@
/**
* @file lv_meter.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_meter.h"
#if LV_USE_METER != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_meter_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_arcs(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area);
static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area);
static void draw_needles(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area);
static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value);
static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_meter_class = {
.constructor_cb = lv_meter_constructor,
.destructor_cb = lv_meter_destructor,
.event_cb = lv_meter_event,
.instance_size = sizeof(lv_meter_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_meter_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Add scale
*====================*/
lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_scale_t * scale = _lv_ll_ins_head(&meter->scale_ll);
LV_ASSERT_MALLOC(scale);
lv_memset_00(scale, sizeof(lv_meter_scale_t));
scale->angle_range = 270;
scale->rotation = 90 + (360 - scale->angle_range) / 2;
scale->min = 0;
scale->max = 100;
scale->tick_cnt = 6;
scale->tick_length = 8;
scale->tick_width = 2;
scale->label_gap = 2;
return scale;
}
void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len, lv_color_t color)
{
scale->tick_cnt = cnt;
scale->tick_width = width;
scale->tick_length = len;
scale->tick_color = color;
lv_obj_invalidate(obj);
}
void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width, uint16_t len, lv_color_t color, int16_t label_gap)
{
scale->tick_major_nth = nth;
scale->tick_major_width = width;
scale->tick_major_length = len;
scale->tick_major_color = color;
scale->label_gap = label_gap;
lv_obj_invalidate(obj);
}
void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range, uint32_t rotation)
{
scale->min = min;
scale->max = max;
scale->angle_range = angle_range;
scale->rotation = rotation;
lv_obj_invalidate(obj);
}
/*=====================
* Add indicator
*====================*/
lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color, int16_t r_mod)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_LINE;
indic->type_data.needle_line.width = width;
indic->type_data.needle_line.color = color;
indic->type_data.needle_line.r_mod = r_mod;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src, lv_coord_t pivot_x, lv_coord_t pivot_y)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_IMG;
indic->type_data.needle_img.src = src;
indic->type_data.needle_img.pivot.x = pivot_x;
indic->type_data.needle_img.pivot.y = pivot_y;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color, int16_t r_mod)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_ARC;
indic->type_data.arc.width = width;
indic->type_data.arc.color = color;
indic->type_data.arc.r_mod = r_mod;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start, lv_color_t color_end, bool local, int16_t width_mod)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_SCALE_LINES;
indic->type_data.scale_lines.color_start = color_start;
indic->type_data.scale_lines.color_end = color_end;
indic->type_data.scale_lines.local_grad = local;
indic->type_data.scale_lines.width_mod = width_mod;
lv_obj_invalidate(obj);
return indic;
}
/*=====================
* Set indicator value
*====================*/
void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_start = indic->start_value;
int32_t old_end = indic->end_value;
indic->start_value = value;
indic->end_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_start, value);
inv_arc(obj, indic, old_end, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_start);
inv_line(obj, indic, old_end);
inv_line(obj, indic, value);
} else {
lv_obj_invalidate(obj);
}
}
void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_value = indic->start_value;
indic->start_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_value, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_value);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_value = indic->end_value;
indic->end_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_value, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_value);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_meter_t * meter = (lv_meter_t *)obj;
_lv_ll_init(&meter->scale_ll, sizeof(lv_meter_scale_t));
_lv_ll_init(&meter->indicator_ll, sizeof(lv_meter_indicator_t));
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_meter_t * meter = (lv_meter_t *)obj;
_lv_ll_clear(&meter->indicator_ll);
_lv_ll_clear(&meter->scale_ll);
}
static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
const lv_area_t * clip_area = lv_event_get_param(e);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
draw_arcs(obj, clip_area, &scale_area);
draw_ticks_and_labels(obj, clip_area, &scale_area);
draw_needles(obj, clip_area, &scale_area);
lv_coord_t r_edge = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_edge;
scale_center.y = scale_area.y1 + r_edge;
lv_draw_rect_dsc_t mid_dsc;
lv_draw_rect_dsc_init(&mid_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &mid_dsc);
lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
lv_area_t nm_cord;
nm_cord.x1 = scale_center.x - w;
nm_cord.y1 = scale_center.y - h;
nm_cord.x2 = scale_center.x + w;
nm_cord.y2 = scale_center.y + h;
lv_draw_rect(&nm_cord, clip_area, &mid_dsc);
}
}
static void draw_arcs(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_draw_arc_dsc_t arc_dsc;
lv_draw_arc_dsc_init(&arc_dsc);
arc_dsc.rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
lv_coord_t r_out = lv_area_get_width(scale_area) / 2 ;
lv_point_t scale_center;
scale_center.x = scale_area->x1 + r_out;
scale_center.y = scale_area->y1 + r_out;
lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
if(indic->type != LV_METER_INDICATOR_TYPE_ARC) continue;
arc_dsc.color = indic->type_data.arc.color;
arc_dsc.width = indic->type_data.arc.width;
arc_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
lv_meter_scale_t * scale = indic->scale;
int32_t start_angle = lv_map(indic->start_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
int32_t end_angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_draw_arc(scale_center.x, scale_center.y, r_out + indic->type_data.arc.r_mod, start_angle, end_angle, clip_area, &arc_dsc);
}
}
static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_point_t p_center;
lv_coord_t r_edge = LV_MIN(lv_area_get_width(scale_area) / 2, lv_area_get_height(scale_area) / 2);
p_center.x = scale_area->x1 + r_edge;
p_center.y = scale_area->y1 + r_edge;
uint8_t i;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_TICKS, &line_dsc);
line_dsc.raw_end = 1;
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_obj_init_draw_label_dsc(obj, LV_PART_TICKS, &label_dsc);
lv_meter_scale_t * scale;
lv_obj_draw_part_dsc_t dsc;
lv_obj_draw_dsc_init(&dsc, clip_area);
#if LV_DRAW_COMPLEX
lv_draw_mask_radius_param_t inner_minor_mask;
lv_draw_mask_radius_param_t inner_major_mask;
lv_draw_mask_radius_param_t outer_mask;
#endif
_LV_LL_READ_BACK(&meter->scale_ll, scale) {
lv_coord_t r_out = r_edge + scale->r_mod;
lv_coord_t r_in_minor = r_out - scale->tick_length;
lv_coord_t r_in_major = r_out - scale->tick_major_length;
#if LV_DRAW_COMPLEX
lv_area_t area_inner_minor;
area_inner_minor.x1 = p_center.x - r_in_minor;
area_inner_minor.y1 = p_center.y - r_in_minor;
area_inner_minor.x2 = p_center.x + r_in_minor;
area_inner_minor.y2 = p_center.y + r_in_minor;
lv_draw_mask_radius_init(&inner_minor_mask, &area_inner_minor, LV_RADIUS_CIRCLE, true);
lv_area_t area_inner_major;
area_inner_major.x1 = p_center.x - r_in_major;
area_inner_major.y1 = p_center.y - r_in_major;
area_inner_major.x2 = p_center.x + r_in_major - 1;
area_inner_major.y2 = p_center.y + r_in_major - 1;
lv_draw_mask_radius_init(&inner_major_mask, &area_inner_major, LV_RADIUS_CIRCLE, true);
lv_area_t area_outer;
area_outer.x1 = p_center.x - r_out;
area_outer.y1 = p_center.y - r_out;
area_outer.x2 = p_center.x + r_out - 1;
area_outer.y2 = p_center.y + r_out - 1;
lv_draw_mask_radius_init(&outer_mask, &area_outer, LV_RADIUS_CIRCLE, false);
int16_t outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
int16_t inner_act_mask_id = -1; /*Will be added later*/
#endif
uint32_t minor_cnt = scale->tick_major_nth ? scale->tick_major_nth - 1 : 0xFFFF;
for(i = 0; i < scale->tick_cnt; i++) {
minor_cnt++;
bool major = false;
if(minor_cnt == scale->tick_major_nth) {
minor_cnt = 0;
major = true;
}
#if LV_DRAW_COMPLEX
inner_act_mask_id = lv_draw_mask_add(major ? &inner_major_mask : &inner_minor_mask, NULL);
#endif
int32_t value_of_line = lv_map(i, 0, scale->tick_cnt - 1, scale->min, scale->max);
lv_color_t line_color = major ? scale->tick_major_color : scale->tick_color;
lv_color_t line_color_ori = line_color;
lv_coord_t line_width_ori = major ? scale->tick_major_width : scale->tick_width;
lv_coord_t line_width = line_width_ori;
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
if(indic->type != LV_METER_INDICATOR_TYPE_SCALE_LINES) continue;
if(value_of_line >= indic->start_value && value_of_line <= indic->end_value) {
line_width += indic->type_data.scale_lines.width_mod;
if(indic->type_data.scale_lines.color_start.full == indic->type_data.scale_lines.color_end.full) {
line_color = indic->type_data.scale_lines.color_start;
} else {
lv_opa_t ratio;
if(indic->type_data.scale_lines.local_grad) {
ratio = lv_map(value_of_line, indic->start_value, indic->end_value, LV_OPA_TRANSP, LV_OPA_COVER);
} else {
ratio = lv_map(value_of_line, scale->min, scale->max, LV_OPA_TRANSP, LV_OPA_COVER);
}
line_color = lv_color_mix(indic->type_data.scale_lines.color_end, indic->type_data.scale_lines.color_start, ratio);
}
}
}
/*`* 256` for extra precision*/
int32_t angle_upscale = ((i * scale->angle_range) << 8) / (scale->tick_cnt - 1);
int32_t angle_low = (angle_upscale >> 8);
int32_t angle_high = angle_low + 1;
int32_t angle_rem = angle_upscale & 0xFF;
/*Interpolate sine and cos*/
int32_t sin_low = lv_trigo_sin(angle_low + scale->rotation);
int32_t sin_high = lv_trigo_sin(angle_high + scale->rotation);
int32_t sin_mid = (sin_low * (256 - angle_rem) + sin_high * angle_rem) >> 8;
int32_t cos_low = lv_trigo_cos(angle_low + scale->rotation);
int32_t cos_high = lv_trigo_cos(angle_high + scale->rotation);
int32_t cos_mid = (cos_low * (256 - angle_rem) + cos_high * angle_rem) >> 8;
line_dsc.color = line_color;
line_dsc.width = line_width;
#if LV_DRAW_COMPLEX
/*Use the interpolated angle to get the outer x and y coordinates.
*Draw a little bit longer lines to be sure the mask will clip them correctly*/
lv_point_t p_outer;
p_outer.x = (int32_t)(((int32_t)cos_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.x;
p_outer.y = (int32_t)(((int32_t)sin_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.y;
lv_draw_line(&p_outer, &p_center, clip_area, &line_dsc);
#else
/*Use the interpolated angle to get the outer and inner x and y coordinates.*/
lv_point_t p_outer;
p_outer.x = (int32_t)(((int32_t)cos_mid * (r_out) + 127) >> (LV_TRIGO_SHIFT)) + p_center.x;
p_outer.y = (int32_t)(((int32_t)sin_mid * (r_out) + 127) >> (LV_TRIGO_SHIFT)) + p_center.y;
lv_point_t p_inner;
lv_coord_t r_in = major ? r_in_major : r_in_minor;
p_inner.x = (int32_t)(((int32_t)cos_mid * (r_in) + 127) >> (LV_TRIGO_SHIFT)) + p_center.x;
p_inner.y = (int32_t)(((int32_t)sin_mid * (r_in) + 127) >> (LV_TRIGO_SHIFT)) + p_center.y;
lv_draw_line(&p_outer, &p_inner, clip_area, &line_dsc);
#endif
line_dsc.color = line_color_ori;
line_dsc.width = line_width_ori;
#if LV_DRAW_COMPLEX
lv_draw_mask_remove_id(inner_act_mask_id);
#endif
/*Draw the text*/
if(major) {
#if LV_DRAW_COMPLEX
lv_draw_mask_remove_id(outer_mask_id);
#endif
uint32_t r_text = r_in_major - scale->label_gap;
lv_point_t p;
p.x = (int32_t)((int32_t)((int32_t)cos_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.x;
p.y = (int32_t)((int32_t)((int32_t)sin_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.y;
lv_draw_label_dsc_t label_dsc_tmp;
lv_memcpy(&label_dsc_tmp, &label_dsc, sizeof(label_dsc_tmp));
dsc.id = i / scale->tick_major_nth;
dsc.value = value_of_line;
dsc.label_dsc = &label_dsc_tmp;
lv_snprintf(dsc.text, sizeof(dsc.text), "%d", value_of_line);
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc);
lv_point_t label_size;
lv_txt_get_size(&label_size, dsc.text, label_dsc.font, label_dsc.letter_space, label_dsc.line_space,
LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_area_t label_cord;
label_cord.x1 = p.x - label_size.x / 2;
label_cord.y1 = p.y - label_size.y / 2;
label_cord.x2 = label_cord.x1 + label_size.x;
label_cord.y2 = label_cord.y1 + label_size.y;
lv_draw_label(&label_cord, clip_area, &label_dsc, dsc.text, NULL);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &dsc);
#if LV_DRAW_COMPLEX
outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
#endif
}
}
#if LV_DRAW_COMPLEX
lv_draw_mask_remove_id(outer_mask_id);
#endif
}
}
static void draw_needles(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_coord_t r_edge = lv_area_get_width(scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area->x1 + r_edge;
scale_center.y = scale_area->y1 + r_edge;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &line_dsc);
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_INDICATOR, &img_dsc);
img_dsc.antialias = 1;
lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
lv_meter_scale_t * scale = indic->scale;
if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_coord_t r_out = r_edge + scale->r_mod + indic->type_data.needle_line.r_mod;
lv_point_t p_end;
p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
line_dsc.color = indic->type_data.needle_line.color;
line_dsc.width = indic->type_data.needle_line.width;
line_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
lv_draw_line(&scale_center, &p_end, clip_area, &line_dsc);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
if(indic->type_data.needle_img.src == NULL) continue;
int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_img_header_t info;
lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
lv_area_t a;
a.x1 = scale_center.x - indic->type_data.needle_img.pivot.x;
a.y1 = scale_center.y - indic->type_data.needle_img.pivot.y;
a.x2 = a.x1 + info.w - 1;
a.y2 = a.y1 + info.h - 1;
img_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
img_dsc.pivot.x = indic->type_data.needle_img.pivot.x;
img_dsc.pivot.y = indic->type_data.needle_img.pivot.y;
angle = angle * 10;
if(angle > 3600) angle -= 3600;
img_dsc.angle = angle;
lv_draw_img(&a, clip_area, indic->type_data.needle_img.src, &img_dsc);
}
}
}
static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value)
{
bool rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_out;
scale_center.y = scale_area.y1 + r_out;
r_out += indic->type_data.arc.r_mod;
lv_meter_scale_t * scale = indic->scale;
int32_t start_angle = lv_map(old_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
int32_t end_angle = lv_map(new_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
lv_area_t a;
lv_draw_arc_get_area(scale_center.x, scale_center.y, r_out, LV_MIN(start_angle, end_angle), LV_MAX(start_angle, end_angle), indic->type_data.arc.width, rounded, &a);
lv_obj_invalidate_area(obj, &a);
}
static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_out;
scale_center.y = scale_area.y1 + r_out;
lv_meter_scale_t * scale = indic->scale;
if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
r_out += scale->r_mod + indic->type_data.needle_line.r_mod;
lv_point_t p_end;
p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
lv_area_t a;
a.x1 = LV_MIN(scale_center.x, p_end.x) - indic->type_data.needle_line.width - 2;
a.y1 = LV_MIN(scale_center.y, p_end.y) - indic->type_data.needle_line.width - 2;
a.x2 = LV_MAX(scale_center.x, p_end.x) + indic->type_data.needle_line.width + 2;
a.y2 = LV_MAX(scale_center.y, p_end.y) + indic->type_data.needle_line.width + 2;
lv_obj_invalidate_area(obj, &a);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_img_header_t info;
lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
angle = angle * 10;
if(angle > 3600) angle -= 3600;
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, info.w, info.h, angle, LV_IMG_ZOOM_NONE, &indic->type_data.needle_img.pivot);
a.x1 += scale_center.x - 2;
a.y1 += scale_center.y - 2;
a.x2 += scale_center.x + 2;
a.y2 += scale_center.y + 2;
lv_obj_invalidate_area(obj, &a);
}
}
#endif

View File

@ -0,0 +1,243 @@
/**
* @file lv_meter.h
*
*/
#ifndef LV_METER_H
#define LV_METER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_METER != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t tick_color;
uint16_t tick_cnt;
uint16_t tick_length;
uint16_t tick_width;
lv_color_t tick_major_color;
uint16_t tick_major_nth;
uint16_t tick_major_length;
uint16_t tick_major_width;
int16_t label_gap;
int16_t label_color;
int32_t min;
int32_t max;
int16_t r_mod;
uint16_t angle_range;
int16_t rotation;
}lv_meter_scale_t;
typedef enum {
LV_METER_INDICATOR_TYPE_NEEDLE_IMG,
LV_METER_INDICATOR_TYPE_NEEDLE_LINE,
LV_METER_INDICATOR_TYPE_SCALE_LINES,
LV_METER_INDICATOR_TYPE_ARC,
}lv_meter_indicator_type_t;
typedef struct {
lv_meter_scale_t * scale;
lv_meter_indicator_type_t type;
lv_opa_t opa;
int32_t start_value;
int32_t end_value;
union {
struct {
const void * src;
lv_point_t pivot;
}needle_img;
struct {
uint16_t width;
int16_t r_mod;
lv_color_t color;
}needle_line;
struct {
uint16_t width;
const void * src;
lv_color_t color;
int16_t r_mod;
}arc;
struct {
int16_t width_mod;
lv_color_t color_start;
lv_color_t color_end;
uint8_t local_grad :1;
}scale_lines;
} type_data;
}lv_meter_indicator_t;
/*Data of line meter*/
typedef struct {
lv_obj_t obj;
lv_ll_t scale_ll;
lv_ll_t indicator_ll;
} lv_meter_t;
extern const lv_obj_class_t lv_meter_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a meter objects
* @param parent pointer to an object, it will be the parent of the new bar.
* @return pointer to the created meter
*/
lv_obj_t * lv_meter_create(lv_obj_t * parent);
/*=====================
* Add scale
*====================*/
/**
* Add a new scale to the meter.
* @param obj pointer to a meter object
* @return the new scale
* @note Indicators can be attached to scales.
*/
lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj);
/**
* Set the properties of the ticks of a scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param cnt number of tick lines
* @param width width of tick lines
* @param len length of tick lines
* @param color color of tick lines
*/
void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len, lv_color_t color);
/**
* Make some "normal" ticks major ticks and set their attributes.
* Texts with the current value are also added to the major ticks.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param nth make every Nth normal tick major tick. (start from the first on the left)
* @param width width of the major ticks
* @param len length of the major ticks
* @param color color of the major ticks
* @param label_gap gap between the major ticks and the labels
*/
void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width, uint16_t len, lv_color_t color, int16_t label_gap);
/**
* Set the value and angular range of a scale.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param min the minimum value
* @param max the maximal value
* @param angle_range the angular range of the scale
* @param rotation the angular offset from the 3 o'clock position (clock-wise)
*/
void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range, uint32_t rotation);
/*=====================
* Add indicator
*====================*/
/**
* Add a needle line indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param width width of the line
* @param color color of the line
* @param r_mod the radius modifier (added to the scale's radius) to get the lines length
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color, int16_t r_mod);
/**
* Add a needle image indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param src the image source of the indicator. path or pointer to ::lv_img_dsc_t
* @param pivot_x the X pivot point of the needle
* @param pivot_y the Y pivot point of the needle
* @return the new indicator
* @note the needle image should point to the right, like -O----->
*/
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src, lv_coord_t pivot_x, lv_coord_t pivot_y);
/**
* Add an arc indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param width width of the arc
* @param color color of the arc
* @param r_mod the radius modifier (added to the scale's radius) to get the outer radius of the arc
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color, int16_t r_mod);
/**
* Add a scale line indicator the scale. It will modify the ticks.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param color_start the start color
* @param color_end the end color
* @param local tell how to map start and end color. true: the indicator's start and end_value; false: the scale's min max value
* @param width_mod add this the affected tick's width
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start, lv_color_t color_end, bool local, int16_t width_mod);
/*=====================
* Set indicator value
*====================*/
/**
* Set the value of the indicator. It will set start and and value to the same value
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**
* Set the start value of the indicator.
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**
* Set the start value of the indicator.
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**********************
* MACROS
**********************/
#endif /*LV_USE_METER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_METER_H*/

View File

@ -0,0 +1,151 @@
/**
* @file lv_msgbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_msgbox.h"
#if LV_USE_MSGBOX
/*********************
* DEFINES
*********************/
#define LV_MSGBOX_FLAG_AUTO_PARENT LV_OBJ_FLAG_WIDGET_1 /*Mark that the parent was automatically created*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void msgbox_close_click_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_msgbox_class = {.base_class = &lv_obj_class};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * txt, const char * btn_txts[], bool add_close_btn)
{
LV_LOG_INFO("begin")
bool auto_parent = false;
if(parent == NULL) {
auto_parent = true;
parent = lv_obj_create(lv_scr_act());
lv_obj_remove_style_all(parent);
lv_obj_set_style_bg_color(parent, lv_palette_main(LV_PALETTE_GREY), 0);
lv_obj_set_style_bg_opa(parent, LV_OPA_50, 0);
lv_obj_set_size(parent, LV_PCT(100), LV_PCT(100));
}
lv_obj_t * mbox = lv_obj_class_create_obj(&lv_msgbox_class, parent);
lv_obj_class_init_obj(mbox);
LV_ASSERT_MALLOC(mbox);
if(mbox == NULL) return NULL;
if(auto_parent) lv_obj_add_flag(mbox, LV_MSGBOX_FLAG_AUTO_PARENT);
lv_obj_set_size(mbox, LV_DPI_DEF * 2, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(mbox, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(mbox, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_t * label;
label = lv_label_create(mbox);
lv_label_set_text(label, title);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
if(add_close_btn) lv_obj_set_flex_grow(label, 1);
else lv_obj_set_width(label, LV_PCT(100));
if(add_close_btn) {
lv_obj_t * close_btn = lv_btn_create(mbox);
lv_obj_set_ext_click_area(close_btn, LV_DPX(10));
lv_obj_add_event_cb(close_btn, msgbox_close_click_event_cb, LV_EVENT_CLICKED, NULL);
label = lv_label_create(close_btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
const lv_font_t * font = lv_obj_get_style_text_font(close_btn, LV_PART_MAIN);
lv_coord_t close_btn_size = lv_font_get_line_height(font) + LV_DPX(10);
lv_obj_set_size(close_btn, close_btn_size, close_btn_size);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
label = lv_label_create(mbox);
lv_label_set_text(label, txt);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_set_width(label, LV_PCT(100));
lv_obj_t * btns = lv_btnmatrix_create(mbox);
lv_btnmatrix_set_map(btns, btn_txts);
lv_btnmatrix_set_btn_ctrl_all(btns, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
uint32_t btn_cnt = 0;
while(btn_txts[btn_cnt] && btn_txts[btn_cnt][0] != '\0') {
btn_cnt++;
}
const lv_font_t * font = lv_obj_get_style_text_font(btns, LV_PART_ITEMS);
lv_coord_t btn_h = lv_font_get_line_height(font) + LV_DPI_DEF / 10;
lv_obj_set_size(btns, btn_cnt * (2 * LV_DPI_DEF / 3), btn_h);
lv_obj_add_flag(btns, LV_OBJ_FLAG_EVENT_BUBBLE); /*To see the event directly on the message box*/
return mbox;
}
lv_obj_t * lv_msgbox_get_title(lv_obj_t * mbox)
{
return lv_obj_get_child(mbox, 0);
}
lv_obj_t * lv_msgbox_get_close_btn(lv_obj_t * mbox)
{
lv_obj_t * obj = lv_obj_get_child(mbox, 1);
if(lv_obj_check_type(obj, &lv_btn_class)) return obj;
else return NULL;
}
lv_obj_t * lv_msgbox_get_text(lv_obj_t * mbox)
{
return lv_obj_get_child(mbox, lv_obj_get_child_cnt(mbox) - 2);
}
lv_obj_t * lv_msgbox_get_btns(lv_obj_t * mbox)
{
return lv_obj_get_child(mbox, lv_obj_get_child_cnt(mbox) - 1);
}
const char * lv_msgbox_get_active_btn_text(lv_obj_t * mbox)
{
lv_obj_t * btnm = lv_msgbox_get_btns(mbox);
return lv_btnmatrix_get_btn_text(btnm, lv_btnmatrix_get_selected_btn(btnm));
}
void lv_msgbox_close(lv_obj_t * mbox)
{
if(lv_obj_has_flag(mbox, LV_MSGBOX_FLAG_AUTO_PARENT)) lv_obj_del(lv_obj_get_parent(mbox));
else lv_obj_del(mbox);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void msgbox_close_click_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * mbox = lv_obj_get_parent(btn);
lv_msgbox_close(mbox);
}
#endif /*LV_USE_MSGBOX*/

View File

@ -0,0 +1,75 @@
/**
* @file lv_mbox.h
*
*/
#ifndef LV_MSGBOX_H
#define LV_MSGBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_MSGBOX
/*Testing of dependencies*/
#if LV_USE_BTNMATRIX == 0
#error "lv_mbox: lv_btnm is required. Enable it in lv_conf.h (LV_USE_BTNMATRIX 1) "
#endif
#if LV_USE_LABEL == 0
#error "lv_mbox: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1) "
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_msgbox_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a message box objects
* @param parent pointer to parent or NULL to create a full screen modal message box
* @param title the title of the message box
* @param txt the text of the message box
* @param btn_txts the buttons as an array of texts terminated by an "" element. E.g. {"btn1", "btn2", ""}
* @param add_close_btn true: add a close button
* @return pointer to the message box object
*/
lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * txt, const char * btn_txts[], bool add_close_btn);
lv_obj_t * lv_msgbox_get_title(lv_obj_t * mbox);
lv_obj_t * lv_msgbox_get_close_btn(lv_obj_t * mbox);
lv_obj_t * lv_msgbox_get_text(lv_obj_t * mbox);
lv_obj_t * lv_msgbox_get_btns(lv_obj_t * mbox);
const char * lv_msgbox_get_active_btn_text(lv_obj_t * mbox);
void lv_msgbox_close(lv_obj_t * mbox);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MSGBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MSGBOX_H*/

View File

@ -0,0 +1,968 @@
/**
* @file lv_span.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_span.h"
#if LV_USE_SPAN != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_spangroup_class
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_span_t * span;
const char * txt;
const lv_font_t * font;
uint16_t bytes;
lv_coord_t txt_w;
lv_coord_t line_h;
lv_coord_t letter_space;
} lv_snippet_t;
struct _snippet_stack {
lv_snippet_t stack[LV_SPAN_SNIPPET_STACK_SIZE];
uint16_t index;
};
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_main(lv_event_t * e);
static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span);
static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span);
static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span);
static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span);
static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span);
static lv_opa_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span);
static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span);
static inline void span_text_check(const char ** text);
static inline bool is_break_char(uint32_t letter);
static void get_txt_coords(const lv_obj_t * span, lv_area_t * area);
static void lv_draw_span(lv_obj_t * spans, const lv_area_t * coords, const lv_area_t * mask);
static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font, lv_coord_t letter_space,
lv_coord_t max_width, lv_text_flag_t flag, lv_coord_t * use_width,
uint32_t * end_ofs);
static void lv_snippet_clear(void);
static uint16_t lv_get_snippet_cnt();
static void lv_snippet_push(lv_snippet_t * item);
static lv_snippet_t * lv_get_snippet(uint16_t index);
/**********************
* STATIC VARIABLES
**********************/
static struct _snippet_stack snippet_stack;
const lv_obj_class_t lv_spangroup_class = {
.base_class = &lv_obj_class,
.constructor_cb = lv_spangroup_constructor,
.destructor_cb = lv_spangroup_destructor,
.event_cb = lv_spangroup_event,
.instance_size = sizeof(lv_spangroup_t),
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a spangroup objects
* @param par pointer to an object, it will be the parent of the new spangroup
* @return pointer to the created spangroup
*/
lv_obj_t * lv_spangroup_create(lv_obj_t * par)
{
lv_obj_t * obj = lv_obj_class_create_obj(&lv_spangroup_class, par);
lv_obj_class_init_obj(obj);
return obj;
}
/**
* Create a span string descriptor and add to spangroup.
* @param obj pointer to a spangroup object.
* @return pointer to the created span.
*/
lv_span_t * lv_spangroup_new_span(lv_obj_t * obj)
{
if(obj == NULL) {
return NULL;
}
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
lv_span_t * span = _lv_ll_ins_tail(&spans->child_ll);
LV_ASSERT_MALLOC(span);
lv_style_init(&span->style);
span->txt = (char *)"";
span->static_flag = 1;
lv_obj_invalidate(obj);
return span;
}
/**
* Remove the span from the spangroup and free memory.
* @param obj pointer to a spangroup object.
* @param span pointer to a span.
*/
void lv_spangroup_del_span(lv_obj_t * obj, lv_span_t * span)
{
if(obj == NULL) {
return;
}
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
lv_span_t * cur_span;
_LV_LL_READ(&spans->child_ll, cur_span) {
if(cur_span == span) {
_lv_ll_remove(&spans->child_ll, cur_span);
if(cur_span->txt && cur_span->static_flag == 0) {
lv_mem_free(cur_span->txt);
}
lv_mem_free(cur_span);
break;
}
}
lv_spangroup_refr_mode(obj);
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a span. Memory will be allocated to store the text by the span.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text(lv_span_t * span, const char * text)
{
if(span == NULL || text == NULL) {
return;
}
if(span->txt == NULL || span->static_flag == 1) {
span->txt = lv_mem_alloc(strlen(text) + 1);
}
else {
lv_mem_realloc(span->txt, strlen(text) + 1);
}
span->static_flag = 0;
strcpy(span->txt, text);
}
/**
* Set a static text. It will not be saved by the span so the 'text' variable
* has to be 'alive' while the span exist.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text_static(lv_span_t * span, const char * text)
{
if(span == NULL || text == NULL) {
return;
}
if(span->txt && span->static_flag == 0) {
lv_mem_free(span->txt);
}
span->static_flag = 1;
span->txt = (char *)text;
}
/**
* Set the align of the spangroup.
* @param obj pointer to a spangroup object.
* @param align see lv_text_align_t for details.
*/
void lv_spangroup_set_align(lv_obj_t * obj, lv_text_align_t align)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(spans->align == align) return;
spans->align = align;
lv_obj_invalidate(obj);
}
/**
* Set the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @param overflow see lv_span_overflow_t for details.
*/
void lv_spangroup_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(spans->overflow == overflow) return;
spans->overflow = overflow;
lv_obj_invalidate(obj);
}
/**
* Set the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @param indent The first line indentation
*/
void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(spans->indent == indent) return;
spans->indent = indent;
lv_obj_invalidate(obj);
}
/**
* Set the mode of the spangroup.
* @param obj pointer to a spangroup object.
* @param mode see lv_span_mode_t for details.
*/
void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(spans->mode == mode) return;
spans->mode = mode;
lv_spangroup_refr_mode(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* get the align of the spangroup.
* @param obj pointer to a spangroup object.
* @return the align value.
*/
lv_text_align_t lv_spangroup_get_align(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
return spans->align;
}
/**
* get the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @return the overflow value.
*/
lv_span_overflow_t lv_spangroup_get_overflow(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
return spans->overflow;
}
/**
* get the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @return the indent value.
*/
lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
return spans->indent;
}
/**
* Set the mode of the spangroup.
* @param obj pointer to a spangroup object.
* @return the mode value.
*/
lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
return spans->mode;
}
/**
* update the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
void lv_spangroup_refr_mode(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(_lv_ll_get_head(&spans->child_ll) == NULL) {
return;
}
if(spans->mode == LV_SPAN_MODE_EXPAND) {
lv_coord_t width = lv_spangroup_get_expand_width(obj);
lv_coord_t height = lv_spangroup_get_max_line_h(obj);
lv_coord_t top_pad = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bottom_pad = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_obj_set_width(obj, width + spans->indent);
lv_obj_set_height(obj, height + top_pad + bottom_pad);
}
else if(spans->mode == LV_SPAN_MODE_BREAK) {
lv_coord_t height = lv_spangroup_get_expand_height(obj, lv_obj_get_width(obj));
lv_obj_set_height(obj, height);
}
lv_obj_invalidate(obj);
}
/**
* get max line height of all span in the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_max_line_h(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
lv_coord_t max_line_h = 0;
lv_span_t * cur_span;
_LV_LL_READ(&spans->child_ll, cur_span) {
const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span);
lv_coord_t line_h = lv_font_get_line_height(font);
if(line_h > max_line_h) {
max_line_h = line_h;
}
}
return max_line_h;
}
/**
* get the width when all span of spangroup on a line. include spangroup pad.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_width(lv_obj_t * obj)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(_lv_ll_get_head(&spans->child_ll) == NULL) {
return 0;
}
lv_coord_t width = 0;
lv_span_t * cur_span;
_LV_LL_READ(&spans->child_ll, cur_span) {
const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span);
lv_coord_t letter_space = lv_span_get_style_text_letter_space(obj, cur_span);
uint32_t j = 0;
const char * cur_txt = cur_span->txt;
span_text_check(&cur_txt);
while(cur_txt[j] != 0) {
uint32_t letter = _lv_txt_encoded_next(cur_txt, &j);
uint32_t letter_next = _lv_txt_encoded_next(&cur_txt[j], NULL);
int32_t letter_w = lv_font_get_glyph_width(font, letter, letter_next);
width = width + letter_w + letter_space;
}
}
lv_coord_t left_pad = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t right_pad = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
width = width + left_pad + right_pad;
return width;
}
/**
* get the height with width fixed. the height include spangroup pad.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width)
{
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
if(_lv_ll_get_head(&spans->child_ll) == NULL) {
return 0;
}
/* init draw variable */
lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE;
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t left_pad = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t right_pad = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t max_width = width - left_pad - right_pad;
lv_coord_t max_w = max_width - spans->indent; /* first line need minus indent */
/* coords of draw span-txt */
lv_point_t txt_pos;
txt_pos.y = 0;
txt_pos.x = 0 + spans->indent; /* first line need add indent */
lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll);
const char * cur_txt = cur_span->txt;
span_text_check(&cur_txt);
uint32_t cur_txt_ofs = 0;
lv_snippet_t snippet; /* use to save cur_span info and push it to stack */
memset(&snippet, 0, sizeof(snippet));
/* the loop control how many lines need to draw */
while(cur_span) {
int snippet_cnt = 0;
lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */
/* the loop control to find a line and push the relevant span info into stack */
while(1) {
/* switch to the next span when current is end */
if(cur_txt[cur_txt_ofs] == '\0') {
cur_span = _lv_ll_get_next(&spans->child_ll, cur_span);
if(cur_span == NULL) break;
cur_txt = cur_span->txt;
span_text_check(&cur_txt);
cur_txt_ofs = 0;
}
/* init span info to snippet. */
if(cur_txt_ofs == 0) {
snippet.span = cur_span;
snippet.font = lv_span_get_style_text_font(obj, cur_span);
snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span);
snippet.line_h = lv_font_get_line_height(snippet.font) + line_space;
}
/* get current span text line info */
uint32_t next_ofs = 0;
lv_coord_t use_width = 0;
bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space,
max_w, txt_flag, &use_width, &next_ofs);
/* break word deal width */
if(isfill && next_ofs > 0 && snippet_cnt > 0) {
uint32_t letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs - 1];
if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) {
letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs];
if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) {
break;
}
}
}
snippet.txt = &cur_txt[cur_txt_ofs];
snippet.bytes = next_ofs;
snippet.txt_w = use_width;
cur_txt_ofs += next_ofs;
if(max_line_h < snippet.line_h) {
max_line_h = snippet.line_h;
}
snippet_cnt ++;
if(isfill) {
break;
}
max_w -= use_width;
}
/* next line init */
txt_pos.x = 0;
txt_pos.y += max_line_h;
max_w = max_width;
}
lv_coord_t top_pad = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bottom_pad = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
txt_pos.y = txt_pos.y + top_pad + bottom_pad - line_space;
return txt_pos.y;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
_lv_ll_init(&spans->child_ll, sizeof(lv_span_t));
spans->indent = 0;
spans->align = LV_TEXT_ALIGN_LEFT;
spans->mode = LV_SPAN_MODE_FIXED;
spans->overflow = LV_SPAN_OVERFLOW_CLIP;
}
static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll);
while(cur_span) {
_lv_ll_remove(&spans->child_ll, cur_span);
if(cur_span->txt && cur_span->static_flag == 0) {
lv_mem_free(cur_span->txt);
}
lv_mem_free(cur_span);
cur_span = _lv_ll_get_head(&spans->child_ll);
}
}
static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/* Call the ancestor's event handler */
if(lv_obj_event_base(MY_CLASS, e) != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
draw_main(e);
}
else if(code == LV_EVENT_STYLE_CHANGED) {
lv_spangroup_refr_mode(obj);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
lv_spangroup_refr_mode(obj);
}
}
static void draw_main(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_area_t txt_coords;
get_txt_coords(obj, &txt_coords);
lv_area_t txt_clip;
bool is_common = _lv_area_intersect(&txt_clip, clip_area, &txt_coords);
if(!is_common) return;
lv_draw_span(obj, &txt_coords, clip_area);
}
static void get_txt_coords(const lv_obj_t * obj, lv_area_t * area)
{
lv_obj_get_coords(obj, area);
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
area->x1 += left;
area->x2 -= right;
area->y1 += top;
area->y2 -= bottom;
}
/**
* @return true for txt fill the max_width.
*/
static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font,
lv_coord_t letter_space, lv_coord_t max_width, lv_text_flag_t flag,
lv_coord_t * use_width, uint32_t * end_ofs)
{
uint32_t ofs = _lv_txt_get_next_line(txt, font, letter_space, max_width, flag);
lv_coord_t width = lv_txt_get_width(txt, ofs, font, letter_space, flag);
*end_ofs = ofs;
*use_width = width;
if(txt[ofs] == '\0' && width < max_width) {
return false;
}
else {
return true;
}
}
static void lv_snippet_push(lv_snippet_t * item)
{
if(snippet_stack.index < LV_SPAN_SNIPPET_STACK_SIZE) {
memcpy(&snippet_stack.stack[snippet_stack.index], item, sizeof(lv_snippet_t));
snippet_stack.index++;
}
else {
LV_LOG_ERROR("span draw stack overflow, please set LV_SPAN_SNIPPET_STACK_SIZE too larger");
}
}
static uint16_t lv_get_snippet_cnt()
{
return snippet_stack.index;
}
static lv_snippet_t * lv_get_snippet(uint16_t index)
{
return &snippet_stack.stack[index];
}
static void lv_snippet_clear(void)
{
snippet_stack.index = 0;
}
static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span)
{
const lv_font_t * font;
lv_style_value_t value;
lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_FONT, &value);
if(res != LV_RES_OK) {
font = lv_obj_get_style_text_font(par, LV_PART_MAIN);
}
else {
font = (const lv_font_t *)value.ptr;
}
return font;
}
static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span)
{
lv_coord_t letter_space;
lv_style_value_t value;
lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_LETTER_SPACE, &value);
if(res != LV_RES_OK) {
letter_space = lv_obj_get_style_text_letter_space(par, LV_PART_MAIN);
}
else {
letter_space = (lv_coord_t)value.num;
}
return letter_space;
}
static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span)
{
lv_style_value_t value;
lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_COLOR, &value);
if(res != LV_RES_OK) {
value.color = lv_obj_get_style_text_color(par, LV_PART_MAIN);
}
return value.color;
}
static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span)
{
lv_opa_t opa;
lv_style_value_t value;
lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_OPA, &value);
if(res != LV_RES_OK) {
opa = (lv_opa_t)lv_obj_get_style_text_opa(par, LV_PART_MAIN);
}
else {
opa = (lv_opa_t)value.num;
}
return opa;
}
static lv_blend_mode_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span)
{
lv_blend_mode_t mode;
lv_style_value_t value;
lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_BLEND_MODE, &value);
if(res != LV_RES_OK) {
mode = (lv_blend_mode_t)lv_obj_get_style_blend_mode(par, LV_PART_MAIN);
}
else {
mode = (lv_blend_mode_t)value.num;
}
return mode;
}
static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span)
{
LV_UNUSED(par);
int32_t decor;
lv_style_value_t value;
lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_DECOR, &value);
if(res != LV_RES_OK) {
decor = LV_TEXT_DECOR_NONE;
}
else {
decor = (int32_t)value.num;
}
return decor;
}
static inline void span_text_check(const char ** text)
{
if(*text == NULL) {
*text = "";
LV_LOG_ERROR("occur an error that span text == NULL");
}
}
static inline bool is_break_char(uint32_t letter)
{
uint8_t i;
bool ret = false;
/*Compare the letter to TXT_BREAK_CHARS*/
for(i = 0; LV_TXT_BREAK_CHARS[i] != '\0'; i++) {
if(letter == (uint32_t)LV_TXT_BREAK_CHARS[i]) {
ret = true; /*If match then it is break char*/
break;
}
}
return ret;
}
/**
* draw span group
* @param spans obj handle
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
*/
static void lv_draw_span(lv_obj_t * obj, const lv_area_t * coords, const lv_area_t * mask)
{
/* return if no draw area */
lv_area_t clipped_area;
bool clip_ok = _lv_area_intersect(&clipped_area, coords, mask);
if(!clip_ok) return;
lv_spangroup_t * spans = (lv_spangroup_t *)obj;
/* return if not span */
if(_lv_ll_get_head(&spans->child_ll) == NULL) {
return;
}
/* init draw variable */
lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE;
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);;
lv_coord_t max_width = lv_area_get_width(coords);
lv_coord_t max_w = max_width - spans->indent; /* first line need minus indent */
/* coords of draw span-txt */
lv_point_t txt_pos;
txt_pos.y = coords->y1;
txt_pos.x = coords->x1 + spans->indent; /* first line need add indent */
lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll);
const char * cur_txt = cur_span->txt;
span_text_check(&cur_txt);
uint32_t cur_txt_ofs = 0;
lv_snippet_t snippet; /* use to save cur_span info and push it to stack */
memset(&snippet, 0, sizeof(snippet));
/* the loop control how many lines need to draw */
while(cur_span) {
lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */
lv_snippet_clear();
/* the loop control to find a line and push the relevant span info into stack */
while(1) {
/* switch to the next span when current is end */
if(cur_txt[cur_txt_ofs] == '\0') {
cur_span = _lv_ll_get_next(&spans->child_ll, cur_span);
if(cur_span == NULL) break;
cur_txt = cur_span->txt;
span_text_check(&cur_txt);
cur_txt_ofs = 0;
}
/* init span info to snippet. */
if(cur_txt_ofs == 0) {
snippet.span = cur_span;
snippet.font = lv_span_get_style_text_font(obj, cur_span);
snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span);
snippet.line_h = lv_font_get_line_height(snippet.font) + line_space;
}
if(spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS) {
/* span txt overflow, don't push */
if(txt_pos.y + snippet.line_h - line_space > coords->y2 + 1) {
break;
}
}
/* get current span text line info */
uint32_t next_ofs = 0;
lv_coord_t use_width = 0;
bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space,
max_w, txt_flag, &use_width, &next_ofs);
/* break word deal width */
if(isfill && next_ofs > 0 && lv_get_snippet_cnt() > 0) {
uint32_t letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs - 1];
if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) {
letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs];
if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) {
break;
}
}
}
snippet.txt = &cur_txt[cur_txt_ofs];
snippet.bytes = next_ofs;
snippet.txt_w = use_width;
cur_txt_ofs += next_ofs;
if(max_line_h < snippet.line_h) {
max_line_h = snippet.line_h;
}
lv_snippet_push(&snippet);
if(isfill) {
break;
}
max_w -= use_width;
}
/* start current line deal width */
uint16_t item_cnt = lv_get_snippet_cnt();
if(item_cnt == 0) { /* break if stack is empty */
break;
}
/*Go the first visible line*/
if(txt_pos.y + max_line_h < mask->y1) {
goto Next_line_init;
}
/* overflow deal width */
bool ellipsis_valid = false;
if(spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS) {
lv_coord_t next_line_h = snippet.line_h;
if(cur_txt[cur_txt_ofs] == '\0') { /* current span deal with ok, need get next line first line height */
next_line_h = 0;
if(cur_span) {
lv_span_t * next_span = _lv_ll_get_next(&spans->child_ll, cur_span);
if(next_span) { /* have the next line */
next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space;
}
}
}
if(txt_pos.y + max_line_h + next_line_h > coords->y2 + 1) {
ellipsis_valid = true;
}
}
/* align deal with */
if(spans->align != LV_TEXT_ALIGN_LEFT) {
lv_coord_t align_ofs = 0;
lv_coord_t txts_w = 0;
for(int i = 0; i < item_cnt; i++) {
lv_snippet_t * pinfo = lv_get_snippet(i);
txts_w += pinfo->txt_w;
}
if(spans->align == LV_TEXT_ALIGN_CENTER) {
align_ofs = (max_width - txts_w) / 2;
}
else if(spans->align == LV_TEXT_ALIGN_RIGHT) {
align_ofs = max_width - txts_w;
}
txt_pos.x += align_ofs;
}
/* draw line letters */
for(int i = 0; i < item_cnt; i++) {
lv_snippet_t * pinfo = lv_get_snippet(i);
/* bidi deal with:todo */
const char * bidi_txt = pinfo->txt;
lv_point_t pos;
pos.x = txt_pos.x;
pos.y = txt_pos.y + max_line_h - pinfo->line_h;
lv_color_t letter_color = lv_span_get_style_text_color(obj, pinfo->span);
lv_opa_t letter_opa = lv_span_get_style_text_opa(obj, pinfo->span);
lv_blend_mode_t blend_mode = lv_span_get_style_text_blend_mode(obj, pinfo->span);
uint32_t txt_bytes = pinfo->bytes;
/* overflow */
uint16_t dot_letter_w = 0;
uint16_t dot_width = 0;
if(ellipsis_valid) {
txt_bytes = strlen(bidi_txt);
dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.');
dot_width = dot_letter_w * 3;
}
lv_coord_t ellipsis_width = coords->x1 + max_width - dot_width;
uint32_t j = 0;
while(j < txt_bytes) {
/* skip invalid fields */
if(pos.x > clipped_area.x2) {
break;
}
uint32_t letter = _lv_txt_encoded_next(bidi_txt, &j);
uint32_t letter_next = _lv_txt_encoded_next(&bidi_txt[j], NULL);
int32_t letter_w = lv_font_get_glyph_width(pinfo->font, letter, letter_next);
/* skip invalid fields */
if(pos.x + letter_w + pinfo->letter_space < clipped_area.x1) {
if(letter_w > 0) {
pos.x = pos.x + letter_w + pinfo->letter_space;
}
continue;
}
if(ellipsis_valid && pos.x + letter_w + pinfo->letter_space > ellipsis_width) {
for(int ell = 0; ell < 3; ell++) {
lv_draw_letter(&pos, &clipped_area, pinfo->font, '.', letter_color, letter_opa, blend_mode);
pos.x = pos.x + dot_letter_w + pinfo->letter_space;
}
break;
}
else {
lv_draw_letter(&pos, &clipped_area, pinfo->font, letter, letter_color, letter_opa, blend_mode);
if(letter_w > 0) {
pos.x = pos.x + letter_w + pinfo->letter_space;
}
}
}
if(ellipsis_valid && i == item_cnt - 1 && pos.x <= ellipsis_width) {
for(int ell = 0; ell < 3; ell++) {
lv_draw_letter(&pos, &clipped_area, pinfo->font, '.', letter_color, letter_opa, blend_mode);
pos.x = pos.x + dot_letter_w + pinfo->letter_space;
}
}
/* draw decor */
lv_text_decor_t decor = lv_span_get_style_text_decor(obj, pinfo->span);
if(decor != LV_TEXT_DECOR_NONE) {
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = letter_color;
line_dsc.width = pinfo->font->underline_thickness ? pinfo->font->underline_thickness : 1;
line_dsc.opa = letter_opa;
line_dsc.blend_mode = blend_mode;
if(decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_point_t p1;
lv_point_t p2;
p1.x = txt_pos.x;
p1.y = pos.y + (pinfo->line_h / 2) + line_dsc.width / 2;
p2.x = pos.x;
p2.y = p1.y;
lv_draw_line(&p1, &p2, mask, &line_dsc);
}
if(decor & LV_TEXT_DECOR_UNDERLINE) {
lv_point_t p1;
lv_point_t p2;
p1.x = txt_pos.x;
p1.y = pos.y + pinfo->line_h + line_dsc.width / 2;
p2.x = pos.x;
p2.y = p1.y;
lv_draw_line(&p1, &p2, &clipped_area, &line_dsc);
}
}
txt_pos.x = pos.x;
}
Next_line_init:
/* next line init */
txt_pos.x = coords->x1;
txt_pos.y += max_line_h;
if(txt_pos.y > clipped_area.y2 + 1) {
return;
}
max_w = max_width;
}
}
#endif

View File

@ -0,0 +1,203 @@
/**
* @file lv_span.h
*
*/
#ifndef LV_SPAN_H
#define LV_SPAN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPAN != 0
/*********************
* DEFINES
*********************/
#ifndef LV_SPAN_SNIPPET_STACK_SIZE
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_SPAN_OVERFLOW_CLIP,
LV_SPAN_OVERFLOW_ELLIPSIS,
};
typedef uint8_t lv_span_overflow_t;
enum {
LV_SPAN_MODE_FIXED, /**< fixed the obj size*/
LV_SPAN_MODE_EXPAND, /**< Expand the object size to the text size*/
LV_SPAN_MODE_BREAK, /**< Keep width, break the too long lines and expand height*/
};
typedef uint8_t lv_span_mode_t;
typedef struct {
char * txt;
lv_style_t style;
uint8_t static_flag : 1;
} lv_span_t;
/** Data of label*/
typedef struct {
lv_obj_t obj;
lv_coord_t indent;
lv_ll_t child_ll;
uint8_t mode : 2;
uint8_t align : 2;
uint8_t overflow : 1;
} lv_spangroup_t;
extern const lv_obj_class_t lv_spangroup_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a spangroup objects
* @param par pointer to an object, it will be the parent of the new spangroup
* @return pointer to the created spangroup
*/
lv_obj_t * lv_spangroup_create(lv_obj_t * par);
/**
* Create a span string descriptor and add to spangroup.
* @param obj pointer to a spangroup object.
* @return pointer to the created span.
*/
lv_span_t * lv_spangroup_new_span(lv_obj_t * obj);
/**
* Remove the span from the spangroup and free memory.
* @param obj pointer to a spangroup object.
* @param span pointer to a span.
*/
void lv_spangroup_del_span(lv_obj_t * obj, lv_span_t * span);
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a span. Memory will be allocated to store the text by the span.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text(lv_span_t * span, const char * text);
/**
* Set a static text. It will not be saved by the span so the 'text' variable
* has to be 'alive' while the span exist.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text_static(lv_span_t * span, const char * text);
/**
* Set the align of the spangroup.
* @param obj pointer to a spangroup object.
* @param align see lv_text_align_t for details.
*/
void lv_spangroup_set_align(lv_obj_t * obj, lv_text_align_t align);
/**
* Set the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @param overflow see lv_span_overflow_t for details.
*/
void lv_spangroup_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow);
/**
* Set the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @param indent The first line indentation
*/
void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent);
/**
* Set the mode of the spangroup.
* @param obj pointer to a spangroup object.
* @param mode see lv_span_mode_t for details.
*/
void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode);
/*=====================
* Getter functions
*====================*/
/**
* get the align of the spangroup.
* @param obj pointer to a spangroup object.
* @return the align value.
*/
lv_text_align_t lv_spangroup_get_align(lv_obj_t * obj);
/**
* get the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @return the overflow value.
*/
lv_span_overflow_t lv_spangroup_get_overflow(lv_obj_t * obj);
/**
* get the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @return the indent value.
*/
lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj);
/**
* get the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj);
/**
* get max line height of all span in the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_max_line_h(lv_obj_t * obj);
/**
* get the width when all span of spangroup on a line. include spangroup pad.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_width(lv_obj_t * obj);
/**
* get the height with width fixed. the height include spangroup pad.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width);
/*=====================
* Other functions
*====================*/
/**
* update the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
void lv_spangroup_refr_mode(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPAN*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_SPAN_H*/

View File

@ -0,0 +1,463 @@
/**
* @file lv_spinbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinbox.h"
#if LV_USE_SPINBOX
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_spinbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_spinbox_updatevalue(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_spinbox_class = {
.constructor_cb = lv_spinbox_constructor,
.event_cb = lv_spinbox_event,
.instance_size = sizeof(lv_spinbox_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.base_class = &lv_textarea_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin")
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param spinbox pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(i > spinbox->range_max) i = spinbox->range_max;
if(i < spinbox->range_min) i = spinbox->range_min;
spinbox->value = i;
lv_spinbox_updatevalue(obj);
}
/**
* 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 * obj, bool b)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->rollover = b;
}
/**
* 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 * obj, uint8_t digit_count, uint8_t separator_position)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
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(spinbox->range_max > max_val - 1) spinbox->range_max = max_val - 1;
if(spinbox->range_min < - max_val + 1) spinbox->range_min = - max_val + 1;
}
spinbox->digit_count = digit_count;
spinbox->dec_point_pos = separator_position;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement
*/
void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->step = step;
lv_spinbox_updatevalue(obj);
}
/**
* 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 * obj, int32_t range_min, int32_t range_max)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->range_max = range_max;
spinbox->range_min = range_min;
if(spinbox->value > spinbox->range_max) spinbox->value = spinbox->range_max;
if(spinbox->value < spinbox->range_min) spinbox->value = spinbox->range_min;
lv_spinbox_updatevalue(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* 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 * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->value;
}
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param spinbox pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->step;
}
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t new_step = spinbox->step / 10;
if((new_step) > 0)
spinbox->step = new_step;
else
spinbox->step = 1;
lv_spinbox_updatevalue(obj);
}
/**
* Select next higher digit for edition
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * 10;
if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Get spinbox rollover function status
* @param spinbox pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->rollover;
}
/**
* Increment spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value + spinbox->step <= spinbox->range_max) {
/*Special mode when zero crossing*/
if((spinbox->value + spinbox->step) > 0 && spinbox->value < 0) spinbox->value = -spinbox->value;
spinbox->value += spinbox->step;
}
else {
// Rollover?
if((spinbox->rollover) && (spinbox->value == spinbox->range_max))
spinbox->value = spinbox->range_min;
else
spinbox->value = spinbox->range_max;
}
lv_spinbox_updatevalue(obj);
}
/**
* Decrement spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value - spinbox->step >= spinbox->range_min) {
/*Special mode when zero crossing*/
if((spinbox->value - spinbox->step) < 0 && spinbox->value > 0) spinbox->value = -spinbox->value;
spinbox->value -= spinbox->step;
}
else {
/*Rollover?*/
if((spinbox->rollover) && (spinbox->value == spinbox->range_min))
spinbox->value = spinbox->range_max;
else
spinbox->value = spinbox->range_min;
}
lv_spinbox_updatevalue(obj);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_LOG_TRACE("begin");
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
/*Initialize the allocated 'ext'*/
spinbox->value = 0;
spinbox->dec_point_pos = 0;
spinbox->digit_count = 5;
spinbox->step = 1;
spinbox->range_max = 99999;
spinbox->range_min = -99999;
spinbox->rollover = false;
lv_textarea_set_one_line(obj, true);
lv_textarea_set_cursor_click_pos(obj, true);
lv_obj_set_width(obj, LV_DPI_DEF);
lv_spinbox_updatevalue(obj);
LV_LOG_TRACE("Spinbox constructor finished");
}
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = LV_RES_OK;
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(code == LV_EVENT_RELEASED) {
/*If released with an ENCODER then move to the next digit*/
lv_indev_t * indev = lv_indev_get_act();
if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) {
if(lv_group_get_editing(lv_obj_get_group(obj))) {
if(spinbox->step > 1) {
lv_spinbox_step_next(obj);
}
else {
/*Restart from the MSB*/
spinbox->step = 1;
uint32_t i;
for(i = 0; i < spinbox->digit_count; i++) {
int32_t new_step = spinbox->step * 10;
if(new_step >= spinbox->range_max) break;
spinbox->step = new_step;
}
lv_spinbox_step_prev(obj);
}
}
}
/*The cursor has been positioned to a digit.
* Set `step` accordingly*/
else {
const char * txt = lv_textarea_get_text(obj);
size_t txt_len = strlen(txt);
if(txt[spinbox->ta.cursor.pos] == '.') {
lv_textarea_cursor_left(obj);
}
else if(spinbox->ta.cursor.pos == (uint32_t)txt_len) {
lv_textarea_set_cursor_pos(obj, txt_len - 1);
}
else if(spinbox->ta.cursor.pos == 0 && spinbox->range_min < 0) {
lv_textarea_set_cursor_pos(obj, 1);
}
size_t len = spinbox->digit_count - 1;
uint16_t cp = spinbox->ta.cursor.pos;
if(spinbox->ta.cursor.pos > spinbox->dec_point_pos && spinbox->dec_point_pos != 0) cp--;
uint32_t pos = len - cp;
if(spinbox->range_min < 0) pos++;
spinbox->step = 1;
uint16_t i;
for(i = 0; i < pos; i++) spinbox->step *= 10;
}
}
else if(code == LV_EVENT_KEY) {
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_increment(obj);
else
lv_spinbox_step_next(obj);
}
else if(c == LV_KEY_LEFT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_decrement(obj);
else
lv_spinbox_step_prev(obj);
}
else if(c == LV_KEY_UP) {
lv_spinbox_increment(obj);
}
else if(c == LV_KEY_DOWN) {
lv_spinbox_decrement(obj);
}
else {
lv_textarea_add_char(obj, c);
}
}
}
static void lv_spinbox_updatevalue(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
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(spinbox->range_min < 0) { // hide sign if there are only positive values
/*Add the sign*/
(*buf_p) = spinbox->value >= 0 ? '+' : '-';
buf_p++;
}
else {
/*Cursor need shift to left*/
cur_shift_left++;
}
int32_t i;
char digits[LV_SPINBOX_MAX_DIGIT_COUNT + 4];
/*Convert the numbers to string (the sign is already handled so always covert positive number)*/
lv_snprintf(digits, sizeof(digits), "%d", LV_ABS(spinbox->value));
/*Add leading zeros*/
int lz_cnt = spinbox->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 = (spinbox->dec_point_pos == 0) ? spinbox->digit_count : spinbox->dec_point_pos;
/*Add the decimal part*/
for(i = 0; i < intDigits && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
if(spinbox->dec_point_pos != 0) {
/*Insert the decimal point*/
(*buf_p) = '.';
buf_p++;
for(/*Leave i*/; i < spinbox->digit_count && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
}
/*Refresh the text*/
lv_textarea_set_text(obj, (char *)buf);
/*Set the cursor position*/
int32_t step = spinbox->step;
uint8_t cur_pos = (uint8_t)spinbox->digit_count;
while(step >= 10) {
step /= 10;
cur_pos--;
}
if(cur_pos > intDigits) cur_pos++; /*Skip the decimal point*/
cur_pos -= cur_shift_left;
lv_textarea_set_cursor_pos(obj, cur_pos);
}
#endif /*LV_USE_SPINBOX*/

View File

@ -0,0 +1,163 @@
/**
* @file lv_spinbox.h
*
*/
#ifndef LV_SPINBOX_H
#define LV_SPINBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPINBOX
/*Testing of dependencies*/
#if LV_USE_TEXTAREA == 0
#error "lv_spinbox: lv_ta is required. Enable it in lv_conf.h (LV_USE_TEXTAREA 1) "
#endif
/*********************
* DEFINES
*********************/
#define LV_SPINBOX_MAX_DIGIT_COUNT 10
/**********************
* TYPEDEFS
**********************/
/*Data of spinbox*/
typedef struct {
lv_textarea_t ta; /*Ext. of ancestor*/
/*New data for this type*/
int32_t value;
int32_t range_max;
int32_t range_min;
int32_t step;
uint16_t digit_count : 4;
uint16_t dec_point_pos : 4; /*if 0, there is no separator and the number is an integer*/
uint16_t rollover : 1; // Set to true for rollover functionality
} lv_spinbox_t;
extern const lv_obj_class_t lv_spinbox_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a spinbox objects
* @param par pointer to an object, it will be the parent of the new spinbox
* @return pointer to the created spinbox
*/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param spinbox pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i);
/**
* 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 * obj, bool b);
/**
* 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 * obj, uint8_t digit_count, uint8_t separator_position);
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement. Can be 1, 10, 100, 1000, etc the digit that will change.
*/
void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step);
/**
* 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 * obj, int32_t range_min, int32_t range_max);
/*=====================
* Getter functions
*====================*/
/**
* Get spinbox rollover function status
* @param spinbox pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t * obj);
/**
* 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 * obj);
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param spinbox pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition by dividing the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * obj);
/**
* Select next higher digit for edition by multiplying the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj);
/**
* Increment spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj);
/**
* Decrement spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPINBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_SPINBOX*/

View File

@ -0,0 +1,88 @@
/**
* @file lv_spinner.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinner.h"
#if LV_USE_SPINNER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void arc_anim_start_angle(void * obj, int32_t v);
static void arc_anim_end_angle(void * obj, int32_t v);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a spinner object
* @param par pointer to an object, it will be the parent of the new spinner
* @return pointer to the created spinner
*/
lv_obj_t * lv_spinner_create(lv_obj_t * parent, uint32_t time, uint32_t arc_length)
{
/*Create the ancestor of spinner*/
lv_obj_t * spinner = lv_arc_create(parent);
LV_ASSERT_MALLOC(spinner);
if(spinner == NULL) return NULL;
lv_obj_remove_style(spinner, NULL, LV_PART_KNOB | LV_STATE_ANY);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, spinner);
lv_anim_set_exec_cb(&a, arc_anim_end_angle);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_time(&a, time);
lv_anim_set_values(&a, arc_length, 360 + arc_length);
lv_anim_start(&a);
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
lv_anim_set_values(&a, 0, 360);
lv_anim_set_exec_cb(&a, arc_anim_start_angle);
lv_anim_start(&a);
lv_arc_set_bg_angles(spinner, 0, 360);
lv_arc_set_rotation(spinner, 270);
return spinner;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void arc_anim_start_angle(void * obj, int32_t v)
{
lv_arc_set_start_angle(obj, (uint16_t) v);
}
static void arc_anim_end_angle(void * obj, int32_t v)
{
lv_arc_set_end_angle(obj, (uint16_t) v);
}
#endif /*LV_USE_SPINNER*/

Some files were not shown because too many files have changed in this diff Show More