1 /**
2  * @file lv_imagebutton.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_imagebutton_private.h"
10 #include "../../misc/lv_area_private.h"
11 #include "../../draw/lv_draw_private.h"
12 #include "../../core/lv_obj_private.h"
13 #include "../../core/lv_obj_event_private.h"
14 #include "../../core/lv_obj_class_private.h"
15 
16 
17 #if LV_USE_IMAGEBUTTON != 0
18 
19 #include "../../stdlib/lv_string.h"
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 #define MY_CLASS (&lv_imagebutton_class)
25 
26 /**********************
27  *      TYPEDEFS
28  **********************/
29 
30 /**********************
31  *  STATIC PROTOTYPES
32  **********************/
33 static void lv_imagebutton_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
34 static void draw_main(lv_event_t * e);
35 static void lv_imagebutton_event(const lv_obj_class_t * class_p, lv_event_t * e);
36 static void refr_image(lv_obj_t * imagebutton);
37 static lv_imagebutton_state_t suggest_state(lv_obj_t * imagebutton, lv_imagebutton_state_t state);
38 static lv_imagebutton_state_t get_state(const lv_obj_t * imagebutton);
39 static void update_src_info(lv_imagebutton_src_info_t * info, const void * src);
40 
41 /**********************
42  *  STATIC VARIABLES
43  **********************/
44 
45 const lv_obj_class_t lv_imagebutton_class = {
46     .base_class = &lv_obj_class,
47     .width_def = LV_SIZE_CONTENT,
48     .height_def = LV_SIZE_CONTENT,
49     .instance_size = sizeof(lv_imagebutton_t),
50     .constructor_cb = lv_imagebutton_constructor,
51     .event_cb = lv_imagebutton_event,
52     .name = "imagebutton",
53 };
54 
55 /**********************
56  *      MACROS
57  **********************/
58 
59 /**********************
60  *   GLOBAL FUNCTIONS
61  **********************/
62 
lv_imagebutton_create(lv_obj_t * parent)63 lv_obj_t * lv_imagebutton_create(lv_obj_t * parent)
64 {
65     LV_LOG_INFO("begin");
66     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
67     lv_obj_class_init_obj(obj);
68     return obj;
69 }
70 
71 /*=====================
72  * Setter functions
73  *====================*/
74 
lv_imagebutton_set_src(lv_obj_t * obj,lv_imagebutton_state_t state,const void * src_left,const void * src_mid,const void * src_right)75 void lv_imagebutton_set_src(lv_obj_t * obj, lv_imagebutton_state_t state, const void * src_left, const void * src_mid,
76                             const void * src_right)
77 {
78     LV_ASSERT_OBJ(obj, MY_CLASS);
79 
80     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
81 
82     if((src_left || src_right) && !src_mid) {
83         LV_LOG_WARN("middle image source is not set while left and/or right image sources are");
84     }
85 
86     update_src_info(&imagebutton->src_left[state], src_left);
87     update_src_info(&imagebutton->src_mid[state], src_mid);
88     update_src_info(&imagebutton->src_right[state], src_right);
89 
90     refr_image(obj);
91 }
92 
lv_imagebutton_set_state(lv_obj_t * obj,lv_imagebutton_state_t state)93 void lv_imagebutton_set_state(lv_obj_t * obj, lv_imagebutton_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_IMAGEBUTTON_STATE_PRESSED ||
99        state == LV_IMAGEBUTTON_STATE_CHECKED_PRESSED) obj_state |= LV_STATE_PRESSED;
100     if(state == LV_IMAGEBUTTON_STATE_DISABLED ||
101        state == LV_IMAGEBUTTON_STATE_CHECKED_DISABLED) obj_state |= LV_STATE_DISABLED;
102     if(state == LV_IMAGEBUTTON_STATE_CHECKED_DISABLED || state == LV_IMAGEBUTTON_STATE_CHECKED_PRESSED ||
103        state == LV_IMAGEBUTTON_STATE_CHECKED_RELEASED) {
104         obj_state |= LV_STATE_CHECKED;
105     }
106 
107     lv_obj_remove_state(obj, LV_STATE_CHECKED | LV_STATE_PRESSED | LV_STATE_DISABLED);
108     lv_obj_add_state(obj, obj_state);
109 
110     refr_image(obj);
111 }
112 
113 /*=====================
114  * Getter functions
115  *====================*/
116 
lv_imagebutton_get_src_left(lv_obj_t * obj,lv_imagebutton_state_t state)117 const void * lv_imagebutton_get_src_left(lv_obj_t * obj, lv_imagebutton_state_t state)
118 {
119     LV_ASSERT_OBJ(obj, MY_CLASS);
120 
121     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
122 
123     return imagebutton->src_left[state].img_src;
124 }
125 
lv_imagebutton_get_src_middle(lv_obj_t * obj,lv_imagebutton_state_t state)126 const void * lv_imagebutton_get_src_middle(lv_obj_t * obj, lv_imagebutton_state_t state)
127 {
128     LV_ASSERT_OBJ(obj, MY_CLASS);
129     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
130 
131     return imagebutton->src_mid[state].img_src;
132 }
133 
lv_imagebutton_get_src_right(lv_obj_t * obj,lv_imagebutton_state_t state)134 const void * lv_imagebutton_get_src_right(lv_obj_t * obj, lv_imagebutton_state_t state)
135 {
136     LV_ASSERT_OBJ(obj, MY_CLASS);
137     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
138 
139     return imagebutton->src_right[state].img_src;
140 }
141 
142 /**********************
143  *   STATIC FUNCTIONS
144  **********************/
145 
lv_imagebutton_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)146 static void lv_imagebutton_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
147 {
148     LV_UNUSED(class_p);
149     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
150     /*Initialize the allocated 'ext'*/
151 
152     lv_memzero(&imagebutton->src_mid, sizeof(imagebutton->src_mid));
153     lv_memzero(&imagebutton->src_left, sizeof(imagebutton->src_left));
154     lv_memzero(&imagebutton->src_right, sizeof(imagebutton->src_right));
155 }
156 
lv_imagebutton_event(const lv_obj_class_t * class_p,lv_event_t * e)157 static void lv_imagebutton_event(const lv_obj_class_t * class_p, lv_event_t * e)
158 {
159     LV_UNUSED(class_p);
160 
161     lv_result_t res = lv_obj_event_base(&lv_imagebutton_class, e);
162     if(res != LV_RESULT_OK) return;
163 
164     lv_event_code_t code = lv_event_get_code(e);
165     lv_obj_t * obj = lv_event_get_current_target(e);
166     if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
167         refr_image(obj);
168     }
169     else if(code == LV_EVENT_DRAW_MAIN) {
170         draw_main(e);
171     }
172     else if(code == LV_EVENT_COVER_CHECK) {
173         lv_cover_check_info_t * info = lv_event_get_param(e);
174         if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
175     }
176     else if(code == LV_EVENT_GET_SELF_SIZE) {
177         lv_point_t * p = lv_event_get_self_size_info(e);
178         lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
179         lv_imagebutton_state_t state  = suggest_state(obj, get_state(obj));
180         if(imagebutton->src_left[state].img_src == NULL &&
181            imagebutton->src_mid[state].img_src != NULL &&
182            imagebutton->src_right[state].img_src == NULL) {
183             p->x = LV_MAX(p->x, imagebutton->src_mid[state].header.w);
184         }
185     }
186 }
187 
draw_main(lv_event_t * e)188 static void draw_main(lv_event_t * e)
189 {
190     lv_obj_t * obj = lv_event_get_current_target(e);
191     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
192     lv_layer_t * layer = lv_event_get_layer(e);
193 
194     /*Just draw_main an image*/
195     lv_imagebutton_state_t state  = suggest_state(obj, get_state(obj));
196 
197     /*Simply draw the middle src if no tiled*/
198     lv_imagebutton_src_info_t * src_info = &imagebutton->src_left[state];
199 
200     int32_t tw = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
201     int32_t th = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
202     lv_area_t coords;
203     lv_area_copy(&coords, &obj->coords);
204     lv_area_increase(&coords, tw, th);
205 
206     lv_draw_image_dsc_t img_dsc;
207     lv_draw_image_dsc_init(&img_dsc);
208     img_dsc.base.layer = layer;
209     lv_obj_init_draw_image_dsc(obj, LV_PART_MAIN, &img_dsc);
210 
211     lv_area_t coords_part;
212     int32_t left_w = 0;
213     int32_t right_w = 0;
214 
215     if(src_info->img_src) {
216         left_w = src_info->header.w;
217         coords_part.x1 = coords.x1;
218         coords_part.y1 = coords.y1;
219         coords_part.x2 = coords.x1 + src_info->header.w - 1;
220         coords_part.y2 = coords.y1 + src_info->header.h - 1;
221         img_dsc.src = src_info->img_src;
222         lv_draw_image(layer, &img_dsc, &coords_part);
223     }
224 
225     src_info = &imagebutton->src_right[state];
226     if(src_info->img_src) {
227         right_w = src_info->header.w;
228         coords_part.x1 = coords.x2 - src_info->header.w + 1;
229         coords_part.y1 = coords.y1;
230         coords_part.x2 = coords.x2;
231         coords_part.y2 = coords.y1 + src_info->header.h - 1;
232         img_dsc.src = src_info->img_src;
233         lv_draw_image(layer, &img_dsc, &coords_part);
234     }
235 
236     src_info = &imagebutton->src_mid[state];
237     if(src_info->img_src) {
238         coords_part.x1 = coords.x1 + left_w;
239         coords_part.x2 = coords.x2 - right_w;
240         coords_part.y1 = coords.y1;
241         coords_part.y2 = coords.y2;
242 
243         lv_area_t clip_area_center;
244         if(lv_area_intersect(&clip_area_center, &coords_part, &layer->_clip_area)) {
245             lv_area_t clip_area_ori = layer->_clip_area;
246             layer->_clip_area = clip_area_center;
247             img_dsc.src = src_info->img_src;
248             img_dsc.tile = 1;
249             lv_draw_image(layer, &img_dsc, &coords_part);
250             layer->_clip_area = clip_area_ori;
251         }
252 
253     }
254 }
255 
refr_image(lv_obj_t * obj)256 static void refr_image(lv_obj_t * obj)
257 {
258     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
259     lv_imagebutton_state_t state  = suggest_state(obj, get_state(obj));
260 
261     const void * src = imagebutton->src_mid[state].img_src;
262     if(src == NULL) return;
263 
264     lv_obj_refresh_self_size(obj);
265     lv_obj_set_height(obj, imagebutton->src_mid[state].header.h); /*Keep the user defined width*/
266 
267     lv_obj_invalidate(obj);
268 }
269 
270 /**
271  * If `src` is not defined for the current state try to get a state which is related to the current but has `src`.
272  * E.g. if the PRESSED src is not set but the RELEASED does, use the RELEASED.
273  * @param imagebutton pointer to an image button
274  * @param state the state to convert
275  * @return the suggested state
276  */
suggest_state(lv_obj_t * obj,lv_imagebutton_state_t state)277 static lv_imagebutton_state_t suggest_state(lv_obj_t * obj, lv_imagebutton_state_t state)
278 {
279     lv_imagebutton_t * imagebutton = (lv_imagebutton_t *)obj;
280     if(imagebutton->src_mid[state].img_src == NULL) {
281         switch(state) {
282             case LV_IMAGEBUTTON_STATE_PRESSED:
283                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_RELEASED;
284                 break;
285             case LV_IMAGEBUTTON_STATE_CHECKED_RELEASED:
286                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_RELEASED;
287                 break;
288             case LV_IMAGEBUTTON_STATE_CHECKED_PRESSED:
289                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_CHECKED_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_CHECKED_RELEASED;
290                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_PRESSED].img_src) return LV_IMAGEBUTTON_STATE_PRESSED;
291                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_RELEASED;
292                 break;
293             case LV_IMAGEBUTTON_STATE_DISABLED:
294                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_RELEASED;
295                 break;
296             case LV_IMAGEBUTTON_STATE_CHECKED_DISABLED:
297                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_CHECKED_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_CHECKED_RELEASED;
298                 if(imagebutton->src_mid[LV_IMAGEBUTTON_STATE_RELEASED].img_src) return LV_IMAGEBUTTON_STATE_RELEASED;
299                 break;
300             default:
301                 break;
302         }
303     }
304 
305     return state;
306 }
307 
get_state(const lv_obj_t * imagebutton)308 static lv_imagebutton_state_t get_state(const lv_obj_t * imagebutton)
309 {
310     LV_ASSERT_OBJ(imagebutton, MY_CLASS);
311 
312     lv_state_t obj_state = lv_obj_get_state(imagebutton);
313 
314     if(obj_state & LV_STATE_DISABLED) {
315         if(obj_state & LV_STATE_CHECKED) return LV_IMAGEBUTTON_STATE_CHECKED_DISABLED;
316         else return LV_IMAGEBUTTON_STATE_DISABLED;
317     }
318 
319     if(obj_state & LV_STATE_CHECKED) {
320         if(obj_state & LV_STATE_PRESSED) return LV_IMAGEBUTTON_STATE_CHECKED_PRESSED;
321         else return LV_IMAGEBUTTON_STATE_CHECKED_RELEASED;
322     }
323     else {
324         if(obj_state & LV_STATE_PRESSED) return LV_IMAGEBUTTON_STATE_PRESSED;
325         else return LV_IMAGEBUTTON_STATE_RELEASED;
326     }
327 }
328 
update_src_info(lv_imagebutton_src_info_t * info,const void * src)329 static void update_src_info(lv_imagebutton_src_info_t * info, const void * src)
330 {
331     if(!src) {
332         lv_memzero(info, sizeof(lv_imagebutton_src_info_t));
333         return;
334     }
335 
336     lv_result_t res = lv_image_decoder_get_info(src, &info->header);
337     if(res != LV_RESULT_OK) {
338         LV_LOG_WARN("can't get info");
339         return;
340     }
341 
342     info->img_src = src;
343 }
344 
345 #endif
346