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