* PXP: Added basic PXP acceleration PXP accelerated features: - fill (+ opacity) - BLIT (+ opacity) - recoloring (+ opacity) - color keying (+ opacity) Recoloring + color keying simultaneously not supported. Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Added abstraction for interrupt handling Previous imlpementation used IRQ polling on PXP, which doesn't allow real CPU offload. Therefore added set of callbacks for interrupt handling that should be implemented by user, with possible RTOS integration. Default/example implementation of callbacks for bare metal and FreeRTOS provided (lv_gpu_nxp_pxp_osa.c), enabled by LV_USE_GPU_NXP_PXP_DEFAULT_OSA switch, accesible via pxp_default_cfg structure. Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Optimized cache flushing Previous implementation flushed areas of (LCD width * object height) size. Cache flush is expensive operation and flushing line by line, smallest possible area, boost performance by shortening time spent on cache flushes. Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Added documentation for NXP PXP accelerator Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: added missing extern c in header files Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Fixed ifdefs - Removed LV_USE_GPU, fixed internal config LV_USE_GPU is not intended as a global GPU enable switch. It's used only for gpu_blend_cb and gpu_fill_cb callbacks, which are obsolete. This patch removes LV_USE_GPU dependency for PXP code, so it's enabled only with LV_USE_GPU_NXP_PXP symbol. Added missing symbols to internal conf, so automatic testd can pass build step. Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Default OSA changed to PXP auto-initialization Auto init feature added so if user run FreeRTOS or bare-metal, no PXP Init code is required. Renamed symbol to be more clear. Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Documentation moved to docs repo Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com> * PXP: Fixed alpha configuration - Coverity issue: AS blend config used uninitialized structure. No impact on functionality, as blend module is not used (porter-duff blends in this case) - Alpha config fixed - swapped alpha values produced different result from SW render Signed-off-by: Jozef Bastek <jozef.bastek@nxp.com>
4825 lines
151 KiB
C
4825 lines
151 KiB
C
/**
|
|
* @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_misc/lv_debug.h"
|
|
#include "../lv_themes/lv_theme.h"
|
|
#include "../lv_draw/lv_draw.h"
|
|
#include "../lv_misc/lv_anim.h"
|
|
#include "../lv_misc/lv_task.h"
|
|
#include "../lv_misc/lv_async.h"
|
|
#include "../lv_misc/lv_fs.h"
|
|
#include "../lv_misc/lv_gc.h"
|
|
#include "../lv_misc/lv_math.h"
|
|
#include "../lv_misc/lv_gc.h"
|
|
#include "../lv_misc/lv_math.h"
|
|
#include "../lv_misc/lv_log.h"
|
|
#include "../lv_hal/lv_hal.h"
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
|
|
#include "lv_gpu/lv_gpu_nxp_pxp.h"
|
|
#include "lv_gpu/lv_gpu_nxp_pxp_osa.h"
|
|
#endif
|
|
|
|
#if defined(LV_GC_INCLUDE)
|
|
#include LV_GC_INCLUDE
|
|
#endif /* LV_ENABLE_GC */
|
|
|
|
|
|
#if defined(LV_USER_DATA_FREE_INCLUDE)
|
|
#include LV_USER_DATA_FREE_INCLUDE
|
|
#endif /* LV_USE_USER_DATA_FREE */
|
|
|
|
#include LV_THEME_DEFAULT_INCLUDE
|
|
|
|
#if LV_USE_GPU_STM32_DMA2D
|
|
#include "../lv_gpu/lv_gpu_stm32_dma2d.h"
|
|
#endif
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
#define LV_OBJX_NAME "lv_obj"
|
|
#define LV_OBJ_DEF_WIDTH (LV_DPX(100))
|
|
#define LV_OBJ_DEF_HEIGHT (LV_DPX(50))
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
typedef struct _lv_event_temp_data {
|
|
lv_obj_t * obj;
|
|
bool deleted;
|
|
struct _lv_event_temp_data * prev;
|
|
} lv_event_temp_data_t;
|
|
|
|
typedef struct {
|
|
lv_obj_t * obj;
|
|
lv_style_property_t prop;
|
|
uint8_t part;
|
|
union {
|
|
lv_color_t _color;
|
|
lv_style_int_t _int;
|
|
lv_opa_t _opa;
|
|
const void * _ptr;
|
|
} start_value;
|
|
union {
|
|
lv_color_t _color;
|
|
lv_style_int_t _int;
|
|
lv_opa_t _opa;
|
|
const void * _ptr;
|
|
} end_value;
|
|
} lv_style_trans_t;
|
|
|
|
typedef struct {
|
|
lv_draw_rect_dsc_t rect;
|
|
lv_draw_label_dsc_t label;
|
|
lv_draw_line_dsc_t line;
|
|
lv_draw_img_dsc_t img;
|
|
lv_style_int_t pad_top;
|
|
lv_style_int_t pad_bottom;
|
|
lv_style_int_t pad_right;
|
|
lv_style_int_t pad_left;
|
|
lv_style_int_t pad_inner;
|
|
lv_style_int_t margin_top;
|
|
lv_style_int_t margin_bottom;
|
|
lv_style_int_t margin_left;
|
|
lv_style_int_t margin_right;
|
|
lv_style_int_t size;
|
|
lv_style_int_t transform_width;
|
|
lv_style_int_t transform_height;
|
|
lv_style_int_t transform_angle;
|
|
lv_style_int_t transform_zoom;
|
|
lv_style_int_t scale_width;
|
|
lv_style_int_t scale_border_width;
|
|
lv_style_int_t scale_end_border_width;
|
|
lv_style_int_t scale_end_line_width;
|
|
lv_color_t scale_grad_color;
|
|
lv_color_t scale_end_color;
|
|
lv_opa_t opa_scale;
|
|
uint32_t clip_corder : 1;
|
|
uint32_t border_post : 1;
|
|
} style_snapshot_t;
|
|
|
|
typedef enum {
|
|
STYLE_COMPARE_SAME,
|
|
STYLE_COMPARE_VISUAL_DIFF,
|
|
STYLE_COMPARE_DIFF,
|
|
} style_snapshot_res_t;
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode);
|
|
static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param);
|
|
static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff);
|
|
static void report_style_mod_core(void * style_p, lv_obj_t * obj);
|
|
static void refresh_children_style(lv_obj_t * obj);
|
|
static void base_dir_refr_children(lv_obj_t * obj);
|
|
static void obj_align_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
|
|
lv_coord_t x_ofs, lv_coord_t y_ofs);
|
|
static void obj_align_mid_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
|
|
lv_coord_t x_ofs, lv_coord_t y_ofs);
|
|
#if LV_USE_ANIMATION
|
|
static lv_style_trans_t * trans_create(lv_obj_t * obj, lv_style_property_t prop, uint8_t part, lv_state_t prev_state,
|
|
lv_state_t new_state);
|
|
static void trans_del(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_style_trans_t * tr_limit);
|
|
static void trans_anim_cb(lv_style_trans_t * tr, lv_anim_value_t v);
|
|
static void trans_anim_start_cb(lv_anim_t * a);
|
|
static void trans_anim_ready_cb(lv_anim_t * a);
|
|
static void opa_scale_anim(lv_obj_t * obj, lv_anim_value_t v);
|
|
static void fade_in_anim_ready(lv_anim_t * a);
|
|
#endif
|
|
static void lv_event_mark_deleted(lv_obj_t * obj);
|
|
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
|
|
static void lv_obj_del_async_cb(void * obj);
|
|
static void obj_del_core(lv_obj_t * obj);
|
|
static void update_style_cache(lv_obj_t * obj, uint8_t part, uint16_t prop);
|
|
static void update_style_cache_children(lv_obj_t * obj);
|
|
static void invalidate_style_cache(lv_obj_t * obj, uint8_t part, lv_style_property_t prop);
|
|
static void style_snapshot(lv_obj_t * obj, uint8_t part, style_snapshot_t * shot);
|
|
static style_snapshot_res_t style_snapshot_compare(style_snapshot_t * shot1, style_snapshot_t * shot2);
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
static bool lv_initialized = false;
|
|
static lv_event_temp_data_t * event_temp_data_head;
|
|
static const void * event_act_data;
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Init. the 'lv' library.
|
|
*/
|
|
void lv_init(void)
|
|
{
|
|
/* Do nothing if already initialized */
|
|
if(lv_initialized) {
|
|
LV_LOG_WARN("lv_init: already inited");
|
|
return;
|
|
}
|
|
|
|
LV_LOG_TRACE("lv_init started");
|
|
|
|
/*Initialize the lv_misc modules*/
|
|
_lv_mem_init();
|
|
_lv_task_core_init();
|
|
|
|
#if LV_USE_FILESYSTEM
|
|
_lv_fs_init();
|
|
#endif
|
|
|
|
#if LV_USE_ANIMATION
|
|
_lv_anim_core_init();
|
|
#endif
|
|
|
|
#if LV_USE_GROUP
|
|
_lv_group_init();
|
|
#endif
|
|
|
|
#if LV_USE_GPU_STM32_DMA2D
|
|
/*Initialize DMA2D GPU*/
|
|
lv_gpu_stm32_dma2d_init();
|
|
#endif
|
|
|
|
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
|
|
if (lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) {
|
|
LV_LOG_ERROR("PXP init error. STOP.\n");
|
|
for ( ; ; ) ;
|
|
}
|
|
#endif
|
|
|
|
_lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(lv_style_trans_t));
|
|
|
|
_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));
|
|
|
|
lv_theme_t * th = LV_THEME_DEFAULT_INIT(LV_THEME_DEFAULT_COLOR_PRIMARY, LV_THEME_DEFAULT_COLOR_SECONDARY,
|
|
LV_THEME_DEFAULT_FLAG,
|
|
LV_THEME_DEFAULT_FONT_SMALL, LV_THEME_DEFAULT_FONT_NORMAL, LV_THEME_DEFAULT_FONT_SUBTITLE, LV_THEME_DEFAULT_FONT_TITLE);
|
|
lv_theme_set_act(th);
|
|
|
|
/*Initialize the screen refresh system*/
|
|
_lv_refr_init();
|
|
|
|
/*Init the input device handling*/
|
|
_lv_indev_init();
|
|
|
|
_lv_img_decoder_init();
|
|
lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
|
|
|
|
/*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. Some characters won't be displayed.")
|
|
}
|
|
|
|
lv_initialized = true;
|
|
LV_LOG_INFO("lv_init ready");
|
|
}
|
|
|
|
#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)
|
|
{
|
|
_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
|
|
|
|
/*--------------------
|
|
* Create and delete
|
|
*-------------------*/
|
|
|
|
/**
|
|
* Create a basic object
|
|
* @param parent pointer to a parent object.
|
|
* If NULL then a screen will be created
|
|
* @param copy pointer to a base object, if not NULL then the new object will be copied from it
|
|
* @return pointer to the new object
|
|
*/
|
|
lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
|
|
{
|
|
lv_obj_t * new_obj = NULL;
|
|
|
|
/*Create a screen*/
|
|
if(parent == NULL) {
|
|
LV_LOG_TRACE("Screen create started");
|
|
lv_disp_t * disp = lv_disp_get_default();
|
|
if(!disp) {
|
|
LV_LOG_WARN("lv_obj_create: not display created to so far. No place to assign the new screen");
|
|
return NULL;
|
|
}
|
|
|
|
new_obj = _lv_ll_ins_head(&disp->scr_ll);
|
|
LV_ASSERT_MEM(new_obj);
|
|
if(new_obj == NULL) return NULL;
|
|
|
|
_lv_memset_00(new_obj, sizeof(lv_obj_t));
|
|
|
|
#if LV_USE_BIDI
|
|
new_obj->base_dir = LV_BIDI_BASE_DIR_DEF;
|
|
#else
|
|
new_obj->base_dir = LV_BIDI_DIR_LTR;
|
|
#endif
|
|
|
|
/*Set the callbacks*/
|
|
new_obj->signal_cb = lv_obj_signal;
|
|
new_obj->design_cb = lv_obj_design;
|
|
new_obj->event_cb = NULL;
|
|
|
|
/*Set coordinates to full screen size*/
|
|
new_obj->coords.x1 = 0;
|
|
new_obj->coords.y1 = 0;
|
|
new_obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1;
|
|
new_obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1;
|
|
}
|
|
/*Create a normal object*/
|
|
else {
|
|
LV_LOG_TRACE("Object create started");
|
|
LV_ASSERT_OBJ(parent, LV_OBJX_NAME);
|
|
|
|
new_obj = _lv_ll_ins_head(&parent->child_ll);
|
|
LV_ASSERT_MEM(new_obj);
|
|
if(new_obj == NULL) return NULL;
|
|
|
|
_lv_memset_00(new_obj, sizeof(lv_obj_t));
|
|
|
|
new_obj->parent = parent;
|
|
|
|
#if LV_USE_BIDI
|
|
new_obj->base_dir = LV_BIDI_DIR_INHERIT;
|
|
#else
|
|
new_obj->base_dir = LV_BIDI_DIR_LTR;
|
|
#endif
|
|
|
|
/*Set the callbacks (signal:cb is required in `lv_obj_get_base_dir` if `LV_USE_ASSERT_OBJ` is enabled)*/
|
|
new_obj->signal_cb = lv_obj_signal;
|
|
new_obj->design_cb = lv_obj_design;
|
|
new_obj->event_cb = NULL;
|
|
|
|
new_obj->coords.y1 = parent->coords.y1;
|
|
new_obj->coords.y2 = parent->coords.y1 + LV_OBJ_DEF_HEIGHT;
|
|
if(lv_obj_get_base_dir(new_obj) == LV_BIDI_DIR_RTL) {
|
|
new_obj->coords.x2 = parent->coords.x2;
|
|
new_obj->coords.x1 = parent->coords.x2 - LV_OBJ_DEF_WIDTH;
|
|
}
|
|
else {
|
|
new_obj->coords.x1 = parent->coords.x1;
|
|
new_obj->coords.x2 = parent->coords.x1 + LV_OBJ_DEF_WIDTH;
|
|
}
|
|
}
|
|
|
|
|
|
_lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t));
|
|
|
|
|
|
new_obj->ext_draw_pad = 0;
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
_lv_memset_00(&new_obj->ext_click_pad, sizeof(new_obj->ext_click_pad));
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
new_obj->ext_click_pad_hor = 0;
|
|
new_obj->ext_click_pad_ver = 0;
|
|
#endif
|
|
|
|
/*Init realign*/
|
|
#if LV_USE_OBJ_REALIGN
|
|
new_obj->realign.align = LV_ALIGN_CENTER;
|
|
new_obj->realign.xofs = 0;
|
|
new_obj->realign.yofs = 0;
|
|
new_obj->realign.base = NULL;
|
|
new_obj->realign.auto_realign = 0;
|
|
#endif
|
|
|
|
/*Init. user date*/
|
|
#if LV_USE_USER_DATA
|
|
_lv_memset_00(&new_obj->user_data, sizeof(lv_obj_user_data_t));
|
|
#endif
|
|
|
|
|
|
#if LV_USE_GROUP
|
|
new_obj->group_p = NULL;
|
|
#endif
|
|
|
|
/*Set attributes*/
|
|
new_obj->adv_hittest = 0;
|
|
new_obj->click = 1;
|
|
new_obj->drag = 0;
|
|
new_obj->drag_throw = 0;
|
|
new_obj->drag_parent = 0;
|
|
new_obj->drag_dir = LV_DRAG_DIR_BOTH;
|
|
new_obj->hidden = 0;
|
|
new_obj->top = 0;
|
|
new_obj->protect = LV_PROTECT_NONE;
|
|
new_obj->parent_event = 0;
|
|
new_obj->gesture_parent = parent ? 1 : 0;
|
|
new_obj->focus_parent = 0;
|
|
new_obj->state = LV_STATE_DEFAULT;
|
|
|
|
new_obj->ext_attr = NULL;
|
|
|
|
lv_style_list_init(&new_obj->style_list);
|
|
if(copy == NULL) {
|
|
if(parent != NULL) lv_theme_apply(new_obj, LV_THEME_OBJ);
|
|
else lv_theme_apply(new_obj, LV_THEME_SCR);
|
|
}
|
|
else {
|
|
lv_style_list_copy(&new_obj->style_list, ©->style_list);
|
|
}
|
|
/*Copy the attributes if required*/
|
|
if(copy != NULL) {
|
|
lv_area_copy(&new_obj->coords, ©->coords);
|
|
new_obj->ext_draw_pad = copy->ext_draw_pad;
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
lv_area_copy(&new_obj->ext_click_pad, ©->ext_click_pad);
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
new_obj->ext_click_pad_hor = copy->ext_click_pad_hor;
|
|
new_obj->ext_click_pad_ver = copy->ext_click_pad_ver;
|
|
#endif
|
|
|
|
/*Set user data*/
|
|
#if LV_USE_USER_DATA
|
|
_lv_memcpy(&new_obj->user_data, ©->user_data, sizeof(lv_obj_user_data_t));
|
|
#endif
|
|
|
|
/*Copy realign*/
|
|
#if LV_USE_OBJ_REALIGN
|
|
new_obj->realign.align = copy->realign.align;
|
|
new_obj->realign.xofs = copy->realign.xofs;
|
|
new_obj->realign.yofs = copy->realign.yofs;
|
|
new_obj->realign.base = copy->realign.base;
|
|
new_obj->realign.auto_realign = copy->realign.auto_realign;
|
|
#endif
|
|
|
|
/*Only copy the `event_cb`. `signal_cb` and `design_cb` will be copied in the derived
|
|
* object type (e.g. `lv_btn`)*/
|
|
new_obj->event_cb = copy->event_cb;
|
|
|
|
/*Copy attributes*/
|
|
new_obj->adv_hittest = copy->adv_hittest;
|
|
new_obj->click = copy->click;
|
|
new_obj->drag = copy->drag;
|
|
new_obj->drag_dir = copy->drag_dir;
|
|
new_obj->drag_throw = copy->drag_throw;
|
|
new_obj->drag_parent = copy->drag_parent;
|
|
new_obj->hidden = copy->hidden;
|
|
new_obj->top = copy->top;
|
|
new_obj->parent_event = copy->parent_event;
|
|
|
|
new_obj->protect = copy->protect;
|
|
new_obj->gesture_parent = copy->gesture_parent;
|
|
new_obj->focus_parent = copy->focus_parent;
|
|
|
|
#if LV_USE_GROUP
|
|
/*Add to the same group*/
|
|
if(copy->group_p != NULL) {
|
|
lv_group_add_obj(copy->group_p, new_obj);
|
|
}
|
|
#endif
|
|
|
|
/*Set the same coordinates for non screen objects*/
|
|
if(lv_obj_get_parent(copy) != NULL && parent != NULL) {
|
|
lv_obj_set_pos(new_obj, lv_obj_get_x(copy), lv_obj_get_y(copy));
|
|
}
|
|
}
|
|
|
|
/*Send a signal to the parent to notify it about the new child*/
|
|
if(parent != NULL) {
|
|
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, new_obj);
|
|
|
|
/*Invalidate the area if not screen created*/
|
|
lv_obj_invalidate(new_obj);
|
|
}
|
|
|
|
LV_LOG_INFO("Object create ready");
|
|
|
|
return new_obj;
|
|
}
|
|
|
|
/**
|
|
* Delete 'obj' and all of its children
|
|
* @param obj pointer to an object to delete
|
|
* @return LV_RES_INV because the object is deleted
|
|
*/
|
|
lv_res_t lv_obj_del(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
lv_obj_invalidate(obj);
|
|
|
|
lv_disp_t * disp = NULL;
|
|
bool act_scr_del = false;
|
|
lv_obj_t * par = lv_obj_get_parent(obj);
|
|
if(par == NULL) {
|
|
disp = lv_obj_get_disp(obj);
|
|
if(!disp) return LV_RES_INV; /*Shouldn't happen*/
|
|
if(disp->act_scr == obj) act_scr_del = true;
|
|
}
|
|
|
|
|
|
obj_del_core(obj);
|
|
|
|
/*Send a signal to the parent to notify it about the child delete*/
|
|
if(par) {
|
|
par->signal_cb(par, LV_SIGNAL_CHILD_CHG, NULL);
|
|
}
|
|
|
|
/*Handle if the active screen was deleted*/
|
|
if(act_scr_del) {
|
|
disp->act_scr = NULL;
|
|
}
|
|
|
|
return LV_RES_INV;
|
|
}
|
|
|
|
#if LV_USE_ANIMATION
|
|
/**
|
|
* 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)
|
|
{
|
|
lv_obj_del(a->var);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* 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(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
lv_async_call(lv_obj_del_async_cb, obj);
|
|
}
|
|
|
|
/**
|
|
* Delete all children of an object
|
|
* @param obj pointer to an object
|
|
*/
|
|
void lv_obj_clean(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
lv_obj_t * child = lv_obj_get_child(obj, NULL);
|
|
while(child) {
|
|
lv_obj_del(child);
|
|
child = lv_obj_get_child(obj, NULL); /*Get the new first child*/
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mark an area of an object as invalid.
|
|
* This area will be redrawn by 'lv_refr_task'
|
|
* @param obj pointer to an object
|
|
* @param area the area to redraw
|
|
*/
|
|
void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
|
|
* @param obj pointer to an object
|
|
*/
|
|
void lv_obj_invalidate(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
/*Truncate the area to the object*/
|
|
lv_area_t obj_coords;
|
|
lv_coord_t ext_size = obj->ext_draw_pad;
|
|
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);
|
|
|
|
}
|
|
|
|
/**
|
|
* 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 lv_obj_t * obj, lv_area_t * area)
|
|
{
|
|
if(lv_obj_get_hidden(obj)) return false;
|
|
|
|
/*Invalidate the object only if it belongs to the curent 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)) {
|
|
|
|
/*Truncate the area to the object*/
|
|
lv_area_t obj_coords;
|
|
lv_coord_t ext_size = obj->ext_draw_pad;
|
|
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_get_hidden(par)) 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;
|
|
}
|
|
|
|
/**
|
|
* 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 lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_area_t obj_coords;
|
|
lv_coord_t ext_size = obj->ext_draw_pad;
|
|
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);
|
|
|
|
}
|
|
|
|
/*=====================
|
|
* Setter functions
|
|
*====================*/
|
|
|
|
/*--------------------
|
|
* Parent/children set
|
|
*--------------------*/
|
|
|
|
/**
|
|
* Set a new parent for an object. Its relative position will be the same.
|
|
* @param obj pointer to an object. Can't be a screen.
|
|
* @param parent pointer to the new parent object. (Can't be NULL)
|
|
*/
|
|
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
LV_ASSERT_OBJ(parent, LV_OBJX_NAME);
|
|
|
|
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_t * old_par = obj->parent;
|
|
lv_point_t old_pos;
|
|
old_pos.y = lv_obj_get_y(obj);
|
|
|
|
lv_bidi_dir_t new_base_dir = lv_obj_get_base_dir(parent);
|
|
|
|
if(new_base_dir != LV_BIDI_DIR_RTL) {
|
|
old_pos.x = lv_obj_get_x(obj);
|
|
}
|
|
else {
|
|
old_pos.x = old_par->coords.x2 - obj->coords.x2;
|
|
}
|
|
|
|
_lv_ll_chg_list(&obj->parent->child_ll, &parent->child_ll, obj, true);
|
|
obj->parent = parent;
|
|
|
|
|
|
if(new_base_dir != LV_BIDI_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*/
|
|
old_par->signal_cb(old_par, LV_SIGNAL_CHILD_CHG, NULL);
|
|
|
|
/*Notify the new parent about the child*/
|
|
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
|
|
|
|
lv_obj_invalidate(obj);
|
|
}
|
|
|
|
/**
|
|
* Move and object to the foreground
|
|
* @param obj pointer to an object
|
|
*/
|
|
void lv_obj_move_foreground(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_t * parent = lv_obj_get_parent(obj);
|
|
|
|
/*Do nothing of already in the foreground*/
|
|
if(_lv_ll_get_head(&parent->child_ll) == obj) return;
|
|
|
|
lv_obj_invalidate(parent);
|
|
|
|
_lv_ll_chg_list(&parent->child_ll, &parent->child_ll, obj, true);
|
|
|
|
/*Notify the new parent about the child*/
|
|
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
|
|
|
|
lv_obj_invalidate(parent);
|
|
}
|
|
|
|
/**
|
|
* Move and object to the background
|
|
* @param obj pointer to an object
|
|
*/
|
|
void lv_obj_move_background(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_t * parent = lv_obj_get_parent(obj);
|
|
|
|
/*Do nothing of already in the background*/
|
|
if(_lv_ll_get_tail(&parent->child_ll) == obj) return;
|
|
|
|
lv_obj_invalidate(parent);
|
|
|
|
_lv_ll_chg_list(&parent->child_ll, &parent->child_ll, obj, false);
|
|
|
|
/*Notify the new parent about the child*/
|
|
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
|
|
|
|
lv_obj_invalidate(parent);
|
|
}
|
|
|
|
/*--------------------
|
|
* Coordinate set
|
|
* ------------------*/
|
|
|
|
/**
|
|
* Set relative the position of an object (relative to the parent)
|
|
* @param obj pointer to an object
|
|
* @param x new distance from the left side of the parent
|
|
* @param y new distance from the top of the parent
|
|
*/
|
|
void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
/*Convert x and y to absolute coordinates*/
|
|
lv_obj_t * par = obj->parent;
|
|
|
|
if(par) {
|
|
x = x + par->coords.x1;
|
|
y = y + par->coords.y1;
|
|
}
|
|
|
|
|
|
/*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);
|
|
|
|
obj->coords.x1 += diff.x;
|
|
obj->coords.y1 += diff.y;
|
|
obj->coords.x2 += diff.x;
|
|
obj->coords.y2 += diff.y;
|
|
|
|
refresh_children_position(obj, diff.x, diff.y);
|
|
|
|
/*Inform the object about its new coordinates*/
|
|
obj->signal_cb(obj, LV_SIGNAL_COORD_CHG, &ori);
|
|
|
|
/*Send a signal to the parent too*/
|
|
if(par) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
|
|
|
|
/*Invalidate the new area*/
|
|
lv_obj_invalidate(obj);
|
|
}
|
|
|
|
/**
|
|
* Set the x coordinate of a object
|
|
* @param obj pointer to an object
|
|
* @param x new distance from the left side from the parent
|
|
*/
|
|
void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_set_pos(obj, x, lv_obj_get_y(obj));
|
|
}
|
|
|
|
/**
|
|
* Set the y coordinate of a object
|
|
* @param obj pointer to an object
|
|
* @param y new distance from the top of the parent
|
|
*/
|
|
void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_set_pos(obj, lv_obj_get_x(obj), y);
|
|
}
|
|
|
|
/**
|
|
* Set the size of an object
|
|
* @param obj pointer to an object
|
|
* @param w new width
|
|
* @param h new height
|
|
*/
|
|
void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/*Invalidate the original area*/
|
|
lv_obj_invalidate(obj);
|
|
|
|
/*Save the original coordinates*/
|
|
lv_area_t ori;
|
|
lv_obj_get_coords(obj, &ori);
|
|
|
|
/*Set the length and height*/
|
|
obj->coords.y2 = obj->coords.y1 + h - 1;
|
|
if(lv_obj_get_base_dir(obj) == LV_BIDI_DIR_RTL) {
|
|
obj->coords.x1 = obj->coords.x2 - w + 1;
|
|
}
|
|
else {
|
|
obj->coords.x2 = obj->coords.x1 + w - 1;
|
|
}
|
|
|
|
/*Send a signal to the object with its new coordinates*/
|
|
obj->signal_cb(obj, LV_SIGNAL_COORD_CHG, &ori);
|
|
|
|
/*Send a signal to the parent too*/
|
|
lv_obj_t * par = lv_obj_get_parent(obj);
|
|
if(par != NULL) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
|
|
|
|
/*Tell the children the parent's size has changed*/
|
|
lv_obj_t * i;
|
|
_LV_LL_READ(obj->child_ll, i) {
|
|
i->signal_cb(i, LV_SIGNAL_PARENT_SIZE_CHG, &ori);
|
|
}
|
|
|
|
/*Invalidate the new area*/
|
|
lv_obj_invalidate(obj);
|
|
|
|
/*Automatically realign the object if required*/
|
|
#if LV_USE_OBJ_REALIGN
|
|
if(obj->realign.auto_realign) lv_obj_realign(obj);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Set the width of an object
|
|
* @param obj pointer to an object
|
|
* @param w new width
|
|
*/
|
|
void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_set_size(obj, w, lv_obj_get_height(obj));
|
|
}
|
|
|
|
/**
|
|
* Set the height of an object
|
|
* @param obj pointer to an object
|
|
* @param h new height
|
|
*/
|
|
void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_set_size(obj, lv_obj_get_width(obj), h);
|
|
}
|
|
|
|
/**
|
|
* Set the width reduced by the left and right padding.
|
|
* @param obj pointer to an object
|
|
* @param w the width without paddings
|
|
*/
|
|
void lv_obj_set_width_fit(lv_obj_t * obj, lv_coord_t w)
|
|
{
|
|
lv_style_int_t pleft = lv_obj_get_style_pad_left(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t pright = lv_obj_get_style_pad_right(obj, LV_OBJ_PART_MAIN);
|
|
|
|
lv_obj_set_width(obj, w - pleft - pright);
|
|
}
|
|
|
|
/**
|
|
* Set the height reduced by the top and bottom padding.
|
|
* @param obj pointer to an object
|
|
* @param h the height without paddings
|
|
*/
|
|
void lv_obj_set_height_fit(lv_obj_t * obj, lv_coord_t h)
|
|
{
|
|
lv_style_int_t ptop = lv_obj_get_style_pad_top(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_OBJ_PART_MAIN);
|
|
|
|
lv_obj_set_height(obj, h - ptop - pbottom);
|
|
}
|
|
|
|
/**
|
|
* Set the width of an object by taking the left and right margin into account.
|
|
* The object width will be `obj_w = w - margin_left - margin_right`
|
|
* @param obj pointer to an object
|
|
* @param w new height including margins
|
|
*/
|
|
void lv_obj_set_width_margin(lv_obj_t * obj, lv_coord_t w)
|
|
{
|
|
lv_style_int_t mleft = lv_obj_get_style_margin_left(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t mright = lv_obj_get_style_margin_right(obj, LV_OBJ_PART_MAIN);
|
|
|
|
lv_obj_set_width(obj, w - mleft - mright);
|
|
}
|
|
|
|
/**
|
|
* Set the height of an object by taking the top and bottom margin into account.
|
|
* The object height will be `obj_h = h - margin_top - margin_bottom`
|
|
* @param obj pointer to an object
|
|
* @param h new height including margins
|
|
*/
|
|
void lv_obj_set_height_margin(lv_obj_t * obj, lv_coord_t h)
|
|
{
|
|
lv_style_int_t mtop = lv_obj_get_style_margin_top(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t mbottom = lv_obj_get_style_margin_bottom(obj, LV_OBJ_PART_MAIN);
|
|
|
|
lv_obj_set_height(obj, h - mtop - mbottom);
|
|
}
|
|
|
|
/**
|
|
* Align an object to an other object.
|
|
* @param obj pointer to an object to align
|
|
* @param base pointer to an object (if NULL the 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
|
|
*/
|
|
void lv_obj_align(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, LV_OBJX_NAME);
|
|
|
|
if(base == NULL) base = lv_obj_get_parent(obj);
|
|
|
|
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
|
|
|
|
obj_align_core(obj, base, align, true, true, x_ofs, y_ofs);
|
|
|
|
#if LV_USE_OBJ_REALIGN
|
|
/*Save the last align parameters to use them in `lv_obj_realign`*/
|
|
obj->realign.align = align;
|
|
obj->realign.xofs = x_ofs;
|
|
obj->realign.yofs = y_ofs;
|
|
obj->realign.base = base;
|
|
obj->realign.mid_align = 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Align an object to an other object horizontally.
|
|
* @param obj pointer to an object to align
|
|
* @param base pointer to an object (if NULL the 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
|
|
*/
|
|
void lv_obj_align_x(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(base == NULL) base = lv_obj_get_parent(obj);
|
|
|
|
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
|
|
|
|
obj_align_core(obj, base, align, true, false, x_ofs, 0);
|
|
}
|
|
|
|
/**
|
|
* Align an object to an other object vertically.
|
|
* @param obj pointer to an object to align
|
|
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
|
|
* @param align type of alignment (see 'lv_align_t' enum)
|
|
* @param y_ofs y coordinate offset after alignment
|
|
*/
|
|
void lv_obj_align_y(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t y_ofs)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(base == NULL) base = lv_obj_get_parent(obj);
|
|
|
|
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
|
|
|
|
obj_align_core(obj, base, align, false, true, 0, y_ofs);
|
|
}
|
|
|
|
/**
|
|
* Align an object's middle point to an other object.
|
|
* @param obj pointer to an object to align
|
|
* @param base pointer to an object (if NULL the 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
|
|
*/
|
|
void lv_obj_align_mid(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, LV_OBJX_NAME);
|
|
|
|
if(base == NULL) {
|
|
base = lv_obj_get_parent(obj);
|
|
}
|
|
|
|
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
|
|
|
|
|
|
obj_align_mid_core(obj, base, align, true, true, x_ofs, y_ofs);
|
|
|
|
#if LV_USE_OBJ_REALIGN
|
|
/*Save the last align parameters to use them in `lv_obj_realign`*/
|
|
obj->realign.align = align;
|
|
obj->realign.xofs = x_ofs;
|
|
obj->realign.yofs = y_ofs;
|
|
obj->realign.base = base;
|
|
obj->realign.mid_align = 1;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Align an object's middle point to an other object horizontally.
|
|
* @param obj pointer to an object to align
|
|
* @param base pointer to an object (if NULL the 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
|
|
*/
|
|
void lv_obj_align_mid_x(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(base == NULL) {
|
|
base = lv_obj_get_parent(obj);
|
|
}
|
|
|
|
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
|
|
|
|
|
|
obj_align_mid_core(obj, base, align, true, false, x_ofs, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Align an object's middle point to an other object vertically.
|
|
* @param obj pointer to an object to align
|
|
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
|
|
* @param align type of alignment (see 'lv_align_t' enum)
|
|
* @param y_ofs y coordinate offset after alignment
|
|
*/
|
|
void lv_obj_align_mid_y(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t y_ofs)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(base == NULL) {
|
|
base = lv_obj_get_parent(obj);
|
|
}
|
|
|
|
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
|
|
|
|
|
|
obj_align_mid_core(obj, base, align, false, true, 0, y_ofs);
|
|
}
|
|
|
|
/**
|
|
* Realign the object based on the last `lv_obj_align` parameters.
|
|
* @param obj pointer to an object
|
|
*/
|
|
void lv_obj_realign(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_OBJ_REALIGN
|
|
if(obj->realign.mid_align)
|
|
lv_obj_align_mid(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
|
|
else
|
|
lv_obj_align(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
|
|
#else
|
|
(void)obj;
|
|
LV_LOG_WARN("lv_obj_realign: no effect because LV_USE_OBJ_REALIGN = 0");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Enable the automatic realign of the object when its size has changed based on the last
|
|
* `lv_obj_align` parameters.
|
|
* @param obj pointer to an object
|
|
* @param en true: enable auto realign; false: disable auto realign
|
|
*/
|
|
void lv_obj_set_auto_realign(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_OBJ_REALIGN
|
|
obj->realign.auto_realign = en ? 1 : 0;
|
|
#else
|
|
(void)obj;
|
|
(void)en;
|
|
LV_LOG_WARN("lv_obj_set_auto_realign: no effect because LV_USE_OBJ_REALIGN = 0");
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the size of an extended clickable area
|
|
* If TINY mode is used, only the largest of the horizontal and vertical padding
|
|
* values are considered.
|
|
* @param obj pointer to an object
|
|
* @param left extended clickable are on the left [px]
|
|
* @param right extended clickable are on the right [px]
|
|
* @param top extended clickable are on the top [px]
|
|
* @param bottom extended clickable are on the bottom [px]
|
|
*/
|
|
void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t left, lv_coord_t right, lv_coord_t top, lv_coord_t bottom)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
obj->ext_click_pad.x1 = left;
|
|
obj->ext_click_pad.x2 = right;
|
|
obj->ext_click_pad.y1 = top;
|
|
obj->ext_click_pad.y2 = bottom;
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
obj->ext_click_pad_hor = LV_MATH_MAX(left, right);
|
|
obj->ext_click_pad_ver = LV_MATH_MAX(top, bottom);
|
|
#else
|
|
(void)obj; /*Unused*/
|
|
(void)left; /*Unused*/
|
|
(void)right; /*Unused*/
|
|
(void)top; /*Unused*/
|
|
(void)bottom; /*Unused*/
|
|
#endif
|
|
}
|
|
|
|
/*---------------------
|
|
* Appearance set
|
|
*--------------------*/
|
|
|
|
/**
|
|
* Add a new style to the style list of an object.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param style pointer to a style to add (Only its pointer will be saved)
|
|
*/
|
|
void lv_obj_add_style(lv_obj_t * obj, uint8_t part, lv_style_t * style)
|
|
{
|
|
if(style == NULL) return;
|
|
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
if(style_dsc == NULL) {
|
|
LV_LOG_WARN("Can't find style with part: %d", part);
|
|
return;
|
|
}
|
|
|
|
_lv_style_list_add_style(style_dsc, style);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, 0xFF, NULL);
|
|
#endif
|
|
lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
|
|
}
|
|
|
|
/**
|
|
* Remove a style from the style list of an object.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param style pointer to a style to remove
|
|
*/
|
|
void lv_obj_remove_style(lv_obj_t * obj, uint8_t part, lv_style_t * style)
|
|
{
|
|
if(style == NULL) return;
|
|
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
if(style_dsc == NULL) {
|
|
LV_LOG_WARN("Can't find style with part: %d", part);
|
|
return;
|
|
}
|
|
|
|
_lv_style_list_remove_style(style_dsc, style);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, 0xFF, NULL);
|
|
#endif
|
|
lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
|
|
}
|
|
|
|
/**
|
|
* Reset a style to the default (empty) state.
|
|
* Release all used memories and cancel pending related transitions.
|
|
* Typically used in `LV_SIGN_CLEAN_UP.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style list should be reseted.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
*/
|
|
void lv_obj_clean_style_list(lv_obj_t * obj, uint8_t part)
|
|
{
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
if(style_dsc == NULL) {
|
|
LV_LOG_WARN("lv_obj_clean_style_list: can't find style with `part`");
|
|
return;
|
|
}
|
|
|
|
_lv_style_list_reset(style_dsc);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, 0xFF, NULL);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Reset a style to the default (empty) state.
|
|
* Release all used memories and cancel pending related transitions.
|
|
* Also notifies the object about the style change.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style list should be reseted.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
*/
|
|
void lv_obj_reset_style_list(lv_obj_t * obj, uint8_t part)
|
|
{
|
|
lv_obj_clean_style_list(obj, part);
|
|
|
|
lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
|
|
}
|
|
|
|
/**
|
|
* Set a local style property of a part of an object in a given state.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop a style property ORed with a state.
|
|
* E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
|
|
* @param the value to set
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_opa()`
|
|
* @note for performance reasons it's not checked if the property really has integer type
|
|
*/
|
|
void _lv_obj_set_style_local_int(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_style_int_t value)
|
|
{
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
_lv_style_list_set_local_int(style_dsc, prop, value);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, prop, NULL);
|
|
#endif
|
|
lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
|
|
}
|
|
|
|
/**
|
|
* Set a local style property of a part of an object in a given state.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop a style property ORed with a state.
|
|
* E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
|
|
* @param the value to set
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_opa()`
|
|
* @note for performance reasons it's not checked if the property really has color type
|
|
*/
|
|
void _lv_obj_set_style_local_color(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_color_t color)
|
|
{
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
_lv_style_list_set_local_color(style_dsc, prop, color);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, prop, NULL);
|
|
#endif
|
|
lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
|
|
}
|
|
|
|
/**
|
|
* Set a local style property of a part of an object in a given state.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop a style property ORed with a state.
|
|
* E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
|
|
* @param the value to set
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_opa()`
|
|
* @note for performance reasons it's not checked if the property really has opacity type
|
|
*/
|
|
void _lv_obj_set_style_local_opa(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_opa_t opa)
|
|
{
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
_lv_style_list_set_local_opa(style_dsc, prop, opa);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, prop, NULL);
|
|
#endif
|
|
lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
|
|
}
|
|
|
|
/**
|
|
* Set a local style property of a part of an object in a given state.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop a style property ORed with a state.
|
|
* E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
|
|
* @param value the value to set
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_opa()`
|
|
* @note for performance reasons it's not checked if the property really has pointer type
|
|
*/
|
|
void _lv_obj_set_style_local_ptr(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, const void * value)
|
|
{
|
|
lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
|
|
_lv_style_list_set_local_ptr(style_dsc, prop, value);
|
|
#if LV_USE_ANIMATION
|
|
trans_del(obj, part, prop, NULL);
|
|
#endif
|
|
lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop a style property ORed with a state.
|
|
* E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
|
|
* @note shouldn't be used directly. Use the specific property remove functions instead.
|
|
* For example: `lv_obj_style_remove_border_opa()`
|
|
* @return true: the property was found and removed; false: the property was not found
|
|
*/
|
|
bool lv_obj_remove_style_local_prop(lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
lv_style_t * style = lv_obj_get_local_style(obj, part);
|
|
if(style) return lv_style_remove_prop(style, prop);
|
|
else return false;
|
|
}
|
|
|
|
/**
|
|
* Notify an object (and its children) about its style is modified
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be refreshed.
|
|
* @param prop `LV_STYLE_PROP_ALL` or an `LV_STYLE_...` property. It is used to optimize what needs to be refreshed.
|
|
*/
|
|
void lv_obj_refresh_style(lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
invalidate_style_cache(obj, part, prop);
|
|
|
|
/*If a real style refresh is required*/
|
|
bool real_refr = false;
|
|
switch(prop) {
|
|
case LV_STYLE_PROP_ALL:
|
|
case LV_STYLE_CLIP_CORNER:
|
|
case LV_STYLE_SIZE:
|
|
case LV_STYLE_TRANSFORM_WIDTH:
|
|
case LV_STYLE_TRANSFORM_HEIGHT:
|
|
case LV_STYLE_TRANSFORM_ANGLE:
|
|
case LV_STYLE_TRANSFORM_ZOOM:
|
|
case LV_STYLE_PAD_TOP:
|
|
case LV_STYLE_PAD_BOTTOM:
|
|
case LV_STYLE_PAD_LEFT:
|
|
case LV_STYLE_PAD_RIGHT:
|
|
case LV_STYLE_PAD_INNER:
|
|
case LV_STYLE_MARGIN_TOP:
|
|
case LV_STYLE_MARGIN_BOTTOM:
|
|
case LV_STYLE_MARGIN_LEFT:
|
|
case LV_STYLE_MARGIN_RIGHT:
|
|
case LV_STYLE_OUTLINE_WIDTH:
|
|
case LV_STYLE_OUTLINE_PAD:
|
|
case LV_STYLE_OUTLINE_OPA:
|
|
case LV_STYLE_SHADOW_WIDTH:
|
|
case LV_STYLE_SHADOW_OPA:
|
|
case LV_STYLE_SHADOW_OFS_X:
|
|
case LV_STYLE_SHADOW_OFS_Y:
|
|
case LV_STYLE_SHADOW_SPREAD:
|
|
case LV_STYLE_VALUE_LETTER_SPACE:
|
|
case LV_STYLE_VALUE_LINE_SPACE:
|
|
case LV_STYLE_VALUE_OFS_X:
|
|
case LV_STYLE_VALUE_OFS_Y:
|
|
case LV_STYLE_VALUE_ALIGN:
|
|
case LV_STYLE_VALUE_STR:
|
|
case LV_STYLE_VALUE_FONT:
|
|
case LV_STYLE_VALUE_OPA:
|
|
case LV_STYLE_TEXT_LETTER_SPACE:
|
|
case LV_STYLE_TEXT_LINE_SPACE:
|
|
case LV_STYLE_TEXT_FONT:
|
|
case LV_STYLE_LINE_WIDTH:
|
|
real_refr = true;
|
|
break;
|
|
default:
|
|
real_refr = false;
|
|
}
|
|
|
|
if(real_refr) {
|
|
lv_obj_invalidate(obj);
|
|
obj->signal_cb(obj, LV_SIGNAL_STYLE_CHG, NULL);
|
|
|
|
switch(prop) {
|
|
case LV_STYLE_PROP_ALL:
|
|
case LV_STYLE_MARGIN_TOP:
|
|
case LV_STYLE_MARGIN_BOTTOM:
|
|
case LV_STYLE_MARGIN_LEFT:
|
|
case LV_STYLE_MARGIN_RIGHT:
|
|
if(obj->parent) obj->parent->signal_cb(obj->parent, LV_SIGNAL_CHILD_CHG, NULL);
|
|
break;
|
|
}
|
|
|
|
lv_obj_invalidate(obj);
|
|
|
|
/*Send style change signals*/
|
|
if(prop == LV_STYLE_PROP_ALL || (prop & LV_STYLE_INHERIT_MASK)) refresh_children_style(obj);
|
|
}
|
|
else {
|
|
lv_obj_invalidate(obj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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_mod(lv_style_t * style)
|
|
{
|
|
lv_disp_t * d = lv_disp_get_next(NULL);
|
|
|
|
while(d) {
|
|
lv_obj_t * i;
|
|
_LV_LL_READ(d->scr_ll, i) {
|
|
report_style_mod_core(style, i);
|
|
}
|
|
d = lv_disp_get_next(d);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable/disable the use of style cahche for an object
|
|
* @param obj pointer to an object
|
|
* @param dis true: disable; false: enable (re-enable)
|
|
*/
|
|
void _lv_obj_disable_style_caching(lv_obj_t * obj, bool dis)
|
|
{
|
|
uint8_t part;
|
|
for(part = 0; part < _LV_OBJ_PART_REAL_FIRST; part++) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) break;
|
|
list->ignore_cache = dis;
|
|
}
|
|
for(part = _LV_OBJ_PART_REAL_FIRST; part < 0xFF; part++) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) break;
|
|
list->ignore_cache = dis;
|
|
}
|
|
}
|
|
|
|
/*-----------------
|
|
* Attribute set
|
|
*----------------*/
|
|
|
|
/**
|
|
* Hide an object. It won't be visible and clickable.
|
|
* @param obj pointer to an object
|
|
* @param en true: hide the object
|
|
*/
|
|
void lv_obj_set_hidden(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
|
|
|
|
obj->hidden = en == false ? 0 : 1;
|
|
|
|
if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
|
|
|
|
lv_obj_t * par = lv_obj_get_parent(obj);
|
|
if(par) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
|
|
}
|
|
|
|
/**
|
|
* Set whether advanced hit-testing is enabled on an object
|
|
* @param obj pointer to an object
|
|
* @param en true: advanced hit-testing is enabled
|
|
*/
|
|
void lv_obj_set_adv_hittest(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->adv_hittest = en == false ? 0 : 1;
|
|
}
|
|
|
|
/**
|
|
* Enable or disable the clicking of an object
|
|
* @param obj pointer to an object
|
|
* @param en true: make the object clickable
|
|
*/
|
|
void lv_obj_set_click(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->click = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Enable to bring this object to the foreground if it
|
|
* or any of its children is clicked
|
|
* @param obj pointer to an object
|
|
* @param en true: enable the auto top feature
|
|
*/
|
|
void lv_obj_set_top(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->top = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Enable the dragging of an object
|
|
* @param obj pointer to an object
|
|
* @param en true: make the object draggable
|
|
*/
|
|
void lv_obj_set_drag(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(en == true) lv_obj_set_click(obj, true); /*Drag is useless without enabled clicking*/
|
|
obj->drag = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Set the directions an object can be dragged in
|
|
* @param obj pointer to an object
|
|
* @param drag_dir bitwise OR of allowed directions an object can be dragged in
|
|
*/
|
|
void lv_obj_set_drag_dir(lv_obj_t * obj, lv_drag_dir_t drag_dir)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->drag_dir = drag_dir;
|
|
|
|
if(obj->drag_dir != 0) lv_obj_set_drag(obj, true); /*Drag direction requires drag*/
|
|
}
|
|
|
|
/**
|
|
* Enable the throwing of an object after is is dragged
|
|
* @param obj pointer to an object
|
|
* @param en true: enable the drag throw
|
|
*/
|
|
void lv_obj_set_drag_throw(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->drag_throw = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Enable to use parent for drag related operations.
|
|
* If trying to drag the object the parent will be moved instead
|
|
* @param obj pointer to an object
|
|
* @param en true: enable the 'drag parent' for the object
|
|
*/
|
|
void lv_obj_set_drag_parent(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->drag_parent = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Enable to use parent for gesture related operations.
|
|
* If trying to gesture the object the parent will be moved instead
|
|
* @param obj pointer to an object
|
|
* @param en true: enable the 'gesture parent' for the object
|
|
*/
|
|
void lv_obj_set_gesture_parent(lv_obj_t * obj, bool en)
|
|
{
|
|
obj->gesture_parent = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Enable to use parent for focus state.
|
|
* When object is focused the parent will get the state instead (visual only)
|
|
* @param obj pointer to an object
|
|
* @param en true: enable the 'focus parent' for the object
|
|
*/
|
|
void lv_obj_set_focus_parent(lv_obj_t * obj, bool en)
|
|
{
|
|
if(lv_obj_is_focused(obj)) {
|
|
if(en) {
|
|
obj->focus_parent = 1;
|
|
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED);
|
|
lv_obj_set_state(lv_obj_get_focused_obj(obj), LV_STATE_FOCUSED);
|
|
}
|
|
else {
|
|
lv_obj_clear_state(lv_obj_get_focused_obj(obj), LV_STATE_FOCUSED | LV_STATE_EDITED);
|
|
lv_obj_set_state(obj, LV_STATE_FOCUSED);
|
|
obj->focus_parent = 0;
|
|
}
|
|
}
|
|
else {
|
|
obj->focus_parent = (en == true ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Propagate the events to the parent too
|
|
* @param obj pointer to an object
|
|
* @param en true: enable the event propagation
|
|
*/
|
|
void lv_obj_set_parent_event(lv_obj_t * obj, bool en)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->parent_event = (en == true ? 1 : 0);
|
|
}
|
|
|
|
/**
|
|
* Set the base direction of the object
|
|
* @param obj pointer to an object
|
|
* @param dir the new base direction. `LV_BIDI_DIR_LTR/RTL/AUTO/INHERIT`
|
|
*/
|
|
void lv_obj_set_base_dir(lv_obj_t * obj, lv_bidi_dir_t dir)
|
|
{
|
|
if(dir != LV_BIDI_DIR_LTR && dir != LV_BIDI_DIR_RTL &&
|
|
dir != LV_BIDI_DIR_AUTO && dir != LV_BIDI_DIR_INHERIT) {
|
|
|
|
LV_LOG_WARN("lv_obj_set_base_dir: invalid base dir");
|
|
return;
|
|
}
|
|
|
|
obj->base_dir = dir;
|
|
lv_signal_send(obj, LV_SIGNAL_BASE_DIR_CHG, NULL);
|
|
|
|
/* Notify the children about the parent base dir has changed.
|
|
* (The children might have `LV_BIDI_DIR_INHERIT`)*/
|
|
base_dir_refr_children(obj);
|
|
}
|
|
|
|
/**
|
|
* Set a bit or bits in the protect filed
|
|
* @param obj pointer to an object
|
|
* @param prot 'OR'-ed values from `lv_protect_t`
|
|
*/
|
|
void lv_obj_add_protect(lv_obj_t * obj, uint8_t prot)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->protect |= prot;
|
|
}
|
|
|
|
/**
|
|
* Clear a bit or bits in the protect filed
|
|
* @param obj pointer to an object
|
|
* @param prot 'OR'-ed values from `lv_protect_t`
|
|
*/
|
|
void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
prot = (~prot) & 0xFF;
|
|
obj->protect &= prot;
|
|
}
|
|
|
|
/**
|
|
* Set the state (fully overwrite) of an object.
|
|
* If specified in the styles a transition animation will be started
|
|
* from the previous state to the current
|
|
* @param obj pointer to an object
|
|
* @param state the new state
|
|
*/
|
|
void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
|
|
{
|
|
if(obj->state == new_state) return;
|
|
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_state_t prev_state = obj->state;
|
|
style_snapshot_res_t cmp_res = STYLE_COMPARE_SAME;
|
|
uint8_t part;
|
|
for(part = 0; part < _LV_OBJ_PART_REAL_FIRST; part++) {
|
|
lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
|
|
if(style_list == NULL) break; /*No more style lists*/
|
|
obj->state = prev_state;
|
|
style_snapshot_t shot_pre;
|
|
style_snapshot(obj, part, &shot_pre);
|
|
obj->state = new_state;
|
|
style_snapshot_t shot_post;
|
|
style_snapshot(obj, part, &shot_post);
|
|
|
|
style_snapshot_res_t r = style_snapshot_compare(&shot_pre, &shot_post);
|
|
if(r == STYLE_COMPARE_DIFF) {
|
|
cmp_res = STYLE_COMPARE_DIFF;
|
|
break;
|
|
}
|
|
if(r == STYLE_COMPARE_VISUAL_DIFF) {
|
|
cmp_res = STYLE_COMPARE_VISUAL_DIFF;
|
|
}
|
|
}
|
|
|
|
obj->state = new_state;
|
|
|
|
if(cmp_res == STYLE_COMPARE_SAME) return;
|
|
|
|
#if LV_USE_ANIMATION == 0
|
|
if(cmp_res == STYLE_COMPARE_DIFF) lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
|
|
else if(cmp_res == STYLE_COMPARE_VISUAL_DIFF) lv_obj_refresh_style(obj, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
|
|
#else
|
|
|
|
for(part = 0; part < _LV_OBJ_PART_REAL_LAST; part++) {
|
|
lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
|
|
if(style_list == NULL) break; /*No more style lists*/
|
|
if(style_list->ignore_trans) continue;
|
|
|
|
lv_style_int_t time = lv_obj_get_style_transition_time(obj, part);
|
|
lv_style_property_t props[LV_STYLE_TRANS_NUM_MAX];
|
|
lv_style_int_t delay = lv_obj_get_style_transition_delay(obj, part);
|
|
lv_anim_path_t * path = lv_obj_get_style_transition_path(obj, part);
|
|
props[0] = lv_obj_get_style_transition_prop_1(obj, part);
|
|
props[1] = lv_obj_get_style_transition_prop_2(obj, part);
|
|
props[2] = lv_obj_get_style_transition_prop_3(obj, part);
|
|
props[3] = lv_obj_get_style_transition_prop_4(obj, part);
|
|
props[4] = lv_obj_get_style_transition_prop_5(obj, part);
|
|
props[5] = lv_obj_get_style_transition_prop_6(obj, part);
|
|
|
|
uint8_t i;
|
|
for(i = 0; i < LV_STYLE_TRANS_NUM_MAX; i++) {
|
|
if(props[i] != 0) {
|
|
_lv_style_list_add_trans_style(style_list);
|
|
|
|
lv_style_trans_t * tr = trans_create(obj, props[i], part, prev_state, new_state);
|
|
|
|
/*If there is a pending anim for this property remove it*/
|
|
if(tr) {
|
|
tr->obj = obj;
|
|
tr->prop = props[i];
|
|
tr->part = part;
|
|
|
|
lv_anim_t a;
|
|
lv_anim_init(&a);
|
|
lv_anim_set_var(&a, tr);
|
|
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)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, time);
|
|
lv_anim_set_delay(&a, delay);
|
|
lv_anim_set_path(&a, path);
|
|
a.early_apply = 0;
|
|
lv_anim_start(&a);
|
|
}
|
|
|
|
}
|
|
}
|
|
if(cmp_res == STYLE_COMPARE_DIFF) lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
|
|
}
|
|
if(cmp_res == STYLE_COMPARE_VISUAL_DIFF) lv_obj_invalidate(obj);
|
|
#endif
|
|
|
|
}
|
|
|
|
/**
|
|
* Add a given state or states to the object. The other state bits will remain unchanged.
|
|
* If specified in the styles a transition animation will be started
|
|
* from the previous state to the current
|
|
* @param obj pointer to an object
|
|
* @param state the state bits to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
|
|
*/
|
|
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_state_t new_state = obj->state | state;
|
|
if(obj->state != new_state) {
|
|
lv_obj_set_state(obj, new_state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove a given state or states to the object. The other state bits will remain unchanged.
|
|
* If specified in the styles a transition animation will be started
|
|
* from the previous state to the current
|
|
* @param obj pointer to an object
|
|
* @param state the state bits to remove. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
|
|
*/
|
|
void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_state_t new_state = obj->state & (~state);
|
|
if(obj->state != new_state) {
|
|
lv_obj_set_state(obj, new_state);
|
|
}
|
|
}
|
|
|
|
#if LV_USE_ANIMATION
|
|
/**
|
|
* Finish all pending transitions on a part of an object
|
|
* @param obj pointer to an object
|
|
* @param part part of the object, e.g `LV_BRN_PART_MAIN` or `LV_OBJ_PART_ALL` for all parts
|
|
*/
|
|
void lv_obj_finish_transitions(lv_obj_t * obj, uint8_t part)
|
|
{
|
|
/*Animate all related transition to the end value*/
|
|
lv_style_trans_t * tr;
|
|
_LV_LL_READ_BACK(LV_GC_ROOT(_lv_obj_style_trans_ll), tr) {
|
|
if(tr->obj == obj && (part == tr->part || part == LV_OBJ_PART_ALL)) {
|
|
trans_anim_cb(tr, 255);
|
|
}
|
|
}
|
|
|
|
/*Free all related transition data*/
|
|
trans_del(obj, part, 0xFF, NULL);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Set a an event handler function for an object.
|
|
* Used by the user to react on event which happens with the object.
|
|
* @param obj pointer to an object
|
|
* @param event_cb the new event function
|
|
*/
|
|
void lv_obj_set_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->event_cb = event_cb;
|
|
}
|
|
|
|
/**
|
|
* Send an event to the object
|
|
* @param obj pointer to an object
|
|
* @param event the type of the event from `lv_event_t`
|
|
* @param data arbitrary data depending on the object 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
|
|
*/
|
|
lv_res_t lv_event_send(lv_obj_t * obj, lv_event_t event, const void * data)
|
|
{
|
|
if(obj == NULL) return LV_RES_OK;
|
|
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_res_t res;
|
|
res = lv_event_send_func(obj->event_cb, obj, event, data);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Send LV_EVENT_REFRESH event to an object
|
|
* @param obj point to an obejct. (Can NOT be NULL)
|
|
* @return LV_RES_OK: success, LV_RES_INV: to object become invalid (e.g. deleted) due to this event.
|
|
*/
|
|
lv_res_t lv_event_send_refresh(lv_obj_t * obj)
|
|
{
|
|
return lv_event_send(obj, LV_EVENT_REFRESH, NULL);
|
|
}
|
|
|
|
/**
|
|
* Send LV_EVENT_REFRESH event to an object and all of its children.
|
|
* @param obj pointer to an object or NULL to refresh all objects of all displays
|
|
*/
|
|
void lv_event_send_refresh_recursive(lv_obj_t * obj)
|
|
{
|
|
if(obj == NULL) {
|
|
/*If no obj specified refresh all screen of all displays */
|
|
lv_disp_t * d = lv_disp_get_next(NULL);
|
|
while(d) {
|
|
lv_obj_t * scr = _lv_ll_get_head(&d->scr_ll);
|
|
while(scr) {
|
|
lv_event_send_refresh_recursive(scr);
|
|
scr = _lv_ll_get_next(&d->scr_ll, scr);
|
|
}
|
|
lv_event_send_refresh_recursive(d->top_layer);
|
|
lv_event_send_refresh_recursive(d->sys_layer);
|
|
|
|
d = lv_disp_get_next(d);
|
|
}
|
|
}
|
|
else {
|
|
|
|
lv_res_t res = lv_event_send_refresh(obj);
|
|
if(res != LV_RES_OK) return; /*If invalid returned do not check the children*/
|
|
|
|
lv_obj_t * child = lv_obj_get_child(obj, NULL);
|
|
while(child) {
|
|
lv_event_send_refresh_recursive(child);
|
|
|
|
child = lv_obj_get_child(obj, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Call an event function with an object, event, and data.
|
|
* @param event_xcb an event callback function. If `NULL` `LV_RES_OK` will return without any actions.
|
|
* (the 'x' in the argument name indicates that its not a fully generic function because it not follows
|
|
* the `func_name(object, callback, ...)` convention)
|
|
* @param obj pointer to an object to associate with the event (can be `NULL` to simply call the `event_cb`)
|
|
* @param event an event
|
|
* @param data pointer to a custom data
|
|
* @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event
|
|
*/
|
|
lv_res_t lv_event_send_func(lv_event_cb_t event_xcb, lv_obj_t * obj, lv_event_t event, const void * data)
|
|
{
|
|
if(obj != NULL) {
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
}
|
|
|
|
/* 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 `even_cb`. */
|
|
lv_event_temp_data_t event_temp_data;
|
|
event_temp_data.obj = obj;
|
|
event_temp_data.deleted = false;
|
|
event_temp_data.prev = NULL;
|
|
|
|
if(event_temp_data_head) {
|
|
event_temp_data.prev = event_temp_data_head;
|
|
}
|
|
event_temp_data_head = &event_temp_data;
|
|
|
|
const void * event_act_data_save = event_act_data;
|
|
event_act_data = data;
|
|
|
|
/*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, event);
|
|
}
|
|
|
|
/*Call the event callback itself*/
|
|
if(event_xcb) event_xcb(obj, event);
|
|
|
|
/*Restore the event data*/
|
|
event_act_data = event_act_data_save;
|
|
|
|
/*Remove this element from the list*/
|
|
event_temp_data_head = event_temp_data_head->prev;
|
|
|
|
if(event_temp_data.deleted) {
|
|
return LV_RES_INV;
|
|
}
|
|
|
|
if(obj) {
|
|
if(obj->parent_event && obj->parent) {
|
|
lv_res_t res = lv_event_send(obj->parent, event, data);
|
|
if(res != LV_RES_OK) {
|
|
return LV_RES_INV;
|
|
}
|
|
}
|
|
}
|
|
|
|
return LV_RES_OK;
|
|
}
|
|
|
|
/**
|
|
* Get the `data` parameter of the current event
|
|
* @return the `data` parameter
|
|
*/
|
|
const void * lv_event_get_data(void)
|
|
{
|
|
return event_act_data;
|
|
}
|
|
|
|
/**
|
|
* Set the a signal function of an object. Used internally by the library.
|
|
* Always call the previous signal function in the new.
|
|
* @param obj pointer to an object
|
|
* @param cb the new signal function
|
|
*/
|
|
void lv_obj_set_signal_cb(lv_obj_t * obj, lv_signal_cb_t signal_cb)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->signal_cb = signal_cb;
|
|
}
|
|
|
|
/**
|
|
* Send an event to the object
|
|
* @param obj pointer to an object
|
|
* @param event the type of the event from `lv_event_t`.
|
|
* @return LV_RES_OK or LV_RES_INV
|
|
*/
|
|
lv_res_t lv_signal_send(lv_obj_t * obj, lv_signal_t signal, void * param)
|
|
{
|
|
if(obj == NULL) return LV_RES_OK;
|
|
|
|
lv_res_t res = LV_RES_OK;
|
|
if(obj->signal_cb) res = obj->signal_cb(obj, signal, param);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Set a new design function for an object
|
|
* @param obj pointer to an object
|
|
* @param design_cb the new design function
|
|
*/
|
|
void lv_obj_set_design_cb(lv_obj_t * obj, lv_design_cb_t design_cb)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->design_cb = design_cb;
|
|
}
|
|
|
|
/*----------------
|
|
* Other set
|
|
*--------------*/
|
|
|
|
/**
|
|
* Allocate a new ext. data for an object
|
|
* @param obj pointer to an object
|
|
* @param ext_size the size of the new ext. data
|
|
* @return pointer to the allocated ext.
|
|
* If out of memory NULL is returned and the original ext is preserved
|
|
*/
|
|
void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
void * new_ext = lv_mem_realloc(obj->ext_attr, ext_size);
|
|
if(new_ext == NULL) return NULL;
|
|
|
|
obj->ext_attr = new_ext;
|
|
return (void *)obj->ext_attr;
|
|
}
|
|
|
|
/**
|
|
* Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object to refresh the extended draw area.
|
|
* he object needs to be invalidated by `lv_obj_invalidate(obj)` manually after this function.
|
|
* @param obj pointer to an object
|
|
*/
|
|
void lv_obj_refresh_ext_draw_pad(lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
obj->ext_draw_pad = 0;
|
|
obj->signal_cb(obj, LV_SIGNAL_REFR_EXT_DRAW_PAD, NULL);
|
|
|
|
}
|
|
|
|
/*=======================
|
|
* Getter functions
|
|
*======================*/
|
|
|
|
/**
|
|
* Return with the screen of an object
|
|
* @param obj pointer to an object
|
|
* @return pointer to a screen
|
|
*/
|
|
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
const lv_obj_t * par = obj;
|
|
const lv_obj_t * act_p;
|
|
|
|
do {
|
|
act_p = par;
|
|
par = lv_obj_get_parent(act_p);
|
|
} while(par != NULL);
|
|
|
|
return (lv_obj_t *)act_p;
|
|
}
|
|
|
|
/**
|
|
* Get the display of an object
|
|
* @param scr pointer to an object
|
|
* @return pointer the object's display
|
|
*/
|
|
lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
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) {
|
|
lv_obj_t * s;
|
|
_LV_LL_READ(d->scr_ll, s) {
|
|
if(s == scr) return d;
|
|
}
|
|
}
|
|
|
|
LV_LOG_WARN("lv_scr_get_disp: screen not found")
|
|
return NULL;
|
|
}
|
|
|
|
/*---------------------
|
|
* Parent/children get
|
|
*--------------------*/
|
|
|
|
/**
|
|
* Returns with the parent of an object
|
|
* @param obj pointer to an object
|
|
* @return pointer to the parent of 'obj'
|
|
*/
|
|
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->parent;
|
|
}
|
|
|
|
/**
|
|
* Iterate through the children of an object (start from the "youngest")
|
|
* @param obj pointer to an object
|
|
* @param child NULL at first call to get the next children
|
|
* and the previous return value later
|
|
* @return the child after 'act_child' or NULL if no more child
|
|
*/
|
|
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_t * result = NULL;
|
|
|
|
if(child == NULL) {
|
|
result = _lv_ll_get_head(&obj->child_ll);
|
|
}
|
|
else {
|
|
result = _lv_ll_get_next(&obj->child_ll, child);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Iterate through the children of an object (start from the "oldest")
|
|
* @param obj pointer to an object
|
|
* @param child NULL at first call to get the next children
|
|
* and the previous return value later
|
|
* @return the child after 'act_child' or NULL if no more child
|
|
*/
|
|
lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_t * result = NULL;
|
|
|
|
if(child == NULL) {
|
|
result = _lv_ll_get_tail(&obj->child_ll);
|
|
}
|
|
else {
|
|
result = _lv_ll_get_prev(&obj->child_ll, child);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Count the children of an object (only children directly on 'obj')
|
|
* @param obj pointer to an object
|
|
* @return children number of 'obj'
|
|
*/
|
|
uint16_t lv_obj_count_children(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_t * i;
|
|
uint16_t cnt = 0;
|
|
|
|
_LV_LL_READ(obj->child_ll, i) cnt++;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/** Recursively count the children of an object
|
|
* @param obj pointer to an object
|
|
* @return children number of 'obj'
|
|
*/
|
|
uint16_t lv_obj_count_children_recursive(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_obj_t * i;
|
|
uint16_t cnt = 0;
|
|
|
|
_LV_LL_READ(obj->child_ll, i) {
|
|
cnt++; /*Count the child*/
|
|
cnt += lv_obj_count_children_recursive(i); /*recursively count children's children*/
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/*---------------------
|
|
* Coordinate get
|
|
*--------------------*/
|
|
|
|
/**
|
|
* Copy the coordinates of an object to an area
|
|
* @param obj pointer to an object
|
|
* @param cords_p pointer to an area to store the coordinates
|
|
*/
|
|
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_area_copy(cords_p, &obj->coords);
|
|
}
|
|
|
|
/**
|
|
* Reduce area retried by `lv_obj_get_coords()` the get graphically usable area of an object.
|
|
* (Without the size of the border or other extra graphical elements)
|
|
* @param coords_p store the result area here
|
|
*/
|
|
void lv_obj_get_inner_coords(const lv_obj_t * obj, lv_area_t * coords_p)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_border_side_t part = lv_obj_get_style_border_side(obj, LV_OBJ_PART_MAIN);
|
|
lv_coord_t w = lv_obj_get_style_border_width(obj, LV_OBJ_PART_MAIN);
|
|
|
|
if(part & LV_BORDER_SIDE_LEFT) coords_p->x1 += w;
|
|
|
|
if(part & LV_BORDER_SIDE_RIGHT) coords_p->x2 -= w;
|
|
|
|
if(part & LV_BORDER_SIDE_TOP) coords_p->y1 += w;
|
|
|
|
if(part & LV_BORDER_SIDE_BOTTOM) coords_p->y2 -= w;
|
|
}
|
|
|
|
/**
|
|
* Get the x coordinate of object
|
|
* @param obj pointer to an object
|
|
* @return distance of 'obj' from the left side of its parent
|
|
*/
|
|
lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_coord_t rel_x;
|
|
lv_obj_t * parent = lv_obj_get_parent(obj);
|
|
if(parent) {
|
|
rel_x = obj->coords.x1 - parent->coords.x1;
|
|
}
|
|
else {
|
|
rel_x = obj->coords.x1;
|
|
}
|
|
return rel_x;
|
|
}
|
|
|
|
/**
|
|
* Get the y coordinate of object
|
|
* @param obj pointer to an object
|
|
* @return distance of 'obj' from the top of its parent
|
|
*/
|
|
lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_coord_t rel_y;
|
|
lv_obj_t * parent = lv_obj_get_parent(obj);
|
|
if(parent) {
|
|
rel_y = obj->coords.y1 - parent->coords.y1;
|
|
}
|
|
else {
|
|
rel_y = obj->coords.y1;
|
|
}
|
|
return rel_y;
|
|
}
|
|
|
|
/**
|
|
* Get the width of an object
|
|
* @param obj pointer to an object
|
|
* @return the width
|
|
*/
|
|
lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return lv_area_get_width(&obj->coords);
|
|
}
|
|
|
|
/**
|
|
* Get the height of an object
|
|
* @param obj pointer to an object
|
|
* @return the height
|
|
*/
|
|
lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return lv_area_get_height(&obj->coords);
|
|
}
|
|
|
|
/**
|
|
* Get that width reduced by the left and right padding.
|
|
* @param obj pointer to an object
|
|
* @return the width which still fits into the container
|
|
*/
|
|
lv_coord_t lv_obj_get_width_fit(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_style_int_t left = lv_obj_get_style_pad_left(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t right = lv_obj_get_style_pad_right(obj, LV_OBJ_PART_MAIN);
|
|
|
|
return lv_obj_get_width(obj) - left - right;
|
|
}
|
|
|
|
/**
|
|
* Get that height reduced by the top an bottom padding.
|
|
* @param obj pointer to an object
|
|
* @return the height which still fits into the container
|
|
*/
|
|
lv_coord_t lv_obj_get_height_fit(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
lv_style_int_t top = lv_obj_get_style_pad_top((lv_obj_t *)obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t bottom = lv_obj_get_style_pad_bottom((lv_obj_t *)obj, LV_OBJ_PART_MAIN);
|
|
|
|
return lv_obj_get_height(obj) - top - bottom;
|
|
}
|
|
|
|
/**
|
|
* Get the height of an object by taking the top and bottom margin into account.
|
|
* The returned height will be `obj_h + margin_top + margin_bottom`
|
|
* @param obj pointer to an object
|
|
* @return the height including thee margins
|
|
*/
|
|
lv_coord_t lv_obj_get_height_margin(lv_obj_t * obj)
|
|
{
|
|
lv_style_int_t mtop = lv_obj_get_style_margin_top(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t mbottom = lv_obj_get_style_margin_bottom(obj, LV_OBJ_PART_MAIN);
|
|
|
|
return lv_obj_get_height(obj) + mtop + mbottom;
|
|
}
|
|
|
|
/**
|
|
* Get the width of an object by taking the left and right margin into account.
|
|
* The returned width will be `obj_w + margin_left + margin_right`
|
|
* @param obj pointer to an object
|
|
* @return the height including thee margins
|
|
*/
|
|
lv_coord_t lv_obj_get_width_margin(lv_obj_t * obj)
|
|
{
|
|
lv_style_int_t mleft = lv_obj_get_style_margin_left(obj, LV_OBJ_PART_MAIN);
|
|
lv_style_int_t mright = lv_obj_get_style_margin_right(obj, LV_OBJ_PART_MAIN);
|
|
|
|
return lv_obj_get_width(obj) + mleft + mright;
|
|
}
|
|
|
|
/**
|
|
* Set that width reduced by the left and right padding of the parent.
|
|
* @param obj pointer to an object
|
|
* @param div indicates how many columns are assumed.
|
|
* If 1 the width will be set the the parent's width
|
|
* If 2 only half parent width - inner padding of the parent
|
|
* If 3 only third parent width - 2 * inner padding of the parent
|
|
* @param span how many columns are combined
|
|
* @return the width according to the given parameters
|
|
*/
|
|
lv_coord_t lv_obj_get_width_grid(lv_obj_t * obj, uint8_t div, uint8_t span)
|
|
{
|
|
lv_coord_t obj_w = lv_obj_get_width_fit(obj);
|
|
lv_style_int_t pinner = lv_obj_get_style_pad_inner(obj, LV_OBJ_PART_MAIN);
|
|
|
|
lv_coord_t r = (obj_w - (div - 1) * pinner) / div;
|
|
|
|
r = r * span + (span - 1) * pinner;
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* Get that height reduced by the top and bottom padding of the parent.
|
|
* @param obj pointer to an object
|
|
* @param div indicates how many rows are assumed.
|
|
* If 1 the height will be set the the parent's height
|
|
* If 2 only half parent height - inner padding of the parent
|
|
* If 3 only third parent height - 2 * inner padding of the parent
|
|
* @param span how many rows are combined
|
|
* @return the height according to the given parameters
|
|
*/
|
|
lv_coord_t lv_obj_get_height_grid(lv_obj_t * obj, uint8_t div, uint8_t span)
|
|
{
|
|
lv_coord_t obj_h = lv_obj_get_height_fit(obj);
|
|
lv_style_int_t pinner = lv_obj_get_style_pad_inner(obj, LV_OBJ_PART_MAIN);
|
|
|
|
lv_coord_t r = (obj_h - (div - 1) * pinner) / div;
|
|
|
|
r = r * span + (span - 1) * pinner;
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* Get the automatic realign property of the object.
|
|
* @param obj pointer to an object
|
|
* @return true: auto realign is enabled; false: auto realign is disabled
|
|
*/
|
|
bool lv_obj_get_auto_realign(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_OBJ_REALIGN
|
|
return obj->realign.auto_realign ? true : false;
|
|
#else
|
|
(void)obj;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the left padding of extended clickable area
|
|
* @param obj pointer to an object
|
|
* @return the extended left padding
|
|
*/
|
|
lv_coord_t lv_obj_get_ext_click_pad_left(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
return obj->ext_click_pad_hor;
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
return obj->ext_click_pad.x1;
|
|
#else
|
|
(void)obj; /*Unused*/
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the right padding of extended clickable area
|
|
* @param obj pointer to an object
|
|
* @return the extended right padding
|
|
*/
|
|
lv_coord_t lv_obj_get_ext_click_pad_right(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
return obj->ext_click_pad_hor;
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
return obj->ext_click_pad.x2;
|
|
#else
|
|
(void)obj; /*Unused*/
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the top padding of extended clickable area
|
|
* @param obj pointer to an object
|
|
* @return the extended top padding
|
|
*/
|
|
lv_coord_t lv_obj_get_ext_click_pad_top(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
return obj->ext_click_pad_ver;
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
return obj->ext_click_pad.y1;
|
|
#else
|
|
(void)obj; /*Unused*/
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the bottom padding of extended clickable area
|
|
* @param obj pointer to an object
|
|
* @return the extended bottom padding
|
|
*/
|
|
lv_coord_t lv_obj_get_ext_click_pad_bottom(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
return obj->ext_click_pad_ver;
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
return obj->ext_click_pad.y2;
|
|
#else
|
|
(void)obj; /*Unused*/
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the extended size attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return the extended size attribute
|
|
*/
|
|
lv_coord_t lv_obj_get_ext_draw_pad(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->ext_draw_pad;
|
|
}
|
|
|
|
/*-----------------
|
|
* Appearance get
|
|
*---------------*/
|
|
|
|
lv_style_list_t * lv_obj_get_style_list(const lv_obj_t * obj, uint8_t part)
|
|
{
|
|
if(part == LV_OBJ_PART_MAIN) return &((lv_obj_t *)obj)->style_list;
|
|
|
|
lv_get_style_info_t info;
|
|
info.part = part;
|
|
info.result = NULL;
|
|
|
|
lv_res_t res;
|
|
res = lv_signal_send((lv_obj_t *)obj, LV_SIGNAL_GET_STYLE, &info);
|
|
|
|
if(res != LV_RES_OK) return NULL;
|
|
|
|
return info.result;
|
|
}
|
|
|
|
/**
|
|
* Get a style property of a part of an object in the object's current state.
|
|
* If there is a running transitions it is taken into account
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be get.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop the property to get. E.g. `LV_STYLE_BORDER_WIDTH`.
|
|
* The state of the object will be added internally
|
|
* @return the value of the property of the given part in the current state.
|
|
* If the property is not found a default value will be returned.
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_width()`
|
|
* @note for performance reasons it's not checked if the property really has integer type
|
|
*/
|
|
lv_style_int_t _lv_obj_get_style_int(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
lv_style_property_t prop_ori = prop;
|
|
|
|
lv_style_attr_t attr;
|
|
attr = prop_ori >> 8;
|
|
|
|
lv_style_int_t value_act;
|
|
lv_res_t res = LV_RES_INV;
|
|
const lv_obj_t * parent = obj;
|
|
while(parent) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(parent, part);
|
|
if(!list->ignore_cache && list->style_cnt > 0) {
|
|
if(!list->valid_cache) update_style_cache((lv_obj_t *)parent, part, prop & (~LV_STYLE_STATE_MASK));
|
|
|
|
bool def = false;
|
|
switch(prop & (~LV_STYLE_STATE_MASK)) {
|
|
case LV_STYLE_BG_GRAD_DIR:
|
|
if(list->bg_grad_dir_none) def = true;
|
|
break;
|
|
case LV_STYLE_CLIP_CORNER:
|
|
if(list->clip_corner_off) def = true;
|
|
break;
|
|
case LV_STYLE_TEXT_LETTER_SPACE:
|
|
case LV_STYLE_TEXT_LINE_SPACE:
|
|
if(list->text_space_zero) def = true;
|
|
break;
|
|
case LV_STYLE_TRANSFORM_ANGLE:
|
|
case LV_STYLE_TRANSFORM_WIDTH:
|
|
case LV_STYLE_TRANSFORM_HEIGHT:
|
|
case LV_STYLE_TRANSFORM_ZOOM:
|
|
if(list->transform_all_zero) def = true;
|
|
break;
|
|
case LV_STYLE_BORDER_WIDTH:
|
|
if(list->border_width_zero) def = true;
|
|
break;
|
|
case LV_STYLE_BORDER_SIDE:
|
|
if(list->border_side_full) def = true;
|
|
break;
|
|
case LV_STYLE_BORDER_POST:
|
|
if(list->border_post_off) def = true;
|
|
break;
|
|
case LV_STYLE_OUTLINE_WIDTH:
|
|
if(list->outline_width_zero) def = true;
|
|
break;
|
|
case LV_STYLE_RADIUS:
|
|
if(list->radius_zero) def = true;
|
|
break;
|
|
case LV_STYLE_SHADOW_WIDTH:
|
|
if(list->shadow_width_zero) def = true;
|
|
break;
|
|
case LV_STYLE_PAD_TOP:
|
|
case LV_STYLE_PAD_BOTTOM:
|
|
case LV_STYLE_PAD_LEFT:
|
|
case LV_STYLE_PAD_RIGHT:
|
|
if(list->pad_all_zero) def = true;
|
|
break;
|
|
case LV_STYLE_BG_BLEND_MODE:
|
|
case LV_STYLE_BORDER_BLEND_MODE:
|
|
case LV_STYLE_IMAGE_BLEND_MODE:
|
|
case LV_STYLE_LINE_BLEND_MODE:
|
|
case LV_STYLE_OUTLINE_BLEND_MODE:
|
|
case LV_STYLE_PATTERN_BLEND_MODE:
|
|
case LV_STYLE_SHADOW_BLEND_MODE:
|
|
case LV_STYLE_TEXT_BLEND_MODE:
|
|
case LV_STYLE_VALUE_BLEND_MODE:
|
|
if(list->blend_mode_all_normal) def = true;
|
|
break;
|
|
}
|
|
|
|
if(def) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lv_state_t state = lv_obj_get_state(parent, part);
|
|
prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
|
|
|
|
res = _lv_style_list_get_int(list, prop, &value_act);
|
|
if(res == LV_RES_OK) return value_act;
|
|
|
|
if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
|
|
|
|
/*If not found, check the `MAIN` style first*/
|
|
if(part != LV_OBJ_PART_MAIN) {
|
|
part = LV_OBJ_PART_MAIN;
|
|
continue;
|
|
}
|
|
|
|
/*Check the parent too.*/
|
|
parent = lv_obj_get_parent(parent);
|
|
}
|
|
|
|
/*Handle unset values*/
|
|
prop = prop & (~LV_STYLE_STATE_MASK);
|
|
switch(prop) {
|
|
case LV_STYLE_BORDER_SIDE:
|
|
return LV_BORDER_SIDE_FULL;
|
|
case LV_STYLE_SIZE:
|
|
return LV_DPI / 20;
|
|
case LV_STYLE_SCALE_WIDTH:
|
|
return LV_DPI / 8;
|
|
case LV_STYLE_BG_GRAD_STOP:
|
|
return 255;
|
|
case LV_STYLE_TRANSFORM_ZOOM:
|
|
return LV_IMG_ZOOM_NONE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get a style property of a part of an object in the object's current state.
|
|
* If there is a running transitions it is taken into account
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be get.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop the property to get. E.g. `LV_STYLE_BORDER_COLOR`.
|
|
* The state of the object will be added internally
|
|
* @return the value of the property of the given part in the current state.
|
|
* If the property is not found a default value will be returned.
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_color()`
|
|
* @note for performance reasons it's not checked if the property really has color type
|
|
*/
|
|
lv_color_t _lv_obj_get_style_color(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
lv_style_property_t prop_ori = prop;
|
|
|
|
lv_style_attr_t attr;
|
|
attr = prop_ori >> 8;
|
|
|
|
lv_color_t value_act;
|
|
lv_res_t res = LV_RES_INV;
|
|
const lv_obj_t * parent = obj;
|
|
while(parent) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(parent, part);
|
|
|
|
lv_state_t state = lv_obj_get_state(parent, part);
|
|
prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
|
|
|
|
res = _lv_style_list_get_color(list, prop, &value_act);
|
|
if(res == LV_RES_OK) return value_act;
|
|
|
|
if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
|
|
|
|
/*If not found, check the `MAIN` style first*/
|
|
if(part != LV_OBJ_PART_MAIN) {
|
|
part = LV_OBJ_PART_MAIN;
|
|
continue;
|
|
}
|
|
|
|
/*Check the parent too.*/
|
|
parent = lv_obj_get_parent(parent);
|
|
}
|
|
|
|
/*Handle unset values*/
|
|
prop = prop & (~LV_STYLE_STATE_MASK);
|
|
switch(prop) {
|
|
case LV_STYLE_BG_COLOR:
|
|
case LV_STYLE_BG_GRAD_COLOR:
|
|
return LV_COLOR_WHITE;
|
|
}
|
|
|
|
return LV_COLOR_BLACK;
|
|
}
|
|
|
|
/**
|
|
* Get a style property of a part of an object in the object's current state.
|
|
* If there is a running transitions it is taken into account
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be get.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop the property to get. E.g. `LV_STYLE_BORDER_OPA`.
|
|
* The state of the object will be added internally
|
|
* @return the value of the property of the given part in the current state.
|
|
* If the property is not found a default value will be returned.
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_opa()`
|
|
* @note for performance reasons it's not checked if the property really has opacity type
|
|
*/
|
|
lv_opa_t _lv_obj_get_style_opa(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
lv_style_property_t prop_ori = prop;
|
|
|
|
lv_style_attr_t attr;
|
|
attr = prop_ori >> 8;
|
|
|
|
lv_opa_t value_act;
|
|
lv_res_t res = LV_RES_INV;
|
|
const lv_obj_t * parent = obj;
|
|
while(parent) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(parent, part);
|
|
|
|
if(!list->ignore_cache && list->style_cnt > 0) {
|
|
if(!list->valid_cache) update_style_cache((lv_obj_t *)parent, part, prop & (~LV_STYLE_STATE_MASK));
|
|
bool def = false;
|
|
switch(prop & (~LV_STYLE_STATE_MASK)) {
|
|
case LV_STYLE_OPA_SCALE:
|
|
if(list->opa_scale_cover) def = true;
|
|
break;
|
|
case LV_STYLE_BG_OPA:
|
|
if(list->bg_opa_cover) return LV_OPA_COVER; /*Special case, not the default value is used*/
|
|
if(list->bg_opa_transp) def = true;
|
|
break;
|
|
case LV_STYLE_IMAGE_RECOLOR_OPA:
|
|
if(list->img_recolor_opa_transp) def = true;
|
|
break;
|
|
}
|
|
|
|
if(def) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
lv_state_t state = lv_obj_get_state(parent, part);
|
|
prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
|
|
|
|
res = _lv_style_list_get_opa(list, prop, &value_act);
|
|
if(res == LV_RES_OK) return value_act;
|
|
|
|
if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
|
|
|
|
/*If not found, check the `MAIN` style first*/
|
|
if(part != LV_OBJ_PART_MAIN) {
|
|
part = LV_OBJ_PART_MAIN;
|
|
continue;
|
|
}
|
|
|
|
/*Check the parent too.*/
|
|
parent = lv_obj_get_parent(parent);
|
|
}
|
|
|
|
/*Handle unset values*/
|
|
prop = prop & (~LV_STYLE_STATE_MASK);
|
|
switch(prop) {
|
|
case LV_STYLE_BG_OPA:
|
|
case LV_STYLE_IMAGE_RECOLOR_OPA:
|
|
case LV_STYLE_PATTERN_RECOLOR_OPA:
|
|
return LV_OPA_TRANSP;
|
|
}
|
|
|
|
return LV_OPA_COVER;
|
|
}
|
|
|
|
/**
|
|
* Get a style property of a part of an object in the object's current state.
|
|
* If there is a running transitions it is taken into account
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be get.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @param prop the property to get. E.g. `LV_STYLE_TEXT_FONT`.
|
|
* The state of the object will be added internally
|
|
* @return the value of the property of the given part in the current state.
|
|
* If the property is not found a default value will be returned.
|
|
* @note shouldn't be used directly. Use the specific property get functions instead.
|
|
* For example: `lv_obj_style_get_border_opa()`
|
|
* @note for performance reasons it's not checked if the property really has pointer type
|
|
*/
|
|
const void * _lv_obj_get_style_ptr(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
lv_style_property_t prop_ori = prop;
|
|
|
|
lv_style_attr_t attr;
|
|
attr = prop_ori >> 8;
|
|
|
|
const void * value_act;
|
|
lv_res_t res = LV_RES_INV;
|
|
const lv_obj_t * parent = obj;
|
|
while(parent) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(parent, part);
|
|
|
|
if(!list->ignore_cache && list->style_cnt > 0) {
|
|
if(!list->valid_cache) update_style_cache((lv_obj_t *)parent, part, prop & (~LV_STYLE_STATE_MASK));
|
|
bool def = false;
|
|
switch(prop & (~LV_STYLE_STATE_MASK)) {
|
|
case LV_STYLE_VALUE_STR:
|
|
if(list->value_txt_str) def = true;
|
|
break;
|
|
case LV_STYLE_PATTERN_IMAGE:
|
|
if(list->pattern_img_null) def = true;
|
|
break;
|
|
case LV_STYLE_TEXT_FONT:
|
|
if(list->text_font_normal) def = true;
|
|
break;
|
|
}
|
|
|
|
if(def) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lv_state_t state = lv_obj_get_state(parent, part);
|
|
prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
|
|
|
|
res = _lv_style_list_get_ptr(list, prop, &value_act);
|
|
if(res == LV_RES_OK) return value_act;
|
|
|
|
if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
|
|
|
|
/*If not found, check the `MAIN` style first*/
|
|
if(part != LV_OBJ_PART_MAIN) {
|
|
part = LV_OBJ_PART_MAIN;
|
|
continue;
|
|
}
|
|
|
|
/*Check the parent too.*/
|
|
parent = lv_obj_get_parent(parent);
|
|
}
|
|
|
|
/*Handle unset values*/
|
|
prop = prop & (~LV_STYLE_STATE_MASK);
|
|
switch(prop) {
|
|
case LV_STYLE_TEXT_FONT:
|
|
case LV_STYLE_VALUE_FONT:
|
|
return lv_theme_get_font_normal();
|
|
#if LV_USE_ANIMATION
|
|
case LV_STYLE_TRANSITION_PATH:
|
|
return &lv_anim_path_def;
|
|
#endif
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Get the local style of a part of an object.
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object which style property should be set.
|
|
* E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
|
|
* @return pointer to the local style if exists else `NULL`.
|
|
*/
|
|
lv_style_t * lv_obj_get_local_style(lv_obj_t * obj, uint8_t part)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
|
|
return lv_style_list_get_local_style(style_list);
|
|
}
|
|
|
|
/*-----------------
|
|
* Attribute get
|
|
*----------------*/
|
|
|
|
/**
|
|
* Get the hidden attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: the object is hidden
|
|
*/
|
|
bool lv_obj_get_hidden(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->hidden == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get whether advanced hit-testing is enabled on an object
|
|
* @param obj pointer to an object
|
|
* @return true: advanced hit-testing is enabled
|
|
*/
|
|
bool lv_obj_get_adv_hittest(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->adv_hittest == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the click enable attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: the object is clickable
|
|
*/
|
|
bool lv_obj_get_click(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->click == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the top enable attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: the auto top feature is enabled
|
|
*/
|
|
bool lv_obj_get_top(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->top == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the drag enable attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: the object is draggable
|
|
*/
|
|
bool lv_obj_get_drag(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->drag == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the directions an object can be dragged
|
|
* @param obj pointer to an object
|
|
* @return bitwise OR of allowed directions an object can be dragged in
|
|
*/
|
|
lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->drag_dir;
|
|
}
|
|
|
|
/**
|
|
* Get the drag throw enable attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: drag throw is enabled
|
|
*/
|
|
bool lv_obj_get_drag_throw(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->drag_throw == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the drag parent attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: drag parent is enabled
|
|
*/
|
|
bool lv_obj_get_drag_parent(const lv_obj_t * obj)
|
|
{
|
|
return obj->drag_parent == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the gesture parent attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: gesture parent is enabled
|
|
*/
|
|
bool lv_obj_get_gesture_parent(const lv_obj_t * obj)
|
|
{
|
|
return obj->gesture_parent == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the focus parent attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: focus parent is enabled
|
|
*/
|
|
bool lv_obj_get_focus_parent(const lv_obj_t * obj)
|
|
{
|
|
return obj->focus_parent == 0 ? false : true;
|
|
}
|
|
|
|
/**
|
|
* Get the drag parent attribute of an object
|
|
* @param obj pointer to an object
|
|
* @return true: drag parent is enabled
|
|
*/
|
|
bool lv_obj_get_parent_event(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->parent_event == 0 ? false : true;
|
|
}
|
|
|
|
|
|
lv_bidi_dir_t lv_obj_get_base_dir(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_BIDI
|
|
const lv_obj_t * parent = obj;
|
|
|
|
while(parent) {
|
|
if(parent->base_dir != LV_BIDI_DIR_INHERIT) return parent->base_dir;
|
|
|
|
parent = lv_obj_get_parent(parent);
|
|
}
|
|
|
|
return LV_BIDI_BASE_DIR_DEF;
|
|
#else
|
|
(void) obj; /*Unused*/
|
|
return LV_BIDI_DIR_LTR;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the protect field of an object
|
|
* @param obj pointer to an object
|
|
* @return protect field ('OR'ed values of `lv_protect_t`)
|
|
*/
|
|
uint8_t lv_obj_get_protect(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->protect;
|
|
}
|
|
|
|
/**
|
|
* Check at least one bit of a given protect bitfield is set
|
|
* @param obj pointer to an object
|
|
* @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
|
|
* @return false: none of the given bits are set, true: at least one bit is set
|
|
*/
|
|
bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return (obj->protect & prot) == 0 ? false : true;
|
|
}
|
|
|
|
lv_state_t lv_obj_get_state(const lv_obj_t * obj, uint8_t part)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
if(part < _LV_OBJ_PART_REAL_LAST) return ((lv_obj_t *)obj)->state;
|
|
|
|
/*If a real part is asked, then use the object's signal to get its state.
|
|
* A real object can be in different state then the main part
|
|
* and only the object itself knows who to get it's state. */
|
|
lv_get_state_info_t info;
|
|
info.part = part;
|
|
info.result = LV_STATE_DEFAULT;
|
|
lv_signal_send((lv_obj_t *)obj, LV_SIGNAL_GET_STATE_DSC, &info);
|
|
|
|
return info.result;
|
|
|
|
}
|
|
|
|
/**
|
|
* Get the signal function of an object
|
|
* @param obj pointer to an object
|
|
* @return the signal function
|
|
*/
|
|
lv_signal_cb_t lv_obj_get_signal_cb(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->signal_cb;
|
|
}
|
|
|
|
/**
|
|
* Get the design function of an object
|
|
* @param obj pointer to an object
|
|
* @return the design function
|
|
*/
|
|
lv_design_cb_t lv_obj_get_design_cb(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->design_cb;
|
|
}
|
|
|
|
/**
|
|
* Get the event function of an object
|
|
* @param obj pointer to an object
|
|
* @return the event function
|
|
*/
|
|
lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->event_cb;
|
|
}
|
|
|
|
/*------------------
|
|
* Other get
|
|
*-----------------*/
|
|
|
|
/**
|
|
* Get the ext pointer
|
|
* @param obj pointer to an object
|
|
* @return the ext pointer but not the dynamic version
|
|
* Use it as ext->data1, and NOT da(ext)->data1
|
|
*/
|
|
void * lv_obj_get_ext_attr(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->ext_attr;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
void lv_obj_get_type(const lv_obj_t * obj, lv_obj_type_t * buf)
|
|
{
|
|
LV_ASSERT_NULL(buf);
|
|
LV_ASSERT_NULL(obj);
|
|
|
|
lv_obj_type_t tmp;
|
|
|
|
_lv_memset_00(buf, sizeof(lv_obj_type_t));
|
|
_lv_memset_00(&tmp, sizeof(lv_obj_type_t));
|
|
|
|
obj->signal_cb((lv_obj_t *)obj, LV_SIGNAL_GET_TYPE, &tmp);
|
|
|
|
uint8_t cnt;
|
|
for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
|
|
if(tmp.type[cnt] == NULL) break;
|
|
}
|
|
|
|
/*Swap the order. The real type comes first*/
|
|
uint8_t i;
|
|
for(i = 0; i < cnt; i++) {
|
|
buf->type[i] = tmp.type[cnt - 1 - i];
|
|
}
|
|
}
|
|
|
|
#if LV_USE_USER_DATA
|
|
|
|
/**
|
|
* Get the object's user data
|
|
* @param obj pointer to an object
|
|
* @return user data
|
|
*/
|
|
lv_obj_user_data_t lv_obj_get_user_data(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return obj->user_data;
|
|
}
|
|
|
|
/**
|
|
* Get a pointer to the object's user data
|
|
* @param obj pointer to an object
|
|
* @return pointer to the user data
|
|
*/
|
|
lv_obj_user_data_t * lv_obj_get_user_data_ptr(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
return (lv_obj_user_data_t *)&obj->user_data;
|
|
}
|
|
|
|
/**
|
|
* Set the object's user data. The data will be copied.
|
|
* @param obj pointer to an object
|
|
* @param data user data
|
|
*/
|
|
void lv_obj_set_user_data(lv_obj_t * obj, lv_obj_user_data_t data)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
_lv_memcpy(&obj->user_data, &data, sizeof(lv_obj_user_data_t));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_GROUP
|
|
return obj->group_p;
|
|
#else
|
|
LV_UNUSED(obj);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Tell whether the object is the focused object of a group or not.
|
|
* @param obj pointer to an object
|
|
* @return true: the object is focused, false: the object is not focused or not in a group
|
|
*/
|
|
bool lv_obj_is_focused(const lv_obj_t * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
#if LV_USE_GROUP
|
|
if(obj->group_p) {
|
|
if(lv_group_get_focused(obj->group_p) == obj) return true;
|
|
}
|
|
return false;
|
|
#else
|
|
LV_UNUSED(obj);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*-------------------
|
|
* OTHER FUNCTIONS
|
|
*------------------*/
|
|
|
|
/**
|
|
* Check if a given screen-space point is on an object's coordinates.
|
|
*
|
|
* This method is intended to be used mainly by advanced hit testing algorithms to check
|
|
* whether the point is even within the object (as an optimization).
|
|
* @param obj object to check
|
|
* @param point screen-space point
|
|
*/
|
|
bool lv_obj_is_point_on_coords(lv_obj_t * obj, const lv_point_t * point)
|
|
{
|
|
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
|
|
lv_area_t ext_area;
|
|
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad_hor;
|
|
ext_area.x2 = obj->coords.x2 + obj->ext_click_pad_hor;
|
|
ext_area.y1 = obj->coords.y1 - obj->ext_click_pad_ver;
|
|
ext_area.y2 = obj->coords.y2 + obj->ext_click_pad_ver;
|
|
|
|
if(!_lv_area_is_point_on(&ext_area, point, 0)) {
|
|
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
|
|
lv_area_t ext_area;
|
|
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad.x1;
|
|
ext_area.x2 = obj->coords.x2 + obj->ext_click_pad.x2;
|
|
ext_area.y1 = obj->coords.y1 - obj->ext_click_pad.y1;
|
|
ext_area.y2 = obj->coords.y2 + obj->ext_click_pad.y2;
|
|
|
|
if(!_lv_area_is_point_on(&ext_area, point, 0)) {
|
|
#else
|
|
if(!_lv_area_is_point_on(&obj->coords, point, 0)) {
|
|
#endif
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Hit-test an object given a particular point in screen space.
|
|
* @param obj object to hit-test
|
|
* @param point screen-space point
|
|
* @return true if the object is considered under the point
|
|
*/
|
|
bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point)
|
|
{
|
|
if(obj->adv_hittest) {
|
|
lv_hit_test_info_t hit_info;
|
|
hit_info.point = point;
|
|
hit_info.result = true;
|
|
obj->signal_cb(obj, LV_SIGNAL_HIT_TEST, &hit_info);
|
|
return hit_info.result;
|
|
}
|
|
else
|
|
return lv_obj_is_point_on_coords(obj, point);
|
|
}
|
|
|
|
/**
|
|
* Used in the signal callback to handle `LV_SIGNAL_GET_TYPE` signal
|
|
* @param obj pointer to an object
|
|
* @param buf pointer to `lv_obj_type_t`. (`param` in the signal callback)
|
|
* @param name name of the object. E.g. "lv_btn". (Only the pointer is saved)
|
|
* @return LV_RES_OK
|
|
*/
|
|
lv_res_t lv_obj_handle_get_type_signal(lv_obj_type_t * buf, const char * name)
|
|
{
|
|
uint8_t i;
|
|
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
|
|
if(buf->type[i] == NULL) break;
|
|
}
|
|
buf->type[i] = name;
|
|
|
|
return LV_RES_OK;
|
|
}
|
|
|
|
/**
|
|
* Initialize a rectangle descriptor from an object's styles
|
|
* @param obj pointer to an object
|
|
* @param type type of style. E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_STYLE_REL` or `LV_PAGE_STYLE_SCRL`
|
|
* @param draw_dsc the descriptor the initialize
|
|
* @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(lv_obj_t * obj, uint8_t part, lv_draw_rect_dsc_t * draw_dsc)
|
|
{
|
|
draw_dsc->radius = lv_obj_get_style_radius(obj, part);
|
|
|
|
#if LV_USE_OPA_SCALE
|
|
lv_opa_t opa_scale = lv_obj_get_style_opa_scale(obj, part);
|
|
if(opa_scale <= 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->pattern_opa = LV_OPA_TRANSP;
|
|
draw_dsc->value_opa = LV_OPA_TRANSP;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
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(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(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);
|
|
}
|
|
|
|
#if LV_USE_BLEND_MODES
|
|
draw_dsc->bg_blend_mode = lv_obj_get_style_bg_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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(obj, part);
|
|
}
|
|
#if LV_USE_BLEND_MODES
|
|
draw_dsc->border_blend_mode = lv_obj_get_style_border_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if LV_USE_OUTLINE
|
|
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 LV_USE_BLEND_MODES
|
|
draw_dsc->outline_blend_mode = lv_obj_get_style_outline_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if LV_USE_PATTERN
|
|
draw_dsc->pattern_image = lv_obj_get_style_pattern_image(obj, part);
|
|
if(draw_dsc->pattern_image) {
|
|
if(draw_dsc->pattern_opa != LV_OPA_TRANSP) {
|
|
draw_dsc->pattern_opa = lv_obj_get_style_pattern_opa(obj, part);
|
|
if(draw_dsc->pattern_opa > LV_OPA_MIN) {
|
|
draw_dsc->pattern_recolor_opa = lv_obj_get_style_pattern_recolor_opa(obj, part);
|
|
draw_dsc->pattern_repeat = lv_obj_get_style_pattern_repeat(obj, part);
|
|
if(lv_img_src_get_type(draw_dsc->pattern_image) == LV_IMG_SRC_SYMBOL) {
|
|
draw_dsc->pattern_recolor = lv_obj_get_style_pattern_recolor(obj, part);
|
|
draw_dsc->pattern_font = lv_obj_get_style_text_font(obj, part);
|
|
}
|
|
else if(draw_dsc->pattern_recolor_opa > LV_OPA_MIN) {
|
|
draw_dsc->pattern_recolor = lv_obj_get_style_pattern_recolor(obj, part);
|
|
}
|
|
#if LV_USE_BLEND_MODES
|
|
draw_dsc->pattern_blend_mode = lv_obj_get_style_pattern_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if LV_USE_SHADOW
|
|
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(obj, part);
|
|
#if LV_USE_BLEND_MODES
|
|
draw_dsc->shadow_blend_mode = lv_obj_get_style_shadow_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if LV_USE_VALUE_STR
|
|
draw_dsc->value_str = lv_obj_get_style_value_str(obj, part);
|
|
if(draw_dsc->value_str) {
|
|
if(draw_dsc->value_opa > LV_OPA_MIN) {
|
|
draw_dsc->value_opa = lv_obj_get_style_value_opa(obj, part);
|
|
if(draw_dsc->value_opa > LV_OPA_MIN) {
|
|
draw_dsc->value_ofs_x = lv_obj_get_style_value_ofs_x(obj, part);
|
|
draw_dsc->value_ofs_y = lv_obj_get_style_value_ofs_y(obj, part);
|
|
draw_dsc->value_color = lv_obj_get_style_value_color(obj, part);
|
|
draw_dsc->value_font = lv_obj_get_style_value_font(obj, part);
|
|
draw_dsc->value_letter_space = lv_obj_get_style_value_letter_space(obj, part);
|
|
draw_dsc->value_line_space = lv_obj_get_style_value_line_space(obj, part);
|
|
draw_dsc->value_align = lv_obj_get_style_value_align(obj, part);
|
|
#if LV_USE_BLEND_MODES
|
|
draw_dsc->value_blend_mode = lv_obj_get_style_value_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if LV_USE_OPA_SCALE
|
|
if(opa_scale < LV_OPA_MAX) {
|
|
draw_dsc->bg_opa = (uint16_t)((uint16_t)draw_dsc->bg_opa * opa_scale) >> 8;
|
|
draw_dsc->border_opa = (uint16_t)((uint16_t)draw_dsc->border_opa * opa_scale) >> 8;
|
|
draw_dsc->shadow_opa = (uint16_t)((uint16_t)draw_dsc->shadow_opa * opa_scale) >> 8;
|
|
draw_dsc->pattern_opa = (uint16_t)((uint16_t)draw_dsc->pattern_opa * opa_scale) >> 8;
|
|
draw_dsc->value_opa = (uint16_t)((uint16_t)draw_dsc->value_opa * opa_scale) >> 8;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint8_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;
|
|
|
|
#if LV_USE_OPA_SCALE
|
|
lv_opa_t opa_scale = lv_obj_get_style_opa_scale(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;
|
|
#endif
|
|
|
|
draw_dsc->color = lv_obj_get_style_text_color(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_USE_BLEND_MODES
|
|
draw_dsc->blend_mode = lv_obj_get_style_text_blend_mode(obj, part);
|
|
#endif
|
|
|
|
draw_dsc->font = lv_obj_get_style_text_font(obj, part);
|
|
|
|
if(draw_dsc->sel_start != LV_DRAW_LABEL_NO_TXT_SEL && draw_dsc->sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
|
|
draw_dsc->color = lv_obj_get_style_text_sel_color(obj, part);
|
|
}
|
|
|
|
#if LV_USE_BIDI
|
|
draw_dsc->bidi_dir = lv_obj_get_base_dir(obj);
|
|
#endif
|
|
}
|
|
|
|
void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint8_t part, lv_draw_img_dsc_t * draw_dsc)
|
|
{
|
|
draw_dsc->opa = lv_obj_get_style_image_opa(obj, part);
|
|
if(draw_dsc->opa <= LV_OPA_MIN) return;
|
|
|
|
#if LV_USE_OPA_SCALE
|
|
lv_opa_t opa_scale = lv_obj_get_style_opa_scale(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;
|
|
#endif
|
|
|
|
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_image_recolor_opa(obj, part);
|
|
if(draw_dsc->recolor_opa > 0) {
|
|
draw_dsc->recolor = lv_obj_get_style_image_recolor(obj, part);
|
|
}
|
|
#if LV_USE_BLEND_MODES
|
|
draw_dsc->blend_mode = lv_obj_get_style_image_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
|
|
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint8_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;
|
|
|
|
#if LV_USE_OPA_SCALE
|
|
lv_opa_t opa_scale = lv_obj_get_style_opa_scale(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;
|
|
#endif
|
|
|
|
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_USE_BLEND_MODES
|
|
draw_dsc->blend_mode = lv_obj_get_style_line_blend_mode(obj, part);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
lv_coord_t lv_obj_get_draw_rect_ext_pad_size(lv_obj_t * obj, uint8_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; /*THe blur adds only half width*/
|
|
sh_width++;
|
|
sh_width += lv_obj_get_style_shadow_spread(obj, part);
|
|
lv_style_int_t sh_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
|
|
lv_style_int_t sh_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
|
|
sh_width += LV_MATH_MAX(LV_MATH_ABS(sh_ofs_x), LV_MATH_ABS(sh_ofs_y));
|
|
s = LV_MATH_MAX(s, sh_width);
|
|
}
|
|
}
|
|
|
|
const char * value_str = lv_obj_get_style_value_str(obj, part);
|
|
if(value_str) {
|
|
lv_opa_t value_opa = lv_obj_get_style_value_opa(obj, part);
|
|
if(value_opa > LV_OPA_MIN) {
|
|
lv_style_int_t letter_space = lv_obj_get_style_value_letter_space(obj, part);
|
|
lv_style_int_t line_space = lv_obj_get_style_value_letter_space(obj, part);
|
|
const lv_font_t * font = lv_obj_get_style_value_font(obj, part);
|
|
|
|
lv_point_t txt_size;
|
|
_lv_txt_get_size(&txt_size, value_str, font, letter_space, line_space, LV_COORD_MAX, LV_TXT_FLAG_NONE);
|
|
|
|
lv_area_t value_area;
|
|
value_area.x1 = 0;
|
|
value_area.y1 = 0;
|
|
value_area.x2 = txt_size.x - 1;
|
|
value_area.y2 = txt_size.y - 1;
|
|
|
|
lv_style_int_t align = lv_obj_get_style_value_align(obj, part);
|
|
lv_style_int_t xofs = lv_obj_get_style_value_ofs_x(obj, part);
|
|
lv_style_int_t yofs = lv_obj_get_style_value_ofs_y(obj, part);
|
|
lv_point_t p_align;
|
|
_lv_area_align(&obj->coords, &value_area, align, &p_align);
|
|
|
|
value_area.x1 += p_align.x + xofs;
|
|
value_area.y1 += p_align.y + yofs;
|
|
value_area.x2 += p_align.x + xofs;
|
|
value_area.y2 += p_align.y + yofs;
|
|
|
|
s = LV_MATH_MAX(s, obj->coords.x1 - value_area.x1);
|
|
s = LV_MATH_MAX(s, obj->coords.y1 - value_area.y1);
|
|
s = LV_MATH_MAX(s, value_area.x2 - obj->coords.x2);
|
|
s = LV_MATH_MAX(s, value_area.y2 - obj->coords.y2);
|
|
}
|
|
}
|
|
|
|
lv_style_int_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_style_int_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
|
|
s = LV_MATH_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_MATH_MAX(w, h);
|
|
if(wh > 0) s += wh;
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Fade in (from transparent to fully cover) an object and all its children using an `opa_scale` animation.
|
|
* @param obj the object to fade in
|
|
* @param time duration of the animation [ms]
|
|
* @param delay wait before the animation starts [ms]
|
|
*/
|
|
void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
|
|
{
|
|
#if LV_USE_ANIMATION
|
|
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, (lv_anim_exec_xcb_t)opa_scale_anim);
|
|
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);
|
|
#else
|
|
(void) obj; /*Unused*/
|
|
(void) time; /*Unused*/
|
|
(void) delay; /*Unused*/
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Fade out (from fully cover to transparent) an object and all its children using an `opa_scale` animation.
|
|
* @param obj the object to fade in
|
|
* @param time duration of the animation [ms]
|
|
* @param delay wait before the animation starts [ms]
|
|
*/
|
|
void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
|
|
{
|
|
#if LV_USE_ANIMATION
|
|
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, (lv_anim_exec_xcb_t)opa_scale_anim);
|
|
lv_anim_set_time(&a, time);
|
|
lv_anim_set_delay(&a, delay);
|
|
lv_anim_start(&a);
|
|
#else
|
|
(void) obj; /*Unused*/
|
|
(void) time; /*Unused*/
|
|
(void) delay; /*Unused*/
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Check if any object has a given type
|
|
* @param obj pointer to an object
|
|
* @param obj_type type of the object. (e.g. "lv_btn")
|
|
* @return true: valid
|
|
*/
|
|
bool lv_debug_check_obj_type(const lv_obj_t * obj, const char * obj_type)
|
|
{
|
|
if(obj_type[0] == '\0') return true;
|
|
|
|
lv_obj_type_t types;
|
|
lv_obj_get_type((lv_obj_t *)obj, &types);
|
|
|
|
uint8_t i;
|
|
for(i = 0; i < LV_MAX_ANCESTOR_NUM; i++) {
|
|
if(types.type[i] == NULL) break;
|
|
if(strcmp(types.type[i], obj_type) == 0) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 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_debug_check_obj_valid(const lv_obj_t * obj)
|
|
{
|
|
lv_disp_t * disp = lv_disp_get_next(NULL);
|
|
while(disp) {
|
|
lv_obj_t * scr;
|
|
_LV_LL_READ(disp->scr_ll, scr) {
|
|
|
|
if(scr == obj) return true;
|
|
bool found = obj_valid_child(scr, obj);
|
|
if(found) return true;
|
|
}
|
|
|
|
disp = lv_disp_get_next(disp);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
static void lv_obj_del_async_cb(void * obj)
|
|
{
|
|
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
|
|
|
|
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_event_send(obj, LV_EVENT_DELETE, NULL);
|
|
|
|
/*Delete from the group*/
|
|
#if LV_USE_GROUP
|
|
lv_group_t * group = lv_obj_get_group(obj);
|
|
if(group) lv_group_remove_obj(obj);
|
|
#endif
|
|
|
|
/*Remove the animations from this object*/
|
|
#if LV_USE_ANIMATION
|
|
lv_anim_del(obj, NULL);
|
|
trans_del(obj, 0xFF, 0xFF, NULL);
|
|
#endif
|
|
|
|
/*Delete the user data*/
|
|
#if LV_USE_USER_DATA
|
|
#if LV_USE_USER_DATA_FREE
|
|
LV_USER_DATA_FREE(obj);
|
|
#endif
|
|
#endif
|
|
|
|
/*Recursively delete the children*/
|
|
lv_obj_t * i;
|
|
i = _lv_ll_get_head(&(obj->child_ll));
|
|
while(i != NULL) {
|
|
/*Call the recursive delete to the child too*/
|
|
obj_del_core(i);
|
|
|
|
/*Set i to the new head node*/
|
|
i = _lv_ll_get_head(&(obj->child_ll));
|
|
}
|
|
|
|
lv_event_mark_deleted(obj);
|
|
|
|
/* 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 LV_USE_GROUP
|
|
if(indev->group == group && obj == lv_indev_get_obj_act()) {
|
|
lv_indev_reset(indev, obj);
|
|
}
|
|
#endif
|
|
indev = lv_indev_get_next(indev);
|
|
}
|
|
|
|
/* All children deleted.
|
|
* Now clean up the object specific data*/
|
|
obj->signal_cb(obj, LV_SIGNAL_CLEANUP, NULL);
|
|
|
|
/*Remove the object from parent's children list*/
|
|
lv_obj_t * par = lv_obj_get_parent(obj);
|
|
if(par == NULL) { /*It is a screen*/
|
|
lv_disp_t * d = lv_obj_get_disp(obj);
|
|
_lv_ll_remove(&d->scr_ll, obj);
|
|
}
|
|
else {
|
|
_lv_ll_remove(&(par->child_ll), obj);
|
|
}
|
|
|
|
/*Delete the base objects*/
|
|
if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr);
|
|
lv_mem_free(obj); /*Free the object itself*/
|
|
}
|
|
|
|
/**
|
|
* Handle the drawing related tasks of the base objects.
|
|
* @param obj pointer to an object
|
|
* @param clip_area the object will be drawn only in this area
|
|
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
|
|
* (return 'true' if yes)
|
|
* LV_DESIGN_DRAW: draw the object (always return 'true')
|
|
* @param return an element of `lv_design_res_t`
|
|
*/
|
|
static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode)
|
|
{
|
|
if(mode == LV_DESIGN_COVER_CHK) {
|
|
if(lv_obj_get_style_clip_corner(obj, LV_OBJ_PART_MAIN)) return LV_DESIGN_RES_MASKED;
|
|
|
|
/*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_OBJ_PART_MAIN);
|
|
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_OBJ_PART_MAIN);
|
|
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_OBJ_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(clip_area, &coords, r) == false) return LV_DESIGN_RES_NOT_COVER;
|
|
|
|
if(lv_obj_get_style_bg_opa(obj, LV_OBJ_PART_MAIN) < LV_OPA_MAX) return LV_DESIGN_RES_NOT_COVER;
|
|
|
|
if(lv_obj_get_style_bg_blend_mode(obj, LV_OBJ_PART_MAIN) != LV_BLEND_MODE_NORMAL) return LV_DESIGN_RES_NOT_COVER;
|
|
if(lv_obj_get_style_border_blend_mode(obj, LV_OBJ_PART_MAIN) != LV_BLEND_MODE_NORMAL) return LV_DESIGN_RES_NOT_COVER;
|
|
if(lv_obj_get_style_opa_scale(obj, LV_OBJ_PART_MAIN) < LV_OPA_MAX) return LV_DESIGN_RES_NOT_COVER;
|
|
|
|
return LV_DESIGN_RES_COVER;
|
|
|
|
}
|
|
else if(mode == LV_DESIGN_DRAW_MAIN) {
|
|
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_OBJ_PART_MAIN)) {
|
|
draw_dsc.border_post = 1;
|
|
}
|
|
|
|
lv_obj_init_draw_rect_dsc(obj, LV_OBJ_PART_MAIN, &draw_dsc);
|
|
|
|
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_OBJ_PART_MAIN);
|
|
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_OBJ_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_obj_get_style_clip_corner(obj, LV_OBJ_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_OBJ_PART_MAIN);
|
|
/* If it has border make the clip area 1 px smaller to avoid color bleeding
|
|
* The border will cover the minimal issue on the edges*/
|
|
if(draw_dsc.border_post && draw_dsc.border_opa >= LV_OPA_MIN && draw_dsc.border_width > 0) {
|
|
lv_area_t cc_area;
|
|
cc_area.x1 = obj->coords.x1 + 1;
|
|
cc_area.y1 = obj->coords.y1 + 1;
|
|
cc_area.x2 = obj->coords.x2 - 1;
|
|
cc_area.y2 = obj->coords.y2 - 1;
|
|
lv_draw_mask_radius_init(mp, &cc_area, r, false);
|
|
}
|
|
/*If no border use the full size.*/
|
|
else {
|
|
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);
|
|
}
|
|
}
|
|
else if(mode == LV_DESIGN_DRAW_POST) {
|
|
if(lv_obj_get_style_clip_corner(obj, LV_OBJ_PART_MAIN)) {
|
|
lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
|
|
_lv_mem_buf_release(param);
|
|
}
|
|
|
|
/*If the border is drawn later disable loading other properties*/
|
|
if(lv_obj_get_style_border_post(obj, LV_OBJ_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.pattern_opa = LV_OPA_TRANSP;
|
|
draw_dsc.shadow_opa = LV_OPA_TRANSP;
|
|
draw_dsc.value_opa = LV_OPA_TRANSP;
|
|
lv_obj_init_draw_rect_dsc(obj, LV_OBJ_PART_MAIN, &draw_dsc);
|
|
|
|
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_OBJ_PART_MAIN);
|
|
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_OBJ_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);
|
|
}
|
|
}
|
|
|
|
return LV_DESIGN_RES_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the really focused object by taking `focus_parent` into account.
|
|
* @param obj the start object
|
|
* @return the object to really focus
|
|
*/
|
|
lv_obj_t * lv_obj_get_focused_obj(const lv_obj_t * obj)
|
|
{
|
|
if(obj == NULL) return NULL;
|
|
const lv_obj_t * focus_obj = obj;
|
|
while(lv_obj_get_focus_parent(focus_obj) != false && focus_obj != NULL) {
|
|
focus_obj = lv_obj_get_parent(focus_obj);
|
|
}
|
|
|
|
return (lv_obj_t *)focus_obj;
|
|
}
|
|
|
|
/**
|
|
* Signal function of the basic object
|
|
* @param obj pointer to an object
|
|
* @param sign signal type
|
|
* @param param parameter for the signal (depends on signal type)
|
|
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
|
|
*/
|
|
static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
|
|
{
|
|
if(sign == LV_SIGNAL_GET_STYLE) {
|
|
lv_get_style_info_t * info = param;
|
|
if(info->part == LV_OBJ_PART_MAIN) info->result = &obj->style_list;
|
|
else info->result = NULL;
|
|
return LV_RES_OK;
|
|
}
|
|
else if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
|
|
|
|
lv_res_t res = LV_RES_OK;
|
|
|
|
if(sign == LV_SIGNAL_CHILD_CHG) {
|
|
/*Return 'invalid' if the child change signal is not enabled*/
|
|
if(lv_obj_is_protected(obj, LV_PROTECT_CHILD_CHG) != false) res = LV_RES_INV;
|
|
}
|
|
else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
|
|
lv_coord_t d = lv_obj_get_draw_rect_ext_pad_size(obj, LV_OBJ_PART_MAIN);
|
|
obj->ext_draw_pad = LV_MATH_MAX(obj->ext_draw_pad, d);
|
|
}
|
|
#if LV_USE_OBJ_REALIGN
|
|
else if(sign == LV_SIGNAL_PARENT_SIZE_CHG) {
|
|
if(obj->realign.auto_realign) {
|
|
lv_obj_realign(obj);
|
|
}
|
|
}
|
|
#endif
|
|
else if(sign == LV_SIGNAL_STYLE_CHG) {
|
|
lv_obj_refresh_ext_draw_pad(obj);
|
|
}
|
|
else if(sign == LV_SIGNAL_PRESSED) {
|
|
lv_obj_add_state(obj, LV_STATE_PRESSED);
|
|
}
|
|
else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
|
|
lv_obj_clear_state(obj, LV_STATE_PRESSED);
|
|
}
|
|
else if(sign == LV_SIGNAL_FOCUS) {
|
|
bool editing = false;
|
|
#if LV_USE_GROUP
|
|
editing = lv_group_get_editing(lv_obj_get_group(obj));
|
|
#endif
|
|
if(editing) {
|
|
uint8_t state = LV_STATE_FOCUSED;
|
|
state |= LV_STATE_EDITED;
|
|
|
|
/*if using focus mode, change target to parent*/
|
|
obj = lv_obj_get_focused_obj(obj);
|
|
|
|
lv_obj_add_state(obj, state);
|
|
}
|
|
else {
|
|
|
|
/*if using focus mode, change target to parent*/
|
|
obj = lv_obj_get_focused_obj(obj);
|
|
|
|
lv_obj_add_state(obj, LV_STATE_FOCUSED);
|
|
lv_obj_clear_state(obj, LV_STATE_EDITED);
|
|
}
|
|
}
|
|
else if(sign == LV_SIGNAL_DEFOCUS) {
|
|
|
|
/*if using focus mode, change target to parent*/
|
|
obj = lv_obj_get_focused_obj(obj);
|
|
|
|
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED);
|
|
}
|
|
else if(sign == LV_SIGNAL_CLEANUP) {
|
|
lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Reposition the children of an object. (Called recursively)
|
|
* @param obj pointer to an object which children will be repositioned
|
|
* @param x_diff x coordinate shift
|
|
* @param y_diff y coordinate shift
|
|
*/
|
|
static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff)
|
|
{
|
|
lv_obj_t * i;
|
|
_LV_LL_READ(obj->child_ll, i) {
|
|
i->coords.x1 += x_diff;
|
|
i->coords.y1 += y_diff;
|
|
i->coords.x2 += x_diff;
|
|
i->coords.y2 += y_diff;
|
|
|
|
refresh_children_position(i, x_diff, y_diff);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh the style of all children of an object. (Called recursively)
|
|
* @param style refresh objects only with this style_list.
|
|
* @param obj pointer to an object
|
|
*/
|
|
static void report_style_mod_core(void * style, lv_obj_t * obj)
|
|
{
|
|
uint8_t part;
|
|
for(part = 0; part != _LV_OBJ_PART_REAL_LAST; part++) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) break;
|
|
|
|
uint8_t ci;
|
|
for(ci = 0; ci < list->style_cnt; ci++) {
|
|
/* changed class to _class to allow compilation as c++ */
|
|
lv_style_t * _class = lv_style_list_get_style(list, ci);
|
|
if(_class == style || style == NULL) {
|
|
lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
lv_obj_t * child = lv_obj_get_child(obj, NULL);
|
|
while(child) {
|
|
report_style_mod_core(style, child);
|
|
child = lv_obj_get_child(obj, child);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
lv_obj_t * child = lv_obj_get_child(obj, NULL);
|
|
while(child != NULL) {
|
|
lv_obj_invalidate(child);
|
|
child->signal_cb(child, LV_SIGNAL_STYLE_CHG, NULL);
|
|
lv_obj_invalidate(child);
|
|
|
|
refresh_children_style(child); /*Check children too*/
|
|
child = lv_obj_get_child(obj, child);
|
|
}
|
|
}
|
|
|
|
static void base_dir_refr_children(lv_obj_t * obj)
|
|
{
|
|
lv_obj_t * child;
|
|
child = lv_obj_get_child(obj, NULL);
|
|
|
|
while(child) {
|
|
if(child->base_dir == LV_BIDI_DIR_INHERIT) {
|
|
lv_signal_send(child, LV_SIGNAL_BASE_DIR_CHG, NULL);
|
|
base_dir_refr_children(child);
|
|
}
|
|
|
|
child = lv_obj_get_child(obj, child);
|
|
}
|
|
}
|
|
|
|
static void obj_align_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
|
|
lv_coord_t x_ofs, lv_coord_t y_ofs)
|
|
{
|
|
lv_point_t new_pos;
|
|
_lv_area_align(&base->coords, &obj->coords, align, &new_pos);
|
|
|
|
/*Bring together the coordination system of base and obj*/
|
|
lv_obj_t * par = lv_obj_get_parent(obj);
|
|
lv_coord_t par_abs_x = par->coords.x1;
|
|
lv_coord_t par_abs_y = par->coords.y1;
|
|
new_pos.x += x_ofs;
|
|
new_pos.y += y_ofs;
|
|
new_pos.x -= par_abs_x;
|
|
new_pos.y -= par_abs_y;
|
|
|
|
if(x_set && y_set) lv_obj_set_pos(obj, new_pos.x, new_pos.y);
|
|
else if(x_set) lv_obj_set_x(obj, new_pos.x);
|
|
else if(y_set) lv_obj_set_y(obj, new_pos.y);
|
|
}
|
|
|
|
static void obj_align_mid_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
|
|
lv_coord_t x_ofs, lv_coord_t y_ofs)
|
|
{
|
|
lv_coord_t new_x = lv_obj_get_x(obj);
|
|
lv_coord_t new_y = lv_obj_get_y(obj);
|
|
|
|
lv_coord_t obj_w_half = lv_obj_get_width(obj) / 2;
|
|
lv_coord_t obj_h_half = lv_obj_get_height(obj) / 2;
|
|
|
|
|
|
switch(align) {
|
|
case LV_ALIGN_CENTER:
|
|
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
|
|
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_IN_TOP_LEFT:
|
|
new_x = -obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
case LV_ALIGN_IN_TOP_MID:
|
|
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_IN_TOP_RIGHT:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_IN_BOTTOM_LEFT:
|
|
new_x = -obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
case LV_ALIGN_IN_BOTTOM_MID:
|
|
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_IN_BOTTOM_RIGHT:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_IN_LEFT_MID:
|
|
new_x = -obj_w_half;
|
|
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_IN_RIGHT_MID:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_TOP_LEFT:
|
|
new_x = -obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_TOP_MID:
|
|
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_TOP_RIGHT:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_BOTTOM_LEFT:
|
|
new_x = -obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_BOTTOM_MID:
|
|
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_BOTTOM_RIGHT:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_LEFT_TOP:
|
|
new_x = -obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_LEFT_MID:
|
|
new_x = -obj_w_half;
|
|
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_LEFT_BOTTOM:
|
|
new_x = -obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_RIGHT_TOP:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = -obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_RIGHT_MID:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
|
|
break;
|
|
|
|
case LV_ALIGN_OUT_RIGHT_BOTTOM:
|
|
new_x = lv_obj_get_width(base) - obj_w_half;
|
|
new_y = lv_obj_get_height(base) - obj_h_half;
|
|
break;
|
|
}
|
|
|
|
/*Bring together the coordination system of base and obj*/
|
|
lv_obj_t * par = lv_obj_get_parent(obj);
|
|
lv_coord_t base_abs_x = base->coords.x1;
|
|
lv_coord_t base_abs_y = base->coords.y1;
|
|
lv_coord_t par_abs_x = par->coords.x1;
|
|
lv_coord_t par_abs_y = par->coords.y1;
|
|
new_x += x_ofs + base_abs_x;
|
|
new_y += y_ofs + base_abs_y;
|
|
new_x -= par_abs_x;
|
|
new_y -= par_abs_y;
|
|
if(x_set && y_set) lv_obj_set_pos(obj, new_x, new_y);
|
|
else if(x_set) lv_obj_set_x(obj, new_x);
|
|
else if(y_set) lv_obj_set_y(obj, new_y);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if LV_USE_ANIMATION
|
|
|
|
/**
|
|
* Allocate and initialize a transition for a property of an object if the properties value is different in the new state.
|
|
* It allocates `lv_style_trans_t` in `_lv_obj_style_trans_ll` and set only `start/end_values`. No animation will be created here.
|
|
* @param obj and object to add the transition
|
|
* @param prop the property to apply the transaction
|
|
* @param part the part of the object to apply the transaction
|
|
* @param prev_state the previous state of the objects
|
|
* @param new_state the new state of the object
|
|
* @return pointer to the allocated `the transaction` variable or `NULL` if no transition created
|
|
*/
|
|
static lv_style_trans_t * trans_create(lv_obj_t * obj, lv_style_property_t prop, uint8_t part, lv_state_t prev_state,
|
|
lv_state_t new_state)
|
|
{
|
|
lv_style_trans_t * tr;
|
|
lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
|
|
lv_style_t * style_trans = _lv_style_list_get_transition_style(style_list);
|
|
|
|
/*Get the previous and current values*/
|
|
if((prop & 0xF) < LV_STYLE_ID_COLOR) { /*Int*/
|
|
style_list->skip_trans = 1;
|
|
obj->state = prev_state;
|
|
lv_style_int_t int1 = _lv_obj_get_style_int(obj, part, prop);
|
|
obj->state = new_state;
|
|
lv_style_int_t int2 = _lv_obj_get_style_int(obj, part, prop);
|
|
style_list->skip_trans = 0;
|
|
|
|
if(int1 == int2) return NULL;
|
|
obj->state = prev_state;
|
|
int1 = _lv_obj_get_style_int(obj, part, prop);
|
|
obj->state = new_state;
|
|
_lv_style_set_int(style_trans, prop, int1); /*Be sure `trans_style` has a valid value */
|
|
|
|
if(prop == LV_STYLE_RADIUS) {
|
|
if(int1 == LV_RADIUS_CIRCLE || int2 == LV_RADIUS_CIRCLE) {
|
|
lv_coord_t whalf = lv_obj_get_width(obj) / 2;
|
|
lv_coord_t hhalf = lv_obj_get_width(obj) / 2;
|
|
if(int1 == LV_RADIUS_CIRCLE) int1 = LV_MATH_MIN(whalf + 1, hhalf + 1);
|
|
if(int2 == LV_RADIUS_CIRCLE) int2 = LV_MATH_MIN(whalf + 1, hhalf + 1);
|
|
}
|
|
}
|
|
|
|
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
|
|
LV_ASSERT_MEM(tr);
|
|
if(tr == NULL) return NULL;
|
|
tr->start_value._int = int1;
|
|
tr->end_value._int = int2;
|
|
}
|
|
else if((prop & 0xF) < LV_STYLE_ID_OPA) { /*Color*/
|
|
style_list->skip_trans = 1;
|
|
obj->state = prev_state;
|
|
lv_color_t c1 = _lv_obj_get_style_color(obj, part, prop);
|
|
obj->state = new_state;
|
|
lv_color_t c2 = _lv_obj_get_style_color(obj, part, prop);
|
|
style_list->skip_trans = 0;
|
|
|
|
if(c1.full == c2.full) return NULL;
|
|
obj->state = prev_state;
|
|
c1 = _lv_obj_get_style_color(obj, part, prop);
|
|
obj->state = new_state;
|
|
_lv_style_set_color(style_trans, prop, c1); /*Be sure `trans_style` has a valid value */
|
|
|
|
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
|
|
LV_ASSERT_MEM(tr);
|
|
if(tr == NULL) return NULL;
|
|
tr->start_value._color = c1;
|
|
tr->end_value._color = c2;
|
|
}
|
|
else if((prop & 0xF) < LV_STYLE_ID_PTR) { /*Opa*/
|
|
style_list->skip_trans = 1;
|
|
obj->state = prev_state;
|
|
lv_opa_t o1 = _lv_obj_get_style_opa(obj, part, prop);
|
|
obj->state = new_state;
|
|
lv_opa_t o2 = _lv_obj_get_style_opa(obj, part, prop);
|
|
style_list->skip_trans = 0;
|
|
|
|
if(o1 == o2) return NULL;
|
|
|
|
obj->state = prev_state;
|
|
o1 = _lv_obj_get_style_opa(obj, part, prop);
|
|
obj->state = new_state;
|
|
_lv_style_set_opa(style_trans, prop, o1); /*Be sure `trans_style` has a valid value */
|
|
|
|
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
|
|
LV_ASSERT_MEM(tr);
|
|
if(tr == NULL) return NULL;
|
|
tr->start_value._opa = o1;
|
|
tr->end_value._opa = o2;
|
|
}
|
|
else { /*Ptr*/
|
|
obj->state = prev_state;
|
|
style_list->skip_trans = 1;
|
|
const void * p1 = _lv_obj_get_style_ptr(obj, part, prop);
|
|
obj->state = new_state;
|
|
const void * p2 = _lv_obj_get_style_ptr(obj, part, prop);
|
|
style_list->skip_trans = 0;
|
|
|
|
if(memcmp(&p1, &p2, sizeof(const void *)) == 0) return NULL;
|
|
obj->state = prev_state;
|
|
p1 = _lv_obj_get_style_ptr(obj, part, prop);
|
|
obj->state = new_state;
|
|
_lv_style_set_ptr(style_trans, prop, p1); /*Be sure `trans_style` has a valid value */
|
|
|
|
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
|
|
LV_ASSERT_MEM(tr);
|
|
if(tr == NULL) return NULL;
|
|
tr->start_value._ptr = p1;
|
|
tr->end_value._ptr = p2;
|
|
}
|
|
|
|
return tr;
|
|
}
|
|
|
|
/**
|
|
* 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" then this. `NULL` is not used
|
|
*/
|
|
static void trans_del(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_style_trans_t * tr_limit)
|
|
{
|
|
lv_style_trans_t * tr;
|
|
lv_style_trans_t * tr_prev;
|
|
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->part || part == 0xFF) && (prop == tr->prop || prop == 0xFF)) {
|
|
/* Remove the transitioned property from trans. style
|
|
* to allow changing it by normal styles*/
|
|
lv_style_list_t * list = lv_obj_get_style_list(tr->obj, tr->part);
|
|
lv_style_t * style_trans = _lv_style_list_get_transition_style(list);
|
|
lv_style_remove_prop(style_trans, tr->prop);
|
|
|
|
lv_anim_del(tr, NULL);
|
|
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
|
|
lv_mem_free(tr);
|
|
}
|
|
tr = tr_prev;
|
|
}
|
|
}
|
|
|
|
static void trans_anim_cb(lv_style_trans_t * tr, lv_anim_value_t v)
|
|
{
|
|
lv_style_list_t * list = lv_obj_get_style_list(tr->obj, tr->part);
|
|
lv_style_t * style = _lv_style_list_get_transition_style(list);
|
|
|
|
if((tr->prop & 0xF) < LV_STYLE_ID_COLOR) { /*Value*/
|
|
lv_style_int_t x;
|
|
if(v == 0) x = tr->start_value._int;
|
|
else if(v == 255) x = tr->end_value._int;
|
|
else x = tr->start_value._int + ((int32_t)((int32_t)(tr->end_value._int - tr->start_value._int) * v) >> 8);
|
|
_lv_style_set_int(style, tr->prop, x);
|
|
}
|
|
else if((tr->prop & 0xF) < LV_STYLE_ID_OPA) { /*Color*/
|
|
lv_color_t x;
|
|
if(v <= 0) x = tr->start_value._color;
|
|
else if(v >= 255) x = tr->end_value._color;
|
|
else x = lv_color_mix(tr->end_value._color, tr->start_value._color, v);
|
|
_lv_style_set_color(style, tr->prop, x);
|
|
}
|
|
else if((tr->prop & 0xF) < LV_STYLE_ID_PTR) { /*Opa*/
|
|
lv_opa_t x;
|
|
if(v <= 0) x = tr->start_value._opa;
|
|
else if(v >= 255) x = tr->end_value._opa;
|
|
else x = tr->start_value._opa + (((tr->end_value._opa - tr->start_value._opa) * v) >> 8);
|
|
_lv_style_set_opa(style, tr->prop, x);
|
|
}
|
|
else {
|
|
const void * x;
|
|
if(v < 128) x = tr->start_value._ptr;
|
|
else x = tr->end_value._ptr;
|
|
_lv_style_set_ptr(style, tr->prop, x);
|
|
}
|
|
lv_obj_refresh_style(tr->obj, tr->part, tr->prop);
|
|
|
|
}
|
|
|
|
static void trans_anim_start_cb(lv_anim_t * a)
|
|
{
|
|
lv_style_trans_t * tr = a->var;
|
|
|
|
lv_style_property_t prop_tmp = tr->prop;
|
|
|
|
/*Start the animation from the current value*/
|
|
if((prop_tmp & 0xF) < LV_STYLE_ID_COLOR) { /*Int*/
|
|
tr->start_value._int = _lv_obj_get_style_int(tr->obj, tr->part, prop_tmp);
|
|
}
|
|
else if((prop_tmp & 0xF) < LV_STYLE_ID_OPA) { /*Color*/
|
|
tr->start_value._color = _lv_obj_get_style_color(tr->obj, tr->part, prop_tmp);
|
|
}
|
|
else if((prop_tmp & 0xF) < LV_STYLE_ID_PTR) { /*Opa*/
|
|
tr->start_value._opa = _lv_obj_get_style_opa(tr->obj, tr->part, prop_tmp);
|
|
}
|
|
else { /*Ptr*/
|
|
tr->start_value._ptr = _lv_obj_get_style_ptr(tr->obj, tr->part, prop_tmp);
|
|
}
|
|
|
|
/*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
|
|
tr->prop = 0;
|
|
/*Delete the relate transition if any*/
|
|
trans_del(tr->obj, tr->part, prop_tmp, tr);
|
|
|
|
tr->prop = prop_tmp;
|
|
|
|
}
|
|
|
|
static void trans_anim_ready_cb(lv_anim_t * a)
|
|
{
|
|
lv_style_trans_t * tr = a->var;
|
|
|
|
/* 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;
|
|
lv_style_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->part == tr->part && tr_i->prop == tr->prop) {
|
|
running = true;
|
|
}
|
|
}
|
|
|
|
if(!running) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(tr->obj, tr->part);
|
|
lv_style_t * style_trans = _lv_style_list_get_transition_style(list);
|
|
lv_style_remove_prop(style_trans, tr->prop);
|
|
}
|
|
|
|
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
|
|
lv_mem_free(tr);
|
|
}
|
|
|
|
static void opa_scale_anim(lv_obj_t * obj, lv_anim_value_t v)
|
|
{
|
|
lv_obj_set_style_local_opa_scale(obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, v);
|
|
}
|
|
|
|
static void fade_in_anim_ready(lv_anim_t * a)
|
|
{
|
|
lv_style_remove_prop(lv_obj_get_local_style(a->var, LV_OBJ_PART_MAIN), LV_STYLE_OPA_SCALE);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void lv_event_mark_deleted(lv_obj_t * obj)
|
|
{
|
|
lv_event_temp_data_t * t = event_temp_data_head;
|
|
|
|
while(t) {
|
|
if(t->obj == obj) t->deleted = true;
|
|
t = t->prev;
|
|
}
|
|
}
|
|
|
|
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
|
|
{
|
|
/*Check all children of `parent`*/
|
|
lv_obj_t * child;
|
|
_LV_LL_READ(parent->child_ll, child) {
|
|
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;
|
|
}
|
|
|
|
static bool style_prop_is_cacheble(lv_style_property_t prop)
|
|
{
|
|
|
|
switch(prop) {
|
|
case LV_STYLE_PROP_ALL:
|
|
case LV_STYLE_BG_GRAD_DIR:
|
|
case LV_STYLE_CLIP_CORNER:
|
|
case LV_STYLE_TEXT_LETTER_SPACE:
|
|
case LV_STYLE_TEXT_LINE_SPACE:
|
|
case LV_STYLE_TEXT_FONT:
|
|
case LV_STYLE_TRANSFORM_ANGLE:
|
|
case LV_STYLE_TRANSFORM_WIDTH:
|
|
case LV_STYLE_TRANSFORM_HEIGHT:
|
|
case LV_STYLE_TRANSFORM_ZOOM:
|
|
case LV_STYLE_BORDER_WIDTH:
|
|
case LV_STYLE_OUTLINE_WIDTH:
|
|
case LV_STYLE_RADIUS:
|
|
case LV_STYLE_SHADOW_WIDTH:
|
|
case LV_STYLE_OPA_SCALE:
|
|
case LV_STYLE_BG_OPA:
|
|
case LV_STYLE_BORDER_SIDE:
|
|
case LV_STYLE_BORDER_POST:
|
|
case LV_STYLE_IMAGE_RECOLOR_OPA:
|
|
case LV_STYLE_VALUE_STR:
|
|
case LV_STYLE_PATTERN_IMAGE:
|
|
case LV_STYLE_PAD_TOP:
|
|
case LV_STYLE_PAD_BOTTOM:
|
|
case LV_STYLE_PAD_LEFT:
|
|
case LV_STYLE_PAD_RIGHT:
|
|
case LV_STYLE_BG_BLEND_MODE:
|
|
case LV_STYLE_BORDER_BLEND_MODE:
|
|
case LV_STYLE_IMAGE_BLEND_MODE:
|
|
case LV_STYLE_LINE_BLEND_MODE:
|
|
case LV_STYLE_OUTLINE_BLEND_MODE:
|
|
case LV_STYLE_PATTERN_BLEND_MODE:
|
|
case LV_STYLE_SHADOW_BLEND_MODE:
|
|
case LV_STYLE_TEXT_BLEND_MODE:
|
|
case LV_STYLE_VALUE_BLEND_MODE:
|
|
return true;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the cache of style list
|
|
* @param obj pointer to an obejct
|
|
* @param part the part of the object
|
|
* @param prop the property which triggered the update
|
|
*/
|
|
static void update_style_cache(lv_obj_t * obj, uint8_t part, uint16_t prop)
|
|
{
|
|
if(style_prop_is_cacheble(prop) == false) return;
|
|
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
|
|
bool ignore_cache_ori = list->ignore_cache;
|
|
list->ignore_cache = 1;
|
|
|
|
#if LV_USE_OPA_SCALE
|
|
list->opa_scale_cover = lv_obj_get_style_opa_scale(obj, part) == LV_OPA_COVER ? 1 : 0;
|
|
#else
|
|
list->opa_scale_cover = 1;
|
|
#endif
|
|
list->text_decor_none = lv_obj_get_style_text_decor(obj, part) == LV_TEXT_DECOR_NONE ? 1 : 0;
|
|
list->text_font_normal = lv_obj_get_style_text_font(obj, part) == LV_THEME_DEFAULT_FONT_NORMAL ? 1 : 0;
|
|
|
|
list->text_space_zero = 1;
|
|
if(lv_obj_get_style_text_letter_space(obj, part) != 0 ||
|
|
lv_obj_get_style_text_line_space(obj, part) != 0) {
|
|
list->text_space_zero = 0;
|
|
}
|
|
|
|
|
|
lv_opa_t bg_opa = lv_obj_get_style_bg_opa(obj, part);
|
|
list->bg_opa_transp = bg_opa == LV_OPA_TRANSP ? 1 : 0;
|
|
list->bg_opa_cover = bg_opa == LV_OPA_COVER ? 1 : 0;
|
|
|
|
list->bg_grad_dir_none = lv_obj_get_style_bg_grad_dir(obj, part) == LV_GRAD_DIR_NONE ? 1 : 0;
|
|
list->border_width_zero = lv_obj_get_style_border_width(obj, part) == 0 ? 1 : 0;
|
|
list->border_side_full = lv_obj_get_style_border_side(obj, part) == LV_BORDER_SIDE_FULL ? 1 : 0;
|
|
list->border_post_off = lv_obj_get_style_border_post(obj, part) == 0 ? 1 : 0;
|
|
list->clip_corner_off = lv_obj_get_style_clip_corner(obj, part) == false ? 1 : 0;
|
|
list->img_recolor_opa_transp = lv_obj_get_style_image_recolor_opa(obj, part) == LV_OPA_TRANSP ? 1 : 0;
|
|
list->outline_width_zero = lv_obj_get_style_outline_width(obj, part) == 0 ? 1 : 0;
|
|
list->pattern_img_null = lv_obj_get_style_pattern_image(obj, part) == NULL ? 1 : 0;
|
|
list->radius_zero = lv_obj_get_style_radius(obj, part) == 0 ? 1 : 0;
|
|
list->shadow_width_zero = lv_obj_get_style_shadow_width(obj, part) == 0 ? 1 : 0;
|
|
list->value_txt_str = lv_obj_get_style_value_str(obj, part) == NULL ? 1 : 0;
|
|
|
|
|
|
list->transform_all_zero = 1;
|
|
if(lv_obj_get_style_transform_angle(obj, part) != 0 ||
|
|
lv_obj_get_style_transform_width(obj, part) != 0 ||
|
|
lv_obj_get_style_transform_height(obj, part) != 0 ||
|
|
lv_obj_get_style_transform_zoom(obj, part) != LV_IMG_ZOOM_NONE) {
|
|
list->transform_all_zero = 0;
|
|
}
|
|
|
|
list->pad_all_zero = 1;
|
|
if(lv_obj_get_style_pad_top(obj, part) != 0 ||
|
|
lv_obj_get_style_pad_bottom(obj, part) != 0 ||
|
|
lv_obj_get_style_pad_left(obj, part) != 0 ||
|
|
lv_obj_get_style_pad_right(obj, part) != 0) {
|
|
list->pad_all_zero = 0;
|
|
}
|
|
|
|
list->blend_mode_all_normal = 1;
|
|
#if LV_USE_BLEND_MODES
|
|
if(lv_obj_get_style_bg_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_border_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_pattern_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_outline_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_value_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_text_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_line_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_image_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
|
|
lv_obj_get_style_shadow_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL) {
|
|
list->blend_mode_all_normal = 0;
|
|
}
|
|
#endif
|
|
list->ignore_cache = ignore_cache_ori;
|
|
list->valid_cache = 1;
|
|
}
|
|
|
|
/**
|
|
* Update the cache of style list
|
|
* @param obj pointer to an object
|
|
* @param part the part of the object
|
|
*/
|
|
static void update_style_cache_children(lv_obj_t * obj)
|
|
{
|
|
uint8_t part;
|
|
for(part = 0; part != _LV_OBJ_PART_REAL_LAST; part++) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) break;
|
|
|
|
bool ignore_cache_ori = list->ignore_cache;
|
|
list->ignore_cache = 1;
|
|
|
|
list->opa_scale_cover = lv_obj_get_style_opa_scale(obj, part) == LV_OPA_COVER ? 1 : 0;
|
|
list->text_decor_none = lv_obj_get_style_text_decor(obj, part) == LV_TEXT_DECOR_NONE ? 1 : 0;
|
|
list->text_font_normal = lv_obj_get_style_text_font(obj, part) == lv_theme_get_font_normal() ? 1 : 0;
|
|
list->img_recolor_opa_transp = lv_obj_get_style_image_recolor_opa(obj, part) == LV_OPA_TRANSP ? 1 : 0;
|
|
|
|
list->text_space_zero = 1;
|
|
if(lv_obj_get_style_text_letter_space(obj, part) != 0 ||
|
|
lv_obj_get_style_text_line_space(obj, part) != 0) {
|
|
list->text_space_zero = 0;
|
|
}
|
|
|
|
list->ignore_cache = ignore_cache_ori;
|
|
}
|
|
|
|
lv_obj_t * child = lv_obj_get_child(obj, NULL);
|
|
while(child) {
|
|
update_style_cache_children(child);
|
|
child = lv_obj_get_child(obj, child);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Mark the object and all of it's children's style lists as invalid.
|
|
* The cache will be updated when a cached property asked nest time
|
|
* @param obj pointer to an object
|
|
*/
|
|
static void invalidate_style_cache(lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
|
|
{
|
|
if(style_prop_is_cacheble(prop) == false) return;
|
|
|
|
if(part != LV_OBJ_PART_ALL) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) return;
|
|
list->valid_cache = 0;
|
|
}
|
|
else {
|
|
|
|
for(part = 0; part < _LV_OBJ_PART_REAL_FIRST; part++) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) break;
|
|
list->valid_cache = 0;
|
|
}
|
|
for(part = _LV_OBJ_PART_REAL_FIRST; part < 0xFF; part++) {
|
|
lv_style_list_t * list = lv_obj_get_style_list(obj, part);
|
|
if(list == NULL) break;
|
|
list->valid_cache = 0;
|
|
}
|
|
}
|
|
|
|
lv_obj_t * child = lv_obj_get_child(obj, NULL);
|
|
while(child) {
|
|
update_style_cache_children(child);
|
|
child = lv_obj_get_child(obj, child);
|
|
}
|
|
}
|
|
|
|
static void style_snapshot(lv_obj_t * obj, uint8_t part, style_snapshot_t * shot)
|
|
{
|
|
_lv_obj_disable_style_caching(obj, true);
|
|
_lv_memset_00(shot, sizeof(style_snapshot_t));
|
|
lv_draw_rect_dsc_init(&shot->rect);
|
|
lv_draw_label_dsc_init(&shot->label);
|
|
lv_draw_img_dsc_init(&shot->img);
|
|
lv_draw_line_dsc_init(&shot->line);
|
|
|
|
lv_obj_init_draw_rect_dsc(obj, part, &shot->rect);
|
|
lv_obj_init_draw_label_dsc(obj, part, &shot->label);
|
|
lv_obj_init_draw_img_dsc(obj, part, &shot->img);
|
|
lv_obj_init_draw_line_dsc(obj, part, &shot->line);
|
|
|
|
|
|
shot->pad_top = lv_obj_get_style_pad_top(obj, part);
|
|
shot->pad_bottom = lv_obj_get_style_pad_bottom(obj, part);
|
|
shot->pad_right = lv_obj_get_style_pad_right(obj, part);
|
|
shot->pad_left = lv_obj_get_style_pad_left(obj, part);
|
|
shot->pad_inner = lv_obj_get_style_pad_inner(obj, part);
|
|
shot->margin_top = lv_obj_get_style_margin_top(obj, part);
|
|
shot->margin_bottom = lv_obj_get_style_margin_bottom(obj, part);
|
|
shot->margin_left = lv_obj_get_style_margin_left(obj, part);
|
|
shot->margin_right = lv_obj_get_style_margin_right(obj, part);
|
|
shot->size = lv_obj_get_style_size(obj, part);
|
|
shot->transform_width = lv_obj_get_style_transform_width(obj, part);
|
|
shot->transform_height = lv_obj_get_style_transform_height(obj, part);
|
|
shot->transform_angle = lv_obj_get_style_transform_angle(obj, part);
|
|
shot->transform_zoom = lv_obj_get_style_transform_zoom(obj, part);
|
|
shot->scale_width = lv_obj_get_style_scale_width(obj, part);
|
|
shot->scale_border_width = lv_obj_get_style_scale_border_width(obj, part);
|
|
shot->scale_end_border_width = lv_obj_get_style_scale_end_border_width(obj, part);
|
|
shot->scale_end_line_width = lv_obj_get_style_scale_end_line_width(obj, part);
|
|
shot->scale_grad_color = lv_obj_get_style_scale_grad_color(obj, part);
|
|
shot->scale_end_color = lv_obj_get_style_scale_end_color(obj, part);
|
|
shot->opa_scale = lv_obj_get_style_opa_scale(obj, part);
|
|
shot->clip_corder = lv_obj_get_style_clip_corner(obj, part);
|
|
shot->border_post = lv_obj_get_style_border_post(obj, part);
|
|
|
|
_lv_obj_disable_style_caching(obj, false);
|
|
}
|
|
|
|
static style_snapshot_res_t style_snapshot_compare(style_snapshot_t * shot1, style_snapshot_t * shot2)
|
|
{
|
|
if(memcmp(shot1, shot2, sizeof(style_snapshot_t)) == 0) return STYLE_COMPARE_SAME;
|
|
|
|
|
|
if(shot1->pad_top != shot2->pad_top) return STYLE_COMPARE_DIFF;
|
|
if(shot1->pad_bottom != shot2->pad_bottom) return STYLE_COMPARE_DIFF;
|
|
if(shot1->pad_left != shot2->pad_right) return STYLE_COMPARE_DIFF;
|
|
if(shot1->pad_right != shot2->pad_right) return STYLE_COMPARE_DIFF;
|
|
if(shot1->pad_top != shot2->pad_top) return STYLE_COMPARE_DIFF;
|
|
if(shot1->pad_inner != shot2->pad_inner) return STYLE_COMPARE_DIFF;
|
|
if(shot1->margin_top != shot2->margin_top) return STYLE_COMPARE_DIFF;
|
|
if(shot1->margin_bottom != shot2->margin_bottom) return STYLE_COMPARE_DIFF;
|
|
if(shot1->margin_left != shot2->margin_right) return STYLE_COMPARE_DIFF;
|
|
if(shot1->margin_right != shot2->margin_right) return STYLE_COMPARE_DIFF;
|
|
if(shot1->margin_top != shot2->margin_top) return STYLE_COMPARE_DIFF;
|
|
if(shot1->transform_width != shot2->transform_width) return STYLE_COMPARE_DIFF;
|
|
if(shot1->transform_height != shot2->transform_height) return STYLE_COMPARE_DIFF;
|
|
if(shot1->transform_angle != shot2->transform_angle) return STYLE_COMPARE_DIFF;
|
|
if(shot1->transform_zoom != shot2->transform_zoom) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.outline_width != shot2->rect.outline_width) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.outline_pad != shot2->rect.outline_pad) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.value_font != shot2->rect.value_font) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.value_align != shot2->rect.value_align) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.value_font != shot2->rect.value_font) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.shadow_spread != shot2->rect.shadow_spread) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.shadow_width != shot2->rect.shadow_width) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.shadow_ofs_x != shot2->rect.shadow_ofs_x) return STYLE_COMPARE_DIFF;
|
|
if(shot1->rect.shadow_ofs_y != shot2->rect.shadow_ofs_y) return STYLE_COMPARE_DIFF;
|
|
|
|
/*If not returned earlier its just a visual difference, a simple redraw is enough*/
|
|
return STYLE_COMPARE_VISUAL_DIFF;
|
|
}
|