1 /**
2  * @file lv_imgbtn.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_imgbtn.h"
11 
12 #if LV_USE_IMGBTN != 0
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 #define MY_CLASS &lv_imgbtn_class
18 
19 /**********************
20  *      TYPEDEFS
21  **********************/
22 
23 /**********************
24  *  STATIC PROTOTYPES
25  **********************/
26 static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
27 static void draw_main(lv_event_t * e);
28 static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e);
29 static void refr_img(lv_obj_t * imgbtn);
30 static lv_imgbtn_state_t suggest_state(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
31 lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn);
32 
33 /**********************
34  *  STATIC VARIABLES
35  **********************/
36 const lv_obj_class_t lv_imgbtn_class = {
37     .base_class = &lv_obj_class,
38     .instance_size = sizeof(lv_imgbtn_t),
39     .constructor_cb = lv_imgbtn_constructor,
40     .event_cb = lv_imgbtn_event,
41 };
42 
43 /**********************
44  *      MACROS
45  **********************/
46 
47 /**********************
48  *   GLOBAL FUNCTIONS
49  **********************/
50 
51 /**
52  * Create an image button object
53  * @param parent pointer to an object, it will be the parent of the new image button
54  * @return pointer to the created image button
55  */
lv_imgbtn_create(lv_obj_t * parent)56 lv_obj_t * lv_imgbtn_create(lv_obj_t * parent)
57 {
58     LV_LOG_INFO("begin");
59     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
60     lv_obj_class_init_obj(obj);
61     return obj;
62 }
63 
64 /*=====================
65  * Setter functions
66  *====================*/
67 
68 /**
69  * Set images for a state of the image button
70  * @param obj pointer to an image button object
71  * @param state for which state set the new image
72  * @param src_left pointer to an image source for the left side of the button (a C array or path to
73  * a file)
74  * @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C
75  * array or path to a file)
76  * @param src_right pointer to an image source for the right side of the button (a C array or path
77  * to a file)
78  */
lv_imgbtn_set_src(lv_obj_t * obj,lv_imgbtn_state_t state,const void * src_left,const void * src_mid,const void * src_right)79 void lv_imgbtn_set_src(lv_obj_t * obj, lv_imgbtn_state_t state, const void * src_left, const void * src_mid,
80                        const void * src_right)
81 {
82     LV_ASSERT_OBJ(obj, MY_CLASS);
83 
84     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
85 
86     imgbtn->img_src_left[state] = src_left;
87     imgbtn->img_src_mid[state] = src_mid;
88     imgbtn->img_src_right[state] = src_right;
89 
90     refr_img(obj);
91 }
92 
lv_imgbtn_set_state(lv_obj_t * obj,lv_imgbtn_state_t state)93 void lv_imgbtn_set_state(lv_obj_t * obj, lv_imgbtn_state_t state)
94 {
95     LV_ASSERT_OBJ(obj, MY_CLASS);
96 
97     lv_state_t obj_state = LV_STATE_DEFAULT;
98     if(state == LV_IMGBTN_STATE_PRESSED || state == LV_IMGBTN_STATE_CHECKED_PRESSED) obj_state |= LV_STATE_PRESSED;
99     if(state == LV_IMGBTN_STATE_DISABLED || state == LV_IMGBTN_STATE_CHECKED_DISABLED) obj_state |= LV_STATE_DISABLED;
100     if(state == LV_IMGBTN_STATE_CHECKED_DISABLED || state == LV_IMGBTN_STATE_CHECKED_PRESSED ||
101        state == LV_IMGBTN_STATE_CHECKED_RELEASED) {
102         obj_state |= LV_STATE_CHECKED;
103     }
104 
105     lv_obj_clear_state(obj, LV_STATE_CHECKED | LV_STATE_PRESSED | LV_STATE_DISABLED);
106     lv_obj_add_state(obj, obj_state);
107 
108     refr_img(obj);
109 }
110 
111 /*=====================
112  * Getter functions
113  *====================*/
114 
115 
116 /**
117  * Get the left image in a given state
118  * @param obj pointer to an image button object
119  * @param state the state where to get the image (from `lv_btn_state_t`) `
120  * @return pointer to the left image source (a C array or path to a file)
121  */
lv_imgbtn_get_src_left(lv_obj_t * obj,lv_imgbtn_state_t state)122 const void * lv_imgbtn_get_src_left(lv_obj_t * obj, lv_imgbtn_state_t state)
123 {
124     LV_ASSERT_OBJ(obj, MY_CLASS);
125 
126     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
127 
128     return imgbtn->img_src_left[state];
129 }
130 
131 /**
132  * Get the middle image in a given state
133  * @param obj pointer to an image button object
134  * @param state the state where to get the image (from `lv_btn_state_t`) `
135  * @return pointer to the middle image source (a C array or path to a file)
136  */
lv_imgbtn_get_src_middle(lv_obj_t * obj,lv_imgbtn_state_t state)137 const void * lv_imgbtn_get_src_middle(lv_obj_t * obj, lv_imgbtn_state_t state)
138 {
139     LV_ASSERT_OBJ(obj, MY_CLASS);
140     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
141 
142     return imgbtn->img_src_mid[state];
143 }
144 
145 /**
146  * Get the right image in a given state
147  * @param obj pointer to an image button object
148  * @param state the state where to get the image (from `lv_btn_state_t`) `
149  * @return pointer to the left image source (a C array or path to a file)
150  */
lv_imgbtn_get_src_right(lv_obj_t * obj,lv_imgbtn_state_t state)151 const void * lv_imgbtn_get_src_right(lv_obj_t * obj, lv_imgbtn_state_t state)
152 {
153     LV_ASSERT_OBJ(obj, MY_CLASS);
154     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
155 
156     return imgbtn->img_src_right[state];
157 }
158 
159 
160 /**********************
161  *   STATIC FUNCTIONS
162  **********************/
163 
lv_imgbtn_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)164 static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
165 {
166     LV_UNUSED(class_p);
167     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
168     /*Initialize the allocated 'ext'*/
169     lv_memset_00((void *)imgbtn->img_src_mid, sizeof(imgbtn->img_src_mid));
170     lv_memset_00(imgbtn->img_src_left, sizeof(imgbtn->img_src_left));
171     lv_memset_00(imgbtn->img_src_right, sizeof(imgbtn->img_src_right));
172 
173     imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
174 }
175 
176 
lv_imgbtn_event(const lv_obj_class_t * class_p,lv_event_t * e)177 static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e)
178 {
179     LV_UNUSED(class_p);
180 
181     lv_res_t res = lv_obj_event_base(&lv_imgbtn_class, e);
182     if(res != LV_RES_OK) return;
183 
184     lv_event_code_t code = lv_event_get_code(e);
185     lv_obj_t * obj = lv_event_get_target(e);
186     if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
187         refr_img(obj);
188     }
189     else if(code == LV_EVENT_DRAW_MAIN) {
190         draw_main(e);
191     }
192     else if(code == LV_EVENT_COVER_CHECK) {
193         lv_cover_check_info_t * info = lv_event_get_param(e);
194         if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
195     }
196     else if(code == LV_EVENT_GET_SELF_SIZE) {
197         lv_point_t * p = lv_event_get_self_size_info(e);
198         lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
199         lv_imgbtn_state_t state  = suggest_state(obj, get_state(obj));
200         if(imgbtn->img_src_left[state] == NULL &&
201            imgbtn->img_src_mid[state] != NULL &&
202            imgbtn->img_src_right[state] == NULL) {
203             lv_img_header_t header;
204             lv_img_decoder_get_info(imgbtn->img_src_mid[state], &header);
205             p->x = LV_MAX(p->x, header.w);
206         }
207     }
208     /*Sent when the widget is checked due to LV_OBJ_FLAG_CHECKABLE */
209     else if(code == LV_EVENT_VALUE_CHANGED) {
210         if(lv_obj_has_state(obj, LV_STATE_CHECKED)) {
211             lv_imgbtn_set_state(obj, LV_IMGBTN_STATE_CHECKED_RELEASED);
212         }
213         else {
214             lv_imgbtn_set_state(obj, LV_IMGBTN_STATE_RELEASED);
215         }
216     }
217 }
218 
draw_main(lv_event_t * e)219 static void draw_main(lv_event_t * e)
220 {
221     lv_obj_t * obj = lv_event_get_target(e);
222     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
223     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
224 
225     /*Just draw_main an image*/
226     lv_imgbtn_state_t state  = suggest_state(obj, get_state(obj));
227 
228     /*Simply draw the middle src if no tiled*/
229     const void * src = imgbtn->img_src_left[state];
230 
231     lv_coord_t tw = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
232     lv_coord_t th = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
233     lv_area_t coords;
234     lv_area_copy(&coords, &obj->coords);
235     coords.x1 -= tw;
236     coords.x2 += tw;
237     coords.y1 -= th;
238     coords.y2 += th;
239 
240     lv_draw_img_dsc_t img_dsc;
241     lv_draw_img_dsc_init(&img_dsc);
242     lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc);
243 
244     lv_img_header_t header;
245     lv_area_t coords_part;
246     lv_coord_t left_w = 0;
247     lv_coord_t right_w = 0;
248 
249     if(src) {
250         lv_img_decoder_get_info(src, &header);
251         left_w = header.w;
252         coords_part.x1 = coords.x1;
253         coords_part.y1 = coords.y1;
254         coords_part.x2 = coords.x1 + header.w - 1;
255         coords_part.y2 = coords.y1 + header.h - 1;
256         lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
257     }
258 
259     src = imgbtn->img_src_right[state];
260     if(src) {
261         lv_img_decoder_get_info(src, &header);
262         right_w = header.w;
263         coords_part.x1 = coords.x2 - header.w + 1;
264         coords_part.y1 = coords.y1;
265         coords_part.x2 = coords.x2;
266         coords_part.y2 = coords.y1 + header.h - 1;
267         lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
268     }
269 
270     src = imgbtn->img_src_mid[state];
271     if(src) {
272         lv_area_t clip_area_center;
273         clip_area_center.x1 = coords.x1 + left_w;
274         clip_area_center.x2 = coords.x2 - right_w;
275         clip_area_center.y1 = coords.y1;
276         clip_area_center.y2 = coords.y2;
277 
278 
279         bool comm_res;
280         comm_res = _lv_area_intersect(&clip_area_center, &clip_area_center, draw_ctx->clip_area);
281         if(comm_res) {
282             lv_coord_t i;
283             lv_img_decoder_get_info(src, &header);
284 
285             const lv_area_t * clip_area_ori = draw_ctx->clip_area;
286             draw_ctx->clip_area = &clip_area_center;
287 
288             coords_part.x1 = coords.x1 + left_w;
289             coords_part.y1 = coords.y1;
290             coords_part.x2 = coords_part.x1 + header.w - 1;
291             coords_part.y2 = coords_part.y1 + header.h - 1;
292 
293             for(i = coords_part.x1; i < (lv_coord_t)(clip_area_center.x2 + header.w - 1); i += header.w) {
294                 lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
295                 coords_part.x1 = coords_part.x2 + 1;
296                 coords_part.x2 += header.w;
297             }
298             draw_ctx->clip_area = clip_area_ori;
299         }
300     }
301 }
302 
refr_img(lv_obj_t * obj)303 static void refr_img(lv_obj_t * obj)
304 {
305     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
306     lv_imgbtn_state_t state  = suggest_state(obj, get_state(obj));
307     lv_img_header_t header;
308 
309     const void * src = imgbtn->img_src_mid[state];
310     if(src == NULL) return;
311 
312     lv_res_t info_res = LV_RES_OK;
313     info_res = lv_img_decoder_get_info(src, &header);
314 
315     if(info_res == LV_RES_OK) {
316         imgbtn->act_cf = header.cf;
317         lv_obj_refresh_self_size(obj);
318         lv_obj_set_height(obj, header.h); /*Keep the user defined width*/
319     }
320     else {
321         imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
322     }
323 
324     lv_obj_invalidate(obj);
325 }
326 
327 /**
328  * If `src` is not defined for the current state try to get a state which is related to the current but has `src`.
329  * E.g. if the PRESSED src is not set but the RELEASED does, use the RELEASED.
330  * @param imgbtn pointer to an image button
331  * @param state the state to convert
332  * @return the suggested state
333  */
suggest_state(lv_obj_t * obj,lv_imgbtn_state_t state)334 static lv_imgbtn_state_t suggest_state(lv_obj_t * obj, lv_imgbtn_state_t state)
335 {
336     lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
337     if(imgbtn->img_src_mid[state] == NULL) {
338         switch(state) {
339             case LV_IMGBTN_STATE_PRESSED:
340                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
341                 break;
342             case LV_IMGBTN_STATE_CHECKED_RELEASED:
343                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
344                 break;
345             case LV_IMGBTN_STATE_CHECKED_PRESSED:
346                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
347                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_PRESSED]) return LV_IMGBTN_STATE_PRESSED;
348                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
349                 break;
350             case LV_IMGBTN_STATE_DISABLED:
351                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
352                 break;
353             case LV_IMGBTN_STATE_CHECKED_DISABLED:
354                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
355                 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
356                 break;
357             default:
358                 break;
359         }
360     }
361 
362     return state;
363 }
364 
get_state(const lv_obj_t * imgbtn)365 lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn)
366 {
367     LV_ASSERT_OBJ(imgbtn, MY_CLASS);
368 
369     lv_state_t obj_state = lv_obj_get_state(imgbtn);
370 
371     if(obj_state & LV_STATE_DISABLED) {
372         if(obj_state & LV_STATE_CHECKED) return LV_IMGBTN_STATE_CHECKED_DISABLED;
373         else return LV_IMGBTN_STATE_DISABLED;
374     }
375 
376     if(obj_state & LV_STATE_CHECKED) {
377         if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_CHECKED_PRESSED;
378         else return LV_IMGBTN_STATE_CHECKED_RELEASED;
379     }
380     else {
381         if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_PRESSED;
382         else return LV_IMGBTN_STATE_RELEASED;
383     }
384 }
385 
386 #endif
387