1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-21 06:53:01 +08:00
lvgl/src/lv_core/lv_obj.c

2650 lines
72 KiB
C
Raw Normal View History

2016-06-08 07:25:08 +02:00
/**
* @file lv_base_obj.c
2018-06-19 09:49:58 +02:00
*
2016-06-08 07:25:08 +02:00
*/
/*********************
* INCLUDES
*********************/
2017-10-09 15:21:26 +02:00
#include "lv_obj.h"
2017-11-21 14:52:23 +01:00
#include "lv_indev.h"
#include "lv_refr.h"
#include "lv_group.h"
2019-02-20 23:58:13 +01:00
#include "lv_disp.h"
2019-09-24 16:30:38 +02:00
#include "../lv_core/lv_debug.h"
2017-12-19 22:00:32 +01:00
#include "../lv_themes/lv_theme.h"
2017-11-26 11:38:28 +01:00
#include "../lv_draw/lv_draw.h"
2017-11-23 20:42:14 +01:00
#include "../lv_misc/lv_anim.h"
2017-11-26 11:38:28 +01:00
#include "../lv_misc/lv_task.h"
#include "../lv_misc/lv_async.h"
2017-11-26 11:38:28 +01:00
#include "../lv_misc/lv_fs.h"
#include "../lv_misc/lv_gc.h"
#include "../lv_misc/lv_math.h"
#include "../lv_misc/lv_gc.h"
#include "../lv_misc/lv_math.h"
2019-11-04 16:56:57 +01:00
#include "../lv_hal/lv_hal.h"
#include <stdint.h>
#include <string.h>
#if defined(LV_GC_INCLUDE)
2019-04-04 07:15:40 +02:00
#include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
2016-06-08 07:25:08 +02:00
/*********************
* DEFINES
*********************/
#define LV_OBJX_NAME "lv_obj"
2019-04-04 07:15:40 +02:00
#define LV_OBJ_DEF_WIDTH (LV_DPI)
#define LV_OBJ_DEF_HEIGHT (2 * LV_DPI / 3)
2017-10-10 16:17:23 +02:00
2016-06-08 07:25:08 +02:00
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_event_temp_data
{
lv_obj_t * obj;
bool deleted;
struct _lv_event_temp_data * prev;
2019-06-06 06:05:40 +02:00
} lv_event_temp_data_t;
2016-06-08 07:25:08 +02:00
/**********************
* STATIC PROTOTYPES
**********************/
2019-02-12 16:41:26 +01:00
static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff);
2017-11-26 11:38:28 +01:00
static void report_style_mod_core(void * style_p, lv_obj_t * obj);
2019-02-12 16:41:26 +01:00
static void refresh_children_style(lv_obj_t * obj);
static void delete_children(lv_obj_t * obj);
static void base_dir_refr_children(lv_obj_t * obj);
static void lv_event_mark_deleted(lv_obj_t * obj);
static void lv_obj_del_async_cb(void * obj);
2019-09-06 19:53:39 +02:00
static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode);
2017-11-15 21:06:44 +01:00
static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param);
2016-06-08 07:25:08 +02:00
/**********************
* STATIC VARIABLES
**********************/
static bool lv_initialized = false;
static lv_event_temp_data_t * event_temp_data_head;
2019-05-07 12:15:02 +02:00
static const void * event_act_data;
2016-06-08 07:25:08 +02:00
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
2018-06-19 09:49:58 +02:00
* Init. the 'lv' library.
2016-06-08 07:25:08 +02:00
*/
void lv_init(void)
{
/* Do nothing if already initialized */
2019-04-04 07:15:40 +02:00
if(lv_initialized) {
2019-02-12 12:21:34 +01:00
LV_LOG_WARN("lv_init: already inited");
return;
}
2018-10-05 17:22:49 +02:00
LV_LOG_TRACE("lv_init started");
2017-11-26 11:38:28 +01:00
/*Initialize the lv_misc modules*/
2017-11-23 21:28:36 +01:00
lv_mem_init();
2019-05-17 07:58:47 +02:00
lv_task_core_init();
#if LV_USE_FILESYSTEM
2017-11-24 17:48:47 +01:00
lv_fs_init();
#endif
#if LV_USE_ANIMATION
2019-05-15 06:15:12 +02:00
lv_anim_core_init();
2017-11-27 17:48:54 +01:00
#endif
#if LV_USE_GROUP
lv_group_init();
#endif
/*Init. the sstyles*/
lv_style_init();
2018-06-19 09:49:58 +02:00
2018-12-26 07:58:06 +01:00
/*Initialize the screen refresh system*/
2016-06-08 07:25:08 +02:00
lv_refr_init();
2016-07-12 15:05:17 +02:00
2019-02-10 11:06:47 +01:00
lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
2018-06-19 09:49:58 +02:00
2017-10-09 15:21:26 +02:00
/*Init the input device handling*/
2017-10-10 16:17:23 +02:00
lv_indev_init();
lv_img_decoder_init();
2019-06-20 18:43:03 +02:00
lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
lv_initialized = true;
2018-10-05 17:22:49 +02:00
LV_LOG_INFO("lv_init ready");
2016-06-08 07:25:08 +02:00
}
/*--------------------
* Create and delete
*-------------------*/
/**
2018-06-19 09:49:58 +02:00
* Create a basic object
2016-10-07 11:15:46 +02:00
* @param parent pointer to a parent object.
2016-06-08 07:25:08 +02:00
* If NULL then a screen will be created
2016-10-07 11:15:46 +02:00
* @param copy pointer to a base object, if not NULL then the new object will be copied from it
2016-06-08 07:25:08 +02:00
* @return pointer to the new object
*/
2019-04-04 07:15:40 +02:00
lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
2016-06-08 07:25:08 +02:00
{
2016-10-07 11:15:46 +02:00
lv_obj_t * new_obj = NULL;
2016-06-08 07:25:08 +02:00
/*Create a screen if the parent is NULL*/
2016-10-07 11:15:46 +02:00
if(parent == NULL) {
2018-10-05 17:22:49 +02:00
LV_LOG_TRACE("Screen create started");
2019-02-20 10:16:33 +01:00
lv_disp_t * disp = lv_disp_get_default();
2019-02-10 11:06:47 +01:00
if(!disp) {
2019-06-06 06:05:40 +02:00
LV_LOG_WARN("lv_obj_create: not display created to so far. No place to assign the new screen");
2019-02-20 10:16:33 +01:00
return NULL;
2019-02-10 11:06:47 +01:00
}
2019-02-10 11:06:47 +01:00
new_obj = lv_ll_ins_head(&disp->scr_ll);
2019-09-24 23:14:17 +02:00
LV_ASSERT_MEM(new_obj);
if(new_obj == NULL) return NULL;
2018-06-19 09:49:58 +02:00
2016-10-07 11:15:46 +02:00
new_obj->par = NULL; /*Screens has no a parent*/
2017-11-24 17:48:47 +01:00
lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t));
2018-06-19 09:49:58 +02:00
2019-11-25 07:15:01 +01:00
/*Set the callbacks*/
new_obj->signal_cb = lv_obj_signal;
new_obj->design_cb = lv_obj_design;
new_obj->event_cb = NULL;
2018-06-19 09:49:58 +02:00
/*Set coordinates to full screen size*/
2019-06-06 06:05:40 +02:00
new_obj->coords.x1 = 0;
new_obj->coords.y1 = 0;
new_obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1;
new_obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1;
new_obj->ext_draw_pad = 0;
2016-06-08 07:25:08 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
memset(&new_obj->ext_click_pad, 0, sizeof(new_obj->ext_click_pad));
#endif
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
new_obj->ext_click_pad_hor = 0;
new_obj->ext_click_pad_ver = 0;
#endif
2016-06-08 07:25:08 +02:00
2018-12-21 14:35:25 +01:00
/*Init realign*/
#if LV_USE_OBJ_REALIGN
2019-04-04 07:15:40 +02:00
new_obj->realign.align = LV_ALIGN_CENTER;
new_obj->realign.xofs = 0;
new_obj->realign.yofs = 0;
new_obj->realign.base = NULL;
2018-12-21 14:35:25 +01:00
new_obj->realign.auto_realign = 0;
#endif
2017-12-19 22:00:32 +01:00
/*Set the default styles*/
2018-06-19 09:49:58 +02:00
lv_theme_t * th = lv_theme_get_current();
2017-12-19 22:00:32 +01:00
if(th) {
2019-06-26 17:51:14 +02:00
new_obj->style_p = th->style.scr;
2017-12-19 22:00:32 +01:00
} else {
new_obj->style_p = &lv_style_scr;
}
2016-06-08 07:25:08 +02:00
/*Init. user date*/
#if LV_USE_USER_DATA
memset(&new_obj->user_data, 0, sizeof(lv_obj_user_data_t));
2017-05-10 16:11:20 +02:00
#endif
#if LV_USE_GROUP
new_obj->group_p = NULL;
#endif
2018-06-19 09:49:58 +02:00
/*Set attributes*/
2019-04-04 07:15:40 +02:00
new_obj->click = 0;
new_obj->drag = 0;
new_obj->drag_throw = 0;
new_obj->drag_parent = 0;
new_obj->drag_dir = 0;
2019-04-04 07:15:40 +02:00
new_obj->hidden = 0;
new_obj->top = 0;
new_obj->protect = LV_PROTECT_NONE;
2019-03-15 22:19:21 +01:00
new_obj->opa_scale_en = 0;
2019-04-04 07:15:40 +02:00
new_obj->opa_scale = LV_OPA_COVER;
2019-03-12 06:20:45 +01:00
new_obj->parent_event = 0;
#if LV_USE_BIDI
new_obj->base_dir = LV_BIDI_BASE_DIR_DEF;
#else
new_obj->base_dir = LV_BIDI_DIR_LTR;
#endif
new_obj->reserved = 0;
2018-06-19 09:49:58 +02:00
new_obj->ext_attr = NULL;
2018-07-25 20:39:24 +02:00
LV_LOG_INFO("Screen create ready");
2018-06-19 09:49:58 +02:00
}
2016-10-17 15:02:18 +02:00
/*parent != NULL create normal obj. on a parent*/
2018-06-19 09:49:58 +02:00
else {
2018-10-05 17:22:49 +02:00
LV_LOG_TRACE("Object create started");
LV_ASSERT_OBJ(parent, LV_OBJX_NAME);
2018-07-25 20:39:24 +02:00
2019-05-03 19:25:58 +02:00
new_obj = lv_ll_ins_head(&parent->child_ll);
2019-09-24 23:14:17 +02:00
LV_ASSERT_MEM(new_obj);
if(new_obj == NULL) return NULL;
2016-10-07 11:15:46 +02:00
new_obj->par = parent; /*Set the parent*/
2017-11-24 17:48:47 +01:00
lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t));
2018-06-19 09:49:58 +02:00
2019-11-25 07:15:01 +01:00
/*Set the callbacks*/
new_obj->signal_cb = lv_obj_signal;
new_obj->design_cb = lv_obj_design;
new_obj->event_cb = NULL;
#if LV_USE_BIDI
new_obj->base_dir = LV_BIDI_DIR_INHERIT;
#else
new_obj->base_dir = LV_BIDI_DIR_LTR;
#endif
2016-06-08 07:25:08 +02:00
/*Set coordinates left top corner of parent*/
2019-06-06 06:05:40 +02:00
new_obj->coords.y1 = parent->coords.y1;
new_obj->coords.y2 = parent->coords.y1 + LV_OBJ_DEF_HEIGHT;
if(lv_obj_get_base_dir(new_obj) == LV_BIDI_DIR_RTL) {
new_obj->coords.x2 = parent->coords.x2;
new_obj->coords.x1 = parent->coords.x2 - LV_OBJ_DEF_WIDTH;
} else {
new_obj->coords.x1 = parent->coords.x1;
new_obj->coords.x2 = parent->coords.x1 + LV_OBJ_DEF_WIDTH;
}
2019-06-06 06:05:40 +02:00
new_obj->ext_draw_pad = 0;
2016-06-08 07:25:08 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
memset(&new_obj->ext_click_pad, 0, sizeof(new_obj->ext_click_pad));
#endif
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
new_obj->ext_click_pad_hor = 0;
new_obj->ext_click_pad_ver = 0;
#endif
2016-06-08 07:25:08 +02:00
2018-11-22 14:59:38 +01:00
/*Init realign*/
#if LV_USE_OBJ_REALIGN
2019-04-04 07:15:40 +02:00
new_obj->realign.align = LV_ALIGN_CENTER;
new_obj->realign.xofs = 0;
new_obj->realign.yofs = 0;
new_obj->realign.base = NULL;
2018-11-22 14:59:38 +01:00
new_obj->realign.auto_realign = 0;
#endif
2016-06-08 07:25:08 +02:00
/*Set appearance*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
new_obj->style_p = th->style.panel;
} else {
new_obj->style_p = &lv_style_plain_color;
}
2018-06-19 09:49:58 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
memset(&new_obj->ext_click_pad, 0, sizeof(new_obj->ext_click_pad));
#endif
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
new_obj->ext_click_pad_hor = 0;
new_obj->ext_click_pad_ver = 0;
#endif
/*Init. user date*/
#if LV_USE_USER_DATA
memset(&new_obj->user_data, 0, sizeof(lv_obj_user_data_t));
2017-05-10 16:11:20 +02:00
#endif
#if LV_USE_GROUP
new_obj->group_p = NULL;
#endif
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Set attributes*/
2019-04-04 07:15:40 +02:00
new_obj->click = 1;
new_obj->drag = 0;
new_obj->drag_dir = LV_DRAG_DIR_BOTH;
2019-04-04 07:15:40 +02:00
new_obj->drag_throw = 0;
new_obj->drag_parent = 0;
new_obj->hidden = 0;
new_obj->top = 0;
new_obj->protect = LV_PROTECT_NONE;
new_obj->opa_scale = LV_OPA_COVER;
2018-06-14 13:08:19 +02:00
new_obj->opa_scale_en = 0;
2019-03-15 22:19:21 +01:00
new_obj->parent_event = 0;
new_obj->reserved = 0;
2018-06-19 09:49:58 +02:00
new_obj->ext_attr = NULL;
2016-06-08 07:25:08 +02:00
}
/*Copy the attributes if required*/
2016-10-07 11:15:46 +02:00
if(copy != NULL) {
LV_ASSERT_OBJ(copy, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
lv_area_copy(&new_obj->coords, &copy->coords);
new_obj->ext_draw_pad = copy->ext_draw_pad;
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
lv_area_copy(&new_obj->ext_click_pad, &copy->ext_click_pad);
#endif
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
new_obj->ext_click_pad_hor = copy->ext_click_pad_hor;
new_obj->ext_click_pad_ver = copy->ext_click_pad_ver;
#endif
/*Set free data*/
#if LV_USE_USER_DATA
memcpy(&new_obj->user_data, &copy->user_data, sizeof(lv_obj_user_data_t));
2017-05-10 16:11:20 +02:00
#endif
2018-12-21 14:35:25 +01:00
/*Copy realign*/
#if LV_USE_OBJ_REALIGN
2019-04-04 07:15:40 +02:00
new_obj->realign.align = copy->realign.align;
new_obj->realign.xofs = copy->realign.xofs;
new_obj->realign.yofs = copy->realign.yofs;
new_obj->realign.base = copy->realign.base;
2018-12-21 14:35:25 +01:00
new_obj->realign.auto_realign = copy->realign.auto_realign;
#endif
/*Only copy the `event_cb`. `signal_cb` and `design_cb` will be copied in the derived
2019-04-04 07:15:40 +02:00
* object type (e.g. `lv_btn`)*/
new_obj->event_cb = copy->event_cb;
/*Copy attributes*/
2019-04-04 07:15:40 +02:00
new_obj->click = copy->click;
new_obj->drag = copy->drag;
new_obj->drag_dir = copy->drag_dir;
2019-04-04 07:15:40 +02:00
new_obj->drag_throw = copy->drag_throw;
new_obj->drag_parent = copy->drag_parent;
new_obj->hidden = copy->hidden;
new_obj->top = copy->top;
2019-03-12 06:20:45 +01:00
new_obj->parent_event = copy->parent_event;
2018-12-21 14:35:25 +01:00
2018-06-14 13:08:19 +02:00
new_obj->opa_scale_en = copy->opa_scale_en;
2019-04-04 07:15:40 +02:00
new_obj->protect = copy->protect;
new_obj->opa_scale = copy->opa_scale;
2016-06-08 07:25:08 +02:00
new_obj->style_p = copy->style_p;
#if LV_USE_GROUP
/*Add to the same group*/
if(copy->group_p != NULL) {
lv_group_add_obj(copy->group_p, new_obj);
}
#endif
2019-05-15 07:34:19 +02:00
/*Set the same coordinates for non screen objects*/
2019-05-15 07:36:13 +02:00
if(lv_obj_get_parent(copy) != NULL && parent != NULL) {
2019-05-15 07:34:19 +02:00
lv_obj_set_pos(new_obj, lv_obj_get_x(copy), lv_obj_get_y(copy));
} else {
lv_obj_set_pos(new_obj, 0, 0);
}
2018-07-25 20:39:24 +02:00
LV_LOG_INFO("Object create ready");
2016-06-08 07:25:08 +02:00
}
/*Send a signal to the parent to notify it about the new child*/
2016-10-07 11:15:46 +02:00
if(parent != NULL) {
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, new_obj);
2016-06-08 07:25:08 +02:00
/*Invalidate the area if not screen created*/
lv_obj_invalidate(new_obj);
2016-06-08 07:25:08 +02:00
}
2018-06-19 09:49:58 +02:00
2016-10-07 11:15:46 +02:00
return new_obj;
2016-06-08 07:25:08 +02:00
}
/**
2016-10-07 11:15:46 +02:00
* Delete 'obj' and all of its children
2017-05-01 16:47:27 +02:00
* @param obj pointer to an object to delete
2017-12-19 22:00:32 +01:00
* @return LV_RES_INV because the object is deleted
2016-06-08 07:25:08 +02:00
*/
2017-11-15 21:06:44 +01:00
lv_res_t lv_obj_del(lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_obj_invalidate(obj);
2018-06-19 09:49:58 +02:00
2019-04-19 05:46:02 +02:00
/*Delete from the group*/
#if LV_USE_GROUP
lv_group_t * group = lv_obj_get_group(obj);
if(group) lv_group_remove_obj(obj);
2018-06-19 09:49:58 +02:00
#endif
2019-06-06 06:05:40 +02:00
/*Remove the animations from this object*/
#if LV_USE_ANIMATION
lv_anim_del(obj, NULL);
#endif
2016-06-08 07:25:08 +02:00
/*Recursively delete the children*/
2016-10-07 11:15:46 +02:00
lv_obj_t * i;
lv_obj_t * i_next;
2017-11-24 17:48:47 +01:00
i = lv_ll_get_head(&(obj->child_ll));
2016-06-08 07:25:08 +02:00
while(i != NULL) {
/*Get the next object before delete this*/
2017-11-24 17:48:47 +01:00
i_next = lv_ll_get_next(&(obj->child_ll), i);
2016-06-08 07:25:08 +02:00
/*Call the recursive del to the child too*/
delete_children(i);
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Set i to the next node*/
i = i_next;
}
/*Let the user free the resources used in `LV_EVENT_DELETE`*/
2019-04-19 05:46:02 +02:00
lv_event_send(obj, LV_EVENT_DELETE, NULL);
lv_event_mark_deleted(obj);
2019-05-03 19:25:58 +02:00
/* Reset all input devices if the object to delete is used*/
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
2018-06-19 09:49:58 +02:00
lv_indev_reset(indev);
}
2019-05-03 15:22:59 +02:00
if(indev->proc.types.pointer.last_pressed == obj) {
indev->proc.types.pointer.last_pressed = NULL;
}
#if LV_USE_GROUP
2019-06-06 06:05:40 +02:00
if(indev->group == group && obj == lv_indev_get_obj_act()) {
lv_indev_reset(indev);
}
#endif
indev = lv_indev_get_next(indev);
}
/* All children deleted.
* Now clean up the object specific data*/
obj->signal_cb(obj, LV_SIGNAL_CLEANUP, NULL);
/*Remove the object from parent's children list*/
lv_obj_t * par = lv_obj_get_parent(obj);
if(par == NULL) { /*It is a screen*/
lv_disp_t * d = lv_obj_get_disp(obj);
2019-11-15 11:04:46 +01:00
lv_ll_remove(&d->scr_ll, obj);
} else {
2019-11-15 11:04:46 +01:00
lv_ll_remove(&(par->child_ll), obj);
}
/*Delete the base objects*/
2019-04-04 07:15:40 +02:00
if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr);
lv_mem_free(obj); /*Free the object itself*/
2016-06-08 07:25:08 +02:00
/*Send a signal to the parent to notify it about the child delete*/
2016-10-07 11:15:46 +02:00
if(par != NULL) {
par->signal_cb(par, LV_SIGNAL_CHILD_CHG, NULL);
2016-06-08 07:25:08 +02:00
}
2017-11-15 21:06:44 +01:00
return LV_RES_INV;
2016-06-08 07:25:08 +02:00
}
2019-07-08 13:00:27 +02:00
/**
* Helper function for asynchronously deleting objects.
* Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
* @param obj object to delete
* @see lv_async_call
*/
void lv_obj_del_async(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_async_call(lv_obj_del_async_cb, obj);
}
/**
* Delete all children of an object
* @param obj pointer to an object
*/
2018-06-19 09:49:58 +02:00
void lv_obj_clean(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
lv_obj_t * child = lv_obj_get_child(obj, NULL);
2018-11-11 19:56:30 +01:00
lv_obj_t * child_next;
while(child) {
2018-11-11 19:56:30 +01:00
/* Read the next child before deleting the current
* because the next couldn't be read from a deleted (invalid) node*/
child_next = lv_obj_get_child(obj, child);
lv_obj_del(child);
2018-11-11 19:56:30 +01:00
child = child_next;
}
}
/**
* Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
* @param obj pointer to an object
*/
void lv_obj_invalidate(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
if(lv_obj_get_hidden(obj)) return;
2018-05-16 23:19:22 +02:00
2019-01-18 22:18:56 +02:00
/*Invalidate the object only if it belongs to the 'LV_GC_ROOT(_lv_act_scr)'*/
lv_obj_t * obj_scr = lv_obj_get_screen(obj);
2019-04-04 07:15:40 +02:00
lv_disp_t * disp = lv_obj_get_disp(obj_scr);
if(obj_scr == lv_disp_get_scr_act(disp) || obj_scr == lv_disp_get_layer_top(disp) ||
obj_scr == lv_disp_get_layer_sys(disp)) {
/*Truncate recursively to the parents*/
2017-11-23 21:28:36 +01:00
lv_area_t area_trunc;
lv_obj_t * par = lv_obj_get_parent(obj);
2019-04-04 07:15:40 +02:00
bool union_ok = true;
/*Start with the original coordinates*/
lv_coord_t ext_size = obj->ext_draw_pad;
2017-11-28 16:15:13 +01:00
lv_area_copy(&area_trunc, &obj->coords);
area_trunc.x1 -= ext_size;
area_trunc.y1 -= ext_size;
area_trunc.x2 += ext_size;
area_trunc.y2 += ext_size;
/*Check through all parents*/
while(par != NULL) {
union_ok = lv_area_intersect(&area_trunc, &area_trunc, &par->coords);
2019-06-06 06:05:40 +02:00
if(union_ok == false) break; /*If no common parts with parent break;*/
if(lv_obj_get_hidden(par)) return; /*If the parent is hidden then the child is hidden and won't be drawn*/
par = lv_obj_get_parent(par);
}
if(union_ok) lv_inv_area(disp, &area_trunc);
}
}
2016-06-08 07:25:08 +02:00
/*=====================
2018-06-19 09:49:58 +02:00
* Setter functions
2016-06-08 07:25:08 +02:00
*====================*/
/*--------------------
2018-06-19 09:49:58 +02:00
* Parent/children set
2016-06-08 07:25:08 +02:00
*--------------------*/
/**
* Set a new parent for an object. Its relative position will be the same.
* @param obj pointer to an object. Can't be a screen.
* @param parent pointer to the new parent object. (Can't be NULL)
2016-06-08 07:25:08 +02:00
*/
2016-10-07 11:15:46 +02:00
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
LV_ASSERT_OBJ(parent, LV_OBJX_NAME);
2018-08-15 12:36:35 +02:00
if(obj->par == NULL) {
LV_LOG_WARN("Can't set the parent of a screen");
return;
}
if(parent == NULL) {
LV_LOG_WARN("Can't set parent == NULL to an object");
return;
}
lv_obj_invalidate(obj);
2018-06-19 09:49:58 +02:00
lv_obj_t * old_par = obj->par;
2017-11-23 21:28:36 +01:00
lv_point_t old_pos;
2016-10-07 11:15:46 +02:00
old_pos.y = lv_obj_get_y(obj);
2018-06-19 09:49:58 +02:00
lv_bidi_dir_t new_base_dir = lv_obj_get_base_dir(parent);
if(new_base_dir != LV_BIDI_DIR_RTL) {
old_pos.x = lv_obj_get_x(obj);
} else {
old_pos.x = old_par->coords.x2 - obj->coords.x2;
}
2019-05-03 19:25:58 +02:00
lv_ll_chg_list(&obj->par->child_ll, &parent->child_ll, obj, true);
2016-10-07 11:15:46 +02:00
obj->par = parent;
if(new_base_dir != LV_BIDI_DIR_RTL) {
lv_obj_set_pos(obj, old_pos.x, old_pos.y);
} else {
/*Align to the right in case of RTL base dir*/
lv_coord_t new_x = lv_obj_get_width(parent) - old_pos.x - lv_obj_get_width(obj);
lv_obj_set_pos(obj, new_x , old_pos.y);
}
/*Notify the original parent because one of its children is lost*/
old_par->signal_cb(old_par, LV_SIGNAL_CHILD_CHG, NULL);
/*Notify the new parent about the child*/
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
2016-06-08 07:25:08 +02:00
lv_obj_invalidate(obj);
2016-06-08 07:25:08 +02:00
}
2019-05-03 19:25:58 +02:00
/**
* Move and object to the foreground
* @param obj pointer to an object
*/
void lv_obj_move_foreground(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-03 19:25:58 +02:00
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Do nothing of already in the foreground*/
if(lv_ll_get_head(&parent->child_ll) == obj) return;
2019-05-03 19:25:58 +02:00
lv_obj_invalidate(parent);
lv_ll_chg_list(&parent->child_ll, &parent->child_ll, obj, true);
/*Notify the new parent about the child*/
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
lv_obj_invalidate(parent);
}
/**
* Move and object to the background
* @param obj pointer to an object
*/
void lv_obj_move_background(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-03 19:25:58 +02:00
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Do nothing of already in the background*/
if(lv_ll_get_tail(&parent->child_ll) == obj) return;
2019-05-03 19:25:58 +02:00
lv_obj_invalidate(parent);
lv_ll_chg_list(&parent->child_ll, &parent->child_ll, obj, false);
/*Notify the new parent about the child*/
parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
lv_obj_invalidate(parent);
}
/*--------------------
* Coordinate set
* ------------------*/
2016-06-08 07:25:08 +02:00
/**
* Set relative the position of an object (relative to the parent)
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param x new distance from the left side of the parent
* @param y new distance from the top of the parent
*/
2017-11-23 21:28:36 +01:00
void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-06-08 07:25:08 +02:00
/*Convert x and y to absolute coordinates*/
2016-10-07 11:15:46 +02:00
lv_obj_t * par = obj->par;
2019-06-06 06:05:40 +02:00
x = x + par->coords.x1;
y = y + par->coords.y1;
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Calculate and set the movement*/
2017-11-23 21:28:36 +01:00
lv_point_t diff;
2019-04-04 07:15:40 +02:00
diff.x = x - obj->coords.x1;
diff.y = y - obj->coords.y1;
/* Do nothing if the position is not changed */
/* It is very important else recursive positioning can
* occur without position change*/
if(diff.x == 0 && diff.y == 0) return;
2018-06-19 09:49:58 +02:00
/*Invalidate the original area*/
lv_obj_invalidate(obj);
/*Save the original coordinates*/
2017-11-23 21:28:36 +01:00
lv_area_t ori;
lv_obj_get_coords(obj, &ori);
obj->coords.x1 += diff.x;
obj->coords.y1 += diff.y;
obj->coords.x2 += diff.x;
obj->coords.y2 += diff.y;
2018-06-19 09:49:58 +02:00
2019-02-12 16:41:26 +01:00
refresh_children_position(obj, diff.x, diff.y);
2018-06-19 09:49:58 +02:00
/*Inform the object about its new coordinates*/
obj->signal_cb(obj, LV_SIGNAL_CORD_CHG, &ori);
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Send a signal to the parent too*/
par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Invalidate the new area*/
lv_obj_invalidate(obj);
2016-06-08 07:25:08 +02:00
}
/**
* Set the x coordinate of a object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param x new distance from the left side from the parent
*/
2017-11-23 21:28:36 +01:00
void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-10-07 11:15:46 +02:00
lv_obj_set_pos(obj, x, lv_obj_get_y(obj));
2016-06-08 07:25:08 +02:00
}
/**
* Set the y coordinate of a object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param y new distance from the top of the parent
*/
2017-11-23 21:28:36 +01:00
void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-10-07 11:15:46 +02:00
lv_obj_set_pos(obj, lv_obj_get_x(obj), y);
2016-06-08 07:25:08 +02:00
}
/**
* Set the size of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2018-06-19 09:49:58 +02:00
* @param w new width
2016-06-08 07:25:08 +02:00
* @param h new height
*/
2017-11-23 21:28:36 +01:00
void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
/* Do nothing if the size is not changed */
/* It is very important else recursive resizing can
* occur without size change*/
2018-06-19 09:49:58 +02:00
if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) {
return;
}
2016-06-08 07:25:08 +02:00
/*Invalidate the original area*/
lv_obj_invalidate(obj);
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Save the original coordinates*/
2017-11-23 21:28:36 +01:00
lv_area_t ori;
lv_obj_get_coords(obj, &ori);
2018-06-19 09:49:58 +02:00
/*Set the length and height*/
obj->coords.y2 = obj->coords.y1 + h - 1;
if(lv_obj_get_base_dir(obj) == LV_BIDI_DIR_RTL) {
obj->coords.x1 = obj->coords.x2 - w + 1;
} else {
obj->coords.x2 = obj->coords.x1 + w - 1;
}
/*Send a signal to the object with its new coordinates*/
obj->signal_cb(obj, LV_SIGNAL_CORD_CHG, &ori);
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
/*Send a signal to the parent too*/
2016-10-07 11:15:46 +02:00
lv_obj_t * par = lv_obj_get_parent(obj);
if(par != NULL) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
2018-06-19 09:49:58 +02:00
/*Tell the children the parent's size has changed*/
lv_obj_t * i;
2019-04-04 07:15:40 +02:00
LV_LL_READ(obj->child_ll, i)
{
i->signal_cb(i, LV_SIGNAL_PARENT_SIZE_CHG, NULL);
}
2016-06-08 07:25:08 +02:00
/*Invalidate the new area*/
lv_obj_invalidate(obj);
2018-11-22 14:59:38 +01:00
/*Automatically realign the object if required*/
#if LV_USE_OBJ_REALIGN
2018-11-22 14:59:38 +01:00
if(obj->realign.auto_realign) lv_obj_realign(obj);
#endif
2016-06-08 07:25:08 +02:00
}
/**
* Set the width of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param w new width
*/
2017-11-23 21:28:36 +01:00
void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-10-07 11:15:46 +02:00
lv_obj_set_size(obj, w, lv_obj_get_height(obj));
2016-06-08 07:25:08 +02:00
}
/**
* Set the height of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param h new height
*/
2017-11-23 21:28:36 +01:00
void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-10-07 11:15:46 +02:00
lv_obj_set_size(obj, lv_obj_get_width(obj), h);
2016-06-08 07:25:08 +02:00
}
/**
2018-06-19 09:49:58 +02:00
* Align an object to an other object.
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object to align
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
2016-06-08 07:25:08 +02:00
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_mod x coordinate shift after alignment
* @param y_mod y coordinate shift after alignment
*/
2019-06-06 06:05:40 +02:00
void lv_obj_align(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2017-11-23 21:28:36 +01:00
lv_coord_t new_x = lv_obj_get_x(obj);
lv_coord_t new_y = lv_obj_get_y(obj);
2016-06-08 07:25:08 +02:00
2016-10-07 11:15:46 +02:00
if(base == NULL) {
base = lv_obj_get_parent(obj);
2016-06-08 07:25:08 +02:00
}
2018-06-19 09:49:58 +02:00
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
switch(align) {
2016-06-08 07:25:08 +02:00
case LV_ALIGN_CENTER:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
2016-06-08 07:25:08 +02:00
break;
case LV_ALIGN_IN_TOP_LEFT:
new_x = 0;
new_y = 0;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_IN_TOP_MID:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
2016-06-08 07:25:08 +02:00
new_y = 0;
break;
case LV_ALIGN_IN_TOP_RIGHT:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
2016-06-08 07:25:08 +02:00
new_y = 0;
break;
case LV_ALIGN_IN_BOTTOM_LEFT:
new_x = 0;
2016-10-07 11:15:46 +02:00
new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_IN_BOTTOM_MID:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
2016-06-08 07:25:08 +02:00
break;
case LV_ALIGN_IN_BOTTOM_RIGHT:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
2016-06-08 07:25:08 +02:00
break;
case LV_ALIGN_IN_LEFT_MID:
new_x = 0;
2016-10-07 11:15:46 +02:00
new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_IN_RIGHT_MID:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_TOP_LEFT:
new_x = 0;
2016-10-07 11:15:46 +02:00
new_y = -lv_obj_get_height(obj);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_TOP_MID:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
2019-04-04 07:15:40 +02:00
new_y = -lv_obj_get_height(obj);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_TOP_RIGHT:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
2019-04-04 07:15:40 +02:00
new_y = -lv_obj_get_height(obj);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_BOTTOM_LEFT:
new_x = 0;
2016-10-07 11:15:46 +02:00
new_y = lv_obj_get_height(base);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_BOTTOM_MID:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
new_y = lv_obj_get_height(base);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_BOTTOM_RIGHT:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
new_y = lv_obj_get_height(base);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_LEFT_TOP:
2019-04-04 07:15:40 +02:00
new_x = -lv_obj_get_width(obj);
2016-06-08 07:25:08 +02:00
new_y = 0;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_LEFT_MID:
2019-04-04 07:15:40 +02:00
new_x = -lv_obj_get_width(obj);
2016-10-07 11:15:46 +02:00
new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_LEFT_BOTTOM:
2019-04-04 07:15:40 +02:00
new_x = -lv_obj_get_width(obj);
2016-10-07 11:15:46 +02:00
new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_RIGHT_TOP:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base);
2016-06-08 07:25:08 +02:00
new_y = 0;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_RIGHT_MID:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base);
new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
case LV_ALIGN_OUT_RIGHT_BOTTOM:
2016-10-07 11:15:46 +02:00
new_x = lv_obj_get_width(base);
new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
2018-06-19 09:49:58 +02:00
break;
2016-06-08 07:25:08 +02:00
}
2016-10-07 11:15:46 +02:00
/*Bring together the coordination system of base and obj*/
2019-04-04 07:15:40 +02:00
lv_obj_t * par = lv_obj_get_parent(obj);
2017-11-23 21:28:36 +01:00
lv_coord_t base_abs_x = base->coords.x1;
lv_coord_t base_abs_y = base->coords.y1;
2019-04-04 07:15:40 +02:00
lv_coord_t par_abs_x = par->coords.x1;
lv_coord_t par_abs_y = par->coords.y1;
new_x += x_mod + base_abs_x;
new_y += y_mod + base_abs_y;
2018-06-19 09:49:58 +02:00
new_x -= par_abs_x;
new_y -= par_abs_y;
2016-06-08 07:25:08 +02:00
2018-06-19 09:49:58 +02:00
lv_obj_set_pos(obj, new_x, new_y);
2018-11-22 14:59:38 +01:00
#if LV_USE_OBJ_REALIGN
2018-11-22 14:59:38 +01:00
/*Save the last align parameters to use them in `lv_obj_realign`*/
2019-04-04 07:15:40 +02:00
obj->realign.align = align;
obj->realign.xofs = x_mod;
obj->realign.yofs = y_mod;
obj->realign.base = base;
2018-11-22 14:59:38 +01:00
obj->realign.origo_align = 0;
#endif
}
/**
2019-06-20 06:20:23 +02:00
* Align an object's middle point to an other object.
2018-11-22 14:59:38 +01:00
* @param obj pointer to an object to align
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_mod x coordinate shift after alignment
* @param y_mod y coordinate shift after alignment
*/
2019-06-06 06:05:40 +02:00
void lv_obj_align_origo(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod)
2018-11-22 14:59:38 +01:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-11-22 14:59:38 +01:00
lv_coord_t new_x = lv_obj_get_x(obj);
lv_coord_t new_y = lv_obj_get_y(obj);
2019-04-04 07:15:40 +02:00
lv_coord_t obj_w_half = lv_obj_get_width(obj) / 2;
2018-11-22 14:59:38 +01:00
lv_coord_t obj_h_half = lv_obj_get_height(obj) / 2;
if(base == NULL) {
base = lv_obj_get_parent(obj);
}
LV_ASSERT_OBJ(base, LV_OBJX_NAME);
2018-11-22 14:59:38 +01:00
switch(align) {
case LV_ALIGN_CENTER:
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
break;
case LV_ALIGN_IN_TOP_LEFT:
new_x = -obj_w_half;
new_y = -obj_h_half;
break;
case LV_ALIGN_IN_TOP_MID:
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
new_y = -obj_h_half;
break;
case LV_ALIGN_IN_TOP_RIGHT:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = -obj_h_half;
break;
case LV_ALIGN_IN_BOTTOM_LEFT:
new_x = -obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_IN_BOTTOM_MID:
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_IN_BOTTOM_RIGHT:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_IN_LEFT_MID:
new_x = -obj_w_half;
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
break;
case LV_ALIGN_IN_RIGHT_MID:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
break;
case LV_ALIGN_OUT_TOP_LEFT:
new_x = -obj_w_half;
new_y = -obj_h_half;
break;
case LV_ALIGN_OUT_TOP_MID:
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
new_y = -obj_h_half;
break;
case LV_ALIGN_OUT_TOP_RIGHT:
new_x = lv_obj_get_width(base) - obj_w_half;
2019-04-04 07:15:40 +02:00
new_y = -obj_h_half;
2018-11-22 14:59:38 +01:00
break;
case LV_ALIGN_OUT_BOTTOM_LEFT:
new_x = -obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_OUT_BOTTOM_MID:
new_x = lv_obj_get_width(base) / 2 - obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_OUT_BOTTOM_RIGHT:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_OUT_LEFT_TOP:
2019-04-04 07:15:40 +02:00
new_x = -obj_w_half;
new_y = -obj_h_half;
2018-11-22 14:59:38 +01:00
break;
case LV_ALIGN_OUT_LEFT_MID:
2019-04-04 07:15:40 +02:00
new_x = -obj_w_half;
2018-11-22 14:59:38 +01:00
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
break;
case LV_ALIGN_OUT_LEFT_BOTTOM:
2019-04-04 07:15:40 +02:00
new_x = -obj_w_half;
2018-11-22 14:59:38 +01:00
new_y = lv_obj_get_height(base) - obj_h_half;
break;
case LV_ALIGN_OUT_RIGHT_TOP:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = -obj_h_half;
break;
case LV_ALIGN_OUT_RIGHT_MID:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = lv_obj_get_height(base) / 2 - obj_h_half;
break;
case LV_ALIGN_OUT_RIGHT_BOTTOM:
new_x = lv_obj_get_width(base) - obj_w_half;
new_y = lv_obj_get_height(base) - obj_h_half;
break;
}
/*Bring together the coordination system of base and obj*/
2019-04-04 07:15:40 +02:00
lv_obj_t * par = lv_obj_get_parent(obj);
2018-11-22 14:59:38 +01:00
lv_coord_t base_abs_x = base->coords.x1;
lv_coord_t base_abs_y = base->coords.y1;
2019-04-04 07:15:40 +02:00
lv_coord_t par_abs_x = par->coords.x1;
lv_coord_t par_abs_y = par->coords.y1;
2018-11-22 14:59:38 +01:00
new_x += x_mod + base_abs_x;
new_y += y_mod + base_abs_y;
new_x -= par_abs_x;
new_y -= par_abs_y;
lv_obj_set_pos(obj, new_x, new_y);
#if LV_USE_OBJ_REALIGN
2018-11-22 14:59:38 +01:00
/*Save the last align parameters to use them in `lv_obj_realign`*/
2019-04-04 07:15:40 +02:00
obj->realign.align = align;
obj->realign.xofs = x_mod;
obj->realign.yofs = y_mod;
obj->realign.base = base;
2018-11-22 14:59:38 +01:00
obj->realign.origo_align = 1;
#endif
}
/**
* Realign the object based on the last `lv_obj_align` parameters.
* @param obj pointer to an object
*/
void lv_obj_realign(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
#if LV_USE_OBJ_REALIGN
2019-04-04 07:15:40 +02:00
if(obj->realign.origo_align)
2019-06-06 06:05:40 +02:00
lv_obj_align_origo(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
2019-04-04 07:15:40 +02:00
else
2019-06-06 06:05:40 +02:00
lv_obj_align(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
2018-11-22 14:59:38 +01:00
#else
2019-04-04 07:15:40 +02:00
(void)obj;
LV_LOG_WARN("lv_obj_realaign: no effect because LV_USE_OBJ_REALIGN = 0");
2018-11-22 14:59:38 +01:00
#endif
}
/**
2019-04-04 07:15:40 +02:00
* Enable the automatic realign of the object when its size has changed based on the last
* `lv_obj_align` parameters.
2018-11-22 14:59:38 +01:00
* @param obj pointer to an object
* @param en true: enable auto realign; false: disable auto realign
*/
void lv_obj_set_auto_realign(lv_obj_t * obj, bool en)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
#if LV_USE_OBJ_REALIGN
2018-11-22 14:59:38 +01:00
obj->realign.auto_realign = en ? 1 : 0;
#else
2019-04-04 07:15:40 +02:00
(void)obj;
(void)en;
LV_LOG_WARN("lv_obj_set_auto_realign: no effect because LV_USE_OBJ_REALIGN = 0");
2018-11-22 14:59:38 +01:00
#endif
2016-06-08 07:25:08 +02:00
}
/**
* Set the size of an extended clickable area
2019-05-24 06:26:56 +02:00
* If TINY mode is used, only the largest of the horizontal and vertical padding
* values are considered.
* @param obj pointer to an object
* @param left extended clickable are on the left [px]
* @param right extended clickable are on the right [px]
* @param top extended clickable are on the top [px]
* @param bottom extended clickable are on the bottom [px]
*/
void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t left, lv_coord_t right, lv_coord_t top, lv_coord_t bottom)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-24 06:26:56 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
obj->ext_click_pad.x1 = left;
obj->ext_click_pad.x2 = right;
obj->ext_click_pad.y1 = top;
obj->ext_click_pad.y2 = bottom;
2019-05-24 06:26:56 +02:00
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
obj->ext_click_pad_hor = LV_MATH_MAX(left, right);
obj->ext_click_pad_ver = LV_MATH_MAX(top, bottom);
2019-06-06 06:04:15 +02:00
#else
2019-06-06 06:05:40 +02:00
(void)obj; /*Unused*/
(void)left; /*Unused*/
(void)right; /*Unused*/
(void)top; /*Unused*/
(void)bottom; /*Unused*/
#endif
2019-05-24 06:26:56 +02:00
}
2016-06-08 07:25:08 +02:00
/*---------------------
2018-06-19 09:49:58 +02:00
* Appearance set
2016-06-08 07:25:08 +02:00
*--------------------*/
/**
* Set a new style for an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param style_p pointer to the new style
*/
2019-04-11 19:59:55 +08:00
void lv_obj_set_style(lv_obj_t * obj, const lv_style_t * style)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-11-17 06:23:26 +01:00
if(style) {
LV_ASSERT_STYLE(style);
}
2016-12-15 10:31:30 +01:00
obj->style_p = style;
/*Send a signal about style change to every children with NULL style*/
2019-02-12 16:41:26 +01:00
refresh_children_style(obj);
2016-06-08 07:25:08 +02:00
2017-10-10 16:15:59 +02:00
/*Notify the object about the style change too*/
lv_obj_refresh_style(obj);
2016-06-08 07:25:08 +02:00
}
/**
* Notify an object about its style is modified
* @param obj pointer to an object
*/
void lv_obj_refresh_style(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_obj_invalidate(obj);
obj->signal_cb(obj, LV_SIGNAL_STYLE_CHG, NULL);
lv_obj_invalidate(obj);
}
/**
* Notify all object if a style is modified
* @param style pointer to a style. Only the objects with this style will be notified
* (NULL to notify all objects)
*/
2017-12-17 01:54:09 +01:00
void lv_obj_report_style_mod(lv_style_t * style)
{
LV_ASSERT_STYLE(style);
2019-02-10 11:06:47 +01:00
lv_disp_t * d = lv_disp_get_next(NULL);
2018-10-17 11:27:08 +02:00
2019-02-10 11:06:47 +01:00
while(d) {
lv_obj_t * i;
2019-04-04 07:15:40 +02:00
LV_LL_READ(d->scr_ll, i)
{
2019-02-10 11:06:47 +01:00
if(i->style_p == style || style == NULL) {
lv_obj_refresh_style(i);
}
2018-10-17 11:27:08 +02:00
2019-02-10 11:06:47 +01:00
report_style_mod_core(style, i);
}
2019-02-20 10:16:33 +01:00
d = lv_disp_get_next(d);
}
}
2016-06-08 07:25:08 +02:00
/*-----------------
* Attribute set
*----------------*/
/**
* Hide an object. It won't be visible and clickable.
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-12-15 10:31:30 +01:00
* @param en true: hide the object
2016-06-08 07:25:08 +02:00
*/
2016-12-15 10:31:30 +01:00
void lv_obj_set_hidden(lv_obj_t * obj, bool en)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-06-06 06:05:40 +02:00
if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
2018-05-16 23:19:22 +02:00
2016-12-15 10:31:30 +01:00
obj->hidden = en == false ? 0 : 1;
2018-05-16 23:19:22 +02:00
2019-06-06 06:05:40 +02:00
if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
2018-05-16 23:19:22 +02:00
2016-10-07 11:15:46 +02:00
lv_obj_t * par = lv_obj_get_parent(obj);
par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
2016-06-08 07:25:08 +02:00
}
/**
* Enable or disable the clicking of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-12-15 10:31:30 +01:00
* @param en true: make the object clickable
2016-06-08 07:25:08 +02:00
*/
2016-12-15 10:31:30 +01:00
void lv_obj_set_click(lv_obj_t * obj, bool en)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->click = (en == true ? 1 : 0);
2016-06-08 07:25:08 +02:00
}
/**
* Enable to bring this object to the foreground if it
* or any of its children is clicked
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-12-15 10:31:30 +01:00
* @param en true: enable the auto top feature
2016-06-08 07:25:08 +02:00
*/
2016-12-15 10:31:30 +01:00
void lv_obj_set_top(lv_obj_t * obj, bool en)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->top = (en == true ? 1 : 0);
2016-06-08 07:25:08 +02:00
}
/**
* Enable the dragging of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-12-15 10:31:30 +01:00
* @param en true: make the object dragable
2016-06-08 07:25:08 +02:00
*/
2016-12-15 10:31:30 +01:00
void lv_obj_set_drag(lv_obj_t * obj, bool en)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-04 07:15:40 +02:00
if(en == true) lv_obj_set_click(obj, true); /*Drag is useless without enabled clicking*/
obj->drag = (en == true ? 1 : 0);
2016-06-08 07:25:08 +02:00
}
/**
* Set the directions an object can be dragged in
* @param obj pointer to an object
* @param drag_dir bitwise OR of allowed directions an object can be dragged in
*/
void lv_obj_set_drag_dir(lv_obj_t * obj, lv_drag_dir_t drag_dir)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->drag_dir = drag_dir;
2019-04-10 06:21:54 +02:00
if(obj->drag_dir != 0) lv_obj_set_drag(obj, true); /*Drag direction requires drag*/
2016-06-08 07:25:08 +02:00
}
/**
* Enable the throwing of an object after is is dragged
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-12-15 10:31:30 +01:00
* @param en true: enable the drag throw
2016-06-08 07:25:08 +02:00
*/
2016-12-15 10:31:30 +01:00
void lv_obj_set_drag_throw(lv_obj_t * obj, bool en)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->drag_throw = (en == true ? 1 : 0);
2016-06-08 07:25:08 +02:00
}
/**
2018-06-19 09:49:58 +02:00
* Enable to use parent for drag related operations.
* If trying to drag the object the parent will be moved instead
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-12-15 10:31:30 +01:00
* @param en true: enable the 'drag parent' for the object
2016-06-08 07:25:08 +02:00
*/
2016-12-15 10:31:30 +01:00
void lv_obj_set_drag_parent(lv_obj_t * obj, bool en)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-12-15 10:31:30 +01:00
obj->drag_parent = (en == true ? 1 : 0);
2016-06-08 07:25:08 +02:00
}
2019-03-12 06:20:45 +01:00
/**
* Propagate the events to the parent too
* @param obj pointer to an object
* @param en true: enable the event propagation
*/
void lv_obj_set_parent_event(lv_obj_t * obj, bool en)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-03-12 06:20:45 +01:00
obj->parent_event = (en == true ? 1 : 0);
}
void lv_obj_set_base_dir(lv_obj_t * obj, lv_bidi_dir_t dir)
{
if(dir != LV_BIDI_DIR_LTR && dir != LV_BIDI_DIR_RTL &&
dir != LV_BIDI_DIR_AUTO && dir != LV_BIDI_DIR_INHERIT) {
LV_LOG_WARN("lv_obj_set_base_dir: invalid base dir");
return;
}
obj->base_dir = dir;
lv_signal_send(obj, LV_SIGNAL_BASE_DIR_CHG, NULL);
/* Notify the children about the parent base dir has changed.
* (The children might have `LV_BIDI_DIR_INHERIT`)*/
base_dir_refr_children(obj);
}
2018-06-14 13:08:19 +02:00
/**
* Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`)
* @param obj pointer to an object
* @param en true: opa scaling is enabled for this object and all children; false: no opa scaling
*/
void lv_obj_set_opa_scale_enable(lv_obj_t * obj, bool en)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-04 07:15:40 +02:00
obj->opa_scale_en = en ? 1 : 0;
2018-06-14 13:08:19 +02:00
}
/**
2019-07-05 07:19:09 +02:00
* Set the opa scale of an object.
* The opacity of this object and all it's children will be scaled down with this factor.
* `lv_obj_set_opa_scale_enable(obj, true)` needs to be called to enable it.
* (not for all children just for the parent where to start the opa scaling)
2018-06-14 13:08:19 +02:00
* @param obj pointer to an object
* @param opa_scale a factor to scale down opacity [0..255]
*/
void lv_obj_set_opa_scale(lv_obj_t * obj, lv_opa_t opa_scale)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-14 13:08:19 +02:00
obj->opa_scale = opa_scale;
lv_obj_invalidate(obj);
}
/**
* Set a bit or bits in the protect filed
* @param obj pointer to an object
2018-08-28 08:04:42 +02:00
* @param prot 'OR'-ed values from `lv_protect_t`
*/
void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->protect |= prot;
}
/**
* Clear a bit or bits in the protect filed
* @param obj pointer to an object
2018-08-28 08:04:42 +02:00
* @param prot 'OR'-ed values from `lv_protect_t`
*/
void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
prot = (~prot) & 0xFF;
obj->protect &= prot;
}
2016-06-08 07:25:08 +02:00
/**
* Set a an event handler function for an object.
* Used by the user to react on event which happens with the object.
* @param obj pointer to an object
* @param event_cb the new event function
*/
void lv_obj_set_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->event_cb = event_cb;
}
/**
* Send an event to the object
* @param obj pointer to an object
* @param event the type of the event from `lv_event_t`
* @param data arbitrary data depending on the object type and the event. (Usually `NULL`)
* @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event
*/
lv_res_t lv_event_send(lv_obj_t * obj, lv_event_t event, const void * data)
{
if(obj == NULL) return LV_RES_OK;
2019-03-03 11:20:49 +01:00
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-07 12:15:02 +02:00
lv_res_t res;
res = lv_event_send_func(obj->event_cb, obj, event, data);
return res;
}
/**
* Call an event function with an object, event, and data.
2019-06-12 23:10:54 +02:00
* @param event_xcb an event callback function. If `NULL` `LV_RES_OK` will return without any actions.
* (the 'x' in the argument name indicates that its not a fully generic function because it not follows
* the `func_name(object, callback, ...)` convention)
2019-05-07 12:15:02 +02:00
* @param obj pointer to an object to associate with the event (can be `NULL` to simply call the `event_cb`)
* @param event an event
* @param data pointer to a custom data
* @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event
*/
2019-06-12 23:10:54 +02:00
lv_res_t lv_event_send_func(lv_event_cb_t event_xcb, lv_obj_t * obj, lv_event_t event, const void * data)
2019-05-07 12:15:02 +02:00
{
/* Build a simple linked list from the objects used in the events
* It's important to know if an this object was deleted by a nested event
* called from this `even_cb`. */
lv_event_temp_data_t event_temp_data;
2019-06-06 06:05:40 +02:00
event_temp_data.obj = obj;
event_temp_data.deleted = false;
2019-06-06 06:05:40 +02:00
event_temp_data.prev = NULL;
if(event_temp_data_head) {
event_temp_data.prev = event_temp_data_head;
}
event_temp_data_head = &event_temp_data;
2019-03-15 22:19:21 +01:00
const void * event_act_data_save = event_act_data;
2019-06-27 07:16:15 +02:00
event_act_data = data;
2019-05-07 12:15:02 +02:00
/*Call the input device's feedback callback if set*/
lv_indev_t * indev_act = lv_indev_get_act();
if(indev_act) {
if(indev_act->driver.feedback_cb) indev_act->driver.feedback_cb(&indev_act->driver, event);
}
/*Call the event callback itself*/
2019-06-12 23:10:54 +02:00
if(event_xcb) event_xcb(obj, event);
2019-03-14 05:34:39 +01:00
/*Restore the event data*/
event_act_data = event_act_data_save;
/*Remove this element from the list*/
event_temp_data_head = event_temp_data_head->prev;
if(event_temp_data.deleted) {
return LV_RES_INV;
}
2019-05-07 12:15:02 +02:00
if(obj) {
if(obj->parent_event && obj->par) {
lv_res_t res = lv_event_send(obj->par, event, data);
if(res != LV_RES_OK) {
return LV_RES_INV;
}
}
}
return LV_RES_OK;
}
/**
* Get the `data` parameter of the current event
* @return the `data` parameter
*/
const void * lv_event_get_data(void)
{
2019-05-07 12:15:02 +02:00
return event_act_data;
}
/**
* Set the a signal function of an object. Used internally by the library.
2016-06-08 07:25:08 +02:00
* Always call the previous signal function in the new.
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
* @param cb the new signal function
*/
void lv_obj_set_signal_cb(lv_obj_t * obj, lv_signal_cb_t signal_cb)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->signal_cb = signal_cb;
}
/**
* Send an event to the object
* @param obj pointer to an object
* @param event the type of the event from `lv_event_t`.
2016-06-08 07:25:08 +02:00
*/
void lv_signal_send(lv_obj_t * obj, lv_signal_t signal, void * param)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
if(obj->signal_cb) obj->signal_cb(obj, signal, param);
2016-06-08 07:25:08 +02:00
}
/**
* Set a new design function for an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
* @param design_cb the new design function
2016-06-08 07:25:08 +02:00
*/
void lv_obj_set_design_cb(lv_obj_t * obj, lv_design_cb_t design_cb)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->design_cb = design_cb;
2016-06-08 07:25:08 +02:00
}
/*----------------
* Other set
*--------------*/
/**
* Allocate a new ext. data for an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @param ext_size the size of the new ext. data
* @return pointer to the allocated ext.
* If out of memory NULL is returned and the original ext is preserved
2016-06-08 07:25:08 +02:00
*/
void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
void * new_ext = lv_mem_realloc(obj->ext_attr, ext_size);
if(new_ext == NULL) return NULL;
2018-06-19 09:49:58 +02:00
obj->ext_attr = new_ext;
2018-06-19 09:49:58 +02:00
return (void *)obj->ext_attr;
2016-06-08 07:25:08 +02:00
}
2016-10-17 15:02:18 +02:00
/**
* Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_draw_pad(lv_obj_t * obj)
2016-10-17 15:02:18 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->ext_draw_pad = 0;
obj->signal_cb(obj, LV_SIGNAL_REFR_EXT_DRAW_PAD, NULL);
2016-10-17 15:02:18 +02:00
2018-06-19 09:49:58 +02:00
lv_obj_invalidate(obj);
2016-10-17 15:02:18 +02:00
}
2016-06-08 07:25:08 +02:00
/*=======================
* Getter functions
*======================*/
/**
* Return with the screen of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return pointer to a screen
*/
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-10-05 17:22:49 +02:00
const lv_obj_t * par = obj;
const lv_obj_t * act_p;
2018-06-19 09:49:58 +02:00
2016-06-08 07:25:08 +02:00
do {
2016-10-07 11:15:46 +02:00
act_p = par;
2019-04-04 07:15:40 +02:00
par = lv_obj_get_parent(act_p);
2018-06-19 09:49:58 +02:00
} while(par != NULL);
return (lv_obj_t *)act_p;
2016-06-08 07:25:08 +02:00
}
2019-02-20 23:58:13 +01:00
/**
* Get the display of an object
* @param scr pointer to an object
* @return pointer the object's display
*/
lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-02-20 23:58:13 +01:00
const lv_obj_t * scr;
2019-04-04 07:15:40 +02:00
if(obj->par == NULL)
scr = obj; /*`obj` is a screen*/
else
scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
2019-02-20 23:58:13 +01:00
lv_disp_t * d;
2019-04-04 07:15:40 +02:00
LV_LL_READ(LV_GC_ROOT(_lv_disp_ll), d)
{
lv_obj_t * s;
2019-04-04 07:15:40 +02:00
LV_LL_READ(d->scr_ll, s)
{
if(s == scr) return d;
}
}
LV_LOG_WARN("lv_scr_get_disp: screen not found")
return NULL;
}
2016-06-08 07:25:08 +02:00
/*---------------------
* Parent/children get
*--------------------*/
/**
* Returns with the parent of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2018-06-19 09:49:58 +02:00
* @return pointer to the parent of 'obj'
2016-06-08 07:25:08 +02:00
*/
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-10-07 11:15:46 +02:00
return obj->par;
2016-06-08 07:25:08 +02:00
}
/**
* Iterate through the children of an object (start from the "youngest")
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
* @param child NULL at first call to get the next children
2016-06-08 07:25:08 +02:00
* and the previous return value later
2016-10-07 11:15:46 +02:00
* @return the child after 'act_child' or NULL if no more child
2016-06-08 07:25:08 +02:00
*/
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_obj_t * result = NULL;
2018-06-19 09:49:58 +02:00
if(child == NULL) {
result = lv_ll_get_head(&obj->child_ll);
2018-06-19 09:49:58 +02:00
} else {
result = lv_ll_get_next(&obj->child_ll, child);
2018-06-19 09:49:58 +02:00
}
2016-06-08 07:25:08 +02:00
return result;
2016-06-08 07:25:08 +02:00
}
/**
* Iterate through the children of an object (start from the "oldest")
* @param obj pointer to an object
* @param child NULL at first call to get the next children
* and the previous return value later
* @return the child after 'act_child' or NULL if no more child
*/
lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_obj_t * result = NULL;
if(child == NULL) {
result = lv_ll_get_tail(&obj->child_ll);
} else {
result = lv_ll_get_prev(&obj->child_ll, child);
}
return result;
}
/**
* Count the children of an object (only children directly on 'obj')
* @param obj pointer to an object
* @return children number of 'obj'
*/
uint16_t lv_obj_count_children(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
lv_obj_t * i;
uint16_t cnt = 0;
LV_LL_READ(obj->child_ll, i) cnt++;
2018-06-19 09:49:58 +02:00
return cnt;
}
/** Recursively count the children of an object
* @param obj pointer to an object
* @return children number of 'obj'
*/
2019-06-06 06:05:40 +02:00
uint16_t lv_obj_count_children_recursive(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_obj_t * i;
uint16_t cnt = 0;
2019-06-06 06:05:40 +02:00
LV_LL_READ(obj->child_ll, i)
{
2019-07-23 08:23:20 +02:00
cnt++; /*Count the child*/
cnt += lv_obj_count_children_recursive(i); /*recursively count children's children*/
}
return cnt;
}
2016-06-08 07:25:08 +02:00
/*---------------------
* Coordinate get
*--------------------*/
/**
* Copy the coordinates of an object to an area
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2018-06-19 09:49:58 +02:00
* @param cords_p pointer to an area to store the coordinates
2016-06-08 07:25:08 +02:00
*/
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2017-11-28 16:15:13 +01:00
lv_area_copy(cords_p, &obj->coords);
2016-06-08 07:25:08 +02:00
}
/**
* Reduce area retried by `lv_obj_get_coords()` the get graphically usable area of an object.
* (Without the size of the border or other extra graphical elements)
* @param coords_p store the result area here
*/
2019-06-27 07:16:15 +02:00
void lv_obj_get_inner_coords(const lv_obj_t * obj, lv_area_t * coords_p)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-06-27 07:16:15 +02:00
const lv_style_t * style = lv_obj_get_style(obj);
if(style->body.border.part & LV_BORDER_PART_LEFT) coords_p->x1 += style->body.border.width;
if(style->body.border.part & LV_BORDER_PART_RIGHT) coords_p->x2 -= style->body.border.width;
if(style->body.border.part & LV_BORDER_PART_TOP) coords_p->y1 += style->body.border.width;
if(style->body.border.part & LV_BORDER_PART_BOTTOM) coords_p->y2 -= style->body.border.width;
}
2016-06-08 07:25:08 +02:00
/**
* Get the x coordinate of object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2018-06-19 09:49:58 +02:00
* @return distance of 'obj' from the left side of its parent
2016-06-08 07:25:08 +02:00
*/
lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2017-11-23 21:28:36 +01:00
lv_coord_t rel_x;
2016-10-07 11:15:46 +02:00
lv_obj_t * parent = lv_obj_get_parent(obj);
2019-10-10 14:34:39 +02:00
if(parent) {
rel_x = obj->coords.x1 - parent->coords.x1;
} else {
rel_x = obj->coords.x1;
}
2016-06-08 07:25:08 +02:00
return rel_x;
}
/**
* Get the y coordinate of object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2018-06-19 09:49:58 +02:00
* @return distance of 'obj' from the top of its parent
2016-06-08 07:25:08 +02:00
*/
lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2017-11-23 21:28:36 +01:00
lv_coord_t rel_y;
2016-10-07 11:15:46 +02:00
lv_obj_t * parent = lv_obj_get_parent(obj);
2019-10-10 14:34:39 +02:00
if(parent) {
rel_y = obj->coords.y1 - parent->coords.y1;
} else {
rel_y = obj->coords.y1;
}
2016-06-08 07:25:08 +02:00
return rel_y;
}
/**
* Get the width of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return the width
*/
lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2017-11-28 16:15:13 +01:00
return lv_area_get_width(&obj->coords);
2016-06-08 07:25:08 +02:00
}
/**
* Get the height of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return the height
*/
lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2017-11-28 16:15:13 +01:00
return lv_area_get_height(&obj->coords);
2016-06-08 07:25:08 +02:00
}
/**
* Get that width reduced by the left and right padding.
* @param obj pointer to an object
* @return the width which still fits into the container
*/
lv_coord_t lv_obj_get_width_fit(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-11 19:59:55 +08:00
const lv_style_t * style = lv_obj_get_style(obj);
2019-03-15 10:18:44 +01:00
return lv_obj_get_width(obj) - style->body.padding.left - style->body.padding.right;
}
/**
* Get that height reduced by the top an bottom padding.
* @param obj pointer to an object
* @return the height which still fits into the container
*/
lv_coord_t lv_obj_get_height_fit(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-11 19:59:55 +08:00
const lv_style_t * style = lv_obj_get_style(obj);
return lv_obj_get_height(obj) - style->body.padding.top - style->body.padding.bottom;
}
2016-10-17 15:02:18 +02:00
2018-11-22 14:59:38 +01:00
/**
* Get the automatic realign property of the object.
* @param obj pointer to an object
* @return true: auto realign is enabled; false: auto realign is disabled
*/
bool lv_obj_get_auto_realign(const lv_obj_t * obj)
2018-11-22 14:59:38 +01:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
#if LV_USE_OBJ_REALIGN
2018-11-22 14:59:38 +01:00
return obj->realign.auto_realign ? true : false;
#else
2019-04-04 07:15:40 +02:00
(void)obj;
2018-11-22 14:59:38 +01:00
return false;
#endif
}
2019-05-24 06:26:56 +02:00
/**
* Get the left padding of extended clickable area
* @param obj pointer to an object
* @return the extended left padding
*/
lv_coord_t lv_obj_get_ext_click_pad_left(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
2019-05-24 06:26:56 +02:00
return obj->ext_click_pad_hor;
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
return obj->ext_click_pad.x1;
#else
2019-06-06 06:05:40 +02:00
(void)obj; /*Unused*/
2019-05-24 06:26:56 +02:00
return 0;
#endif
}
/**
2019-05-24 06:26:56 +02:00
* Get the right padding of extended clickable area
* @param obj pointer to an object
2019-05-24 06:26:56 +02:00
* @return the extended right padding
*/
2019-05-24 06:26:56 +02:00
lv_coord_t lv_obj_get_ext_click_pad_right(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-24 06:26:56 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
return obj->ext_click_pad_hor;
2019-05-24 06:26:56 +02:00
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
return obj->ext_click_pad.x2;
#else
2019-06-06 06:05:40 +02:00
(void)obj; /*Unused*/
2019-05-24 06:26:56 +02:00
return 0;
#endif
}
/**
2019-05-24 06:26:56 +02:00
* Get the top padding of extended clickable area
* @param obj pointer to an object
2019-05-24 06:26:56 +02:00
* @return the extended top padding
*/
2019-05-24 06:26:56 +02:00
lv_coord_t lv_obj_get_ext_click_pad_top(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-24 06:26:56 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
return obj->ext_click_pad_ver;
2019-05-24 06:26:56 +02:00
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
return obj->ext_click_pad.y1;
#else
2019-06-06 06:05:40 +02:00
(void)obj; /*Unused*/
2019-05-24 06:26:56 +02:00
return 0;
#endif
2019-05-24 06:26:56 +02:00
}
2016-10-17 15:02:18 +02:00
/**
2019-05-24 06:26:56 +02:00
* Get the bottom padding of extended clickable area
2016-10-17 15:02:18 +02:00
* @param obj pointer to an object
2019-05-24 06:26:56 +02:00
* @return the extended bottom padding
2016-10-17 15:02:18 +02:00
*/
2019-05-24 06:26:56 +02:00
lv_coord_t lv_obj_get_ext_click_pad_bottom(const lv_obj_t * obj)
2016-10-17 15:02:18 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-24 06:26:56 +02:00
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
return obj->ext_click_pad_ver;
2019-05-24 06:26:56 +02:00
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
return obj->ext_click_pad.y2;
#else
2019-06-06 06:05:40 +02:00
(void)obj; /*Unused*/
2019-05-24 06:26:56 +02:00
return 0;
#endif
2019-05-24 06:26:56 +02:00
}
2016-10-17 15:02:18 +02:00
2018-11-22 14:59:38 +01:00
/**
* Get the extended size attribute of an object
2018-11-22 14:59:38 +01:00
* @param obj pointer to an object
* @return the extended size attribute
2018-11-22 14:59:38 +01:00
*/
lv_coord_t lv_obj_get_ext_draw_pad(const lv_obj_t * obj)
2018-11-22 14:59:38 +01:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->ext_draw_pad;
2018-11-22 14:59:38 +01:00
}
2016-06-08 07:25:08 +02:00
/*-----------------
* Appearance get
*---------------*/
/**
* Get the style pointer of an object (if NULL get style of the parent)
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return pointer to a style
*/
2019-04-11 19:59:55 +08:00
const lv_style_t * lv_obj_get_style(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-11 19:59:55 +08:00
const lv_style_t * style_act = obj->style_p;
2017-07-19 15:19:10 +02:00
if(style_act == NULL) {
lv_obj_t * par = obj->par;
2016-06-08 07:25:08 +02:00
while(par) {
if(par->style_p) {
2017-10-19 12:46:49 +02:00
if(par->style_p->glass == 0) {
#if LV_USE_GROUP == 0
2017-07-19 15:19:10 +02:00
style_act = par->style_p;
#else
2019-06-11 13:51:14 +02:00
/*If a parent is focused then use then focused style*/
2018-06-19 09:49:58 +02:00
lv_group_t * g = lv_obj_get_group(par);
if(lv_group_get_focused(g) == par) {
style_act = lv_group_mod_style(g, par->style_p);
} else {
style_act = par->style_p;
}
#endif
2017-07-19 15:19:10 +02:00
break;
}
2017-04-24 16:16:36 +02:00
}
par = par->par;
}
}
#if LV_USE_GROUP
if(obj->group_p) {
if(lv_group_get_focused(obj->group_p) == obj) {
style_act = lv_group_mod_style(obj->group_p, style_act);
2017-07-19 15:19:10 +02:00
}
}
#endif
2017-10-10 16:17:23 +02:00
if(style_act == NULL) style_act = &lv_style_plain;
2017-10-10 16:17:23 +02:00
2017-07-19 15:19:10 +02:00
return style_act;
2016-06-08 07:25:08 +02:00
}
/*-----------------
* Attribute get
*----------------*/
/**
* Get the hidden attribute of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return true: the object is hidden
*/
bool lv_obj_get_hidden(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016-10-07 11:15:46 +02:00
return obj->hidden == 0 ? false : true;
2016-06-08 07:25:08 +02:00
}
/**
* Get the click enable attribute of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return true: the object is clickable
*/
bool lv_obj_get_click(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->click == 0 ? false : true;
2016-06-08 07:25:08 +02:00
}
/**
* Get the top enable attribute of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return true: the auto top feture is enabled
*/
bool lv_obj_get_top(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->top == 0 ? false : true;
2016-06-08 07:25:08 +02:00
}
/**
* Get the drag enable attribute of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return true: the object is dragable
*/
bool lv_obj_get_drag(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
return obj->drag == 0 ? false : true;
2016-06-08 07:25:08 +02:00
}
/**
* Get the directions an object can be dragged
* @param obj pointer to an object
* @return bitwise OR of allowed directions an object can be dragged in
*/
lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->drag_dir;
}
2016-06-08 07:25:08 +02:00
/**
2018-12-26 07:58:06 +01:00
* Get the drag throw enable attribute of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return true: drag throw is enabled
*/
bool lv_obj_get_drag_throw(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->drag_throw == 0 ? false : true;
2016-06-08 07:25:08 +02:00
}
/**
* Get the drag parent attribute of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return true: drag parent is enabled
*/
bool lv_obj_get_drag_parent(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
2016-10-07 11:15:46 +02:00
return obj->drag_parent == 0 ? false : true;
2016-06-08 07:25:08 +02:00
}
2019-03-12 06:20:45 +01:00
/**
* Get the drag parent attribute of an object
* @param obj pointer to an object
* @return true: drag parent is enabled
*/
bool lv_obj_get_parent_event(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-03-12 06:20:45 +01:00
return obj->parent_event == 0 ? false : true;
}
lv_bidi_dir_t lv_obj_get_base_dir(const lv_obj_t * obj)
{
#if LV_USE_BIDI
const lv_obj_t * parent = obj;
while(parent) {
if(parent->base_dir != LV_BIDI_DIR_INHERIT) return parent->base_dir;
parent = lv_obj_get_parent(parent);
}
return LV_BIDI_BASE_DIR_DEF;
#else
2019-11-25 06:44:13 +01:00
(void) obj; /*Unused*/
return LV_BIDI_DIR_LTR;
#endif
}
2018-06-14 13:08:19 +02:00
/**
* Get the opa scale enable parameter
* @param obj pointer to an object
* @return true: opa scaling is enabled for this object and all children; false: no opa scaling
2018-06-14 13:08:19 +02:00
*/
lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj)
2018-06-14 13:08:19 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-14 13:08:19 +02:00
return obj->opa_scale_en == 0 ? false : true;
}
/**
* Get the opa scale parameter of an object
* @param obj pointer to an object
* @return opa scale [0..255]
*/
lv_opa_t lv_obj_get_opa_scale(const lv_obj_t * obj)
2018-06-14 13:08:19 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-10-05 17:22:49 +02:00
const lv_obj_t * parent = obj;
2018-06-14 13:08:19 +02:00
2018-06-19 09:49:58 +02:00
while(parent) {
if(parent->opa_scale_en) return parent->opa_scale;
parent = lv_obj_get_parent(parent);
}
2018-06-14 13:08:19 +02:00
2018-06-19 09:49:58 +02:00
return LV_OPA_COVER;
2018-06-14 13:08:19 +02:00
}
/**
* Get the protect field of an object
* @param obj pointer to an object
2018-08-28 08:04:42 +02:00
* @return protect field ('OR'ed values of `lv_protect_t`)
*/
uint8_t lv_obj_get_protect(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-04 07:15:40 +02:00
return obj->protect;
}
/**
* Check at least one bit of a given protect bitfield is set
* @param obj pointer to an object
2018-08-28 08:04:42 +02:00
* @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
* @return false: none of the given bits are set, true: at least one bit is set
*/
bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-04 07:15:40 +02:00
return (obj->protect & prot) == 0 ? false : true;
}
2016-06-08 07:25:08 +02:00
/**
* Get the signal function of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return the signal function
*/
lv_signal_cb_t lv_obj_get_signal_cb(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->signal_cb;
2016-06-08 07:25:08 +02:00
}
/**
* Get the design function of an object
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return the design function
*/
lv_design_cb_t lv_obj_get_design_cb(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->design_cb;
2016-06-08 07:25:08 +02:00
}
/**
* Get the event function of an object
* @param obj pointer to an object
* @return the event function
*/
lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->event_cb;
}
2016-06-08 07:25:08 +02:00
/*------------------
* Other get
*-----------------*/
/**
* Get the ext pointer
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
* @return the ext pointer but not the dynamic version
* Use it as ext->data1, and NOT da(ext)->data1
*/
void * lv_obj_get_ext_attr(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-06-19 09:49:58 +02:00
return obj->ext_attr;
2016-06-08 07:25:08 +02:00
}
2018-02-28 15:37:41 +01:00
/**
* Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
* E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
* @param obj pointer to an object which type should be get
* @param buf pointer to an `lv_obj_type_t` buffer to store the types
*/
void lv_obj_get_type(const lv_obj_t * obj, lv_obj_type_t * buf)
2018-02-28 15:37:41 +01:00
{
LV_ASSERT_NULL(buf);
LV_ASSERT_NULL(obj);
2018-02-28 15:37:41 +01:00
lv_obj_type_t tmp;
memset(buf, 0, sizeof(lv_obj_type_t));
memset(&tmp, 0, sizeof(lv_obj_type_t));
obj->signal_cb((lv_obj_t *)obj, LV_SIGNAL_GET_TYPE, &tmp);
2018-02-28 15:37:41 +01:00
uint8_t cnt;
for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
if(tmp.type[cnt] == NULL) break;
}
/*Swap the order. The real type comes first*/
uint8_t i;
for(i = 0; i < cnt; i++) {
buf->type[i] = tmp.type[cnt - 1 - i];
}
}
#if LV_USE_USER_DATA
2019-05-07 12:15:02 +02:00
2016-06-08 07:25:08 +02:00
/**
* Get the object's user data
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
* @return user data
2016-06-08 07:25:08 +02:00
*/
lv_obj_user_data_t lv_obj_get_user_data(const lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-05-07 12:15:02 +02:00
return obj->user_data;
}
/**
* Get a pointer to the object's user data
* @param obj pointer to an object
* @return pointer to the user data
*/
lv_obj_user_data_t * lv_obj_get_user_data_ptr(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return (lv_obj_user_data_t *)&obj->user_data;
2016-06-08 07:25:08 +02:00
}
2019-04-14 13:24:12 +08:00
/**
2019-04-18 14:12:30 +08:00
* Set the object's user data. The data will be copied.
2019-04-14 13:24:12 +08:00
* @param obj pointer to an object
* @param data user data
*/
void lv_obj_set_user_data(lv_obj_t * obj, lv_obj_user_data_t data)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2019-04-18 14:12:30 +08:00
memcpy(&obj->user_data, &data, sizeof(lv_obj_user_data_t));
2019-04-14 13:24:12 +08:00
}
2017-04-24 16:16:36 +02:00
#endif
2016-06-08 07:25:08 +02:00
#if LV_USE_GROUP
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
void * lv_obj_get_group(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->group_p;
}
2018-07-13 00:37:28 +02:00
/**
2019-05-03 19:25:58 +02:00
* Tell whether the object is the focused object of a group or not.
2018-07-13 00:37:28 +02:00
* @param obj pointer to an object
* @return true: the object is focused, false: the object is not focused or not in a group
*/
bool lv_obj_is_focused(const lv_obj_t * obj)
2018-07-13 00:37:28 +02:00
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2018-10-05 17:22:49 +02:00
if(obj->group_p) {
if(lv_group_get_focused(obj->group_p) == obj) return true;
}
2018-07-13 00:37:28 +02:00
return false;
}
#endif
/*-------------------
* OTHER FUNCTIONS
*------------------*/
/**
* Used in the signal callback to handle `LV_SIGNAL_GET_TYPE` signal
* @param obj pointer to an object
* @param buf pointer to `lv_obj_type_t`. (`param` in the signal callback)
* @param name name of the object. E.g. "lv_btn". (Only the pointer is saved)
* @return LV_RES_OK
*/
lv_res_t lv_obj_handle_get_type_signal(lv_obj_type_t * buf, const char * name)
{
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = name;
return LV_RES_OK;
}
2016-06-08 07:25:08 +02:00
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_del_async_cb(void * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
lv_obj_del(obj);
}
2016-06-08 07:25:08 +02:00
/**
* Handle the drawing related tasks of the base objects.
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2019-09-06 19:53:39 +02:00
* @param clip_area the object will be drawn only in this area
2016-06-08 07:25:08 +02:00
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
2019-09-06 19:53:39 +02:00
* @param return an element of `lv_design_res_t`
2016-06-08 07:25:08 +02:00
*/
2019-09-06 19:53:39 +02:00
static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode)
2016-06-08 07:25:08 +02:00
{
if(mode == LV_DESIGN_COVER_CHK) {
/*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
2019-09-06 19:53:39 +02:00
if(lv_area_is_in(clip_area, &obj->coords) == false) return LV_DESIGN_RES_NOT_COVER;
2019-04-11 19:59:55 +08:00
const lv_style_t * style = lv_obj_get_style(obj);
2019-10-16 20:25:53 +02:00
if(style->body.corner_mask) return LV_DESIGN_RES_MASKED;
/*Can cover the area only if fully solid (no opacity)*/
2019-09-06 19:53:39 +02:00
if(style->body.opa < LV_OPA_MAX) return LV_DESIGN_RES_NOT_COVER;
2016-06-08 07:25:08 +02:00
/* Because of the radius it is not sure the area is covered
* Check the areas where there is no radius*/
lv_coord_t r = style->body.radius;
2019-09-06 19:53:39 +02:00
if(r == LV_RADIUS_CIRCLE) return LV_DESIGN_RES_NOT_COVER;
2017-11-23 21:28:36 +01:00
lv_area_t area_tmp;
/*Check horizontally without radius*/
lv_obj_get_coords(obj, &area_tmp);
area_tmp.x1 += r;
area_tmp.x2 -= r;
2019-09-06 19:53:39 +02:00
if(lv_area_is_in(clip_area, &area_tmp) == false) return LV_DESIGN_RES_NOT_COVER;
/*Check vertically without radius*/
lv_obj_get_coords(obj, &area_tmp);
area_tmp.y1 += r;
area_tmp.y2 -= r;
2019-09-06 19:53:39 +02:00
if(lv_area_is_in(clip_area, &area_tmp) == false) return LV_DESIGN_RES_NOT_COVER;
return LV_DESIGN_RES_COVER;
2019-10-16 20:25:53 +02:00
}
else if(mode == LV_DESIGN_DRAW_MAIN) {
2019-04-11 19:59:55 +08:00
const lv_style_t * style = lv_obj_get_style(obj);
2019-09-06 19:53:39 +02:00
lv_draw_rect(&obj->coords, clip_area, style, lv_obj_get_opa_scale(obj));
2019-10-16 20:25:53 +02:00
if(style->body.corner_mask) {
lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
lv_draw_mask_radius_init(mp, &obj->coords, style->body.radius, false);
2019-11-16 11:22:23 +01:00
/*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
lv_draw_mask_add(mp, obj + 8);
2019-10-16 20:25:53 +02:00
}
}
else if(mode == LV_DESIGN_DRAW_POST) {
const lv_style_t * style = lv_obj_get_style(obj);
if(style->body.corner_mask) {
lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
lv_mem_buf_release(param);
2019-10-16 20:25:53 +02:00
}
}
2019-09-06 19:53:39 +02:00
return LV_DESIGN_RES_OK;
2016-06-08 07:25:08 +02:00
}
2017-11-15 21:06:44 +01:00
/**
* Signal function of the basic object
* @param obj pointer to an object
* @param sign signal type
* @param param parameter for the signal (depends on signal type)
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
{
if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
2017-12-02 20:43:50 +01:00
2017-11-15 21:06:44 +01:00
lv_res_t res = LV_RES_OK;
2018-02-28 15:37:41 +01:00
if(sign == LV_SIGNAL_CHILD_CHG) {
/*Return 'invalid' if the child change signal is not enabled*/
if(lv_obj_is_protected(obj, LV_PROTECT_CHILD_CHG) != false) res = LV_RES_INV;
} else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
const lv_style_t * style = lv_obj_get_style(obj);
lv_coord_t shadow = (style->body.shadow.width >> 1) + 1;
shadow += style->body.shadow.spread;
2019-09-05 21:44:29 +02:00
shadow += LV_MATH_MAX(LV_MATH_ABS(style->body.shadow.offset.x), LV_MATH_ABS(style->body.shadow.offset.y));
if(shadow > obj->ext_draw_pad) obj->ext_draw_pad = shadow;
2019-04-04 07:15:40 +02:00
} else if(sign == LV_SIGNAL_STYLE_CHG) {
lv_obj_refresh_ext_draw_pad(obj);
2017-11-15 21:06:44 +01:00
}
return res;
}
2016-06-08 07:25:08 +02:00
/**
* Reposition the children of an object. (Called recursively)
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object which children will be repositioned
2016-06-08 07:25:08 +02:00
* @param x_diff x coordinate shift
* @param y_diff y coordinate shift
*/
2019-02-12 16:41:26 +01:00
static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff)
2016-06-08 07:25:08 +02:00
{
2018-06-19 09:49:58 +02:00
lv_obj_t * i;
2019-04-04 07:15:40 +02:00
LV_LL_READ(obj->child_ll, i)
{
i->coords.x1 += x_diff;
i->coords.y1 += y_diff;
i->coords.x2 += x_diff;
i->coords.y2 += y_diff;
2018-06-19 09:49:58 +02:00
2019-02-12 16:41:26 +01:00
refresh_children_position(i, x_diff, y_diff);
2016-06-08 07:25:08 +02:00
}
}
/**
* Refresh the style of all children of an object. (Called recursively)
2018-09-05 11:42:18 +02:00
* @param style_p refresh objects only with this style.
2016-10-07 11:15:46 +02:00
* @param obj pointer to an object
2016-06-08 07:25:08 +02:00
*/
2017-11-26 11:38:28 +01:00
static void report_style_mod_core(void * style_p, lv_obj_t * obj)
2016-06-08 07:25:08 +02:00
{
2016-10-07 11:15:46 +02:00
lv_obj_t * i;
2019-04-04 07:15:40 +02:00
LV_LL_READ(obj->child_ll, i)
{
2018-09-05 11:42:18 +02:00
if(i->style_p == style_p || style_p == NULL) {
2019-02-12 16:41:26 +01:00
refresh_children_style(i);
2017-10-30 17:11:56 +01:00
lv_obj_refresh_style(i);
2016-06-08 07:25:08 +02:00
}
2018-06-19 09:49:58 +02:00
2017-11-26 11:38:28 +01:00
report_style_mod_core(style_p, i);
2016-06-08 07:25:08 +02:00
}
}
/**
* Recursively refresh the style of the children. Go deeper until a not NULL style is found
* because the NULL styles are inherited from the parent
* @param obj pointer to an object
*/
2019-02-12 16:41:26 +01:00
static void refresh_children_style(lv_obj_t * obj)
{
lv_obj_t * child = lv_obj_get_child(obj, NULL);
while(child != NULL) {
if(child->style_p == NULL) {
2019-04-04 07:15:40 +02:00
refresh_children_style(child); /*Check children too*/
lv_obj_refresh_style(child); /*Notify the child about the style change*/
2017-10-19 12:46:49 +02:00
} else if(child->style_p->glass) {
2017-10-05 11:29:21 +02:00
/*Children with 'glass' parent might be effected if their style == NULL*/
2019-02-12 16:41:26 +01:00
refresh_children_style(child);
}
2018-08-26 17:04:10 +02:00
child = lv_obj_get_child(obj, child);
}
}
/**
* Called by 'lv_obj_del' to delete the children objects
* @param obj pointer to an object (all of its children will be deleted)
*/
static void delete_children(lv_obj_t * obj)
{
2018-06-19 09:49:58 +02:00
lv_obj_t * i;
lv_obj_t * i_next;
i = lv_ll_get_head(&(obj->child_ll));
2019-04-04 07:15:40 +02:00
/*Remove from the group; remove before transversing children so that
* the object still has access to all children during the
* LV_SIGNAL_DEFOCUS call*/
#if LV_USE_GROUP
lv_group_t * group = lv_obj_get_group(obj);
if(group) lv_group_remove_obj(obj);
#endif
2018-06-19 09:49:58 +02:00
while(i != NULL) {
/*Get the next object before delete this*/
i_next = lv_ll_get_next(&(obj->child_ll), i);
2018-06-19 09:49:58 +02:00
/*Call the recursive del to the child too*/
delete_children(i);
2018-06-19 09:49:58 +02:00
/*Set i to the next node*/
i = i_next;
}
2019-04-19 05:46:02 +02:00
/*Let the suer free the resources used in `LV_EVENT_DELETE`*/
lv_event_send(obj, LV_EVENT_DELETE, NULL);
lv_event_mark_deleted(obj);
2019-04-19 05:46:02 +02:00
2018-06-19 09:49:58 +02:00
/*Remove the animations from this object*/
#if LV_USE_ANIMATION
2018-06-19 09:49:58 +02:00
lv_anim_del(obj, NULL);
2017-11-27 17:48:54 +01:00
#endif
2018-06-19 09:49:58 +02:00
/* Reset the input devices if
* the object to delete is used*/
lv_indev_t * indev = lv_indev_get_next(NULL);
2018-06-19 09:49:58 +02:00
while(indev) {
if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
2018-06-19 09:49:58 +02:00
lv_indev_reset(indev);
}
2019-05-03 15:22:59 +02:00
if(indev->proc.types.pointer.last_pressed == obj) {
indev->proc.types.pointer.last_pressed = NULL;
}
#if LV_USE_GROUP
2019-06-06 06:05:40 +02:00
if(indev->group == group && obj == lv_indev_get_obj_act()) {
lv_indev_reset(indev);
}
#endif
indev = lv_indev_get_next(indev);
2018-06-19 09:49:58 +02:00
}
/* Clean up the object specific data*/
obj->signal_cb(obj, LV_SIGNAL_CLEANUP, NULL);
2018-06-19 09:49:58 +02:00
/*Remove the object from parent's children list*/
lv_obj_t * par = lv_obj_get_parent(obj);
2019-11-15 11:04:46 +01:00
lv_ll_remove(&(par->child_ll), obj);
2018-06-19 09:49:58 +02:00
/*Delete the base objects*/
2019-04-04 07:15:40 +02:00
if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr);
2018-06-19 09:49:58 +02:00
lv_mem_free(obj); /*Free the object itself*/
}
static void base_dir_refr_children(lv_obj_t * obj)
{
lv_obj_t * child;
child = lv_obj_get_child(obj, NULL);
while(child) {
if(child->base_dir == LV_BIDI_DIR_INHERIT) {
lv_signal_send(child, LV_SIGNAL_BASE_DIR_CHG, NULL);
base_dir_refr_children(child);
}
child = lv_obj_get_child(obj, child);
}
}
static void lv_event_mark_deleted(lv_obj_t * obj)
{
lv_event_temp_data_t * t = event_temp_data_head;
while(t) {
if(t->obj == obj) t->deleted = true;
t = t->prev;
}
}