1
2
3 /**
4 * @file lv_bar.c
5 *
6 */
7
8 /*********************
9 * INCLUDES
10 *********************/
11 #include "lv_bar.h"
12 #if LV_USE_BAR != 0
13
14 #include "../lv_draw/lv_draw.h"
15 #include "../lv_themes/lv_theme.h"
16 #include "../lv_misc/lv_anim.h"
17 #include <stdio.h>
18
19 /*********************
20 * DEFINES
21 *********************/
22
23 /**********************
24 * TYPEDEFS
25 **********************/
26
27 /**********************
28 * STATIC PROTOTYPES
29 **********************/
30 static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode);
31 static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param);
32
33 #if LV_USE_ANIMATION
34 static void lv_bar_anim(void * bar, lv_anim_value_t value);
35 static void lv_bar_anim_ready(lv_anim_t * a);
36 #endif
37
38 /**********************
39 * STATIC VARIABLES
40 **********************/
41 static lv_design_cb_t ancestor_design_f;
42 static lv_signal_cb_t ancestor_signal;
43
44 /**********************
45 * MACROS
46 **********************/
47
48 /**********************
49 * GLOBAL FUNCTIONS
50 **********************/
51
52 /**
53 * Create a bar objects
54 * @param par pointer to an object, it will be the parent of the new bar
55 * @param copy pointer to a bar object, if not NULL then the new object will be copied from it
56 * @return pointer to the created bar
57 */
lv_bar_create(lv_obj_t * par,const lv_obj_t * copy)58 lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy)
59 {
60 LV_LOG_TRACE("lv_bar create started");
61
62 /*Create the ancestor basic object*/
63 lv_obj_t * new_bar = lv_obj_create(par, copy);
64 lv_mem_assert(new_bar);
65 if(new_bar == NULL) return NULL;
66
67 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_bar);
68 if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_cb(new_bar);
69
70 /*Allocate the object type specific extended data*/
71 lv_bar_ext_t * ext = lv_obj_allocate_ext_attr(new_bar, sizeof(lv_bar_ext_t));
72 lv_mem_assert(ext);
73 if(ext == NULL) return NULL;
74
75 ext->min_value = 0;
76 ext->max_value = 100;
77 ext->cur_value = 0;
78 #if LV_USE_ANIMATION
79 ext->anim_time = 200;
80 ext->anim_start = 0;
81 ext->anim_end = 0;
82 ext->anim_state = LV_BAR_ANIM_STATE_INV;
83 #endif
84 ext->sym = 0;
85 ext->style_indic = &lv_style_pretty_color;
86
87 lv_obj_set_signal_cb(new_bar, lv_bar_signal);
88 lv_obj_set_design_cb(new_bar, lv_bar_design);
89
90 /*Init the new bar object*/
91 if(copy == NULL) {
92 lv_obj_set_click(new_bar, false);
93 lv_obj_set_size(new_bar, LV_DPI * 2, LV_DPI / 3);
94 lv_bar_set_value(new_bar, ext->cur_value, false);
95
96 lv_theme_t * th = lv_theme_get_current();
97 if(th) {
98 lv_bar_set_style(new_bar, LV_BAR_STYLE_BG, th->style.bar.bg);
99 lv_bar_set_style(new_bar, LV_BAR_STYLE_INDIC, th->style.bar.indic);
100 } else {
101 lv_obj_set_style(new_bar, &lv_style_pretty);
102 }
103 } else {
104 lv_bar_ext_t * ext_copy = lv_obj_get_ext_attr(copy);
105 ext->min_value = ext_copy->min_value;
106 ext->max_value = ext_copy->max_value;
107 ext->cur_value = ext_copy->cur_value;
108 ext->style_indic = ext_copy->style_indic;
109 ext->sym = ext_copy->sym;
110 /*Refresh the style with new signal function*/
111 lv_obj_refresh_style(new_bar);
112
113 lv_bar_set_value(new_bar, ext->cur_value, false);
114 }
115
116 LV_LOG_INFO("bar created");
117
118 return new_bar;
119 }
120
121 /*=====================
122 * Setter functions
123 *====================*/
124
125 /**
126 * Set a new value on the bar
127 * @param bar pointer to a bar object
128 * @param value new value
129 * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediatelly
130 */
lv_bar_set_value(lv_obj_t * bar,int16_t value,lv_anim_enable_t anim)131 void lv_bar_set_value(lv_obj_t * bar, int16_t value, lv_anim_enable_t anim)
132 {
133 #if LV_USE_ANIMATION == 0
134 anim = false;
135 #endif
136 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
137 if(ext->cur_value == value) return;
138
139 int16_t new_value;
140 new_value = value > ext->max_value ? ext->max_value : value;
141 new_value = new_value < ext->min_value ? ext->min_value : new_value;
142
143 if(ext->cur_value == new_value) return;
144
145 if(anim == LV_ANIM_OFF) {
146 ext->cur_value = new_value;
147 lv_obj_invalidate(bar);
148 } else {
149 #if LV_USE_ANIMATION
150 /*No animation in progress -> simply set the values*/
151 if(ext->anim_state == LV_BAR_ANIM_STATE_INV) {
152 ext->anim_start = ext->cur_value;
153 ext->anim_end = new_value;
154 }
155 /*Animation in progress. Start from the animation end value*/
156 else {
157 ext->anim_start = ext->anim_end;
158 ext->anim_end = new_value;
159 }
160
161 lv_anim_t a;
162 a.var = bar;
163 a.start = LV_BAR_ANIM_STATE_START;
164 a.end = LV_BAR_ANIM_STATE_END;
165 a.exec_cb = (lv_anim_exec_xcb_t)lv_bar_anim;
166 a.path_cb = lv_anim_path_linear;
167 a.ready_cb = lv_bar_anim_ready;
168 a.act_time = 0;
169 a.time = ext->anim_time;
170 a.playback = 0;
171 a.playback_pause = 0;
172 a.repeat = 0;
173 a.repeat_pause = 0;
174
175 lv_anim_create(&a);
176 #endif
177 }
178 }
179
180 /**
181 * Set minimum and the maximum values of a bar
182 * @param bar pointer to the bar object
183 * @param min minimum value
184 * @param max maximum value
185 */
lv_bar_set_range(lv_obj_t * bar,int16_t min,int16_t max)186 void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max)
187 {
188 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
189 if(ext->min_value == min && ext->max_value == max) return;
190
191 ext->max_value = max;
192 ext->min_value = min;
193 if(ext->cur_value > max) {
194 ext->cur_value = max;
195 lv_bar_set_value(bar, ext->cur_value, false);
196 }
197 if(ext->cur_value < min) {
198 ext->cur_value = min;
199 lv_bar_set_value(bar, ext->cur_value, false);
200 }
201 lv_obj_invalidate(bar);
202 }
203
204 /**
205 * Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum
206 * position.
207 * @param bar pointer to a bar object
208 * @param en true: enable disable symmetric behavior; false: disable
209 */
lv_bar_set_sym(lv_obj_t * bar,bool en)210 void lv_bar_set_sym(lv_obj_t * bar, bool en)
211 {
212 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
213 ext->sym = en ? 1 : 0;
214 }
215
216 /**
217 * Set the animation time of the bar
218 * @param bar pointer to a bar object
219 * @param anim_time the animation time in milliseconds.
220 */
lv_bar_set_anim_time(lv_obj_t * bar,uint16_t anim_time)221 void lv_bar_set_anim_time(lv_obj_t * bar, uint16_t anim_time)
222 {
223 #if LV_USE_ANIMATION
224 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
225 ext->anim_time = anim_time;
226 #else
227 (void)bar; /*Unused*/
228 (void)anim_time; /*Unused*/
229 #endif
230 }
231
232 /**
233 * Set a style of a bar
234 * @param bar pointer to a bar object
235 * @param type which style should be set
236 * @param style pointer to a style
237 */
lv_bar_set_style(lv_obj_t * bar,lv_bar_style_t type,const lv_style_t * style)238 void lv_bar_set_style(lv_obj_t * bar, lv_bar_style_t type, const lv_style_t * style)
239 {
240 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
241
242 switch(type) {
243 case LV_BAR_STYLE_BG: lv_obj_set_style(bar, style); break;
244 case LV_BAR_STYLE_INDIC:
245 ext->style_indic = style;
246 lv_obj_refresh_ext_draw_pad(bar);
247 break;
248 }
249 }
250
251 /*=====================
252 * Getter functions
253 *====================*/
254
255 /**
256 * Get the value of a bar
257 * @param bar pointer to a bar object
258 * @return the value of the bar
259 */
lv_bar_get_value(const lv_obj_t * bar)260 int16_t lv_bar_get_value(const lv_obj_t * bar)
261 {
262 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
263 /*If animated tell that it's already at the end value*/
264 #if LV_USE_ANIMATION
265 if(ext->anim_state != LV_BAR_ANIM_STATE_INV) return ext->anim_end;
266 #endif
267 /*No animation, simple return the current value*/
268 return ext->cur_value;
269 }
270
271 /**
272 * Get the minimum value of a bar
273 * @param bar pointer to a bar object
274 * @return the minimum value of the bar
275 */
lv_bar_get_min_value(const lv_obj_t * bar)276 int16_t lv_bar_get_min_value(const lv_obj_t * bar)
277 {
278 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
279 return ext->min_value;
280 }
281
282 /**
283 * Get the maximum value of a bar
284 * @param bar pointer to a bar object
285 * @return the maximum value of the bar
286 */
lv_bar_get_max_value(const lv_obj_t * bar)287 int16_t lv_bar_get_max_value(const lv_obj_t * bar)
288 {
289 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
290 return ext->max_value;
291 }
292
293 /**
294 * Get whether the bar is symmetric or not.
295 * @param bar pointer to a bar object
296 * @return true: symmetric is enabled; false: disable
297 */
lv_bar_get_sym(lv_obj_t * bar)298 bool lv_bar_get_sym(lv_obj_t * bar)
299 {
300 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
301 return ext->sym ? true : false;
302 }
303
304 /**
305 * Get the animation time of the bar
306 * @param bar pointer to a bar object
307 * @return the animation time in milliseconds.
308 */
lv_bar_get_anim_time(lv_obj_t * bar)309 uint16_t lv_bar_get_anim_time(lv_obj_t * bar)
310 {
311 #if LV_USE_ANIMATION
312 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
313 return ext->anim_time;
314 #else
315 (void)bar; /*Unused*/
316 return 0;
317 #endif
318 }
319
320 /**
321 * Get a style of a bar
322 * @param bar pointer to a bar object
323 * @param type which style should be get
324 * @return style pointer to a style
325 */
lv_bar_get_style(const lv_obj_t * bar,lv_bar_style_t type)326 const lv_style_t * lv_bar_get_style(const lv_obj_t * bar, lv_bar_style_t type)
327 {
328 const lv_style_t * style = NULL;
329 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
330
331 switch(type) {
332 case LV_BAR_STYLE_BG: style = lv_obj_get_style(bar); break;
333 case LV_BAR_STYLE_INDIC: style = ext->style_indic; break;
334 default: style = NULL; break;
335 }
336
337 return style;
338 }
339
340 /**********************
341 * STATIC FUNCTIONS
342 **********************/
343
344 /**
345 * Handle the drawing related tasks of the bars
346 * @param bar pointer to an object
347 * @param mask the object will be drawn only in this area
348 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
349 * (return 'true' if yes)
350 * LV_DESIGN_DRAW: draw the object (always return 'true')
351 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
352 * @param return true/false, depends on 'mode'
353 */
lv_bar_design(lv_obj_t * bar,const lv_area_t * mask,lv_design_mode_t mode)354 static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode)
355 {
356 if(mode == LV_DESIGN_COVER_CHK) {
357 /*Return false if the object is not covers the mask area*/
358 return ancestor_design_f(bar, mask, mode);
359 } else if(mode == LV_DESIGN_DRAW_MAIN) {
360 lv_opa_t opa_scale = lv_obj_get_opa_scale(bar);
361
362 #if LV_USE_GROUP == 0
363 ancestor_design_f(bar, mask, mode);
364 #else
365 /* Draw the borders later if the bar is focused.
366 * At value = 100% the indicator can cover to whole background and the focused style won't
367 * be visible*/
368 if(lv_obj_is_focused(bar)) {
369 const lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG);
370 lv_style_t style_tmp;
371 lv_style_copy(&style_tmp, style_bg);
372 style_tmp.body.border.width = 0;
373 lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale);
374 } else {
375 ancestor_design_f(bar, mask, mode);
376 }
377 #endif
378 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
379
380 if(ext->cur_value != ext->min_value || ext->sym
381 #if LV_USE_ANIMATION
382 || ext->anim_start != LV_BAR_ANIM_STATE_INV
383 #endif
384 ) {
385 const lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC);
386 lv_area_t indic_area;
387 lv_area_copy(&indic_area, &bar->coords);
388 indic_area.x1 += style_indic->body.padding.left;
389 indic_area.x2 -= style_indic->body.padding.right;
390 indic_area.y1 += style_indic->body.padding.top;
391 indic_area.y2 -= style_indic->body.padding.bottom;
392
393 lv_coord_t w = lv_area_get_width(&indic_area);
394 lv_coord_t h = lv_area_get_height(&indic_area);
395
396 if(w >= h) {
397 /*Horizontal*/
398 #if LV_USE_ANIMATION
399 if(ext->anim_state != LV_BAR_ANIM_STATE_INV) {
400 /*Calculate the coordinates of anim. start and end*/
401 lv_coord_t anim_start_x =
402 (int32_t)((int32_t)w * (ext->anim_start - ext->min_value)) / (ext->max_value - ext->min_value);
403 lv_coord_t anim_end_x =
404 (int32_t)((int32_t)w * (ext->anim_end - ext->min_value)) / (ext->max_value - ext->min_value);
405
406 /*Calculate the real position based on `anim_state` (between `anim_start` and
407 * `anim_end`)*/
408 indic_area.x2 =
409 anim_start_x + (((anim_end_x - anim_start_x) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM);
410 } else
411 #endif
412 {
413 indic_area.x2 =
414 (int32_t)((int32_t)w * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value);
415 }
416
417 indic_area.x2 = indic_area.x1 + indic_area.x2 - 1;
418 if(ext->sym && ext->min_value < 0 && ext->max_value > 0) {
419 /*Calculate the coordinate of the zero point*/
420 lv_coord_t zero;
421 zero = indic_area.x1 + (-ext->min_value * w) / (ext->max_value - ext->min_value);
422 if(indic_area.x2 > zero)
423 indic_area.x1 = zero;
424 else {
425 indic_area.x1 = indic_area.x2;
426 indic_area.x2 = zero;
427 }
428 }
429 } else {
430 #if LV_USE_ANIMATION
431 if(ext->anim_state != LV_BAR_ANIM_STATE_INV) {
432 /*Calculate the coordinates of anim. start and end*/
433 lv_coord_t anim_start_y =
434 (int32_t)((int32_t)h * (ext->anim_start - ext->min_value)) / (ext->max_value - ext->min_value);
435 lv_coord_t anim_end_y =
436 (int32_t)((int32_t)h * (ext->anim_end - ext->min_value)) / (ext->max_value - ext->min_value);
437
438 /*Calculate the real position based on `anim_state` (between `anim_start` and
439 * `anim_end`)*/
440 indic_area.y1 =
441 anim_start_y + (((anim_end_y - anim_start_y) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM);
442 } else
443 #endif
444 {
445 indic_area.y1 =
446 (int32_t)((int32_t)h * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value);
447 }
448
449 indic_area.y1 = indic_area.y2 - indic_area.y1 + 1;
450
451 if(ext->sym && ext->min_value < 0 && ext->max_value > 0) {
452 /*Calculate the coordinate of the zero point*/
453 lv_coord_t zero;
454 zero = indic_area.y2 - (-ext->min_value * h) / (ext->max_value - ext->min_value);
455 if(indic_area.y1 < zero)
456 indic_area.y2 = zero;
457 else {
458 indic_area.y2 = indic_area.y1;
459 indic_area.y1 = zero;
460 }
461 }
462 }
463
464 /*Draw the indicator*/
465 lv_draw_rect(&indic_area, mask, style_indic, opa_scale);
466 }
467 } else if(mode == LV_DESIGN_DRAW_POST) {
468 #if LV_USE_GROUP
469 /*Draw the border*/
470 if(lv_obj_is_focused(bar)) {
471 lv_opa_t opa_scale = lv_obj_get_opa_scale(bar);
472 const lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG);
473 lv_style_t style_tmp;
474 lv_style_copy(&style_tmp, style_bg);
475 style_tmp.body.opa = LV_OPA_TRANSP;
476 style_tmp.body.shadow.width = 0;
477 lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale);
478 }
479 #endif
480 }
481 return true;
482 }
483
484 /**
485 * Signal function of the bar
486 * @param bar pointer to a bar object
487 * @param sign a signal type from lv_signal_t enum
488 * @param param pointer to a signal specific variable
489 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
490 */
lv_bar_signal(lv_obj_t * bar,lv_signal_t sign,void * param)491 static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param)
492 {
493 lv_res_t res;
494
495 /* Include the ancient signal function */
496 res = ancestor_signal(bar, sign, param);
497 if(res != LV_RES_OK) return res;
498
499 if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
500 const lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC);
501 if(style_indic->body.shadow.width > bar->ext_draw_pad) bar->ext_draw_pad = style_indic->body.shadow.width;
502 } else if(sign == LV_SIGNAL_GET_TYPE) {
503 lv_obj_type_t * buf = param;
504 uint8_t i;
505 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
506 if(buf->type[i] == NULL) break;
507 }
508 buf->type[i] = "lv_bar";
509 }
510
511 return res;
512 }
513
514 #if LV_USE_ANIMATION
lv_bar_anim(void * bar,lv_anim_value_t value)515 static void lv_bar_anim(void * bar, lv_anim_value_t value)
516 {
517 lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
518 ext->anim_state = value;
519 lv_obj_invalidate(bar);
520 }
521
lv_bar_anim_ready(lv_anim_t * a)522 static void lv_bar_anim_ready(lv_anim_t * a)
523 {
524 lv_bar_ext_t * ext = lv_obj_get_ext_attr(a->var);
525 ext->anim_state = LV_BAR_ANIM_STATE_INV;
526 lv_bar_set_value(a->var, ext->anim_end, false);
527 }
528 #endif
529
530 #endif
531