1 /**
2 * @file lv_imgbtn.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "../lv_misc/lv_debug.h"
11 #include "../lv_themes/lv_theme.h"
12 #include "lv_imgbtn.h"
13 #include "lv_label.h"
14
15
16 #if LV_USE_IMGBTN != 0
17
18 /*********************
19 * DEFINES
20 *********************/
21 #define LV_OBJX_NAME "lv_imgbtn"
22
23 /**********************
24 * TYPEDEFS
25 **********************/
26
27 /**********************
28 * STATIC PROTOTYPES
29 **********************/
30 static lv_design_res_t lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * clip_area, lv_design_mode_t mode);
31 static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param);
32 static void refr_img(lv_obj_t * imgbtn);
33
34 /**********************
35 * STATIC VARIABLES
36 **********************/
37 static lv_signal_cb_t ancestor_signal;
38 static lv_design_cb_t ancestor_design;
39
40 /**********************
41 * MACROS
42 **********************/
43
44 /**********************
45 * GLOBAL FUNCTIONS
46 **********************/
47
48 /**
49 * Create a image button object
50 * @param par pointer to an object, it will be the parent of the new image button
51 * @param copy pointer to a image button object, if not NULL then the new object will be copied from
52 * it
53 * @return pointer to the created image button
54 */
lv_imgbtn_create(lv_obj_t * par,const lv_obj_t * copy)55 lv_obj_t * lv_imgbtn_create(lv_obj_t * par, const lv_obj_t * copy)
56 {
57 LV_LOG_TRACE("image button create started");
58
59 /*Create the ancestor of image button*/
60 lv_obj_t * imgbtn = lv_btn_create(par, copy);
61 LV_ASSERT_MEM(imgbtn);
62 if(imgbtn == NULL) return NULL;
63
64 /*Allocate the image button type specific extended data*/
65 lv_imgbtn_ext_t * ext = lv_obj_allocate_ext_attr(imgbtn, sizeof(lv_imgbtn_ext_t));
66 LV_ASSERT_MEM(ext);
67 if(ext == NULL) {
68 lv_obj_del(imgbtn);
69 return NULL;
70 }
71
72 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(imgbtn);
73 if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(imgbtn);
74
75 /*Initialize the allocated 'ext' */
76 _lv_memset_00((void *)ext->img_src_mid, sizeof(ext->img_src_mid));
77 #if LV_IMGBTN_TILED
78 _lv_memset_00(ext->img_src_left, sizeof(ext->img_src_left));
79 _lv_memset_00(ext->img_src_right, sizeof(ext->img_src_right));
80 #endif
81 ext->tiled = 0;
82
83 ext->act_cf = LV_IMG_CF_UNKNOWN;
84
85 /*The signal and design functions are not copied so set them here*/
86 lv_obj_set_signal_cb(imgbtn, lv_imgbtn_signal);
87 lv_obj_set_design_cb(imgbtn, lv_imgbtn_design);
88
89 /*Init the new image button image button*/
90 if(copy == NULL) {
91 lv_theme_apply(imgbtn, LV_THEME_IMGBTN);
92 }
93 /*Copy an existing image button*/
94 else {
95 lv_imgbtn_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
96 _lv_memcpy((void *)ext->img_src_mid, copy_ext->img_src_mid, sizeof(ext->img_src_mid));
97 #if LV_IMGBTN_TILED
98 _lv_memcpy((void *)ext->img_src_left, copy_ext->img_src_left, sizeof(ext->img_src_left));
99 _lv_memcpy((void *)ext->img_src_right, copy_ext->img_src_right, sizeof(ext->img_src_right));
100 #endif
101 ext->tiled = copy_ext->tiled;
102 /*Refresh the style with new signal function*/
103 lv_obj_refresh_style(imgbtn, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
104 }
105
106 LV_LOG_INFO("image button created");
107
108 return imgbtn;
109 }
110
111 /*=====================
112 * Setter functions
113 *====================*/
114
115 /**
116 * Set images for a state of the image button
117 * @param imgbtn pointer to an image button object
118 * @param state for which state set the new image (from `lv_btn_state_t`) `
119 * @param src pointer to an image source (a C array or path to a file)
120 */
lv_imgbtn_set_src(lv_obj_t * imgbtn,lv_btn_state_t state,const void * src)121 void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src)
122 {
123 LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME);
124
125 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
126
127 ext->img_src_mid[state] = src;
128 #if LV_IMGBTN_TILED
129 ext->img_src_left[state] = NULL;
130 ext->img_src_right[state] = NULL;
131 #endif
132 ext->tiled = 0;
133 refr_img(imgbtn);
134 }
135
136 #if LV_IMGBTN_TILED
137 /**
138 * Set images for a state of the image button
139 * @param imgbtn pointer to an image button object
140 * @param state for which state set the new image (from `lv_btn_state_t`) `
141 * @param src_left pointer to an image source for the left side of the button (a C array or path to
142 * a file)
143 * @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C
144 * array or path to a file)
145 * @param src_right pointer to an image source for the right side of the button (a C array or path
146 * to a file)
147 */
lv_imgbtn_set_src_tiled(lv_obj_t * imgbtn,lv_btn_state_t state,const void * src_left,const void * src_mid,const void * src_right)148 void lv_imgbtn_set_src_tiled(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src_left, const void * src_mid,
149 const void * src_right)
150 {
151 LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME);
152
153 if(lv_img_src_get_type(src_left) == LV_IMG_SRC_SYMBOL ||
154 lv_img_src_get_type(src_mid) == LV_IMG_SRC_SYMBOL ||
155 lv_img_src_get_type(src_right) == LV_IMG_SRC_SYMBOL) {
156 LV_LOG_WARN("lv_imgbtn_set_src: symbols are not supported in tiled mode");
157 return;
158 }
159
160 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
161
162 ext->img_src_left[state] = src_left;
163 ext->img_src_mid[state] = src_mid;
164 ext->img_src_right[state] = src_right;
165
166 ext->tiled = 1;
167
168 refr_img(imgbtn);
169 }
170
171 #endif
172
173 /*=====================
174 * Getter functions
175 *====================*/
176
177 /**
178 * Get the images in a given state
179 * @param imgbtn pointer to an image button object
180 * @param state the state where to get the image (from `lv_btn_state_t`) `
181 * @return pointer to an image source (a C array or path to a file)
182 */
lv_imgbtn_get_src(lv_obj_t * imgbtn,lv_btn_state_t state)183 const void * lv_imgbtn_get_src(lv_obj_t * imgbtn, lv_btn_state_t state)
184 {
185 LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME);
186
187 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
188
189 return ext->img_src_mid[state];
190 }
191 #if LV_IMGBTN_TILED
192
193 /**
194 * Get the left image in a given state
195 * @param imgbtn pointer to an image button object
196 * @param state the state where to get the image (from `lv_btn_state_t`) `
197 * @return pointer to the left image source (a C array or path to a file)
198 */
lv_imgbtn_get_src_left(lv_obj_t * imgbtn,lv_btn_state_t state)199 const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_btn_state_t state)
200 {
201 LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME);
202
203 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
204
205 return ext->img_src_left[state];
206 }
207
208 /**
209 * Get the middle image in a given state
210 * @param imgbtn pointer to an image button object
211 * @param state the state where to get the image (from `lv_btn_state_t`) `
212 * @return pointer to the middle image source (a C array or path to a file)
213 */
lv_imgbtn_get_src_middle(lv_obj_t * imgbtn,lv_btn_state_t state)214 const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_btn_state_t state)
215 {
216 LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME);
217
218 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
219
220 return ext->img_src_mid[state];
221 }
222
223 /**
224 * Get the right image in a given state
225 * @param imgbtn pointer to an image button object
226 * @param state the state where to get the image (from `lv_btn_state_t`) `
227 * @return pointer to the left image source (a C array or path to a file)
228 */
lv_imgbtn_get_src_right(lv_obj_t * imgbtn,lv_btn_state_t state)229 const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_btn_state_t state)
230 {
231 LV_ASSERT_OBJ(imgbtn, LV_OBJX_NAME);
232
233 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
234
235 return ext->img_src_right[state];
236 }
237
238 #endif
239
240 /*=====================
241 * Other functions
242 *====================*/
243
244 /*
245 * New object specific "other" functions come here
246 */
247
248 /**********************
249 * STATIC FUNCTIONS
250 **********************/
251
252 /**
253 * Handle the drawing related tasks of the image buttons
254 * @param imgbtn pointer to an object
255 * @param clip_area the object will be drawn only in this area
256 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
257 * (return 'true' if yes)
258 * LV_DESIGN_DRAW: draw the object (always return 'true')
259 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
260 * @param return an element of `lv_design_res_t`
261 */
lv_imgbtn_design(lv_obj_t * imgbtn,const lv_area_t * clip_area,lv_design_mode_t mode)262 static lv_design_res_t lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * clip_area, lv_design_mode_t mode)
263 {
264 /*Return false if the object is not covers the mask_p area*/
265 if(mode == LV_DESIGN_COVER_CHK) {
266 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
267 lv_design_res_t cover = LV_DESIGN_RES_NOT_COVER;
268 if(ext->act_cf == LV_IMG_CF_TRUE_COLOR || ext->act_cf == LV_IMG_CF_RAW) {
269 cover = _lv_area_is_in(clip_area, &imgbtn->coords, 0) ? LV_DESIGN_RES_COVER : LV_DESIGN_RES_NOT_COVER;
270 }
271
272 return cover;
273 }
274 /*Draw the object*/
275 else if(mode == LV_DESIGN_DRAW_MAIN) {
276 lv_area_t img_coords;
277
278 lv_obj_get_coords(imgbtn, &img_coords);
279
280 lv_draw_rect_dsc_t bg_dsc;
281 lv_draw_rect_dsc_init(&bg_dsc);
282 lv_obj_init_draw_rect_dsc(imgbtn, LV_IMGBTN_PART_MAIN, &bg_dsc);
283
284 /*If the border is drawn later disable loading its properties*/
285 if(lv_obj_get_style_border_post(imgbtn, LV_OBJ_PART_MAIN)) {
286 bg_dsc.border_opa = LV_OPA_TRANSP;
287 }
288
289 lv_area_t bg_coords;
290 lv_area_copy(&bg_coords, &img_coords);
291 bg_coords.x1 -= lv_obj_get_style_pad_left(imgbtn, LV_IMGBTN_PART_MAIN);
292 bg_coords.x2 += lv_obj_get_style_pad_right(imgbtn, LV_IMGBTN_PART_MAIN);
293 bg_coords.y1 -= lv_obj_get_style_pad_top(imgbtn, LV_IMGBTN_PART_MAIN);
294 bg_coords.y2 += lv_obj_get_style_pad_bottom(imgbtn, LV_IMGBTN_PART_MAIN);
295
296 lv_draw_rect(&bg_coords, clip_area, &bg_dsc);
297
298 if(lv_obj_get_style_clip_corner(imgbtn, LV_OBJ_PART_MAIN)) {
299 lv_draw_mask_radius_param_t * mp = _lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
300
301 lv_coord_t r = lv_obj_get_style_radius(imgbtn, LV_OBJ_PART_MAIN);
302
303 lv_draw_mask_radius_init(mp, &bg_coords, r, false);
304 /*Add the mask and use `img+8` as custom id. Don't use `obj` directly because it might be used by the user*/
305 lv_draw_mask_add(mp, imgbtn + 8);
306 }
307
308 /*Just draw an image*/
309 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
310 lv_btn_state_t state = lv_imgbtn_get_state(imgbtn);
311
312 /*Simply draw the middle src if no tiled*/
313 if(!ext->tiled) {
314 const void * src = ext->img_src_mid[state];
315 if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) {
316 lv_draw_label_dsc_t label_dsc;
317 lv_draw_label_dsc_init(&label_dsc);
318 lv_obj_init_draw_label_dsc(imgbtn, LV_IMGBTN_PART_MAIN, &label_dsc);
319 lv_draw_label(&imgbtn->coords, clip_area, &label_dsc, src, NULL);
320 }
321 else {
322 lv_draw_img_dsc_t img_dsc;
323 lv_draw_img_dsc_init(&img_dsc);
324 lv_obj_init_draw_img_dsc(imgbtn, LV_IMGBTN_PART_MAIN, &img_dsc);
325 lv_draw_img(&imgbtn->coords, clip_area, src, &img_dsc);
326 }
327 }
328 else {
329 #if LV_IMGBTN_TILED
330 const void * src = ext->img_src_left[state];
331 if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) {
332 LV_LOG_WARN("lv_imgbtn_design: SYMBOLS are not supported in tiled mode")
333 return LV_DESIGN_RES_OK;
334 }
335
336 lv_coord_t w = lv_obj_get_style_transform_width(imgbtn, LV_OBJ_PART_MAIN);
337 lv_coord_t h = lv_obj_get_style_transform_height(imgbtn, LV_OBJ_PART_MAIN);
338 lv_area_t coords;
339 lv_area_copy(&coords, &imgbtn->coords);
340 coords.x1 -= w;
341 coords.x2 += w;
342 coords.y1 -= h;
343 coords.y2 += h;
344
345 lv_draw_img_dsc_t img_dsc;
346 lv_draw_img_dsc_init(&img_dsc);
347 lv_obj_init_draw_img_dsc(imgbtn, LV_IMGBTN_PART_MAIN, &img_dsc);
348
349 lv_img_header_t header;
350 lv_area_t coords_part;
351 lv_coord_t left_w = 0;
352 lv_coord_t right_w = 0;
353
354 if(src) {
355 lv_img_decoder_get_info(src, &header);
356 left_w = header.w;
357 coords_part.x1 = coords.x1;
358 coords_part.y1 = coords.y1;
359 coords_part.x2 = coords.x1 + header.w - 1;
360 coords_part.y2 = coords.y1 + header.h - 1;
361 lv_draw_img(&coords_part, clip_area, src, &img_dsc);
362 }
363
364 src = ext->img_src_right[state];
365 if(src) {
366 lv_img_decoder_get_info(src, &header);
367 right_w = header.w;
368 coords_part.x1 = coords.x2 - header.w + 1;
369 coords_part.y1 = coords.y1;
370 coords_part.x2 = coords.x2;
371 coords_part.y2 = coords.y1 + header.h - 1;
372 lv_draw_img(&coords_part, clip_area, src, &img_dsc);
373 }
374
375 src = ext->img_src_mid[state];
376 if(src) {
377 lv_area_t clip_center_area;
378 clip_center_area.x1 = coords.x1 + left_w;
379 clip_center_area.x2 = coords.x2 - right_w;
380 clip_center_area.y1 = coords.y1;
381 clip_center_area.y2 = coords.y2;
382
383 bool comm_res;
384 comm_res = _lv_area_intersect(&clip_center_area, &clip_center_area, clip_area);
385 if(comm_res) {
386 lv_coord_t obj_w = lv_obj_get_width(imgbtn);
387 lv_coord_t i;
388 lv_img_decoder_get_info(src, &header);
389
390 coords_part.x1 = coords.x1 + left_w;
391 coords_part.y1 = coords.y1;
392 coords_part.x2 = coords_part.x1 + header.w - 1;
393 coords_part.y2 = coords_part.y1 + header.h - 1;
394
395 for(i = 0; i < obj_w - right_w - left_w; i += header.w) {
396
397 lv_draw_img(&coords_part, &clip_center_area, src, &img_dsc);
398 coords_part.x1 = coords_part.x2 + 1;
399 coords_part.x2 += header.w;
400 }
401 }
402 }
403 #endif
404 }
405 }
406 /*Post draw when the children are drawn*/
407 else if(mode == LV_DESIGN_DRAW_POST) {
408 if(lv_obj_get_style_clip_corner(imgbtn, LV_OBJ_PART_MAIN)) {
409 lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(imgbtn + 8);
410 _lv_mem_buf_release(param);
411 }
412
413 lv_draw_rect_dsc_t draw_dsc;
414 lv_draw_rect_dsc_init(&draw_dsc);
415
416 /*If the border is drawn later disable loading other properties*/
417 if(lv_obj_get_style_border_post(imgbtn, LV_OBJ_PART_MAIN)) {
418 draw_dsc.bg_opa = LV_OPA_TRANSP;
419 draw_dsc.pattern_opa = LV_OPA_TRANSP;
420 draw_dsc.shadow_opa = LV_OPA_TRANSP;
421 lv_obj_init_draw_rect_dsc(imgbtn, LV_OBJ_PART_MAIN, &draw_dsc);
422
423
424 lv_area_t bg_coords;
425 lv_area_copy(&bg_coords, &imgbtn->coords);
426 bg_coords.x1 -= lv_obj_get_style_pad_left(imgbtn, LV_IMGBTN_PART_MAIN);
427 bg_coords.x2 += lv_obj_get_style_pad_right(imgbtn, LV_IMGBTN_PART_MAIN);
428 bg_coords.y1 -= lv_obj_get_style_pad_top(imgbtn, LV_IMGBTN_PART_MAIN);
429 bg_coords.y2 += lv_obj_get_style_pad_bottom(imgbtn, LV_IMGBTN_PART_MAIN);
430
431 lv_draw_rect(&bg_coords, clip_area, &draw_dsc);
432 }
433 }
434
435 return LV_DESIGN_RES_OK;
436 }
437
438 /**
439 * Signal function of the image button
440 * @param imgbtn pointer to a image button object
441 * @param sign a signal type from lv_signal_t enum
442 * @param param pointer to a signal specific variable
443 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
444 */
lv_imgbtn_signal(lv_obj_t * imgbtn,lv_signal_t sign,void * param)445 static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param)
446 {
447 lv_res_t res;
448
449 /* Include the ancient signal function */
450 res = ancestor_signal(imgbtn, sign, param);
451 if(res != LV_RES_OK) return res;
452 if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
453
454 if(sign == LV_SIGNAL_STYLE_CHG) {
455 /* If the style changed then the button was clicked, released etc. so probably the state was
456 * changed as well Set the new image for the new state.*/
457 refr_img(imgbtn);
458 }
459 else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
460 /*Handle the padding of the background*/
461 lv_style_int_t left = lv_obj_get_style_pad_left(imgbtn, LV_IMGBTN_PART_MAIN);
462 lv_style_int_t right = lv_obj_get_style_pad_right(imgbtn, LV_IMGBTN_PART_MAIN);
463 lv_style_int_t top = lv_obj_get_style_pad_top(imgbtn, LV_IMGBTN_PART_MAIN);
464 lv_style_int_t bottom = lv_obj_get_style_pad_bottom(imgbtn, LV_IMGBTN_PART_MAIN);
465
466 imgbtn->ext_draw_pad = LV_MATH_MAX(imgbtn->ext_draw_pad, left);
467 imgbtn->ext_draw_pad = LV_MATH_MAX(imgbtn->ext_draw_pad, right);
468 imgbtn->ext_draw_pad = LV_MATH_MAX(imgbtn->ext_draw_pad, top);
469 imgbtn->ext_draw_pad = LV_MATH_MAX(imgbtn->ext_draw_pad, bottom);
470 }
471 else if(sign == LV_SIGNAL_PRESSED || sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
472 refr_img(imgbtn);
473 }
474 else if(sign == LV_SIGNAL_CLEANUP) {
475 /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
476 }
477
478 return res;
479 }
480
refr_img(lv_obj_t * imgbtn)481 static void refr_img(lv_obj_t * imgbtn)
482 {
483 lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
484 lv_btn_state_t state = lv_imgbtn_get_state(imgbtn);
485 lv_img_header_t header;
486
487 const void * src = ext->img_src_mid[state];
488 if(src == NULL) return;
489
490 lv_res_t info_res = LV_RES_OK;
491 if(lv_img_src_get_type(src) == LV_IMG_SRC_SYMBOL) {
492 const lv_font_t * font = lv_obj_get_style_text_font(imgbtn, LV_IMGBTN_PART_MAIN);
493 header.h = lv_font_get_line_height(font);
494 header.w = _lv_txt_get_width(src, (uint16_t)strlen(src), font, 0, LV_TXT_FLAG_NONE);
495 header.always_zero = 0;
496 header.cf = LV_IMG_CF_ALPHA_1BIT;
497 }
498 else {
499 info_res = lv_img_decoder_get_info(src, &header);
500 }
501
502 if(info_res == LV_RES_OK) {
503 ext->act_cf = header.cf;
504 if(ext->tiled) lv_obj_set_height(imgbtn, header.h); /*Keep the sure defined width*/
505 else lv_obj_set_size(imgbtn, header.w, header.h);
506 }
507 else {
508 ext->act_cf = LV_IMG_CF_UNKNOWN;
509 }
510
511 lv_obj_invalidate(imgbtn);
512 }
513
514 #endif
515