1 /**
2  * @file lv_cont.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_cont.h"
11 #if LV_USE_CONT != 0
12 
13 #include <stdbool.h>
14 #include <stdint.h>
15 #include <string.h>
16 
17 #include "../lv_draw/lv_draw.h"
18 #include "../lv_draw/lv_draw_basic.h"
19 #include "../lv_themes/lv_theme.h"
20 #include "../lv_misc/lv_area.h"
21 #include "../lv_misc/lv_color.h"
22 #include "../lv_misc/lv_math.h"
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 
28 /**********************
29  *      TYPEDEFS
30  **********************/
31 
32 /**********************
33  *  STATIC PROTOTYPES
34  **********************/
35 static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param);
36 static void lv_cont_refr_layout(lv_obj_t * cont);
37 static void lv_cont_layout_col(lv_obj_t * cont);
38 static void lv_cont_layout_row(lv_obj_t * cont);
39 static void lv_cont_layout_center(lv_obj_t * cont);
40 static void lv_cont_layout_pretty(lv_obj_t * cont);
41 static void lv_cont_layout_grid(lv_obj_t * cont);
42 static void lv_cont_refr_autofit(lv_obj_t * cont);
43 
44 /**********************
45  *  STATIC VARIABLES
46  **********************/
47 static lv_signal_cb_t ancestor_signal;
48 
49 /**********************
50  *      MACROS
51  **********************/
52 
53 /**********************
54  *   GLOBAL FUNCTIONS
55  **********************/
56 
57 /**
58  * Create a container objects
59  * @param par pointer to an object, it will be the parent of the new container
60  * @param copy pointer to a container object, if not NULL then the new object will be copied from it
61  * @return pointer to the created container
62  */
lv_cont_create(lv_obj_t * par,const lv_obj_t * copy)63 lv_obj_t * lv_cont_create(lv_obj_t * par, const lv_obj_t * copy)
64 {
65 
66     LV_LOG_TRACE("container create started");
67 
68     /*Create a basic object*/
69     lv_obj_t * new_cont = lv_obj_create(par, copy);
70     lv_mem_assert(new_cont);
71     if(new_cont == NULL) return NULL;
72 
73     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_cont);
74 
75     lv_obj_allocate_ext_attr(new_cont, sizeof(lv_cont_ext_t));
76     lv_cont_ext_t * ext = lv_obj_get_ext_attr(new_cont);
77     if(ext == NULL) return NULL;
78 
79     lv_mem_assert(ext);
80     ext->fit_left   = LV_FIT_NONE;
81     ext->fit_right  = LV_FIT_NONE;
82     ext->fit_top    = LV_FIT_NONE;
83     ext->fit_bottom = LV_FIT_NONE;
84     ext->layout     = LV_LAYOUT_OFF;
85 
86     lv_obj_set_signal_cb(new_cont, lv_cont_signal);
87 
88     /*Init the new container*/
89     if(copy == NULL) {
90         /*Set the default styles if it's not screen*/
91         if(par != NULL) {
92             lv_theme_t * th = lv_theme_get_current();
93             if(th) {
94                 lv_cont_set_style(new_cont, LV_CONT_STYLE_MAIN, th->style.cont);
95             } else {
96                 lv_cont_set_style(new_cont, LV_CONT_STYLE_MAIN, &lv_style_pretty);
97             }
98         }
99     }
100     /*Copy an existing object*/
101     else {
102         lv_cont_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
103         ext->fit_left            = copy_ext->fit_left;
104         ext->fit_right           = copy_ext->fit_right;
105         ext->fit_top             = copy_ext->fit_top;
106         ext->fit_bottom          = copy_ext->fit_bottom;
107         ext->layout              = copy_ext->layout;
108 
109         /*Refresh the style with new signal function*/
110         lv_obj_refresh_style(new_cont);
111     }
112 
113     LV_LOG_INFO("container created");
114 
115     return new_cont;
116 }
117 
118 /*=====================
119  * Setter functions
120  *====================*/
121 
122 /**
123  * Set a layout on a container
124  * @param cont pointer to a container object
125  * @param layout a layout from 'lv_cont_layout_t'
126  */
lv_cont_set_layout(lv_obj_t * cont,lv_layout_t layout)127 void lv_cont_set_layout(lv_obj_t * cont, lv_layout_t layout)
128 {
129     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
130     if(ext->layout == layout) return;
131 
132     ext->layout = layout;
133 
134     /*Send a signal to refresh the layout*/
135     cont->signal_cb(cont, LV_SIGNAL_CHILD_CHG, NULL);
136 }
137 
138 /**
139  * Set the fit policy in all 4 directions separately.
140  * It tell how to change the container's size automatically.
141  * @param cont pointer to a container object
142  * @param left left fit policy from `lv_fit_t`
143  * @param right right fit policy from `lv_fit_t`
144  * @param top bottom fit policy from `lv_fit_t`
145  * @param bottom bottom fit policy from `lv_fit_t`
146  */
lv_cont_set_fit4(lv_obj_t * cont,lv_fit_t left,lv_fit_t right,lv_fit_t top,lv_fit_t bottom)147 void lv_cont_set_fit4(lv_obj_t * cont, lv_fit_t left, lv_fit_t right, lv_fit_t top, lv_fit_t bottom)
148 {
149     lv_obj_invalidate(cont);
150     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
151     if(ext->fit_left == left && ext->fit_right == right && ext->fit_top == top && ext->fit_bottom == bottom) {
152         return;
153     }
154 
155     ext->fit_left   = left;
156     ext->fit_right  = right;
157     ext->fit_top    = top;
158     ext->fit_bottom = bottom;
159 
160     /*Send a signal to refresh the layout*/
161     cont->signal_cb(cont, LV_SIGNAL_CHILD_CHG, NULL);
162 }
163 
164 /*=====================
165  * Getter functions
166  *====================*/
167 
168 /**
169  * Get the layout of a container
170  * @param cont pointer to container object
171  * @return the layout from 'lv_cont_layout_t'
172  */
lv_cont_get_layout(const lv_obj_t * cont)173 lv_layout_t lv_cont_get_layout(const lv_obj_t * cont)
174 {
175     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
176     return ext->layout;
177 }
178 
179 /**
180  * Get left fit mode of a container
181  * @param cont pointer to a container object
182  * @return an element of `lv_fit_t`
183  */
lv_cont_get_fit_left(const lv_obj_t * cont)184 lv_fit_t lv_cont_get_fit_left(const lv_obj_t * cont)
185 {
186     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
187     return ext->fit_left;
188 }
189 
190 /**
191  * Get right fit mode of a container
192  * @param cont pointer to a container object
193  * @return an element of `lv_fit_t`
194  */
lv_cont_get_fit_right(const lv_obj_t * cont)195 lv_fit_t lv_cont_get_fit_right(const lv_obj_t * cont)
196 {
197     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
198     return ext->fit_right;
199 }
200 
201 /**
202  * Get top fit mode of a container
203  * @param cont pointer to a container object
204  * @return an element of `lv_fit_t`
205  */
lv_cont_get_fit_top(const lv_obj_t * cont)206 lv_fit_t lv_cont_get_fit_top(const lv_obj_t * cont)
207 {
208     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
209     return ext->fit_top;
210 }
211 
212 /**
213  * Get bottom fit mode of a container
214  * @param cont pointer to a container object
215  * @return an element of `lv_fit_t`
216  */
lv_cont_get_fit_bottom(const lv_obj_t * cont)217 lv_fit_t lv_cont_get_fit_bottom(const lv_obj_t * cont)
218 {
219     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
220     return ext->fit_bottom;
221 }
222 
223 /**********************
224  *   STATIC FUNCTIONS
225  **********************/
226 
227 /**
228  * Signal function of the container
229  * @param cont pointer to a container object
230  * @param sign a signal type from lv_signal_t enum
231  * @param param pointer to a signal specific variable
232  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
233  */
lv_cont_signal(lv_obj_t * cont,lv_signal_t sign,void * param)234 static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param)
235 {
236     lv_res_t res;
237 
238     /* Include the ancient signal function */
239     res = ancestor_signal(cont, sign, param);
240     if(res != LV_RES_OK) return res;
241 
242     if(sign == LV_SIGNAL_STYLE_CHG) { /*Recalculate the padding if the style changed*/
243         lv_cont_refr_layout(cont);
244         lv_cont_refr_autofit(cont);
245     } else if(sign == LV_SIGNAL_CHILD_CHG) {
246         lv_cont_refr_layout(cont);
247         lv_cont_refr_autofit(cont);
248     } else if(sign == LV_SIGNAL_CORD_CHG) {
249         if(lv_obj_get_width(cont) != lv_area_get_width(param) || lv_obj_get_height(cont) != lv_area_get_height(param)) {
250             lv_cont_refr_layout(cont);
251             lv_cont_refr_autofit(cont);
252         }
253     } else if(sign == LV_SIGNAL_PARENT_SIZE_CHG) {
254         /*FLOOD and FILL fit needs to be refreshed if the parent size has changed*/
255         lv_cont_refr_autofit(cont);
256 
257     } else if(sign == LV_SIGNAL_GET_TYPE) {
258         lv_obj_type_t * buf = param;
259         uint8_t i;
260         for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
261             if(buf->type[i] == NULL) break;
262         }
263         buf->type[i] = "lv_cont";
264     }
265 
266     return res;
267 }
268 
269 /**
270  * Refresh the layout of a container
271  * @param cont pointer to an object which layout should be refreshed
272  */
lv_cont_refr_layout(lv_obj_t * cont)273 static void lv_cont_refr_layout(lv_obj_t * cont)
274 {
275     lv_layout_t type = lv_cont_get_layout(cont);
276 
277     /*'cont' has to be at least 1 child*/
278     if(lv_obj_get_child(cont, NULL) == NULL) return;
279 
280     if(type == LV_LAYOUT_OFF) return;
281 
282     if(type == LV_LAYOUT_CENTER) {
283         lv_cont_layout_center(cont);
284     } else if(type == LV_LAYOUT_COL_L || type == LV_LAYOUT_COL_M || type == LV_LAYOUT_COL_R) {
285         lv_cont_layout_col(cont);
286     } else if(type == LV_LAYOUT_ROW_T || type == LV_LAYOUT_ROW_M || type == LV_LAYOUT_ROW_B) {
287         lv_cont_layout_row(cont);
288     } else if(type == LV_LAYOUT_PRETTY) {
289         lv_cont_layout_pretty(cont);
290     } else if(type == LV_LAYOUT_GRID) {
291         lv_cont_layout_grid(cont);
292     }
293 }
294 
295 /**
296  * Handle column type layouts
297  * @param cont pointer to an object which layout should be handled
298  */
lv_cont_layout_col(lv_obj_t * cont)299 static void lv_cont_layout_col(lv_obj_t * cont)
300 {
301     lv_layout_t type = lv_cont_get_layout(cont);
302     lv_obj_t * child;
303 
304     /*Adjust margin and get the alignment type*/
305     lv_align_t align;
306     const lv_style_t * style = lv_obj_get_style(cont);
307     lv_coord_t hpad_corr;
308 
309     switch(type) {
310         case LV_LAYOUT_COL_L:
311             hpad_corr = style->body.padding.left;
312             align     = LV_ALIGN_IN_TOP_LEFT;
313             break;
314         case LV_LAYOUT_COL_M:
315             hpad_corr = 0;
316             align     = LV_ALIGN_IN_TOP_MID;
317             break;
318         case LV_LAYOUT_COL_R:
319             hpad_corr = -style->body.padding.right;
320             align     = LV_ALIGN_IN_TOP_RIGHT;
321             break;
322         default:
323             hpad_corr = 0;
324             align     = LV_ALIGN_IN_TOP_LEFT;
325             break;
326     }
327 
328     /* Disable child change action because the children will be moved a lot
329      * an unnecessary child change signals could be sent*/
330     lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
331     /* Align the children */
332     lv_coord_t last_cord = style->body.padding.top;
333     LV_LL_READ_BACK(cont->child_ll, child)
334     {
335         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
336 
337         lv_obj_align(child, cont, align, hpad_corr, last_cord);
338         last_cord += lv_obj_get_height(child) + style->body.padding.inner;
339     }
340 
341     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
342 }
343 
344 /**
345  * Handle row type layouts
346  * @param cont pointer to an object which layout should be handled
347  */
lv_cont_layout_row(lv_obj_t * cont)348 static void lv_cont_layout_row(lv_obj_t * cont)
349 {
350     lv_layout_t type = lv_cont_get_layout(cont);
351     lv_obj_t * child;
352 
353     /*Adjust margin and get the alignment type*/
354     lv_align_t align;
355     const lv_style_t * style = lv_obj_get_style(cont);
356     lv_coord_t vpad_corr;
357 
358     switch(type) {
359         case LV_LAYOUT_ROW_T:
360             vpad_corr = style->body.padding.top;
361             align     = LV_ALIGN_IN_TOP_LEFT;
362             break;
363         case LV_LAYOUT_ROW_M:
364             vpad_corr = 0;
365             align     = LV_ALIGN_IN_LEFT_MID;
366             break;
367         case LV_LAYOUT_ROW_B:
368             vpad_corr = -style->body.padding.bottom;
369             align     = LV_ALIGN_IN_BOTTOM_LEFT;
370             break;
371         default:
372             vpad_corr = 0;
373             align     = LV_ALIGN_IN_TOP_LEFT;
374             break;
375     }
376 
377     /* Disable child change action because the children will be moved a lot
378      * an unnecessary child change signals could be sent*/
379     lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
380 
381     /* Align the children */
382     lv_coord_t last_cord = style->body.padding.left;
383     LV_LL_READ_BACK(cont->child_ll, child)
384     {
385         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
386 
387         lv_obj_align(child, cont, align, last_cord, vpad_corr);
388         last_cord += lv_obj_get_width(child) + style->body.padding.inner;
389     }
390 
391     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
392 }
393 
394 /**
395  * Handle the center layout
396  * @param cont pointer to an object which layout should be handled
397  */
lv_cont_layout_center(lv_obj_t * cont)398 static void lv_cont_layout_center(lv_obj_t * cont)
399 {
400     lv_obj_t * child;
401     const lv_style_t * style = lv_obj_get_style(cont);
402     uint32_t obj_num         = 0;
403     lv_coord_t h_tot         = 0;
404 
405     LV_LL_READ(cont->child_ll, child)
406     {
407         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
408         h_tot += lv_obj_get_height(child) + style->body.padding.inner;
409         obj_num++;
410     }
411 
412     if(obj_num == 0) return;
413 
414     h_tot -= style->body.padding.inner;
415 
416     /* Disable child change action because the children will be moved a lot
417      * an unnecessary child change signals could be sent*/
418     lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
419 
420     /* Align the children */
421     lv_coord_t last_cord = -(h_tot / 2);
422     LV_LL_READ_BACK(cont->child_ll, child)
423     {
424         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
425 
426         lv_obj_align(child, cont, LV_ALIGN_CENTER, 0, last_cord + lv_obj_get_height(child) / 2);
427         last_cord += lv_obj_get_height(child) + style->body.padding.inner;
428     }
429 
430     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
431 }
432 
433 /**
434  * Handle the pretty layout. Put as many object as possible in row
435  * then begin a new row
436  * @param cont pointer to an object which layout should be handled
437  */
lv_cont_layout_pretty(lv_obj_t * cont)438 static void lv_cont_layout_pretty(lv_obj_t * cont)
439 {
440     lv_obj_t * child_rs;  /* Row starter child */
441     lv_obj_t * child_rc;  /* Row closer child */
442     lv_obj_t * child_tmp; /* Temporary child */
443     const lv_style_t * style = lv_obj_get_style(cont);
444     lv_coord_t w_obj         = lv_obj_get_width(cont);
445     lv_coord_t act_y         = style->body.padding.top;
446     /* Disable child change action because the children will be moved a lot
447      * an unnecessary child change signals could be sent*/
448 
449     child_rs = lv_ll_get_tail(&cont->child_ll); /*Set the row starter child*/
450     if(child_rs == NULL) return;                /*Return if no child*/
451 
452     lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
453 
454     child_rc = child_rs; /*Initially the the row starter and closer is the same*/
455     while(child_rs != NULL) {
456         lv_coord_t h_row = 0;
457         lv_coord_t w_row =
458             style->body.padding.left + style->body.padding.right; /*The width is at least the left+right hpad*/
459         uint32_t obj_num = 0;
460 
461         /*Find the row closer object and collect some data*/
462         do {
463             if(lv_obj_get_hidden(child_rc) == false && lv_obj_is_protected(child_rc, LV_PROTECT_POS) == false) {
464                 /*If this object is already not fit then break*/
465                 if(w_row + lv_obj_get_width(child_rc) > w_obj) {
466                     /*Step back one child because the last already not fit, so the previous is the
467                      * closer*/
468                     if(child_rc != NULL && obj_num != 0) {
469                         child_rc = lv_ll_get_next(&cont->child_ll, child_rc);
470                     }
471                     break;
472                 }
473                 w_row += lv_obj_get_width(child_rc) + style->body.padding.inner; /*Add the object width + opad*/
474                 h_row = LV_MATH_MAX(h_row, lv_obj_get_height(child_rc));         /*Search the highest object*/
475                 obj_num++;
476                 if(lv_obj_is_protected(child_rc, LV_PROTECT_FOLLOW))
477                     break; /*If can not be followed by an other object then break here*/
478             }
479             child_rc = lv_ll_get_prev(&cont->child_ll, child_rc); /*Load the next object*/
480             if(obj_num == 0)
481                 child_rs = child_rc; /*If the first object was hidden (or too long) then set the
482                                         next as first */
483         } while(child_rc != NULL);
484 
485         /*If the object is too long  then align it to the middle*/
486         if(obj_num == 0) {
487             if(child_rc != NULL) {
488                 lv_obj_align(child_rc, cont, LV_ALIGN_IN_TOP_MID, 0, act_y);
489                 h_row = lv_obj_get_height(child_rc); /*Not set previously because of the early break*/
490             }
491         }
492         /*If there is only one object in the row then align it to the middle*/
493         else if(obj_num == 1) {
494             lv_obj_align(child_rs, cont, LV_ALIGN_IN_TOP_MID, 0, act_y);
495         }
496         /*If there are two object in the row then align them proportionally*/
497         else if(obj_num == 2) {
498             lv_obj_t * obj1 = child_rs;
499             lv_obj_t * obj2 = lv_ll_get_prev(&cont->child_ll, child_rs);
500             w_row           = lv_obj_get_width(obj1) + lv_obj_get_width(obj2);
501             lv_coord_t pad  = (w_obj - w_row) / 3;
502             lv_obj_align(obj1, cont, LV_ALIGN_IN_TOP_LEFT, pad, act_y + (h_row - lv_obj_get_height(obj1)) / 2);
503             lv_obj_align(obj2, cont, LV_ALIGN_IN_TOP_RIGHT, -pad, act_y + (h_row - lv_obj_get_height(obj2)) / 2);
504         }
505         /* Align the children (from child_rs to child_rc)*/
506         else {
507             w_row -= style->body.padding.inner * obj_num;
508             lv_coord_t new_opad = (w_obj - w_row) / (obj_num - 1);
509             lv_coord_t act_x    = style->body.padding.left; /*x init*/
510             child_tmp           = child_rs;
511             while(child_tmp != NULL) {
512                 if(lv_obj_get_hidden(child_tmp) == false && lv_obj_is_protected(child_tmp, LV_PROTECT_POS) == false) {
513                     lv_obj_align(child_tmp, cont, LV_ALIGN_IN_TOP_LEFT, act_x,
514                                  act_y + (h_row - lv_obj_get_height(child_tmp)) / 2);
515                     act_x += lv_obj_get_width(child_tmp) + new_opad;
516                 }
517                 if(child_tmp == child_rc) break;
518                 child_tmp = lv_ll_get_prev(&cont->child_ll, child_tmp);
519             }
520         }
521 
522         if(child_rc == NULL) break;
523         act_y += style->body.padding.inner + h_row;           /*y increment*/
524         child_rs = lv_ll_get_prev(&cont->child_ll, child_rc); /*Go to the next object*/
525         child_rc = child_rs;
526     }
527     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
528 }
529 
530 /**
531  * Handle the grid layout. Align same-sized objects in a grid
532  * @param cont pointer to an object which layout should be handled
533  */
lv_cont_layout_grid(lv_obj_t * cont)534 static void lv_cont_layout_grid(lv_obj_t * cont)
535 {
536     lv_obj_t * child;
537     const lv_style_t * style = lv_obj_get_style(cont);
538     lv_coord_t w_tot         = lv_obj_get_width(cont);
539     lv_coord_t w_obj         = lv_obj_get_width(lv_obj_get_child(cont, NULL));
540     lv_coord_t w_fit         =  lv_obj_get_width_fit(cont);
541     lv_coord_t h_obj         = lv_obj_get_height(lv_obj_get_child(cont, NULL));
542     uint16_t obj_row         = (w_fit) / (w_obj + style->body.padding.inner); /*Obj. num. in a row*/
543     lv_coord_t x_ofs;
544     if(obj_row > 1) {
545         x_ofs = w_obj + (w_fit - (obj_row * w_obj)) / (obj_row - 1);
546     } else {
547         x_ofs = w_tot / 2 - w_obj / 2;
548     }
549     lv_coord_t y_ofs = h_obj + style->body.padding.inner;
550 
551     /* Disable child change action because the children will be moved a lot
552      * an unnecessary child change signals could be sent*/
553     lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
554 
555     /* Align the children */
556     lv_coord_t act_x = style->body.padding.left;
557     lv_coord_t act_y = style->body.padding.top;
558     uint16_t obj_cnt = 0;
559     LV_LL_READ_BACK(cont->child_ll, child)
560     {
561         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
562 
563         if(obj_row > 1) {
564             lv_obj_set_pos(child, act_x, act_y);
565             act_x += x_ofs;
566         } else {
567             lv_obj_set_pos(child, x_ofs, act_y);
568         }
569         obj_cnt++;
570 
571         if(obj_cnt >= obj_row) {
572             obj_cnt = 0;
573             act_x   = style->body.padding.left;
574             act_y += y_ofs;
575         }
576     }
577 
578     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
579 }
580 
581 /**
582  * Handle auto fit. Set the size of the object to involve all children.
583  * @param cont pointer to an object which size will be modified
584  */
lv_cont_refr_autofit(lv_obj_t * cont)585 static void lv_cont_refr_autofit(lv_obj_t * cont)
586 {
587     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
588 
589     if(ext->fit_left == LV_FIT_NONE && ext->fit_right == LV_FIT_NONE && ext->fit_top == LV_FIT_NONE &&
590        ext->fit_bottom == LV_FIT_NONE) {
591         return;
592     }
593 
594     lv_area_t tight_area;
595     lv_area_t ori;
596     const lv_style_t * style = lv_obj_get_style(cont);
597     lv_obj_t * child_i;
598 
599     lv_obj_t * par               = lv_obj_get_parent(cont);
600     const lv_style_t * par_style = lv_obj_get_style(par);
601     lv_area_t flood_area;
602     lv_area_copy(&flood_area, &par->coords);
603     flood_area.x1 += par_style->body.padding.left;
604     flood_area.x2 -= par_style->body.padding.right;
605     flood_area.y1 += par_style->body.padding.top;
606     flood_area.y2 -= par_style->body.padding.bottom;
607 
608     /*Search the side coordinates of the children*/
609     lv_obj_get_coords(cont, &ori);
610     lv_obj_get_coords(cont, &tight_area);
611 
612     bool has_children = lv_ll_is_empty(&cont->child_ll) ? false : true;
613 
614     if(has_children) {
615         tight_area.x1 = LV_COORD_MAX;
616         tight_area.y1 = LV_COORD_MAX;
617         tight_area.x2 = LV_COORD_MIN;
618         tight_area.y2 = LV_COORD_MIN;
619 
620         LV_LL_READ(cont->child_ll, child_i)
621         {
622             if(lv_obj_get_hidden(child_i) != false) continue;
623             tight_area.x1 = LV_MATH_MIN(tight_area.x1, child_i->coords.x1);
624             tight_area.y1 = LV_MATH_MIN(tight_area.y1, child_i->coords.y1);
625             tight_area.x2 = LV_MATH_MAX(tight_area.x2, child_i->coords.x2);
626             tight_area.y2 = LV_MATH_MAX(tight_area.y2, child_i->coords.y2);
627         }
628 
629         tight_area.x1 -= style->body.padding.left;
630         tight_area.x2 += style->body.padding.right;
631         tight_area.y1 -= style->body.padding.top;
632         tight_area.y2 += style->body.padding.bottom;
633     }
634 
635     lv_area_t new_area;
636     lv_area_copy(&new_area, &ori);
637 
638     switch(ext->fit_left) {
639         case LV_FIT_TIGHT: new_area.x1 = tight_area.x1; break;
640         case LV_FIT_FLOOD: new_area.x1 = flood_area.x1; break;
641         case LV_FIT_FILL: new_area.x1 = has_children ? LV_MATH_MIN(tight_area.x1, flood_area.x1) : flood_area.x1; break;
642         default: break;
643     }
644 
645     switch(ext->fit_right) {
646         case LV_FIT_TIGHT: new_area.x2 = tight_area.x2; break;
647         case LV_FIT_FLOOD: new_area.x2 = flood_area.x2; break;
648         case LV_FIT_FILL: new_area.x2 = has_children ? LV_MATH_MAX(tight_area.x2, flood_area.x2) : flood_area.x2; break;
649         default: break;
650     }
651 
652     switch(ext->fit_top) {
653         case LV_FIT_TIGHT: new_area.y1 = tight_area.y1; break;
654         case LV_FIT_FLOOD: new_area.y1 = flood_area.y1; break;
655         case LV_FIT_FILL: new_area.y1 = has_children ? LV_MATH_MIN(tight_area.y1, flood_area.y1) : flood_area.y1; break;
656         default: break;
657     }
658 
659     switch(ext->fit_bottom) {
660         case LV_FIT_TIGHT: new_area.y2 = tight_area.y2; break;
661         case LV_FIT_FLOOD: new_area.y2 = flood_area.y2; break;
662         case LV_FIT_FILL: new_area.y2 = has_children ? LV_MATH_MAX(tight_area.y2, flood_area.y2) : flood_area.y2; break;
663         default: break;
664     }
665 
666     /*Do nothing if the coordinates are not changed*/
667     if(cont->coords.x1 != new_area.x1 || cont->coords.y1 != new_area.y1 || cont->coords.x2 != new_area.x2 ||
668        cont->coords.y2 != new_area.y2) {
669 
670         lv_obj_invalidate(cont);
671         lv_area_copy(&cont->coords, &new_area);
672         lv_obj_invalidate(cont);
673 
674         /*Notify the object about its new coordinates*/
675         cont->signal_cb(cont, LV_SIGNAL_CORD_CHG, &ori);
676 
677         /*Inform the parent about the new coordinates*/
678         par->signal_cb(par, LV_SIGNAL_CHILD_CHG, cont);
679 
680         if(lv_obj_get_auto_realign(cont)) {
681             lv_obj_realign(cont);
682         }
683 
684         /*Tell the children the parent's size has changed*/
685         LV_LL_READ(cont->child_ll, child_i)
686         {
687             child_i->signal_cb(child_i, LV_SIGNAL_PARENT_SIZE_CHG, NULL);
688         }
689     }
690 }
691 
692 #endif
693