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