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