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_misc/lv_debug.h"
16 #include "../lv_draw/lv_draw.h"
17 #include "../lv_themes/lv_theme.h"
18 #include "../lv_misc/lv_area.h"
19 #include "../lv_misc/lv_color.h"
20 #include "../lv_misc/lv_math.h"
21 
22 /*********************
23  *      DEFINES
24  *********************/
25 #define LV_OBJX_NAME "lv_btn"
26 #define LV_BTN_INK_VALUE_MAX 256
27 #define LV_BTN_INK_VALUE_MAX_SHIFT 8
28 
29 /**********************
30  *      TYPEDEFS
31  **********************/
32 
33 /**********************
34  *  STATIC PROTOTYPES
35  **********************/
36 static lv_design_res_t lv_btn_design(lv_obj_t * btn, const lv_area_t * clip_area, lv_design_mode_t mode);
37 static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param);
38 
39 /**********************
40  *  STATIC VARIABLES
41  **********************/
42 static lv_signal_cb_t ancestor_signal;
43 static lv_design_cb_t ancestor_design;
44 
45 /**********************
46  *      MACROS
47  **********************/
48 
49 /**********************
50  *   GLOBAL FUNCTIONS
51  **********************/
52 
53 /**
54  * Create a button object
55  * @param par pointer to an object, it will be the parent of the new button
56  * @param copy pointer to a button object, if not NULL then the new object will be copied from it
57  * @return pointer to the created button
58  */
lv_btn_create(lv_obj_t * par,const lv_obj_t * copy)59 lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy)
60 {
61     LV_LOG_TRACE("button create started");
62 
63     lv_obj_t * btn;
64 
65     btn = lv_cont_create(par, copy);
66     LV_ASSERT_MEM(btn);
67     if(btn == NULL) return NULL;
68 
69     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(btn);
70     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(btn);
71 
72     /*Allocate the extended data*/
73     lv_btn_ext_t * ext = lv_obj_allocate_ext_attr(btn, sizeof(lv_btn_ext_t));
74     LV_ASSERT_MEM(ext);
75     if(ext == NULL) {
76         lv_obj_del(btn);
77         return NULL;
78     }
79 
80     ext->checkable = 0;
81 
82     lv_obj_set_signal_cb(btn, lv_btn_signal);
83     lv_obj_set_design_cb(btn, lv_btn_design);
84 
85     /*If no copy do the basic initialization*/
86     if(copy == NULL) {
87         /*Set layout if the button is not a screen*/
88         if(par) {
89             lv_obj_set_size(btn, LV_DPI, LV_DPI / 3);
90             lv_btn_set_layout(btn, LV_LAYOUT_CENTER);
91         }
92 
93         lv_obj_set_click(btn, true); /*Be sure the button is clickable*/
94 
95         lv_theme_apply(btn, LV_THEME_BTN);
96     }
97     /*Copy 'copy'*/
98     else {
99         lv_btn_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
100         ext->checkable             = copy_ext->checkable;
101 
102         /*Refresh the style with new signal function*/
103         lv_obj_refresh_style(btn, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
104     }
105 
106     LV_LOG_INFO("button created");
107 
108     return btn;
109 }
110 
111 /*=====================
112  * Setter functions
113  *====================*/
114 
115 /**
116  * Enable the toggled states
117  * @param btn pointer to a button object
118  * @param tgl true: enable toggled states, false: disable
119  */
lv_btn_set_checkable(lv_obj_t * btn,bool tgl)120 void lv_btn_set_checkable(lv_obj_t * btn, bool tgl)
121 {
122     LV_ASSERT_OBJ(btn, LV_OBJX_NAME);
123 
124     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
125 
126     ext->checkable = tgl != false ? 1 : 0;
127 }
128 
129 /**
130  * Set the state of the button
131  * @param btn pointer to a button object
132  * @param state the new state of the button (from lv_btn_state_t enum)
133  */
lv_btn_set_state(lv_obj_t * btn,lv_btn_state_t state)134 void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state)
135 {
136     LV_ASSERT_OBJ(btn, LV_OBJX_NAME);
137 
138     switch(state) {
139         case LV_BTN_STATE_RELEASED:
140             lv_obj_clear_state(btn, LV_STATE_PRESSED | LV_STATE_CHECKED | LV_STATE_DISABLED);
141             break;
142         case LV_BTN_STATE_PRESSED:
143             lv_obj_clear_state(btn, LV_STATE_CHECKED | LV_STATE_DISABLED);
144             lv_obj_add_state(btn, LV_STATE_PRESSED);
145             break;
146         case LV_BTN_STATE_CHECKED_RELEASED:
147             lv_obj_add_state(btn, LV_STATE_CHECKED);
148             lv_obj_clear_state(btn, LV_STATE_PRESSED | LV_STATE_DISABLED);
149             break;
150         case LV_BTN_STATE_CHECKED_PRESSED:
151             lv_obj_add_state(btn, LV_STATE_PRESSED | LV_STATE_CHECKED);
152             lv_obj_clear_state(btn, LV_STATE_DISABLED);
153             break;
154         case LV_BTN_STATE_DISABLED:
155             lv_obj_clear_state(btn, LV_STATE_PRESSED | LV_STATE_CHECKED);
156             lv_obj_add_state(btn, LV_STATE_DISABLED);
157             break;
158         case LV_BTN_STATE_CHECKED_DISABLED:
159             lv_obj_clear_state(btn, LV_STATE_PRESSED);
160             lv_obj_add_state(btn, LV_STATE_DISABLED | LV_STATE_CHECKED);
161             break;
162     }
163 }
164 
165 /**
166  * Toggle the state of the button (ON->OFF, OFF->ON)
167  * @param btn pointer to a button object
168  */
lv_btn_toggle(lv_obj_t * btn)169 void lv_btn_toggle(lv_obj_t * btn)
170 {
171     LV_ASSERT_OBJ(btn, LV_OBJX_NAME);
172 
173     if(lv_obj_get_state(btn, LV_BTN_PART_MAIN) & LV_STATE_CHECKED) {
174         lv_obj_clear_state(btn, LV_STATE_CHECKED);
175     }
176     else {
177         lv_obj_add_state(btn, LV_STATE_CHECKED);
178     }
179 }
180 
181 /*=====================
182  * Getter functions
183  *====================*/
184 
185 /**
186  * Get the current state of the button
187  * @param btn pointer to a button object
188  * @return the state of the button (from lv_btn_state_t enum).
189  * If the button is in disabled state `LV_BTN_STATE_DISABLED` will be ORed to the other button states.
190  */
lv_btn_get_state(const lv_obj_t * btn)191 lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn)
192 {
193     LV_ASSERT_OBJ(btn, LV_OBJX_NAME);
194 
195     lv_state_t obj_state = lv_obj_get_state(btn, LV_BTN_PART_MAIN);
196 
197     if(obj_state & LV_STATE_DISABLED) {
198         if(obj_state & LV_STATE_CHECKED) return LV_BTN_STATE_CHECKED_DISABLED;
199         else return LV_BTN_STATE_DISABLED;
200     }
201 
202     if(obj_state & LV_STATE_CHECKED) {
203         if(obj_state & LV_STATE_PRESSED) return LV_BTN_STATE_CHECKED_PRESSED;
204         else return LV_BTN_STATE_CHECKED_RELEASED;
205     }
206     else {
207         if(obj_state & LV_STATE_PRESSED) return LV_BTN_STATE_PRESSED;
208         else return LV_BTN_STATE_RELEASED;
209     }
210 }
211 
212 /**
213  * Get the toggle enable attribute of the button
214  * @param btn pointer to a button object
215  * @return true: toggle enabled, false: disabled
216  */
lv_btn_get_checkable(const lv_obj_t * btn)217 bool lv_btn_get_checkable(const lv_obj_t * btn)
218 {
219     LV_ASSERT_OBJ(btn, LV_OBJX_NAME);
220 
221     lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
222 
223     return ext->checkable != 0 ? true : false;
224 }
225 
226 /**********************
227  *   STATIC FUNCTIONS
228  **********************/
229 
230 /**
231  * Handle the drawing related tasks of the drop down lists
232  * @param btn pointer to an object
233  * @param mask the object will be drawn only in this area
234  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
235  *                                  (return 'true' if yes)
236  *             LV_DESIGN_DRAW: draw the object (always return 'true')
237  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
238  * @param return an element of `lv_design_res_t`
239  */
lv_btn_design(lv_obj_t * btn,const lv_area_t * clip_area,lv_design_mode_t mode)240 static lv_design_res_t lv_btn_design(lv_obj_t * btn, const lv_area_t * clip_area, lv_design_mode_t mode)
241 {
242     if(mode == LV_DESIGN_COVER_CHK) {
243         return ancestor_design(btn, clip_area, mode);
244     }
245     else if(mode == LV_DESIGN_DRAW_MAIN) {
246         ancestor_design(btn, clip_area, mode);
247     }
248     else if(mode == LV_DESIGN_DRAW_POST) {
249         ancestor_design(btn, clip_area, mode);
250     }
251 
252     return LV_DESIGN_RES_OK;
253 }
254 
255 /**
256  * Signal function of the button
257  * @param btn pointer to a button object
258  * @param sign a signal type from lv_signal_t enum
259  * @param param pointer to a signal specific variable
260  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
261  */
lv_btn_signal(lv_obj_t * btn,lv_signal_t sign,void * param)262 static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param)
263 {
264     lv_res_t res;
265 
266     /* Include the ancient signal function */
267     res = ancestor_signal(btn, sign, param);
268     if(res != LV_RES_OK) return res;
269     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
270 
271     bool tgl           = lv_btn_get_checkable(btn);
272 
273     if(sign == LV_SIGNAL_RELEASED) {
274         /*If not dragged and it was not long press action then
275          *change state and run the action*/
276         if(lv_indev_is_dragging(param) == false && tgl) {
277             uint32_t toggled = 0;
278             if(lv_obj_get_state(btn, LV_BTN_PART_MAIN) & LV_STATE_CHECKED) {
279                 lv_btn_set_state(btn, LV_BTN_STATE_RELEASED);
280                 toggled = 0;
281             }
282             else {
283                 lv_btn_set_state(btn, LV_BTN_STATE_CHECKED_RELEASED);
284                 toggled = 1;
285             }
286 
287             res = lv_event_send(btn, LV_EVENT_VALUE_CHANGED, &toggled);
288             if(res != LV_RES_OK) return res;
289         }
290     }
291     else if(sign == LV_SIGNAL_CONTROL) {
292 #if LV_USE_GROUP
293         char c = *((char *)param);
294         if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
295             if(lv_btn_get_checkable(btn)) {
296                 lv_btn_set_state(btn, LV_BTN_STATE_CHECKED_RELEASED);
297 
298                 uint32_t state = 1;
299                 res            = lv_event_send(btn, LV_EVENT_VALUE_CHANGED, &state);
300                 if(res != LV_RES_OK) return res;
301             }
302 
303         }
304         else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
305             if(lv_btn_get_checkable(btn)) {
306                 lv_btn_set_state(btn, LV_BTN_STATE_RELEASED);
307 
308                 uint32_t state = 0;
309                 res            = lv_event_send(btn, LV_EVENT_VALUE_CHANGED, &state);
310                 if(res != LV_RES_OK) return res;
311             }
312         }
313 #endif
314     }
315 
316     return res;
317 }
318 
319 #endif
320