/** * @file lv_imgbtn.c * */ /********************* * INCLUDES *********************/ #include "../lv_core/lv_debug.h" #include "lv_imgbtn.h" #include "lv_label.h" #if LV_USE_IMGBTN != 0 /********************* * DEFINES *********************/ #define LV_OBJX_NAME "lv_imgbtn" /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static lv_design_res_t lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * clip_area, lv_design_mode_t mode); static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param); static void refr_img(lv_obj_t * imgbtn); /********************** * STATIC VARIABLES **********************/ static lv_signal_cb_t ancestor_signal; static lv_design_cb_t ancestor_design; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Create a image button object * @param par pointer to an object, it will be the parent of the new image button * @param copy pointer to a image button object, if not NULL then the new object will be copied from * it * @return pointer to the created image button */ lv_obj_t * lv_imgbtn_create(lv_obj_t * par, const lv_obj_t * copy) { LV_LOG_TRACE("image button create started"); /*Create the ancestor of image button*/ lv_obj_t * new_imgbtn = lv_btn_create(par, copy); LV_ASSERT_MEM(new_imgbtn); if(new_imgbtn == NULL) return NULL; /*Allocate the image button type specific extended data*/ lv_imgbtn_ext_t * ext = lv_obj_allocate_ext_attr(new_imgbtn, sizeof(lv_imgbtn_ext_t)); LV_ASSERT_MEM(ext); if(ext == NULL) { lv_obj_del(new_imgbtn); return NULL; } if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_imgbtn); if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_imgbtn); /*Initialize the allocated 'ext' */ #if LV_IMGBTN_TILED == 0 memset((void*)ext->img_src, 0, sizeof(ext->img_src)); #else memset(ext->img_src_left, 0, sizeof(ext->img_src_left)); memset(ext->img_src_mid, 0, sizeof(ext->img_src_mid)); memset(ext->img_src_right, 0, sizeof(ext->img_src_right)); #endif ext->act_cf = LV_IMG_CF_UNKNOWN; /*The signal and design functions are not copied so set them here*/ lv_obj_set_signal_cb(new_imgbtn, lv_imgbtn_signal); lv_obj_set_design_cb(new_imgbtn, lv_imgbtn_design); /*Init the new image button image button*/ if(copy == NULL) { } /*Copy an existing image button*/ else { lv_imgbtn_ext_t * copy_ext = lv_obj_get_ext_attr(copy); #if LV_IMGBTN_TILED == 0 memcpy((void*)ext->img_src, copy_ext->img_src, sizeof(ext->img_src)); #else memcpy((void*)ext->img_src_left, copy_ext->img_src_left, sizeof(ext->img_src_left)); memcpy((void*)ext->img_src_mid, copy_ext->img_src_mid, sizeof(ext->img_src_mid)); memcpy((void*)ext->img_src_right, copy_ext->img_src_right, sizeof(ext->img_src_right)); #endif /*Refresh the style with new signal function*/ lv_obj_refresh_style(new_imgbtn); } LV_LOG_INFO("image button created"); return new_imgbtn; } /*===================== * Setter functions *====================*/ #if LV_IMGBTN_TILED == 0 /** * Set images for a state of the image button * @param imgbtn pointer to an image button object * @param state for which state set the new image (from `lv_btn_state_t`) ` * @param src pointer to an image source (a C array or path to a file) */ void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src) { LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME); lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); ext->img_src[state] = src; refr_img(imgbtn); } #else /** * Set images for a state of the image button * @param imgbtn pointer to an image button object * @param state for which state set the new image (from `lv_btn_state_t`) ` * @param src_left pointer to an image source for the left side of the button (a C array or path to * a file) * @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C * array or path to a file) * @param src_right pointer to an image source for the right side of the button (a C array or path * to a file) */ void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src_left, const void * src_mid, const void * src_right) { LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME); if(lv_img_src_get_type(src_left) == LV_IMG_SRC_SYMBOL || lv_img_src_get_type(src_mid) == LV_IMG_SRC_SYMBOL || lv_img_src_get_type(src_right) == LV_IMG_SRC_SYMBOL ) { LV_LOG_WARN("lv_imgbtn_set_src: symbols are not supported in tiled mode"); return; } lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); ext->img_src_left[state] = src_left; ext->img_src_mid[state] = src_mid; ext->img_src_right[state] = src_right; refr_img(imgbtn); } #endif /*===================== * Getter functions *====================*/ #if LV_IMGBTN_TILED == 0 /** * Get the images in a given state * @param imgbtn pointer to an image button object * @param state the state where to get the image (from `lv_btn_state_t`) ` * @return pointer to an image source (a C array or path to a file) */ const void * lv_imgbtn_get_src(lv_obj_t * imgbtn, lv_btn_state_t state) { LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME); lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); return ext->img_src[state]; } #else /** * Get the left image in a given state * @param imgbtn pointer to an image button object * @param state the state where to get the image (from `lv_btn_state_t`) ` * @return pointer to the left image source (a C array or path to a file) */ const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_btn_state_t state) { LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME); lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); return ext->img_src_left[state]; } /** * Get the middle image in a given state * @param imgbtn pointer to an image button object * @param state the state where to get the image (from `lv_btn_state_t`) ` * @return pointer to the middle image source (a C array or path to a file) */ const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_btn_state_t state) { LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME); lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); return ext->img_src_mid[state]; } /** * Get the right image in a given state * @param imgbtn pointer to an image button object * @param state the state where to get the image (from `lv_btn_state_t`) ` * @return pointer to the left image source (a C array or path to a file) */ const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_btn_state_t state) { LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME); lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); return ext->img_src_right[state]; } #endif /*===================== * Other functions *====================*/ /* * New object specific "other" functions come here */ /********************** * STATIC FUNCTIONS **********************/ /** * Handle the drawing related tasks of the image buttons * @param imgbtn pointer to an object * @param clip_area the object will be drawn only in this area * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area * (return 'true' if yes) * LV_DESIGN_DRAW: draw the object (always return 'true') * LV_DESIGN_DRAW_POST: drawing after every children are drawn * @param return an element of `lv_design_res_t` */ static lv_design_res_t lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * clip_area, lv_design_mode_t mode) { /*Return false if the object is not covers the mask_p area*/ if(mode == LV_DESIGN_COVER_CHK) { lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); lv_design_res_t cover = LV_DESIGN_RES_NOT_COVER; if(ext->act_cf == LV_IMG_CF_TRUE_COLOR || ext->act_cf == LV_IMG_CF_RAW) { cover = lv_area_is_in(clip_area, &imgbtn->coords, 0) ? LV_DESIGN_RES_COVER : LV_DESIGN_RES_NOT_COVER; } return cover; } /*Draw the object*/ else if(mode == LV_DESIGN_DRAW_MAIN) { /*Just draw an image*/ lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); lv_btn_state_t state = lv_imgbtn_get_state(imgbtn); #if LV_IMGBTN_TILED == 0 const void * src = ext->img_src[state]; if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) { lv_draw_label_dsc_t label_dsc; lv_draw_label_dsc_init(&label_dsc); lv_obj_init_draw_label_dsc(imgbtn, LV_IMGBTN_PART_MAIN, &label_dsc); lv_draw_label(&imgbtn->coords, clip_area, &label_dsc, src, NULL); } else { lv_draw_img_dsc_t img_dsc; lv_draw_img_dsc_init(&img_dsc); lv_draw_img(&imgbtn->coords, clip_area, src, &img_dsc); } #else const void * src = ext->img_src_left[state]; if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) { LV_LOG_WARN("lv_imgbtn_design: SYMBOLS are not supported in tiled mode") return LV_DESIGN_RES_OK; } lv_img_header_t header; lv_area_t coords; lv_coord_t left_w = 0; lv_coord_t right_w = 0; if(src) { lv_img_decoder_get_info(src, &header); left_w = header.w; coords.x1 = imgbtn->coords.x1; coords.y1 = imgbtn->coords.y1; coords.x2 = coords.x1 + header.w - 1; coords.y2 = coords.y1 + header.h - 1; lv_draw_img(&coords, clip_area, src, style, 0, NULL, LV_IMG_ZOOM_NONE, false, opa_scale); } src = ext->img_src_right[state]; if(src) { lv_img_decoder_get_info(src, &header); right_w = header.w; coords.x1 = imgbtn->coords.x2 - header.w + 1; coords.y1 = imgbtn->coords.y1; coords.x2 = imgbtn->coords.x2; coords.y2 = imgbtn->coords.y1 + header.h - 1; lv_draw_img(&coords, clip_area, src, style, 0, NULL, LV_IMG_ZOOM_NONE, false, opa_scale); } src = ext->img_src_mid[state]; if(src) { lv_area_t clip_center_area; clip_center_area.x1 = imgbtn->coords.x1 + left_w; clip_center_area.x2 = imgbtn->coords.x2 - right_w; clip_center_area.y1 = imgbtn->coords.y1; clip_center_area.y2 = imgbtn->coords.y2; bool comm_res; comm_res = lv_area_intersect(&clip_center_area, &clip_center_area, clip_area); if(comm_res) { lv_coord_t obj_w = lv_obj_get_width(imgbtn); lv_coord_t i; lv_img_decoder_get_info(src, &header); coords.x1 = imgbtn->coords.x1 + left_w; coords.y1 = imgbtn->coords.y1; coords.x2 = coords.x1 + header.w - 1; coords.y2 = imgbtn->coords.y1 + header.h - 1; for(i = 0; i < obj_w - right_w - left_w; i += header.w) { lv_draw_img(&coords, &clip_center_area, src, style, 0, NULL, LV_IMG_ZOOM_NONE, false, opa_scale); coords.x1 = coords.x2 + 1; coords.x2 += header.w; } } } #endif } /*Post draw when the children are drawn*/ else if(mode == LV_DESIGN_DRAW_POST) { } return LV_DESIGN_RES_OK; } /** * Signal function of the image button * @param imgbtn pointer to a image button object * @param sign a signal type from lv_signal_t enum * @param param pointer to a signal specific variable * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted */ static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param) { lv_res_t res; /* Include the ancient signal function */ res = ancestor_signal(imgbtn, sign, param); if(res != LV_RES_OK) return res; if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); if(sign == LV_SIGNAL_STYLE_CHG) { /* If the style changed then the button was clicked, released etc. so probably the state was * changed as well Set the new image for the new state.*/ refr_img(imgbtn); } else if(sign == LV_SIGNAL_CLEANUP) { /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ } return res; } static void refr_img(lv_obj_t * imgbtn) { lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); lv_btn_state_t state = lv_imgbtn_get_state(imgbtn); lv_img_header_t header; #if LV_IMGBTN_TILED == 0 const void * src = ext->img_src[state]; #else const void * src = ext->img_src_mid[state]; #endif lv_res_t info_res = LV_RES_OK; if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) { const lv_font_t * font = lv_obj_get_style_ptr(imgbtn, LV_IMGBTN_PART_MAIN, LV_STYLE_FONT); header.h = lv_font_get_line_height(font); header.w = lv_txt_get_width(src, (uint16_t)strlen(src), font, 0, LV_TXT_FLAG_NONE); header.always_zero = 0; header.cf = LV_IMG_CF_ALPHA_1BIT; } else { info_res = lv_img_decoder_get_info(src, &header); } if(info_res == LV_RES_OK) { ext->act_cf = header.cf; #if LV_IMGBTN_TILED == 0 lv_obj_set_size(imgbtn, header.w, header.h); #else lv_obj_set_height(imgbtn, header.h); #endif } else { ext->act_cf = LV_IMG_CF_UNKNOWN; } lv_obj_invalidate(imgbtn); } #endif