1 /**
2  * @file lv_btn.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_btn.h"
11 #if LV_USE_BTN != 0
12 
13 #include <string.h>
14 #include "../lv_core/lv_group.h"
15 #include "../lv_draw/lv_draw.h"
16 #include "../lv_themes/lv_theme.h"
17 #include "../lv_misc/lv_area.h"
18 #include "../lv_misc/lv_color.h"
19 #include "../lv_misc/lv_math.h"
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 #define LV_BTN_INK_VALUE_MAX 256
25 #define LV_BTN_INK_VALUE_MAX_SHIFT 8
26 
27 /**********************
28  *      TYPEDEFS
29  **********************/
30 
31 /**********************
32  *  STATIC PROTOTYPES
33  **********************/
34 static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode);
35 static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param);
36 
37 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
38 static void lv_btn_ink_effect_anim(lv_obj_t * btn, lv_anim_value_t val);
39 static void lv_btn_ink_effect_anim_ready(lv_anim_t * a);
40 #endif
41 
42 /**********************
43  *  STATIC VARIABLES
44  **********************/
45 static lv_signal_cb_t ancestor_signal;
46 static lv_design_cb_t ancestor_design;
47 
48 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
49 static lv_coord_t ink_act_value;
50 static lv_obj_t * ink_obj;
51 static lv_btn_state_t ink_bg_state;
52 static lv_btn_state_t ink_top_state;
53 static bool ink_ready;
54 static bool ink_playback;
55 static lv_point_t ink_point;
56 #endif
57 
58 /**********************
59  *      MACROS
60  **********************/
61 
62 /**********************
63  *   GLOBAL FUNCTIONS
64  **********************/
65 
66 /**
67  * Create a button object
68  * @param par pointer to an object, it will be the parent of the new button
69  * @param copy pointer to a button object, if not NULL then the new object will be copied from it
70  * @return pointer to the created button
71  */
lv_btn_create(lv_obj_t * par,const lv_obj_t * copy)72 lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy)
73 {
74     LV_LOG_TRACE("button create started");
75 
76     lv_obj_t * new_btn;
77 
78     new_btn = lv_cont_create(par, copy);
79     lv_mem_assert(new_btn);
80     if(new_btn == NULL) return NULL;
81 
82     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_btn);
83     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_btn);
84 
85     /*Allocate the extended data*/
86     lv_btn_ext_t * ext = lv_obj_allocate_ext_attr(new_btn, sizeof(lv_btn_ext_t));
87     lv_mem_assert(ext);
88     if(ext == NULL) return NULL;
89 
90     ext->state = LV_BTN_STATE_REL;
91 
92     ext->styles[LV_BTN_STATE_REL]     = &lv_style_btn_rel;
93     ext->styles[LV_BTN_STATE_PR]      = &lv_style_btn_pr;
94     ext->styles[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel;
95     ext->styles[LV_BTN_STATE_TGL_PR]  = &lv_style_btn_tgl_pr;
96     ext->styles[LV_BTN_STATE_INA]     = &lv_style_btn_ina;
97 
98     ext->toggle = 0;
99 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
100     ext->ink_in_time   = 0;
101     ext->ink_wait_time = 0;
102     ext->ink_out_time  = 0;
103 #endif
104 
105     lv_obj_set_signal_cb(new_btn, lv_btn_signal);
106     lv_obj_set_design_cb(new_btn, lv_btn_design);
107 
108     /*If no copy do the basic initialization*/
109     if(copy == NULL) {
110         /*Set layout if the button is not a screen*/
111         if(par != NULL) {
112             lv_btn_set_layout(new_btn, LV_LAYOUT_CENTER);
113         }
114 
115         lv_obj_set_click(new_btn, true); /*Be sure the button is clickable*/
116 
117         /*Set the default styles*/
118         lv_theme_t * th = lv_theme_get_current();
119         if(th) {
120             lv_btn_set_style(new_btn, LV_BTN_STYLE_REL, th->style.btn.rel);
121             lv_btn_set_style(new_btn, LV_BTN_STYLE_PR, th->style.btn.pr);
122             lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_REL, th->style.btn.tgl_rel);
123             lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_PR, th->style.btn.tgl_pr);
124             lv_btn_set_style(new_btn, LV_BTN_STYLE_INA, th->style.btn.ina);
125         } else {
126             lv_obj_set_style(new_btn, ext->styles[LV_BTN_STATE_REL]);
127         }
128     }
129     /*Copy 'copy'*/
130     else {
131         lv_btn_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
132         ext->state              = copy_ext->state;
133         ext->toggle             = copy_ext->toggle;
134 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
135         ext->ink_in_time   = copy_ext->ink_in_time;
136         ext->ink_wait_time = copy_ext->ink_wait_time;
137         ext->ink_out_time  = copy_ext->ink_out_time;
138 #endif
139         memcpy(ext->styles, copy_ext->styles, sizeof(ext->styles));
140 
141         /*Refresh the style with new signal function*/
142         lv_obj_refresh_style(new_btn);
143     }
144 
145     LV_LOG_INFO("button created");
146 
147     return new_btn;
148 }
149 
150 /*=====================
151  * Setter functions
152  *====================*/
153 
154 /**
155  * Enable the toggled states
156  * @param btn pointer to a button object
157  * @param tgl true: enable toggled states, false: disable
158  */
lv_btn_set_toggle(lv_obj_t * btn,bool tgl)159 void lv_btn_set_toggle(lv_obj_t * btn, bool tgl)
160 {
161     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
162 
163     ext->toggle = tgl != false ? 1 : 0;
164 }
165 
166 /**
167  * Set the state of the button
168  * @param btn pointer to a button object
169  * @param state the new state of the button (from lv_btn_state_t enum)
170  */
lv_btn_set_state(lv_obj_t * btn,lv_btn_state_t state)171 void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state)
172 {
173     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
174     if(ext->state != state) {
175         ext->state = state;
176         lv_obj_set_style(btn, ext->styles[state]);
177     }
178 }
179 
180 /**
181  * Toggle the state of the button (ON->OFF, OFF->ON)
182  * @param btn pointer to a button object
183  */
lv_btn_toggle(lv_obj_t * btn)184 void lv_btn_toggle(lv_obj_t * btn)
185 {
186     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
187     switch(ext->state) {
188         case LV_BTN_STATE_REL: lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); break;
189         case LV_BTN_STATE_PR: lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR); break;
190         case LV_BTN_STATE_TGL_REL: lv_btn_set_state(btn, LV_BTN_STATE_REL); break;
191         case LV_BTN_STATE_TGL_PR: lv_btn_set_state(btn, LV_BTN_STATE_PR); break;
192         default: break;
193     }
194 }
195 
196 /**
197  * Set time of the ink effect (draw a circle on click to animate in the new state)
198  * @param btn pointer to a button object
199  * @param time the time of the ink animation
200  */
lv_btn_set_ink_in_time(lv_obj_t * btn,uint16_t time)201 void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time)
202 {
203 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
204     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
205     ext->ink_in_time   = time;
206 #else
207     (void)btn;  /*Unused*/
208     (void)time; /*Unused*/
209     LV_LOG_WARN("`lv_btn_set_ink_ink_time` has no effect if LV_BTN_INK_EFEFCT or LV_USE_ANIMATION "
210                 "is disabled")
211 #endif
212 }
213 
214 /**
215  * Set the wait time before the ink disappears
216  * @param btn pointer to a button object
217  * @param time the time of the ink animation
218  */
lv_btn_set_ink_wait_time(lv_obj_t * btn,uint16_t time)219 void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time)
220 {
221 
222 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
223     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
224     ext->ink_wait_time = time;
225 #else
226     (void)btn;  /*Unused*/
227     (void)time; /*Unused*/
228     LV_LOG_WARN("`lv_btn_set_ink_wait_time` has no effect if LV_BTN_INK_EFEFCT or LV_USE_ANIMATION "
229                 "is disabled")
230 #endif
231 }
232 
233 /**
234  * Set time of the ink out effect (animate to the released state)
235  * @param btn pointer to a button object
236  * @param time the time of the ink animation
237  */
lv_btn_set_ink_out_time(lv_obj_t * btn,uint16_t time)238 void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time)
239 {
240 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
241     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
242     ext->ink_out_time  = time;
243 #else
244     (void)btn;  /*Unused*/
245     (void)time; /*Unused*/
246     LV_LOG_WARN("`lv_btn_set_ink_out_time` has no effect if LV_BTN_INK_EFEFCT or LV_USE_ANIMATION "
247                 "is disabled")
248 #endif
249 }
250 
251 /**
252  * Set a style of a button
253  * @param btn pointer to a button object
254  * @param type which style should be set
255  * @param style pointer to a style
256  */
lv_btn_set_style(lv_obj_t * btn,lv_btn_style_t type,const lv_style_t * style)257 void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, const lv_style_t * style)
258 {
259     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
260 
261     switch(type) {
262         case LV_BTN_STYLE_REL: ext->styles[LV_BTN_STATE_REL] = style; break;
263         case LV_BTN_STYLE_PR: ext->styles[LV_BTN_STATE_PR] = style; break;
264         case LV_BTN_STYLE_TGL_REL: ext->styles[LV_BTN_STATE_TGL_REL] = style; break;
265         case LV_BTN_STYLE_TGL_PR: ext->styles[LV_BTN_STATE_TGL_PR] = style; break;
266         case LV_BTN_STYLE_INA: ext->styles[LV_BTN_STATE_INA] = style; break;
267     }
268 
269     /*Refresh the object with the new style*/
270     lv_obj_set_style(btn, ext->styles[ext->state]);
271 }
272 
273 /*=====================
274  * Getter functions
275  *====================*/
276 
277 /**
278  * Get the current state of the button
279  * @param btn pointer to a button object
280  * @return the state of the button (from lv_btn_state_t enum)
281  */
lv_btn_get_state(const lv_obj_t * btn)282 lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn)
283 {
284     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
285     return ext->state;
286 }
287 
288 /**
289  * Get the toggle enable attribute of the button
290  * @param btn pointer to a button object
291  * @return true: toggle enabled, false: disabled
292  */
lv_btn_get_toggle(const lv_obj_t * btn)293 bool lv_btn_get_toggle(const lv_obj_t * btn)
294 {
295     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
296 
297     return ext->toggle != 0 ? true : false;
298 }
299 
300 /**
301  * Get time of the ink in effect (draw a circle on click to animate in the new state)
302  * @param btn pointer to a button object
303  * @return the time of the ink animation
304  */
lv_btn_get_ink_in_time(const lv_obj_t * btn)305 uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn)
306 {
307 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
308     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
309     return ext->ink_in_time;
310 #else
311     (void)btn; /*Unused*/
312     return 0;
313 #endif
314 }
315 
316 /**
317  * Get the wait time before the ink disappears
318  * @param btn pointer to a button object
319  * @return the time of the ink animation
320  */
lv_btn_get_ink_wait_time(const lv_obj_t * btn)321 uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn)
322 {
323 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
324     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
325     return ext->ink_wait_time;
326 #else
327     (void)btn; /*Unused*/
328     return 0;
329 #endif
330 }
331 /**
332  * Get time of the ink out effect (animate to the releases state)
333  * @param btn pointer to a button object
334  * @return the time of the ink animation
335  */
lv_btn_get_ink_out_time(const lv_obj_t * btn)336 uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn)
337 {
338 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
339     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
340     return ext->ink_in_time;
341 #else
342     (void)btn; /*Unused*/
343     return 0;
344 #endif
345 }
346 
347 /**
348  * Get a style of a button
349  * @param btn pointer to a button object
350  * @param type which style should be get
351  * @return style pointer to a style
352  */
lv_btn_get_style(const lv_obj_t * btn,lv_btn_style_t type)353 const lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type)
354 {
355     const lv_style_t * style = NULL;
356     lv_btn_ext_t * ext       = lv_obj_get_ext_attr(btn);
357     lv_btn_state_t state     = lv_btn_get_state(btn);
358 
359     /* If the style of the current state is asked then return object style.
360      * If the button is focused then this style is updated by the group's
361      * `style_mod_cb` function */
362     if((type == LV_BTN_STYLE_REL && state == LV_BTN_STATE_REL) ||
363        (type == LV_BTN_STYLE_PR && state == LV_BTN_STATE_PR) ||
364        (type == LV_BTN_STYLE_TGL_REL && state == LV_BTN_STATE_TGL_REL) ||
365        (type == LV_BTN_STYLE_TGL_PR && state == LV_BTN_STATE_TGL_PR) ||
366        (type == LV_BTN_STYLE_INA && state == LV_BTN_STATE_INA)) {
367 
368         style = lv_obj_get_style(btn);
369     } else {
370         switch(type) {
371             case LV_BTN_STYLE_REL: style = ext->styles[LV_BTN_STATE_REL]; break;
372             case LV_BTN_STYLE_PR: style = ext->styles[LV_BTN_STATE_PR]; break;
373             case LV_BTN_STYLE_TGL_REL: style = ext->styles[LV_BTN_STATE_TGL_REL]; break;
374             case LV_BTN_STYLE_TGL_PR: style = ext->styles[LV_BTN_STATE_TGL_PR]; break;
375             case LV_BTN_STYLE_INA: style = ext->styles[LV_BTN_STATE_INA]; break;
376             default: style = NULL; break;
377         }
378     }
379 
380     return style;
381 }
382 
383 /**********************
384  *   STATIC FUNCTIONS
385  **********************/
386 
387 /**
388  * Handle the drawing related tasks of the drop down lists
389  * @param btn pointer to an object
390  * @param mask the object will be drawn only in this area
391  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
392  *                                  (return 'true' if yes)
393  *             LV_DESIGN_DRAW: draw the object (always return 'true')
394  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
395  * @param return true/false, depends on 'mode'
396  */
lv_btn_design(lv_obj_t * btn,const lv_area_t * mask,lv_design_mode_t mode)397 static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode)
398 {
399     if(mode == LV_DESIGN_COVER_CHK) {
400         return false;
401     } else if(mode == LV_DESIGN_DRAW_MAIN) {
402 
403 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
404         if(btn != ink_obj) {
405             ancestor_design(btn, mask, mode);
406         } else {
407             lv_opa_t opa_scale = lv_obj_get_opa_scale(btn);
408             lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
409 
410             /*Draw the normal button*/
411             if(ink_playback == false) {
412                 lv_style_t style_tmp;
413                 lv_style_copy(&style_tmp, ext->styles[ink_bg_state]);
414                 style_tmp.body.shadow.width = ext->styles[ink_top_state]->body.shadow.width;
415                 lv_draw_rect(&btn->coords, mask, &style_tmp, opa_scale);
416 
417                 lv_coord_t w     = lv_obj_get_width(btn);
418                 lv_coord_t h     = lv_obj_get_height(btn);
419                 lv_coord_t r_max = LV_MATH_MIN(w, h) / 2;
420 
421                 /*In the first part of the animation increase the size of the circle (ink effect) */
422                 lv_area_t cir_area;
423 
424                 lv_coord_t coord_state =
425                     ink_act_value < LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value : LV_BTN_INK_VALUE_MAX / 2;
426                 lv_point_t p_act;
427                 p_act.x          = ink_point.x;
428                 p_act.y          = ink_point.y;
429                 lv_coord_t x_err = (btn->coords.x1 + w / 2) - p_act.x;
430                 lv_coord_t y_err = (btn->coords.y1 + h / 2) - p_act.y;
431 
432                 p_act.x += (x_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1);
433                 p_act.y += (y_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1);
434 
435                 lv_coord_t half_side = LV_MATH_MAX(w, h) / 2;
436                 cir_area.x1          = p_act.x - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
437                 cir_area.y1          = p_act.y - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
438                 cir_area.x2          = p_act.x + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
439                 cir_area.y2          = p_act.y + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
440 
441                 lv_area_intersect(&cir_area, &btn->coords,
442                                   &cir_area); /*Limit the area. (It might be too big on the smaller side)*/
443 
444                 /*In the second part animate the radius. Circle -> body.radius*/
445                 lv_coord_t r_state =
446                     ink_act_value > LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value - LV_BTN_INK_VALUE_MAX / 2 : 0;
447 
448                 lv_style_copy(&style_tmp, ext->styles[ink_top_state]);
449                 style_tmp.body.radius       = r_max + (((ext->styles[ink_bg_state]->body.radius - r_max) * r_state) >>
450                                                  (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
451                 style_tmp.body.border.width = 0;
452 
453                 /*Draw the circle*/
454                 lv_draw_rect(&cir_area, mask, &style_tmp, opa_scale);
455             } else {
456                 lv_style_t res;
457                 lv_style_copy(&res, ext->styles[ink_bg_state]);
458                 lv_style_mix(ext->styles[ink_bg_state], ext->styles[ink_top_state], &res, ink_act_value);
459                 lv_draw_rect(&btn->coords, mask, &res, opa_scale);
460             }
461         }
462 #else
463         ancestor_design(btn, mask, mode);
464 #endif
465     } else if(mode == LV_DESIGN_DRAW_POST) {
466         ancestor_design(btn, mask, mode);
467     }
468 
469     return true;
470 }
471 
472 /**
473  * Signal function of the button
474  * @param btn pointer to a button object
475  * @param sign a signal type from lv_signal_t enum
476  * @param param pointer to a signal specific variable
477  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
478  */
lv_btn_signal(lv_obj_t * btn,lv_signal_t sign,void * param)479 static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param)
480 {
481     lv_res_t res;
482 
483     /* Include the ancient signal function */
484     res = ancestor_signal(btn, sign, param);
485     if(res != LV_RES_OK) return res;
486 
487     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
488     bool tgl           = lv_btn_get_toggle(btn);
489 
490     if(sign == LV_SIGNAL_PRESSED) {
491         /*Refresh the state*/
492         if(ext->state == LV_BTN_STATE_REL) {
493             lv_btn_set_state(btn, LV_BTN_STATE_PR);
494 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
495             ink_bg_state  = LV_BTN_STATE_REL;
496             ink_top_state = LV_BTN_STATE_PR;
497 #endif
498         } else if(ext->state == LV_BTN_STATE_TGL_REL) {
499             lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
500 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
501             ink_bg_state  = LV_BTN_STATE_TGL_REL;
502             ink_top_state = LV_BTN_STATE_TGL_PR;
503 #endif
504         }
505 
506 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
507         /*Forget the old inked button*/
508         if(ink_obj != NULL && ink_obj != btn) {
509             lv_anim_del(ink_obj, (lv_anim_exec_xcb_t)lv_btn_ink_effect_anim);
510             lv_obj_invalidate(ink_obj);
511             ink_obj = NULL;
512         }
513         /*Save the new data for inking and start it's animation if enabled*/
514         if(ext->ink_in_time > 0) {
515             ink_obj      = btn;
516             ink_playback = false;
517             ink_ready    = false;
518             lv_indev_get_point(lv_indev_get_act(), &ink_point);
519 
520             lv_anim_t a;
521             a.var            = btn;
522             a.start          = 0;
523             a.end            = LV_BTN_INK_VALUE_MAX;
524             a.exec_cb        = (lv_anim_exec_xcb_t)lv_btn_ink_effect_anim;
525             a.path_cb        = lv_anim_path_linear;
526             a.ready_cb       = lv_btn_ink_effect_anim_ready;
527             a.act_time       = 0;
528             a.time           = ext->ink_in_time;
529             a.playback       = 0;
530             a.playback_pause = 0;
531             a.repeat         = 0;
532             a.repeat_pause   = 0;
533             lv_anim_create(&a);
534         }
535 #endif
536     } else if(sign == LV_SIGNAL_PRESS_LOST) {
537         /*Refresh the state*/
538         if(ext->state == LV_BTN_STATE_PR)
539             lv_btn_set_state(btn, LV_BTN_STATE_REL);
540         else if(ext->state == LV_BTN_STATE_TGL_PR)
541             lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
542     } else if(sign == LV_SIGNAL_PRESSING) {
543         /*When the button begins to drag revert pressed states to released*/
544         if(lv_indev_is_dragging(param) != false) {
545             if(ext->state == LV_BTN_STATE_PR)
546                 lv_btn_set_state(btn, LV_BTN_STATE_REL);
547             else if(ext->state == LV_BTN_STATE_TGL_PR)
548                 lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
549         }
550     } else if(sign == LV_SIGNAL_RELEASED) {
551         /*If not dragged and it was not long press action then
552          *change state and run the action*/
553         if(lv_indev_is_dragging(param) == false) {
554             uint32_t toggled = 0;
555             if(ext->state == LV_BTN_STATE_PR && tgl == false) {
556                 lv_btn_set_state(btn, LV_BTN_STATE_REL);
557                 toggled = 0;
558             } else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == false) {
559                 lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
560                 toggled = 1;
561             } else if(ext->state == LV_BTN_STATE_PR && tgl == true) {
562                 lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
563                 toggled = 1;
564             } else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == true) {
565                 lv_btn_set_state(btn, LV_BTN_STATE_REL);
566                 toggled = 0;
567             }
568 
569             if(tgl) {
570                 res = lv_event_send(btn, LV_EVENT_VALUE_CHANGED, &toggled);
571                 if(res != LV_RES_OK) return res;
572             }
573 
574         } else { /*If dragged change back the state*/
575             if(ext->state == LV_BTN_STATE_PR) {
576                 lv_btn_set_state(btn, LV_BTN_STATE_REL);
577             } else if(ext->state == LV_BTN_STATE_TGL_PR) {
578                 lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
579             }
580         }
581 
582 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
583         /*Draw the toggled state in the inking instead*/
584         if(ext->toggle) {
585             ink_top_state = ext->state;
586         }
587         /*If not a toggle button and the "IN" inking is ready then start an "OUT" inking*/
588         else if(ink_ready && ext->ink_out_time > 0) {
589             ink_obj      = btn;
590             ink_playback = true; /*It is the playback. If not set `lv_btn_ink_effect_anim_ready`
591                                     will start its own playback*/
592             lv_indev_get_point(lv_indev_get_act(), &ink_point);
593 
594             lv_anim_t a;
595             a.var            = ink_obj;
596             a.start          = LV_BTN_INK_VALUE_MAX;
597             a.end            = 0;
598             a.exec_cb        = (lv_anim_exec_xcb_t)lv_btn_ink_effect_anim;
599             a.path_cb        = lv_anim_path_linear;
600             a.ready_cb       = lv_btn_ink_effect_anim_ready;
601             a.act_time       = 0;
602             a.time           = ext->ink_out_time;
603             a.playback       = 0;
604             a.playback_pause = 0;
605             a.repeat         = 0;
606             a.repeat_pause   = 0;
607             lv_anim_create(&a);
608         }
609 #endif
610     } else if(sign == LV_SIGNAL_CONTROL) {
611         char c = *((char *)param);
612         if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
613             if(lv_btn_get_toggle(btn)) {
614                 lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
615 
616                 uint32_t state = 1;
617                 res            = lv_event_send(btn, LV_EVENT_VALUE_CHANGED, &state);
618                 if(res != LV_RES_OK) return res;
619             }
620 
621         } else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
622             if(lv_btn_get_toggle(btn)) {
623                 lv_btn_set_state(btn, LV_BTN_STATE_REL);
624 
625                 uint32_t state = 0;
626                 res            = lv_event_send(btn, LV_EVENT_VALUE_CHANGED, &state);
627                 if(res != LV_RES_OK) return res;
628             }
629         }
630     } else if(sign == LV_SIGNAL_CLEANUP) {
631 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
632         if(btn == ink_obj) {
633             lv_anim_del(ink_obj, (lv_anim_exec_xcb_t)lv_btn_ink_effect_anim);
634             ink_obj = NULL;
635         }
636 #endif
637     } else if(sign == LV_SIGNAL_GET_TYPE) {
638         lv_obj_type_t * buf = param;
639         uint8_t i;
640         for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
641             if(buf->type[i] == NULL) break;
642         }
643         buf->type[i] = "lv_btn";
644     }
645 
646     return res;
647 }
648 
649 #if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
650 
651 /**
652  * The animator function of inking. CAlled to increase the radius of ink
653  * @param btn pointer to the animated button
654  * @param val the new radius
655  */
lv_btn_ink_effect_anim(lv_obj_t * btn,lv_anim_value_t val)656 static void lv_btn_ink_effect_anim(lv_obj_t * btn, lv_anim_value_t val)
657 {
658     if(btn) {
659         ink_act_value = val;
660         lv_obj_invalidate(btn);
661     }
662 }
663 
664 /**
665  * Called to clean up when the ink animation is ready
666  * @param a unused
667  */
lv_btn_ink_effect_anim_ready(lv_anim_t * a)668 static void lv_btn_ink_effect_anim_ready(lv_anim_t * a)
669 {
670     (void)a; /*Unused*/
671 
672     lv_btn_ext_t * ext   = lv_obj_get_ext_attr(ink_obj);
673     lv_btn_state_t state = lv_btn_get_state(ink_obj);
674 
675     lv_obj_invalidate(ink_obj);
676     ink_ready = true;
677 
678     if((state == LV_BTN_STATE_REL || state == LV_BTN_STATE_TGL_REL) && ext->toggle == 0 && ink_playback == false) {
679         lv_anim_t new_a;
680         new_a.var            = ink_obj;
681         new_a.start          = LV_BTN_INK_VALUE_MAX;
682         new_a.end            = 0;
683         new_a.exec_cb        = (lv_anim_exec_xcb_t)lv_btn_ink_effect_anim;
684         new_a.path_cb        = lv_anim_path_linear;
685         new_a.ready_cb       = lv_btn_ink_effect_anim_ready;
686         new_a.act_time       = -ext->ink_wait_time;
687         new_a.time           = ext->ink_out_time;
688         new_a.playback       = 0;
689         new_a.playback_pause = 0;
690         new_a.repeat         = 0;
691         new_a.repeat_pause   = 0;
692         lv_anim_create(&new_a);
693 
694         ink_playback = true;
695     } else {
696         ink_obj = NULL;
697     }
698 }
699 #endif /*LV_USE_ANIMATION*/
700 
701 #endif
702