1 /**
2 * @file lv_sw.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_switch.h"
10
11 #if LV_USE_SWITCH != 0
12
13 /*Testing of dependencies*/
14 #if LV_USE_SLIDER == 0
15 #error "lv_sw: lv_slider is required. Enable it in lv_conf.h (LV_USE_SLIDER 1) "
16 #endif
17
18 #include "../lv_misc/lv_debug.h"
19 #include "../lv_themes/lv_theme.h"
20 #include "../lv_misc/lv_math.h"
21 #include "../lv_core/lv_indev.h"
22 #include "../lv_core/lv_disp.h"
23 #include "lv_img.h"
24
25 /*********************
26 * DEFINES
27 *********************/
28 #define LV_OBJX_NAME "lv_switch"
29
30 /**********************
31 * TYPEDEFS
32 **********************/
33
34 /**********************
35 * STATIC PROTOTYPES
36 **********************/
37 static lv_res_t lv_switch_signal(lv_obj_t * sw, lv_signal_t sign, void * param);
38 static lv_design_res_t lv_switch_design(lv_obj_t * sw, const lv_area_t * clip_area, lv_design_mode_t mode);
39 static lv_style_list_t * lv_switch_get_style(lv_obj_t * sw, uint8_t part);
40 static lv_style_list_t * lv_switch_get_style(lv_obj_t * sw, uint8_t part);
41
42 /**********************
43 * STATIC VARIABLES
44 **********************/
45 static lv_signal_cb_t ancestor_signal;
46 static lv_design_cb_t ancestor_design;
47
48 /**********************
49 * MACROS
50 **********************/
51
52 /**********************
53 * GLOBAL FUNCTIONS
54 **********************/
55
56 /**
57 * Create a switch objects
58 * @param par pointer to an object, it will be the parent of the new switch
59 * @param copy pointer to a switch object, if not NULL then the new object will be copied from it
60 * @return pointer to the created switch
61 */
lv_switch_create(lv_obj_t * par,const lv_obj_t * copy)62 lv_obj_t * lv_switch_create(lv_obj_t * par, const lv_obj_t * copy)
63 {
64 LV_LOG_TRACE("switch create started");
65
66 /*Create the ancestor of switch*/
67 lv_obj_t * sw = lv_bar_create(par, copy);
68 LV_ASSERT_MEM(sw);
69
70 if(sw == NULL) return NULL;
71
72 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(sw);
73 if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(sw);
74
75 /*Allocate the switch type specific extended data*/
76 lv_switch_ext_t * ext = lv_obj_allocate_ext_attr(sw, sizeof(lv_switch_ext_t));
77 LV_ASSERT_MEM(ext);
78 if(ext == NULL) {
79 lv_obj_del(sw);
80 return NULL;
81 }
82
83 lv_style_list_init(&ext->style_knob);
84
85 /*The signal and design functions are not copied so set them here*/
86 lv_obj_set_signal_cb(sw, lv_switch_signal);
87 lv_obj_set_design_cb(sw, lv_switch_design);
88
89 /*Init the new switch switch*/
90 if(copy == NULL) {
91 lv_obj_set_click(sw, true);
92 lv_obj_add_protect(sw, LV_PROTECT_PRESS_LOST);
93 lv_obj_set_size(sw, LV_DPX(60), LV_DPX(35));
94 lv_bar_set_range(sw, 0, 1);
95
96 lv_theme_apply(sw, LV_THEME_SWITCH);
97 }
98 /*Copy an existing switch*/
99 else {
100 lv_switch_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
101
102 lv_style_list_copy(&ext->style_knob, ©_ext->style_knob);
103 lv_obj_refresh_style(sw, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
104 }
105
106 /*Refresh the style with new signal function*/
107
108 LV_LOG_INFO("switch created");
109
110 return sw;
111 }
112
113 /*=====================
114 * Setter functions
115 *====================*/
116
117 /**
118 * Turn ON the switch
119 * @param sw pointer to a switch object
120 * @param anim LV_ANOM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
121 */
lv_switch_on(lv_obj_t * sw,lv_anim_enable_t anim)122 void lv_switch_on(lv_obj_t * sw, lv_anim_enable_t anim)
123 {
124 LV_ASSERT_OBJ(sw, LV_OBJX_NAME);
125
126 #if LV_USE_ANIMATION == 0
127 anim = LV_ANIM_OFF;
128 #endif
129 if(lv_bar_get_value(sw) == 1)
130 return;
131 lv_bar_set_value(sw, 1, anim);
132 lv_obj_add_state(sw, LV_STATE_CHECKED);
133 }
134
135 /**
136 * Turn OFF the switch
137 * @param sw pointer to a switch object
138 * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
139 */
lv_switch_off(lv_obj_t * sw,lv_anim_enable_t anim)140 void lv_switch_off(lv_obj_t * sw, lv_anim_enable_t anim)
141 {
142 LV_ASSERT_OBJ(sw, LV_OBJX_NAME);
143
144 #if LV_USE_ANIMATION == 0
145 anim = LV_ANIM_OFF;
146 #endif
147 if(lv_bar_get_value(sw) == 0)
148 return;
149 lv_bar_set_value(sw, 0, anim);
150 lv_obj_clear_state(sw, LV_STATE_CHECKED);
151 }
152
153 /**
154 * Toggle the position of the switch
155 * @param sw pointer to a switch object
156 * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
157 * @return resulting state of the switch.
158 */
lv_switch_toggle(lv_obj_t * sw,lv_anim_enable_t anim)159 bool lv_switch_toggle(lv_obj_t * sw, lv_anim_enable_t anim)
160 {
161 LV_ASSERT_OBJ(sw, LV_OBJX_NAME);
162
163 #if LV_USE_ANIMATION == 0
164 anim = LV_ANIM_OFF;
165 #endif
166
167 bool state = lv_switch_get_state(sw);
168 if(state)
169 lv_switch_off(sw, anim);
170 else
171 lv_switch_on(sw, anim);
172
173 return !state;
174 }
175
176 /*=====================
177 * Getter functions
178 *====================*/
179
180 /**********************
181 * STATIC FUNCTIONS
182 **********************/
183
184 /**
185 * Handle the drawing related tasks of the sliders
186 * @param slider pointer to an object
187 * @param clip_area the object will be drawn only in this area
188 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
189 * (return 'true' if yes)
190 * LV_DESIGN_DRAW: draw the object (always return 'true')
191 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
192 * @param return an element of `lv_design_res_t`
193 */
lv_switch_design(lv_obj_t * sw,const lv_area_t * clip_area,lv_design_mode_t mode)194 static lv_design_res_t lv_switch_design(lv_obj_t * sw, const lv_area_t * clip_area, lv_design_mode_t mode)
195 {
196 /*Return false if the object is not covers the mask_p area*/
197 if(mode == LV_DESIGN_COVER_CHK) {
198 return LV_DESIGN_RES_NOT_COVER;
199 }
200 /*Draw the object*/
201 else if(mode == LV_DESIGN_DRAW_MAIN) {
202 lv_bidi_dir_t base_dir = lv_obj_get_base_dir(sw);
203
204 /*The ancestor design function will draw the background and the indicator.
205 * It also sets ext->bar.indic_area*/
206 ancestor_design(sw, clip_area, mode);
207
208 lv_switch_ext_t * ext = lv_obj_get_ext_attr(sw);
209
210 lv_coord_t objw = lv_obj_get_width(sw);
211 lv_coord_t objh = lv_obj_get_height(sw);
212 lv_coord_t knob_size = objh;
213 lv_area_t knob_area;
214
215 lv_style_int_t bg_left = lv_obj_get_style_pad_left(sw, LV_SWITCH_PART_BG);
216 lv_style_int_t bg_right = lv_obj_get_style_pad_right(sw, LV_SWITCH_PART_BG);
217
218 lv_coord_t max_indic_w = objw - bg_left - bg_right;
219 lv_coord_t act_indic_w = lv_area_get_width(&ext->bar.indic_area);
220 if(base_dir != LV_BIDI_DIR_RTL) {
221 knob_area.x1 = ext->bar.indic_area.x2 - ((act_indic_w * knob_size) / max_indic_w);
222 knob_area.x2 = knob_area.x1 + knob_size;
223 }
224 else {
225 knob_area.x2 = ext->bar.indic_area.x1 + ((act_indic_w * knob_size) / max_indic_w);
226 knob_area.x1 = knob_area.x2 - knob_size;
227 }
228
229 knob_area.y1 = sw->coords.y1;
230 knob_area.y2 = sw->coords.y2;
231
232 lv_style_int_t knob_left = lv_obj_get_style_pad_left(sw, LV_SWITCH_PART_KNOB);
233 lv_style_int_t knob_right = lv_obj_get_style_pad_right(sw, LV_SWITCH_PART_KNOB);
234 lv_style_int_t knob_top = lv_obj_get_style_pad_top(sw, LV_SWITCH_PART_KNOB);
235 lv_style_int_t knob_bottom = lv_obj_get_style_pad_bottom(sw, LV_SWITCH_PART_KNOB);
236
237 /*Apply the paddings on the knob area*/
238 knob_area.x1 -= knob_left;
239 knob_area.x2 += knob_right;
240 knob_area.y1 -= knob_top;
241 knob_area.y2 += knob_bottom;
242
243 lv_draw_rect_dsc_t knob_rect_dsc;
244 lv_draw_rect_dsc_init(&knob_rect_dsc);
245 lv_obj_init_draw_rect_dsc(sw, LV_SWITCH_PART_KNOB, &knob_rect_dsc);
246
247 lv_draw_rect(&knob_area, clip_area, &knob_rect_dsc);
248
249 }
250 /*Post draw when the children are drawn*/
251 else if(mode == LV_DESIGN_DRAW_POST) {
252 return ancestor_design(sw, clip_area, mode);
253 }
254
255 return LV_DESIGN_RES_OK;
256 }
257
258
259 /**
260 * Signal function of the switch
261 * @param sw pointer to a switch object
262 * @param sign a signal type from lv_signal_t enum
263 * @param param pointer to a signal specific variable
264 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
265 */
lv_switch_signal(lv_obj_t * sw,lv_signal_t sign,void * param)266 static lv_res_t lv_switch_signal(lv_obj_t * sw, lv_signal_t sign, void * param)
267 {
268 lv_res_t res;
269
270 if(sign == LV_SIGNAL_GET_STYLE) {
271 lv_get_style_info_t * info = param;
272 info->result = lv_switch_get_style(sw, info->part);
273 if(info->result != NULL) return LV_RES_OK;
274 else return ancestor_signal(sw, sign, param);
275 }
276
277 if(sign == LV_SIGNAL_GET_TYPE) {
278 res = ancestor_signal(sw, sign, param);
279 if(res != LV_RES_OK) return res;
280 return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
281 }
282
283 /* Include the ancient signal function */
284 res = ancestor_signal(sw, sign, param);
285 if(res != LV_RES_OK) return res;
286
287 if(sign == LV_SIGNAL_CLEANUP) {
288 lv_obj_clean_style_list(sw, LV_SWITCH_PART_KNOB);
289 }
290 else if(sign == LV_SIGNAL_RELEASED) {
291 if(lv_switch_get_state(sw)) lv_switch_off(sw, LV_ANIM_ON);
292 else lv_switch_on(sw, LV_ANIM_ON);
293
294 res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, NULL);
295 if(res != LV_RES_OK) return res;
296
297 }
298 else if(sign == LV_SIGNAL_CONTROL) {
299 #if LV_USE_GROUP
300 char c = *((char *)param);
301 if(c == LV_KEY_RIGHT || c == LV_KEY_UP) lv_switch_on(sw, LV_ANIM_ON);
302 else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) lv_switch_off(sw, LV_ANIM_ON);
303
304 res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, NULL);
305 if(res != LV_RES_OK) return res;
306 #endif
307 }
308 else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
309 lv_style_int_t knob_left = lv_obj_get_style_pad_left(sw, LV_SWITCH_PART_KNOB);
310 lv_style_int_t knob_right = lv_obj_get_style_pad_right(sw, LV_SWITCH_PART_KNOB);
311 lv_style_int_t knob_top = lv_obj_get_style_pad_top(sw, LV_SWITCH_PART_KNOB);
312 lv_style_int_t knob_bottom = lv_obj_get_style_pad_bottom(sw, LV_SWITCH_PART_KNOB);
313
314 /* The smaller size is the knob diameter*/
315 lv_coord_t knob_size = LV_MATH_MIN(lv_obj_get_width(sw), lv_obj_get_height(sw)) >> 1;
316 knob_size += LV_MATH_MAX(LV_MATH_MAX(knob_left, knob_right), LV_MATH_MAX(knob_bottom, knob_top));
317 knob_size += 2; /*For rounding error*/
318
319 knob_size += lv_obj_get_draw_rect_ext_pad_size(sw, LV_SWITCH_PART_KNOB);
320
321 /*Indic. size is handled by bar*/
322 sw->ext_draw_pad = LV_MATH_MAX(sw->ext_draw_pad, knob_size);
323 }
324 else if(sign == LV_SIGNAL_GET_EDITABLE) {
325 #if LV_USE_GROUP
326 bool * editable = (bool *)param;
327 *editable = false; /*The ancestor slider is editable the switch is not*/
328 #endif
329 }
330
331 return res;
332 }
333
lv_switch_get_style(lv_obj_t * sw,uint8_t part)334 static lv_style_list_t * lv_switch_get_style(lv_obj_t * sw, uint8_t part)
335 {
336 LV_ASSERT_OBJ(sw, LV_OBJX_NAME);
337
338 lv_switch_ext_t * ext = lv_obj_get_ext_attr(sw);
339 lv_style_list_t * style_dsc_p;
340
341 switch(part) {
342 case LV_SWITCH_PART_BG:
343 style_dsc_p = &sw->style_list;
344 break;
345 case LV_SWITCH_PART_INDIC:
346 style_dsc_p = &ext->bar.style_indic;
347 break;
348 case LV_SWITCH_PART_KNOB:
349 style_dsc_p = &ext->style_knob;
350 break;
351 default:
352 style_dsc_p = NULL;
353 }
354
355 return style_dsc_p;
356 }
357 #endif
358