1 /**
2 * @file lv_scale.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_scale_private.h"
10 #include "../../core/lv_obj_private.h"
11 #include "../../core/lv_obj_class_private.h"
12 #if LV_USE_SCALE != 0
13
14 #include "../../core/lv_group.h"
15 #include "../../misc/lv_assert.h"
16 #include "../../misc/lv_math.h"
17 #include "../../draw/lv_draw_arc.h"
18
19 /*********************
20 * DEFINES
21 *********************/
22 #define MY_CLASS (&lv_scale_class)
23
24 #define LV_SCALE_LABEL_TXT_LEN (20U)
25 #define LV_SCALE_DEFAULT_ANGLE_RANGE ((uint32_t) 270U)
26 #define LV_SCALE_DEFAULT_ROTATION ((int32_t) 135U)
27 #define LV_SCALE_TICK_IDX_DEFAULT_ID ((uint32_t) 255U)
28 #define LV_SCALE_DEFAULT_LABEL_GAP ((uint32_t) 15U)
29
30 /**********************
31 * TYPEDEFS
32 **********************/
33
34 /**********************
35 * STATIC PROTOTYPES
36 **********************/
37
38 static void lv_scale_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
39 static void lv_scale_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
40 static void lv_scale_event(const lv_obj_class_t * class_p, lv_event_t * event);
41
42 static void scale_draw_main(lv_obj_t * obj, lv_event_t * event);
43 static void scale_draw_indicator(lv_obj_t * obj, lv_event_t * event);
44 static void scale_draw_label(lv_obj_t * obj, lv_event_t * event, lv_draw_label_dsc_t * label_dsc,
45 const uint32_t major_tick_idx, const int32_t tick_value, lv_point_t * tick_point_b, const uint32_t tick_idx);
46 static void scale_calculate_main_compensation(lv_obj_t * obj);
47
48 static void scale_get_center(const lv_obj_t * obj, lv_point_t * center, int32_t * arc_r);
49 static void scale_get_tick_points(lv_obj_t * obj, const uint32_t tick_idx, bool is_major_tick,
50 lv_point_t * tick_point_a, lv_point_t * tick_point_b);
51 static void scale_get_label_coords(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc, lv_point_t * tick_point,
52 lv_area_t * label_coords);
53 static void scale_set_indicator_label_properties(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
54 lv_style_t * indicator_section_style);
55 static void scale_set_line_properties(lv_obj_t * obj, lv_draw_line_dsc_t * line_dsc, lv_style_t * section_style,
56 lv_part_t part);
57 static void scale_set_arc_properties(lv_obj_t * obj, lv_draw_arc_dsc_t * arc_dsc, lv_style_t * section_style);
58 /* Helpers */
59 static void scale_find_section_tick_idx(lv_obj_t * obj);
60 static void scale_store_main_line_tick_width_compensation(lv_obj_t * obj, const uint32_t tick_idx,
61 const bool is_major_tick, const int32_t major_tick_width, const int32_t minor_tick_width);
62 static void scale_store_section_line_tick_width_compensation(lv_obj_t * obj, const bool is_major_tick,
63 lv_draw_line_dsc_t * major_tick_dsc, lv_draw_line_dsc_t * minor_tick_dsc,
64 const int32_t tick_value, const uint8_t tick_idx, lv_point_t * tick_point_a);
65 static void scale_build_custom_label_text(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
66 const uint16_t major_tick_idx);
67
68 static void scale_free_line_needle_points_cb(lv_event_t * e);
69
70 /**********************
71 * STATIC VARIABLES
72 **********************/
73
74 const lv_obj_class_t lv_scale_class = {
75 .constructor_cb = lv_scale_constructor,
76 .destructor_cb = lv_scale_destructor,
77 .event_cb = lv_scale_event,
78 .instance_size = sizeof(lv_scale_t),
79 .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
80 .base_class = &lv_obj_class,
81 .name = "scale",
82 };
83
84 /**********************
85 * MACROS
86 **********************/
87
88 /**********************
89 * GLOBAL FUNCTIONS
90 **********************/
91
lv_scale_create(lv_obj_t * parent)92 lv_obj_t * lv_scale_create(lv_obj_t * parent)
93 {
94 LV_LOG_INFO("begin");
95 lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
96 lv_obj_class_init_obj(obj);
97 return obj;
98 }
99
100 /*======================
101 * Add/remove functions
102 *=====================*/
103
104 /*
105 * New object specific "add" or "remove" functions come here
106 */
107
108 /*=====================
109 * Setter functions
110 *====================*/
111
lv_scale_set_mode(lv_obj_t * obj,lv_scale_mode_t mode)112 void lv_scale_set_mode(lv_obj_t * obj, lv_scale_mode_t mode)
113 {
114 LV_ASSERT_OBJ(obj, MY_CLASS);
115 lv_scale_t * scale = (lv_scale_t *)obj;
116
117 scale->mode = mode;
118
119 lv_obj_invalidate(obj);
120 }
121
lv_scale_set_total_tick_count(lv_obj_t * obj,uint32_t total_tick_count)122 void lv_scale_set_total_tick_count(lv_obj_t * obj, uint32_t total_tick_count)
123 {
124 LV_ASSERT_OBJ(obj, MY_CLASS);
125 lv_scale_t * scale = (lv_scale_t *)obj;
126
127 scale->total_tick_count = total_tick_count;
128
129 lv_obj_invalidate(obj);
130 }
131
lv_scale_set_major_tick_every(lv_obj_t * obj,uint32_t major_tick_every)132 void lv_scale_set_major_tick_every(lv_obj_t * obj, uint32_t major_tick_every)
133 {
134 LV_ASSERT_OBJ(obj, MY_CLASS);
135 lv_scale_t * scale = (lv_scale_t *)obj;
136
137 scale->major_tick_every = major_tick_every;
138
139 lv_obj_invalidate(obj);
140 }
141
lv_scale_set_label_show(lv_obj_t * obj,bool show_label)142 void lv_scale_set_label_show(lv_obj_t * obj, bool show_label)
143 {
144 LV_ASSERT_OBJ(obj, MY_CLASS);
145 lv_scale_t * scale = (lv_scale_t *)obj;
146
147 scale->label_enabled = show_label;
148
149 lv_obj_invalidate(obj);
150 }
151
lv_scale_set_range(lv_obj_t * obj,int32_t min,int32_t max)152 void lv_scale_set_range(lv_obj_t * obj, int32_t min, int32_t max)
153 {
154 LV_ASSERT_OBJ(obj, MY_CLASS);
155 lv_scale_t * scale = (lv_scale_t *)obj;
156
157 scale->range_min = min;
158 scale->range_max = max;
159
160 lv_obj_invalidate(obj);
161 }
162
lv_scale_set_angle_range(lv_obj_t * obj,uint32_t angle_range)163 void lv_scale_set_angle_range(lv_obj_t * obj, uint32_t angle_range)
164 {
165 LV_ASSERT_OBJ(obj, MY_CLASS);
166 lv_scale_t * scale = (lv_scale_t *)obj;
167
168 scale->angle_range = angle_range;
169
170 lv_obj_invalidate(obj);
171 }
172
lv_scale_set_rotation(lv_obj_t * obj,int32_t rotation)173 void lv_scale_set_rotation(lv_obj_t * obj, int32_t rotation)
174 {
175 LV_ASSERT_OBJ(obj, MY_CLASS);
176 lv_scale_t * scale = (lv_scale_t *)obj;
177 int32_t normalized_angle = rotation;
178
179 if(normalized_angle < 0 || normalized_angle > 360) {
180 normalized_angle = rotation % 360;
181
182 if(normalized_angle < 0) {
183 normalized_angle += 360;
184 }
185 }
186
187 scale->rotation = normalized_angle;
188 lv_obj_invalidate(obj);
189 }
190
lv_scale_set_line_needle_value(lv_obj_t * obj,lv_obj_t * needle_line,int32_t needle_length,int32_t value)191 void lv_scale_set_line_needle_value(lv_obj_t * obj, lv_obj_t * needle_line, int32_t needle_length,
192 int32_t value)
193 {
194 int32_t angle;
195 int32_t scale_width, scale_height;
196 int32_t actual_needle_length;
197 int32_t needle_length_x, needle_length_y;
198 lv_point_precise_t * needle_line_points = NULL;
199
200 LV_ASSERT_OBJ(obj, MY_CLASS);
201 lv_scale_t * scale = (lv_scale_t *)obj;
202 if((scale->mode != LV_SCALE_MODE_ROUND_INNER) &&
203 (scale->mode != LV_SCALE_MODE_ROUND_OUTER)) {
204 return;
205 }
206
207 lv_obj_align(needle_line, LV_ALIGN_TOP_LEFT, 0, 0);
208
209 scale_width = lv_obj_get_style_width(obj, LV_PART_MAIN);
210 scale_height = lv_obj_get_style_height(obj, LV_PART_MAIN);
211
212 if(scale_width != scale_height) {
213 return;
214 }
215
216 if(needle_length >= scale_width / 2) {
217 actual_needle_length = scale_width / 2;
218 }
219 else if(needle_length >= 0) {
220 actual_needle_length = needle_length;
221 }
222 else if(needle_length + scale_width / 2 < 0) {
223 actual_needle_length = 0;
224 }
225 else {
226 actual_needle_length = scale_width / 2 + needle_length;
227 }
228
229 if(value < scale->range_min) {
230 angle = 0;
231 }
232 else if(value > scale->range_max) {
233 angle = scale->angle_range;
234 }
235 else {
236 angle = scale->angle_range * (value - scale->range_min) / (scale->range_max - scale->range_min);
237 }
238
239 needle_length_x = (actual_needle_length * lv_trigo_cos(scale->rotation + angle)) >> LV_TRIGO_SHIFT;
240 needle_length_y = (actual_needle_length * lv_trigo_sin(scale->rotation + angle)) >> LV_TRIGO_SHIFT;
241
242 if(lv_line_is_point_array_mutable(needle_line) && lv_line_get_point_count(needle_line) >= 2) {
243 needle_line_points = lv_line_get_points_mutable(needle_line);
244 }
245
246 if(needle_line_points == NULL) {
247 uint32_t i;
248 uint32_t line_event_cnt = lv_obj_get_event_count(needle_line);
249 for(i = 0; i < line_event_cnt; i--) {
250 lv_event_dsc_t * dsc = lv_obj_get_event_dsc(needle_line, i);
251 if(lv_event_dsc_get_cb(dsc) == scale_free_line_needle_points_cb) {
252 needle_line_points = lv_event_dsc_get_user_data(dsc);
253 break;
254 }
255 }
256 }
257
258 if(needle_line_points == NULL) {
259 needle_line_points = lv_malloc(sizeof(lv_point_precise_t) * 2);
260 LV_ASSERT_MALLOC(needle_line_points);
261 if(needle_line_points == NULL) return;
262 lv_obj_add_event_cb(needle_line, scale_free_line_needle_points_cb, LV_EVENT_DELETE, needle_line_points);
263 }
264
265 needle_line_points[0].x = scale_width / 2;
266 needle_line_points[0].y = scale_height / 2;
267 needle_line_points[1].x = scale_width / 2 + needle_length_x;
268 needle_line_points[1].y = scale_height / 2 + needle_length_y;
269
270 lv_line_set_points_mutable(needle_line, needle_line_points, 2);
271 }
272
lv_scale_set_image_needle_value(lv_obj_t * obj,lv_obj_t * needle_img,int32_t value)273 void lv_scale_set_image_needle_value(lv_obj_t * obj, lv_obj_t * needle_img, int32_t value)
274 {
275 int32_t angle;
276 LV_ASSERT_OBJ(obj, MY_CLASS);
277 lv_scale_t * scale = (lv_scale_t *)obj;
278 if((scale->mode != LV_SCALE_MODE_ROUND_INNER) &&
279 (scale->mode != LV_SCALE_MODE_ROUND_OUTER)) {
280 return;
281 }
282
283 if(value < scale->range_min) {
284 angle = 0;
285 }
286 else if(value > scale->range_max) {
287 angle = scale->angle_range;
288 }
289 else {
290 angle = scale->angle_range * (value - scale->range_min) / (scale->range_max - scale->range_min);
291 }
292
293 lv_image_set_rotation(needle_img, (scale->rotation + angle) * 10);
294 }
295
lv_scale_set_text_src(lv_obj_t * obj,const char * txt_src[])296 void lv_scale_set_text_src(lv_obj_t * obj, const char * txt_src[])
297 {
298 LV_ASSERT_OBJ(obj, MY_CLASS);
299 lv_scale_t * scale = (lv_scale_t *)obj;
300
301 scale->txt_src = txt_src;
302 scale->custom_label_cnt = 0;
303 if(scale->txt_src) {
304 int32_t idx;
305 for(idx = 0; txt_src[idx]; ++idx) {
306 scale->custom_label_cnt++;
307 }
308 }
309
310 lv_obj_invalidate(obj);
311 }
312
lv_scale_set_post_draw(lv_obj_t * obj,bool en)313 void lv_scale_set_post_draw(lv_obj_t * obj, bool en)
314 {
315 LV_ASSERT_OBJ(obj, MY_CLASS);
316 lv_scale_t * scale = (lv_scale_t *)obj;
317
318 scale->post_draw = en;
319
320 lv_obj_invalidate(obj);
321 }
322
lv_scale_set_draw_ticks_on_top(lv_obj_t * obj,bool en)323 void lv_scale_set_draw_ticks_on_top(lv_obj_t * obj, bool en)
324 {
325 LV_ASSERT_OBJ(obj, MY_CLASS);
326 lv_scale_t * scale = (lv_scale_t *)obj;
327
328 scale->draw_ticks_on_top = en;
329
330 lv_obj_invalidate(obj);
331 }
332
lv_scale_add_section(lv_obj_t * obj)333 lv_scale_section_t * lv_scale_add_section(lv_obj_t * obj)
334 {
335 LV_ASSERT_OBJ(obj, MY_CLASS);
336
337 lv_scale_t * scale = (lv_scale_t *)obj;
338 lv_scale_section_t * section = lv_ll_ins_head(&scale->section_ll);
339 LV_ASSERT_MALLOC(section);
340 if(section == NULL) return NULL;
341
342 /* Section default values */
343 lv_memzero(section, sizeof(lv_scale_section_t));
344 section->first_tick_idx_in_section = LV_SCALE_TICK_IDX_DEFAULT_ID;
345 section->last_tick_idx_in_section = LV_SCALE_TICK_IDX_DEFAULT_ID;
346 /* Initial range is [0..-1] to make it "neutral" (i.e. will not be drawn until user
347 * sets a different range). `range_min` is already 0 from `lv_memzero()` above. */
348 section->range_max = -1;
349
350 return section;
351 }
352
lv_scale_section_set_range(lv_scale_section_t * section,int32_t min,int32_t max)353 void lv_scale_section_set_range(lv_scale_section_t * section, int32_t min, int32_t max)
354 {
355 if(NULL == section) return;
356
357 section->range_min = min;
358 section->range_max = max;
359 }
360
lv_scale_section_set_style(lv_scale_section_t * section,lv_part_t part,lv_style_t * section_part_style)361 void lv_scale_section_set_style(lv_scale_section_t * section, lv_part_t part, lv_style_t * section_part_style)
362 {
363 if(NULL == section) return;
364
365 switch(part) {
366 case LV_PART_MAIN:
367 section->main_style = section_part_style;
368 break;
369 case LV_PART_INDICATOR:
370 section->indicator_style = section_part_style;
371 break;
372 case LV_PART_ITEMS:
373 section->items_style = section_part_style;
374 break;
375 default:
376 /* Invalid part */
377 break;
378 }
379 }
380
381 /*=====================
382 * Getter functions
383 *====================*/
384
lv_scale_get_mode(lv_obj_t * obj)385 lv_scale_mode_t lv_scale_get_mode(lv_obj_t * obj)
386 {
387 lv_scale_t * scale = (lv_scale_t *)obj;
388 return scale->mode;
389 }
390
lv_scale_get_total_tick_count(lv_obj_t * obj)391 int32_t lv_scale_get_total_tick_count(lv_obj_t * obj)
392 {
393 lv_scale_t * scale = (lv_scale_t *)obj;
394 return scale->total_tick_count;
395 }
396
lv_scale_get_major_tick_every(lv_obj_t * obj)397 int32_t lv_scale_get_major_tick_every(lv_obj_t * obj)
398 {
399 lv_scale_t * scale = (lv_scale_t *)obj;
400 return scale->major_tick_every;
401 }
402
lv_scale_get_rotation(lv_obj_t * obj)403 lv_scale_mode_t lv_scale_get_rotation(lv_obj_t * obj)
404 {
405 lv_scale_t * scale = (lv_scale_t *)obj;
406 return scale->rotation;
407 }
408
lv_scale_get_label_show(lv_obj_t * obj)409 bool lv_scale_get_label_show(lv_obj_t * obj)
410 {
411 lv_scale_t * scale = (lv_scale_t *)obj;
412 return scale->label_enabled;
413 }
414
lv_scale_get_angle_range(lv_obj_t * obj)415 uint32_t lv_scale_get_angle_range(lv_obj_t * obj)
416 {
417 lv_scale_t * scale = (lv_scale_t *)obj;
418 return scale->angle_range;
419 }
420
lv_scale_get_range_min_value(lv_obj_t * obj)421 int32_t lv_scale_get_range_min_value(lv_obj_t * obj)
422 {
423 lv_scale_t * scale = (lv_scale_t *)obj;
424 return scale->range_min;
425 }
426
lv_scale_get_range_max_value(lv_obj_t * obj)427 int32_t lv_scale_get_range_max_value(lv_obj_t * obj)
428 {
429 lv_scale_t * scale = (lv_scale_t *)obj;
430 return scale->range_max;
431 }
432
433 /*=====================
434 * Other functions
435 *====================*/
436
437 /**********************
438 * STATIC FUNCTIONS
439 **********************/
440
lv_scale_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)441 static void lv_scale_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
442 {
443 LV_UNUSED(class_p);
444 LV_TRACE_OBJ_CREATE("begin");
445
446 lv_scale_t * scale = (lv_scale_t *)obj;
447
448 lv_ll_init(&scale->section_ll, sizeof(lv_scale_section_t));
449
450 scale->total_tick_count = LV_SCALE_TOTAL_TICK_COUNT_DEFAULT;
451 scale->major_tick_every = LV_SCALE_MAJOR_TICK_EVERY_DEFAULT;
452 scale->mode = LV_SCALE_MODE_HORIZONTAL_BOTTOM;
453 scale->label_enabled = LV_SCALE_LABEL_ENABLED_DEFAULT;
454 scale->angle_range = LV_SCALE_DEFAULT_ANGLE_RANGE;
455 scale->rotation = LV_SCALE_DEFAULT_ROTATION;
456 scale->range_min = 0;
457 scale->range_max = 100;
458 scale->last_tick_width = 0;
459 scale->first_tick_width = 0;
460 scale->post_draw = false;
461 scale->draw_ticks_on_top = false;
462 scale->custom_label_cnt = 0;
463 scale->txt_src = NULL;
464
465 lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
466
467 LV_TRACE_OBJ_CREATE("finished");
468 }
469
lv_scale_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)470 static void lv_scale_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
471 {
472 LV_UNUSED(class_p);
473 LV_TRACE_OBJ_CREATE("begin");
474
475 lv_scale_t * scale = (lv_scale_t *)obj;
476 lv_scale_section_t * section;
477 while(scale->section_ll.head) {
478 section = lv_ll_get_head(&scale->section_ll);
479 lv_ll_remove(&scale->section_ll, section);
480 lv_free(section);
481 }
482 lv_ll_clear(&scale->section_ll);
483
484 LV_TRACE_OBJ_CREATE("finished");
485 }
486
lv_scale_event(const lv_obj_class_t * class_p,lv_event_t * event)487 static void lv_scale_event(const lv_obj_class_t * class_p, lv_event_t * event)
488 {
489 LV_UNUSED(class_p);
490
491 /*Call the ancestor's event handler*/
492 lv_result_t res = lv_obj_event_base(MY_CLASS, event);
493 if(res != LV_RESULT_OK) return;
494
495 lv_event_code_t event_code = lv_event_get_code(event);
496 lv_obj_t * obj = lv_event_get_current_target(event);
497 lv_scale_t * scale = (lv_scale_t *) obj;
498 LV_UNUSED(scale);
499
500 if(event_code == LV_EVENT_DRAW_MAIN) {
501 if(scale->post_draw == false) {
502 scale_find_section_tick_idx(obj);
503 scale_calculate_main_compensation(obj);
504
505 if(scale->draw_ticks_on_top) {
506 scale_draw_main(obj, event);
507 scale_draw_indicator(obj, event);
508 }
509 else {
510 scale_draw_indicator(obj, event);
511 scale_draw_main(obj, event);
512 }
513 }
514 }
515 if(event_code == LV_EVENT_DRAW_POST) {
516 if(scale->post_draw == true) {
517 scale_find_section_tick_idx(obj);
518 scale_calculate_main_compensation(obj);
519
520 if(scale->draw_ticks_on_top) {
521 scale_draw_main(obj, event);
522 scale_draw_indicator(obj, event);
523 }
524 else {
525 scale_draw_indicator(obj, event);
526 scale_draw_main(obj, event);
527 }
528 }
529 }
530 else if(event_code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
531 /* NOTE: Extend scale draw size so the first tick label can be shown */
532 lv_event_set_ext_draw_size(event, 100);
533 }
534 else {
535 /* Nothing to do. Invalid event */
536 }
537 }
538
scale_draw_indicator(lv_obj_t * obj,lv_event_t * event)539 static void scale_draw_indicator(lv_obj_t * obj, lv_event_t * event)
540 {
541 lv_scale_t * scale = (lv_scale_t *)obj;
542 lv_layer_t * layer = lv_event_get_layer(event);
543
544 if(scale->total_tick_count <= 1) return;
545
546 lv_draw_label_dsc_t label_dsc;
547 lv_draw_label_dsc_init(&label_dsc);
548 label_dsc.base.layer = layer;
549 /* Formatting the labels with the configured style for LV_PART_INDICATOR */
550 lv_obj_init_draw_label_dsc(obj, LV_PART_INDICATOR, &label_dsc);
551
552 /* Major tick style */
553 lv_draw_line_dsc_t major_tick_dsc;
554 lv_draw_line_dsc_init(&major_tick_dsc);
555 major_tick_dsc.base.layer = layer;
556 lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
557 if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
558 major_tick_dsc.raw_end = 0;
559 }
560
561 /* Configure line draw descriptor for the minor tick drawing */
562 lv_draw_line_dsc_t minor_tick_dsc;
563 lv_draw_line_dsc_init(&minor_tick_dsc);
564 minor_tick_dsc.base.layer = layer;
565 lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
566
567 /* Main line style */
568 lv_draw_line_dsc_t main_line_dsc;
569 lv_draw_line_dsc_init(&main_line_dsc);
570 main_line_dsc.base.layer = layer;
571 lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &main_line_dsc);
572
573 /* These 2 values need to be signed since they are being passed
574 * to `lv_map()` which expects signed integers. */
575 const int32_t total_tick_count = scale->total_tick_count;
576 int32_t tick_idx = 0;
577 uint32_t major_tick_idx = 0U;
578 for(tick_idx = 0; tick_idx < total_tick_count; tick_idx++) {
579 /* A major tick is the one which has a label in it */
580 bool is_major_tick = false;
581 if(tick_idx % scale->major_tick_every == 0) is_major_tick = true;
582 if(is_major_tick) major_tick_idx++;
583
584 const int32_t tick_value = lv_map(tick_idx, 0, total_tick_count - 1, scale->range_min, scale->range_max);
585
586 label_dsc.base.id1 = tick_idx;
587 label_dsc.base.id2 = tick_value;
588 label_dsc.base.layer = layer;
589
590 /* Overwrite label and tick properties if tick value is within section range */
591 lv_scale_section_t * section;
592 LV_LL_READ_BACK(&scale->section_ll, section) {
593 if(section->range_min <= tick_value && section->range_max >= tick_value) {
594 if(is_major_tick) {
595 scale_set_indicator_label_properties(obj, &label_dsc, section->indicator_style);
596 scale_set_line_properties(obj, &major_tick_dsc, section->indicator_style, LV_PART_INDICATOR);
597 }
598 else {
599 scale_set_line_properties(obj, &minor_tick_dsc, section->items_style, LV_PART_ITEMS);
600 }
601 break;
602 }
603 else {
604 /* Tick is not in section, get the proper styles */
605 lv_obj_init_draw_label_dsc(obj, LV_PART_INDICATOR, &label_dsc);
606 lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
607 lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
608 }
609 }
610
611 /* The tick is represented by a line. We need two points to draw it */
612 lv_point_t tick_point_a;
613 lv_point_t tick_point_b;
614 scale_get_tick_points(obj, tick_idx, is_major_tick, &tick_point_a, &tick_point_b);
615
616 /* Setup a label if they're enabled and we're drawing a major tick */
617 if(scale->label_enabled && is_major_tick) {
618 scale_draw_label(obj, event, &label_dsc, major_tick_idx, tick_value, &tick_point_b, tick_idx);
619 }
620
621 if(is_major_tick) {
622 major_tick_dsc.p1 = lv_point_to_precise(&tick_point_a);
623 major_tick_dsc.p2 = lv_point_to_precise(&tick_point_b);
624 lv_draw_line(layer, &major_tick_dsc);
625 }
626 else {
627 minor_tick_dsc.p1 = lv_point_to_precise(&tick_point_a);
628 minor_tick_dsc.p2 = lv_point_to_precise(&tick_point_b);
629 lv_draw_line(layer, &minor_tick_dsc);
630 }
631 }
632 }
633
scale_draw_label(lv_obj_t * obj,lv_event_t * event,lv_draw_label_dsc_t * label_dsc,const uint32_t major_tick_idx,const int32_t tick_value,lv_point_t * tick_point_b,const uint32_t tick_idx)634 static void scale_draw_label(lv_obj_t * obj, lv_event_t * event, lv_draw_label_dsc_t * label_dsc,
635 const uint32_t major_tick_idx, const int32_t tick_value, lv_point_t * tick_point_b,
636 const uint32_t tick_idx)
637 {
638 lv_scale_t * scale = (lv_scale_t *)obj;
639 lv_layer_t * layer = lv_event_get_layer(event);
640
641 /* Label text setup */
642 char text_buffer[LV_SCALE_LABEL_TXT_LEN] = {0};
643 lv_area_t label_coords;
644
645 /* Check if the custom text array has element for this major tick index */
646 if(scale->txt_src) {
647 scale_build_custom_label_text(obj, label_dsc, major_tick_idx);
648 }
649 else { /* Add label with mapped values */
650 lv_snprintf(text_buffer, sizeof(text_buffer), "%" LV_PRId32, tick_value);
651 label_dsc->text = text_buffer;
652 label_dsc->text_local = 1;
653 }
654
655 int32_t translate_x = lv_obj_get_style_translate_x(obj, LV_PART_INDICATOR);
656 int32_t translate_y = lv_obj_get_style_translate_y(obj, LV_PART_INDICATOR);
657 int32_t label_rotation = lv_obj_get_style_transform_rotation(obj, LV_PART_INDICATOR);
658 int32_t translate_rotation = 0;
659
660 if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)
661 || (LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode || LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
662 lv_point_t label_origin;
663 label_origin.x = tick_point_b->x + translate_x;
664 label_origin.y = tick_point_b->y + translate_y;
665 scale_get_label_coords(obj, label_dsc, &label_origin, &label_coords);
666 label_rotation = (label_rotation & LV_SCALE_ROTATION_ANGLE_MASK);
667 }
668 else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
669 translate_rotation = lv_obj_get_style_translate_radial(obj, LV_PART_INDICATOR);
670 uint32_t label_gap = lv_obj_get_style_pad_radial(obj, LV_PART_INDICATOR) + LV_SCALE_DEFAULT_LABEL_GAP;
671
672 lv_area_t scale_area;
673 lv_obj_get_content_coords(obj, &scale_area);
674
675 /* Find the center of the scale */
676 lv_point_t center_point;
677 int32_t radius_edge = LV_MIN(lv_area_get_width(&scale_area) / 2, lv_area_get_height(&scale_area) / 2);
678 center_point.x = scale_area.x1 + radius_edge;
679 center_point.y = scale_area.y1 + radius_edge;
680
681 const int32_t major_len = lv_obj_get_style_length(obj, LV_PART_INDICATOR);
682
683 /* Also take into consideration the letter space of the style */
684 int32_t angle_upscale = ((tick_idx * scale->angle_range) * 10U) / (scale->total_tick_count - 1U) +
685 (translate_rotation * 10);
686 angle_upscale += scale->rotation * 10;
687
688 uint32_t radius_text = 0;
689 if(LV_SCALE_MODE_ROUND_INNER == scale->mode) {
690 radius_text = (radius_edge - major_len) - (label_gap + label_dsc->letter_space);
691 }
692 else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode) {
693 radius_text = (radius_edge + major_len) + (label_gap + label_dsc->letter_space);
694 }
695 else { /* Nothing to do */ }
696
697 lv_point_t point;
698 point.x = center_point.x + radius_text + translate_x;
699 point.y = center_point.y + translate_y;
700 int32_t label_rotation_temp = 0;
701
702 if(label_rotation & LV_SCALE_LABEL_ROTATE_MATCH_TICKS) {
703 label_rotation_temp = (label_rotation & LV_SCALE_ROTATION_ANGLE_MASK) + angle_upscale;
704
705 /* keep text upright if the user asked for it, otherwise it will be upside-down on half the dial */
706 if(label_rotation & LV_SCALE_LABEL_ROTATE_KEEP_UPRIGHT) {
707 while(label_rotation_temp > 3600) {
708 label_rotation_temp -= 3600;
709 }
710 if(label_rotation_temp > 900 && label_rotation_temp < 2400) {
711 label_rotation_temp += 1800;
712 }
713 }
714 label_rotation = label_rotation_temp;
715 }
716 else {
717 label_rotation = label_rotation & LV_SCALE_ROTATION_ANGLE_MASK;
718 }
719
720 lv_point_transform(&point, angle_upscale, LV_SCALE_NONE, LV_SCALE_NONE, ¢er_point, false);
721 scale_get_label_coords(obj, label_dsc, &point, &label_coords);
722 }
723 /* Invalid mode */
724 else {
725 return;
726 }
727
728 if(label_rotation > 0) {
729 /*Draw the label to a new layer and draw the layer rotated*/
730 lv_layer_t * layer_label = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, &label_coords);
731 lv_draw_label(layer_label, label_dsc, &label_coords);
732
733 lv_point_t pivot_point;
734 /* Set pivot point to the center of the label so it matches the scale curve */
735 pivot_point.x = lv_area_get_width(&label_coords) / 2;
736 pivot_point.y = lv_area_get_height(&label_coords) / 2;
737
738 lv_draw_image_dsc_t layer_draw_dsc;
739 lv_draw_image_dsc_init(&layer_draw_dsc);
740 layer_draw_dsc.src = layer_label;
741 layer_draw_dsc.rotation = label_rotation;
742 layer_draw_dsc.pivot = pivot_point;
743 lv_draw_layer(layer, &layer_draw_dsc, &label_coords);
744 }
745 else {
746 lv_draw_label(layer, label_dsc, &label_coords);
747 }
748 }
749
scale_calculate_main_compensation(lv_obj_t * obj)750 static void scale_calculate_main_compensation(lv_obj_t * obj)
751 {
752 lv_scale_t * scale = (lv_scale_t *)obj;
753
754 const uint32_t total_tick_count = scale->total_tick_count;
755
756 if(total_tick_count <= 1) return;
757 /* Not supported in round modes */
758 if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) return;
759
760 /* Major tick style */
761 lv_draw_line_dsc_t major_tick_dsc;
762 lv_draw_line_dsc_init(&major_tick_dsc);
763 lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
764
765 /* Configure line draw descriptor for the minor tick drawing */
766 lv_draw_line_dsc_t minor_tick_dsc;
767 lv_draw_line_dsc_init(&minor_tick_dsc);
768 lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
769
770 uint32_t tick_idx = 0;
771 for(tick_idx = 0; tick_idx < total_tick_count; tick_idx++) {
772
773 const bool is_major_tick = tick_idx % scale->major_tick_every == 0;
774
775 const int32_t tick_value = lv_map(tick_idx, 0, total_tick_count - 1, scale->range_min, scale->range_max);
776
777 /* Overwrite label and tick properties if tick value is within section range */
778 lv_scale_section_t * section;
779 LV_LL_READ_BACK(&scale->section_ll, section) {
780 if(section->range_min <= tick_value && section->range_max >= tick_value) {
781 if(is_major_tick) {
782 scale_set_line_properties(obj, &major_tick_dsc, section->indicator_style, LV_PART_INDICATOR);
783 }
784 else {
785 scale_set_line_properties(obj, &minor_tick_dsc, section->items_style, LV_PART_ITEMS);
786 }
787 break;
788 }
789 else {
790 /* Tick is not in section, get the proper styles */
791 lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
792 lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
793 }
794 }
795
796 /* The tick is represented by a line. We need two points to draw it */
797 lv_point_t tick_point_a;
798 lv_point_t tick_point_b;
799 scale_get_tick_points(obj, tick_idx, is_major_tick, &tick_point_a, &tick_point_b);
800
801 /* Store initial and last tick widths to be used in the main line drawing */
802 scale_store_main_line_tick_width_compensation(obj, tick_idx, is_major_tick, major_tick_dsc.width, minor_tick_dsc.width);
803 /* Store the first and last section tick vertical/horizontal position */
804 scale_store_section_line_tick_width_compensation(obj, is_major_tick, &major_tick_dsc, &minor_tick_dsc,
805 tick_value, tick_idx, &tick_point_a);
806 }
807 }
808
scale_draw_main(lv_obj_t * obj,lv_event_t * event)809 static void scale_draw_main(lv_obj_t * obj, lv_event_t * event)
810 {
811 lv_scale_t * scale = (lv_scale_t *)obj;
812 lv_layer_t * layer = lv_event_get_layer(event);
813
814 if(scale->total_tick_count <= 1) return;
815
816 if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)
817 || (LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode || LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
818
819 /* Configure both line and label draw descriptors for the tick and label drawings */
820 lv_draw_line_dsc_t line_dsc;
821 lv_draw_line_dsc_init(&line_dsc);
822 line_dsc.base.layer = layer;
823 lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
824
825 /* Get style properties so they can be used in the main line drawing */
826 const int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
827 const int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
828 const int32_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;
829 const int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
830 const int32_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
831
832 int32_t x_ofs = 0;
833 int32_t y_ofs = 0;
834
835 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) {
836 x_ofs = obj->coords.x2 + (line_dsc.width / 2) - pad_right;
837 y_ofs = obj->coords.y1 + pad_top;
838 }
839 else if(LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
840 x_ofs = obj->coords.x1 + (line_dsc.width / 2) + pad_left;
841 y_ofs = obj->coords.y1 + pad_top;
842 }
843 if(LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) {
844 x_ofs = obj->coords.x1 + pad_right;
845 y_ofs = obj->coords.y1 + (line_dsc.width / 2) + pad_top;
846 }
847 else if(LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode) {
848 x_ofs = obj->coords.x1 + pad_left;
849 y_ofs = obj->coords.y2 + (line_dsc.width / 2) - pad_bottom;
850 }
851 else { /* Nothing to do */ }
852
853 lv_point_t main_line_point_a;
854 lv_point_t main_line_point_b;
855
856 /* Setup the tick points */
857 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
858 main_line_point_a.x = x_ofs - 1;
859 main_line_point_a.y = y_ofs;
860 main_line_point_b.x = x_ofs - 1;
861 main_line_point_b.y = obj->coords.y2 - pad_bottom;
862
863 /* Adjust main line with initial and last tick width */
864 main_line_point_a.y -= scale->last_tick_width / 2;
865 main_line_point_b.y += scale->first_tick_width / 2;
866 }
867 else {
868 main_line_point_a.x = x_ofs;
869 main_line_point_a.y = y_ofs;
870 /* X of second point starts at the edge of the object minus the left pad */
871 main_line_point_b.x = obj->coords.x2 - (pad_left);
872 main_line_point_b.y = y_ofs;
873
874 /* Adjust main line with initial and last tick width */
875 main_line_point_a.x -= scale->last_tick_width / 2;
876 main_line_point_b.x += scale->first_tick_width / 2;
877 }
878
879 line_dsc.p1 = lv_point_to_precise(&main_line_point_a);
880 line_dsc.p2 = lv_point_to_precise(&main_line_point_b);
881 lv_draw_line(layer, &line_dsc);
882
883 lv_scale_section_t * section;
884 LV_LL_READ_BACK(&scale->section_ll, section) {
885 lv_draw_line_dsc_t section_line_dsc;
886 lv_draw_line_dsc_init(§ion_line_dsc);
887 section_line_dsc.base.layer = layer;
888 lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, §ion_line_dsc);
889
890 /* Calculate the points of the section line */
891 lv_point_t section_point_a;
892 lv_point_t section_point_b;
893
894 const int32_t first_tick_width_halved = (int32_t)(section->first_tick_in_section_width / 2);
895 const int32_t last_tick_width_halved = (int32_t)(section->last_tick_in_section_width / 2);
896
897 /* Calculate the position of the section based on the ticks (first and last) index */
898 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
899 /* Calculate position of the first tick in the section */
900 section_point_a.x = main_line_point_a.x;
901 section_point_a.y = section->first_tick_in_section.y + first_tick_width_halved;
902
903 /* Calculate position of the last tick in the section */
904 section_point_b.x = main_line_point_a.x;
905 section_point_b.y = section->last_tick_in_section.y - last_tick_width_halved;
906 }
907 else {
908 /* Calculate position of the first tick in the section */
909 section_point_a.x = section->first_tick_in_section.x - first_tick_width_halved;
910 section_point_a.y = main_line_point_a.y;
911
912 /* Calculate position of the last tick in the section */
913 section_point_b.x = section->last_tick_in_section.x + last_tick_width_halved;
914 section_point_b.y = main_line_point_a.y;
915 }
916
917 scale_set_line_properties(obj, §ion_line_dsc, section->main_style, LV_PART_MAIN);
918
919 section_line_dsc.p1.x = section_point_a.x;
920 section_line_dsc.p1.y = section_point_a.y;
921 section_line_dsc.p2.x = section_point_b.x;
922 section_line_dsc.p2.y = section_point_b.y;
923 lv_draw_line(layer, §ion_line_dsc);
924 }
925 }
926 else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
927 /* Configure arc draw descriptors for the main part */
928 lv_draw_arc_dsc_t arc_dsc;
929 lv_draw_arc_dsc_init(&arc_dsc);
930 arc_dsc.base.layer = layer;
931 lv_obj_init_draw_arc_dsc(obj, LV_PART_MAIN, &arc_dsc);
932
933 lv_point_t arc_center;
934 int32_t arc_radius;
935 scale_get_center(obj, &arc_center, &arc_radius);
936
937 /* TODO: Add compensation for the width of the first and last tick over the arc */
938 const int32_t start_angle = lv_map(scale->range_min, scale->range_min, scale->range_max, scale->rotation,
939 scale->rotation + scale->angle_range);
940 const int32_t end_angle = lv_map(scale->range_max, scale->range_min, scale->range_max, scale->rotation,
941 scale->rotation + scale->angle_range);
942
943 arc_dsc.center = arc_center;
944 arc_dsc.radius = arc_radius;
945 arc_dsc.start_angle = start_angle;
946 arc_dsc.end_angle = end_angle;
947
948 lv_draw_arc(layer, &arc_dsc);
949
950 lv_scale_section_t * section;
951 LV_LL_READ_BACK(&scale->section_ll, section) {
952 lv_draw_arc_dsc_t main_arc_section_dsc;
953 lv_draw_arc_dsc_init(&main_arc_section_dsc);
954 main_arc_section_dsc.base.layer = layer;
955 lv_obj_init_draw_arc_dsc(obj, LV_PART_MAIN, &main_arc_section_dsc);
956
957 lv_point_t section_arc_center;
958 int32_t section_arc_radius;
959 scale_get_center(obj, §ion_arc_center, §ion_arc_radius);
960
961 /* TODO: Add compensation for the width of the first and last tick over the arc */
962 const int32_t section_start_angle = lv_map(section->range_min, scale->range_min, scale->range_max, scale->rotation,
963 scale->rotation + scale->angle_range);
964 const int32_t section_end_angle = lv_map(section->range_max, scale->range_min, scale->range_max, scale->rotation,
965 scale->rotation + scale->angle_range);
966
967 scale_set_arc_properties(obj, &main_arc_section_dsc, section->main_style);
968
969 main_arc_section_dsc.center = section_arc_center;
970 main_arc_section_dsc.radius = section_arc_radius;
971 main_arc_section_dsc.start_angle = section_start_angle;
972 main_arc_section_dsc.end_angle = section_end_angle;
973
974 lv_draw_arc(layer, &main_arc_section_dsc);
975 }
976 }
977 else { /* Nothing to do */ }
978 }
979
980 /**
981 * Get center point and radius of scale arc
982 * @param obj pointer to a scale object
983 * @param center pointer to center
984 * @param arc_r pointer to arc radius
985 */
scale_get_center(const lv_obj_t * obj,lv_point_t * center,int32_t * arc_r)986 static void scale_get_center(const lv_obj_t * obj, lv_point_t * center, int32_t * arc_r)
987 {
988 int32_t left_bg = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
989 int32_t right_bg = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
990 int32_t top_bg = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
991 int32_t bottom_bg = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
992
993 int32_t r = (LV_MIN(lv_obj_get_width(obj) - left_bg - right_bg, lv_obj_get_height(obj) - top_bg - bottom_bg)) / 2;
994
995 center->x = obj->coords.x1 + r + left_bg;
996 center->y = obj->coords.y1 + r + top_bg;
997
998 if(arc_r) *arc_r = r;
999 }
1000
1001 /**
1002 * Get points for ticks
1003 *
1004 * In order to draw ticks we need two points, this interface returns both points for all scale modes.
1005 *
1006 * @param obj pointer to a scale object
1007 * @param tick_idx index of the current tick
1008 * @param is_major_tick true if tick_idx is a major tick
1009 * @param tick_point_a pointer to point 'a' of the tick
1010 * @param tick_point_b pointer to point 'b' of the tick
1011 */
scale_get_tick_points(lv_obj_t * obj,const uint32_t tick_idx,bool is_major_tick,lv_point_t * tick_point_a,lv_point_t * tick_point_b)1012 static void scale_get_tick_points(lv_obj_t * obj, const uint32_t tick_idx, bool is_major_tick,
1013 lv_point_t * tick_point_a, lv_point_t * tick_point_b)
1014 {
1015 lv_scale_t * scale = (lv_scale_t *)obj;
1016
1017 /* Main line style */
1018 lv_draw_line_dsc_t main_line_dsc;
1019 lv_draw_line_dsc_init(&main_line_dsc);
1020 lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &main_line_dsc);
1021
1022 int32_t minor_len = 0;
1023 int32_t major_len = 0;
1024 int32_t radial_offset = 0;
1025
1026 if(is_major_tick) {
1027 major_len = lv_obj_get_style_length(obj, LV_PART_INDICATOR);
1028 radial_offset = lv_obj_get_style_radial_offset(obj, LV_PART_INDICATOR);
1029 }
1030 else {
1031 minor_len = lv_obj_get_style_length(obj, LV_PART_ITEMS);
1032 radial_offset = lv_obj_get_style_radial_offset(obj, LV_PART_ITEMS);
1033 }
1034
1035 if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)
1036 || (LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode || LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
1037
1038 /* Get style properties so they can be used in the tick and label drawing */
1039 const int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
1040 const int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
1041 const int32_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;
1042 const int32_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
1043 const int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
1044 const int32_t tick_pad_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS);
1045 const int32_t tick_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS);
1046 const int32_t tick_pad_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS);
1047 const int32_t tick_pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS);
1048
1049 int32_t x_ofs = 0;
1050 int32_t y_ofs = 0;
1051
1052 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) {
1053 x_ofs = obj->coords.x2 + (main_line_dsc.width / 2) - pad_right;
1054 y_ofs = obj->coords.y1 + (pad_top + tick_pad_top);
1055 }
1056 else if(LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1057 x_ofs = obj->coords.x1 + (main_line_dsc.width / 2) + pad_left;
1058 y_ofs = obj->coords.y1 + (pad_top + tick_pad_top);
1059 }
1060 else if(LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) {
1061 x_ofs = obj->coords.x1 + (pad_right + tick_pad_right);
1062 y_ofs = obj->coords.y1 + (main_line_dsc.width / 2) + pad_top;
1063 }
1064 /* LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode */
1065 else {
1066 x_ofs = obj->coords.x1 + (pad_left + tick_pad_left);
1067 y_ofs = obj->coords.y2 + (main_line_dsc.width / 2) - pad_bottom;
1068 }
1069
1070 /* Adjust length when tick will be drawn on horizontal top or vertical right scales */
1071 if((LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1072 if(is_major_tick) {
1073 major_len *= -1;
1074 }
1075 else {
1076 minor_len *= -1;
1077 }
1078 }
1079 else { /* Nothing to do */ }
1080
1081 const int32_t tick_length = is_major_tick ? major_len : minor_len;
1082 /* NOTE
1083 * Minus 1 because tick count starts at 0
1084 * TODO
1085 * What if total_tick_count is 1? This will lead to an division by 0 further down */
1086 const uint32_t tmp_tick_count = scale->total_tick_count - 1U;
1087
1088 /* Calculate the position of the tick points based on the mode and tick index */
1089 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1090 /* Vertical position starts at y2 of the scale main line, we start at y2 because the ticks are drawn from bottom to top */
1091 int32_t vertical_position = obj->coords.y2 - (pad_bottom + tick_pad_bottom);
1092
1093 /* Position the last tick */
1094 if(tmp_tick_count == tick_idx) {
1095 vertical_position = y_ofs;
1096 }
1097 /* Otherwise adjust the tick position depending of its index and number of ticks on the scale */
1098 else if(0 != tick_idx) {
1099 const int32_t scale_total_height = lv_obj_get_height(obj) - (pad_top + pad_bottom + tick_pad_top + tick_pad_bottom);
1100 const int32_t offset = ((int32_t) tick_idx * (int32_t) scale_total_height) / (int32_t)(tmp_tick_count);
1101 vertical_position -= offset;
1102 }
1103 else { /* Nothing to do */ }
1104
1105 tick_point_a->x = x_ofs - 1; /* Move extra pixel out of scale boundary */
1106 tick_point_a->y = vertical_position;
1107 tick_point_b->x = tick_point_a->x - tick_length;
1108 tick_point_b->y = vertical_position;
1109 }
1110 else {
1111 /* Horizontal position starts at x1 of the scale main line */
1112 int32_t horizontal_position = x_ofs;
1113
1114 /* Position the last tick */
1115 if(tmp_tick_count == tick_idx) {
1116 horizontal_position = obj->coords.x2 - (pad_left + tick_pad_left);
1117 }
1118 /* Otherwise adjust the tick position depending of its index and number of ticks on the scale */
1119 else if(0U != tick_idx) {
1120 const int32_t scale_total_width = lv_obj_get_width(obj) - (pad_right + pad_left + tick_pad_right + tick_pad_left);
1121 const int32_t offset = ((int32_t) tick_idx * (int32_t) scale_total_width) / (int32_t)(tmp_tick_count);
1122 horizontal_position += offset;
1123 }
1124 else { /* Nothing to do */ }
1125
1126 tick_point_a->x = horizontal_position;
1127 tick_point_a->y = y_ofs;
1128 tick_point_b->x = horizontal_position;
1129 tick_point_b->y = tick_point_a->y + tick_length;
1130 }
1131 }
1132 else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
1133 lv_area_t scale_area;
1134 lv_obj_get_content_coords(obj, &scale_area);
1135
1136 /* Find the center of the scale */
1137 lv_point_t center_point;
1138 const int32_t radius_edge = LV_MIN(lv_area_get_width(&scale_area) / 2, lv_area_get_height(&scale_area) / 2);
1139 center_point.x = scale_area.x1 + radius_edge;
1140 center_point.y = scale_area.y1 + radius_edge;
1141
1142 int32_t angle_upscale = (int32_t)((tick_idx * scale->angle_range) * 10U) / (scale->total_tick_count - 1U);
1143 angle_upscale += scale->rotation * 10;
1144
1145 /* Draw a little bit longer lines to be sure the mask will clip them correctly
1146 * and to get a better precision. Adding the main line width to the calculation so we don't have gaps
1147 * between the arc and the ticks */
1148 int32_t point_closer_to_arc = 0;
1149 int32_t adjusted_radio_with_tick_len = 0;
1150 if(LV_SCALE_MODE_ROUND_INNER == scale->mode) {
1151 point_closer_to_arc = radius_edge - main_line_dsc.width;
1152 adjusted_radio_with_tick_len = point_closer_to_arc - (is_major_tick ? major_len : minor_len);
1153 }
1154 /* LV_SCALE_MODE_ROUND_OUTER == scale->mode */
1155 else {
1156 point_closer_to_arc = radius_edge - main_line_dsc.width;
1157 adjusted_radio_with_tick_len = point_closer_to_arc + (is_major_tick ? major_len : minor_len);
1158 }
1159
1160 tick_point_a->x = center_point.x + point_closer_to_arc + radial_offset;
1161 tick_point_a->y = center_point.y;
1162 lv_point_transform(tick_point_a, angle_upscale, LV_SCALE_NONE, LV_SCALE_NONE, ¢er_point, false);
1163
1164 tick_point_b->x = center_point.x + adjusted_radio_with_tick_len + radial_offset;
1165 tick_point_b->y = center_point.y;
1166 lv_point_transform(tick_point_b, angle_upscale, LV_SCALE_NONE, LV_SCALE_NONE, ¢er_point, false);
1167 }
1168 else { /* Nothing to do */ }
1169 }
1170
1171 /**
1172 * Get coordinates for label
1173 *
1174 * @param obj pointer to a scale object
1175 * @param label_dsc pointer to label descriptor
1176 * @param tick_point pointer to reference tick
1177 * @param label_coords pointer to label coordinates output
1178 */
scale_get_label_coords(lv_obj_t * obj,lv_draw_label_dsc_t * label_dsc,lv_point_t * tick_point,lv_area_t * label_coords)1179 static void scale_get_label_coords(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc, lv_point_t * tick_point,
1180 lv_area_t * label_coords)
1181 {
1182 lv_scale_t * scale = (lv_scale_t *)obj;
1183
1184 /* Reserve appropriate size for the tick label */
1185 lv_point_t label_size;
1186 lv_text_get_size(&label_size, label_dsc->text,
1187 label_dsc->font, label_dsc->letter_space, label_dsc->line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
1188
1189 /* Set the label draw area at some distance of the major tick */
1190 if((LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) || (LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
1191 label_coords->x1 = tick_point->x - (label_size.x / 2);
1192 label_coords->x2 = tick_point->x + (label_size.x / 2);
1193
1194 if(LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) {
1195 label_coords->y1 = tick_point->y + lv_obj_get_style_pad_bottom(obj, LV_PART_INDICATOR);
1196 label_coords->y2 = label_coords->y1 + label_size.y;
1197 }
1198 else {
1199 label_coords->y2 = tick_point->y - lv_obj_get_style_pad_top(obj, LV_PART_INDICATOR);
1200 label_coords->y1 = label_coords->y2 - label_size.y;
1201 }
1202 }
1203 else if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1204 label_coords->y1 = tick_point->y - (label_size.y / 2);
1205 label_coords->y2 = tick_point->y + (label_size.y / 2);
1206
1207 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) {
1208 label_coords->x1 = tick_point->x - label_size.x - lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
1209 label_coords->x2 = tick_point->x - lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
1210 }
1211 else {
1212 label_coords->x1 = tick_point->x + lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
1213 label_coords->x2 = tick_point->x + label_size.x + lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
1214 }
1215 }
1216 else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
1217 label_coords->x1 = tick_point->x - (label_size.x / 2);
1218 label_coords->y1 = tick_point->y - (label_size.y / 2);
1219 label_coords->x2 = label_coords->x1 + label_size.x;
1220 label_coords->y2 = label_coords->y1 + label_size.y;
1221 }
1222 else { /* Nothing to do */ }
1223 }
1224
1225 /**
1226 * Set line properties
1227 *
1228 * Checks if the line has a custom section configuration or not and sets the properties accordingly.
1229 *
1230 * @param obj pointer to a scale object
1231 * @param line_dsc pointer to line descriptor
1232 * @param items_section_style pointer to indicator section style
1233 * @param part line part, example: LV_PART_INDICATOR, LV_PART_ITEMS, LV_PART_MAIN
1234 */
scale_set_line_properties(lv_obj_t * obj,lv_draw_line_dsc_t * line_dsc,lv_style_t * section_style,lv_part_t part)1235 static void scale_set_line_properties(lv_obj_t * obj, lv_draw_line_dsc_t * line_dsc, lv_style_t * section_style,
1236 lv_part_t part)
1237 {
1238 if(section_style) {
1239 lv_style_value_t value;
1240 lv_style_res_t res;
1241
1242 /* Line width */
1243 res = lv_style_get_prop(section_style, LV_STYLE_LINE_WIDTH, &value);
1244 if(res == LV_STYLE_RES_FOUND) {
1245 line_dsc->width = (int32_t)value.num;
1246 }
1247 else {
1248 line_dsc->width = lv_obj_get_style_line_width(obj, part);
1249 }
1250
1251 /* Line color */
1252 res = lv_style_get_prop(section_style, LV_STYLE_LINE_COLOR, &value);
1253 if(res == LV_STYLE_RES_FOUND) {
1254 line_dsc->color = value.color;
1255 }
1256 else {
1257 line_dsc->color = lv_obj_get_style_line_color(obj, part);
1258 }
1259
1260 /* Line opa */
1261 res = lv_style_get_prop(section_style, LV_STYLE_LINE_OPA, &value);
1262 if(res == LV_STYLE_RES_FOUND) {
1263 line_dsc->opa = (lv_opa_t)value.num;
1264 }
1265 else {
1266 line_dsc->opa = lv_obj_get_style_line_opa(obj, part);
1267 }
1268 }
1269 else {
1270 line_dsc->color = lv_obj_get_style_line_color(obj, part);
1271 line_dsc->opa = lv_obj_get_style_line_opa(obj, part);
1272 line_dsc->width = lv_obj_get_style_line_width(obj, part);
1273 }
1274 }
1275
1276 /**
1277 * Set arc properties
1278 *
1279 * Checks if the arc has a custom section configuration or not and sets the properties accordingly.
1280 *
1281 * @param obj pointer to a scale object
1282 * @param arc_dsc pointer to arc descriptor
1283 * @param items_section_style pointer to indicator section style
1284 */
scale_set_arc_properties(lv_obj_t * obj,lv_draw_arc_dsc_t * arc_dsc,lv_style_t * section_style)1285 static void scale_set_arc_properties(lv_obj_t * obj, lv_draw_arc_dsc_t * arc_dsc, lv_style_t * section_style)
1286 {
1287 if(section_style) {
1288 lv_style_value_t value;
1289 lv_style_res_t res;
1290
1291 /* arc width */
1292 res = lv_style_get_prop(section_style, LV_STYLE_ARC_WIDTH, &value);
1293 if(res == LV_STYLE_RES_FOUND) {
1294 arc_dsc->width = (int32_t)value.num;
1295 }
1296 else {
1297 arc_dsc->width = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
1298 }
1299
1300 /* arc color */
1301 res = lv_style_get_prop(section_style, LV_STYLE_ARC_COLOR, &value);
1302 if(res == LV_STYLE_RES_FOUND) {
1303 arc_dsc->color = value.color;
1304 }
1305 else {
1306 arc_dsc->color = lv_obj_get_style_arc_color(obj, LV_PART_MAIN);
1307 }
1308
1309 /* arc opa */
1310 res = lv_style_get_prop(section_style, LV_STYLE_ARC_OPA, &value);
1311 if(res == LV_STYLE_RES_FOUND) {
1312 arc_dsc->opa = (lv_opa_t)value.num;
1313 }
1314 else {
1315 arc_dsc->opa = lv_obj_get_style_arc_opa(obj, LV_PART_MAIN);
1316 }
1317
1318 /* arc rounded */
1319 res = lv_style_get_prop(section_style, LV_STYLE_ARC_ROUNDED, &value);
1320 if(res == LV_STYLE_RES_FOUND) {
1321 arc_dsc->rounded = (uint8_t)value.num;
1322 }
1323 else {
1324 arc_dsc->rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_MAIN);
1325 }
1326
1327 /* arc image src */
1328 res = lv_style_get_prop(section_style, LV_STYLE_ARC_IMAGE_SRC, &value);
1329 if(res == LV_STYLE_RES_FOUND) {
1330 arc_dsc->img_src = (const void *)value.ptr;
1331 }
1332 else {
1333 arc_dsc->img_src = lv_obj_get_style_arc_image_src(obj, LV_PART_MAIN);
1334 }
1335 }
1336 else {
1337 arc_dsc->color = lv_obj_get_style_arc_color(obj, LV_PART_MAIN);
1338 arc_dsc->opa = lv_obj_get_style_arc_opa(obj, LV_PART_MAIN);
1339 arc_dsc->width = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
1340 arc_dsc->rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_MAIN);
1341 arc_dsc->img_src = lv_obj_get_style_arc_image_src(obj, LV_PART_MAIN);
1342 }
1343 }
1344
1345 /**
1346 * Set indicator label properties
1347 *
1348 * Checks if the indicator has a custom section configuration or not and sets the properties accordingly.
1349 *
1350 * @param obj pointer to a scale object
1351 * @param label_dsc pointer to label descriptor
1352 * @param items_section_style pointer to indicator section style
1353 */
scale_set_indicator_label_properties(lv_obj_t * obj,lv_draw_label_dsc_t * label_dsc,lv_style_t * indicator_section_style)1354 static void scale_set_indicator_label_properties(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
1355 lv_style_t * indicator_section_style)
1356 {
1357 if(indicator_section_style) {
1358 lv_style_value_t value;
1359 lv_style_res_t res;
1360
1361 /* Text color */
1362 res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_COLOR, &value);
1363 if(res == LV_STYLE_RES_FOUND) {
1364 label_dsc->color = value.color;
1365 }
1366 else {
1367 label_dsc->color = lv_obj_get_style_text_color(obj, LV_PART_INDICATOR);
1368 }
1369
1370 /* Text opa */
1371 res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_OPA, &value);
1372 if(res == LV_STYLE_RES_FOUND) {
1373 label_dsc->opa = (lv_opa_t)value.num;
1374 }
1375 else {
1376 label_dsc->opa = lv_obj_get_style_text_opa(obj, LV_PART_INDICATOR);
1377 }
1378
1379 /* Text letter space */
1380 res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_LETTER_SPACE, &value);
1381 if(res == LV_STYLE_RES_FOUND) {
1382 label_dsc->letter_space = (int32_t)value.num;
1383 }
1384 else {
1385 label_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_INDICATOR);
1386 }
1387
1388 /* Text font */
1389 res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_FONT, &value);
1390 if(res == LV_STYLE_RES_FOUND) {
1391 label_dsc->font = (const lv_font_t *)value.ptr;
1392 }
1393 else {
1394 label_dsc->font = lv_obj_get_style_text_font(obj, LV_PART_INDICATOR);
1395 }
1396 }
1397 else {
1398 /* If label is not within a range then get the indicator style */
1399 label_dsc->color = lv_obj_get_style_text_color(obj, LV_PART_INDICATOR);
1400 label_dsc->opa = lv_obj_get_style_text_opa(obj, LV_PART_INDICATOR);
1401 label_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_INDICATOR);
1402 label_dsc->font = lv_obj_get_style_text_font(obj, LV_PART_INDICATOR);
1403 }
1404 }
1405
scale_find_section_tick_idx(lv_obj_t * obj)1406 static void scale_find_section_tick_idx(lv_obj_t * obj)
1407 {
1408 lv_scale_t * scale = (lv_scale_t *)obj;
1409
1410 const int32_t min_out = scale->range_min;
1411 const int32_t max_out = scale->range_max;
1412 const uint32_t total_tick_count = scale->total_tick_count;
1413
1414 /* Section handling */
1415 uint32_t tick_idx = 0;
1416 for(tick_idx = 0; tick_idx < total_tick_count; tick_idx++) {
1417 bool is_major_tick = false;
1418 if(tick_idx % scale->major_tick_every == 0) is_major_tick = true;
1419
1420 const int32_t tick_value = lv_map(tick_idx, 0, total_tick_count - 1, min_out, max_out);
1421
1422 lv_scale_section_t * section;
1423 LV_LL_READ_BACK(&scale->section_ll, section) {
1424 if(section->range_min <= tick_value && section->range_max >= tick_value) {
1425 if(LV_SCALE_TICK_IDX_DEFAULT_ID == section->first_tick_idx_in_section) {
1426 section->first_tick_idx_in_section = tick_idx;
1427 section->first_tick_idx_is_major = is_major_tick;
1428 }
1429 if(LV_SCALE_TICK_IDX_DEFAULT_ID == section->last_tick_idx_in_section) {
1430 /* This gets it initialized when the beginning and ending range values are the same. */
1431 section->last_tick_idx_in_section = tick_idx;
1432 section->last_tick_idx_is_major = is_major_tick;
1433 }
1434 /* Now keep setting the `last_tick_idx_...` values as we
1435 * proceed through the `for` loop so it is left with the
1436 * actual last-tick value that is within the Scale's range. */
1437 else if(section->first_tick_idx_in_section != tick_idx) {
1438 section->last_tick_idx_in_section = tick_idx;
1439 section->last_tick_idx_is_major = is_major_tick;
1440 }
1441 }
1442 else {
1443 /* `tick_value` is outside Section's range.
1444 * Nothing to do. */
1445 }
1446 }
1447 }
1448 }
1449
1450 /**
1451 * Stores the width of the initial and last tick of the main line
1452 *
1453 * This width is used to compensate the main line drawing taking into consideration the widths of both ticks
1454 *
1455 * @param obj pointer to a scale object
1456 * @param tick_idx index of the current tick
1457 * @param is_major_tick true if tick_idx is a major tick
1458 * @param major_tick_width width of the major tick
1459 * @param minor_tick_width width of the minor tick
1460 */
scale_store_main_line_tick_width_compensation(lv_obj_t * obj,const uint32_t tick_idx,const bool is_major_tick,const int32_t major_tick_width,const int32_t minor_tick_width)1461 static void scale_store_main_line_tick_width_compensation(lv_obj_t * obj, const uint32_t tick_idx,
1462 const bool is_major_tick, const int32_t major_tick_width, const int32_t minor_tick_width)
1463 {
1464 lv_scale_t * scale = (lv_scale_t *)obj;
1465 const bool is_first_tick = 0U == tick_idx;
1466 const bool is_last_tick = scale->total_tick_count == tick_idx;
1467 const int32_t tick_width = is_major_tick ? major_tick_width : minor_tick_width;
1468
1469 /* Exit early if tick_idx is neither the first nor last tick on the main line */
1470 if(((!is_last_tick) && (!is_first_tick))
1471 /* Exit early if scale mode is round. It doesn't support main line compensation */
1472 || ((LV_SCALE_MODE_ROUND_INNER == scale->mode) || (LV_SCALE_MODE_ROUND_OUTER == scale->mode))) {
1473 return;
1474 }
1475
1476 if(is_last_tick) {
1477 /* Mode is vertical */
1478 if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1479 scale->last_tick_width = tick_width;
1480 }
1481 /* Mode is horizontal */
1482 else {
1483 scale->first_tick_width = tick_width;
1484 }
1485 }
1486 /* is_first_tick */
1487 else {
1488 /* Mode is vertical */
1489 if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1490 scale->first_tick_width = tick_width;
1491 }
1492 /* Mode is horizontal */
1493 else {
1494 scale->last_tick_width = tick_width;
1495 }
1496 }
1497 }
1498
1499 /**
1500 * Sets the text of the tick label descriptor when using custom labels
1501 *
1502 * Sets the text pointer when valid custom label is available, otherwise set it to NULL.
1503 *
1504 * @param obj pointer to a scale object
1505 * @param label_dsc pointer to the label descriptor
1506 * @param major_tick_idx index of the current major tick
1507 */
scale_build_custom_label_text(lv_obj_t * obj,lv_draw_label_dsc_t * label_dsc,const uint16_t major_tick_idx)1508 static void scale_build_custom_label_text(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
1509 const uint16_t major_tick_idx)
1510 {
1511 lv_scale_t * scale = (lv_scale_t *) obj;
1512
1513 /* Check if the scale has valid custom labels available,
1514 * this avoids reading past txt_src array when the scale requires more tick labels than available */
1515 if(major_tick_idx <= scale->custom_label_cnt) {
1516 if(scale->txt_src[major_tick_idx - 1U]) {
1517 label_dsc->text = scale->txt_src[major_tick_idx - 1U];
1518 label_dsc->text_local = 0;
1519 }
1520 else {
1521 label_dsc->text = NULL;
1522 }
1523 }
1524 else {
1525 label_dsc->text = NULL;
1526 }
1527 }
1528
1529 /**
1530 * Stores tick width compensation information for main line sections
1531 *
1532 * @param obj pointer to a scale object
1533 * @param is_major_tick Indicates if tick is major or not
1534 * @param major_tick_dsc pointer to the major_tick_dsc
1535 * @param minor_tick_dsc pointer to the minor_tick_dsc
1536 * @param tick_value Current tick value, used to know if tick_idx belongs to a section or not
1537 * @param tick_idx Current tick index
1538 * @param tick_point_a Pointer to tick point a
1539 */
scale_store_section_line_tick_width_compensation(lv_obj_t * obj,const bool is_major_tick,lv_draw_line_dsc_t * major_tick_dsc,lv_draw_line_dsc_t * minor_tick_dsc,const int32_t tick_value,const uint8_t tick_idx,lv_point_t * tick_point_a)1540 static void scale_store_section_line_tick_width_compensation(lv_obj_t * obj, const bool is_major_tick,
1541 lv_draw_line_dsc_t * major_tick_dsc, lv_draw_line_dsc_t * minor_tick_dsc,
1542 const int32_t tick_value, const uint8_t tick_idx, lv_point_t * tick_point_a)
1543 {
1544 lv_scale_t * scale = (lv_scale_t *) obj;
1545 lv_scale_section_t * section;
1546
1547 LV_LL_READ_BACK(&scale->section_ll, section) {
1548 if(section->range_min <= tick_value && section->range_max >= tick_value) {
1549 if(is_major_tick) {
1550 scale_set_line_properties(obj, major_tick_dsc, section->indicator_style, LV_PART_INDICATOR);
1551 }
1552 else {
1553 scale_set_line_properties(obj, minor_tick_dsc, section->items_style, LV_PART_ITEMS);
1554 }
1555 }
1556
1557 int32_t tmp_width = 0;
1558
1559 if(tick_idx == section->first_tick_idx_in_section) {
1560 if(section->first_tick_idx_is_major) {
1561 tmp_width = major_tick_dsc->width;
1562 }
1563 else {
1564 tmp_width = minor_tick_dsc->width;
1565 }
1566
1567 section->first_tick_in_section = *tick_point_a;
1568 /* Add 1px as adjustment if tmp_width is odd */
1569 if(tmp_width & 0x01U) {
1570 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1571 tmp_width += 1;
1572 }
1573 else {
1574 tmp_width -= 1;
1575 }
1576 }
1577 section->first_tick_in_section_width = tmp_width;
1578 }
1579
1580 /* This can also apply when
1581 * (tick_idx == section->first_tick_idx_in_section) when the
1582 * beginning and ending vlues of the range are the same. */
1583 if(tick_idx == section->last_tick_idx_in_section) {
1584 if(section->last_tick_idx_is_major) {
1585 tmp_width = major_tick_dsc->width;
1586 }
1587 else {
1588 tmp_width = minor_tick_dsc->width;
1589 }
1590
1591 section->last_tick_in_section = *tick_point_a;
1592 /* Add 1px as adjustment if tmp_width is odd */
1593 if(tmp_width & 0x01U) {
1594 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1595 tmp_width -= 1;
1596 }
1597 else {
1598 tmp_width += 1;
1599 }
1600 }
1601 section->last_tick_in_section_width = tmp_width;
1602 }
1603 else { /* Nothing to do */ }
1604 }
1605 }
1606
scale_free_line_needle_points_cb(lv_event_t * e)1607 static void scale_free_line_needle_points_cb(lv_event_t * e)
1608 {
1609 lv_point_precise_t * needle_line_points = lv_event_get_user_data(e);
1610 lv_free(needle_line_points);
1611 }
1612
1613 #endif
1614