/** * @file lv_base_obj.c * */ /********************* * INCLUDES *********************/ #include "lv_obj.h" #include "lv_indev.h" #include "lv_refr.h" #include "lv_group.h" #include "../lv_themes/lv_theme.h" #include "../lv_draw/lv_draw.h" #include "../lv_draw/lv_draw_rbasic.h" #include "../lv_misc/lv_anim.h" #include "../lv_misc/lv_task.h" #include "../lv_misc/lv_fs.h" #include "../lv_misc/lv_ufs.h" #include #include #include "../lv_misc/lv_gc.h" #if defined(LV_GC_INCLUDE) # include LV_GC_INCLUDE #endif /* LV_ENABLE_GC */ /********************* * DEFINES *********************/ #define LV_OBJ_DEF_WIDTH (LV_DPI) #define LV_OBJ_DEF_HEIGHT (2 * LV_DPI / 3) /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff); static void report_style_mod_core(void * style_p, lv_obj_t * obj); static void refresh_children_style(lv_obj_t * obj); static void delete_children(lv_obj_t * obj); static bool lv_obj_design(lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode); static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param); /********************** * STATIC VARIABLES **********************/ static bool _lv_initialized = false; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Init. the 'lv' library. */ void lv_init(void) { /* Do nothing if already initialized */ if (_lv_initialized) return; LV_GC_ROOT(_lv_def_scr) = NULL; LV_GC_ROOT(_lv_act_scr) = NULL; LV_GC_ROOT(_lv_top_layer) = NULL; LV_GC_ROOT(_lv_sys_layer) = NULL; LV_GC_ROOT(_lv_disp_list) = NULL; LV_GC_ROOT(_lv_indev_list) = NULL; LV_LOG_TRACE("lv_init started"); /*Initialize the lv_misc modules*/ lv_mem_init(); lv_task_init(); #if USE_LV_FILESYSTEM lv_fs_init(); lv_ufs_init(); #endif lv_font_init(); #if USE_LV_ANIMATION lv_anim_init(); #endif /*Init. the sstyles*/ lv_style_init(); /*Initialize the screen refresh system*/ lv_refr_init(); /*Create the default screen*/ lv_ll_init(&LV_GC_ROOT(_lv_scr_ll), sizeof(lv_obj_t)); LV_GC_ROOT(_lv_def_scr) = lv_obj_create(NULL, NULL); LV_GC_ROOT(_lv_act_scr) = LV_GC_ROOT(_lv_def_scr); LV_GC_ROOT(_lv_top_layer) = lv_obj_create(NULL, NULL); lv_obj_set_style(LV_GC_ROOT(_lv_top_layer), &lv_style_transp_fit); LV_GC_ROOT(_lv_sys_layer) = lv_obj_create(NULL, NULL); lv_obj_set_style(LV_GC_ROOT(_lv_sys_layer), &lv_style_transp_fit); /*Refresh the screen*/ lv_obj_invalidate(LV_GC_ROOT(_lv_act_scr)); #if LV_INDEV_READ_PERIOD != 0 /*Init the input device handling*/ lv_indev_init(); #endif _lv_initialized = true; LV_LOG_INFO("lv_init ready"); } /*-------------------- * Create and delete *-------------------*/ /** * Create a basic object * @param parent pointer to a parent object. * If NULL then a screen will be created * @param copy pointer to a base object, if not NULL then the new object will be copied from it * @return pointer to the new object */ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy) { lv_obj_t * new_obj = NULL; /*Create a screen if the parent is NULL*/ if(parent == NULL) { LV_LOG_TRACE("Screen create started"); new_obj = lv_ll_ins_head(&LV_GC_ROOT(_lv_scr_ll)); lv_mem_assert(new_obj); if(new_obj == NULL) return NULL; new_obj->par = NULL; /*Screens has no a parent*/ lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t)); /*Set coordinates to full screen size*/ new_obj->coords.x1 = 0; new_obj->coords.y1 = 0; new_obj->coords.x2 = LV_HOR_RES - 1; new_obj->coords.y2 = LV_VER_RES - 1; new_obj->ext_size = 0; /*Init realign*/ #if LV_OBJ_REALIGN new_obj->realign.align = LV_ALIGN_CENTER; new_obj->realign.xofs = 0; new_obj->realign.yofs = 0; new_obj->realign.base = NULL; new_obj->realign.auto_realign = 0; #endif /*Set the default styles*/ lv_theme_t * th = lv_theme_get_current(); if(th) { new_obj->style_p = th->bg; } else { new_obj->style_p = &lv_style_scr; } /*Set virtual functions*/ lv_obj_set_signal_func(new_obj, lv_obj_signal); lv_obj_set_design_func(new_obj, lv_obj_design); /*Set free data*/ #ifdef LV_OBJ_FREE_NUM_TYPE new_obj->free_num = 0; #endif #if LV_OBJ_FREE_PTR != 0 new_obj->free_ptr = NULL; #endif #if USE_LV_GROUP new_obj->group_p = NULL; #endif /*Set attributes*/ new_obj->click = 0; new_obj->drag = 0; new_obj->drag_throw = 0; new_obj->drag_parent = 0; new_obj->hidden = 0; new_obj->top = 0; new_obj->opa_scale_en = 0; new_obj->protect = LV_PROTECT_NONE; new_obj->opa_scale = LV_OPA_COVER; new_obj->ext_attr = NULL; LV_LOG_INFO("Screen create ready"); } /*parent != NULL create normal obj. on a parent*/ else { LV_LOG_TRACE("Object create started"); new_obj = lv_ll_ins_head(&(parent)->child_ll); lv_mem_assert(new_obj); if(new_obj == NULL) return NULL; new_obj->par = parent; /*Set the parent*/ lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t)); /*Set coordinates left top corner of parent*/ new_obj->coords.x1 = parent->coords.x1; new_obj->coords.y1 = parent->coords.y1; new_obj->coords.x2 = parent->coords.x1 + LV_OBJ_DEF_WIDTH; new_obj->coords.y2 = parent->coords.y1 + LV_OBJ_DEF_HEIGHT; new_obj->ext_size = 0; /*Init realign*/ #if LV_OBJ_REALIGN new_obj->realign.align = LV_ALIGN_CENTER; new_obj->realign.xofs = 0; new_obj->realign.yofs = 0; new_obj->realign.base = NULL; new_obj->realign.auto_realign = 0; #endif /*Set appearance*/ lv_theme_t * th = lv_theme_get_current(); if(th) { new_obj->style_p = th->panel; } else { new_obj->style_p = &lv_style_plain_color; } /*Set virtual functions*/ lv_obj_set_signal_func(new_obj, lv_obj_signal); lv_obj_set_design_func(new_obj, lv_obj_design); /*Set free data*/ #ifdef LV_OBJ_FREE_NUM_TYPE new_obj->free_num = 0; #endif #if LV_OBJ_FREE_PTR != 0 new_obj->free_ptr = NULL; #endif #if USE_LV_GROUP new_obj->group_p = NULL; #endif /*Set attributes*/ new_obj->click = 1; new_obj->drag = 0; 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; new_obj->opa_scale_en = 0; new_obj->ext_attr = NULL; } if(copy != NULL) { lv_area_copy(&new_obj->coords, ©->coords); new_obj->ext_size = copy->ext_size; /*Set free data*/ #ifdef LV_OBJ_FREE_NUM_TYPE new_obj->free_num = copy->free_num; #endif #if LV_OBJ_FREE_PTR != 0 new_obj->free_ptr = copy->free_ptr; #endif /*Copy realign*/ #if LV_OBJ_REALIGN new_obj->realign.align = copy->realign.align; new_obj->realign.xofs = copy->realign.xofs; new_obj->realign.yofs = copy->realign.yofs; new_obj->realign.base = copy->realign.base; new_obj->realign.auto_realign = copy->realign.auto_realign; #endif /*Set attributes*/ new_obj->click = copy->click; new_obj->drag = copy->drag; new_obj->drag_throw = copy->drag_throw; new_obj->drag_parent = copy->drag_parent; new_obj->hidden = copy->hidden; new_obj->top = copy->top; new_obj->opa_scale_en = copy->opa_scale_en; new_obj->protect = copy->protect; new_obj->opa_scale = copy->opa_scale; new_obj->style_p = copy->style_p; #if USE_LV_GROUP /*Add to the same group*/ if(copy->group_p != NULL) { lv_group_add_obj(copy->group_p, new_obj); } #endif /*Set the same coordinates for non screen objects*/ if(lv_obj_get_parent(copy) != NULL && parent != NULL) { lv_obj_set_pos(new_obj, lv_obj_get_x(copy), lv_obj_get_y(copy)); } else { lv_obj_set_pos(new_obj, 0, 0); } LV_LOG_INFO("Object create ready"); } /*Send a signal to the parent to notify it about the new child*/ if(parent != NULL) { parent->signal_func(parent, LV_SIGNAL_CHILD_CHG, new_obj); /*Invalidate the area if not screen created*/ lv_obj_invalidate(new_obj); } return new_obj; } /** * Delete 'obj' and all of its children * @param obj pointer to an object to delete * @return LV_RES_INV because the object is deleted */ lv_res_t lv_obj_del(lv_obj_t * obj) { lv_obj_invalidate(obj); /*Delete from the group*/ #if USE_LV_GROUP if(obj->group_p != NULL) lv_group_remove_obj(obj); #endif /*Remove the animations from this object*/ #if USE_LV_ANIMATION lv_anim_del(obj, NULL); #endif /*Recursively delete the children*/ lv_obj_t * i; lv_obj_t * i_next; i = lv_ll_get_head(&(obj->child_ll)); while(i != NULL) { /*Get the next object before delete this*/ i_next = lv_ll_get_next(&(obj->child_ll), i); /*Call the recursive del to the child too*/ delete_children(i); /*Set i to the next node*/ i = i_next; } /*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_ll_rem(&LV_GC_ROOT(_lv_scr_ll), obj); } else { lv_ll_rem(&(par->child_ll), obj); } /* Reset all input devices if * the currently pressed object is deleted*/ lv_indev_t * indev = lv_indev_next(NULL); while(indev) { if(indev->proc.act_obj == obj || indev->proc.last_obj == obj) { lv_indev_reset(indev); } indev = lv_indev_next(indev); } /* All children deleted. * Now clean up the object specific data*/ obj->signal_func(obj, LV_SIGNAL_CLEANUP, NULL); /*Delete the base objects*/ if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr); lv_mem_free(obj); /*Free the object itself*/ /*Send a signal to the parent to notify it about the child delete*/ if(par != NULL) { par->signal_func(par, LV_SIGNAL_CHILD_CHG, NULL); } return LV_RES_INV; } /** * Delete all children of an object * @param obj pointer to an object */ void lv_obj_clean(lv_obj_t * obj) { lv_obj_t * child = lv_obj_get_child(obj, NULL); lv_obj_t * child_next; while(child) { /* 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); 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) { if(lv_obj_get_hidden(obj)) return; /*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); if(obj_scr == lv_scr_act() || obj_scr == lv_layer_top() || obj_scr == lv_layer_sys()) { /*Truncate recursively to the parents*/ lv_area_t area_trunc; lv_obj_t * par = lv_obj_get_parent(obj); bool union_ok = true; /*Start with the original coordinates*/ lv_coord_t ext_size = obj->ext_size; 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); 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 != false) lv_inv_area(&area_trunc); } } /*===================== * Setter functions *====================*/ /*-------------- * Screen set *--------------*/ /** * Load a new screen * @param scr pointer to a screen */ void lv_scr_load(lv_obj_t * scr) { LV_GC_ROOT(_lv_act_scr) = scr; lv_obj_invalidate(LV_GC_ROOT(_lv_act_scr)); } /*-------------------- * Parent/children set *--------------------*/ /** * Set a new parent for an object. Its relative position will be the same. * @param obj pointer to an object. Can't be a screen. * @param parent pointer to the new parent object. (Can't be NULL) */ void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent) { 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); lv_point_t old_pos; old_pos.x = lv_obj_get_x(obj); old_pos.y = lv_obj_get_y(obj); lv_obj_t * old_par = obj->par; lv_ll_chg_list(&obj->par->child_ll, &parent->child_ll, obj); obj->par = parent; lv_obj_set_pos(obj, old_pos.x, old_pos.y); /*Notify the original parent because one of its children is lost*/ old_par->signal_func(old_par, LV_SIGNAL_CHILD_CHG, NULL); /*Notify the new parent about the child*/ parent->signal_func(parent, LV_SIGNAL_CHILD_CHG, obj); lv_obj_invalidate(obj); } /*-------------------- * Coordinate set * ------------------*/ /** * Set relative the position of an object (relative to the parent) * @param obj pointer to an object * @param x new distance from the left side of the parent * @param y new distance from the top of the parent */ void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) { /*Convert x and y to absolute coordinates*/ lv_obj_t * par = obj->par; x = x + par->coords.x1; y = y + par->coords.y1; /*Calculate and set the movement*/ lv_point_t diff; diff.x = x - obj->coords.x1; diff.y = y - obj->coords.y1; /* Do nothing if the position is not changed */ /* It is very important else recursive positioning can * occur without position change*/ if(diff.x == 0 && diff.y == 0) return; /*Invalidate the original area*/ lv_obj_invalidate(obj); /*Save the original coordinates*/ lv_area_t ori; lv_obj_get_coords(obj, &ori); obj->coords.x1 += diff.x; obj->coords.y1 += diff.y; obj->coords.x2 += diff.x; obj->coords.y2 += diff.y; refresh_children_position(obj, diff.x, diff.y); /*Inform the object about its new coordinates*/ obj->signal_func(obj, LV_SIGNAL_CORD_CHG, &ori); /*Send a signal to the parent too*/ par->signal_func(par, LV_SIGNAL_CHILD_CHG, obj); /*Invalidate the new area*/ lv_obj_invalidate(obj); } /** * Set the x coordinate of a object * @param obj pointer to an object * @param x new distance from the left side from the parent */ void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x) { lv_obj_set_pos(obj, x, lv_obj_get_y(obj)); } /** * Set the y coordinate of a object * @param obj pointer to an object * @param y new distance from the top of the parent */ void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y) { lv_obj_set_pos(obj, lv_obj_get_x(obj), y); } /** * Set the size of an object * @param obj pointer to an object * @param w new width * @param h new height */ void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h) { /* Do nothing if the size is not changed */ /* It is very important else recursive resizing can * occur without size change*/ if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) { return; } /*Invalidate the original area*/ lv_obj_invalidate(obj); /*Save the original coordinates*/ lv_area_t ori; lv_obj_get_coords(obj, &ori); //Set the length and height obj->coords.x2 = obj->coords.x1 + w - 1; obj->coords.y2 = obj->coords.y1 + h - 1; /*Send a signal to the object with its new coordinates*/ obj->signal_func(obj, LV_SIGNAL_CORD_CHG, &ori); /*Send a signal to the parent too*/ lv_obj_t * par = lv_obj_get_parent(obj); if(par != NULL) par->signal_func(par, LV_SIGNAL_CHILD_CHG, obj); /*Invalidate the new area*/ lv_obj_invalidate(obj); /*Automatically realign the object if required*/ #if LV_OBJ_REALIGN if(obj->realign.auto_realign) lv_obj_realign(obj); #endif } /** * Set the width of an object * @param obj pointer to an object * @param w new width */ void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w) { lv_obj_set_size(obj, w, lv_obj_get_height(obj)); } /** * Set the height of an object * @param obj pointer to an object * @param h new height */ void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h) { lv_obj_set_size(obj, lv_obj_get_width(obj), h); } /** * Align an object to an other object. * @param obj pointer to an object to align * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it. * @param align type of alignment (see 'lv_align_t' enum) * @param x_mod x coordinate shift after alignment * @param y_mod y coordinate shift after alignment */ 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) { lv_coord_t new_x = lv_obj_get_x(obj); lv_coord_t new_y = lv_obj_get_y(obj); if(base == NULL) { base = lv_obj_get_parent(obj); } switch(align) { case LV_ALIGN_CENTER: 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; break; case LV_ALIGN_IN_TOP_LEFT: new_x = 0; new_y = 0; break; case LV_ALIGN_IN_TOP_MID: new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2; new_y = 0; break; case LV_ALIGN_IN_TOP_RIGHT: new_x = lv_obj_get_width(base) - lv_obj_get_width(obj); new_y = 0; break; case LV_ALIGN_IN_BOTTOM_LEFT: new_x = 0; new_y = lv_obj_get_height(base) - lv_obj_get_height(obj); break; case LV_ALIGN_IN_BOTTOM_MID: 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); break; case LV_ALIGN_IN_BOTTOM_RIGHT: new_x = lv_obj_get_width(base) - lv_obj_get_width(obj); new_y = lv_obj_get_height(base) - lv_obj_get_height(obj); break; case LV_ALIGN_IN_LEFT_MID: new_x = 0; new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2; break; case LV_ALIGN_IN_RIGHT_MID: 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; break; case LV_ALIGN_OUT_TOP_LEFT: new_x = 0; new_y = -lv_obj_get_height(obj); break; case LV_ALIGN_OUT_TOP_MID: new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2; new_y = - lv_obj_get_height(obj); break; case LV_ALIGN_OUT_TOP_RIGHT: new_x = lv_obj_get_width(base) - lv_obj_get_width(obj); new_y = - lv_obj_get_height(obj); break; case LV_ALIGN_OUT_BOTTOM_LEFT: new_x = 0; new_y = lv_obj_get_height(base); break; case LV_ALIGN_OUT_BOTTOM_MID: new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2; new_y = lv_obj_get_height(base); break; case LV_ALIGN_OUT_BOTTOM_RIGHT: new_x = lv_obj_get_width(base) - lv_obj_get_width(obj); new_y = lv_obj_get_height(base); break; case LV_ALIGN_OUT_LEFT_TOP: new_x = - lv_obj_get_width(obj); new_y = 0; break; case LV_ALIGN_OUT_LEFT_MID: new_x = - lv_obj_get_width(obj); new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2; break; case LV_ALIGN_OUT_LEFT_BOTTOM: new_x = - lv_obj_get_width(obj); new_y = lv_obj_get_height(base) - lv_obj_get_height(obj); break; case LV_ALIGN_OUT_RIGHT_TOP: new_x = lv_obj_get_width(base); new_y = 0; break; case LV_ALIGN_OUT_RIGHT_MID: new_x = lv_obj_get_width(base); new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2; break; case LV_ALIGN_OUT_RIGHT_BOTTOM: new_x = lv_obj_get_width(base); new_y = lv_obj_get_height(base) - lv_obj_get_height(obj); break; } /*Bring together the coordination system of base and obj*/ lv_obj_t * par = lv_obj_get_parent(obj); lv_coord_t base_abs_x = base->coords.x1; lv_coord_t base_abs_y = base->coords.y1; lv_coord_t par_abs_x = par->coords.x1; lv_coord_t par_abs_y = par->coords.y1; new_x += x_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_OBJ_REALIGN /*Save the last align parameters to use them in `lv_obj_realign`*/ obj->realign.align = align; obj->realign.xofs = x_mod; obj->realign.yofs = y_mod; obj->realign.base = base; obj->realign.origo_align = 0; #endif } /** * Align an object to an other object. * @param obj pointer to an object to align * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it. * @param align type of alignment (see 'lv_align_t' enum) * @param x_mod x coordinate shift after alignment * @param y_mod y coordinate shift after alignment */ 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) { lv_coord_t new_x = lv_obj_get_x(obj); lv_coord_t new_y = lv_obj_get_y(obj); lv_coord_t obj_w_half = lv_obj_get_width(obj) / 2; lv_coord_t obj_h_half = lv_obj_get_height(obj) / 2; if(base == NULL) { base = lv_obj_get_parent(obj); } switch(align) { case LV_ALIGN_CENTER: new_x = lv_obj_get_width(base) / 2 - obj_w_half; new_y = lv_obj_get_height(base) / 2 - obj_h_half; break; case LV_ALIGN_IN_TOP_LEFT: new_x = -obj_w_half; new_y = -obj_h_half; break; case LV_ALIGN_IN_TOP_MID: new_x = lv_obj_get_width(base) / 2 - obj_w_half; new_y = -obj_h_half; break; case LV_ALIGN_IN_TOP_RIGHT: new_x = lv_obj_get_width(base) - obj_w_half; new_y = -obj_h_half; break; case LV_ALIGN_IN_BOTTOM_LEFT: new_x = -obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_IN_BOTTOM_MID: new_x = lv_obj_get_width(base) / 2 - obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_IN_BOTTOM_RIGHT: new_x = lv_obj_get_width(base) - obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_IN_LEFT_MID: new_x = -obj_w_half; new_y = lv_obj_get_height(base) / 2 - obj_h_half; break; case LV_ALIGN_IN_RIGHT_MID: new_x = lv_obj_get_width(base) - obj_w_half; new_y = lv_obj_get_height(base) / 2 - obj_h_half; break; case LV_ALIGN_OUT_TOP_LEFT: new_x = -obj_w_half; new_y = -obj_h_half; break; case LV_ALIGN_OUT_TOP_MID: new_x = lv_obj_get_width(base) / 2 - obj_w_half; new_y = -obj_h_half; break; case LV_ALIGN_OUT_TOP_RIGHT: new_x = lv_obj_get_width(base) - obj_w_half; new_y = - obj_h_half; break; case LV_ALIGN_OUT_BOTTOM_LEFT: new_x = -obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_OUT_BOTTOM_MID: new_x = lv_obj_get_width(base) / 2 - obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_OUT_BOTTOM_RIGHT: new_x = lv_obj_get_width(base) - obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_OUT_LEFT_TOP: new_x = - obj_w_half ; new_y = - obj_h_half; break; case LV_ALIGN_OUT_LEFT_MID: new_x = - obj_w_half; new_y = lv_obj_get_height(base) / 2 - obj_h_half; break; case LV_ALIGN_OUT_LEFT_BOTTOM: new_x = - obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; case LV_ALIGN_OUT_RIGHT_TOP: new_x = lv_obj_get_width(base) - obj_w_half; new_y = -obj_h_half; break; case LV_ALIGN_OUT_RIGHT_MID: new_x = lv_obj_get_width(base) - obj_w_half; new_y = lv_obj_get_height(base) / 2 - obj_h_half; break; case LV_ALIGN_OUT_RIGHT_BOTTOM: new_x = lv_obj_get_width(base) - obj_w_half; new_y = lv_obj_get_height(base) - obj_h_half; break; } /*Bring together the coordination system of base and obj*/ lv_obj_t * par = lv_obj_get_parent(obj); lv_coord_t base_abs_x = base->coords.x1; lv_coord_t base_abs_y = base->coords.y1; lv_coord_t par_abs_x = par->coords.x1; lv_coord_t par_abs_y = par->coords.y1; new_x += x_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_OBJ_REALIGN /*Save the last align parameters to use them in `lv_obj_realign`*/ obj->realign.align = align; obj->realign.xofs = x_mod; obj->realign.yofs = y_mod; obj->realign.base = base; 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) { #if LV_OBJ_REALIGN if(obj->realign.origo_align) lv_obj_align_origo(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs); else lv_obj_align(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs); #else (void) obj; LV_LOG_WARN("lv_obj_realaign: no effect because LV_OBJ_REALIGN = 0"); #endif } /** * Enable the automatic realign of the object when its size has changed based on the last `lv_obj_align` parameters. * @param obj pointer to an object * @param en true: enable auto realign; false: disable auto realign */ void lv_obj_set_auto_realign(lv_obj_t * obj, bool en) { #if LV_OBJ_REALIGN obj->realign.auto_realign = en ? 1 : 0; #else (void) obj; (void) en; LV_LOG_WARN("lv_obj_set_auto_realign: no effect because LV_OBJ_REALIGN = 0"); #endif } /*--------------------- * Appearance set *--------------------*/ /** * Set a new style for an object * @param obj pointer to an object * @param style_p pointer to the new style */ void lv_obj_set_style(lv_obj_t * obj, lv_style_t * style) { obj->style_p = style; /*Send a signal about style change to every children with NULL style*/ refresh_children_style(obj); /*Notify the object about the style change too*/ lv_obj_refresh_style(obj); } /** * Notify an object about its style is modified * @param obj pointer to an object */ void lv_obj_refresh_style(lv_obj_t * obj) { lv_obj_invalidate(obj); obj->signal_func(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) */ void lv_obj_report_style_mod(lv_style_t * style) { lv_obj_t * i; LL_READ(LV_GC_ROOT(_lv_scr_ll), i) { if(i->style_p == style || style == NULL) { lv_obj_refresh_style(i); } report_style_mod_core(style, i); } } /*----------------- * Attribute set *----------------*/ /** * Hide an object. It won't be visible and clickable. * @param obj pointer to an object * @param en true: hide the object */ void lv_obj_set_hidden(lv_obj_t * obj, bool en) { if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */ obj->hidden = en == false ? 0 : 1; if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */ lv_obj_t * par = lv_obj_get_parent(obj); par->signal_func(par, LV_SIGNAL_CHILD_CHG, obj); } /** * Enable or disable the clicking of an object * @param obj pointer to an object * @param en true: make the object clickable */ void lv_obj_set_click(lv_obj_t * obj, bool en) { obj->click = (en == true ? 1 : 0); } /** * Enable to bring this object to the foreground if it * or any of its children is clicked * @param obj pointer to an object * @param en true: enable the auto top feature */ void lv_obj_set_top(lv_obj_t * obj, bool en) { obj->top = (en == true ? 1 : 0); } /** * Enable the dragging of an object * @param obj pointer to an object * @param en true: make the object dragable */ void lv_obj_set_drag(lv_obj_t * obj, bool en) { if(en == true) lv_obj_set_click(obj, true); /*Drag is useless without enabled clicking*/ obj->drag = (en == true ? 1 : 0); } /** * Enable the throwing of an object after is is dragged * @param obj pointer to an object * @param en true: enable the drag throw */ void lv_obj_set_drag_throw(lv_obj_t * obj, bool en) { obj->drag_throw = (en == true ? 1 : 0); } /** * Enable to use parent for drag related operations. * If trying to drag the object the parent will be moved instead * @param obj pointer to an object * @param en true: enable the 'drag parent' for the object */ void lv_obj_set_drag_parent(lv_obj_t * obj, bool en) { obj->drag_parent = (en == true ? 1 : 0); } /** * 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) { obj->opa_scale_en = en ? 1 : 0; } /** * Set the opa scale of an object * @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) { obj->opa_scale = opa_scale; lv_obj_invalidate(obj); } /** * Set a bit or bits in the protect filed * @param obj pointer to an object * @param prot 'OR'-ed values from `lv_protect_t` */ void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot) { obj->protect |= prot; } /** * Clear a bit or bits in the protect filed * @param obj pointer to an object * @param prot 'OR'-ed values from `lv_protect_t` */ void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot) { prot = (~prot) & 0xFF; obj->protect &= prot; } /** * Set the signal function of an object. * Always call the previous signal function in the new. * @param obj pointer to an object * @param fp the new signal function */ void lv_obj_set_signal_func(lv_obj_t * obj, lv_signal_func_t fp) { obj->signal_func = fp; } /** * Set a new design function for an object * @param obj pointer to an object * @param fp the new design function */ void lv_obj_set_design_func(lv_obj_t * obj, lv_design_func_t fp) { obj->design_func = fp; } /*---------------- * Other set *--------------*/ /** * Allocate a new ext. data for an object * @param obj pointer to an object * @param ext_size the size of the new ext. data * @return Normal pointer to the allocated ext */ void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size) { obj->ext_attr = lv_mem_realloc(obj->ext_attr, ext_size); return (void *)obj->ext_attr; } /** * Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object * @param obj pointer to an object */ void lv_obj_refresh_ext_size(lv_obj_t * obj) { obj->ext_size = 0; obj->signal_func(obj, LV_SIGNAL_REFR_EXT_SIZE, NULL); lv_obj_invalidate(obj); } #ifdef LV_OBJ_FREE_NUM_TYPE /** * Set an application specific number for an object. * It can help to identify objects in the application. * @param obj pointer to an object * @param free_num the new free number */ void lv_obj_set_free_num(lv_obj_t * obj, LV_OBJ_FREE_NUM_TYPE free_num) { obj->free_num = free_num; } #endif #if LV_OBJ_FREE_PTR != 0 /** * Set an application specific pointer for an object. * It can help to identify objects in the application. * @param obj pointer to an object * @param free_p the new free pinter */ void lv_obj_set_free_ptr(lv_obj_t * obj, void * free_p) { obj->free_ptr = free_p; } #endif #if USE_LV_ANIMATION /** * Animate an object * @param obj pointer to an object to animate * @param type type of animation from 'lv_anim_builtin_t'. 'OR' it with ANIM_IN or ANIM_OUT * @param time time of animation in milliseconds * @param delay delay before the animation in milliseconds * @param cb a function to call when the animation is ready */ void lv_obj_animate(lv_obj_t * obj, lv_anim_builtin_t type, uint16_t time, uint16_t delay, void (*cb)(lv_obj_t *)) { lv_obj_t * par = lv_obj_get_parent(obj); /*Get the direction*/ bool out = (type & LV_ANIM_DIR_MASK) == LV_ANIM_IN ? false : true; type = type & (~LV_ANIM_DIR_MASK); lv_anim_t a; a.var = obj; a.time = time; a.act_time = (int32_t) - delay; a.end_cb = (void(*)(void *))cb; a.path = lv_anim_path_linear; a.playback_pause = 0; a.repeat_pause = 0; a.playback = 0; a.repeat = 0; /*Init to ANIM_IN*/ switch(type) { case LV_ANIM_FLOAT_LEFT: a.fp = (void(*)(void *, int32_t))lv_obj_set_x; a.start = -lv_obj_get_width(obj); a.end = lv_obj_get_x(obj); break; case LV_ANIM_FLOAT_RIGHT: a.fp = (void(*)(void *, int32_t))lv_obj_set_x; a.start = lv_obj_get_width(par); a.end = lv_obj_get_x(obj); break; case LV_ANIM_FLOAT_TOP: a.fp = (void(*)(void *, int32_t))lv_obj_set_y; a.start = -lv_obj_get_height(obj); a.end = lv_obj_get_y(obj); break; case LV_ANIM_FLOAT_BOTTOM: a.fp = (void(*)(void *, int32_t))lv_obj_set_y; a.start = lv_obj_get_height(par); a.end = lv_obj_get_y(obj); break; case LV_ANIM_GROW_H: a.fp = (void(*)(void *, int32_t))lv_obj_set_width; a.start = 0; a.end = lv_obj_get_width(obj); break; case LV_ANIM_GROW_V: a.fp = (void(*)(void *, int32_t))lv_obj_set_height; a.start = 0; a.end = lv_obj_get_height(obj); break; case LV_ANIM_NONE: a.fp = NULL; a.start = 0; a.end = 0; break; default: break; } /*Swap start and end in case of ANIM OUT*/ if(out != false) { int32_t tmp = a.start; a.start = a.end; a.end = tmp; } lv_anim_create(&a); } #endif /*======================= * Getter functions *======================*/ /*------------------ * Screen get *-----------------*/ /** * Return with a pointer to the active screen * @return pointer to the active screen object (loaded by 'lv_scr_load()') */ lv_obj_t * lv_scr_act(void) { return LV_GC_ROOT(_lv_act_scr); } /** * Return with the top layer. (Same on every screen and it is above the normal screen layer) * @return pointer to the top layer object (transparent screen sized lv_obj) */ lv_obj_t * lv_layer_top(void) { return LV_GC_ROOT(_lv_top_layer); } /** * Return with the system layer. (Same on every screen and it is above the all other layers) * It is used for example by the cursor * @return pointer to the system layer object (transparent screen sized lv_obj) */ lv_obj_t * lv_layer_sys(void) { return LV_GC_ROOT(_lv_sys_layer); } /** * Return with the screen of an object * @param obj pointer to an object * @return pointer to a screen */ lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj) { const lv_obj_t * par = obj; const lv_obj_t * act_p; do { act_p = par; par = lv_obj_get_parent(act_p); } while(par != NULL); return (lv_obj_t *)act_p; } /*--------------------- * Parent/children get *--------------------*/ /** * Returns with the parent of an object * @param obj pointer to an object * @return pointer to the parent of 'obj' */ lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj) { return obj->par; } /** * Iterate through the children of an object (start from the "youngest") * @param obj pointer to an object * @param child NULL at first call to get the next children * and the previous return value later * @return the child after 'act_child' or NULL if no more child */ lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child) { lv_obj_t * result = NULL; if(child == NULL) { result = lv_ll_get_head(&obj->child_ll); } else { result = lv_ll_get_next(&obj->child_ll, child); } return result; } /** * Iterate through the children of an object (start from the "oldest") * @param obj pointer to an object * @param child NULL at first call to get the next children * and the previous return value later * @return the child after 'act_child' or NULL if no more child */ lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child) { lv_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_obj_t * i; uint16_t cnt = 0; LL_READ(obj->child_ll, i) cnt++; return cnt; } /*--------------------- * Coordinate get *--------------------*/ /** * Copy the coordinates of an object to an area * @param obj pointer to an object * @param cords_p pointer to an area to store the coordinates */ void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p) { lv_area_copy(cords_p, &obj->coords); } /** * Get the x coordinate of object * @param obj pointer to an object * @return distance of 'obj' from the left side of its parent */ lv_coord_t lv_obj_get_x(const lv_obj_t * obj) { lv_coord_t rel_x; lv_obj_t * parent = lv_obj_get_parent(obj); rel_x = obj->coords.x1 - parent->coords.x1; return rel_x; } /** * Get the y coordinate of object * @param obj pointer to an object * @return distance of 'obj' from the top of its parent */ lv_coord_t lv_obj_get_y(const lv_obj_t * obj) { lv_coord_t rel_y; lv_obj_t * parent = lv_obj_get_parent(obj); rel_y = obj->coords.y1 - parent->coords.y1; return rel_y; } /** * Get the width of an object * @param obj pointer to an object * @return the width */ lv_coord_t lv_obj_get_width(const lv_obj_t * obj) { return lv_area_get_width(&obj->coords); } /** * Get the height of an object * @param obj pointer to an object * @return the height */ lv_coord_t lv_obj_get_height(const lv_obj_t * obj) { return lv_area_get_height(&obj->coords); } /** * Get the extended size attribute of an object * @param obj pointer to an object * @return the extended size attribute */ lv_coord_t lv_obj_get_ext_size(const lv_obj_t * obj) { return obj->ext_size; } /** * 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(lv_obj_t * obj) { #if LV_OBJ_REALIGN return obj->realign.auto_realign ? true : false; #else (void) obj; return false; #endif } /*----------------- * Appearance get *---------------*/ /** * Get the style pointer of an object (if NULL get style of the parent) * @param obj pointer to an object * @return pointer to a style */ lv_style_t * lv_obj_get_style(const lv_obj_t * obj) { lv_style_t * style_act = obj->style_p; if(style_act == NULL) { lv_obj_t * par = obj->par; while(par) { if(par->style_p) { if(par->style_p->glass == 0) { #if USE_LV_GROUP == 0 style_act = par->style_p; #else /*Is a parent is focused then use then focused style*/ 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 break; } } par = par->par; } } #if USE_LV_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); } } #endif if(style_act == NULL) style_act = &lv_style_plain; return style_act; } /*----------------- * Attribute get *----------------*/ /** * Get the hidden attribute of an object * @param obj pointer to an object * @return true: the object is hidden */ bool lv_obj_get_hidden(const lv_obj_t * obj) { return obj->hidden == 0 ? false : true; } /** * Get the click enable attribute of an object * @param obj pointer to an object * @return true: the object is clickable */ bool lv_obj_get_click(const lv_obj_t * obj) { return obj->click == 0 ? false : true; } /** * Get the top enable attribute of an object * @param obj pointer to an object * @return true: the auto top feture is enabled */ bool lv_obj_get_top(const lv_obj_t * obj) { return obj->top == 0 ? false : true; } /** * Get the drag enable attribute of an object * @param obj pointer to an object * @return true: the object is dragable */ bool lv_obj_get_drag(const lv_obj_t * obj) { return obj->drag == 0 ? false : true; } /** * Get the drag throw enable attribute of an object * @param obj pointer to an object * @return true: drag throw is enabled */ bool lv_obj_get_drag_throw(const lv_obj_t * obj) { return obj->drag_throw == 0 ? false : true; } /** * Get the drag parent attribute of an object * @param obj pointer to an object * @return true: drag parent is enabled */ bool lv_obj_get_drag_parent(const lv_obj_t * obj) { return obj->drag_parent == 0 ? false : true; } /** * Get the 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 */ lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj) { 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) { const lv_obj_t * parent = obj; while(parent) { if(parent->opa_scale_en) return parent->opa_scale; parent = lv_obj_get_parent(parent); } return LV_OPA_COVER; } /** * Get the protect field of an object * @param obj pointer to an object * @return protect field ('OR'ed values of `lv_protect_t`) */ uint8_t lv_obj_get_protect(const lv_obj_t * obj) { return obj->protect ; } /** * Check at least one bit of a given protect bitfield is set * @param obj pointer to an object * @param prot protect bits to test ('OR'ed values of `lv_protect_t`) * @return false: none of the given bits are set, true: at least one bit is set */ bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot) { return (obj->protect & prot) == 0 ? false : true ; } /** * Get the signal function of an object * @param obj pointer to an object * @return the signal function */ lv_signal_func_t lv_obj_get_signal_func(const lv_obj_t * obj) { return obj->signal_func; } /** * Get the design function of an object * @param obj pointer to an object * @return the design function */ lv_design_func_t lv_obj_get_design_func(const lv_obj_t * obj) { return obj->design_func; } /*------------------ * Other get *-----------------*/ /** * Get the ext pointer * @param obj pointer to an object * @return the ext pointer but not the dynamic version * Use it as ext->data1, and NOT da(ext)->data1 */ void * lv_obj_get_ext_attr(const lv_obj_t * obj) { return obj->ext_attr; } /** * Get object's and its ancestors type. Put their name in `type_buf` starting with the current type. * E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj" * @param obj pointer to an object which type should be get * @param buf pointer to an `lv_obj_type_t` buffer to store the types */ void lv_obj_get_type(lv_obj_t * obj, lv_obj_type_t * buf) { lv_obj_type_t tmp; memset(buf, 0, sizeof(lv_obj_type_t)); memset(&tmp, 0, sizeof(lv_obj_type_t)); obj->signal_func(obj, LV_SIGNAL_GET_TYPE, &tmp); uint8_t cnt; for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) { if(tmp.type[cnt] == NULL) break; } /*Swap the order. The real type comes first*/ uint8_t i; for(i = 0; i < cnt; i++) { buf->type[i] = tmp.type[cnt - 1 - i]; } } #ifdef LV_OBJ_FREE_NUM_TYPE /** * Get the free number * @param obj pointer to an object * @return the free number */ LV_OBJ_FREE_NUM_TYPE lv_obj_get_free_num(const lv_obj_t * obj) { return obj->free_num; } #endif #if LV_OBJ_FREE_PTR != 0 /** * Get the free pointer * @param obj pointer to an object * @return the free pointer */ void * lv_obj_get_free_ptr(const lv_obj_t * obj) { return obj->free_ptr; } #endif #if USE_LV_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) { return obj->group_p; } /** * Tell whether the ohe object is the focused object of a group or not. * @param obj pointer to an object * @return true: the object is focused, false: the object is not focused or not in a group */ bool lv_obj_is_focused(const lv_obj_t * obj) { if(obj->group_p) { if(lv_group_get_focused(obj->group_p) == obj) return true; } return false; } #endif /********************** * STATIC FUNCTIONS **********************/ /** * Handle the drawing related tasks of the base objects. * @param obj pointer to an object * @param mask the object will be drawn only in this area * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area * (return 'true' if yes) * LV_DESIGN_DRAW: draw the object (always return 'true') * @param return true/false, depends on 'mode' */ static bool lv_obj_design(lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode) { if(mode == LV_DESIGN_COVER_CHK) { /* Because of the radius it is not sure the area is covered * Check the areas where there is no radius*/ lv_style_t * style = lv_obj_get_style(obj); if(style->body.empty != 0) return false; uint16_t r = style->body.radius; if(r == LV_RADIUS_CIRCLE) return false; lv_area_t area_tmp; /*Check horizontally without radius*/ lv_obj_get_coords(obj, &area_tmp); area_tmp.x1 += r; area_tmp.x2 -= r; if(lv_area_is_in(mask_p, &area_tmp) == false) return false; /*Check vertically without radius*/ lv_obj_get_coords(obj, &area_tmp); area_tmp.y1 += r; area_tmp.y2 -= r; if(lv_area_is_in(mask_p, &area_tmp) == false) return false; } else if(mode == LV_DESIGN_DRAW_MAIN) { lv_style_t * style = lv_obj_get_style(obj); lv_draw_rect(&obj->coords, mask_p, style, lv_obj_get_opa_scale(obj)); } return true; } /** * 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) { (void)param; lv_res_t res = LV_RES_OK; lv_style_t * style = lv_obj_get_style(obj); lv_indev_t *indev_act = lv_indev_get_act(); if(sign > _LV_SIGNAL_FEEDBACK_SECTION_START && sign < _LV_SIGNAL_FEEDBACK_SECTION_END) { if(indev_act != NULL && indev_act->feedback != NULL) indev_act->feedback(indev_act, sign); } 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_SIZE) { if(style->body.shadow.width > obj->ext_size) obj->ext_size = style->body.shadow.width; } else if(sign == LV_SIGNAL_STYLE_CHG) { lv_obj_refresh_ext_size(obj); } else if(sign == LV_SIGNAL_GET_TYPE) { lv_obj_type_t * buf = param; buf->type[0] = "lv_obj"; } return res; } /** * Reposition the children of an object. (Called recursively) * @param obj pointer to an object which children will be repositioned * @param x_diff x coordinate shift * @param y_diff y coordinate shift */ static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff) { lv_obj_t * i; LL_READ(obj->child_ll, i) { i->coords.x1 += x_diff; i->coords.y1 += y_diff; i->coords.x2 += x_diff; i->coords.y2 += y_diff; refresh_children_position(i, x_diff, y_diff); } } /** * Refresh the style of all children of an object. (Called recursively) * @param style_p refresh objects only with this style. * @param obj pointer to an object */ static void report_style_mod_core(void * style_p, lv_obj_t * obj) { lv_obj_t * i; LL_READ(obj->child_ll, i) { if(i->style_p == style_p || style_p == NULL) { refresh_children_style(i); lv_obj_refresh_style(i); } report_style_mod_core(style_p, i); } } /** * Recursively refresh the style of the children. Go deeper until a not NULL style is found * because the NULL styles are inherited from the parent * @param obj pointer to an object */ static void refresh_children_style(lv_obj_t * obj) { lv_obj_t * child = lv_obj_get_child(obj, NULL); while(child != NULL) { if(child->style_p == NULL) { refresh_children_style(child); /*Check children too*/ lv_obj_refresh_style(child); /*Notify the child about the style change*/ } else if(child->style_p->glass) { /*Children with 'glass' parent might be effected if their style == NULL*/ refresh_children_style(child); } 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) { lv_obj_t * i; lv_obj_t * i_next; i = lv_ll_get_head(&(obj->child_ll)); /*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 USE_LV_GROUP if(obj->group_p != NULL) lv_group_remove_obj(obj); #endif while(i != NULL) { /*Get the next object before delete this*/ i_next = lv_ll_get_next(&(obj->child_ll), i); /*Call the recursive del to the child too*/ delete_children(i); /*Set i to the next node*/ i = i_next; } /*Remove the animations from this object*/ #if USE_LV_ANIMATION lv_anim_del(obj, NULL); #endif /* Reset the input devices if * the currently pressed object is deleted*/ lv_indev_t * indev = lv_indev_next(NULL); while(indev) { if(indev->proc.act_obj == obj || indev->proc.last_obj == obj) { lv_indev_reset(indev); } indev = lv_indev_next(indev); } /*Remove the object from parent's children list*/ lv_obj_t * par = lv_obj_get_parent(obj); lv_ll_rem(&(par->child_ll), obj); /* Clean up the object specific data*/ obj->signal_func(obj, LV_SIGNAL_CLEANUP, NULL); /*Delete the base objects*/ if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr); lv_mem_free(obj); /*Free the object itself*/ }