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 * Get the left image in a given state
117 * @param obj pointer to an image button object
118 * @param state the state where to get the image (from `lv_btn_state_t`) `
119 * @return pointer to the left image source (a C array or path to a file)
120 */
lv_imgbtn_get_src_left(lv_obj_t * obj,lv_imgbtn_state_t state)121 const void * lv_imgbtn_get_src_left(lv_obj_t * obj, lv_imgbtn_state_t state)
122 {
123 LV_ASSERT_OBJ(obj, MY_CLASS);
124
125 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
126
127 return imgbtn->img_src_left[state];
128 }
129
130 /**
131 * Get the middle image in a given state
132 * @param obj pointer to an image button object
133 * @param state the state where to get the image (from `lv_btn_state_t`) `
134 * @return pointer to the middle image source (a C array or path to a file)
135 */
lv_imgbtn_get_src_middle(lv_obj_t * obj,lv_imgbtn_state_t state)136 const void * lv_imgbtn_get_src_middle(lv_obj_t * obj, lv_imgbtn_state_t state)
137 {
138 LV_ASSERT_OBJ(obj, MY_CLASS);
139 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
140
141 return imgbtn->img_src_mid[state];
142 }
143
144 /**
145 * Get the right image in a given state
146 * @param obj pointer to an image button object
147 * @param state the state where to get the image (from `lv_btn_state_t`) `
148 * @return pointer to the left image source (a C array or path to a file)
149 */
lv_imgbtn_get_src_right(lv_obj_t * obj,lv_imgbtn_state_t state)150 const void * lv_imgbtn_get_src_right(lv_obj_t * obj, lv_imgbtn_state_t state)
151 {
152 LV_ASSERT_OBJ(obj, MY_CLASS);
153 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
154
155 return imgbtn->img_src_right[state];
156 }
157
158 /**********************
159 * STATIC FUNCTIONS
160 **********************/
161
lv_imgbtn_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)162 static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
163 {
164 LV_UNUSED(class_p);
165 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
166 /*Initialize the allocated 'ext'*/
167 lv_memset_00((void *)imgbtn->img_src_mid, sizeof(imgbtn->img_src_mid));
168 lv_memset_00(imgbtn->img_src_left, sizeof(imgbtn->img_src_left));
169 lv_memset_00(imgbtn->img_src_right, sizeof(imgbtn->img_src_right));
170
171 imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
172 }
173
lv_imgbtn_event(const lv_obj_class_t * class_p,lv_event_t * e)174 static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e)
175 {
176 LV_UNUSED(class_p);
177
178 lv_res_t res = lv_obj_event_base(&lv_imgbtn_class, e);
179 if(res != LV_RES_OK) return;
180
181 lv_event_code_t code = lv_event_get_code(e);
182 lv_obj_t * obj = lv_event_get_target(e);
183 if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
184 refr_img(obj);
185 }
186 else if(code == LV_EVENT_DRAW_MAIN) {
187 draw_main(e);
188 }
189 else if(code == LV_EVENT_COVER_CHECK) {
190 lv_cover_check_info_t * info = lv_event_get_param(e);
191 if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
192 }
193 else if(code == LV_EVENT_GET_SELF_SIZE) {
194 lv_point_t * p = lv_event_get_self_size_info(e);
195 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
196 lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
197 if(imgbtn->img_src_left[state] == NULL &&
198 imgbtn->img_src_mid[state] != NULL &&
199 imgbtn->img_src_right[state] == NULL) {
200 lv_img_header_t header;
201 lv_img_decoder_get_info(imgbtn->img_src_mid[state], &header);
202 p->x = LV_MAX(p->x, header.w);
203 }
204 }
205 /*Sent when the widget is checked due to LV_OBJ_FLAG_CHECKABLE */
206 else if(code == LV_EVENT_VALUE_CHANGED) {
207 if(lv_obj_has_state(obj, LV_STATE_CHECKED)) {
208 lv_imgbtn_set_state(obj, LV_IMGBTN_STATE_CHECKED_RELEASED);
209 }
210 else {
211 lv_imgbtn_set_state(obj, LV_IMGBTN_STATE_RELEASED);
212 }
213 }
214 }
215
draw_main(lv_event_t * e)216 static void draw_main(lv_event_t * e)
217 {
218 lv_obj_t * obj = lv_event_get_target(e);
219 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
220 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
221
222 /*Just draw_main an image*/
223 lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
224
225 /*Simply draw the middle src if no tiled*/
226 const void * src = imgbtn->img_src_left[state];
227
228 lv_coord_t tw = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
229 lv_coord_t th = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
230 lv_area_t coords;
231 lv_area_copy(&coords, &obj->coords);
232 coords.x1 -= tw;
233 coords.x2 += tw;
234 coords.y1 -= th;
235 coords.y2 += th;
236
237 lv_draw_img_dsc_t img_dsc;
238 lv_draw_img_dsc_init(&img_dsc);
239 lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc);
240
241 lv_img_header_t header;
242 lv_area_t coords_part;
243 lv_coord_t left_w = 0;
244 lv_coord_t right_w = 0;
245
246 if(src) {
247 lv_img_decoder_get_info(src, &header);
248 left_w = header.w;
249 coords_part.x1 = coords.x1;
250 coords_part.y1 = coords.y1;
251 coords_part.x2 = coords.x1 + header.w - 1;
252 coords_part.y2 = coords.y1 + header.h - 1;
253 lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
254 }
255
256 src = imgbtn->img_src_right[state];
257 if(src) {
258 lv_img_decoder_get_info(src, &header);
259 right_w = header.w;
260 coords_part.x1 = coords.x2 - header.w + 1;
261 coords_part.y1 = coords.y1;
262 coords_part.x2 = coords.x2;
263 coords_part.y2 = coords.y1 + header.h - 1;
264 lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
265 }
266
267 src = imgbtn->img_src_mid[state];
268 if(src) {
269 lv_area_t clip_area_center;
270 clip_area_center.x1 = coords.x1 + left_w;
271 clip_area_center.x2 = coords.x2 - right_w;
272 clip_area_center.y1 = coords.y1;
273 clip_area_center.y2 = coords.y2;
274
275 bool comm_res;
276 comm_res = _lv_area_intersect(&clip_area_center, &clip_area_center, draw_ctx->clip_area);
277 if(comm_res) {
278 lv_coord_t i;
279 lv_img_decoder_get_info(src, &header);
280
281 const lv_area_t * clip_area_ori = draw_ctx->clip_area;
282 draw_ctx->clip_area = &clip_area_center;
283
284 coords_part.x1 = coords.x1 + left_w;
285 coords_part.y1 = coords.y1;
286 coords_part.x2 = coords_part.x1 + header.w - 1;
287 coords_part.y2 = coords_part.y1 + header.h - 1;
288
289 for(i = coords_part.x1; i < (lv_coord_t)(clip_area_center.x2 + header.w - 1); i += header.w) {
290 lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
291 coords_part.x1 = coords_part.x2 + 1;
292 coords_part.x2 += header.w;
293 }
294 draw_ctx->clip_area = clip_area_ori;
295 }
296 }
297 }
298
refr_img(lv_obj_t * obj)299 static void refr_img(lv_obj_t * obj)
300 {
301 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
302 lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
303 lv_img_header_t header;
304
305 const void * src = imgbtn->img_src_mid[state];
306 if(src == NULL) return;
307
308 lv_res_t info_res = LV_RES_OK;
309 info_res = lv_img_decoder_get_info(src, &header);
310
311 if(info_res == LV_RES_OK) {
312 imgbtn->act_cf = header.cf;
313 lv_obj_refresh_self_size(obj);
314 lv_obj_set_height(obj, header.h); /*Keep the user defined width*/
315 }
316 else {
317 imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
318 }
319
320 lv_obj_invalidate(obj);
321 }
322
323 /**
324 * If `src` is not defined for the current state try to get a state which is related to the current but has `src`.
325 * E.g. if the PRESSED src is not set but the RELEASED does, use the RELEASED.
326 * @param imgbtn pointer to an image button
327 * @param state the state to convert
328 * @return the suggested state
329 */
suggest_state(lv_obj_t * obj,lv_imgbtn_state_t state)330 static lv_imgbtn_state_t suggest_state(lv_obj_t * obj, lv_imgbtn_state_t state)
331 {
332 lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
333 if(imgbtn->img_src_mid[state] == NULL) {
334 switch(state) {
335 case LV_IMGBTN_STATE_PRESSED:
336 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
337 break;
338 case LV_IMGBTN_STATE_CHECKED_RELEASED:
339 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
340 break;
341 case LV_IMGBTN_STATE_CHECKED_PRESSED:
342 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
343 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_PRESSED]) return LV_IMGBTN_STATE_PRESSED;
344 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
345 break;
346 case LV_IMGBTN_STATE_DISABLED:
347 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
348 break;
349 case LV_IMGBTN_STATE_CHECKED_DISABLED:
350 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
351 if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
352 break;
353 default:
354 break;
355 }
356 }
357
358 return state;
359 }
360
get_state(const lv_obj_t * imgbtn)361 lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn)
362 {
363 LV_ASSERT_OBJ(imgbtn, MY_CLASS);
364
365 lv_state_t obj_state = lv_obj_get_state(imgbtn);
366
367 if(obj_state & LV_STATE_DISABLED) {
368 if(obj_state & LV_STATE_CHECKED) return LV_IMGBTN_STATE_CHECKED_DISABLED;
369 else return LV_IMGBTN_STATE_DISABLED;
370 }
371
372 if(obj_state & LV_STATE_CHECKED) {
373 if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_CHECKED_PRESSED;
374 else return LV_IMGBTN_STATE_CHECKED_RELEASED;
375 }
376 else {
377 if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_PRESSED;
378 else return LV_IMGBTN_STATE_RELEASED;
379 }
380 }
381
382 #endif
383