This commit is contained in:
2026-02-26 19:56:43 -08:00
commit f000ee7c19
1137 changed files with 603879 additions and 0 deletions

View File

@@ -0,0 +1,312 @@
/**
* @file lv_global.h
*
*/
#ifndef LV_GLOBAL_H
#define LV_GLOBAL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../draw/lv_draw.h"
#if LV_USE_DRAW_SW
#include "../draw/sw/lv_draw_sw.h"
#endif
#include "../misc/lv_anim.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color_op.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_log.h"
#include "../misc/lv_style.h"
#include "../misc/lv_timer.h"
#include "../osal/lv_os_private.h"
#include "../debugging/sysmon/lv_sysmon.h"
#include "../stdlib/builtin/lv_tlsf.h"
#if LV_USE_FONT_COMPRESSED
#include "../font/fmt_txt/lv_font_fmt_txt_private.h"
#endif
#include "../tick/lv_tick.h"
#include "../layouts/lv_layout.h"
#include "../misc/lv_types.h"
#include "../misc/lv_timer_private.h"
#include "../misc/lv_anim_private.h"
#include "../tick/lv_tick_private.h"
#include "../draw/lv_draw_buf_private.h"
#include "../draw/lv_draw_private.h"
#include "../draw/sw/lv_draw_sw_private.h"
#include "../draw/sw/lv_draw_sw_mask_private.h"
#include "../stdlib/builtin/lv_tlsf_private.h"
#include "../debugging/sysmon/lv_sysmon_private.h"
#include "../debugging/test/lv_test_private.h"
#include "../layouts/lv_layout_private.h"
/*********************
* DEFINES
*********************/
#define ZERO_MEM_SENTINEL 0xa1b2c3d4
/**********************
* TYPEDEFS
**********************/
#if LV_USE_SPAN != 0
struct _snippet_stack;
#endif
#if LV_USE_FREETYPE
struct _lv_freetype_context_t;
#endif
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
struct _lv_profiler_builtin_ctx_t;
#endif
#if LV_USE_NUTTX
struct _lv_nuttx_ctx_t;
#endif
typedef struct _lv_global_t {
/**
* User data for the LVGL library. Move from the bottom of the struct
* to avoid breaking the ABI. E.g., if the user data is used by a
* closed-source library, this can help to avoid re-compiling the library
* when the lvgl-related configs are changed.
*/
void * user_data;
bool inited;
bool deinit_in_progress; /**< Can be used e.g. in the LV_EVENT_DELETE to deinit the drivers too */
lv_ll_t disp_ll;
lv_display_t * disp_refresh;
lv_display_t * disp_default;
lv_ll_t style_trans_ll;
bool style_refresh;
uint32_t style_custom_table_size;
uint32_t style_last_custom_prop_id;
uint8_t * style_custom_prop_flag_lookup_table;
lv_ll_t group_ll;
lv_group_t * group_default;
lv_ll_t indev_ll;
lv_indev_t * indev_active;
lv_obj_t * indev_obj_active;
uint32_t layout_count;
lv_layout_dsc_t * layout_list;
bool layout_update_mutex;
uint32_t memory_zero;
uint32_t math_rand_seed;
lv_event_t * event_header;
uint32_t event_last_register_id;
lv_timer_state_t timer_state;
lv_anim_state_t anim_state;
lv_tick_state_t tick_state;
lv_draw_buf_handlers_t draw_buf_handlers;
lv_draw_buf_handlers_t font_draw_buf_handlers;
lv_draw_buf_handlers_t image_cache_draw_buf_handlers; /**< Ensure that all assigned draw buffers
* can be managed by image cache. */
lv_ll_t img_decoder_ll;
#if LV_USE_OS != LV_OS_NONE
lv_mutex_t img_decoder_info_lock;
lv_mutex_t img_decoder_open_lock;
#endif
lv_cache_t * img_cache;
lv_cache_t * img_header_cache;
lv_draw_global_info_t draw_info;
lv_ll_t draw_sw_blend_handler_ll;
#if defined(LV_DRAW_SW_SHADOW_CACHE_SIZE) && LV_DRAW_SW_SHADOW_CACHE_SIZE > 0
lv_draw_sw_shadow_cache_t sw_shadow_cache;
#endif
#if LV_DRAW_SW_COMPLEX
lv_draw_sw_mask_radius_circle_dsc_arr_t sw_circle_cache;
#endif
#if LV_USE_LOG
lv_log_print_g_cb_t custom_log_print_cb;
#endif
#if LV_USE_LOG && LV_LOG_USE_TIMESTAMP
uint32_t log_last_log_time;
#endif
#if LV_USE_THEME_SIMPLE
void * theme_simple;
#endif
#if LV_USE_THEME_DEFAULT
void * theme_default;
#endif
#if LV_USE_THEME_MONO
void * theme_mono;
#endif
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
lv_tlsf_state_t tlsf_state;
#endif
lv_ll_t fsdrv_ll;
#if LV_USE_FS_STDIO != '\0'
lv_fs_drv_t stdio_fs_drv;
#endif
#if LV_USE_FS_POSIX
lv_fs_drv_t posix_fs_drv;
#endif
#if LV_USE_FS_FATFS
lv_fs_drv_t fatfs_fs_drv;
#endif
#if LV_USE_FS_WIN32 != '\0'
lv_fs_drv_t win32_fs_drv;
#endif
#if LV_USE_FS_UEFI
lv_fs_drv_t uefi_fs_drv;
#endif
#if LV_USE_FS_LITTLEFS
lv_fs_drv_t littlefs_fs_drv;
#endif
#if LV_USE_FS_ARDUINO_ESP_LITTLEFS
lv_fs_drv_t arduino_esp_littlefs_fs_drv;
#endif
#if LV_USE_FS_ARDUINO_SD
lv_fs_drv_t arduino_sd_fs_drv;
#endif
#if LV_USE_FS_FROGFS
lv_fs_drv_t frogfs_fs_drv;
#endif
#if LV_USE_FREETYPE
struct _lv_freetype_context_t * ft_context;
#endif
#if LV_USE_FONT_COMPRESSED
lv_font_fmt_rle_t font_fmt_rle;
#endif
#if LV_USE_SPAN != 0
struct _snippet_stack * span_snippet_stack;
#endif
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
struct _lv_profiler_builtin_ctx_t * profiler_context;
#endif
#if LV_USE_FILE_EXPLORER
lv_style_t file_explorer_quick_access_style;
size_t file_explorer_count;
#endif
#if LV_USE_MEM_MONITOR
lv_sysmon_backend_data_t sysmon_mem;
#endif
#if LV_USE_IME_PINYIN != 0
size_t ime_cand_len;
#endif
#if LV_USE_OBJ_ID_BUILTIN
void * objid_array;
uint32_t objid_count;
#endif
#if LV_USE_TEST
lv_test_state_t test_state;
#endif
#if LV_USE_TRANSLATION
lv_ll_t translation_packs_ll;
const char * translation_selected_lang;
#endif
#if LV_USE_NUTTX
struct _lv_nuttx_ctx_t * nuttx_ctx;
#endif
#if LV_USE_OS != LV_OS_NONE
lv_mutex_t lv_general_mutex;
#endif
#if defined(__linux__)
lv_linux_proc_stat_t linux_last_proc_stat;
#if LV_SYSMON_PROC_IDLE_AVAILABLE
uint64_t linux_last_self_proc_time_ticks;
lv_linux_proc_stat_t linux_last_system_total_ticks_stat;
#endif
#endif
#if LV_USE_OS == LV_OS_FREERTOS
uint32_t freertos_idle_time_sum;
uint32_t freertos_non_idle_time_sum;
uint32_t freertos_task_switch_timestamp;
bool freertos_idle_task_running;
#endif
#if LV_USE_EVDEV
lv_evdev_discovery_t * evdev_discovery;
#endif
#if LV_USE_DRAW_EVE
lv_draw_eve_unit_t * draw_eve_unit;
#endif
} lv_global_t;
/**********************
* MACROS
**********************/
#if LV_ENABLE_GLOBAL_CUSTOM
#include LV_GLOBAL_CUSTOM_INCLUDE
#ifndef LV_GLOBAL_CUSTOM
#define LV_GLOBAL_CUSTOM() lv_global_default()
#endif
#define LV_GLOBAL_DEFAULT() LV_GLOBAL_CUSTOM()
#else
LV_ATTRIBUTE_EXTERN_DATA extern lv_global_t lv_global;
#define LV_GLOBAL_DEFAULT() (&lv_global)
#endif
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_ENABLE_GLOBAL_CUSTOM
/**
* Get the default global object for current thread
* @return pointer to the default global object
*/
lv_global_t * lv_global_default(void);
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GLOBAL_H*/

View File

@@ -0,0 +1,566 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_group_private.h"
#include "../core/lv_obj_private.h"
#include "../core/lv_global.h"
#include "../indev/lv_indev.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
#define default_group LV_GLOBAL_DEFAULT()->group_default
#define group_ll_p &(LV_GLOBAL_DEFAULT()->group_ll)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *));
static void lv_group_refocus(lv_group_t * g);
static lv_indev_t * get_indev(const lv_group_t * g);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_group_init(void)
{
lv_ll_init(group_ll_p, sizeof(lv_group_t));
}
void lv_group_deinit(void)
{
lv_ll_clear(group_ll_p);
}
lv_group_t * lv_group_create(void)
{
lv_group_t * group = lv_ll_ins_head(group_ll_p);
LV_ASSERT_MALLOC(group);
if(group == NULL) return NULL;
lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
group->obj_focus = NULL;
group->frozen = 0;
group->focus_cb = NULL;
group->edge_cb = NULL;
group->editing = 0;
group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
group->wrap = 1;
group->user_data = NULL;
#if LV_USE_EXT_DATA
group->ext_data.free_cb = NULL;
group->ext_data.data = NULL;
#endif
return group;
}
void lv_group_delete(lv_group_t * group)
{
/*Defocus the currently focused object*/
LV_ASSERT_NULL(group);
if(group->obj_focus != NULL) {
lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
}
/*Remove the group from any indev devices */
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(lv_indev_get_group(indev) == group) {
lv_indev_set_group(indev, NULL);
}
indev = lv_indev_get_next(indev);
}
/*If the group is the default group, set the default group as NULL*/
if(group == lv_group_get_default()) lv_group_set_default(NULL);
lv_ll_clear(&(group->obj_ll));
lv_ll_remove(group_ll_p, group);
#if LV_USE_EXT_DATA
if(group->ext_data.free_cb) {
group->ext_data.free_cb(group->ext_data.data);
group->ext_data.data = NULL;
}
#endif
lv_free(group);
}
void lv_group_set_default(lv_group_t * group)
{
default_group = group;
}
lv_group_t * lv_group_get_default(void)
{
return default_group;
}
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
if(group == NULL) return;
LV_LOG_TRACE("begin");
/*Be sure the object is removed from its current group*/
lv_group_remove_obj(obj);
if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
obj->spec_attr->group_p = group;
lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
LV_ASSERT_MALLOC(next);
if(next == NULL) return;
*next = obj;
/*If the head and the tail is equal then there is only one object in the linked list.
*In this case automatically activate it*/
if(lv_ll_get_head(&group->obj_ll) == next) {
lv_group_refocus(group);
}
LV_LOG_TRACE("finished");
}
void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2)
{
lv_group_t * g1 = lv_obj_get_group(obj1);
lv_group_t * g2 = lv_obj_get_group(obj2);
if(g1 != g2) return;
if(g1 == NULL) return;
/*Do not add the object twice*/
lv_obj_t ** obj_i;
LV_LL_READ(&g1->obj_ll, obj_i) {
if((*obj_i) == obj1)(*obj_i) = obj2;
else if((*obj_i) == obj2)(*obj_i) = obj1;
}
lv_obj_t * focused = lv_group_get_focused(g1);
if(focused == obj1) lv_group_focus_obj(obj2);
else if(focused == obj2) lv_group_focus_obj(obj1);
}
void lv_group_remove_obj(lv_obj_t * obj)
{
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
LV_LOG_TRACE("begin");
/*Focus on the next object*/
if(g->obj_focus && *g->obj_focus == obj) {
if(g->frozen) g->frozen = 0;
/*If this is the only object in the group then focus to nothing.*/
if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
}
/*If there more objects in the group then focus to the next/prev object*/
else {
lv_group_refocus(g);
}
}
/*If the focuses object is still the same then it was the only object in the group but it will
*be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
*zero objects*/
if(g->obj_focus && *g->obj_focus == obj) {
g->obj_focus = NULL;
}
/*Search the object and remove it from its group*/
lv_obj_t ** i;
LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
lv_ll_remove(&g->obj_ll, i);
lv_free(i);
if(obj->spec_attr) obj->spec_attr->group_p = NULL;
break;
}
}
LV_LOG_TRACE("finished");
}
void lv_group_remove_all_objs(lv_group_t * group)
{
LV_ASSERT_NULL(group);
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
group->obj_focus = NULL;
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
}
lv_ll_clear(&(group->obj_ll));
}
void lv_group_focus_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
if(g->frozen != 0) return;
/*On defocus edit mode must be leaved*/
lv_group_set_editing(g, false);
lv_obj_t ** i;
LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
if(g->obj_focus != NULL && obj != *g->obj_focus) { /*Do not defocus if the same object needs to be focused again*/
lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
if(res != LV_RESULT_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
if(g->obj_focus != NULL) {
if(g->focus_cb) g->focus_cb(g);
lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
if(res != LV_RESULT_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
break;
}
}
}
void lv_group_focus_next(lv_group_t * group)
{
LV_ASSERT_NULL(group);
bool focus_changed = focus_next_core(group, lv_ll_get_head, lv_ll_get_next);
if(group->edge_cb) {
if(!focus_changed)
group->edge_cb(group, true);
}
}
void lv_group_focus_prev(lv_group_t * group)
{
LV_ASSERT_NULL(group);
bool focus_changed = focus_next_core(group, lv_ll_get_tail, lv_ll_get_prev);
if(group->edge_cb) {
if(!focus_changed)
group->edge_cb(group, false);
}
}
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
LV_ASSERT_NULL(group);
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
LV_ASSERT_NULL(group);
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RESULT_OK;
if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RESULT_OK;
return lv_obj_send_event(act, LV_EVENT_KEY, &c);
}
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
if(group == NULL) return;
group->focus_cb = focus_cb;
}
void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb)
{
LV_ASSERT_NULL(group);
group->edge_cb = edge_cb;
}
void lv_group_set_editing(lv_group_t * group, bool edit)
{
LV_ASSERT_NULL(group);
uint8_t en_val = edit ? 1 : 0;
if(en_val == group->editing) return; /*Do not set the same mode again*/
group->editing = en_val;
lv_obj_t * focused = lv_group_get_focused(group);
if(focused) {
lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RESULT_OK) return;
lv_obj_invalidate(focused);
}
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
{
LV_ASSERT_NULL(group);
group->refocus_policy = policy & 0x01;
}
void lv_group_set_wrap(lv_group_t * group, bool en)
{
LV_ASSERT_NULL(group);
group->wrap = en ? 1 : 0;
}
lv_obj_t * lv_group_get_focused(const lv_group_t * group)
{
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
{
if(!group) return NULL;
return group->focus_cb;
}
lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group)
{
if(!group) return NULL;
return group->edge_cb;
}
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing;
}
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap;
}
uint32_t lv_group_get_obj_count(lv_group_t * group)
{
LV_ASSERT_NULL(group);
return lv_ll_get_len(&group->obj_ll);
}
lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index)
{
uint32_t len = 0;
lv_obj_t ** obj;
LV_LL_READ(&group->obj_ll, obj) {
if(len == index) {
return *obj;
}
len++;
}
return NULL;
}
uint32_t lv_group_get_count(void)
{
return lv_ll_get_len(group_ll_p);
}
lv_group_t * lv_group_by_index(uint32_t index)
{
uint32_t len = 0;
lv_group_t * group;
LV_LL_READ_BACK(group_ll_p, group) {
if(len == index) {
return group;
}
len++;
}
return NULL;
}
#if LV_USE_EXT_DATA
void lv_group_set_external_data(lv_group_t * group, void * data, void (* free_cb)(void * data))
{
if(!group) {
LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL group");
return;
}
group->ext_data.data = data;
group->ext_data.free_cb = free_cb;
}
#endif
void lv_group_set_user_data(lv_group_t * group, void * user_data)
{
if(group == NULL) return;
group->user_data = user_data;
}
void * lv_group_get_user_data(const lv_group_t * group)
{
if(group == NULL) return NULL;
return group->user_data;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_group_refocus(lv_group_t * g)
{
/*Refocus must temporarily allow wrapping to work correctly*/
uint8_t temp_wrap = g->wrap;
g->wrap = 1;
if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
lv_group_focus_next(g);
else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
lv_group_focus_prev(g);
/*Restore wrap property*/
g->wrap = temp_wrap;
}
static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *))
{
bool focus_changed = false;
if(group->frozen) return focus_changed;
lv_obj_t ** obj_next = group->obj_focus;
lv_obj_t ** obj_sentinel = NULL;
bool can_move = true;
bool can_begin = true;
for(;;) {
if(obj_next == NULL) {
if(group->wrap || obj_sentinel == NULL) {
if(!can_begin) return focus_changed;
obj_next = begin(&group->obj_ll);
can_move = false;
can_begin = false;
}
else {
/*Currently focused object is the last/first in the group, keep it that way*/
return focus_changed;
}
}
if(obj_sentinel == NULL) {
obj_sentinel = obj_next;
if(obj_sentinel == NULL) return focus_changed; /*Group is empty*/
}
if(can_move) {
obj_next = move(&group->obj_ll, obj_next);
/*Give up if we walked the entire list and haven't found another visible object*/
if(obj_next == obj_sentinel) return focus_changed;
}
can_move = true;
if(obj_next == NULL) continue;
if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
/*Hidden objects don't receive focus.
*If any parent is hidden, the object is also hidden)*/
lv_obj_t * parent = *obj_next;
while(parent) {
if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
parent = lv_obj_get_parent(parent);
}
if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
/*If we got her a good candidate is found*/
break;
}
if(obj_next == group->obj_focus) return focus_changed; /*There's only one visible object and it's already focused*/
if(group->obj_focus) {
lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
if(res != LV_RESULT_OK) return focus_changed;
lv_obj_invalidate(*group->obj_focus);
}
group->obj_focus = obj_next;
lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RESULT_OK) return focus_changed;
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
focus_changed = true;
return focus_changed;
}
/**
* Find an indev preferably with POINTER type (because it's the most generic) that uses the given group.
* In other words, find an indev, that is related to the given group.
* In the worst case simply return the latest indev
* @param g a group the find in the indevs
* @return the suggested indev
*/
static lv_indev_t * get_indev(const lv_group_t * g)
{
lv_indev_t * indev_guess = NULL;
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
/*Prefer POINTER*/
if(indev_type == LV_INDEV_TYPE_POINTER) return indev;
if(lv_indev_get_group(indev) == g) {
indev_guess = indev;
}
indev = lv_indev_get_next(indev);
}
return indev_guess;
}

View File

@@ -0,0 +1,278 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../misc/lv_ll.h"
/*********************
* DEFINES
*********************/
/** Predefined keys to control which Widget has focus via lv_group_send(group, c) */
typedef enum {
LV_KEY_UP = 17, /*0x11*/
LV_KEY_DOWN = 18, /*0x12*/
LV_KEY_RIGHT = 19, /*0x13*/
LV_KEY_LEFT = 20, /*0x14*/
LV_KEY_ESC = 27, /*0x1B*/
LV_KEY_DEL = 127, /*0x7F*/
LV_KEY_BACKSPACE = 8, /*0x08*/
LV_KEY_ENTER = 10, /*0x0A, '\n'*/
LV_KEY_NEXT = 9, /*0x09, '\t'*/
LV_KEY_PREV = 11, /*0x0B, '*/
LV_KEY_HOME = 2, /*0x02, STX*/
LV_KEY_END = 3, /*0x03, ETX*/
} lv_key_t;
/**********************
* TYPEDEFS
**********************/
typedef void (*lv_group_focus_cb_t)(lv_group_t *);
typedef void (*lv_group_edge_cb_t)(lv_group_t *, bool);
typedef enum {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
} lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create new Widget group.
* @return pointer to the new Widget group
*/
lv_group_t * lv_group_create(void);
/**
* Delete group object.
* @param group pointer to a group
*/
void lv_group_delete(lv_group_t * group);
/**
* Set default group. New Widgets will be added to this group if it's enabled in
* their class with `add_to_def_group = true`.
* @param group pointer to a group (can be `NULL`)
*/
void lv_group_set_default(lv_group_t * group);
/**
* Get default group.
* @return pointer to the default group
*/
lv_group_t * lv_group_get_default(void);
/**
* Add an Widget to group.
* @param group pointer to a group
* @param obj pointer to a Widget to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj);
/**
* Swap 2 Widgets in group. Widgets must be in the same group.
* @param obj1 pointer to a Widget
* @param obj2 pointer to another Widget
*/
void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2);
/**
* Remove a Widget from its group.
* @param obj pointer to Widget to remove
*/
void lv_group_remove_obj(lv_obj_t * obj);
/**
* Remove all Widgets from a group.
* @param group pointer to a group
*/
void lv_group_remove_all_objs(lv_group_t * group);
/**
* Focus on a Widget (defocus the current).
* @param obj pointer to Widget to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj);
/**
* Focus on next Widget in a group (defocus the current).
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus on previous Widget in a group (defocus the current).
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not allow changing focus from current Widget.
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en);
/**
* Send a control character to Widget that has focus in a group.
* @param group pointer to a group
* @param c a character (use LV_KEY_.. to navigate)
* @return result of Widget with focus in group.
*/
lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c);
/**
* Set a function for a group which will be called when a new Widget has focus.
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
/**
* Set a function for a group which will be called when a focus edge is reached
* @param group pointer to a group
* @param edge_cb the call back function or NULL if unused
*/
void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb);
/**
* Set whether the next or previous Widget in a group gets focus when Widget that has
* focus is deleted.
* @param group pointer to a group
* @param policy new refocus policy enum
*/
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit);
/**
* Set whether moving focus to next/previous Widget will allow wrapping from
* first->last or last->first Widget.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
void lv_group_set_wrap(lv_group_t * group, bool en);
/**
* Get Widget that has focus, or NULL if there isn't one.
* @param group pointer to a group
* @return pointer to Widget with focus
*/
lv_obj_t * lv_group_get_focused(const lv_group_t * group);
/**
* Get focus callback function of a group.
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
/**
* Get edge callback function of a group.
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group);
/**
* Get current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group);
/**
* Get whether moving focus to next/previous Widget will allow wrapping from
* first->last or last->first Widget.
* @param group pointer to group
*/
bool lv_group_get_wrap(lv_group_t * group);
/**
* Get number of Widgets in group.
* @param group pointer to a group
* @return number of Widgets in the group
*/
uint32_t lv_group_get_obj_count(lv_group_t * group);
/**
* Get nth Widget within group.
* @param group pointer to a group
* @param index index of Widget within the group
* @return pointer to Widget
*/
lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index);
/**
* Get the number of groups.
* @return number of groups
*/
uint32_t lv_group_get_count(void);
/**
* Get a group by its index.
* @param index index of the group
* @return pointer to the group
*/
lv_group_t * lv_group_by_index(uint32_t index);
#if LV_USE_EXT_DATA
/**
* @brief Attaches external user data and destructor callback to a group
*
* Associates custom user data with an LVGL group and specifies a destructor function
* that will be automatically invoked when the group is deleted to properly clean up
* the associated resources.
*
* @param group Pointer to a group
* @param data User-defined data pointer to associate with a group
* @param free_cb Callback function for cleaning up ext_data when group is deleted.
* Receives ext_data as parameter. NULL means no cleanup required.
*/
void lv_group_set_external_data(lv_group_t * group, void * data, void (* free_cb)(void * data));
#endif
/**
* Set user data to the group
* @param group pointer to a group
* @param user_data pointer to user data
*/
void lv_group_set_user_data(lv_group_t * group, void * user_data);
/**
* Get a pointer to the user data of the group
* @param indev pointer to a group
* @return pointer to the user data or NULL if group is NULL
*/
void * lv_group_get_user_data(const lv_group_t * group);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_H*/

View File

@@ -0,0 +1,78 @@
/**
* @file lv_group_private.h
*
*/
#ifndef LV_GROUP_PRIVATE_H
#define LV_GROUP_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_ext_data.h"
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Groups can be used to logically hold objects so that they can be individually focused.
* They are NOT for laying out objects on a screen (try layouts for that).
*/
struct _lv_group_t {
#if LV_USE_EXT_DATA
lv_ext_data_t ext_data;
#endif
lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/
lv_obj_t ** obj_focus; /**< The object in focus*/
lv_group_focus_cb_t focus_cb; /**< A function to call when a new object is focused (optional)*/
lv_group_edge_cb_t edge_cb; /**< A function to call when an edge is reached, no more focus
targets are available in this direction (to allow edge feedback
like a sound or a scroll bounce) */
void * user_data;
uint8_t frozen : 1; /**< 1: can't focus to new object*/
uint8_t editing : 1; /**< 1: Edit mode, 0: Navigate mode*/
uint8_t refocus_policy : 1; /**< 1: Focus prev if focused on deletion. 0: Focus next if focused on
deletion.*/
uint8_t wrap : 1; /**< 1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end
of list.*/
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init the group module
* @remarks Internal function, do not call directly.
*/
void lv_group_init(void);
/**
* Deinit the group module
* @remarks Internal function, do not call directly.
*/
void lv_group_deinit(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_PRIVATE_H*/

1342
inc/lvgl/src/core/lv_obj.c Normal file

File diff suppressed because it is too large Load Diff

501
inc/lvgl/src/core/lv_obj.h Normal file
View File

@@ -0,0 +1,501 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../misc/lv_style.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_assert.h"
#include "lv_obj_tree.h"
#include "lv_obj_pos.h"
#include "lv_obj_scroll.h"
#include "lv_obj_style.h"
#include "lv_obj_draw.h"
#include "lv_obj_class.h"
#include "lv_obj_event.h"
#include "lv_obj_property.h"
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* On/Off features controlling the object's behavior.
* OR-ed values are possible
*
* Note: update obj flags corresponding properties below
* whenever add/remove flags or change bit definition of flags.
*/
typedef enum {
LV_OBJ_FLAG_HIDDEN = (1u << 0), /**< Make the object hidden. (Like it wasn't there at all)*/
LV_OBJ_FLAG_CLICKABLE = (1u << 1), /**< Make the object clickable by the input devices*/
LV_OBJ_FLAG_CLICK_FOCUSABLE = (1u << 2), /**< Add focused state to the object when clicked*/
LV_OBJ_FLAG_CHECKABLE = (1u << 3), /**< Toggle checked state when the object is clicked*/
LV_OBJ_FLAG_SCROLLABLE = (1u << 4), /**< Make the object scrollable*/
LV_OBJ_FLAG_SCROLL_ELASTIC = (1u << 5), /**< Allow scrolling inside but with slower speed*/
LV_OBJ_FLAG_SCROLL_MOMENTUM = (1u << 6), /**< Make the object scroll further when "thrown"*/
LV_OBJ_FLAG_SCROLL_ONE = (1u << 7), /**< Allow scrolling only one snappable children*/
LV_OBJ_FLAG_SCROLL_CHAIN_HOR = (1u << 8), /**< Allow propagating the horizontal scroll to a parent*/
LV_OBJ_FLAG_SCROLL_CHAIN_VER = (1u << 9), /**< Allow propagating the vertical scroll to a parent*/
LV_OBJ_FLAG_SCROLL_CHAIN = (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER),
LV_OBJ_FLAG_SCROLL_ON_FOCUS = (1u << 10), /**< Automatically scroll object to make it visible when focused*/
LV_OBJ_FLAG_SCROLL_WITH_ARROW = (1u << 11), /**< Allow scrolling the focused object with arrow keys*/
LV_OBJ_FLAG_SNAPPABLE = (1u << 12), /**< If scroll snap is enabled on the parent it can snap to this object*/
LV_OBJ_FLAG_PRESS_LOCK = (1u << 13), /**< Keep the object pressed even if the press slid from the object*/
LV_OBJ_FLAG_EVENT_BUBBLE = (1u << 14), /**< Propagate the events to the parent too*/
LV_OBJ_FLAG_GESTURE_BUBBLE = (1u << 15), /**< Propagate the gestures to the parent*/
LV_OBJ_FLAG_ADV_HITTEST = (1u << 16), /**< Allow performing more accurate hit (click) test. E.g. consider rounded corners.*/
LV_OBJ_FLAG_IGNORE_LAYOUT = (1u << 17), /**< Make the object not positioned by the layouts*/
LV_OBJ_FLAG_FLOATING = (1u << 18), /**< Do not scroll the object when the parent scrolls and ignore layout*/
LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS = (1u << 19), /**< Send `LV_EVENT_DRAW_TASK_ADDED` events*/
LV_OBJ_FLAG_OVERFLOW_VISIBLE = (1u << 20),/**< Do not clip the children to the parent's ext draw size*/
LV_OBJ_FLAG_EVENT_TRICKLE = (1u << 21), /**< Propagate the events to the children too*/
LV_OBJ_FLAG_STATE_TRICKLE = (1u << 22), /**< Propagate the states to the children too*/
LV_OBJ_FLAG_LAYOUT_1 = (1u << 23), /**< Custom flag, free to use by layouts*/
LV_OBJ_FLAG_LAYOUT_2 = (1u << 24), /**< Custom flag, free to use by layouts*/
#if LV_USE_FLEX
LV_OBJ_FLAG_FLEX_IN_NEW_TRACK = LV_OBJ_FLAG_LAYOUT_1, /**< Start a new flex track on this item*/
#endif
LV_OBJ_FLAG_WIDGET_1 = (1u << 25), /**< Custom flag, free to use by widget*/
LV_OBJ_FLAG_WIDGET_2 = (1u << 26), /**< Custom flag, free to use by widget*/
LV_OBJ_FLAG_USER_1 = (1u << 27), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_2 = (1u << 28), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_3 = (1u << 29), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_4 = (1u << 30), /**< Custom flag, free to use by user*/
} lv_obj_flag_t;
#if LV_USE_OBJ_PROPERTY
enum _lv_signed_prop_id_t {
/*OBJ flag properties */
LV_PROPERTY_ID(OBJ, FLAG_START, LV_PROPERTY_TYPE_INT, 0),
LV_PROPERTY_ID(OBJ, FLAG_HIDDEN, LV_PROPERTY_TYPE_INT, 0),
LV_PROPERTY_ID(OBJ, FLAG_CLICKABLE, LV_PROPERTY_TYPE_INT, 1),
LV_PROPERTY_ID(OBJ, FLAG_CLICK_FOCUSABLE, LV_PROPERTY_TYPE_INT, 2),
LV_PROPERTY_ID(OBJ, FLAG_CHECKABLE, LV_PROPERTY_TYPE_INT, 3),
LV_PROPERTY_ID(OBJ, FLAG_SCROLLABLE, LV_PROPERTY_TYPE_INT, 4),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ELASTIC, LV_PROPERTY_TYPE_INT, 5),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_MOMENTUM, LV_PROPERTY_TYPE_INT, 6),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ONE, LV_PROPERTY_TYPE_INT, 7),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_HOR, LV_PROPERTY_TYPE_INT, 8),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_VER, LV_PROPERTY_TYPE_INT, 9),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ON_FOCUS, LV_PROPERTY_TYPE_INT, 10),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_WITH_ARROW, LV_PROPERTY_TYPE_INT, 11),
LV_PROPERTY_ID(OBJ, FLAG_SNAPPABLE, LV_PROPERTY_TYPE_INT, 12),
LV_PROPERTY_ID(OBJ, FLAG_PRESS_LOCK, LV_PROPERTY_TYPE_INT, 13),
LV_PROPERTY_ID(OBJ, FLAG_EVENT_BUBBLE, LV_PROPERTY_TYPE_INT, 14),
LV_PROPERTY_ID(OBJ, FLAG_GESTURE_BUBBLE, LV_PROPERTY_TYPE_INT, 15),
LV_PROPERTY_ID(OBJ, FLAG_ADV_HITTEST, LV_PROPERTY_TYPE_INT, 16),
LV_PROPERTY_ID(OBJ, FLAG_IGNORE_LAYOUT, LV_PROPERTY_TYPE_INT, 17),
LV_PROPERTY_ID(OBJ, FLAG_FLOATING, LV_PROPERTY_TYPE_INT, 18),
LV_PROPERTY_ID(OBJ, FLAG_SEND_DRAW_TASK_EVENTS, LV_PROPERTY_TYPE_INT, 19),
LV_PROPERTY_ID(OBJ, FLAG_OVERFLOW_VISIBLE, LV_PROPERTY_TYPE_INT, 20),
LV_PROPERTY_ID(OBJ, FLAG_EVENT_TRICKLE, LV_PROPERTY_TYPE_INT, 21),
LV_PROPERTY_ID(OBJ, FLAG_STATE_TRICKLE, LV_PROPERTY_TYPE_INT, 22),
LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_1, LV_PROPERTY_TYPE_INT, 23),
LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_2, LV_PROPERTY_TYPE_INT, 24),
LV_PROPERTY_ID(OBJ, FLAG_FLEX_IN_NEW_TRACK, LV_PROPERTY_TYPE_INT, 23), /*Mapped to FLAG_LAYOUT_1*/
LV_PROPERTY_ID(OBJ, FLAG_WIDGET_1, LV_PROPERTY_TYPE_INT, 25),
LV_PROPERTY_ID(OBJ, FLAG_WIDGET_2, LV_PROPERTY_TYPE_INT, 26),
LV_PROPERTY_ID(OBJ, FLAG_USER_1, LV_PROPERTY_TYPE_INT, 27),
LV_PROPERTY_ID(OBJ, FLAG_USER_2, LV_PROPERTY_TYPE_INT, 28),
LV_PROPERTY_ID(OBJ, FLAG_USER_3, LV_PROPERTY_TYPE_INT, 29),
LV_PROPERTY_ID(OBJ, FLAG_USER_4, LV_PROPERTY_TYPE_INT, 30),
LV_PROPERTY_ID(OBJ, FLAG_END, LV_PROPERTY_TYPE_INT, 30),
LV_PROPERTY_ID(OBJ, STATE_START, LV_PROPERTY_TYPE_INT, 31),
LV_PROPERTY_ID(OBJ, STATE_ALT, LV_PROPERTY_TYPE_INT, 31),
/*1 reserved*/
LV_PROPERTY_ID(OBJ, STATE_CHECKED, LV_PROPERTY_TYPE_INT, 33),
LV_PROPERTY_ID(OBJ, STATE_FOCUSED, LV_PROPERTY_TYPE_INT, 34),
LV_PROPERTY_ID(OBJ, STATE_FOCUS_KEY, LV_PROPERTY_TYPE_INT, 35),
LV_PROPERTY_ID(OBJ, STATE_EDITED, LV_PROPERTY_TYPE_INT, 36),
LV_PROPERTY_ID(OBJ, STATE_HOVERED, LV_PROPERTY_TYPE_INT, 37),
LV_PROPERTY_ID(OBJ, STATE_PRESSED, LV_PROPERTY_TYPE_INT, 38),
LV_PROPERTY_ID(OBJ, STATE_SCROLLED, LV_PROPERTY_TYPE_INT, 39),
LV_PROPERTY_ID(OBJ, STATE_DISABLED, LV_PROPERTY_TYPE_INT, 40),
/*2 reserved*/
LV_PROPERTY_ID(OBJ, STATE_USER_1, LV_PROPERTY_TYPE_INT, 43),
LV_PROPERTY_ID(OBJ, STATE_USER_2, LV_PROPERTY_TYPE_INT, 44),
LV_PROPERTY_ID(OBJ, STATE_USER_3, LV_PROPERTY_TYPE_INT, 45),
LV_PROPERTY_ID(OBJ, STATE_USER_4, LV_PROPERTY_TYPE_INT, 46),
LV_PROPERTY_ID(OBJ, STATE_ANY, LV_PROPERTY_TYPE_INT, 47),
LV_PROPERTY_ID(OBJ, STATE_END, LV_PROPERTY_TYPE_INT, 47),
/*OBJ normal properties*/
LV_PROPERTY_ID(OBJ, PARENT, LV_PROPERTY_TYPE_OBJ, 48),
LV_PROPERTY_ID(OBJ, X, LV_PROPERTY_TYPE_INT, 49),
LV_PROPERTY_ID(OBJ, Y, LV_PROPERTY_TYPE_INT, 50),
LV_PROPERTY_ID(OBJ, W, LV_PROPERTY_TYPE_INT, 51),
LV_PROPERTY_ID(OBJ, H, LV_PROPERTY_TYPE_INT, 52),
LV_PROPERTY_ID(OBJ, CONTENT_WIDTH, LV_PROPERTY_TYPE_INT, 53),
LV_PROPERTY_ID(OBJ, CONTENT_HEIGHT, LV_PROPERTY_TYPE_INT, 54),
LV_PROPERTY_ID(OBJ, LAYOUT, LV_PROPERTY_TYPE_INT, 55),
LV_PROPERTY_ID(OBJ, ALIGN, LV_PROPERTY_TYPE_INT, 56),
LV_PROPERTY_ID(OBJ, SCROLLBAR_MODE, LV_PROPERTY_TYPE_INT, 57),
LV_PROPERTY_ID(OBJ, SCROLL_DIR, LV_PROPERTY_TYPE_INT, 58),
LV_PROPERTY_ID(OBJ, SCROLL_SNAP_X, LV_PROPERTY_TYPE_INT, 59),
LV_PROPERTY_ID(OBJ, SCROLL_SNAP_Y, LV_PROPERTY_TYPE_INT, 60),
LV_PROPERTY_ID(OBJ, SCROLL_X, LV_PROPERTY_TYPE_INT, 61),
LV_PROPERTY_ID(OBJ, SCROLL_Y, LV_PROPERTY_TYPE_INT, 62),
LV_PROPERTY_ID(OBJ, SCROLL_TOP, LV_PROPERTY_TYPE_INT, 63),
LV_PROPERTY_ID(OBJ, SCROLL_BOTTOM, LV_PROPERTY_TYPE_INT, 64),
LV_PROPERTY_ID(OBJ, SCROLL_LEFT, LV_PROPERTY_TYPE_INT, 65),
LV_PROPERTY_ID(OBJ, SCROLL_RIGHT, LV_PROPERTY_TYPE_INT, 66),
LV_PROPERTY_ID(OBJ, SCROLL_END, LV_PROPERTY_TYPE_POINT, 67),
LV_PROPERTY_ID(OBJ, EXT_DRAW_SIZE, LV_PROPERTY_TYPE_INT, 68),
LV_PROPERTY_ID(OBJ, EVENT_COUNT, LV_PROPERTY_TYPE_INT, 69),
LV_PROPERTY_ID(OBJ, SCREEN, LV_PROPERTY_TYPE_OBJ, 70),
LV_PROPERTY_ID(OBJ, DISPLAY, LV_PROPERTY_TYPE_POINTER, 71),
LV_PROPERTY_ID(OBJ, CHILD_COUNT, LV_PROPERTY_TYPE_INT, 72),
LV_PROPERTY_ID(OBJ, INDEX, LV_PROPERTY_TYPE_INT, 73),
LV_PROPERTY_OBJ_END,
};
#endif
/**
* Make the base object's class publicly available.
*/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_obj_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a base object (a rectangle)
* @param parent pointer to a parent object. If NULL then a screen will be created.
* @return pointer to the new object
*/
lv_obj_t * lv_obj_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set one or more flags
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Remove one or more flags
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to clear.
*/
void lv_obj_remove_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Set add or remove one or more flags.
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to update.
* @param v true: add the flags; false: remove the flags
*/
void lv_obj_set_flag(lv_obj_t * obj, lv_obj_flag_t f, bool v);
/**
* Add one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state);
/**
* Remove one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_remove_state(lv_obj_t * obj, lv_state_t state);
/**
* Add or remove one or more states to the object. The other state bits will remain unchanged.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
* @param v true: add the states; false: remove the states
*/
void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v);
/**
* Set the user_data field of the object
* @param obj pointer to an object
* @param user_data pointer to the new user_data.
*/
void lv_obj_set_user_data(lv_obj_t * obj, void * user_data);
/** Allow only one RADIO_BUTTON sibling to be checked
* @param obj pointer to a widget
* @param en enable or disable radio button behavior
*/
void lv_obj_set_radio_button(lv_obj_t * obj, bool en);
/*=======================
* Getter functions
*======================*/
/**
* Check if a given flag or all the given flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: all flags are set; false: not all flags are set
*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Check if a given flag or any of the flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: at least one flag is set; false: none of the flags are set
*/
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Get the state of an object
* @param obj pointer to an object
* @return the state (OR-ed values from `lv_state_t`)
*/
lv_state_t lv_obj_get_state(const lv_obj_t * obj);
/**
* Check if the object is in a given state or not.
* @param obj pointer to an object
* @param state a state or combination of states to check
* @return true: `obj` is in `state`; false: `obj` is not in `state`
*/
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state);
/** Get whether the object is a radio button
* @param obj pointer to a widget
* @return true if radio button behavior is enabled
*/
bool lv_obj_is_radio_button(const lv_obj_t * obj);
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
lv_group_t * lv_obj_get_group(const lv_obj_t * obj);
/**
* Get the user_data field of the object
* @param obj pointer to an object
* @return the pointer to the user_data of the object
*/
void * lv_obj_get_user_data(lv_obj_t * obj);
/*=======================
* Other functions
*======================*/
/**
* Allocate special data for an object if not allocated yet.
* @param obj pointer to an object
*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj);
/**
* Check the type of obj.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `class_p` is the `obj` class.
*/
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Check if any object has a given class (type).
* It checks the ancestor classes too.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `obj` has the given class
*/
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Get the class (type) of the object
* @param obj pointer to an object
* @return the class (type) of the object
*/
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj);
/**
* Check if any object is still "alive".
* @param obj pointer to an object
* @return true: valid
*/
bool lv_obj_is_valid(const lv_obj_t * obj);
/**
* Utility to set an object reference to NULL when it gets deleted.
* The reference should be in a location that will not become invalid
* during the object's lifetime, i.e. static or allocated.
* @param obj_ptr a pointer to a pointer to an object
*/
void lv_obj_null_on_delete(lv_obj_t ** obj_ptr);
/**
* Add an event handler to a widget that will load a screen on a trigger.
* @param obj pointer to widget which should load the screen
* @param trigger an event code, e.g. `LV_EVENT_CLICKED`
* @param screen the screen to load (must be a valid widget)
* @param anim_type element of `lv_screen_load_anim_t` the screen load animation
* @param duration duration of the animation in milliseconds
* @param delay delay before the screen load in milliseconds
*/
void lv_obj_add_screen_load_event(lv_obj_t * obj, lv_event_code_t trigger, lv_obj_t * screen,
lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay);
/**
* Add an event handler to a widget that will create a screen on a trigger.
* The created screen will be deleted when it's unloaded
* @param obj pointer to widget which should load the screen
* @param trigger an event code, e.g. `LV_EVENT_CLICKED`
* @param screen_create_cb a callback to create the screen, e.g. `lv_obj_t * myscreen_create(void)`
* @param anim_type element of `lv_screen_load_anim_t` the screen load animation
* @param duration duration of the animation in milliseconds
* @param delay delay before the screen load in milliseconds
*/
void lv_obj_add_screen_create_event(lv_obj_t * obj, lv_event_code_t trigger, lv_screen_create_cb_t screen_create_cb,
lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay);
/**
* Play a timeline animation on a trigger
* @param obj pointer to widget which should trigger playing the animation
* @param trigger an event code, e.g. `LV_EVENT_CLICKED`
* @param at pointer to an animation timeline
* @param delay wait time before starting the animation
* @param reverse true: play in reverse
*/
void lv_obj_add_play_timeline_event(lv_obj_t * obj, lv_event_code_t trigger, lv_anim_timeline_t * at, uint32_t delay,
bool reverse);
#if LV_USE_OBJ_ID
/**
* Set an id for an object.
* @param obj pointer to an object
* @param id the id of the object
*/
void lv_obj_set_id(lv_obj_t * obj, void * id);
/**
* Get the id of an object.
* @param obj pointer to an object
* @return the id of the object
*/
void * lv_obj_get_id(const lv_obj_t * obj);
/**
* DEPRECATED IDs are used only to print the widget trees.
* To find a widget use `lv_obj_find_by_name`
*
* Get the child object by its id.
* It will check children and grandchildren recursively.
* Function `lv_obj_id_compare` is used to matched obj id with given id.
*
* @param obj pointer to an object
* @param id the id of the child object
* @return pointer to the child object or NULL if not found
*/
lv_obj_t * lv_obj_find_by_id(const lv_obj_t * obj, const void * id);
/**
* Assign id to object if not previously assigned.
* This function gets called automatically when LV_OBJ_ID_AUTO_ASSIGN is enabled.
*
* Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method to generate object ID.
* Otherwise, these functions including `lv_obj_[set|assign|free|stringify]_id` and
* `lv_obj_id_compare`should be implemented externally.
*
* @param class_p the class this obj belongs to. Note obj->class_p is the class currently being constructed.
* @param obj pointer to an object
*/
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**
* Free resources allocated by `lv_obj_assign_id` or `lv_obj_set_id`.
* This function is also called automatically when object is deleted.
* @param obj pointer to an object
*/
void lv_obj_free_id(lv_obj_t * obj);
/**
* Compare two obj id, return 0 if they are equal.
*
* Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method for compare.
* Otherwise, it must be implemented externally.
*
* @param id1: the first id
* @param id2: the second id
* @return 0 if they are equal, non-zero otherwise.
*/
int lv_obj_id_compare(const void * id1, const void * id2);
/**
* Format an object's id into a string.
* @param obj pointer to an object
* @param buf buffer to write the string into
* @param len length of the buffer
*/
const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len);
#if LV_USE_OBJ_ID_BUILTIN
/**
* Free resources used by builtin ID generator.
*/
void lv_objid_builtin_destroy(void);
#endif
#endif /*LV_USE_OBJ_ID*/
/**********************
* MACROS
**********************/
#if LV_USE_ASSERT_OBJ
# define LV_ASSERT_OBJ(obj_p, obj_class) \
do { \
LV_ASSERT_MSG(obj_p != NULL, "The object is NULL"); \
LV_ASSERT_MSG(lv_obj_has_class(obj_p, obj_class) == true, "Incompatible object type."); \
LV_ASSERT_MSG(lv_obj_is_valid(obj_p) == true, "The object is invalid, deleted or corrupted?"); \
} while(0)
# else
# define LV_ASSERT_OBJ(obj_p, obj_class) LV_ASSERT_NULL(obj_p)
#endif
#if LV_USE_LOG && LV_LOG_TRACE_OBJ_CREATE
# define LV_TRACE_OBJ_CREATE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
# define LV_TRACE_OBJ_CREATE(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_H*/

View File

@@ -0,0 +1,233 @@
/**
* @file lv_obj_class.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "../themes/lv_theme.h"
#include "../display/lv_display.h"
#include "../display/lv_display_private.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj);
static uint32_t get_instance_size(const lv_obj_class_t * class_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
{
LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent);
uint32_t s = get_instance_size(class_p);
lv_obj_t * obj = lv_malloc_zeroed(s);
if(obj == NULL) return NULL;
obj->class_p = class_p;
obj->parent = parent;
/*Create a screen*/
if(parent == NULL) {
LV_TRACE_OBJ_CREATE("creating a screen");
lv_display_t * disp = lv_display_get_default();
if(!disp) {
LV_LOG_WARN("No display created yet. No place to assign the new screen");
lv_free(obj);
return NULL;
}
if(disp->screens == NULL) {
disp->screen_cnt = 0;
}
lv_obj_t ** screens = lv_realloc(disp->screens, sizeof(lv_obj_t *) * (disp->screen_cnt + 1));
LV_ASSERT_MALLOC(screens);
if(screens == NULL) {
lv_free(obj);
return NULL;
}
disp->screen_cnt++;
disp->screens = screens;
disp->screens[disp->screen_cnt - 1] = obj;
/*Set coordinates to full screen size*/
obj->coords.x1 = 0;
obj->coords.y1 = 0;
obj->coords.x2 = lv_display_get_horizontal_resolution(NULL) - 1;
obj->coords.y2 = lv_display_get_vertical_resolution(NULL) - 1;
}
/*Create a normal object*/
else {
LV_TRACE_OBJ_CREATE("creating normal object");
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent->spec_attr == NULL) {
lv_obj_allocate_spec_attr(parent);
}
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
}
return obj;
}
void lv_obj_class_init_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_obj_mark_layout_as_dirty(obj);
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj);
lv_obj_construct(obj->class_p, obj);
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
lv_obj_refresh_self_size(obj);
lv_group_t * def_group = lv_group_get_default();
if(def_group && lv_obj_is_group_def(obj)) {
lv_group_add_obj(def_group, obj);
}
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
/*Call the ancestor's event handler to the parent to notify it about the new child.
*Also triggers layout update*/
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj);
/*Invalidate the area if not screen created*/
lv_obj_invalidate(obj);
}
}
void lv_obj_destruct(lv_obj_t * obj)
{
#if LV_USE_EXT_DATA
if(obj->ext_data.free_cb) {
obj->ext_data.free_cb(obj->ext_data.data);
obj->ext_data.data = NULL;
}
#endif
if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj);
if(obj->class_p->base_class) {
/*Don't let the descendant methods run during destructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Call the base class's destructor too*/
lv_obj_destruct(obj);
}
}
bool lv_obj_is_editable(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which editable is set*/
while(class_p && class_p->editable == LV_OBJ_CLASS_EDITABLE_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->editable == LV_OBJ_CLASS_EDITABLE_TRUE;
}
bool lv_obj_is_group_def(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which group_def is set*/
while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE;
}
#if LV_USE_EXT_DATA
void lv_obj_set_external_data(lv_obj_t * obj, void * data, void (* free_cb)(void * data))
{
if(!obj) {
LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL object");
return;
}
obj->ext_data.data = data;
obj->ext_data.free_cb = free_cb;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
if(LV_USE_OBJ_NAME) {
LV_ASSERT_NULL(class_p->name);
}
#if LV_USE_EXT_DATA
obj->ext_data.free_cb = NULL;
obj->ext_data.data = NULL;
#endif
if(obj->class_p->base_class) {
const lv_obj_class_t * original_class_p = obj->class_p;
/*Don't let the descendant methods run during constructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Construct the base first*/
lv_obj_construct(class_p, obj);
/*Restore the original class*/
obj->class_p = original_class_p;
}
if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(class_p, obj);
}
static uint32_t get_instance_size(const lv_obj_class_t * class_p)
{
/*Find a base in which instance size is set*/
const lv_obj_class_t * base = class_p;
while(base && base->instance_size == 0) base = base->base_class;
if(base == NULL) return 0; /*Never happens: set at least in `lv_obj` class*/
return base->instance_size;
}

View File

@@ -0,0 +1,89 @@
/**
* @file lv_obj_class.h
*
*/
#ifndef LV_OBJ_CLASS_H
#define LV_OBJ_CLASS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "lv_obj_property.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_OBJ_CLASS_EDITABLE_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_EDITABLE_TRUE,
LV_OBJ_CLASS_EDITABLE_FALSE,
} lv_obj_class_editable_t;
typedef enum {
LV_OBJ_CLASS_GROUP_DEF_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_GROUP_DEF_TRUE,
LV_OBJ_CLASS_GROUP_DEF_FALSE,
} lv_obj_class_group_def_t;
typedef enum {
LV_OBJ_CLASS_THEME_INHERITABLE_FALSE, /**< Do not inherit theme from base class. */
LV_OBJ_CLASS_THEME_INHERITABLE_TRUE,
} lv_obj_class_theme_inheritable_t;
typedef void (*lv_obj_class_event_cb_t)(lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an object form a class descriptor
* @param class_p pointer to a class
* @param parent pointer to an object where the new object should be created
* @return pointer to the created object
*/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent);
void lv_obj_class_init_obj(lv_obj_t * obj);
bool lv_obj_is_editable(lv_obj_t * obj);
bool lv_obj_is_group_def(lv_obj_t * obj);
#if LV_USE_EXT_DATA
/**
* @brief Associates an array of external data pointers with an LVGL object
*
* Associates custom user data with an LVGL object and specifies a destructor function
* that will be automatically invoked when the object is deleted to properly clean up
* the associated resources.
*
* @param obj Target LVGL object
* @param data User-defined data pointer to associate with a object
* @param free_cb Cleanup function called for each non-NULL data pointer during
* object deletion. Receives single data pointer as parameter.
* NULL means no automatic cleanup.
*/
void lv_obj_set_external_data(lv_obj_t * obj, void * data, void (* free_cb)(void * data));
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_H*/

View File

@@ -0,0 +1,78 @@
/**
* @file lv_obj_class_private.h
*
*/
#ifndef LV_OBJ_CLASS_PRIVATE_H
#define LV_OBJ_CLASS_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_class.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Describe the common methods of every object.
* Similar to a C++ class.
*/
struct _lv_obj_class_t {
const lv_obj_class_t * base_class;
/** class_p is the final class while obj->class_p is the class currently being [de]constructed. */
void (*constructor_cb)(const lv_obj_class_t * class_p, lv_obj_t * obj);
void (*destructor_cb)(const lv_obj_class_t * class_p, lv_obj_t * obj);
/** class_p is the class in which event is being processed. */
void (*event_cb)(const lv_obj_class_t * class_p, lv_event_t * e); /**< Widget type specific event function*/
#if LV_USE_OBJ_PROPERTY
uint32_t prop_index_start;
uint32_t prop_index_end;
const lv_property_ops_t * properties;
uint32_t properties_count;
#if LV_USE_OBJ_PROPERTY_NAME
/* An array of property ID and name */
const lv_property_name_t * property_names;
uint32_t names_count;
#endif
#endif
void * user_data;
const char * name;
int32_t width_def;
int32_t height_def;
uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
uint32_t instance_size : 16;
uint32_t theme_inheritable : 1; /**< Value from ::lv_obj_class_theme_inheritable_t*/
};
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_obj_destruct(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_PRIVATE_H*/

View File

@@ -0,0 +1,512 @@
/**
* @file lv_obj_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw_private.h"
#include "lv_obj_private.h"
#include "lv_obj_style.h"
#include "../display/lv_display.h"
#include "../indev/lv_indev.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static inline lv_opa_t get_layer_opa(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc);
static lv_color_t normal_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc,
lv_color_t color);
static lv_color32_t image_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,
const lv_draw_dsc_base_t * base_dsc, lv_color_t color, lv_opa_t opa);
static void drop_shadow_init(const lv_obj_t * obj, lv_part_t part, lv_draw_dsc_base_t * base_dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_rect_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(part != LV_PART_MAIN) {
if(opa <= LV_OPA_MIN) {
draw_dsc->bg_opa = LV_OPA_TRANSP;
draw_dsc->bg_image_opa = LV_OPA_TRANSP;
draw_dsc->border_opa = LV_OPA_TRANSP;
draw_dsc->outline_opa = LV_OPA_TRANSP;
draw_dsc->shadow_opa = LV_OPA_TRANSP;
LV_PROFILER_DRAW_END;
return;
}
}
draw_dsc->radius = lv_obj_get_style_radius(obj, part);
if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
if(draw_dsc->bg_opa > LV_OPA_MIN) {
lv_color_t bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
draw_dsc->bg_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, bg_color);
const lv_grad_dsc_t * grad = lv_obj_get_style_bg_grad(obj, part);
if(grad && grad->dir != LV_GRAD_DIR_NONE) {
lv_memcpy(&draw_dsc->bg_grad, grad, sizeof(*grad));
}
else {
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.stops[0].color = draw_dsc->bg_color;
lv_color_t bg_grad_color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
draw_dsc->bg_grad.stops[1].color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, bg_grad_color);
draw_dsc->bg_grad.stops[0].frac = lv_obj_get_style_bg_main_stop(obj, part);
draw_dsc->bg_grad.stops[1].frac = lv_obj_get_style_bg_grad_stop(obj, part);
draw_dsc->bg_grad.stops[0].opa = lv_obj_get_style_bg_main_opa(obj, part);
draw_dsc->bg_grad.stops[1].opa = lv_obj_get_style_bg_grad_opa(obj, part);
}
}
}
}
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
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);
lv_color_t border_color = lv_obj_get_style_border_color_filtered(obj, part);
draw_dsc->border_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, border_color);
}
}
}
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
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);
lv_color_t outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
draw_dsc->outline_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, outline_color);
}
}
}
if(draw_dsc->bg_image_opa != LV_OPA_TRANSP) {
draw_dsc->bg_image_src = lv_obj_get_style_bg_image_src(obj, part);
if(draw_dsc->bg_image_src) {
draw_dsc->bg_image_opa = lv_obj_get_style_bg_image_opa(obj, part);
if(draw_dsc->bg_image_opa > LV_OPA_MIN) {
if(lv_image_src_get_type(draw_dsc->bg_image_src) == LV_IMAGE_SRC_SYMBOL) {
draw_dsc->bg_image_symbol_font = lv_obj_get_style_text_font(obj, part);
lv_color_t text_color = lv_obj_get_style_text_color_filtered(obj, part);
draw_dsc->bg_image_recolor = normal_apply_layer_recolor(obj, part, &draw_dsc->base, text_color);
}
else {
lv_color_t bg_image_recolor = lv_obj_get_style_bg_image_recolor_filtered(obj, part);
lv_opa_t bg_image_recolor_opa = lv_obj_get_style_bg_image_recolor_opa(obj, part);
lv_color32_t result = image_apply_layer_recolor(obj, part, &draw_dsc->base, bg_image_recolor, bg_image_recolor_opa);
draw_dsc->bg_image_recolor_opa = result.alpha;
draw_dsc->bg_image_recolor = lv_color_make(result.red, result.green, result.blue);
draw_dsc->bg_image_tiled = lv_obj_get_style_bg_image_tiled(obj, part);
}
draw_dsc->bg_image_colorkey = lv_obj_get_style_image_colorkey(obj, part);
}
}
}
if(draw_dsc->shadow_opa) {
draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
if(draw_dsc->shadow_width) {
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_offset_x = lv_obj_get_style_shadow_offset_x(obj, part);
draw_dsc->shadow_offset_y = lv_obj_get_style_shadow_offset_y(obj, part);
draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
lv_color_t shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
draw_dsc->shadow_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, shadow_color);
}
}
}
}
if(opa < LV_OPA_MAX) {
draw_dsc->bg_opa = LV_OPA_MIX2(draw_dsc->bg_opa, opa);
draw_dsc->bg_image_opa = LV_OPA_MIX2(draw_dsc->bg_image_opa, opa);
draw_dsc->border_opa = LV_OPA_MIX2(draw_dsc->border_opa, opa);
draw_dsc->shadow_opa = LV_OPA_MIX2(draw_dsc->shadow_opa, opa);
draw_dsc->outline_opa = LV_OPA_MIX2(draw_dsc->outline_opa, opa);
}
drop_shadow_init(obj, part, &draw_dsc->base);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_label_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_color_t text_color = lv_obj_get_style_text_color_filtered(obj, part);
draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, text_color);
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);
draw_dsc->font = lv_obj_get_style_text_font(obj, part);
#if LV_USE_BIDI
draw_dsc->bidi_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
#endif
draw_dsc->align = lv_obj_get_style_text_align(obj, part);
drop_shadow_init(obj, part, &draw_dsc->base);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_image_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_image_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->opa = lv_obj_get_style_image_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
draw_dsc->rotation = 0;
draw_dsc->scale_x = LV_SCALE_NONE;
draw_dsc->scale_y = LV_SCALE_NONE;
draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
lv_color_t recolor = lv_obj_get_style_image_recolor_filtered(obj, part);
lv_opa_t recolor_opa = lv_obj_get_style_image_recolor_opa(obj, part);
lv_color32_t result = image_apply_layer_recolor(obj, part, &draw_dsc->base, recolor, recolor_opa);
draw_dsc->recolor_opa = result.alpha;
draw_dsc->recolor = lv_color_make(result.red, result.green, result.blue);
draw_dsc->colorkey = lv_obj_get_style_image_colorkey(obj, part);
if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
drop_shadow_init(obj, part, &draw_dsc->base);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
draw_dsc->width = lv_obj_get_style_line_width(obj, part);
if(draw_dsc->width == 0) {
LV_PROFILER_DRAW_END;
return;
}
lv_color_t line_color = lv_obj_get_style_line_color_filtered(obj, part);
draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, line_color);
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;
drop_shadow_init(obj, part, &draw_dsc->base);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
if(draw_dsc->width == 0) {
LV_PROFILER_DRAW_END;
return;
}
draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_color_t arc_color = lv_obj_get_style_arc_color_filtered(obj, part);
draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, arc_color);
draw_dsc->img_src = lv_obj_get_style_arc_image_src(obj, part);
draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
drop_shadow_init(obj, part, &draw_dsc->base);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_blur_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_blur_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->blur_radius = lv_obj_get_style_blur_radius(obj, part);
draw_dsc->quality = lv_obj_get_style_blur_quality(obj, part);
/*Radius might be set earlier as it's already known*/
if(draw_dsc->corner_radius == 0) {
draw_dsc->corner_radius = lv_obj_get_style_radius(obj, part);
}
LV_PROFILER_DRAW_END;
}
int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part)
{
LV_PROFILER_DRAW_BEGIN;
int32_t s = 0;
int32_t sh_width = lv_obj_get_style_shadow_width(obj, part);
if(sh_width) {
lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
if(sh_opa > LV_OPA_MIN) {
sh_width = sh_width / 2 + 1; /*The blur adds only half width*/
sh_width += lv_obj_get_style_shadow_spread(obj, part);
int32_t sh_ofs_x = lv_obj_get_style_shadow_offset_x(obj, part);
int32_t sh_ofs_y = lv_obj_get_style_shadow_offset_y(obj, part);
sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
s = LV_MAX(s, sh_width);
}
}
int32_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) {
int32_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
s = LV_MAX(s, outline_pad + outline_width);
}
}
int32_t drop_shadow_size = 0;
if(lv_obj_get_style_drop_shadow_opa(obj, part) > 0) {
drop_shadow_size += LV_MAX(LV_ABS(lv_obj_get_style_drop_shadow_offset_x(obj, part)),
LV_ABS(lv_obj_get_style_drop_shadow_offset_y(obj, part)));
drop_shadow_size += lv_obj_get_style_drop_shadow_radius(obj, part) + 1;
}
s += drop_shadow_size;
int32_t w = lv_obj_get_style_transform_width(obj, part);
int32_t h = lv_obj_get_style_transform_height(obj, part);
int32_t wh = LV_MAX(w, h);
if(wh > 0) s += wh;
LV_PROFILER_DRAW_END;
return s;
}
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
{
LV_PROFILER_DRAW_BEGIN;
LV_ASSERT_OBJ(obj, MY_CLASS);
int32_t s_old = lv_obj_get_ext_draw_size(obj);
int32_t s_new = 0;
lv_obj_send_event(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
/*Store the result if the special attrs already allocated*/
if(obj->spec_attr) {
obj->spec_attr->ext_draw_size = s_new;
}
/*Allocate spec. attrs. only if the result is not zero.
*Zero is the default value if the spec. attr. are not defined.*/
else if(s_new != 0) {
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->ext_draw_size = s_new;
}
if(s_new != s_old) lv_obj_invalidate(obj);
LV_PROFILER_DRAW_END;
}
int32_t lv_obj_get_ext_draw_size(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->ext_draw_size;
else return 0;
}
lv_layer_type_t lv_obj_get_layer_type(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_layer_type_t)obj->spec_attr->layer_type;
else return LV_LAYER_TYPE_NONE;
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline lv_opa_t get_layer_opa(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc)
{
if(base_dsc->layer) {
/* Accessing the layer opa directly is faster than using get style opa recursive */
if(part == LV_PART_MAIN) {
return base_dsc->layer->opa;
}
return LV_OPA_MIX2(base_dsc->layer->opa, lv_obj_get_style_opa(obj, part));
}
/* fallback to old recursive style opa */
return lv_obj_get_style_opa_recursive(obj, part);
}
static lv_color_t normal_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc,
lv_color_t color)
{
lv_color32_t recolor;
if(base_dsc->layer) {
recolor = base_dsc->layer->recolor;
if(part != LV_PART_MAIN) {
recolor = lv_obj_style_apply_recolor(obj, part, recolor);
}
}
else {
recolor = lv_obj_get_style_recolor_recursive(obj, part);
}
return lv_color_mix(lv_color_make(recolor.red, recolor.green, recolor.blue), color, recolor.alpha);
}
static lv_color32_t image_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,
const lv_draw_dsc_base_t * base_dsc, lv_color_t color, lv_opa_t opa)
{
lv_color32_t recolor;
if(base_dsc->layer) {
recolor = base_dsc->layer->recolor;
if(part != LV_PART_MAIN) {
recolor = lv_obj_style_apply_recolor(obj, part, recolor);
}
}
else {
recolor = lv_obj_get_style_recolor_recursive(obj, part);
}
if(opa > LV_OPA_TRANSP && recolor.alpha > LV_OPA_TRANSP) {
return lv_color_over32(recolor, lv_color_to_32(color, opa));
}
else if(recolor.alpha > LV_OPA_TRANSP) {
return recolor;
}
else {
return lv_color_to_32(color, opa);
}
}
static void drop_shadow_init(const lv_obj_t * obj, lv_part_t part, lv_draw_dsc_base_t * base_dsc)
{
base_dsc->drop_shadow_opa = lv_obj_get_style_drop_shadow_opa(obj, part);
if(base_dsc->drop_shadow_opa) {
base_dsc->drop_shadow_blur_radius = lv_obj_get_style_drop_shadow_radius(obj, part);
base_dsc->drop_shadow_ofs_x = lv_obj_get_style_drop_shadow_offset_x(obj, part);
base_dsc->drop_shadow_ofs_y = lv_obj_get_style_drop_shadow_offset_y(obj, part);
base_dsc->drop_shadow_color = lv_obj_get_style_drop_shadow_color(obj, part);
base_dsc->drop_shadow_color = normal_apply_layer_recolor(obj, part, base_dsc, base_dsc->drop_shadow_color);
base_dsc->drop_shadow_quality = lv_obj_get_style_drop_shadow_quality(obj, part);
}
}

View File

@@ -0,0 +1,142 @@
/**
* @file lv_obj_draw.h
*
*/
#ifndef LV_OBJ_DRAW_H
#define LV_OBJ_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../draw/lv_draw_rect.h"
#include "../draw/lv_draw_label.h"
#include "../draw/lv_draw_image.h"
#include "../draw/lv_draw_line.h"
#include "../draw/lv_draw_arc.h"
#include "../draw/lv_draw_triangle.h"
#include "../draw/lv_draw_blur.h"
#include "lv_obj_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** Store the type of layer required to render a widget.*/
typedef enum {
/**No layer is needed. */
LV_LAYER_TYPE_NONE,
/**Simple layer means that the layer can be rendered in chunks.
* For example with opa_layered = 140 it's possible to render only 10 lines
* from the layer. When it's ready go to the next 10 lines.
* It avoids large memory allocations for the layer buffer.
* The buffer size for a chunk can be set by `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` in lv_conf.h.*/
LV_LAYER_TYPE_SIMPLE,
/**The widget is transformed and cannot be rendered in chunks.
* It's because - due to the transformations - pixel outside of
* a given area will also contribute to the final image.
* In this case there is no limitation on the buffer size.
* LVGL will allocate as large buffer as needed to render the transformed area.*/
LV_LAYER_TYPE_TRANSFORM,
} lv_layer_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a rectangle draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized.
* Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`.
* @note Only the relevant fields will be set.
* E.g. if `border width == 0` the other border properties won't be evaluated.
*/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_rect_dsc_t * draw_dsc);
/**
* Initialize a label draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized.
* Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_label_dsc_t * draw_dsc);
/**
* Initialize an image draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_image_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_image_dsc_t * draw_dsc);
/**
* Initialize a line draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_t * draw_dsc);
/**
* Initialize an arc draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc);
/**
* Initialize a blur draw descriptor from an object's styles in its current state.
* draw_dsc->radius will only be calculated if it's 0 initially. Radius can be set before calling this function
* to avoid getting it twice.
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_blur_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_blur_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_blur_dsc_t * draw_dsc);
/**
* Get the required extra size (around the object's part) to draw shadow, outline, value etc.
* @param obj pointer to an object
* @param part part of the object
* @return the extra size required around the object
*/
int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part);
/**
* Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size.
* The result will be saved in `obj`.
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_H*/

View File

@@ -0,0 +1,48 @@
/**
* @file lv_obj_draw_private.h
*
*/
#ifndef LV_OBJ_DRAW_PRIVATE_H
#define LV_OBJ_DRAW_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the extended draw area of an object.
* @param obj pointer to an object
* @return the size extended draw area around the real coordinates
*/
int32_t lv_obj_get_ext_draw_size(const lv_obj_t * obj);
lv_layer_type_t lv_obj_get_layer_type(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_PRIVATE_H*/

View File

@@ -0,0 +1,491 @@
/**
* @file lv_obj_event.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_event_private.h"
#include "lv_obj_event_private.h"
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_private.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t event_send_core(lv_event_t * e);
static bool event_is_bubbled(lv_event_t * e);
static bool event_is_trickled(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_EVENT
#define LV_TRACE_EVENT(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define LV_TRACE_EVENT(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param)
{
if(obj == NULL) return LV_RESULT_OK;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_event_t e;
e.current_target = obj;
e.original_target = obj;
e.code = event_code;
e.user_data = NULL;
e.param = param;
e.deleted = 0;
e.stop_bubbling = 0;
e.stop_processing = 0;
e.stop_trickling = 0;
lv_event_push(&e);
/*Send the event*/
lv_result_t res = event_send_core(&e);
/*Remove this element from the list*/
lv_event_pop(&e);
return res;
}
lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e)
{
const lv_obj_class_t * base;
if(class_p == NULL) base = ((lv_obj_t *)e->current_target)->class_p;
else base = class_p->base_class;
/*Find a base in which call the ancestor's event handler_cb if set*/
while(base && base->event_cb == NULL) base = base->base_class;
if(base == NULL) return LV_RESULT_OK;
if(base->event_cb == NULL) return LV_RESULT_OK;
/*Call the actual event callback*/
e->user_data = NULL;
LV_PROFILER_EVENT_BEGIN_TAG(lv_event_code_get_name(e->code));
base->event_cb(base, e);
LV_PROFILER_EVENT_END_TAG(lv_event_code_get_name(e->code));
lv_result_t res = LV_RESULT_OK;
/*Stop if the object is deleted*/
if(e->deleted) res = LV_RESULT_INVALID;
return res;
}
lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
return lv_event_add(&obj->spec_attr->event_list, event_cb, filter, user_data);
}
uint32_t lv_obj_get_event_count(lv_obj_t * obj)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return 0;
return lv_event_get_count(&obj->spec_attr->event_list);
}
lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return NULL;
return lv_event_get_dsc(&obj->spec_attr->event_list, index);
}
bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return false;
return lv_event_remove(&obj->spec_attr->event_list, index);
}
bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc)
{
LV_ASSERT_NULL(obj);
LV_ASSERT_NULL(dsc);
if(obj->spec_attr == NULL) return false;
return lv_event_remove_dsc(&obj->spec_attr->event_list, dsc);
}
uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_NULL(obj);
uint32_t event_cnt = lv_obj_get_event_count(obj);
uint32_t removed_count = 0;
int32_t i;
if(event_cnt == 0) return 0;
for(i = event_cnt - 1; i >= 0; i--) {
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
if(dsc && dsc->cb == event_cb) {
lv_obj_remove_event(obj, i);
removed_count++;
}
}
return removed_count;
}
uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data)
{
LV_ASSERT_NULL(obj);
uint32_t event_cnt = lv_obj_get_event_count(obj);
uint32_t removed_count = 0;
int32_t i;
if(event_cnt == 0) return 0;
for(i = event_cnt - 1; i >= 0; i--) {
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
if(dsc && (event_cb == NULL || dsc->cb == event_cb) && dsc->user_data == user_data) {
lv_obj_remove_event(obj, i);
removed_count ++;
}
}
return removed_count;
}
lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e)
{
return lv_event_get_current_target(e);
}
lv_obj_t * lv_event_get_target_obj(lv_event_t * e)
{
return lv_event_get_target(e);
}
lv_indev_t * lv_event_get_indev(lv_event_t * e)
{
if(e->code == LV_EVENT_PRESSED ||
e->code == LV_EVENT_PRESSING ||
e->code == LV_EVENT_PRESS_LOST ||
e->code == LV_EVENT_SHORT_CLICKED ||
e->code == LV_EVENT_LONG_PRESSED ||
e->code == LV_EVENT_LONG_PRESSED_REPEAT ||
e->code == LV_EVENT_CLICKED ||
e->code == LV_EVENT_RELEASED ||
e->code == LV_EVENT_SCROLL_BEGIN ||
e->code == LV_EVENT_SCROLL_END ||
e->code == LV_EVENT_SCROLL ||
e->code == LV_EVENT_GESTURE ||
e->code == LV_EVENT_KEY ||
e->code == LV_EVENT_FOCUSED ||
e->code == LV_EVENT_DEFOCUSED ||
e->code == LV_EVENT_LEAVE ||
e->code == LV_EVENT_HOVER_OVER ||
e->code == LV_EVENT_HOVER_LEAVE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_layer_t * lv_event_get_layer(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_MAIN ||
e->code == LV_EVENT_DRAW_MAIN_BEGIN ||
e->code == LV_EVENT_DRAW_MAIN_END ||
e->code == LV_EVENT_DRAW_POST ||
e->code == LV_EVENT_DRAW_POST_BEGIN ||
e->code == LV_EVENT_DRAW_POST_END) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_old_size(lv_event_t * e)
{
if(e->code == LV_EVENT_SIZE_CHANGED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
uint32_t lv_event_get_key(lv_event_t * e)
{
if(e->code == LV_EVENT_KEY) {
uint32_t * k = lv_event_get_param(e);
if(k) return *k;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
int32_t lv_event_get_rotary_diff(lv_event_t * e)
{
if(e->code == LV_EVENT_ROTARY) {
int32_t * r = lv_event_get_param(e);
if(r) return *r;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e)
{
if(e->code == LV_EVENT_SCROLL_BEGIN) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size)
{
if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
int32_t * cur_size = lv_event_get_param(e);
*cur_size = LV_MAX(*cur_size, size);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_point_t * lv_event_get_self_size_info(lv_event_t * e)
{
if(e->code == LV_EVENT_GET_SELF_SIZE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e)
{
if(e->code == LV_EVENT_HIT_TEST) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
const lv_area_t * lv_event_get_cover_area(lv_event_t * e)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
return p->area;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
if(res > p->res) p->res = res; /*Save only "stronger" results*/
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_TASK_ADDED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_state_t lv_event_get_prev_state(lv_event_t * e)
{
if(e->code == LV_EVENT_STATE_CHANGED) {
lv_state_t * state = lv_event_get_param(e);
return state ? *state : 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t event_send_core(lv_event_t * e)
{
LV_TRACE_EVENT("Sending event %d to %p with %p param", e->code, (void *)e->original_target, e->param);
lv_indev_t * indev_act = lv_indev_active();
if(indev_act) {
if(e->stop_processing) return LV_RESULT_OK;
if(e->deleted) return LV_RESULT_INVALID;
}
lv_obj_t * target = e->current_target;
lv_result_t res = LV_RESULT_OK;
lv_event_list_t * list = target->spec_attr ? &target->spec_attr->event_list : NULL;
res = lv_event_send(list, e, true);
if(res != LV_RESULT_OK || e->stop_processing) return res;
res = lv_obj_event_base(NULL, e);
if(res != LV_RESULT_OK || e->stop_processing) return res;
res = lv_event_send(list, e, false);
if(res != LV_RESULT_OK || e->stop_processing) return res;
lv_obj_t * parent = lv_obj_get_parent(e->current_target);
if(parent && event_is_bubbled(e)) {
e->current_target = parent;
res = event_send_core(e);
}
if(res != LV_RESULT_OK) return res;
/*Trickle down to children if enabled*/
if(event_is_trickled(e)) {
uint32_t child_count = lv_obj_get_child_count(target);
/* we don't want the event to bubble up again when trickling down */
e->stop_bubbling = 1;
for(uint32_t i = 0; i < child_count && res == LV_RESULT_OK && !e->stop_processing; i++) {
lv_obj_t * child = lv_obj_get_child(target, i);
if(child) {
e->current_target = child;
res = event_send_core(e);
if(res != LV_RESULT_OK) {
LV_LOG_WARN("Trickle down event %d to child %p failed", e->code, (void *)child);
break;
}
}
}
}
return res;
}
static bool event_is_bubbled(lv_event_t * e)
{
if(e->stop_bubbling) return false;
/*Event codes that always bubble*/
switch(e->code) {
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
return true;
default:
break;
}
/*Check other codes only if bubbling is enabled*/
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) == false) return false;
switch(e->code) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_TASK_ADDED:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}
static bool event_is_trickled(lv_event_t * e)
{
if(e->stop_trickling) return false;
/*Check other codes only if trickle is enabled*/
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_TRICKLE) == false) return false;
switch(e->code) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_TASK_ADDED:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}

View File

@@ -0,0 +1,213 @@
/**
* @file lv_obj_event.h
*
*/
#ifndef LV_OBJ_EVENT_H
#define LV_OBJ_EVENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_event.h"
#include "../indev/lv_indev.h"
#include "lv_obj_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** Cover check results.*/
typedef enum {
LV_COVER_RES_COVER = 0,
LV_COVER_RES_NOT_COVER = 1,
LV_COVER_RES_MASKED = 2,
} lv_cover_res_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Send an event to the object
* @param obj pointer to an object
* @param event_code the type of the event from `lv_event_t`
* @param param arbitrary data depending on the widget type and the event. (Usually `NULL`)
* @return LV_RESULT_OK: `obj` was not deleted in the event; LV_RESULT_INVALID: `obj` was deleted in the event_code
*/
lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param);
/**
* Used by the widgets internally to call the ancestor widget types's event handler
* @param class_p pointer to the class of the widget (NOT the ancestor class)
* @param e pointer to the event descriptor
* @return LV_RESULT_OK: the target object was not deleted in the event; LV_RESULT_INVALID: it was deleted in the event_code
*/
lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e);
/**
* Get the current target of the event. It's the object which event handler being called.
* If the event is not bubbled it's the same as "original" target.
* @param e pointer to the event descriptor
* @return the target of the event_code
*/
lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e);
/**
* Get the object originally targeted by the event. It's the same even if the event is bubbled.
* @param e pointer to the event descriptor
* @return pointer to the original target of the event_code
*/
lv_obj_t * lv_event_get_target_obj(lv_event_t * e);
/**
* Add an event handler function for an object.
* Used by the user to react on event which happens with the object.
* An object can have multiple event handler. They will be called in the same order as they were added.
* @param obj pointer to an object
* @param filter an event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be used to receive all the events.
* @param event_cb the new event function
* @param user_data custom data will be available in `event_cb`
* @return handler to the event. It can be used in `lv_obj_remove_event_dsc`.
*/
lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
uint32_t lv_obj_get_event_count(lv_obj_t * obj);
lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index);
bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index);
bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc);
/**
* Remove an event_cb from an object
* @param obj pointer to a obj
* @param event_cb the event_cb of the event to remove
* @return the count of the event removed
*/
uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb);
/**
* Remove an event_cb with user_data
* @param obj pointer to a obj
* @param event_cb the event_cb of the event to remove
* @param user_data user_data
* @return the count of the event removed
*/
uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data);
/**
* Get the input device passed as parameter to indev related events.
* @param e pointer to an event
* @return the indev that triggered the event or NULL if called on a not indev related event
*/
lv_indev_t * lv_event_get_indev(lv_event_t * e);
/**
* Get the draw context which should be the first parameter of the draw functions.
* Namely: `LV_EVENT_DRAW_MAIN/POST`, `LV_EVENT_DRAW_MAIN/POST_BEGIN`, `LV_EVENT_DRAW_MAIN/POST_END`
* @param e pointer to an event
* @return pointer to a draw context or NULL if called on an unrelated event
*/
lv_layer_t * lv_event_get_layer(lv_event_t * e);
/**
* Get the old area of the object before its size was changed. Can be used in `LV_EVENT_SIZE_CHANGED`
* @param e pointer to an event
* @return the old absolute area of the object or NULL if called on an unrelated event
*/
const lv_area_t * lv_event_get_old_size(lv_event_t * e);
/**
* Get the key passed as parameter to an event. Can be used in `LV_EVENT_KEY`
* @param e pointer to an event
* @return the triggering key or NULL if called on an unrelated event
*/
uint32_t lv_event_get_key(lv_event_t * e);
/**
* Get the signed rotary encoder diff. passed as parameter to an event. Can be used in `LV_EVENT_ROTARY`
* @param e pointer to an event
* @return the triggering key or NULL if called on an unrelated event
*/
int32_t lv_event_get_rotary_diff(lv_event_t * e);
/**
* Get the animation descriptor of a scrolling. Can be used in `LV_EVENT_SCROLL_BEGIN`
* @param e pointer to an event
* @return the animation that will scroll the object. (can be modified as required)
*/
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e);
/**
* Set the new extra draw size. Can be used in `LV_EVENT_REFR_EXT_DRAW_SIZE`
* @param e pointer to an event
* @param size The new extra draw size
*/
void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size);
/**
* Get a pointer to an `lv_point_t` variable in which the self size should be saved (width in `point->x` and height `point->y`).
* Can be used in `LV_EVENT_GET_SELF_SIZE`
* @param e pointer to an event
* @return pointer to `lv_point_t` or NULL if called on an unrelated event
*/
lv_point_t * lv_event_get_self_size_info(lv_event_t * e);
/**
* Get a pointer to an `lv_hit_test_info_t` variable in which the hit test result should be saved. Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return pointer to `lv_hit_test_info_t` or NULL if called on an unrelated event
*/
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e);
/**
* Get a pointer to an area which should be examined whether the object fully covers it or not.
* Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return an area with absolute coordinates to check
*/
const lv_area_t * lv_event_get_cover_area(lv_event_t * e);
/**
* Set the result of cover checking. Can be used in `LV_EVENT_COVER_CHECK`
* @param e pointer to an event
* @param res an element of ::lv_cover_check_info_t
*/
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
/**
* Get the draw task which was just added.
* Can be used in `LV_EVENT_DRAW_TASK_ADDED event`
* @param e pointer to an event
* @return the added draw task
*/
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e);
/**
* Get the previous state before the state change.
* Can be used in `LV_EVENT_STATE_CHANGED` event
* @param e pointer to an event
* @return the previous state
*/
lv_state_t lv_event_get_prev_state(lv_event_t * e);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_EVENT_H*/

View File

@@ -0,0 +1,62 @@
/**
* @file lv_obj_event_private.h
*
*/
#ifndef LV_OBJ_EVENT_PRIVATE_H
#define LV_OBJ_EVENT_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_event.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Used as the event parameter of ::LV_EVENT_HIT_TEST to check if an `point` can click the object or not.
* `res` should be set like this:
* - If already set to `false` another event wants that point non clickable. If you want to respect it leave it as `false` or set `true` to overwrite it.
* - If already set `true` and `point` shouldn't be clickable set to `false`
* - If already set to `true` you agree that `point` can click the object leave it as `true`
*/
struct _lv_hit_test_info_t {
const lv_point_t * point; /**< A point relative to screen to check if it can click the object or not*/
bool res; /**< true: `point` can click the object; false: it cannot*/
};
/**
* Used as the event parameter of ::LV_EVENT_COVER_CHECK to check if an area is covered by the object or not.
* In the event use `const lv_area_t * area = lv_event_get_cover_area(e)` to get the area to check
* and `lv_event_set_cover_res(e, res)` to set the result.
*/
struct _lv_cover_check_info_t {
lv_cover_res_t res;
const lv_area_t * area;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_EVENT_PRIVATE_H*/

View File

@@ -0,0 +1,120 @@
/**
* @file lv_obj_id_builtin.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "lv_global.h"
#include "../osal/lv_os_private.h"
#include "../stdlib/lv_sprintf.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
typedef struct _class_info_t {
const lv_obj_class_t * class_p;
uint32_t obj_count;
} class_info_t;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
#if LV_USE_OBJ_ID && LV_USE_OBJ_ID_BUILTIN
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_ASSERT(obj && class_p);
uint32_t i;
uint32_t id = 0;
lv_global_t * global = LV_GLOBAL_DEFAULT();
class_info_t * info = NULL;
if(obj == NULL || class_p == NULL) return;
if(global == NULL) return;
obj->id = NULL;
for(i = 0; i < global->objid_count; i ++) {
info = ((class_info_t *)global->objid_array) + i;
if(class_p == info->class_p) break;
}
/*Resize array*/
if(i == global->objid_count) {
void * array = lv_realloc(global->objid_array, sizeof(class_info_t) * (global->objid_count + 1));
LV_ASSERT_MALLOC(array);
if(array == NULL) return;
global->objid_array = array;
global->objid_count ++;
info = ((class_info_t *)global->objid_array) + i;
info->obj_count = 0;
info->class_p = class_p;
}
id = ++info->obj_count;
obj->id = (void *)(lv_uintptr_t)id;
}
void lv_obj_set_id(lv_obj_t * obj, void * id)
{
LV_ASSERT_NULL(obj);
if(obj->id) lv_obj_free_id(obj);
obj->id = id;
}
void lv_obj_free_id(lv_obj_t * obj)
{
obj->id = NULL;
}
const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len)
{
if(obj == NULL || obj->class_p == NULL) return NULL;
if(buf == NULL) return NULL;
const char * name = obj->class_p->name;
if(name == NULL) name = "nameless";
lv_snprintf(buf, len, "%s%" LV_PRIu32 "", name, (uint32_t)(lv_uintptr_t)obj->id);
return buf;
}
void lv_objid_builtin_destroy(void)
{
lv_global_t * global = LV_GLOBAL_DEFAULT();
if(global == NULL) return;
lv_free(global->objid_array);
global->objid_count = 0;
}
int lv_obj_id_compare(const void * id1, const void * id2)
{
return id1 == id2 ? 0 : 1;
}
#endif /*LV_USE_OBJ_ID_BUILTIN*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,567 @@
/**
* @file lv_obj_pos.h
*
*/
#ifndef LV_OBJ_POS_H
#define LV_OBJ_POS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
/** No flags */
LV_OBJ_POINT_TRANSFORM_FLAG_NONE = 0x00,
/** Consider the transformation properties of the parents too */
LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE = 0x01,
/** Execute the inverse of the transformation (-angle and 1/zoom) */
LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE = 0x02,
/** Both inverse and recursive*/
LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE_RECURSIVE = 0x03,
} lv_obj_point_transform_flag_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Set the position of an object relative to the set alignment.
* @param obj pointer to an object
* @param x new x coordinate
* @param y new y coordinate
* @note With default alignment it's the distance from the top left corner
* @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
* @note The position is interpreted on the content area of the parent
* @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
*/
void lv_obj_set_pos(lv_obj_t * obj, int32_t x, int32_t y);
/**
* Set the x coordinate of an object
* @param obj pointer to an object
* @param x new x coordinate
* @note With default alignment it's the distance from the top left corner
* @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
* @note The position is interpreted on the content area of the parent
* @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
*/
void lv_obj_set_x(lv_obj_t * obj, int32_t x);
/**
* Set the y coordinate of an object
* @param obj pointer to an object
* @param y new y coordinate
* @note With default alignment it's the distance from the top left corner
* @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
* @note The position is interpreted on the content area of the parent
* @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
*/
void lv_obj_set_y(lv_obj_t * obj, int32_t y);
/**
* Set the size of an object.
* @param obj pointer to an object
* @param w the new width
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_size(lv_obj_t * obj, int32_t w, int32_t h);
/**
* Recalculate the size of the object
* @param obj pointer to an object
* @return true: the size has been changed
*/
bool lv_obj_refr_size(lv_obj_t * obj);
/**
* Set the width of an object
* @param obj pointer to an object
* @param w the new width
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_width(lv_obj_t * obj, int32_t w);
/**
* Set the height of an object
* @param obj pointer to an object
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_height(lv_obj_t * obj, int32_t h);
/**
* Set the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @param w the width without paddings in pixels
*/
void lv_obj_set_content_width(lv_obj_t * obj, int32_t w);
/**
* Set the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @param h the height without paddings in pixels
*/
void lv_obj_set_content_height(lv_obj_t * obj, int32_t h);
/**
* Set a layout for an object
* @param obj pointer to an object
* @param layout pointer to a layout descriptor to set
*/
void lv_obj_set_layout(lv_obj_t * obj, uint32_t layout);
/**
* Test whether the and object is positioned by a layout or not
* @param obj pointer to an object to test
* @return true: positioned by a layout; false: not positioned by a layout
*/
bool lv_obj_is_layout_positioned(const lv_obj_t * obj);
/**
* Mark the object for layout update.
* @param obj pointer to an object whose children need to be updated
*/
void lv_obj_mark_layout_as_dirty(lv_obj_t * obj);
/**
* Update the layout of an object.
* @param obj pointer to an object whose position and size needs to be updated
*/
void lv_obj_update_layout(const lv_obj_t * obj);
/**
* Change the alignment of an object.
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
*/
void lv_obj_set_align(lv_obj_t * obj, lv_align_t align);
/**
* Change the alignment of an object and set new coordinates.
* Equivalent to:
* lv_obj_set_align(obj, align);
* lv_obj_set_pos(obj, x_ofs, y_ofs);
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
*/
void lv_obj_align(lv_obj_t * obj, lv_align_t align, int32_t x_ofs, int32_t y_ofs);
/**
* Align an object to another object.
* @param obj pointer to an object to align
* @param base pointer to another object (if NULL `obj`s parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
* @note if the position or size of `base` changes `obj` needs to be aligned manually again
*/
void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, int32_t x_ofs,
int32_t y_ofs);
/**
* Align an object to the center on its parent.
* @param obj pointer to an object to align
* @note if the parent size changes `obj` needs to be aligned manually again
*/
void lv_obj_center(lv_obj_t * obj);
/**
* Set the transform matrix of an object
* @param obj pointer to an object
* @param matrix pointer to a matrix to set
* @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
*/
void lv_obj_set_transform(lv_obj_t * obj, const lv_matrix_t * matrix);
/**
* Reset the transform matrix of an object to identity matrix
* @param obj pointer to an object
* @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
*/
void lv_obj_reset_transform(lv_obj_t * obj);
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
* @param coords pointer to an area to store the coordinates
*/
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords);
/**
* Get the x coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the left side of its parent plus the parent's left padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the left padding of the parent, and not on the left edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_x(const lv_obj_t * obj);
/**
* Get the x2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the right side of its parent plus the parent's right padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the right padding of the parent, and not on the right edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_x2(const lv_obj_t * obj);
/**
* Get the y coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the top side of its parent plus the parent's top padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the top padding of the parent, and not on the top edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_y(const lv_obj_t * obj);
/**
* Get the y2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the bottom side of its parent plus the parent's bottom padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the bottom padding of the parent, and not on the bottom edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_y2(const lv_obj_t * obj);
/**
* Get the actually set x coordinate of object, i.e. the offset from the set alignment
* @param obj pointer to an object
* @return the set x coordinate
*/
int32_t lv_obj_get_x_aligned(const lv_obj_t * obj);
/**
* Get the actually set y coordinate of object, i.e. the offset from the set alignment
* @param obj pointer to an object
* @return the set y coordinate
*/
int32_t lv_obj_get_y_aligned(const lv_obj_t * obj);
/**
* Get the width of an object
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the width in pixels
*/
int32_t lv_obj_get_width(const lv_obj_t * obj);
/**
* Get the height of an object
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the height in pixels
*/
int32_t lv_obj_get_height(const lv_obj_t * obj);
/**
* Get the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the width which still fits into its parent without causing overflow (making the parent scrollable)
*/
int32_t lv_obj_get_content_width(const lv_obj_t * obj);
/**
* Get the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the height which still fits into the parent without causing overflow (making the parent scrollable)
*/
int32_t lv_obj_get_content_height(const lv_obj_t * obj);
/**
* Get the area reduced by the paddings and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @param area the area which still fits into the parent without causing overflow (making the parent scrollable)
*/
void lv_obj_get_content_coords(const lv_obj_t * obj, lv_area_t * area);
/**
* Get the width occupied by the "parts" of the widget. E.g. the width of all columns of a table.
* @param obj pointer to an object
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
int32_t lv_obj_get_self_width(const lv_obj_t * obj);
/**
* Get the height occupied by the "parts" of the widget. E.g. the height of all rows of a table.
* @param obj pointer to an object
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
int32_t lv_obj_get_self_height(const lv_obj_t * obj);
/**
* Get the style width actually used by the object after clamping the width within the min max range.
* @param obj pointer to an object
* @return the min/max/normal width set by `lv_obj_set_style_<min/max>_width()`
* @note This is not the calculated size, so if the size was set as `LV_SIZE_CONTENT` or `LV_PCT()`
* then that value will be returned.
*/
int32_t lv_obj_get_style_clamped_width(lv_obj_t * obj);
/**
* Get the style height actually used by the object after clamping the height within the min max range.
* @param obj pointer to an object
* @return the min/max/normal height set by `lv_obj_set_style_<min/max>_height()`
* @note This is not the calculated size, so if the size was set as `LV_SIZE_CONTENT` or `LV_PCT()`
* then that value will be returned.
*/
int32_t lv_obj_get_style_clamped_height(lv_obj_t * obj);
/**
* @brief Determine if the object's resolved width was limited by its minimum width constraint.
*
* This function reports whether, in the most recent layout / size calculation, the object's
* final (used) width had to be raised to satisfy a minimum width requirement.
*
* @param obj Pointer to a valid object.
* @return true The computed width == the effective minimum width (i.e. it was clamped).
* @return false The width is larger than the minimum (not minclamped).
*/
bool lv_obj_is_width_min(lv_obj_t * obj);
/**
* @brief Determine if the object's resolved height was limited by its minimum height constraint.
*
* This function reports whether, in the most recent layout / size calculation, the object's
* final (used) height had to be raised to satisfy a minimum height requirement.
*
* @param obj Pointer to a valid object.
* @return true The computed height == the effective minimum height (i.e. it was clamped).
* @return false The height is larger than the minimum (not minclamped).
*/
bool lv_obj_is_height_min(lv_obj_t * obj);
/**
* @brief Determine if the object's resolved width was limited by its maximum width constraint.
*
* This function reports whether, in the most recent layout / size calculation, the object's
* final (used) width had to be raised to satisfy a maximum width requirement.
*
* @param obj Pointer to a valid object.
* @return true The computed width == the effective maximum width (i.e. it was clamped).
* @return false The width is smaller than the maximum (not minclamped).
*/
bool lv_obj_is_width_max(lv_obj_t * obj);
/**
* @brief Determine if the object's resolved height was limited by its maximum height constraint.
*
* This function reports whether, in the most recent layout / size calculation, the object's
* final (used) height had to be raised to satisfy a maximum height requirement.
*
* @param obj Pointer to a valid object.
* @return true The computed height == the effective maximum height (i.e. it was clamped).
* @return false The height is smaller than the maximum (not minclamped).
*/
bool lv_obj_is_height_max(lv_obj_t * obj);
/**
* Handle if the size of the internal ("virtual") content of an object has changed.
* @param obj pointer to an object
* @return false: nothing happened; true: refresh happened
*/
bool lv_obj_refresh_self_size(lv_obj_t * obj);
void lv_obj_refr_pos(lv_obj_t * obj);
void lv_obj_move_to(lv_obj_t * obj, int32_t x, int32_t y);
void lv_obj_move_children_by(lv_obj_t * obj, int32_t x_diff, int32_t y_diff, bool ignore_floating);
/**
* Get the transform matrix of an object
* @param obj pointer to an object
* @return pointer to the transform matrix or NULL if not set
*/
const lv_matrix_t * lv_obj_get_transform(const lv_obj_t * obj);
/**
* Transform a point using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used
* @param p a point to transform, the result will be written back here too
* @param flags OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
*/
void lv_obj_transform_point(const lv_obj_t * obj, lv_point_t * p, lv_obj_point_transform_flag_t flags);
/**
* Transform an array of points using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used
* @param points the array of points to transform, the result will be written back here too
* @param count number of points in the array
* @param flags OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
*/
void lv_obj_transform_point_array(const lv_obj_t * obj, lv_point_t points[], size_t count,
lv_obj_point_transform_flag_t flags);
/**
* Transform an area using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used
* @param area an area to transform, the result will be written back here too
* @param flags OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
*/
void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, lv_obj_point_transform_flag_t flags);
/**
* Mark an area of an object as invalid.
* The area will be truncated to the object's area and marked for redraw.
* @param obj pointer to an object
* @param area the area to redraw
* @return LV_RESULT_OK: the area is invalidated; LV_RESULT_INVALID: the area wasn't invalidated.
* (maybe it was off-screen or fully clipped)
*/
lv_result_t lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area);
/**
* Mark the object as invalid to redrawn its area
* @param obj pointer to an object
* @return LV_RESULT_OK: the area is invalidated; LV_RESULT_INVALID: the area wasn't invalidated.
* (maybe it was off-screen or fully clipped)
*/
lv_result_t lv_obj_invalidate(const lv_obj_t * obj);
/**
* Tell whether an area of an object is visible (even partially) now or not
* @param obj pointer to an object
* @param area the are to check. The visible part of the area will be written back here.
* @return true visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area);
/**
* Tell whether an object is visible (even partially) now or not
* @param obj pointer to an object
* @return true: visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_is_visible(const lv_obj_t * obj);
/**
* Set the size of an extended clickable area
* @param obj pointer to an object
* @param size extended clickable area in all 4 directions [px]
*/
void lv_obj_set_ext_click_area(lv_obj_t * obj, int32_t size);
/**
* Get the an area where to object can be clicked.
* It's the object's normal area plus the extended click area.
* @param obj pointer to an object
* @param area store the result area here
*/
void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area);
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point (absolute coordinate)
* @return true: if the object is considered under the point
*/
bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point);
/**
* Clamp a width between min and max width. If the min/max width is in percentage value use the ref_width
* @param width width to clamp
* @param min_width the minimal width
* @param max_width the maximal width
* @param ref_width the reference width used when min/max width is in percentage
* @return the clamped width
*/
int32_t lv_clamp_width(int32_t width, int32_t min_width, int32_t max_width, int32_t ref_width);
/**
* Clamp a height between min and max height. If the min/max height is in percentage value use the ref_height
* @param height height to clamp
* @param min_height the minimal height
* @param max_height the maximal height
* @param ref_height the reference height used when min/max height is in percentage
* @return the clamped height
*/
int32_t lv_clamp_height(int32_t height, int32_t min_height, int32_t max_height, int32_t ref_height);
/**
* @brief Calculates the width in pixels of an LVGL object based on its style and parent for a given width `prop`.
* @param obj Pointer to the LVGL object whose width is being calculated.
* @param prop Which style width to calculate for. Valid values are: LV_STYLE_WIDTH, LV_STYLE_MIN_WIDTH, or
* LV_STYLE_MAX_WIDTH.
* @return The computed width for the object:
* @note If the style width is a fixed value, that value is returned.
* @note If the style width is `LV_SIZE_CONTENT`, the content width is calculated and returned.
* @note If the style width is a `LV_PCT()`, the percentage is applied to the parent's width.
*/
int32_t lv_obj_calc_dynamic_width(lv_obj_t * obj, lv_style_prop_t prop);
/**
* @brief Calculates the height in pixels of an LVGL object based on its style and parent for a given height `prop`.
* @param obj Pointer to the LVGL object whose height is being calculated.
* @param prop Which style height to calculate for. Valid values are: LV_STYLE_HEIGHT, LV_STYLE_MIN_HEIGHT, or
* LV_STYLE_MAX_HEIGHT.
* @return The computed height for the object:
* @note If the style height is a fixed value, that value is returned.
* @note If the style height is `LV_SIZE_CONTENT`, the content height is calculated and returned.
* @note If the style height is a `LV_PCT()`, the percentage is applied to the parent's height.
*/
int32_t lv_obj_calc_dynamic_height(lv_obj_t * obj, lv_style_prop_t prop);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_POS_H*/

View File

@@ -0,0 +1,103 @@
/**
* @file lv_obj_private.h
*
*/
#ifndef LV_OBJ_PRIVATE_H
#define LV_OBJ_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_ext_data.h"
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Special, rarely used attributes.
* They are allocated automatically if any elements is set.
*/
struct _lv_obj_spec_attr_t {
lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
lv_group_t * group_p;
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_matrix_t * matrix; /**< The transform matrix*/
#endif
lv_event_list_t event_list;
#if LV_USE_OBJ_NAME
const char * name; /**< Pointer to the name */
#endif
lv_point_t scroll; /**< The current X/Y scroll offset*/
int32_t ext_click_pad; /**< Extra click padding in all direction*/
int32_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
uint16_t child_cnt; /**< Number of children*/
uint16_t scrollbar_mode : 2; /**< How to display scrollbars, see `lv_scrollbar_mode_t`*/
uint16_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally, see `lv_scroll_snap_t`*/
uint16_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/
uint16_t scroll_dir : 4; /**< The allowed scroll direction(s), see `lv_dir_t`*/
uint16_t layer_type : 2; /**< Cache the layer type here. Element of lv_intermediate_layer_type_t */
uint16_t name_static : 1; /**< 1: `name` was not dynamically allocated */
};
struct _lv_obj_t {
#if LV_USE_EXT_DATA
lv_ext_data_t ext_data;
#endif
const lv_obj_class_t * class_p;
lv_obj_t * parent;
lv_obj_spec_attr_t * spec_attr;
lv_obj_style_t * styles;
#if LV_OBJ_STYLE_CACHE
uint32_t style_main_prop_is_set;
uint32_t style_other_prop_is_set;
#endif
void * user_data;
#if LV_USE_OBJ_ID
void * id;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
uint16_t state;
uint16_t layout_inv : 1;
uint16_t readjust_scroll_after_layout : 1;
uint16_t scr_layout_inv : 1;
uint16_t skip_trans : 1;
uint16_t style_cnt : 6;
uint16_t h_layout : 1;
uint16_t w_layout : 1;
uint16_t h_ignore_size : 1; /* ignore this obj when calculating content height of parent */
uint16_t w_ignore_size : 1; /* ignore this obj when calculating content width of parent */
uint16_t is_deleting : 1;
uint16_t radio_button : 1; /**< Allow only one RADIO_BUTTON sibling to be checked*/
/** The widget is rendered at least once already.
* It's used to skip initial animations and transitions. */
uint16_t rendered : 1;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_PRIVATE_H*/

View File

@@ -0,0 +1,339 @@
/**
* @file lv_obj_property.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_private.h"
#include "../core/lv_obj.h"
#include "../stdlib/lv_string.h"
#include "../misc/lv_utils.h"
#include "lv_obj_property.h"
#include "lv_obj_class_private.h"
#if LV_USE_OBJ_PROPERTY
/*********************
* DEFINES
*********************/
#define HANDLE_PROPERTY_TYPE(type, field) \
if(!set) { \
value->field = ((lv_property_get_##type##_t)(prop->getter))(obj); \
} else { \
switch(LV_PROPERTY_ID_TYPE2(prop->id)) { \
case LV_PROPERTY_ID_INVALID: \
((lv_property_set_##type##_t)(prop->setter))(obj, value->field); \
break; \
case LV_PROPERTY_TYPE_INT: \
((lv_property_set_##type##_integer_t)(prop->setter))(obj, value->arg1.field, value->arg2.num); \
break; \
case LV_PROPERTY_TYPE_BOOL: \
((lv_property_set_##type##_boolean_t)(prop->setter))(obj, value->arg1.field, value->arg2.enable); \
break; \
case LV_PROPERTY_TYPE_PRECISE: \
((lv_property_set_##type##_precise_t)(prop->setter))(obj, value->arg1.field, value->arg2.precise); \
break; \
case LV_PROPERTY_TYPE_COLOR: \
((lv_property_set_##type##_color_t)(prop->setter))(obj, value->arg1.field, value->arg2.color); \
break; \
case LV_PROPERTY_TYPE_POINTER: \
case LV_PROPERTY_TYPE_IMGSRC: \
case LV_PROPERTY_TYPE_TEXT: \
case LV_PROPERTY_TYPE_OBJ: \
case LV_PROPERTY_TYPE_DISPLAY: \
case LV_PROPERTY_TYPE_FONT: \
((lv_property_set_##type##_pointer_t)(prop->setter))(obj, value->arg1.field, value->arg2.ptr); \
break; \
} \
}
/**********************
* TYPEDEFS
**********************/
typedef int32_t integer;
typedef bool boolean;
typedef lv_value_precise_t precise;
typedef lv_color_t color;
typedef const void * pointer;
#define DEFINE_PROPERTY_SETTER_TYPES(type) \
typedef void (*lv_property_set_##type##_t)(lv_obj_t *, type); \
typedef void (*lv_property_set_##type##_integer_t)(lv_obj_t *, type, int32_t); \
typedef void (*lv_property_set_##type##_boolean_t)(lv_obj_t *, type, bool); \
typedef void (*lv_property_set_##type##_precise_t)(lv_obj_t *, type, lv_value_precise_t); \
typedef void (*lv_property_set_##type##_color_t)(lv_obj_t *, type, lv_color_t); \
typedef void (*lv_property_set_##type##_pointer_t)(lv_obj_t *, type, const void *)
DEFINE_PROPERTY_SETTER_TYPES(integer);
DEFINE_PROPERTY_SETTER_TYPES(boolean);
DEFINE_PROPERTY_SETTER_TYPES(precise);
DEFINE_PROPERTY_SETTER_TYPES(color);
DEFINE_PROPERTY_SETTER_TYPES(pointer);
typedef void (*lv_property_set_point_t)(lv_obj_t *, lv_point_t *);
typedef lv_result_t (*lv_property_setter_t)(lv_obj_t *, lv_prop_id_t, const lv_property_t *);
typedef integer(*lv_property_get_integer_t)(const lv_obj_t *);
typedef bool (*lv_property_get_boolean_t)(const lv_obj_t *);
typedef lv_value_precise_t (*lv_property_get_precise_t)(const lv_obj_t *);
typedef lv_color_t (*lv_property_get_color_t)(const lv_obj_t *);
typedef void * (*lv_property_get_pointer_t)(const lv_obj_t *);
typedef lv_point_t (*lv_property_get_point_t)(lv_obj_t *);
typedef lv_result_t (*lv_property_getter_t)(const lv_obj_t *, lv_prop_id_t, lv_property_t *);
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set);
static int property_name_compare(const void * ref, const void * element);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value)
{
LV_ASSERT(obj && value);
uint32_t index = LV_PROPERTY_ID_INDEX(value->id);
if(value->id == LV_PROPERTY_ID_INVALID || index > LV_PROPERTY_ID_ANY) {
LV_LOG_WARN("Invalid property id set to %p", obj);
return LV_RESULT_INVALID;
}
if(index < LV_PROPERTY_ID_START) {
lv_obj_set_local_style_prop(obj, index, value->style, value->selector);
return LV_RESULT_OK;
}
return obj_property(obj, value->id, (lv_property_t *)value, true);
}
lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count)
{
for(uint32_t i = 0; i < count; i++) {
lv_result_t result = lv_obj_set_property(obj, &value[i]);
if(result != LV_RESULT_OK) {
return result;
}
}
return LV_RESULT_OK;
}
lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id)
{
lv_result_t result;
lv_property_t value = { 0 };
uint32_t index = LV_PROPERTY_ID_INDEX(id);
if(id == LV_PROPERTY_ID_INVALID || index > LV_PROPERTY_ID_ANY) {
LV_LOG_WARN("Invalid property id to get from %p", obj);
value.id = LV_PROPERTY_ID_INVALID;
value.num = 0;
return value;
}
if(index < LV_PROPERTY_ID_START) {
value.style = lv_obj_get_style_prop(obj, LV_PART_MAIN, index);
value.id = id;
value.selector = LV_PART_MAIN | obj->state;
return value;
}
result = obj_property(obj, id, &value, false);
if(result != LV_RESULT_OK)
value.id = LV_PROPERTY_ID_INVALID;
return value;
}
lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, lv_part_t part)
{
lv_property_t value;
uint32_t index = LV_PROPERTY_ID_INDEX(id);
if(index == LV_PROPERTY_ID_INVALID || index >= LV_PROPERTY_ID_START) {
LV_LOG_WARN("invalid style property id 0x%" LV_PRIx32, id);
value.id = LV_PROPERTY_ID_INVALID;
value.num = 0;
return value;
}
value.style = lv_obj_get_style_prop(obj, part, index);
value.id = id;
value.selector = part | obj->state;
return value;
}
lv_prop_id_t lv_style_property_get_id(const char * name)
{
#if LV_USE_OBJ_PROPERTY_NAME
lv_property_name_t * found;
/*Check style property*/
found = lv_utils_bsearch(name, lv_style_property_names, sizeof(lv_style_property_names) / sizeof(lv_property_name_t),
sizeof(lv_property_name_t), property_name_compare);
if(found) return found->id;
#else
LV_UNUSED(name);
#endif
return LV_PROPERTY_ID_INVALID;
}
lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name)
{
#if LV_USE_OBJ_PROPERTY_NAME
const lv_property_name_t * names;
lv_property_name_t * found;
names = clz->property_names;
if(names == NULL) {
/* try base class*/
return LV_PROPERTY_ID_INVALID;
}
found = lv_utils_bsearch(name, names, clz->names_count, sizeof(lv_property_name_t), property_name_compare);
if(found) return found->id;
#else
LV_UNUSED(obj);
LV_UNUSED(name);
LV_UNUSED(property_name_compare);
#endif
return LV_PROPERTY_ID_INVALID;
}
lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name)
{
#if LV_USE_OBJ_PROPERTY_NAME
const lv_obj_class_t * clz;
lv_prop_id_t id;
for(clz = obj->class_p; clz; clz = clz->base_class) {
id = lv_obj_class_property_get_id(clz, name);
if(id != LV_PROPERTY_ID_INVALID) return id;
}
/*Check style property*/
id = lv_style_property_get_id(name);
if(id != LV_PROPERTY_ID_INVALID) return id;
#else
LV_UNUSED(obj);
LV_UNUSED(name);
LV_UNUSED(property_name_compare);
#endif
return LV_PROPERTY_ID_INVALID;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set)
{
const lv_property_ops_t * properties;
const lv_property_ops_t * prop;
const lv_obj_class_t * clz;
uint32_t index = LV_PROPERTY_ID_INDEX(id);
for(clz = obj->class_p ; clz; clz = clz->base_class) {
properties = clz->properties;
if(properties == NULL) {
/* try base class*/
continue;
}
if(id != LV_PROPERTY_ID_ANY && (index < clz->prop_index_start || index > clz->prop_index_end)) {
/* try base class*/
continue;
}
/*Check if there's setter available for this class*/
for(uint32_t i = 0; i < clz->properties_count; i++) {
prop = &properties[i];
/*pass id and value directly to widget's property method*/
if(prop->id == LV_PROPERTY_ID_ANY) {
value->id = prop->id;
if(set) return ((lv_property_setter_t)prop->setter)(obj, id, value);
else return ((lv_property_getter_t)prop->getter)(obj, id, value);
}
/*Not this id, check next*/
if(prop->id != id)
continue;
/*id matched but we got null pointer to functions*/
if(set ? prop->setter == NULL : prop->getter == NULL) {
LV_LOG_WARN("NULL %s provided, id: 0x%" LV_PRIx32, set ? "setter" : "getter", id);
return LV_RESULT_INVALID;
}
/*Update value id if it's a read*/
if(!set) value->id = prop->id;
switch(LV_PROPERTY_ID_TYPE(prop->id)) {
case LV_PROPERTY_TYPE_INT:
HANDLE_PROPERTY_TYPE(integer, num);
break;
case LV_PROPERTY_TYPE_BOOL:
HANDLE_PROPERTY_TYPE(boolean, enable);
break;
case LV_PROPERTY_TYPE_PRECISE:
HANDLE_PROPERTY_TYPE(precise, precise);
break;
case LV_PROPERTY_TYPE_COLOR:
HANDLE_PROPERTY_TYPE(color, color);
break;
case LV_PROPERTY_TYPE_POINTER:
case LV_PROPERTY_TYPE_IMGSRC:
case LV_PROPERTY_TYPE_TEXT:
case LV_PROPERTY_TYPE_OBJ:
case LV_PROPERTY_TYPE_DISPLAY:
case LV_PROPERTY_TYPE_FONT:
HANDLE_PROPERTY_TYPE(pointer, ptr);
break;
case LV_PROPERTY_TYPE_POINT: {
lv_point_t * point = &value->point;
if(set)((lv_property_set_point_t)(prop->setter))(obj, point);
else *point = ((lv_property_get_point_t)(prop->getter))(obj);
break;
}
default: {
LV_LOG_WARN("Unknown property id: 0x%08" LV_PRIx32, prop->id);
return LV_RESULT_INVALID;
}
}
return LV_RESULT_OK;
}
/*If no setter found, try base class then*/
}
LV_LOG_WARN("Unknown property id: 0x%08" LV_PRIx32, id);
return LV_RESULT_INVALID;
}
static int property_name_compare(const void * ref, const void * element)
{
const lv_property_name_t * prop = element;
return lv_strcmp(ref, prop->name);
}
#endif /*LV_USE_OBJ_PROPERTY*/

View File

@@ -0,0 +1,283 @@
/**
* @file lv_obj_property.h
*
*/
#ifndef LV_OBJ_PROPERTY_H
#define LV_OBJ_PROPERTY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_style.h"
#include "lv_obj_style.h"
#if LV_USE_OBJ_PROPERTY
/*********************
* DEFINES
*********************/
/*All possible property value types*/
#define LV_PROPERTY_TYPE_INVALID 0 /*Use default 0 as invalid to detect program outliers*/
#define LV_PROPERTY_TYPE_INT 1 /*int32_t type*/
#define LV_PROPERTY_TYPE_PRECISE 2 /*lv_value_precise_t, int32_t or float depending on LV_USE_FLOAT*/
#define LV_PROPERTY_TYPE_COLOR 3 /*ARGB8888 type*/
#define LV_PROPERTY_TYPE_POINT 4 /*lv_point_t */
#define LV_PROPERTY_TYPE_POINTER 5 /*void * pointer*/
#define LV_PROPERTY_TYPE_IMGSRC 6 /*Special pointer for image*/
#define LV_PROPERTY_TYPE_TEXT 7 /*Special pointer of char* */
#define LV_PROPERTY_TYPE_OBJ 8 /*Special pointer of lv_obj_t* */
#define LV_PROPERTY_TYPE_DISPLAY 9 /*Special pointer of lv_display_t* */
#define LV_PROPERTY_TYPE_FONT 10 /*Special pointer of lv_font_t* */
#define LV_PROPERTY_TYPE_BOOL 11 /*int32_t type*/
#define LV_PROPERTY_TYPE_SHIFT 28
#define LV_PROPERTY_TYPE2_SHIFT 24
/* Example:
* LV_PROPERTY_ID(OBJ, FLAG_CLICKABLE, LV_PROPERTY_TYPE_INT, 1),
* produces
* LV_PROPERTY_OBJ_FLAG_CLICKABLE = (LV_PROPERTY_OBJ_START + (1)) | ((LV_PROPERTY_TYPE_INT) << LV_PROPERTY_TYPE_SHIFT)
*/
#define LV_PROPERTY_ID(clz, name, type, index) LV_PROPERTY_## clz ##_##name = (LV_PROPERTY_## clz ##_START + ((int)index)) | ((type) << LV_PROPERTY_TYPE_SHIFT)
/* Example:
* LV_PROPERTY_ID2(SLIDER, VALUE, LV_PROPERTY_TYPE_INT, LV_PROPERTY_TYPE_BOOL, 0)
* produces
* LV_PROPERTY_SLIDER_VALUE = (LV_PROPERTY_SLIDER_START + (0)) | ((LV_PROPERTY_TYPE_INT) << LV_PROPERTY_TYPE_SHIFT) | ((LV_PROPERTY_TYPE_BOOL) << LV_PROPERTY_TYPE2_SHIFT)
*/
#define LV_PROPERTY_ID2(clz, name, type, type2, index) LV_PROPERTY_ID(clz, name, type, index) | ((type2) << LV_PROPERTY_TYPE2_SHIFT)
#define LV_PROPERTY_ID_TYPE(id) ((id) >> LV_PROPERTY_TYPE_SHIFT)
#define LV_PROPERTY_ID_TYPE2(id) ((id) >> LV_PROPERTY_TYPE_SHIFT)
#define LV_PROPERTY_ID_INDEX(id) ((id) & 0xfffffff)
/*Set properties from an array of lv_property_t*/
#define LV_OBJ_SET_PROPERTY_ARRAY(obj, array) lv_obj_set_properties(obj, array, LV_ARRAYLEN(array))
/* Helper to implement class definition of property and property names */
/* *INDENT-OFF* */
#if LV_USE_OBJ_PROPERTY_NAME
#define LV_PROPERTY_CLASS_FIELDS(widget, uppercase) \
.prop_index_start = LV_PROPERTY_##uppercase##_START, \
.prop_index_end = LV_PROPERTY_##uppercase##_END, \
.properties = lv_##widget##_properties, \
.properties_count = LV_ARRAYLEN(lv_##widget##_properties), \
.property_names = lv_##widget##_property_names, \
.names_count = LV_ARRAYLEN(lv_##widget##_property_names)
#else
#define LV_PROPERTY_CLASS_FIELDS(widget, uppercase) \
.prop_index_start = LV_PROPERTY_##uppercase##_START, \
.prop_index_end = LV_PROPERTY_##uppercase##_END, \
.properties = lv_##widget##_properties, \
.properties_count = LV_ARRAYLEN(lv_##widget##_properties)
#endif
/* *INDENT-ON* */
/**********************
* TYPEDEFS
**********************/
/**
* Group of predefined widget ID start value.
*/
enum _lv_prop_id_range_boundary_t {
LV_PROPERTY_ID_INVALID = 0,
/*ID 0x01 to 0xff are style ID, check lv_style_prop_t*/
LV_PROPERTY_STYLE_START = 0x00,
LV_PROPERTY_ID_START = 0x0100, /*ID smaller than 0xff is style ID*/
/*Define the property ID for every widget here. */
LV_PROPERTY_OBJ_START = 0x0100, /* lv_obj.c */
LV_PROPERTY_IMAGE_START = 0x0200, /* lv_image.c */
LV_PROPERTY_LABEL_START = 0x0300, /* lv_label.c */
LV_PROPERTY_KEYBOARD_START = 0x0400, /* lv_keyboard.c */
LV_PROPERTY_TEXTAREA_START = 0x0500, /* lv_textarea.c */
LV_PROPERTY_ROLLER_START = 0x0600, /* lv_roller.c */
LV_PROPERTY_DROPDOWN_START = 0x0700, /* lv_dropdown.c */
LV_PROPERTY_SLIDER_START = 0x0800, /* lv_slider.c */
LV_PROPERTY_ANIMIMAGE_START = 0x0900, /* lv_animimage.c */
LV_PROPERTY_ARC_START = 0x0a00, /* lv_arc.c */
LV_PROPERTY_BAR_START = 0x0b00, /* lv_bar.c */
LV_PROPERTY_SWITCH_START = 0x0c00, /* lv_switch.c */
LV_PROPERTY_CHECKBOX_START = 0x0d00, /* lv_checkbox.c */
LV_PROPERTY_LED_START = 0x0e00, /* lv_led.c */
LV_PROPERTY_LINE_START = 0x0f00, /* lv_line.c */
LV_PROPERTY_SCALE_START = 0x1000, /* lv_scale.c */
LV_PROPERTY_SPINBOX_START = 0x1100, /* lv_spinbox.c */
LV_PROPERTY_SPINNER_START = 0x1200, /* lv_spinner.c */
LV_PROPERTY_TABLE_START = 0x1300, /* lv_table.c */
LV_PROPERTY_TABVIEW_START = 0x1400, /* lv_tabview.c */
LV_PROPERTY_BUTTONMATRIX_START = 0x1500, /* lv_buttonmatrix.c */
LV_PROPERTY_SPAN_START = 0x1600, /* lv_span.c */
LV_PROPERTY_MENU_START = 0x1700, /* lv_menu.c */
LV_PROPERTY_CHART_START = 0x1800, /* lv_chart.c */
/*Special ID, use it to extend ID and make sure it's unique and compile time determinant*/
LV_PROPERTY_ID_BUILTIN_LAST = 0xffff, /*ID of 0x10000 ~ 0xfffffff is reserved for user*/
LV_PROPERTY_ID_ANY = 0x7ffffffe, /*Special ID used by lvgl to intercept all setter/getter call.*/
};
struct _lv_property_name_t {
const char * name;
lv_prop_id_t id;
};
typedef struct {
lv_prop_id_t id;
union {
int32_t num; /**< Signed integer number (enums or "normal" numbers)*/
uint32_t num_u; /**< Unsigned integer number (opacity, Booleans) */
bool enable; /**< Booleans */
const void * ptr; /**< Constant pointers (font, cone text, etc.) */
lv_color_t color; /**< Colors */
lv_value_precise_t precise; /**< float or int for precise value */
lv_point_t point; /**< Point, contains two int32_t */
struct {
/**
* Note that place struct member `style` at first place is intended.
* `style` shares same memory with `num`, `ptr`, `color`.
* So we set the style value directly without using `prop.style.num`.
*
* E.g.
*
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .num = 123,
* .selector = LV_STATE_PRESSED,
* }
*
* instead of:
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .style.num = 123, // note this line.
* .selector = LV_STATE_PRESSED,
* }
*/
lv_style_value_t style; /**< Make sure it's the first element in struct. */
uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */
};
/**
* For some properties like slider range, it contains two simple (4-byte) values
* so we can use `arg1.num` and `arg2.num` to set the argument.
*/
struct {
union {
int32_t num;
uint32_t num_u;
bool enable;
const void * ptr;
lv_color_t color;
lv_value_precise_t precise;
} arg1, arg2;
};
};
} lv_property_t;
typedef struct {
lv_prop_id_t id;
void * setter; /**< Callback used to set property. */
void * getter; /**< Callback used to get property. */
} lv_property_ops_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set Widget property.
* @param obj pointer to Widget
* @param value property value to set
* @return return LV_RESULT_OK if call succeeded
*/
lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value);
/**
* Set multiple Widget properties. Helper `LV_OBJ_SET_PROPERTY_ARRAY` can be used for constant property array.
* @param obj pointer to Widget
* @param value property value array to set
* @param count number of array elements
* @return return LV_RESULT_OK if call succeeded
*/
lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count);
/*=====================
* Getter functions
*====================*/
/**
* Read property value from Widget.
* If id is a style property, computes the style of PART_MAIN.
* @param obj pointer to Widget
* @param id ID of property to read
* @return return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed.
*/
lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id);
/**
* Read style property value from Widget
* @param obj pointer to Widget
* @param id ID of style property
* @param part part for which the style property should be computed
* @return return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed.
*/
lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, lv_part_t part);
/**
* Get property ID by recursively searching for name in Widget's class hierarchy, and
* if still not found, then search style properties.
* Requires to enabling `LV_USE_OBJ_PROPERTY_NAME`.
* @param obj pointer to Widget whose class and base-class hierarchy are to be searched.
* @param name property name
* @return property ID found or `LV_PROPERTY_ID_INVALID` if not found.
*/
lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name);
/**
* Get property ID by doing a non-recursive search for name directly in Widget class properties.
* Requires enabling `LV_USE_OBJ_PROPERTY_NAME`.
* @param clz pointer to Widget class that has specified property.
* @param name property name
* @return property ID found or `LV_PROPERTY_ID_INVALID` if not found.
*/
lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name);
/**
* Get style property ID by name. Requires enabling `LV_USE_OBJ_PROPERTY_NAME`.
* @param name property name
* @return property ID found or `LV_PROPERTY_ID_INVALID` if not found.
*/
lv_prop_id_t lv_style_property_get_id(const char * name);
/**********************
* MACROS
**********************/
#include "../widgets/property/lv_obj_property_names.h"
#include "../widgets/property/lv_style_properties.h"
#else
#define LV_PROPERTY_CLASS_FIELDS(widget, uppercase)
#endif /*LV_USE_OBJ_PROPERTY*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_PROPERTY_H*/

View File

@@ -0,0 +1,817 @@
/**
* @file lv_obj_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll_private.h"
#include "../misc/lv_anim_private.h"
#include "lv_obj_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_scroll.h"
#include "../display/lv_display.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
#ifndef SCROLL_ANIM_TIME_MIN
#define SCROLL_ANIM_TIME_MIN 200 /*ms*/
#endif
#ifndef SCROLL_ANIM_TIME_MAX
#define SCROLL_ANIM_TIME_MAX 400 /*ms*/
#endif
#define SCROLLBAR_MIN_SIZE (LV_DPX(10))
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scroll_x_anim(void * obj, int32_t v);
static void scroll_y_anim(void * obj, int32_t v);
static void scroll_end_cb(lv_anim_t * a);
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
lv_anim_enable_t anim_en);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(obj->spec_attr->scrollbar_mode == mode) return;
obj->spec_attr->scrollbar_mode = mode;
lv_obj_invalidate(obj);
}
void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
{
lv_obj_allocate_spec_attr(obj);
if(dir != obj->spec_attr->scroll_dir) {
obj->spec_attr->scroll_dir = dir;
}
}
void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_x = align;
}
void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_y = align;
}
/*=====================
* Getter functions
*====================*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_scrollbar_mode_t) obj->spec_attr->scrollbar_mode;
else return LV_SCROLLBAR_MODE_AUTO;
}
lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_dir_t) obj->spec_attr->scroll_dir;
else return LV_DIR_ALL;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_x;
else return LV_SCROLL_SNAP_NONE;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_y;
else return LV_SCROLL_SNAP_NONE;
}
int32_t lv_obj_get_scroll_x(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
int32_t lv_obj_get_scroll_y(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
int32_t lv_obj_get_scroll_top(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
int32_t lv_obj_get_scroll_bottom(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
int32_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(obj);
for(i = 0; i < child_cnt; i++) {
const lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
int32_t tmp_y = child->coords.y2 + lv_obj_get_style_margin_bottom(child, LV_PART_MAIN);
child_res = LV_MAX(child_res, tmp_y);
}
int32_t space_top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
int32_t space_bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.y2 - space_bottom);
}
int32_t self_h = lv_obj_get_self_height(obj);
self_h = self_h - (lv_obj_get_height(obj) - space_top - space_bottom);
self_h -= lv_obj_get_scroll_y(obj);
return LV_MAX(child_res, self_h);
}
int32_t lv_obj_get_scroll_left(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Normally can't scroll the object out on the left.
*So simply use the current scroll position as "left size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
/*With RTL base direction scrolling the left is normal so find the left most coordinate*/
int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
int32_t child_res = 0;
uint32_t i;
int32_t x1 = LV_COORD_MAX;
uint32_t child_cnt = lv_obj_get_child_count(obj);
for(i = 0; i < child_cnt; i++) {
const lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
int32_t tmp_x = child->coords.x1 - lv_obj_get_style_margin_left(child, LV_PART_MAIN);
x1 = LV_MIN(x1, tmp_x);
}
if(x1 != LV_COORD_MAX) {
child_res = x1;
child_res = (obj->coords.x1 + space_left) - child_res;
}
else {
child_res = LV_COORD_MIN;
}
int32_t self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
self_w += lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
int32_t lv_obj_get_scroll_right(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*With RTL base dir can't scroll to the object out on the right.
*So simply use the current scroll position as "right size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->scroll.x;
}
/*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
int32_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(obj);
for(i = 0; i < child_cnt; i++) {
const lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
int32_t tmp_x = child->coords.x2 + lv_obj_get_style_margin_right(child, LV_PART_MAIN);
child_res = LV_MAX(child_res, tmp_x);
}
int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.x2 - space_right);
}
int32_t self_w;
self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
self_w -= lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end)
{
lv_anim_t * a;
a = lv_anim_get(obj, scroll_x_anim);
end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
a = lv_anim_get(obj, scroll_y_anim);
end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
}
/*=====================
* Other functions
*====================*/
void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
{
if(dx == 0 && dy == 0) return;
/*We need to know the final sizes for bound check*/
lv_obj_update_layout(obj);
/*Don't let scroll more than naturally possible by the size of the content*/
int32_t x_current = -lv_obj_get_scroll_x(obj);
int32_t x_bounded = x_current + dx;
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(x_bounded > 0) x_bounded = 0;
if(x_bounded < 0) {
int32_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x_bounded < -scroll_max) x_bounded = -scroll_max;
}
}
else {
if(x_bounded < 0) x_bounded = 0;
if(x_bounded > 0) {
int32_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x_bounded > scroll_max) x_bounded = scroll_max;
}
}
/*Don't let scroll more than naturally possible by the size of the content*/
int32_t y_current = -lv_obj_get_scroll_y(obj);
int32_t y_bounded = y_current + dy;
if(y_bounded > 0) y_bounded = 0;
if(y_bounded < 0) {
int32_t scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
if(scroll_max < 0) scroll_max = 0;
if(y_bounded < -scroll_max) y_bounded = -scroll_max;
}
dx = x_bounded - x_current;
dy = y_bounded - y_current;
if(dx || dy) {
lv_obj_scroll_by(obj, dx, dy, anim_en);
}
}
void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
{
if(dx == 0 && dy == 0) return;
if(anim_en) {
lv_display_t * d = lv_obj_get_display(obj);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_deleted_cb(&a, scroll_end_cb);
if(dx) {
uint32_t t = lv_anim_speed_clamped((lv_display_get_horizontal_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
SCROLL_ANIM_TIME_MAX);
lv_anim_set_duration(&a, t);
int32_t sx = lv_obj_get_scroll_x(obj);
lv_anim_set_values(&a, -sx, -sx + dx);
lv_anim_set_exec_cb(&a, scroll_x_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_result_t res;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RESULT_OK) return;
lv_anim_start(&a);
}
if(dy) {
uint32_t t = lv_anim_speed_clamped((lv_display_get_vertical_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
SCROLL_ANIM_TIME_MAX);
lv_anim_set_duration(&a, t);
int32_t sy = lv_obj_get_scroll_y(obj);
lv_anim_set_values(&a, -sy, -sy + dy);
lv_anim_set_exec_cb(&a, scroll_y_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_result_t res;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RESULT_OK) return;
lv_anim_start(&a);
}
}
else {
/*Remove pending animations*/
lv_anim_delete(obj, scroll_y_anim);
lv_anim_delete(obj, scroll_x_anim);
lv_result_t res;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(res != LV_RESULT_OK) return;
res = lv_obj_scroll_by_raw(obj, dx, dy);
if(res != LV_RESULT_OK) return;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_END, NULL);
if(res != LV_RESULT_OK) return;
}
}
void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en)
{
lv_obj_scroll_to_x(obj, x, anim_en);
lv_obj_scroll_to_y(obj, y, anim_en);
}
void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en)
{
lv_anim_delete(obj, scroll_x_anim);
int32_t scroll_x = lv_obj_get_scroll_x(obj);
int32_t diff = -x + scroll_x;
lv_obj_scroll_by_bounded(obj, diff, 0, anim_en);
}
void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en)
{
lv_anim_delete(obj, scroll_y_anim);
int32_t scroll_y = lv_obj_get_scroll_y(obj);
int32_t diff = -y + scroll_y;
lv_obj_scroll_by_bounded(obj, 0, diff, anim_en);
}
void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
scroll_area_into_view(&obj->coords, obj, &p, anim_en);
}
void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
lv_obj_t * child = obj;
lv_obj_t * parent = lv_obj_get_parent(child);
while(parent) {
scroll_area_into_view(&obj->coords, child, &p, anim_en);
child = parent;
parent = lv_obj_get_parent(parent);
}
}
lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y)
{
if(x == 0 && y == 0) return LV_RESULT_OK;
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll.x += x;
obj->spec_attr->scroll.y += y;
lv_obj_move_children_by(obj, x, y, true);
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_SCROLL, NULL);
if(res != LV_RESULT_OK) return res;
lv_obj_invalidate(obj);
return LV_RESULT_OK;
}
bool lv_obj_is_scrolling(const lv_obj_t * obj)
{
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) return true;
indev = lv_indev_get_next(indev);
}
if(lv_anim_get((lv_obj_t *)obj, scroll_x_anim) != NULL ||
lv_anim_get((lv_obj_t *)obj, scroll_y_anim) != NULL) {
return true;
}
return false;
}
void lv_obj_stop_scroll_anim(const lv_obj_t * obj)
{
lv_anim_delete((lv_obj_t *)obj, scroll_y_anim);
lv_anim_delete((lv_obj_t *)obj, scroll_x_anim);
}
void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
lv_obj_update_layout(obj);
lv_point_t p;
lv_indev_scroll_get_snap_dist(obj, &p);
if(p.x == LV_COORD_MAX || p.x == LV_COORD_MIN) p.x = 0;
if(p.y == LV_COORD_MAX || p.y == LV_COORD_MIN) p.y = 0;
lv_obj_scroll_by(obj, p.x, p.y, anim_en);
}
void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
{
lv_area_set(hor_area, 0, 0, -1, -1);
lv_area_set(ver_area, 0, 0, -1, -1);
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
lv_scrollbar_mode_t sm = lv_obj_get_scrollbar_mode(obj);
if(sm == LV_SCROLLBAR_MODE_OFF) return;
/*If there is no indev scrolling this object but `mode==active` return*/
lv_indev_t * indev = lv_indev_get_next(NULL);
if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) break;
indev = lv_indev_get_next(indev);
}
if(indev == NULL) return;
}
int32_t st = lv_obj_get_scroll_top(obj);
int32_t sb = lv_obj_get_scroll_bottom(obj);
int32_t sl = lv_obj_get_scroll_left(obj);
int32_t sr = lv_obj_get_scroll_right(obj);
lv_dir_t dir = lv_obj_get_scroll_dir(obj);
bool ver_draw = false;
if((dir & LV_DIR_VER) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
ver_draw = true;
}
bool hor_draw = false;
if((dir & LV_DIR_HOR) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
hor_draw = true;
}
if(!hor_draw && !ver_draw) return;
bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL;
int32_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
int32_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
int32_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
int32_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
int32_t thickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
int32_t length = lv_obj_get_style_length(obj, LV_PART_SCROLLBAR);
int32_t obj_h = lv_obj_get_height(obj);
int32_t obj_w = lv_obj_get_width(obj);
/*Space required for the vertical and horizontal scrollbars*/
int32_t ver_reg_space = ver_draw ? thickness : 0;
int32_t hor_req_space = hor_draw ? thickness : 0;
int32_t rem;
if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) <= LV_OPA_MIN &&
lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) <= LV_OPA_MIN) {
return;
}
/*Draw vertical scrollbar if the mode is ON or can be scrolled in this direction*/
int32_t content_h = obj_h + st + sb;
if(ver_draw && content_h) {
ver_area->y1 = obj->coords.y1;
ver_area->y2 = obj->coords.y2;
if(rtl) {
ver_area->x1 = obj->coords.x1 + left_space;
ver_area->x2 = ver_area->x1 + thickness - 1;
}
else {
ver_area->x2 = obj->coords.x2 - right_space;
ver_area->x1 = ver_area->x2 - thickness + 1;
}
int32_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
sb_h = LV_MAX(length > 0 ? length : sb_h, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
sb_h = LV_MIN(sb_h, obj_h); /*Limit scrollbar length to parent height*/
rem = (obj_h - top_space - bottom_space - hor_req_space) -
sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
int32_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
if(scroll_h <= 0) {
ver_area->y1 = obj->coords.y1 + top_space;
ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1;
}
else {
int32_t sb_y = (rem * sb) / scroll_h;
sb_y = rem - sb_y;
ver_area->y1 = obj->coords.y1 + sb_y + top_space;
ver_area->y2 = ver_area->y1 + sb_h - 1;
if(ver_area->y1 < obj->coords.y1 + top_space) {
ver_area->y1 = obj->coords.y1 + top_space;
if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2) {
ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
}
}
if(ver_area->y2 > obj->coords.y2 - hor_req_space - bottom_space) {
ver_area->y2 = obj->coords.y2 - hor_req_space - bottom_space;
if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1) {
ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
}
}
}
}
/*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
int32_t content_w = obj_w + sl + sr;
if(hor_draw && content_w) {
hor_area->y2 = obj->coords.y2 - bottom_space;
hor_area->y1 = hor_area->y2 - thickness + 1;
hor_area->x1 = obj->coords.x1;
hor_area->x2 = obj->coords.x2;
int32_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
sb_w = LV_MAX(length > 0 ? length : sb_w, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
sb_w = LV_MIN(sb_w, obj_w); /*Limit scrollbar length to parent width*/
rem = (obj_w - left_space - right_space - ver_reg_space) -
sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
int32_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
if(scroll_w <= 0) {
if(rtl) {
hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space - 1;
hor_area->x2 = obj->coords.x2 - right_space;
}
else {
hor_area->x1 = obj->coords.x1 + left_space;
hor_area->x2 = obj->coords.x2 - right_space - ver_reg_space - 1;
}
}
else {
int32_t sb_x = (rem * sr) / scroll_w;
sb_x = rem - sb_x;
if(rtl) {
hor_area->x1 = obj->coords.x1 + sb_x + left_space + ver_reg_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
if(hor_area->x1 < obj->coords.x1 + left_space + ver_reg_space) {
hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
}
if(hor_area->x2 > obj->coords.x2 - right_space) {
hor_area->x2 = obj->coords.x2 - right_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
}
else {
hor_area->x1 = obj->coords.x1 + sb_x + left_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
if(hor_area->x1 < obj->coords.x1 + left_space) {
hor_area->x1 = obj->coords.x1 + left_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
}
if(hor_area->x2 > obj->coords.x2 - ver_reg_space - right_space) {
hor_area->x2 = obj->coords.x2 - ver_reg_space - right_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
}
}
}
}
void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
{
lv_area_t hor_area;
lv_area_t ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
}
void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the bottom side is not remains scrolled in*/
/*With snapping the content can't be scrolled in*/
if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
int32_t st = lv_obj_get_scroll_top(obj);
int32_t sb = lv_obj_get_scroll_bottom(obj);
if(sb < 0 && st > 0) {
sb = LV_MIN(st, -sb);
lv_obj_scroll_by(obj, 0, sb, anim_en);
}
}
if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
int32_t sl = lv_obj_get_scroll_left(obj);
int32_t sr = lv_obj_get_scroll_right(obj);
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
/*Be sure the left side is not remains scrolled in*/
if(sr < 0 && sl > 0) {
sr = LV_MIN(sl, -sr);
lv_obj_scroll_by(obj, sr, 0, anim_en);
}
}
else {
/*Be sure the right side is not remains scrolled in*/
if(sl < 0 && sr > 0) {
sr = LV_MIN(sr, -sl);
lv_obj_scroll_by(obj, sl, 0, anim_en);
}
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scroll_x_anim(void * obj, int32_t v)
{
lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
}
static void scroll_y_anim(void * obj, int32_t v)
{
lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
}
static void scroll_end_cb(lv_anim_t * a)
{
/*Do not sent END event if there wasn't a BEGIN*/
if(a->start_cb_called) lv_obj_send_event(a->var, LV_EVENT_SCROLL_END, NULL);
}
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
lv_anim_enable_t anim_en)
{
lv_obj_t * parent = lv_obj_get_parent(child);
if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
int32_t snap_goal = 0;
int32_t act = 0;
const lv_area_t * area_tmp;
int32_t y_scroll = 0;
lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
int32_t stop = lv_obj_get_style_space_top(parent, LV_PART_MAIN);
int32_t sbottom = lv_obj_get_style_space_bottom(parent, LV_PART_MAIN);
int32_t top_diff = parent->coords.y1 + stop - area_tmp->y1 - scroll_value->y;
int32_t bottom_diff = -(parent->coords.y2 - sbottom - area_tmp->y2 - scroll_value->y);
int32_t parent_h = lv_obj_get_height(parent) - stop - sbottom;
if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
else if(top_diff > 0) {
y_scroll = top_diff;
/*Do not let scrolling in*/
int32_t st = lv_obj_get_scroll_top(parent);
if(st - y_scroll < 0) y_scroll = 0;
}
else if(bottom_diff > 0) {
y_scroll = -bottom_diff;
/*Do not let scrolling in*/
int32_t sb = lv_obj_get_scroll_bottom(parent);
if(sb + y_scroll < 0) y_scroll = 0;
}
switch(snap_y) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.y1 + stop;
act = area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.y2 - sbottom;
act = area_tmp->y2 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.y1 + stop + parent_h / 2;
act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_NONE:
break;
}
int32_t x_scroll = 0;
lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
int32_t sleft = lv_obj_get_style_space_left(parent, LV_PART_MAIN);
int32_t sright = lv_obj_get_style_space_right(parent, LV_PART_MAIN);
int32_t left_diff = parent->coords.x1 + sleft - area_tmp->x1 - scroll_value->x;
int32_t right_diff = -(parent->coords.x2 - sright - area_tmp->x2 - scroll_value->x);
if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
else if(left_diff > 0) {
x_scroll = left_diff;
/*Do not let scrolling in*/
int32_t sl = lv_obj_get_scroll_left(parent);
if(sl - x_scroll < 0) x_scroll = 0;
}
else if(right_diff > 0) {
x_scroll = -right_diff;
/*Do not let scrolling in*/
int32_t sr = lv_obj_get_scroll_right(parent);
if(sr + x_scroll < 0) x_scroll = 0;
}
int32_t parent_w = lv_obj_get_width(parent) - sleft - sright;
switch(snap_x) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.x1 + sleft;
act = area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.x2 - sright;
act = area_tmp->x2 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.x1 + sleft + parent_w / 2;
act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_NONE:
break;
}
/*Remove any pending scroll animations.*/
lv_anim_delete(parent, scroll_y_anim);
lv_anim_delete(parent, scroll_x_anim);
if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
scroll_value->x += anim_en ? x_scroll : 0;
scroll_value->y += anim_en ? y_scroll : 0;
lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
}

View File

@@ -0,0 +1,301 @@
/**
* @file lv_obj_scroll.h
*
*/
#ifndef LV_OBJ_SCROLL_H
#define LV_OBJ_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
/** Scrollbar modes: shows when should the scrollbars be visible*/
typedef enum {
LV_SCROLLBAR_MODE_OFF, /**< Never show scrollbars*/
LV_SCROLLBAR_MODE_ON, /**< Always show scrollbars*/
LV_SCROLLBAR_MODE_ACTIVE, /**< Show scroll bars when Widget is being scrolled*/
LV_SCROLLBAR_MODE_AUTO, /**< Show scroll bars when the content is large enough to be scrolled*/
} lv_scrollbar_mode_t;
/** Scroll span align options. Tells where to align the snappable children when scroll stops.*/
typedef enum {
LV_SCROLL_SNAP_NONE, /**< Do not align, leave where it is*/
LV_SCROLL_SNAP_START, /**< Align to the left/top*/
LV_SCROLL_SNAP_END, /**< Align to the right/bottom*/
LV_SCROLL_SNAP_CENTER /**< Align to the center*/
} lv_scroll_snap_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set how the scrollbars should behave.
* @param obj pointer to Widget
* @param mode LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
*/
void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode);
/**
* Set direction Widget can be scrolled
* @param obj pointer to Widget
* @param dir one or more bit-wise OR-ed values of `lv_dir_t` enumeration
*/
void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir);
/**
* Set where to snap the children when scrolling ends horizontally
* @param obj pointer to Widget
* @param align value from `lv_scroll_snap_t` enumeration
*/
void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align);
/**
* Set where to snap the children when scrolling ends vertically
* @param obj pointer to Widget
* @param align value from `lv_scroll_snap_t` enumeration
*/
void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align);
/*=====================
* Getter functions
*====================*/
/**
* Get the current scroll mode (when to hide the scrollbars)
* @param obj pointer to Widget
* @return the current scroll mode from `lv_scrollbar_mode_t`
*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj);
/**
* Get directions Widget can be scrolled (set with `lv_obj_set_scroll_dir()`)
* @param obj pointer to Widget
* @return current scroll direction bit(s)
*/
lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj);
/**
* Get where to snap child Widgets when horizontal scrolling ends.
* @param obj pointer to Widget
* @return current snap value from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj);
/**
* Get where to snap child Widgets when vertical scrolling ends.
* @param obj pointer to Widget
* @return current snap value from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj);
/**
* Get current X scroll position. Identical to `lv_obj_get_scroll_left()`.
* @param obj pointer to scrollable container Widget
* @return current scroll position from left edge
* - If Widget is not scrolled return 0.
* - If scrolled return > 0.
* - If scrolled inside (elastic scroll) return < 0.
*/
int32_t lv_obj_get_scroll_x(const lv_obj_t * obj);
/**
* Get current Y scroll position. Identical to `lv_obj_get_scroll_top()`.
* @param obj pointer to scrollable container Widget
* @return current scroll position from top edge
* - If Widget is not scrolled return 0.
* - If scrolled return > 0.
* - If scrolled inside (elastic scroll) return < 0.
*/
int32_t lv_obj_get_scroll_y(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled down
* before its top edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged below its normal top-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled down before its top edge appears
*/
int32_t lv_obj_get_scroll_top(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled up
* before its bottom edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged above its normal bottom-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled up before its bottom edge appears
*/
int32_t lv_obj_get_scroll_bottom(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled right
* before its left edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged farther right than its normal left-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled right before its left edge appears
*/
int32_t lv_obj_get_scroll_left(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled left
* before its right edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged farther left than its normal right-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled left before its right edge appears
*/
int32_t lv_obj_get_scroll_right(const lv_obj_t * obj);
/**
* Get the X and Y coordinates where the scrolling will end for Widget if a scrolling animation is in progress.
* If no scrolling animation, give the current `x` or `y` scroll position.
* @param obj pointer to scrollable Widget
* @param end pointer to `lv_point_t` object in which to store result
*/
void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end);
/*=====================
* Other functions
*====================*/
/**
* Scroll by given amount of pixels.
* @param obj pointer to scrollable Widget to scroll
* @param dx pixels to scroll horizontally
* @param dy pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
* @note > 0 value means scroll right/bottom (show the more content on the right/bottom)
* @note e.g. dy = -20 means scroll down 20 px
*/
void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en);
/**
* Scroll by given amount of pixels.
* `dx` and `dy` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param dx pixels to scroll horizontally
* @param dy pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
* @note e.g. dy = -20 means scroll down 20 px
*/
void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en);
/**
* Scroll to given coordinate on Widget.
* `x` and `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en);
/**
* Scroll to X coordinate on Widget.
* `x` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param x pixels to scroll horizontally
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en);
/**
* Scroll to Y coordinate on Widget.
* `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en);
/**
* Scroll `obj`'s parent Widget until `obj` becomes visible.
* @param obj pointer to Widget to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Scroll `obj`'s parent Widgets recursively until `obj` becomes visible.
* Widget will be scrolled into view even it has nested scrollable parents.
* @param obj pointer to Widget to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Tell whether Widget is being scrolled or not at this moment
* @param obj pointer to Widget
* @return true: `obj` is being scrolled
*/
bool lv_obj_is_scrolling(const lv_obj_t * obj);
/**
* Stop scrolling the current object
*
* @param obj The object being scrolled
*/
void lv_obj_stop_scroll_anim(const lv_obj_t * obj);
/**
* Check children of `obj` and scroll `obj` to fulfill scroll_snap settings.
* @param obj Widget whose children need to be checked and snapped
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Get the area of the scrollbars
* @param obj pointer to Widget
* @param hor pointer to store the area of the horizontal scrollbar
* @param ver pointer to store the area of the vertical scrollbar
*/
void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
/**
* Invalidate the area of the scrollbars
* @param obj pointer to Widget
*/
void lv_obj_scrollbar_invalidate(lv_obj_t * obj);
/**
* Checks if the content is scrolled "in" and adjusts it to a normal position.
* @param obj pointer to Widget
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_H*/

View File

@@ -0,0 +1,50 @@
/**
* @file lv_obj_scroll_private.h
*
*/
#ifndef LV_OBJ_SCROLL_PRIVATE_H
#define LV_OBJ_SCROLL_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Low level function to scroll by given x and y coordinates.
* `LV_EVENT_SCROLL` is sent.
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @return `LV_RESULT_INVALID`: to object was deleted in `LV_EVENT_SCROLL`;
* `LV_RESULT_OK`: if the object is still valid
*/
lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_PRIVATE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,443 @@
/**
* @file lv_obj_style.h
*
*/
#ifndef LV_OBJ_STYLE_H
#define LV_OBJ_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_bidi.h"
#include "../misc/lv_style.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Possible states of a widget.
* OR-ed values are possible
*/
typedef enum {
LV_STATE_DEFAULT = 0,
LV_STATE_ALT = 1 << 0,
/*1 reserved*/
LV_STATE_CHECKED = 1 << 2,
LV_STATE_FOCUSED = 1 << 3,
LV_STATE_FOCUS_KEY = 1 << 4,
LV_STATE_EDITED = 1 << 5,
LV_STATE_HOVERED = 1 << 6,
LV_STATE_PRESSED = 1 << 7,
LV_STATE_SCROLLED = 1 << 8,
LV_STATE_DISABLED = 1 << 9,
/*2 reserved*/
LV_STATE_USER_1 = 1 << 12,
LV_STATE_USER_2 = 1 << 13,
LV_STATE_USER_3 = 1 << 14,
LV_STATE_USER_4 = 1 << 15,
LV_STATE_ANY = 0xFFFF, /**< Special value can be used in some functions to target all states*/
} lv_state_t;
/**
* The possible parts of widgets.
* The parts can be considered as the internal building block of the widgets.
* E.g. slider = background + indicator + knob
* Not all parts are used by every widget
*/
typedef enum {
LV_PART_MAIN = 0x000000, /**< A background like rectangle*/
LV_PART_SCROLLBAR = 0x010000, /**< The scrollbar(s)*/
LV_PART_INDICATOR = 0x020000, /**< Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox*/
LV_PART_KNOB = 0x030000, /**< Like handle to grab to adjust the value*/
LV_PART_SELECTED = 0x040000, /**< Indicate the currently selected option or section*/
LV_PART_ITEMS = 0x050000, /**< Used if the widget has multiple similar elements (e.g. table cells)*/
LV_PART_CURSOR = 0x060000, /**< Mark a specific place e.g. for text area's cursor or on a chart*/
LV_PART_CUSTOM_FIRST = 0x080000, /**< Extension point for custom widgets*/
LV_PART_ANY = 0x0F0000, /**< Special value can be used in some functions to target all parts*/
} lv_part_t;
typedef enum {
LV_STYLE_STATE_CMP_SAME, /**< The style properties in the 2 states are identical */
LV_STYLE_STATE_CMP_DIFF_REDRAW, /**< The differences can be shown with a simple redraw */
LV_STYLE_STATE_CMP_DIFF_DRAW_PAD, /**< The differences can be shown with a simple redraw */
LV_STYLE_STATE_CMP_DIFF_LAYOUT, /**< The differences can be shown with a simple redraw */
} lv_style_state_cmp_t;
/**
* A joint type for `lv_part_t` and `lv_state_t`. Example values
* - `0`: means `LV_PART_MAIN | LV_STATE_DEFAULT`
* - `LV_STATE_PRSSED`
* - `LV_PART_KNOB`
* - `LV_PART_KNOB | LV_STATE_PRESSED | LV_STATE_CHECKED`
*/
typedef uint32_t lv_style_selector_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add a style to an object.
* @param obj pointer to an object
* @param style pointer to a style to add
* @param selector OR-ed value of parts and state to which the style should be added
*
* Examples:
* @code
* lv_obj_add_style(btn, &style_btn, 0); //Default button style
*
* lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); //Overwrite only some colors to red when pressed
* @endcode
*/
void lv_obj_add_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
/**
* Replaces a style of an object, preserving the order of the style stack (local styles and transitions are ignored).
* @param obj pointer to an object
* @param old_style pointer to a style to replace.
* @param new_style pointer to a style to replace the old style with.
* @param selector OR-ed values of states and a part to replace only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
*
* Examples:
* @code
* lv_obj_replace_style(obj, &yellow_style, &blue_style, LV_PART_ANY | LV_STATE_ANY); //Replace a specific style
*
* lv_obj_replace_style(obj, &yellow_style, &blue_style, LV_PART_MAIN | LV_STATE_PRESSED); //Replace a specific style assigned to the main part when it is pressed
* @endcode
*/
bool lv_obj_replace_style(lv_obj_t * obj, const lv_style_t * old_style, const lv_style_t * new_style,
lv_style_selector_t selector);
/**
* Remove a style from an object.
* @param obj pointer to an object
* @param style pointer to a style to remove. Can be NULL to check only the selector
* @param selector OR-ed values of states and a part to remove only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
*
* Examples:
* @code
* lv_obj_remove_style(obj, &style, LV_PART_ANY | LV_STATE_ANY); //Remove a specific style
*
* lv_obj_remove_style(obj, NULL, LV_PART_MAIN | LV_STATE_ANY); //Remove all styles from the main part
*
* lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY); //Remove all styles
* @endcode
*/
void lv_obj_remove_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
/**
* Remove all styles added by a theme from a widget
* @param selector OR-ed values of states and a part to remove only styles with matching selectors.
* LV_STATE_ANY and LV_PART_ANY can be used
* @param obj pointer to a widget
*/
void lv_obj_remove_theme(lv_obj_t * obj, lv_style_selector_t selector);
/**
* Remove all styles from an object
* @param obj pointer to an object
*/
void lv_obj_remove_style_all(lv_obj_t * 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_change(lv_style_t * style);
/**
* Notify an object and its children about its style is modified.
* @param obj pointer to an object
* @param part the part whose style was changed. E.g. `LV_PART_ANY`, `LV_PART_MAIN`
* @param prop `LV_STYLE_PROP_ANY` or an `LV_STYLE_...` property.
* It is used to optimize what needs to be refreshed.
* `LV_STYLE_PROP_INV` to perform only a style cache update
*/
void lv_obj_refresh_style(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Temporary disable a style for a selector. It will look like is the style wasn't added
* @param obj pointer to an object
* @param style pointer to a style
* @param selector the selector of a style (e.g. LV_STATE_PRESSED | LV_PART_KNOB)
* @param dis true: disable the style, false: enable the style
*/
void lv_obj_style_set_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, bool dis);
/**
* Get if a given style is disabled on an object.
* @param obj pointer to an object
* @param style pointer to a style
* @param selector the selector of a style (e.g. LV_STATE_PRESSED | LV_PART_KNOB)
* @return true: disable the style, false: enable the style
*/
bool lv_obj_style_get_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
/**
* Enable or disable automatic style refreshing when a new style is added/removed to/from an object
* or any other style change happens.
* @param en true: enable refreshing; false: disable refreshing
*/
void lv_obj_enable_style_refresh(bool en);
/**
* Get the value of a style property. The current state of the object will be considered.
* Inherited properties will be inherited.
* If a property is not set a default value will be returned.
* @param obj pointer to an object
* @param part a part from which the property should be get
* @param prop the property to get
* @return the value of the property.
* Should be read from the correct field of the `lv_style_value_t` according to the type of the property.
*/
lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Check if an object has a specified style property for a given style selector.
* @param obj pointer to an object
* @param selector the style selector to be checked, defining the scope of the style to be examined.
* @param prop the property to be checked.
* @return true if the object has the specified selector and property, false otherwise.
*/
bool lv_obj_has_style_prop(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop);
/**
* Set local style property on an object's part and state.
* @param obj pointer to an object
* @param prop the property
* @param value value of the property. The correct element should be set according to the type of the property
* @param selector OR-ed value of parts and state for which the style should be set
*/
void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
lv_style_selector_t selector);
lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
lv_style_selector_t selector);
/**
* Remove a local style property from a part of an object with a given state.
* @param obj pointer to an object
* @param prop a style property to remove.
* @param selector OR-ed value of parts and state for which the style should be removed
* @return true the property was found and removed; false: the property was not found
*/
bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector);
/**
* Used internally for color filtering
*/
lv_style_value_t lv_obj_style_apply_color_filter(const lv_obj_t * obj, lv_part_t part, lv_style_value_t v);
/**
* Fade in an an object and all its children.
* @param obj the object to fade in
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay);
/**
* Fade out an an object and all its children.
* @param obj the object to fade out
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay);
static inline lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
{
return (lv_state_t)(selector & 0xFFFF);
}
static inline lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
{
return (lv_part_t)(selector & 0xFF0000);
}
#include "lv_obj_style_gen.h"
static inline void lv_obj_set_style_pad_all(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_hor(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
}
static inline void lv_obj_set_style_pad_ver(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_margin_all(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_margin_left(obj, value, selector);
lv_obj_set_style_margin_right(obj, value, selector);
lv_obj_set_style_margin_top(obj, value, selector);
lv_obj_set_style_margin_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_margin_hor(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_margin_left(obj, value, selector);
lv_obj_set_style_margin_right(obj, value, selector);
}
static inline void lv_obj_set_style_margin_ver(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_margin_top(obj, value, selector);
lv_obj_set_style_margin_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_row(obj, value, selector);
lv_obj_set_style_pad_column(obj, value, selector);
}
static inline void lv_obj_set_style_size(lv_obj_t * obj, int32_t width, int32_t height,
lv_style_selector_t selector)
{
lv_obj_set_style_width(obj, width, selector);
lv_obj_set_style_height(obj, height, selector);
}
static inline void lv_obj_set_style_transform_scale(lv_obj_t * obj, int32_t value,
lv_style_selector_t selector)
{
lv_obj_set_style_transform_scale_x(obj, value, selector);
lv_obj_set_style_transform_scale_y(obj, value, selector);
}
static inline int32_t lv_obj_get_style_space_left(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_left(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_LEFT) ? padding + border_width : padding;
}
static inline int32_t lv_obj_get_style_space_right(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_right(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_RIGHT) ? padding + border_width : padding;
}
static inline int32_t lv_obj_get_style_space_top(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_top(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_TOP) ? padding + border_width : padding;
}
static inline int32_t lv_obj_get_style_space_bottom(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_bottom(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_BOTTOM) ? padding + border_width : padding;
}
lv_text_align_t lv_obj_calculate_style_text_align(const lv_obj_t * obj, lv_part_t part, const char * txt);
static inline int32_t lv_obj_get_style_transform_scale_x_safe(const lv_obj_t * obj, lv_part_t part)
{
int32_t scale = lv_obj_get_style_transform_scale_x(obj, part);
return scale > 0 ? scale : 1;
}
static inline int32_t lv_obj_get_style_transform_scale_y_safe(const lv_obj_t * obj, lv_part_t part)
{
int32_t scale = lv_obj_get_style_transform_scale_y(obj, part);
return scale > 0 ? scale : 1;
}
/**
* Get the `opa` style property from all parents and multiply and `>> 8` them.
* @param obj the object whose opacity should be get
* @param part the part whose opacity should be get. Non-MAIN parts will consider the `opa` of the MAIN part too
* @return the final opacity considering the parents' opacity too
*/
lv_opa_t lv_obj_get_style_opa_recursive(const lv_obj_t * obj, lv_part_t part);
/**
* Apply recolor effect to the input color based on the object's style properties.
* @param obj the target object containing recolor style properties
* @param part the part to retrieve recolor styles.
* @param color the original color to be modified
* @return the blended color after applying recolor and opacity
*/
lv_color32_t lv_obj_style_apply_recolor(const lv_obj_t * obj, lv_part_t part, lv_color32_t color);
/**
* Get the `recolor` style property from all parents and blend them recursively.
* @param obj the object whose recolor value should be retrieved
* @param part the target part to check. Non-MAIN parts will also consider
* the `recolor` value from the MAIN part during calculation
* @return the final blended recolor value combining all parent's recolor values
*/
lv_color32_t lv_obj_get_style_recolor_recursive(const lv_obj_t * obj, lv_part_t part);
#if LV_USE_OBSERVER
/**
* Disable a style if a subject's value is not equal to a reference value
* @param obj pointer to Widget
* @param style pointer to a style
* @param selector pointer to a selector
* @param subject pointer to Subject
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector,
lv_subject_t * subject, int32_t ref_value);
/**
* Connect a subject's value to a style property of a widget.
* @param obj pointer to a Widget
* @param prop a style property
* @param selector a selector for which the property should be added, e.g. `LV_PART_KNOB | LV_STATE_PRESSED`
* @param subject pointer a Subject to which value the property should be bound
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector,
lv_subject_t * subject);
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_STYLE_H*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
/**
* @file lv_obj_style_private.h
*
*/
#ifndef LV_OBJ_STYLE_PRIVATE_H
#define LV_OBJ_STYLE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_style_t {
const lv_style_t * style;
uint32_t selector : 24;
uint32_t is_local : 1;
uint32_t is_trans : 1;
uint32_t is_disabled : 1;
uint32_t is_theme : 1; /**< The style is added by a theme */
};
struct _lv_obj_style_transition_dsc_t {
uint16_t time;
uint16_t delay;
lv_style_selector_t selector;
lv_style_prop_t prop;
lv_anim_path_cb_t path_cb;
void * user_data;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the object related style manager module.
* Called by LVGL in `lv_init()`
*/
void lv_obj_style_init(void);
/**
* Deinitialize the object related style manager module.
* Called by LVGL in `lv_deinit()`
*/
void lv_obj_style_deinit(void);
/**
* Used internally to create a style transition
* @param obj
* @param part
* @param prev_state
* @param new_state
* @param tr
*/
void lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state,
lv_state_t new_state, const lv_obj_style_transition_dsc_t * tr);
/**
* Used internally to compare the appearance of an object in 2 states
* @param obj
* @param state1
* @param state2
* @return
*/
lv_style_state_cmp_t lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
/**
* Update the layer type of a widget bayed on its current styles.
* The result will be stored in `obj->spec_attr->layer_type`
* @param obj the object whose layer should be updated
*/
void lv_obj_update_layer_type(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_STYLE_PRIVATE_H*/

View File

@@ -0,0 +1,854 @@
/**
* @file lv_obj_tree.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_private.h"
#include "lv_obj_class_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_private.h"
#include "../display/lv_display.h"
#include "../display/lv_display_private.h"
#include "../misc/lv_anim_private.h"
#include "../misc/lv_async.h"
#include "../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
#define disp_ll_p &(LV_GLOBAL_DEFAULT()->disp_ll)
#define OBJ_DUMP_STRING_LEN 128
#define LV_OBJ_NAME_MAX_LEN 128
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_delete_async_cb(void * obj);
static void obj_delete_core(lv_obj_t * obj);
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
static void dump_tree_core(lv_obj_t * obj, int32_t depth);
static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj);
#if LV_USE_OBJ_NAME
static lv_obj_t * find_by_name_direct(const lv_obj_t * parent, const char * name, size_t len);
#endif /*LV_USE_OBJ_NAME*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_delete(lv_obj_t * obj)
{
if(obj->is_deleting)
return;
LV_LOG_TRACE("begin (delete %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * par = lv_obj_get_parent(obj);
lv_display_t * disp = NULL;
bool act_screen_del = false;
if(par == NULL) {
disp = lv_obj_get_display(obj);
if(!disp) return; /*Shouldn't happen*/
if(disp->act_scr == obj) act_screen_del = true;
}
obj_delete_core(obj);
/*Call the ancestor's event handler to the parent to notify it about the child delete*/
if(par && !par->is_deleting) {
lv_obj_scrollbar_invalidate(par);
lv_obj_send_event(par, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_send_event(par, LV_EVENT_CHILD_DELETED, NULL);
}
/*Handle if the active screen was deleted*/
if(act_screen_del) {
LV_LOG_WARN("the active screen was deleted");
disp->act_scr = NULL;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", (void *)obj);
}
void lv_obj_clean(lv_obj_t * obj)
{
LV_LOG_TRACE("begin (clean %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
uint32_t cnt = lv_obj_get_child_count(obj);
lv_obj_t * child = lv_obj_get_first_not_deleting_child(obj);
while(child) {
obj_delete_core(child);
child = lv_obj_get_first_not_deleting_child(obj);
}
/*Just to remove scroll animations if any*/
lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
if(obj->spec_attr) {
obj->spec_attr->scroll.x = 0;
obj->spec_attr->scroll.y = 0;
}
if(lv_obj_get_child_count(obj) < cnt) {
lv_obj_send_event(obj, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_send_event(obj, LV_EVENT_CHILD_DELETED, NULL);
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (clean %p)", (void *)obj);
}
void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a, NULL);
lv_anim_set_duration(&a, 1);
lv_anim_set_delay(&a, delay_ms);
lv_anim_set_completed_cb(&a, lv_obj_delete_anim_completed_cb);
lv_anim_start(&a);
}
void lv_obj_delete_anim_completed_cb(lv_anim_t * a)
{
lv_obj_delete(a->var);
}
void lv_obj_delete_async(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_async_call(lv_obj_delete_async_cb, obj);
}
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_OBJ(parent, MY_CLASS);
if(obj->parent == NULL) {
LV_LOG_WARN("Can't set the parent of a screen");
return;
}
if(parent == NULL) {
LV_LOG_WARN("Can't set parent == NULL to an object");
return;
}
if(parent == obj->parent) {
return;
}
lv_obj_invalidate(obj);
lv_obj_allocate_spec_attr(parent);
lv_obj_t * old_parent = obj->parent;
/*Remove the object from the old parent's child list*/
int32_t i;
for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_count(old_parent) - 2; i++) {
old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
}
old_parent->spec_attr->child_cnt--;
if(old_parent->spec_attr->child_cnt) {
old_parent->spec_attr->children = lv_realloc(old_parent->spec_attr->children,
old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
}
else {
lv_free(old_parent->spec_attr->children);
old_parent->spec_attr->children = NULL;
}
/*Add the child to the new parent as the last (newest child)*/
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
parent->spec_attr->children[lv_obj_get_child_count(parent) - 1] = obj;
obj->parent = parent;
/*Notify the original parent because one of its children is lost*/
lv_obj_scrollbar_invalidate(old_parent);
lv_obj_send_event(old_parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(old_parent, LV_EVENT_CHILD_DELETED, NULL);
/*Notify the new parent about the child*/
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, NULL);
lv_obj_mark_layout_as_dirty(obj);
lv_obj_invalidate(obj);
}
void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/* Check parent validity */
lv_obj_t * parent = lv_obj_get_parent(obj);
if(!parent) {
LV_LOG_WARN("parent is NULL");
return;
}
const uint32_t parent_child_count = lv_obj_get_child_count(parent);
/* old_index only can be 0 or greater, this point cannot be reached if the parent is not null */
const int32_t old_index = lv_obj_get_index(obj);
LV_ASSERT(0 <= old_index);
if(index < 0) {
index += parent_child_count;
}
/* Index was negative and the absolute value is greater than parent child count */
if((index < 0)
/* Index is same or bigger than parent child count */
|| (index >= (int32_t) parent_child_count)
/* If both previous and new index are the same */
|| (index == old_index)) {
return;
}
int32_t i = old_index;
if(index < old_index) {
while(i > index) {
parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
i--;
}
}
else {
while(i < index) {
parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
i++;
}
}
parent->spec_attr->children[index] = obj;
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_invalidate(parent);
}
void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
{
LV_ASSERT_OBJ(obj1, MY_CLASS);
LV_ASSERT_OBJ(obj2, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj1);
lv_obj_t * parent2 = lv_obj_get_parent(obj2);
uint_fast32_t index1 = lv_obj_get_index(obj1);
uint_fast32_t index2 = lv_obj_get_index(obj2);
lv_obj_send_event(parent2, LV_EVENT_CHILD_DELETED, obj2);
lv_obj_send_event(parent, LV_EVENT_CHILD_DELETED, obj1);
parent->spec_attr->children[index1] = obj2;
obj2->parent = parent;
parent2->spec_attr->children[index2] = obj1;
obj1->parent = parent2;
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj2);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj2);
lv_obj_send_event(parent2, LV_EVENT_CHILD_CHANGED, obj1);
lv_obj_send_event(parent2, LV_EVENT_CHILD_CREATED, obj1);
lv_obj_invalidate(parent);
if(parent != parent2) {
lv_obj_invalidate(parent2);
}
lv_group_swap_obj(obj1, obj2);
}
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * par = obj;
const lv_obj_t * act_par;
do {
act_par = par;
par = lv_obj_get_parent(act_par);
} while(par != NULL);
return (lv_obj_t *)act_par;
}
lv_display_t * lv_obj_get_display(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * scr;
if(obj->parent == NULL) scr = obj; /*`obj` is a screen*/
else scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
lv_display_t * d;
lv_ll_t * disp_head = disp_ll_p;
LV_LL_READ(disp_head, d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
if(d->screens[i] == scr) return d;
}
}
LV_LOG_WARN("No screen found");
return NULL;
}
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
{
if(obj == NULL) return NULL;
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->parent;
}
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
uint32_t idu;
if(idx < 0) {
idx = obj->spec_attr->child_cnt + idx;
if(idx < 0) return NULL;
idu = (uint32_t) idx;
}
else {
idu = idx;
}
if(idu >= obj->spec_attr->child_cnt) return NULL;
else return obj->spec_attr->children[idx];
}
lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
int32_t i;
int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
if(idx >= 0) {
for(i = 0; i < cnt; i++) {
if(obj->spec_attr->children[i]->class_p == class_p) {
if(idx == 0) return obj->spec_attr->children[i];
else idx--;
}
}
}
else {
idx++; /*-1 means the first child*/
for(i = cnt - 1; i >= 0; i--) {
if(obj->spec_attr->children[i]->class_p == class_p) {
if(idx == 0) return obj->spec_attr->children[i];
else idx++;
}
}
}
return NULL;
}
lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx)
{
lv_obj_t * parent = lv_obj_get_parent(obj);
int32_t sibling_idx = (int32_t)lv_obj_get_index(obj) + idx;
if(sibling_idx < 0) return NULL;
return lv_obj_get_child(parent, sibling_idx);
}
lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
int32_t sibling_idx = (int32_t)lv_obj_get_index_by_type(obj, class_p) + idx;
if(sibling_idx < 0) return NULL;
return lv_obj_get_child_by_type(parent, sibling_idx, class_p);
}
uint32_t lv_obj_get_child_count(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->child_cnt;
}
uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return 0;
uint32_t i;
uint32_t cnt = 0;
for(i = 0; i < obj->spec_attr->child_cnt; i++) {
if(obj->spec_attr->children[i]->class_p == class_p) cnt++;
}
return cnt;
}
#if LV_USE_OBJ_NAME
void lv_obj_set_name(lv_obj_t * obj, const char * name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(!obj->spec_attr->name_static && obj->spec_attr->name) lv_free((void *)obj->spec_attr->name);
if(name == NULL) {
obj->spec_attr->name = NULL;
obj->spec_attr->name_static = 1;
}
else {
obj->spec_attr->name = lv_strdup(name);
obj->spec_attr->name_static = 0;
}
}
void lv_obj_set_name_static(lv_obj_t * obj, const char * name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(!obj->spec_attr->name_static && obj->spec_attr->name) lv_free((void *)obj->spec_attr->name);
obj->spec_attr->name = name;
obj->spec_attr->name_static = 1;
}
const char * lv_obj_get_name(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
else return obj->spec_attr->name;
}
void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const char * name = lv_obj_get_name(obj);
/*Use a default name which auto-indexing*/
char name_buf[LV_OBJ_NAME_MAX_LEN];
if(name == NULL) {
lv_snprintf(name_buf, sizeof(name_buf), "%s_#", obj->class_p->name);
name = name_buf;
}
size_t name_len = lv_strlen(name);
lv_obj_t * parent = lv_obj_get_parent(obj);
/*If the last character is # automatically index the children with the same name start*/
if(parent && name_len > 0 && name[name_len - 1] == '#') {
uint32_t child_cnt = lv_obj_get_child_count(parent);
uint32_t cnt = 0;
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(parent, i);
/*All siblings older siblings are checked, craft the name of this widget*/
if(child == obj) {
char num_buf[8];
size_t num_len;
num_len = lv_snprintf(num_buf, sizeof(num_buf), "%d", cnt);
/*Is there enough space for the name and the index?*/
if(buf_size > name_len + num_len) {
/*E.g. buf = "some_name_", so trim the # from the end*/
lv_strncpy(buf, name, name_len - 1);
lv_strcpy(&buf[name_len - 1], num_buf);
}
else {
/*Use the name as it is as a fallback*/
lv_strlcpy(buf, obj->spec_attr->name, buf_size);
}
break;
}
/*Check the older siblings. IF they start with the same name count them*/
else {
const char * child_name = lv_obj_get_name(child);
if(child_name == NULL) {
/*If the name we are looking for start with the child's class name
*increment the index. E.g. <class_name>_#*/
size_t class_name_len = lv_strlen(child->class_p->name);
if(name_len > 3 && class_name_len == name_len - 2 &&
lv_strncmp(child->class_p->name, name, class_name_len) == 0) {
cnt++;
}
}
/*The name is set, check if it's e.g. <some_name>#*/
else {
if(lv_strcmp(child->spec_attr->name, name) == 0) {
cnt++;
}
}
}
}
}
else {
/*Just use the set name*/
lv_strlcpy(buf, obj->spec_attr->name, buf_size);
}
}
lv_obj_t * lv_obj_get_child_by_name(const lv_obj_t * parent, const char * path)
{
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent == NULL || parent->spec_attr == NULL || path == NULL) return NULL;
while(*path) {
const char * segment = path;
uint32_t len = 0;
/* Calculate the length of the current segment */
while(path[len] && path[len] != '/')
len++;
/* Look for a child whose resolved name exactly matches the segment */
lv_obj_t * child = find_by_name_direct(parent, segment, len);
if(!child) return NULL; /*Segment not found*/
/* Advance to the next segment */
path += len;
if(*path == '/') path++; /* Skip the '/' */
/* If there is no further segment, we've found the target child */
if(*path == '\0') return child;
parent = child;
}
return NULL;
}
lv_obj_t * lv_obj_find_by_name(const lv_obj_t * parent, const char * name)
{
if(parent == NULL) parent = lv_display_get_screen_active(NULL);
if(parent == NULL) return NULL;
lv_obj_t * child = find_by_name_direct(parent, name, UINT16_MAX);
if(child) return child;
/*Search children recursively*/
uint32_t child_cnt = lv_obj_get_child_count(parent);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
child = parent->spec_attr->children[i];
lv_obj_t * found = lv_obj_find_by_name(child, name);
if(found != NULL) return found;
}
return NULL;
}
#endif /*LV_USE_OBJ_NAME*/
int32_t lv_obj_get_index(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return -1;
int32_t i = 0;
for(i = 0; i < parent->spec_attr->child_cnt; i++) {
if(parent->spec_attr->children[i] == obj) return i;
}
/*Shouldn't reach this point*/
LV_ASSERT(0);
return -1;
}
int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return 0xFFFFFFFF;
uint32_t i = 0;
uint32_t idx = 0;
for(i = 0; i < parent->spec_attr->child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
if(child->class_p == class_p) {
if(child == obj) return idx;
idx++;
}
}
/*Can happen if there was no children with the given type*/
return -1;
}
void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
walk_core(start_obj, cb, user_data);
}
void lv_obj_dump_tree(lv_obj_t * start_obj)
{
if(start_obj == NULL) {
lv_display_t * disp = lv_display_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
dump_tree_core(disp->screens[i], 0);
}
disp = lv_display_get_next(disp);
}
}
else {
dump_tree_core(start_obj, 0);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_delete_async_cb(void * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_delete(obj);
}
static void obj_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
{
/* If the input device is already in the release state,
* there is no need to wait for the input device to be released
*/
if(lv_indev_get_state(indev) != LV_INDEV_STATE_RELEASED) {
/*Wait for release to avoid accidentally triggering other obj to be clicked*/
lv_indev_wait_release(indev);
}
/*Reset the input device*/
lv_indev_reset(indev, obj);
}
static void obj_delete_core(lv_obj_t * obj)
{
if(obj->is_deleting)
return;
obj->is_deleting = true;
/*Let the user free the resources used in `LV_EVENT_DELETE`*/
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_DELETE, NULL);
if(res == LV_RESULT_INVALID) {
obj->is_deleting = false;
return;
}
/*Clean registered event_cb*/
if(obj->spec_attr) lv_event_remove_all(&(obj->spec_attr->event_list));
/*Recursively delete the children*/
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_delete_core(child);
child = lv_obj_get_child(obj, 0);
}
lv_group_t * group = lv_obj_get_group(obj);
/*Reset all input devices if the object to delete is used*/
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_POINTER || indev_type == LV_INDEV_TYPE_BUTTON) {
if(indev->pointer.act_obj == obj || indev->pointer.scroll_obj == obj) {
obj_indev_reset(indev, obj);
}
if(indev->pointer.last_pressed == obj) {
indev->pointer.last_pressed = NULL;
}
if(indev->pointer.last_hovered == obj) {
indev->pointer.last_hovered = NULL;
}
}
if(indev->group == group && obj == lv_indev_get_active_obj()) {
obj_indev_reset(indev, obj);
}
indev = lv_indev_get_next(indev);
}
/*Delete all pending async del-s*/
lv_result_t async_cancel_res = LV_RESULT_OK;
while(async_cancel_res == LV_RESULT_OK) {
async_cancel_res = lv_async_call_cancel(lv_obj_delete_async_cb, obj);
}
/*All children deleted. Now clean up the object specific data*/
lv_obj_destruct(obj);
/*Remove the screen for the screen list*/
if(obj->parent == NULL) {
lv_display_t * disp = lv_obj_get_display(obj);
uint32_t i;
/*Find the screen in the list*/
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) break;
}
uint32_t id = i;
for(i = id; i < disp->screen_cnt - 1; i++) {
disp->screens[i] = disp->screens[i + 1];
}
disp->screen_cnt--;
disp->screens = lv_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
}
/*Remove the object from the child list of its parent*/
else {
int32_t id = lv_obj_get_index(obj);
uint16_t i;
for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
}
obj->parent->spec_attr->child_cnt--;
obj->parent->spec_attr->children = lv_realloc(obj->parent->spec_attr->children,
obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
}
/*Free the object itself*/
lv_free(obj);
}
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
if(obj == NULL) {
lv_display_t * disp = lv_display_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
walk_core(disp->screens[i], cb, user_data);
}
disp = lv_display_get_next(disp);
}
return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
}
res = cb(obj, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
uint32_t i;
for(i = 0; i < lv_obj_get_child_count(obj); i++) {
res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
}
}
return LV_OBJ_TREE_WALK_NEXT;
}
static void dump_tree_core(lv_obj_t * obj, int32_t depth)
{
#if LV_USE_LOG
const char * id;
#if LV_USE_OBJ_ID
char buf[OBJ_DUMP_STRING_LEN];
id = lv_obj_stringify_id(obj, buf, sizeof(buf));
if(id == NULL) id = "obj0";
#else
id = "obj0";
#endif
/*id of `obj0` is an invalid id for builtin id*/
LV_LOG_USER("%*sobj:%p, id:%s;", (int)(2 * depth), "", (void *)obj, id);
#endif /*LV_USE_LOG*/
if(obj && obj->spec_attr && obj->spec_attr->child_cnt) {
for(uint32_t i = 0; i < obj->spec_attr->child_cnt; i++) {
dump_tree_core(lv_obj_get_child(obj, i), depth + 1);
}
}
}
static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
int32_t i;
int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
for(i = 0; i < cnt; i++) {
if(!obj->spec_attr->children[i]->is_deleting) {
return obj->spec_attr->children[i];
}
}
return NULL;
}
#if LV_USE_OBJ_NAME
static lv_obj_t * find_by_name_direct(const lv_obj_t * parent, const char * name, size_t len)
{
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(parent);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
char child_name_resolved[LV_OBJ_NAME_MAX_LEN];
lv_obj_get_name_resolved(child, child_name_resolved, sizeof(child_name_resolved));
if(len == UINT16_MAX) {
if(lv_strcmp(child_name_resolved, name) == 0) return child;
}
else {
if(lv_strncmp(child_name_resolved, name, len) == 0 &&
child_name_resolved[len] == '\0') {
return child;
}
}
}
return NULL;
}
#endif /*LV_USE_OBJ_NAME*/

View File

@@ -0,0 +1,321 @@
/**
* @file lv_obj_tree.h
*
*/
#ifndef LV_OBJ_TREE_H
#define LV_OBJ_TREE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_anim.h"
#include "../display/lv_display.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_OBJ_TREE_WALK_NEXT,
LV_OBJ_TREE_WALK_SKIP_CHILDREN,
LV_OBJ_TREE_WALK_END,
} lv_obj_tree_walk_res_t;
typedef lv_obj_tree_walk_res_t (*lv_obj_tree_walk_cb_t)(lv_obj_t *, void *);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Delete an object and all of its children.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETE` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_delete(lv_obj_t * obj);
/**
* Delete all children of an object.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETE` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_clean(lv_obj_t * obj);
/**
* Delete an object after some delay
* @param obj pointer to an object
* @param delay_ms time to wait before delete in milliseconds
*/
void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms);
/**
* 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_delete_anim_completed_cb(lv_anim_t * a);
/**
* Helper function for asynchronously deleting objects.
* Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
* @param obj object to delete
* @see lv_async_call
*/
void lv_obj_delete_async(lv_obj_t * obj);
/**
* Move the parent of an object. The relative coordinates will be kept.
*
* @param obj pointer to an object whose parent needs to be changed
* @param parent pointer to the new parent
*/
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);
/**
* Swap the positions of two objects.
* When used in listboxes, it can be used to sort the listbox items.
* @param obj1 pointer to the first object
* @param obj2 pointer to the second object
*/
void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2);
/**
* moves the object to the given index in its parent.
* When used in listboxes, it can be used to sort the listbox items.
* @param obj pointer to the object to be moved.
* @param index new index in parent. -1 to count from the back
* @note to move to the background: lv_obj_move_to_index(obj, 0)
* @note to move forward (up): lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)
*/
void lv_obj_move_to_index(lv_obj_t * obj, int32_t index);
/**
* Get the screen of an object
* @param obj pointer to an object
* @return pointer to the object's screen
*/
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj);
/**
* Get the display of the object
* @param obj pointer to an object
* @return pointer to the object's display
*/
lv_display_t * lv_obj_get_display(const lv_obj_t * obj);
/**
* Get the parent of an object
* @param obj pointer to an object
* @return the parent of the object. (NULL if `obj` was a screen)
*/
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj);
/**
* Get the child of an object by the child's index.
* @param obj pointer to an object whose child should be get
* @param idx the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @return pointer to the child or NULL if the index was invalid
*/
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx);
/**
* Get the child of an object by the child's index. Consider the children only with a given type.
* @param obj pointer to an object whose child should be get
* @param idx the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @param class_p the type of the children to check
* @return pointer to the child or NULL if the index was invalid
*/
lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx,
const lv_obj_class_t * class_p);
/**
* Return a sibling of an object
* @param obj pointer to an object whose sibling should be get
* @param idx 0: `obj` itself
* -1: the first older sibling
* -2: the next older sibling
* 1: the first younger sibling
* 2: the next younger sibling
* etc
* @return pointer to the requested sibling or NULL if there is no such sibling
*/
lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx);
/**
* Return a sibling of an object. Consider the siblings only with a given type.
* @param obj pointer to an object whose sibling should be get
* @param idx 0: `obj` itself
* -1: the first older sibling
* -2: the next older sibling
* 1: the first younger sibling
* 2: the next younger sibling
* etc
* @param class_p the type of the children to check
* @return pointer to the requested sibling or NULL if there is no such sibling
*/
lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx,
const lv_obj_class_t * class_p);
/**
* Get the number of children
* @param obj pointer to an object
* @return the number of children
*/
uint32_t lv_obj_get_child_count(const lv_obj_t * obj);
/**
* Get the number of children having a given type.
* @param obj pointer to an object
* @param class_p the type of the children to check
* @return the number of children
*/
uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
#if LV_USE_OBJ_NAME
/**
* Set a name for a widget. The name will be allocated and freed when the
* widget is deleted or a new name is set.
* @param obj pointer to an object
* @param name the name to set. If set to `NULL` the default "<widget_type>_#"
* name will be used.
* @note If the name ends with a `#`, older siblings with the same name
* will be counted, and the `#` will be replaced by the index of the
* given widget. For example, creating multiple widgets with the name
* "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
* "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved`
* is called, so the result reflects the currently existing widgets at
* that time.
*/
void lv_obj_set_name(lv_obj_t * obj, const char * name);
/**
* Set a name for a widget. Only a pointer will be saved.
* @param obj pointer to an object
* @param name the name to set. If set to `NULL` the default "<widget_type>_#"
* name will be used.
* @note If the name ends with a `#`, older siblings with the same name
* will be counted, and the `#` will be replaced by the index of the
* given widget. For example, creating multiple widgets with the name
* "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
* "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved`
* is called, so the result reflects the currently existing widgets at
* that time.
*/
void lv_obj_set_name_static(lv_obj_t * obj, const char * name);
/**
* Get the set name as it was set.
* @param obj pointer to an object
* @return get the set name or NULL if it wasn't set yet
*/
const char * lv_obj_get_name(const lv_obj_t * obj);
/**
* Get the set name or craft a name automatically.
* @param obj pointer to an object
* @param buf buffer to store the name
* @param buf_size the size of the buffer in bytes
* @note If the name ends with a `#`, older siblings with the same name
* will be counted, and the `#` will be replaced by the index of the
* given widget. For example, creating multiple widgets with the name
* "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
* "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved`
* is called, so the result reflects the currently existing widgets at
* that time.
*/
void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size);
/**
* Find a child with a given name on a parent. This child doesn't have to be the
* direct child of the parent. First direct children of the parent will be checked,
* and the direct children of the first child, etc. (Breadth-first search).
*
* If the name of a widget was not set a name like "lv_button_1" will
* be created for it using `lv_obj_get_name_resolved`.
*
* @param parent the widget where the search should start
* @return the found widget or NULL if not found.
*/
lv_obj_t * lv_obj_find_by_name(const lv_obj_t * parent, const char * name);
/**
* Get an object by name. The name can be a path too, for example
* "main_container/lv_button_1/label".
* In this case the first part of the name-path should be the direct child of the parent,
* the second part, should the direct child of first one, etc.
*
* If the name of a widget was not set a name like "lv_button_1" will
* be created for it using `lv_obj_get_name_resolved`.
*
* @param parent the widget where the search should start
* @return the found widget or NULL if not found.
*/
lv_obj_t * lv_obj_get_child_by_name(const lv_obj_t * parent, const char * name_path);
#endif /*LV_USE_OBJ_NAME*/
/**
* Get the index of a child.
* @param obj pointer to an object
* @return the child index of the object.
* E.g. 0: the oldest (firstly created child).
* (-1 if child could not be found or no parent exists)
*/
int32_t lv_obj_get_index(const lv_obj_t * obj);
/**
* Get the index of a child. Consider the children only with a given type.
* @param obj pointer to an object
* @param class_p the type of the children to check
* @return the child index of the object.
* E.g. 0: the oldest (firstly created child with the given class).
* (-1 if child could not be found or no parent exists)
*/
int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Iterate through all children of any object.
* @param start_obj start integrating from this object
* @param cb call this callback on the objects
* @param user_data pointer to any user related data (will be passed to `cb`)
*/
void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**
* Iterate through all children of any object and print their ID.
* @param start_obj start integrating from this object
*/
void lv_obj_dump_tree(lv_obj_t * start_obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_TREE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,622 @@
/**
* @file lv_observer.h
*
*/
#ifndef LV_OBSERVER_H
#define LV_OBSERVER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_ext_data.h"
#include "lv_obj.h"
#if LV_USE_OBSERVER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Values for lv_subject_t's `type` field
*/
typedef enum {
LV_SUBJECT_TYPE_INVALID = 0, /**< indicates Subject not initialized yet */
LV_SUBJECT_TYPE_NONE = 1, /**< a null value like None or NILt */
LV_SUBJECT_TYPE_INT = 2, /**< an int32_t */
LV_SUBJECT_TYPE_FLOAT = 3, /**< a float, requires `LV_USE_FLOAT 1` */
LV_SUBJECT_TYPE_POINTER = 4, /**< a void pointer */
LV_SUBJECT_TYPE_COLOR = 5, /**< an lv_color_t */
LV_SUBJECT_TYPE_GROUP = 6, /**< an array of Subjects */
LV_SUBJECT_TYPE_STRING = 7, /**< a char pointer */
} lv_subject_type_t;
/**
* A common type to handle all the various observable types in the same way
*/
typedef union {
int32_t num; /**< Integer number (opacity, enums, booleans or "normal" numbers) */
const void * pointer; /**< Constant pointer (string buffer, format string, font, cone text, etc.) */
lv_color_t color; /**< Color */
#if LV_USE_FLOAT
float float_v; /**< Floating point value*/
#endif
} lv_subject_value_t;
/**
* The Subject (an observable value)
*/
struct _lv_subject_t {
#if LV_USE_EXT_DATA
lv_ext_data_t ext_data;
#endif
lv_ll_t subs_ll; /**< Subscribers */
lv_subject_value_t value; /**< Current value */
lv_subject_value_t prev_value; /**< Previous value */
lv_subject_value_t min_value; /**< Minimum value for min. int or float*/
lv_subject_value_t max_value; /**< Maximum value for max. int or float*/
void * user_data; /**< Additional parameter, can be used freely by user */
uint32_t type : 4; /**< One of the LV_SUBJECT_TYPE_... values */
uint32_t size : 24; /**< String buffer size or group length */
uint32_t notify_restart_query : 1; /**< If an Observer was deleted during notification,
* start notifying from the beginning. */
};
/**
* Callback called to notify Observer that Subject's value has changed
* @param observer pointer to Observer
* @param subject pointer to Subject being observed
*/
typedef void (*lv_observer_cb_t)(lv_observer_t * observer, lv_subject_t * subject);
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_USE_EXT_DATA
/**
* @brief Attaches external user data to an integer Subject with lifecycle management
*
* Associates arbitrary user-defined data with an LVGL observer and registers a destructor
* callback that will be automatically invoked when the observer is deleted. This enables:
* - Safe resource cleanup through the destructor mechanism
* - Contextual data storage for observer callbacks
* - Proper memory management for observer-related resources
*
* @param subject pointer to Subject
* @param data User-defined data pointer to associate
* @param free_cb Cleanup function called when:
* - Observer is explicitly deleted
* - Observed object is deleted
* - New data replaces current association
* NULL indicates no cleanup required
*/
void lv_subject_set_external_data(lv_subject_t * subject, void * data, void (* free_cb)(void * data));
#endif
/**
* Initialize an integer-type Subject.
* @param subject pointer to Subject
* @param value initial value
*/
void lv_subject_init_int(lv_subject_t * subject, int32_t value);
/**
* Set value of an integer Subject and notify Observers.
* @param subject pointer to Subject
* @param value new value
*/
void lv_subject_set_int(lv_subject_t * subject, int32_t value);
/**
* Get current value of an integer Subject.
* @param subject pointer to Subject
* @return current value
*/
int32_t lv_subject_get_int(lv_subject_t * subject);
/**
* Get previous value of an integer Subject.
* @param subject pointer to Subject
* @return current value
*/
int32_t lv_subject_get_previous_int(lv_subject_t * subject);
/**
* Set a minimum value for an integer subject
* @param subject pointer to Subject
* @param min_value the minimum value
*/
void lv_subject_set_min_value_int(lv_subject_t * subject, int32_t min_value);
/**
* Set a maximum value for an integer subject
* @param subject pointer to Subject
* @param max_value the maximum value
*/
void lv_subject_set_max_value_int(lv_subject_t * subject, int32_t max_value);
#if LV_USE_FLOAT
/**
* Initialize an float-type Subject.
* @param subject pointer to Subject
* @param value initial value
*/
void lv_subject_init_float(lv_subject_t * subject, float value);
/**
* Set value of an float Subject and notify Observers.
* @param subject pointer to Subject
* @param value new value
*/
void lv_subject_set_float(lv_subject_t * subject, float value);
/**
* Get current value of an float Subject.
* @param subject pointer to Subject
* @return current value
*/
float lv_subject_get_float(lv_subject_t * subject);
/**
* Get previous value of an float Subject.
* @param subject pointer to Subject
* @return current value
*/
float lv_subject_get_previous_float(lv_subject_t * subject);
/**
* Set a minimum value for a float subject
* @param subject pointer to Subject
* @param min_value the minimum value
*/
void lv_subject_set_min_value_float(lv_subject_t * subject, float min_value);
/**
* Set a maximum value for a float subject
* @param subject pointer to Subject
* @param max_value the maximum value
*/
void lv_subject_set_max_value_float(lv_subject_t * subject, float max_value);
#endif /*LV_USE_FLOAT*/
/**
* Initialize a string-type Subject.
* @param subject pointer to Subject
* @param buf pointer to buffer to store string
* @param prev_buf pointer to buffer to store previous string; can be NULL if not used
* @param size size of buffer(s)
* @param value initial value of string, e.g. "hello"
* @note A string Subject stores its own copy of the string, not just the pointer.
*/
void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value);
/**
* Copy a string to a Subject and notify Observers if it changed.
* @param subject pointer to Subject
* @param buf new string
*/
void lv_subject_copy_string(lv_subject_t * subject, const char * buf);
/**
* Format a new string, updating Subject, and notify Observers if it changed.
* @param subject pointer to Subject
* @param format format string
*/
void lv_subject_snprintf(lv_subject_t * subject, const char * format, ...) LV_FORMAT_ATTRIBUTE(2, 3);
/**
* Get current value of a string Subject.
* @param subject pointer to Subject
* @return pointer to buffer containing current value
*/
const char * lv_subject_get_string(lv_subject_t * subject);
/**
* Get previous value of a string Subject.
* @param subject pointer to Subject
* @return pointer to buffer containing previous value
* @note NULL will be returned if NULL was passed in `lv_subject_init_string()`
* as `prev_buf`.
*/
const char * lv_subject_get_previous_string(lv_subject_t * subject);
/**
* Initialize a pointer-type Subject.
* @param subject pointer to Subject
* @param value initial value
*/
void lv_subject_init_pointer(lv_subject_t * subject, void * value);
/**
* Set value of a pointer Subject and notify Observers (regardless of whether it changed).
* @param subject pointer to Subject
* @param ptr new value
*/
void lv_subject_set_pointer(lv_subject_t * subject, void * ptr);
/**
* Get current value of a pointer Subject.
* @param subject pointer to Subject
* @return current value
*/
const void * lv_subject_get_pointer(lv_subject_t * subject);
/**
* Get previous value of a pointer Subject.
* @param subject pointer to Subject
* @return previous value
*/
const void * lv_subject_get_previous_pointer(lv_subject_t * subject);
/**
* Initialize a color-type Subject.
* @param subject pointer to Subject
* @param color initial value
*/
void lv_subject_init_color(lv_subject_t * subject, lv_color_t color);
/**
* Set value of a color Subject and notify Observers if it changed.
* @param subject pointer to Subject
* @param color new value
*/
void lv_subject_set_color(lv_subject_t * subject, lv_color_t color);
/**
* Get current value of a color Subject.
* @param subject pointer to Subject
* @return current value
*/
lv_color_t lv_subject_get_color(lv_subject_t * subject);
/**
* Get previous value of a color Subject.
* @param subject pointer to Subject
* @return previous value
*/
lv_color_t lv_subject_get_previous_color(lv_subject_t * subject);
/**
* Initialize a Group-type Subject.
* @param group_subject pointer to Group-type Subject
* @param list list of other Subject addresses; when any of these have values
updated, Observers of `group_subject` will be notified.
* @param list_len number of elements in `list[]`
*/
void lv_subject_init_group(lv_subject_t * group_subject, lv_subject_t * list[], uint32_t list_len);
/**
* Remove all Observers from a Subject and free allocated memory, and delete
* any associated Widget-Binding events. This leaves `subject` "disconnected" from
* all Observers and all associated Widget events established through Widget Binding.
* @param subject pointer to Subject
* @note This can safely be called regardless of whether any Observers
* added with `lv_subject_add_observer_obj()` or bound to a Widget Property
* with one of the `..._bind_...()` functions.
*/
void lv_subject_deinit(lv_subject_t * subject);
/**
* Get an element from Subject Group's list.
* @param subject pointer to Group-type Subject
* @param index index of element to get
* @return pointer to indexed Subject from list, or NULL if index is out of bounds
*/
lv_subject_t * lv_subject_get_group_element(lv_subject_t * subject, int32_t index);
/**
* Add Observer to Subject. When Subject's value changes `observer_cb` will be called.
* @param subject pointer to Subject
* @param observer_cb notification callback
* @param user_data optional user data
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_subject_add_observer(lv_subject_t * subject, lv_observer_cb_t observer_cb, void * user_data);
/**
* Add Observer to Subject for a Widget.
* When the Widget is deleted, Observer will be unsubscribed from Subject automatically.
* @param subject pointer to Subject
* @param observer_cb notification callback
* @param obj pointer to Widget
* @param user_data optional user data
* @return pointer to newly-created Observer
* @note Do not call `lv_observer_remove()` on Observers created this way.
* Only clean up such Observers by either:
* - deleting the Widget, or
* - calling `lv_subject_deinit()` to gracefully de-couple and
* remove all Observers.
*/
lv_observer_t * lv_subject_add_observer_obj(lv_subject_t * subject, lv_observer_cb_t observer_cb, lv_obj_t * obj,
void * user_data);
/**
* Add an Observer to a Subject and also save a target pointer.
* @param subject pointer to Subject
* @param observer_cb notification callback
* @param target any pointer (NULL is okay)
* @param user_data optional user data
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_subject_add_observer_with_target(lv_subject_t * subject, lv_observer_cb_t observer_cb,
void * target, void * user_data);
/**
* Remove Observer from its Subject.
* @param observer pointer to Observer
*/
void lv_observer_remove(lv_observer_t * observer);
/**
* Remove Observers associated with Widget `obj` from specified `subject` or all Subjects.
* @param obj pointer to Widget whose Observers should be removed
* @param subject Subject to remove Widget from, or NULL to remove from all Subjects
* @note This function can be used e.g. when a Widget's Subject(s) needs to
* be replaced by other Subject(s)
*/
void lv_obj_remove_from_subject(lv_obj_t * obj, lv_subject_t * subject);
/**
* Get target of an Observer.
* @param observer pointer to Observer
* @return pointer to saved target
*/
void * lv_observer_get_target(lv_observer_t * observer);
/**
* Get target Widget of Observer.
* This is the same as `lv_observer_get_target()`, except it returns `target`
* as an `lv_obj_t *`.
* @param observer pointer to Observer
* @return pointer to saved Widget target
*/
lv_obj_t * lv_observer_get_target_obj(lv_observer_t * observer);
/**
* Get Observer's user data.
* @param observer pointer to Observer
* @return void pointer to saved user data
*/
void * lv_observer_get_user_data(const lv_observer_t * observer);
/**
* Notify all Observers of Subject.
* @param subject pointer to Subject
*/
void lv_subject_notify(lv_subject_t * subject);
/**
* Add an event handler to increment (or decrement) the value of a subject on a trigger.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param step value to add on trigger
* if the minimum value is reached, the maximum value will be set on rollover.
*/
lv_subject_increment_dsc_t * lv_obj_add_subject_increment_event(lv_obj_t * obj, lv_subject_t * subject,
lv_event_code_t trigger, int32_t step);
/**
* Set the minimum subject value to set by the event
* @param obj pointer to the Widget to which the event is attached
* @param dsc pointer to the descriptor returned by `lv_obj_add_subject_increment_event()`
* @param min_value the minimum value to set
*/
void lv_obj_set_subject_increment_event_min_value(lv_obj_t * obj, lv_subject_increment_dsc_t * dsc, int32_t min_value);
/**
* Set the maximum subject value to set by the event
* @param obj pointer to the Widget to which the event is attached
* @param dsc pointer to the descriptor returned by `lv_obj_add_subject_increment_event()`
* @param max_value the maximum value to set
*/
void lv_obj_set_subject_increment_event_max_value(lv_obj_t * obj, lv_subject_increment_dsc_t * dsc, int32_t max_value);
/**
* Set what to do when the min/max value is crossed.
* @param obj pointer to the Widget to which the event is attached
* @param dsc pointer to the descriptor returned by `lv_obj_add_subject_increment_event()`
* @param rollover false: stop at the min/max value; true: jump to the other end
* @note the subject also can have min/max values and always the smaller range will be considered
*/
void lv_obj_set_subject_increment_event_rollover(lv_obj_t * obj, lv_subject_increment_dsc_t * dsc, bool rollover);
/**
* Toggle the value of an integer subject on an event. If it was != 0 it will be 0.
* If it was 0, it will be 1.
* @param obj pointer to a widget
* @param subject pointer to a subject to toggle
* @param trigger the trigger on which the subject should be changed
*/
void lv_obj_add_subject_toggle_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger);
/**
* Set the value of an integer subject.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param value the value to set
*/
void lv_obj_add_subject_set_int_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, int32_t value);
#if LV_USE_FLOAT
/**
* Set the value of a float subject.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param value the value to set
*/
void lv_obj_add_subject_set_float_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, float value);
#endif
/**
* Set the value of a string subject.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param value the value to set
*/
void lv_obj_add_subject_set_string_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger,
const char * value);
/**
* Set Widget's flag(s) if an integer Subject's value is equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is not equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag,
int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is greater than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_gt(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is greater than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_ge(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is less than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_lt(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is less than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_le(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set a Widget's state(s) if an integer Subject's value is not equal to a reference value, clear flag otherwise
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state,
int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is greater than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_gt(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is greater than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_ge(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is less than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_lt(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is less than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_le(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set an integer Subject to 1 when a Widget is checked and set it 0 when unchecked, and
* clear Widget's checked state when Subject's value changes to 0 and set it when non-zero.
* @param obj pointer to Widget
* @param subject pointer to a Subject
* @return pointer to newly-created Observer
* @note Ensure Widget's `LV_OBJ_FLAG_CHECKABLE` flag is set.
*/
lv_observer_t * lv_obj_bind_checked(lv_obj_t * obj, lv_subject_t * subject);
/**********************
* MACROS
**********************/
#endif /*LV_USE_OBSERVER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBSERVER_H*/

View File

@@ -0,0 +1,67 @@
/**
* @file lv_observer_private.h
*
*/
#ifndef LV_OBSERVER_PRIVATE_H
#define LV_OBSERVER_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_observer.h"
#if LV_USE_OBSERVER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* The observer object: a descriptor returned when subscribing LVGL widgets to subjects
*/
struct _lv_observer_t {
lv_subject_t * subject; /**< Observed subject */
lv_observer_cb_t cb; /**< Callback that notifies when value changes */
void * target; /**< A target for the observer, e.g. a widget or any pointer */
void * user_data; /**< Additional parameter supplied when subscribing */
uint32_t auto_free_user_data : 1; /**< Automatically free user data when observer is removed */
uint32_t notified : 1; /**< Was observer already notified? */
uint32_t for_obj : 1; /**< Is `target` a pointer to a Widget (`lv_obj_t *`)? */
};
/**
* Descriptor created by `lv_obj_add_subject_increment_event()`
*/
struct _lv_subject_increment_dsc_t {
lv_subject_t * subject; /**< The subject to adjust*/
int32_t step; /**< The step add to the subject */
bool rollover; /**< Where to start over from the other end when one end is exceeded*/
int32_t min_value; /**< Don't set a value smaller than this */
int32_t max_value; /**< Don't set a value larger than this */
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_OBSERVER */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBSERVER_PRIVATE_H*/

1466
inc/lvgl/src/core/lv_refr.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../display/lv_display.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
* can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
* (e.g. progress bar) this function can be called when the screen should be updated.
* @param disp pointer to display to refresh. NULL to refresh all displays.
*/
void lv_refr_now(lv_display_t * disp);
/**
* Redrawn on object and all its children using the passed draw context
* @param layer pointer to a layer where to draw.
* @param obj the start object from the redraw should start
*/
void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj);
/**
* Called periodically to handle the refreshing
* @param timer pointer to the timer itself, or `NULL`
*/
void lv_display_refr_timer(lv_timer_t * timer);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_H*/

View File

@@ -0,0 +1,85 @@
/**
* @file lv_refr_private.h
*
*/
#ifndef LV_REFR_PRIVATE_H
#define LV_REFR_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the screen refresh subsystem
*/
void lv_refr_init(void);
/**
* Deinitialize the screen refresh subsystem
*/
void lv_refr_deinit(void);
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
* @return LV_RESULT_OK: the area is invalidated; LV_RESULT_INVALID: the area wasn't invalidated.
*/
lv_result_t lv_inv_area(lv_display_t * disp, const lv_area_t * area_p);
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_display_t * lv_refr_get_disp_refreshing(void);
/**
* Set the display which is being refreshed
* @param disp the display being refreshed
*/
void lv_refr_set_disp_refreshing(lv_display_t * disp);
/**
* Search the most top object which fully covers an area
* @param area_p pointer to an area
* @param obj the first object to start the searching (typically a screen)
* @return
*/
lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
/**
* Render an object to a layer
* @param layer target drawing layer
* @param obj object to render
*/
void lv_obj_refr(lv_layer_t * layer, lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_PRIVATE_H*/