1 /**
2 * @file lv_sw.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_sw.h"
10
11 #if LV_USE_SW != 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_themes/lv_theme.h"
19 #include "../lv_misc/lv_math.h"
20
21 /*********************
22 * DEFINES
23 *********************/
24
25 /**********************
26 * TYPEDEFS
27 **********************/
28
29 /**********************
30 * STATIC PROTOTYPES
31 **********************/
32 static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param);
33
34 /**********************
35 * STATIC VARIABLES
36 **********************/
37 static lv_signal_cb_t ancestor_signal;
38
39 /**********************
40 * MACROS
41 **********************/
42
43 /**********************
44 * GLOBAL FUNCTIONS
45 **********************/
46
47 /**
48 * Create a switch objects
49 * @param par pointer to an object, it will be the parent of the new switch
50 * @param copy pointer to a switch object, if not NULL then the new object will be copied from it
51 * @return pointer to the created switch
52 */
lv_sw_create(lv_obj_t * par,const lv_obj_t * copy)53 lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy)
54 {
55 LV_LOG_TRACE("switch create started");
56
57 /*Create the ancestor of switch*/
58 lv_obj_t * new_sw = lv_slider_create(par, copy);
59 lv_mem_assert(new_sw);
60 if(new_sw == NULL) return NULL;
61
62 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_sw);
63
64 /*Allocate the switch type specific extended data*/
65 lv_sw_ext_t * ext = lv_obj_allocate_ext_attr(new_sw, sizeof(lv_sw_ext_t));
66 lv_mem_assert(ext);
67 if(ext == NULL) return NULL;
68
69 /*Initialize the allocated 'ext' */
70 ext->changed = 0;
71 #if LV_USE_ANIMATION
72 ext->anim_time = 0;
73 #endif
74 ext->style_knob_off = ext->slider.style_knob;
75 ext->style_knob_on = ext->slider.style_knob;
76
77 /*The signal and design functions are not copied so set them here*/
78 lv_obj_set_signal_cb(new_sw, lv_sw_signal);
79
80 /*Init the new switch switch*/
81 if(copy == NULL) {
82 lv_obj_set_size(new_sw, 2 * LV_DPI / 3, LV_DPI / 3);
83 lv_slider_set_knob_in(new_sw, true);
84 lv_slider_set_range(new_sw, 0, LV_SW_MAX_VALUE);
85
86 /*Set the default styles*/
87 lv_theme_t * th = lv_theme_get_current();
88 if(th) {
89 lv_sw_set_style(new_sw, LV_SW_STYLE_BG, th->style.sw.bg);
90 lv_sw_set_style(new_sw, LV_SW_STYLE_INDIC, th->style.sw.indic);
91 lv_sw_set_style(new_sw, LV_SW_STYLE_KNOB_OFF, th->style.sw.knob_off);
92 lv_sw_set_style(new_sw, LV_SW_STYLE_KNOB_ON, th->style.sw.knob_on);
93 } else {
94 /*Let the slider' style*/
95 }
96
97 }
98 /*Copy an existing switch*/
99 else {
100 lv_sw_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
101 ext->style_knob_off = copy_ext->style_knob_off;
102 ext->style_knob_on = copy_ext->style_knob_on;
103 #if LV_USE_ANIMATION
104 ext->anim_time = copy_ext->anim_time;
105 #endif
106
107 if(lv_sw_get_state(new_sw))
108 lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
109 else
110 lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
111
112 /*Refresh the style with new signal function*/
113 lv_obj_refresh_style(new_sw);
114 }
115
116 LV_LOG_INFO("switch created");
117
118 return new_sw;
119 }
120
121 /*=====================
122 * Setter functions
123 *====================*/
124
125 /**
126 * Turn ON the switch
127 * @param sw pointer to a switch objec
128 * @param anim LV_ANOM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
129 */
lv_sw_on(lv_obj_t * sw,lv_anim_enable_t anim)130 void lv_sw_on(lv_obj_t * sw, lv_anim_enable_t anim)
131 {
132 #if LV_USE_ANIMATION == 0
133 anim = LV_ANIM_OFF;
134 #endif
135 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
136 lv_slider_set_value(sw, LV_SW_MAX_VALUE, anim);
137 lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
138 }
139
140 /**
141 * Turn OFF the switch
142 * @param sw pointer to a switch object
143 * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
144 */
lv_sw_off(lv_obj_t * sw,lv_anim_enable_t anim)145 void lv_sw_off(lv_obj_t * sw, lv_anim_enable_t anim)
146 {
147 #if LV_USE_ANIMATION == 0
148 anim = LV_ANIM_OFF;
149 #endif
150 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
151 lv_slider_set_value(sw, 0, anim);
152 lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
153 }
154
155 /**
156 * Toggle the position of the switch
157 * @param sw pointer to a switch object
158 * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
159 * @return resulting state of the switch.
160 */
lv_sw_toggle(lv_obj_t * sw,lv_anim_enable_t anim)161 bool lv_sw_toggle(lv_obj_t * sw, lv_anim_enable_t anim)
162 {
163 #if LV_USE_ANIMATION == 0
164 anim = LV_ANIM_OFF;
165 #endif
166
167 bool state = lv_sw_get_state(sw);
168 if(state)
169 lv_sw_off(sw, anim);
170 else
171 lv_sw_on(sw, anim);
172
173 return !state;
174 }
175
176 /**
177 * Set a style of a switch
178 * @param sw pointer to a switch object
179 * @param type which style should be set
180 * @param style pointer to a style
181 */
lv_sw_set_style(lv_obj_t * sw,lv_sw_style_t type,const lv_style_t * style)182 void lv_sw_set_style(lv_obj_t * sw, lv_sw_style_t type, const lv_style_t * style)
183 {
184 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
185
186 switch(type) {
187 case LV_SLIDER_STYLE_BG: lv_slider_set_style(sw, LV_SLIDER_STYLE_BG, style); break;
188 case LV_SLIDER_STYLE_INDIC: lv_bar_set_style(sw, LV_SLIDER_STYLE_INDIC, style); break;
189 case LV_SW_STYLE_KNOB_OFF:
190 ext->style_knob_off = style;
191 if(lv_sw_get_state(sw) == 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style);
192 break;
193 case LV_SW_STYLE_KNOB_ON:
194 ext->style_knob_on = style;
195 if(lv_sw_get_state(sw) != 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style);
196 break;
197 }
198 }
199
lv_sw_set_anim_time(lv_obj_t * sw,uint16_t anim_time)200 void lv_sw_set_anim_time(lv_obj_t * sw, uint16_t anim_time)
201 {
202 #if LV_USE_ANIMATION
203 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
204 ext->anim_time = anim_time;
205 #else
206 (void)sw;
207 (void)anim_time;
208 #endif
209 }
210
211 /*=====================
212 * Getter functions
213 *====================*/
214
215 /**
216 * Get a style of a switch
217 * @param sw pointer to a switch object
218 * @param type which style should be get
219 * @return style pointer to a style
220 */
lv_sw_get_style(const lv_obj_t * sw,lv_sw_style_t type)221 const lv_style_t * lv_sw_get_style(const lv_obj_t * sw, lv_sw_style_t type)
222 {
223 const lv_style_t * style = NULL;
224 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
225
226 switch(type) {
227 case LV_SW_STYLE_BG: style = lv_slider_get_style(sw, LV_SLIDER_STYLE_BG); break;
228 case LV_SW_STYLE_INDIC: style = lv_slider_get_style(sw, LV_SLIDER_STYLE_INDIC); break;
229 case LV_SW_STYLE_KNOB_OFF: style = ext->style_knob_off; break;
230 case LV_SW_STYLE_KNOB_ON: style = ext->style_knob_on; break;
231 default: style = NULL; break;
232 }
233
234 return style;
235 }
236
lv_sw_get_anim_time(const lv_obj_t * sw)237 uint16_t lv_sw_get_anim_time(const lv_obj_t * sw)
238 {
239
240 #if LV_USE_ANIMATION
241 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
242 return ext->anim_time;
243 #else
244 (void)sw; /*Unused*/
245 return 0;
246 #endif
247 }
248
249 /**********************
250 * STATIC FUNCTIONS
251 **********************/
252
253 /**
254 * Signal function of the switch
255 * @param sw pointer to a switch object
256 * @param sign a signal type from lv_signal_t enum
257 * @param param pointer to a signal specific variable
258 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
259 */
lv_sw_signal(lv_obj_t * sw,lv_signal_t sign,void * param)260 static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param)
261 {
262 lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
263
264 /*Save the current (old) value before slider signal modifies it. It will be required in the
265 * later calculations*/
266 int16_t old_val;
267 if(sign == LV_SIGNAL_PRESSING)
268 old_val = ext->slider.drag_value;
269 else
270 old_val = lv_slider_get_value(sw);
271
272 /*Don't let the slider to call the action. Switch handles it differently*/
273 lv_event_cb_t event_cb = sw->event_cb;
274 sw->event_cb = NULL;
275
276 lv_res_t res;
277 /* Include the ancient signal function */
278
279 res = ancestor_signal(sw, sign, param);
280 if(res != LV_RES_OK) return res;
281
282 sw->event_cb = event_cb;
283
284 if(sign == LV_SIGNAL_CLEANUP) {
285 /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
286 } else if(sign == LV_SIGNAL_PRESSED) {
287
288 /*Save the x coordinate of the pressed point to see if the switch was slid*/
289 lv_indev_t * indev = lv_indev_get_act();
290 if(indev) {
291 lv_point_t p;
292 lv_indev_get_point(indev, &p);
293 ext->start_x = p.x;
294 }
295 ext->slided = 0;
296 ext->changed = 0;
297 } else if(sign == LV_SIGNAL_PRESSING) {
298 /*See if the switch was slid (moved at least a little)*/
299 lv_indev_t * indev = lv_indev_get_act();
300 if(indev) {
301 lv_point_t p = {0, 0};
302 lv_indev_get_point(indev, &p);
303 if(LV_MATH_ABS(p.x - ext->start_x) > LV_INDEV_DEF_DRAG_LIMIT) ext->slided = 1;
304 }
305
306 /*If didn't slide then revert the min/max value. So click without slide won't move the
307 * switch as a slider*/
308 if(ext->slided == 0) {
309 if(lv_sw_get_state(sw))
310 ext->slider.drag_value = LV_SW_MAX_VALUE;
311 else
312 ext->slider.drag_value = 0;
313 }
314
315 /*If explicitly changed (by slide) don't need to be toggled on release*/
316 int16_t threshold = LV_SW_MAX_VALUE / 2;
317 if((old_val < threshold && ext->slider.drag_value > threshold) ||
318 (old_val > threshold && ext->slider.drag_value < threshold)) {
319 ext->changed = 1;
320 }
321 } else if(sign == LV_SIGNAL_PRESS_LOST) {
322 if(lv_sw_get_state(sw)) {
323 lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
324 lv_slider_set_value(sw, LV_SW_MAX_VALUE, LV_ANIM_ON);
325 if(res != LV_RES_OK) return res;
326 } else {
327 lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
328 lv_slider_set_value(sw, 0, LV_ANIM_ON);
329 if(res != LV_RES_OK) return res;
330 }
331 } else if(sign == LV_SIGNAL_RELEASED) {
332 /*If not dragged then toggle the switch*/
333 if(ext->changed == 0) {
334 int32_t state;
335 if(lv_sw_get_state(sw)) {
336 lv_sw_off(sw, LV_ANIM_ON);
337 state = 0;
338 } else {
339 lv_sw_on(sw, LV_ANIM_ON);
340 state = 1;
341 }
342
343 res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, &state);
344 if(res != LV_RES_OK) return res;
345 }
346 /*If the switch was dragged then calculate the new state based on the current position*/
347 else {
348 int16_t v = lv_slider_get_value(sw);
349 int32_t state;
350 if(v > LV_SW_MAX_VALUE / 2) {
351 lv_sw_on(sw, LV_ANIM_ON);
352 state = 1;
353 } else {
354 lv_sw_off(sw, LV_ANIM_ON);
355 state = 0;
356 }
357 res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, &state);
358 if(res != LV_RES_OK) return res;
359 }
360 } else if(sign == LV_SIGNAL_CONTROL) {
361 char c = *((char *)param);
362 uint32_t state;
363 if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
364 lv_slider_set_value(sw, LV_SW_MAX_VALUE, true);
365 state = 1;
366 res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, &state);
367 if(res != LV_RES_OK) return res;
368 } else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
369 lv_slider_set_value(sw, 0, true);
370 state = 0;
371 res = lv_event_send(sw, LV_EVENT_VALUE_CHANGED, &state);
372 if(res != LV_RES_OK) return res;
373 }
374 } else if(sign == LV_SIGNAL_GET_EDITABLE) {
375 bool * editable = (bool *)param;
376 *editable = false; /*The ancestor slider is editable the switch is not*/
377 } else if(sign == LV_SIGNAL_GET_TYPE) {
378 lv_obj_type_t * buf = param;
379 uint8_t i;
380 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
381 if(buf->type[i] == NULL) break;
382 }
383 buf->type[i] = "lv_sw";
384 }
385
386 return res;
387 }
388
389 #endif
390