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