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 #include "../misc/lv_assert.h"
14 #include "../misc/lv_math.h"
15 #include "../misc/lv_anim.h"
16 #include "../core/lv_indev.h"
17 #include "../core/lv_disp.h"
18 #include "lv_img.h"
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 #define MY_CLASS &lv_switch_class
24 
25 #define LV_SWITCH_IS_ANIMATING(sw) (((sw)->anim_state) != LV_SWITCH_ANIM_STATE_INV)
26 
27 /** Switch animation start value. (Not the real value of the switch just indicates process animation)*/
28 #define LV_SWITCH_ANIM_STATE_START 0
29 
30 /** Switch animation end value.  (Not the real value of the switch just indicates process animation)*/
31 #define LV_SWITCH_ANIM_STATE_END   256
32 
33 /** Mark no animation is in progress*/
34 #define LV_SWITCH_ANIM_STATE_INV   -1
35 
36 /**********************
37  *      TYPEDEFS
38  **********************/
39 
40 /**********************
41  *  STATIC PROTOTYPES
42  **********************/
43 static void lv_switch_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
44 static void lv_switch_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
45 static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e);
46 static void draw_main(lv_event_t * e);
47 
48 static void lv_switch_anim_exec_cb(void * sw, int32_t value);
49 static void lv_switch_trigger_anim(lv_obj_t * obj);
50 static void lv_switch_anim_ready(lv_anim_t * a);
51 /**********************
52  *  STATIC VARIABLES
53  **********************/
54 const lv_obj_class_t lv_switch_class = {
55     .constructor_cb = lv_switch_constructor,
56     .destructor_cb = lv_switch_destructor,
57     .event_cb = lv_switch_event,
58     .width_def = (4 * LV_DPI_DEF) / 10,
59     .height_def = (4 * LV_DPI_DEF) / 17,
60     .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
61     .instance_size = sizeof(lv_switch_t),
62     .base_class = &lv_obj_class
63 };
64 
65 /**********************
66  *      MACROS
67  **********************/
68 
69 /**********************
70  *   GLOBAL FUNCTIONS
71  **********************/
72 
lv_switch_create(lv_obj_t * parent)73 lv_obj_t * lv_switch_create(lv_obj_t * parent)
74 {
75     LV_LOG_INFO("begin");
76     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
77     lv_obj_class_init_obj(obj);
78     return obj;
79 }
80 
81 /**********************
82  *   STATIC FUNCTIONS
83  **********************/
84 
lv_switch_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)85 static void lv_switch_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
86 {
87     LV_UNUSED(class_p);
88     LV_TRACE_OBJ_CREATE("begin");
89 
90     lv_switch_t * sw = (lv_switch_t *)obj;
91 
92     sw->anim_state = LV_SWITCH_ANIM_STATE_INV;
93 
94     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
95     lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
96     lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
97 
98     LV_TRACE_OBJ_CREATE("finished");
99 }
100 
lv_switch_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)101 static void lv_switch_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
102 {
103     LV_UNUSED(class_p);
104     lv_switch_t * sw = (lv_switch_t *)obj;
105 
106     lv_anim_del(sw, NULL);
107 }
108 
lv_switch_event(const lv_obj_class_t * class_p,lv_event_t * e)109 static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e)
110 {
111     LV_UNUSED(class_p);
112 
113     lv_res_t res;
114 
115     /*Call the ancestor's event handler*/
116     res = lv_obj_event_base(MY_CLASS, e);
117     if(res != LV_RES_OK) return;
118 
119     lv_event_code_t code = lv_event_get_code(e);
120     lv_obj_t * obj = lv_event_get_target(e);
121 
122     if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
123         lv_coord_t knob_left = lv_obj_get_style_pad_left(obj,   LV_PART_KNOB);
124         lv_coord_t knob_right = lv_obj_get_style_pad_right(obj,  LV_PART_KNOB);
125         lv_coord_t knob_top = lv_obj_get_style_pad_top(obj,    LV_PART_KNOB);
126         lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
127 
128         /*The smaller size is the knob diameter*/
129         lv_coord_t knob_size = LV_MAX4(knob_left, knob_right, knob_bottom, knob_top);
130         knob_size += _LV_SWITCH_KNOB_EXT_AREA_CORRECTION;
131         knob_size += lv_obj_calculate_ext_draw_size(obj, LV_PART_KNOB);
132 
133         lv_coord_t * s = lv_event_get_param(e);
134         *s = LV_MAX(*s, knob_size);
135         *s = LV_MAX(*s, lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR));
136     }
137     else if(code == LV_EVENT_VALUE_CHANGED) {
138         lv_switch_trigger_anim(obj);
139         lv_obj_invalidate(obj);
140     }
141     else if(code == LV_EVENT_DRAW_MAIN) {
142         draw_main(e);
143     }
144 }
145 
draw_main(lv_event_t * e)146 static void draw_main(lv_event_t * e)
147 {
148     lv_obj_t * obj = lv_event_get_target(e);
149     lv_switch_t * sw = (lv_switch_t *)obj;
150 
151     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
152 
153     /*Calculate the indicator area*/
154     lv_coord_t bg_left = lv_obj_get_style_pad_left(obj,     LV_PART_MAIN);
155     lv_coord_t bg_right = lv_obj_get_style_pad_right(obj,   LV_PART_MAIN);
156     lv_coord_t bg_top = lv_obj_get_style_pad_top(obj,       LV_PART_MAIN);
157     lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
158 
159     /*Draw the indicator*/
160     /*Respect the background's padding*/
161     lv_area_t indic_area;
162     lv_area_copy(&indic_area, &obj->coords);
163     indic_area.x1 += bg_left;
164     indic_area.x2 -= bg_right;
165     indic_area.y1 += bg_top;
166     indic_area.y2 -= bg_bottom;
167 
168     lv_draw_rect_dsc_t draw_indic_dsc;
169     lv_draw_rect_dsc_init(&draw_indic_dsc);
170     lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &draw_indic_dsc);
171     lv_draw_rect(draw_ctx, &draw_indic_dsc, &indic_area);
172 
173     /*Draw the knob*/
174     lv_coord_t anim_value_x = 0;
175     lv_coord_t knob_size = lv_obj_get_height(obj);
176     lv_coord_t anim_length = lv_area_get_width(&obj->coords) - knob_size;
177 
178     if(LV_SWITCH_IS_ANIMATING(sw)) {
179         /* Use the animation's coordinate */
180         anim_value_x = (anim_length * sw->anim_state) / LV_SWITCH_ANIM_STATE_END;
181     }
182     else {
183         /* Use LV_STATE_CHECKED to decide the coordinate */
184         bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
185         anim_value_x = chk ? anim_length : 0;
186     }
187 
188     if(LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN)) {
189         anim_value_x = anim_length - anim_value_x;
190     }
191 
192     lv_area_t knob_area;
193     knob_area.x1 = obj->coords.x1 + anim_value_x;
194     knob_area.x2 = knob_area.x1 + knob_size;
195 
196     knob_area.y1 = obj->coords.y1;
197     knob_area.y2 = obj->coords.y2;
198 
199     lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
200     lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
201     lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
202     lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
203 
204     /*Apply the paddings on the knob area*/
205     knob_area.x1 -= knob_left;
206     knob_area.x2 += knob_right;
207     knob_area.y1 -= knob_top;
208     knob_area.y2 += knob_bottom;
209 
210     lv_draw_rect_dsc_t knob_rect_dsc;
211     lv_draw_rect_dsc_init(&knob_rect_dsc);
212     lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
213 
214     lv_draw_rect(draw_ctx, &knob_rect_dsc, &knob_area);
215 }
216 
lv_switch_anim_exec_cb(void * var,int32_t value)217 static void lv_switch_anim_exec_cb(void * var, int32_t value)
218 {
219     lv_switch_t * sw = var;
220     sw->anim_state = value;
221     lv_obj_invalidate((lv_obj_t *)sw);
222 }
223 
224 /**
225  * Resets the switch's animation state to "no animation in progress".
226  */
lv_switch_anim_ready(lv_anim_t * a)227 static void lv_switch_anim_ready(lv_anim_t * a)
228 {
229     lv_switch_t * sw = a->var;
230     sw->anim_state = LV_SWITCH_ANIM_STATE_INV;
231     lv_obj_invalidate((lv_obj_t *)sw);
232 }
233 
234 /**
235  * Starts an animation for the switch knob. if the anim_time style property is greater than 0
236  * @param obj the switch to animate
237  */
lv_switch_trigger_anim(lv_obj_t * obj)238 static void lv_switch_trigger_anim(lv_obj_t * obj)
239 {
240     LV_ASSERT_OBJ(obj, MY_CLASS);
241     lv_switch_t * sw = (lv_switch_t *)obj;
242 
243     uint32_t anim_dur_full = lv_obj_get_style_anim_time(obj, LV_PART_MAIN);
244 
245     if(anim_dur_full > 0) {
246         bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
247         int32_t anim_start;
248         int32_t anim_end;
249         /*No animation in progress -> simply set the values*/
250         if(sw->anim_state == LV_SWITCH_ANIM_STATE_INV) {
251             anim_start = chk ? LV_SWITCH_ANIM_STATE_START : LV_SWITCH_ANIM_STATE_END;
252             anim_end   = chk ? LV_SWITCH_ANIM_STATE_END : LV_SWITCH_ANIM_STATE_START;
253         }
254         /*Animation in progress. Start from the animation end value*/
255         else {
256             anim_start = sw->anim_state;
257             anim_end   = chk ? LV_SWITCH_ANIM_STATE_END : LV_SWITCH_ANIM_STATE_START;
258         }
259         /*Calculate actual animation duration*/
260         uint32_t anim_dur = (anim_dur_full * LV_ABS(anim_start - anim_end)) / LV_SWITCH_ANIM_STATE_END;
261 
262         /*Stop the previous animation if it exists*/
263         lv_anim_del(sw, NULL);
264 
265         lv_anim_t a;
266         lv_anim_init(&a);
267         lv_anim_set_var(&a, sw);
268         lv_anim_set_exec_cb(&a, lv_switch_anim_exec_cb);
269         lv_anim_set_values(&a, anim_start, anim_end);
270         lv_anim_set_ready_cb(&a, lv_switch_anim_ready);
271         lv_anim_set_time(&a, anim_dur);
272         lv_anim_start(&a);
273     }
274 }
275 
276 
277 #endif
278