1 /**
2  * @file lv_slider.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_slider_private.h"
10 #include "../../misc/lv_area_private.h"
11 #include "../../core/lv_obj_private.h"
12 #include "../../core/lv_obj_event_private.h"
13 #include "../../core/lv_obj_class_private.h"
14 #if LV_USE_SLIDER != 0
15 
16 #include "../../misc/lv_assert.h"
17 #include "../../core/lv_group.h"
18 #include "../../indev/lv_indev.h"
19 #include "../../indev/lv_indev_private.h"
20 #include "../../display/lv_display.h"
21 #include "../../draw/lv_draw.h"
22 #include "../../stdlib/lv_string.h"
23 #include "../../misc/lv_math.h"
24 #include "../image/lv_image.h"
25 
26 /*********************
27  *      DEFINES
28  *********************/
29 #define MY_CLASS (&lv_slider_class)
30 
31 #define LV_SLIDER_KNOB_COORD(is_reversed, area) (is_reversed ? area.x1 : area.x2)
32 #define LV_SLIDER_KNOB_COORD_VERTICAL(is_reversed, area) (is_reversed ? area.y2 : area.y1)
33 
34 /**********************
35  *      TYPEDEFS
36  **********************/
37 
38 /**********************
39  *  STATIC PROTOTYPES
40  **********************/
41 static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
42 static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e);
43 static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const int32_t knob_size, const bool hor);
44 static void draw_knob(lv_event_t * e);
45 static bool is_slider_horizontal(lv_obj_t * obj);
46 static void drag_start(lv_obj_t * obj);
47 static void update_knob_pos(lv_obj_t * obj, bool check_drag);
48 
49 /**********************
50  *  STATIC VARIABLES
51  **********************/
52 
53 #if LV_USE_OBJ_PROPERTY
54 static const lv_property_ops_t properties[] = {
55     {
56         .id = LV_PROPERTY_SLIDER_VALUE,
57         .setter = lv_slider_set_value,
58         .getter = lv_slider_get_value,
59     },
60     {
61         .id = LV_PROPERTY_SLIDER_LEFT_VALUE,
62         .setter = lv_slider_set_left_value,
63         .getter = lv_slider_get_left_value,
64     },
65     {
66         .id = LV_PROPERTY_SLIDER_RANGE,
67         .setter = lv_slider_set_range,
68         .getter = NULL,
69     },
70     {
71         .id = LV_PROPERTY_SLIDER_MIN_VALUE,
72         .setter = NULL,
73         .getter = lv_slider_get_min_value,
74     },
75     {
76         .id = LV_PROPERTY_SLIDER_MAX_VALUE,
77         .setter = NULL,
78         .getter = lv_slider_get_max_value,
79     },
80     {
81         .id = LV_PROPERTY_SLIDER_MODE,
82         .setter = lv_slider_set_mode,
83         .getter = lv_slider_get_mode,
84     },
85     {
86         .id = LV_PROPERTY_SLIDER_IS_DRAGGED,
87         .setter = NULL,
88         .getter = lv_slider_is_dragged,
89     },
90     {
91         .id = LV_PROPERTY_SLIDER_IS_SYMMETRICAL,
92         .setter = NULL,
93         .getter = lv_slider_is_symmetrical,
94     },
95 };
96 #endif
97 
98 const lv_obj_class_t lv_slider_class = {
99     .constructor_cb = lv_slider_constructor,
100     .event_cb = lv_slider_event,
101     .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
102     .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
103     .instance_size = sizeof(lv_slider_t),
104     .base_class = &lv_bar_class,
105     .name = "slider",
106 #if LV_USE_OBJ_PROPERTY
107     .prop_index_start = LV_PROPERTY_SLIDER_START,
108     .prop_index_end = LV_PROPERTY_SLIDER_END,
109     .properties = properties,
110     .properties_count = sizeof(properties) / sizeof(properties[0]),
111 
112 #if LV_USE_OBJ_PROPERTY_NAME
113     .property_names = lv_slider_property_names,
114     .names_count = sizeof(lv_slider_property_names) / sizeof(lv_property_name_t),
115 #endif
116 #endif
117 };
118 
119 /**********************
120  *      MACROS
121  **********************/
122 
123 /**********************
124  *   GLOBAL FUNCTIONS
125  **********************/
126 
lv_slider_create(lv_obj_t * parent)127 lv_obj_t * lv_slider_create(lv_obj_t * parent)
128 {
129     LV_LOG_INFO("begin");
130     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
131     lv_obj_class_init_obj(obj);
132     return obj;
133 }
134 
lv_slider_is_dragged(const lv_obj_t * obj)135 bool lv_slider_is_dragged(const lv_obj_t * obj)
136 {
137     LV_ASSERT_OBJ(obj, MY_CLASS);
138     lv_slider_t * slider = (lv_slider_t *)obj;
139 
140     return slider->dragging;
141 }
142 
lv_slider_set_value(lv_obj_t * obj,int32_t value,lv_anim_enable_t anim)143 void lv_slider_set_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
144 {
145     lv_bar_set_value(obj, value, anim);
146 }
147 
lv_slider_set_left_value(lv_obj_t * obj,int32_t value,lv_anim_enable_t anim)148 void lv_slider_set_left_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
149 {
150     lv_bar_set_start_value(obj, value, anim);
151 }
152 
lv_slider_set_range(lv_obj_t * obj,int32_t min,int32_t max)153 void lv_slider_set_range(lv_obj_t * obj, int32_t min, int32_t max)
154 {
155     lv_bar_set_range(obj, min, max);
156 }
157 
lv_slider_set_mode(lv_obj_t * obj,lv_slider_mode_t mode)158 void lv_slider_set_mode(lv_obj_t * obj, lv_slider_mode_t mode)
159 {
160     lv_bar_set_mode(obj, (lv_bar_mode_t)mode);
161 }
162 
lv_slider_set_orientation(lv_obj_t * obj,lv_slider_orientation_t orientation)163 void lv_slider_set_orientation(lv_obj_t * obj, lv_slider_orientation_t orientation)
164 {
165     lv_bar_set_orientation(obj, (lv_bar_orientation_t)orientation);
166 }
167 
lv_slider_get_value(const lv_obj_t * obj)168 int32_t lv_slider_get_value(const lv_obj_t * obj)
169 {
170     return lv_bar_get_value(obj);
171 }
172 
lv_slider_get_left_value(const lv_obj_t * obj)173 int32_t lv_slider_get_left_value(const lv_obj_t * obj)
174 {
175     return lv_bar_get_start_value(obj);
176 }
177 
lv_slider_get_min_value(const lv_obj_t * obj)178 int32_t lv_slider_get_min_value(const lv_obj_t * obj)
179 {
180     return lv_bar_get_min_value(obj);
181 }
182 
lv_slider_get_max_value(const lv_obj_t * obj)183 int32_t lv_slider_get_max_value(const lv_obj_t * obj)
184 {
185     return lv_bar_get_max_value(obj);
186 }
187 
lv_slider_get_mode(lv_obj_t * slider)188 lv_slider_mode_t lv_slider_get_mode(lv_obj_t * slider)
189 {
190     lv_bar_mode_t mode = lv_bar_get_mode(slider);
191     if(mode == LV_BAR_MODE_SYMMETRICAL) return LV_SLIDER_MODE_SYMMETRICAL;
192     else if(mode == LV_BAR_MODE_RANGE) return LV_SLIDER_MODE_RANGE;
193     else return LV_SLIDER_MODE_NORMAL;
194 }
195 
lv_slider_get_orientation(lv_obj_t * slider)196 lv_slider_orientation_t lv_slider_get_orientation(lv_obj_t * slider)
197 {
198     lv_bar_orientation_t ori = lv_bar_get_orientation(slider);
199     if(ori == LV_BAR_ORIENTATION_HORIZONTAL) return LV_SLIDER_ORIENTATION_HORIZONTAL;
200     else if(ori == LV_BAR_ORIENTATION_VERTICAL) return LV_SLIDER_ORIENTATION_VERTICAL;
201     else return LV_SLIDER_ORIENTATION_AUTO;
202 }
203 
lv_slider_is_symmetrical(lv_obj_t * obj)204 bool lv_slider_is_symmetrical(lv_obj_t * obj)
205 {
206     return lv_bar_is_symmetrical(obj);
207 }
208 
209 /**********************
210  *   STATIC FUNCTIONS
211  **********************/
212 
lv_slider_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)213 static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
214 {
215     LV_UNUSED(class_p);
216     lv_slider_t * slider = (lv_slider_t *)obj;
217 
218     /*Initialize the allocated 'slider'*/
219     slider->value_to_set = NULL;
220     slider->dragging = 0U;
221     slider->left_knob_focus = 0U;
222 
223     lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
224     lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
225     lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
226     lv_obj_set_ext_click_area(obj, LV_DPX(8));
227 }
228 
lv_slider_event(const lv_obj_class_t * class_p,lv_event_t * e)229 static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e)
230 {
231     LV_UNUSED(class_p);
232 
233     lv_result_t res;
234 
235     /*Call the ancestor's event handler*/
236     res = lv_obj_event_base(MY_CLASS, e);
237     if(res != LV_RESULT_OK) return;
238 
239     lv_event_code_t code = lv_event_get_code(e);
240     lv_obj_t * obj = lv_event_get_current_target(e);
241     lv_slider_t * slider = (lv_slider_t *)obj;
242     lv_slider_mode_t type = lv_slider_get_mode(obj);
243 
244     /*Advanced hit testing: react only on dragging the knob(s)*/
245     if(code == LV_EVENT_HIT_TEST) {
246         lv_hit_test_info_t * info = lv_event_get_param(e);
247         int32_t ext_click_area = obj->spec_attr ? obj->spec_attr->ext_click_pad : 0;
248 
249         /*Ordinary slider: was the knob area hit?*/
250         lv_area_t a;
251         lv_area_copy(&a, &slider->right_knob_area);
252         lv_area_increase(&a, ext_click_area, ext_click_area);
253         info->res = lv_area_is_point_on(&a, info->point, 0);
254 
255         /*There's still a chance that there is a hit if there is another knob*/
256         if((info->res == false) && (type == LV_SLIDER_MODE_RANGE)) {
257             lv_area_copy(&a, &slider->left_knob_area);
258             lv_area_increase(&a, ext_click_area, ext_click_area);
259             info->res = lv_area_is_point_on(&a, info->point, 0);
260         }
261     }
262     else if(code == LV_EVENT_PRESSED) {
263         /*Save the pressed coordinates*/
264         lv_indev_get_point(lv_indev_active(), &slider->pressed_point);
265         lv_obj_transform_point(obj, &slider->pressed_point, LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE_RECURSIVE);
266     }
267     else if(code == LV_EVENT_PRESSING) {
268         update_knob_pos(obj, true);
269     }
270     else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
271         update_knob_pos(obj, false);
272         slider->dragging = false;
273         slider->value_to_set = NULL;
274 
275         lv_obj_invalidate(obj);
276 
277         /*Leave edit mode if released. (No need to wait for LONG_PRESS)*/
278         lv_group_t * g   = lv_obj_get_group(obj);
279         bool editing     = lv_group_get_editing(g);
280         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_active());
281         if(indev_type == LV_INDEV_TYPE_ENCODER) {
282             if(editing) {
283                 if(lv_slider_get_mode(obj) == LV_SLIDER_MODE_RANGE) {
284                     if(slider->left_knob_focus == 0) slider->left_knob_focus = 1;
285                     else {
286                         slider->left_knob_focus = 0;
287                         lv_group_set_editing(g, false);
288                     }
289                 }
290                 else {
291                     lv_group_set_editing(g, false);
292                 }
293             }
294         }
295         else if(indev_type == LV_INDEV_TYPE_POINTER) {
296             if(is_slider_horizontal(obj)) lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
297             else  lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
298         }
299     }
300     else if(code == LV_EVENT_FOCUSED) {
301         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_active());
302         if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
303             slider->left_knob_focus = 0;
304         }
305     }
306     else if(code == LV_EVENT_SIZE_CHANGED) {
307         if(is_slider_horizontal(obj)) {
308             lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
309             lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
310         }
311         else {
312             lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
313             lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
314         }
315         lv_obj_refresh_ext_draw_size(obj);
316     }
317     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
318         int32_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
319         int32_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
320         int32_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
321         int32_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
322 
323         /*The smaller size is the knob diameter*/
324         int32_t trans_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB);
325         int32_t trans_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB);
326         int32_t knob_size = LV_MIN(lv_obj_get_width(obj) + 2 * trans_w, lv_obj_get_height(obj) + 2 * trans_h) >> 1;
327         knob_size += LV_MAX(LV_MAX(knob_left, knob_right), LV_MAX(knob_bottom, knob_top));
328         knob_size += 2;         /*For rounding error*/
329         knob_size += lv_obj_calculate_ext_draw_size(obj, LV_PART_KNOB);
330 
331         /*Indic. size is handled by bar*/
332         int32_t * s = lv_event_get_param(e);
333         *s  = LV_MAX(*s, knob_size);
334 
335     }
336     else if(code == LV_EVENT_KEY) {
337         uint32_t c = lv_event_get_key(e);
338 
339         if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
340             if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) + 1, LV_ANIM_ON);
341             else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) + 1, LV_ANIM_ON);
342         }
343         else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
344             if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) - 1, LV_ANIM_ON);
345             else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) - 1, LV_ANIM_ON);
346         }
347         else {
348             return;
349         }
350 
351         res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
352         if(res != LV_RESULT_OK) return;
353     }
354     else if(code == LV_EVENT_ROTARY) {
355         int32_t r = lv_event_get_rotary_diff(e);
356 
357         if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) + r, LV_ANIM_ON);
358         else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) + 1, LV_ANIM_ON);
359 
360         res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
361         if(res != LV_RESULT_OK) return;
362     }
363     else if(code == LV_EVENT_DRAW_MAIN) {
364         draw_knob(e);
365     }
366 }
367 
draw_knob(lv_event_t * e)368 static void draw_knob(lv_event_t * e)
369 {
370     lv_obj_t * obj = lv_event_get_current_target(e);
371     lv_slider_t * slider = (lv_slider_t *)obj;
372     lv_layer_t * layer = lv_event_get_layer(e);
373 
374     const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
375     const bool is_horizontal = is_slider_horizontal(obj);
376     const bool is_reversed = slider->bar.val_reversed ^ (is_rtl && is_horizontal);
377 
378     lv_area_t knob_area;
379     int32_t knob_size;
380     bool is_symmetrical = lv_slider_is_symmetrical(obj);
381 
382     if(is_horizontal) {
383         knob_size = lv_obj_get_height(obj);
384         if(is_symmetrical &&
385            slider->bar.cur_value < 0) knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_reversed, slider->bar.indic_area);
386         else knob_area.x1 = LV_SLIDER_KNOB_COORD(is_reversed, slider->bar.indic_area);
387     }
388     else {
389         knob_size = lv_obj_get_width(obj);
390         if(is_symmetrical &&
391            slider->bar.cur_value < 0) knob_area.y1 =  LV_SLIDER_KNOB_COORD_VERTICAL(!is_reversed, slider->bar.indic_area);
392         else knob_area.y1 = LV_SLIDER_KNOB_COORD_VERTICAL(is_reversed, slider->bar.indic_area);
393     }
394     lv_draw_rect_dsc_t knob_rect_dsc;
395     lv_draw_rect_dsc_init(&knob_rect_dsc);
396     knob_rect_dsc.base.layer = layer;
397     lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
398     /* Update knob area with knob style */
399     position_knob(obj, &knob_area, knob_size, is_horizontal);
400     /* Update right knob area with calculated knob area */
401     lv_area_copy(&slider->right_knob_area, &knob_area);
402 
403     if(lv_slider_get_mode(obj) != LV_SLIDER_MODE_RANGE) {
404         lv_draw_rect(layer, &knob_rect_dsc, &slider->right_knob_area);
405     }
406     else {
407         /*Save the draw part_draw_dsc. because it can be modified in the event*/
408         lv_draw_rect_dsc_t knob_rect_dsc_tmp;
409         lv_memcpy(&knob_rect_dsc_tmp, &knob_rect_dsc, sizeof(lv_draw_rect_dsc_t));
410         /* Draw the right knob */
411         lv_draw_rect(layer, &knob_rect_dsc, &slider->right_knob_area);
412 
413         /*Calculate the second knob area*/
414         if(is_horizontal) {
415             /*use !is_reversed to get the other knob*/
416             knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_reversed, slider->bar.indic_area);
417         }
418         else {
419             knob_area.y1 = LV_SLIDER_KNOB_COORD_VERTICAL(!is_reversed, slider->bar.indic_area);
420         }
421         position_knob(obj, &knob_area, knob_size, is_horizontal);
422         lv_area_copy(&slider->left_knob_area, &knob_area);
423 
424         lv_memcpy(&knob_rect_dsc, &knob_rect_dsc_tmp, sizeof(lv_draw_rect_dsc_t));
425 
426         lv_draw_rect(layer, &knob_rect_dsc, &slider->left_knob_area);
427     }
428 }
429 
position_knob(lv_obj_t * obj,lv_area_t * knob_area,const int32_t knob_size,const bool hor)430 static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const int32_t knob_size, const bool hor)
431 {
432     if(hor) {
433         knob_area->x1 -= (knob_size >> 1);
434         knob_area->x2 = knob_area->x1 + knob_size - 1;
435         knob_area->y1 = obj->coords.y1;
436         knob_area->y2 = obj->coords.y2;
437     }
438     else {
439         knob_area->y1 -= (knob_size >> 1);
440         knob_area->y2 = knob_area->y1 + knob_size - 1;
441         knob_area->x1 = obj->coords.x1;
442         knob_area->x2 = obj->coords.x2;
443     }
444 
445     int32_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
446     int32_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
447     int32_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
448     int32_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
449 
450     int32_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB);
451     int32_t transf_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB);
452 
453     /*Apply the paddings on the knob area*/
454     knob_area->x1 -= knob_left + transf_w;
455     knob_area->x2 += knob_right + transf_w;
456     knob_area->y1 -= knob_top + transf_h;
457     knob_area->y2 += knob_bottom + transf_h;
458 }
459 
is_slider_horizontal(lv_obj_t * obj)460 static bool is_slider_horizontal(lv_obj_t * obj)
461 {
462     lv_slider_t * slider = (lv_slider_t *)obj;
463     if(slider->bar.orientation == LV_BAR_ORIENTATION_AUTO) return lv_obj_get_width(obj) >= lv_obj_get_height(obj);
464     else if(slider->bar.orientation == LV_BAR_ORIENTATION_HORIZONTAL) return true;
465     else return false;
466 }
467 
drag_start(lv_obj_t * obj)468 static void drag_start(lv_obj_t * obj)
469 {
470     lv_slider_t * slider = (lv_slider_t *)obj;
471     lv_slider_mode_t mode = lv_slider_get_mode(obj);
472     lv_point_t p;
473     slider->dragging = true;
474     if(mode == LV_SLIDER_MODE_NORMAL || mode == LV_SLIDER_MODE_SYMMETRICAL) {
475         slider->value_to_set = &slider->bar.cur_value;
476     }
477     else if(mode == LV_SLIDER_MODE_RANGE) {
478         lv_indev_get_point(lv_indev_active(), &p);
479         lv_obj_transform_point(obj, &p, LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE_RECURSIVE);
480         const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
481         const bool is_horizontal = is_slider_horizontal(obj);
482         const bool is_reversed = slider->bar.val_reversed ^ (is_rtl && is_horizontal);
483         int32_t dist_left, dist_right;
484         if(is_horizontal) {
485             if((!is_reversed && p.x > slider->right_knob_area.x2) || (is_reversed && p.x < slider->right_knob_area.x1)) {
486                 slider->value_to_set = &slider->bar.cur_value;
487             }
488             else if((!is_reversed && p.x < slider->left_knob_area.x1) || (is_reversed && p.x > slider->left_knob_area.x2)) {
489                 slider->value_to_set = &slider->bar.start_value;
490             }
491             else {
492                 /*Calculate the distance from each knob*/
493                 dist_left = LV_ABS((slider->left_knob_area.x1 + (slider->left_knob_area.x2 - slider->left_knob_area.x1) / 2) - p.x);
494                 dist_right = LV_ABS((slider->right_knob_area.x1 + (slider->right_knob_area.x2 - slider->right_knob_area.x1) / 2) - p.x);
495                 /*Use whichever one is closer*/
496                 if(dist_right < dist_left) {
497                     slider->value_to_set = &slider->bar.cur_value;
498                     slider->left_knob_focus = 0;
499                 }
500                 else {
501                     slider->value_to_set = &slider->bar.start_value;
502                     slider->left_knob_focus = 1;
503                 }
504             }
505         }
506         else {
507             if((!is_reversed && p.y < slider->right_knob_area.y1) || (is_reversed && p.y > slider->right_knob_area.y2)) {
508                 slider->value_to_set = &slider->bar.cur_value;
509             }
510             else if((!is_reversed && p.y > slider->left_knob_area.y2) || (is_reversed && p.y < slider->left_knob_area.y1)) {
511                 slider->value_to_set = &slider->bar.start_value;
512             }
513             else {
514                 /*Calculate the distance from each knob*/
515                 dist_left = LV_ABS((slider->left_knob_area.y1 + (slider->left_knob_area.y2 - slider->left_knob_area.y1) / 2) - p.y);
516                 dist_right = LV_ABS((slider->right_knob_area.y1 + (slider->right_knob_area.y2 - slider->right_knob_area.y1) / 2) - p.y);
517 
518                 /*Use whichever one is closer*/
519                 if(dist_right < dist_left) {
520                     slider->value_to_set = &slider->bar.cur_value;
521                     slider->left_knob_focus = 0;
522                 }
523                 else {
524                     slider->value_to_set = &slider->bar.start_value;
525                     slider->left_knob_focus = 1;
526                 }
527             }
528         }
529     }
530 }
531 
update_knob_pos(lv_obj_t * obj,bool check_drag)532 static void update_knob_pos(lv_obj_t * obj, bool check_drag)
533 {
534     lv_slider_t * slider = (lv_slider_t *)obj;
535     lv_indev_t * indev = lv_indev_active();
536     if(lv_indev_get_type(indev) != LV_INDEV_TYPE_POINTER)
537         return;
538     if(lv_indev_get_scroll_obj(indev) != NULL)
539         return;
540 
541     lv_point_t p;
542     lv_indev_get_point(indev, &p);
543     lv_obj_transform_point(obj, &p, LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE_RECURSIVE);
544 
545     bool is_hor = is_slider_horizontal(obj);
546 
547     if(check_drag && !slider->dragging) {
548         int32_t ofs = is_hor ? (p.x - slider->pressed_point.x) : (p.y - slider->pressed_point.y);
549 
550         /*Stop processing when offset is below scroll_limit*/
551         if(LV_ABS(ofs) < indev->scroll_limit) {
552             return;
553         }
554     }
555 
556     if(!slider->value_to_set) {
557         /*Ready to start drag*/
558         drag_start(obj);
559     }
560 
561     int32_t new_value = 0;
562     const int32_t range = slider->bar.max_value - slider->bar.min_value;
563     const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
564     const bool is_horizontal = is_slider_horizontal(obj);
565     const bool is_reversed = slider->bar.val_reversed ^ (is_rtl && is_horizontal);
566 
567     if(is_hor) {
568         const int32_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
569         const int32_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
570         const int32_t w = lv_obj_get_width(obj);
571         const int32_t indic_w = w - bg_left - bg_right;
572 
573         if(is_reversed) {
574             /*Make the point relative to the indicator*/
575             new_value = (obj->coords.x2 - bg_right) - p.x;
576         }
577         else {
578             /*Make the point relative to the indicator*/
579             new_value = p.x - (obj->coords.x1 + bg_left);
580         }
581         if(indic_w) {
582             new_value = (new_value * range + indic_w / 2) / indic_w;
583             new_value += slider->bar.min_value;
584         }
585     }
586     else {
587         const int32_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
588         const int32_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
589         const int32_t h = lv_obj_get_height(obj);
590         const int32_t indic_h = h - bg_bottom - bg_top;
591 
592         if(is_reversed) {
593             /*Make the point relative to the indicator*/
594             new_value = p.y - (obj->coords.y1 + bg_top);
595         }
596         else {
597             /*Make the point relative to the indicator*/
598             new_value = p.y - (obj->coords.y2 + bg_bottom);
599             new_value = -new_value;
600         }
601         new_value = (new_value * range + indic_h / 2) / indic_h;
602         new_value += slider->bar.min_value;
603     }
604 
605     int32_t real_max_value = slider->bar.max_value;
606     int32_t real_min_value = slider->bar.min_value;
607     /*Figure out the min. and max. for this mode*/
608     if(slider->value_to_set == &slider->bar.start_value) {
609         real_max_value = slider->bar.cur_value;
610     }
611     else {
612         real_min_value = slider->bar.start_value;
613     }
614 
615     new_value = LV_CLAMP(real_min_value, new_value, real_max_value);
616     if(*slider->value_to_set != new_value) {
617         *slider->value_to_set = new_value;
618         if(is_hor)
619             lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
620         else
621             lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
622 
623         lv_obj_invalidate(obj);
624         lv_result_t res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
625         if(res != LV_RESULT_OK)
626             return;
627     }
628 }
629 
630 #endif
631