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_misc/lv_debug.h"
18 #include "../lv_draw/lv_draw.h"
19 #include "../lv_draw/lv_draw_mask.h"
20 #include "../lv_themes/lv_theme.h"
21 #include "../lv_misc/lv_area.h"
22 #include "../lv_misc/lv_color.h"
23 #include "../lv_misc/lv_math.h"
24 
25 /*********************
26  *      DEFINES
27  *********************/
28 #define LV_OBJX_NAME "lv_cont"
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 
34 /**********************
35  *  STATIC PROTOTYPES
36  **********************/
37 static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param);
38 static lv_style_list_t * lv_cont_get_style(lv_obj_t * cont, uint8_t type);
39 static void lv_cont_refr_layout(lv_obj_t * cont);
40 static void lv_cont_layout_col(lv_obj_t * cont);
41 static void lv_cont_layout_row(lv_obj_t * cont);
42 static void lv_cont_layout_center(lv_obj_t * cont);
43 static void lv_cont_layout_pretty(lv_obj_t * cont);
44 static void lv_cont_layout_grid(lv_obj_t * cont);
45 static void lv_cont_refr_autofit(lv_obj_t * cont);
46 
47 /**********************
48  *  STATIC VARIABLES
49  **********************/
50 static lv_design_cb_t ancestor_design;
51 static lv_signal_cb_t ancestor_signal;
52 
53 /**********************
54  *      MACROS
55  **********************/
56 
57 /**********************
58  *   GLOBAL FUNCTIONS
59  **********************/
60 
61 /**
62  * Create a container objects
63  * @param par pointer to an object, it will be the parent of the new container
64  * @param copy pointer to a container object, if not NULL then the new object will be copied from it
65  * @return pointer to the created container
66  */
lv_cont_create(lv_obj_t * par,const lv_obj_t * copy)67 lv_obj_t * lv_cont_create(lv_obj_t * par, const lv_obj_t * copy)
68 {
69 
70     LV_LOG_TRACE("container create started");
71 
72     /*Create a basic object*/
73     lv_obj_t * cont = lv_obj_create(par, copy);
74     LV_ASSERT_MEM(cont);
75     if(cont == NULL) return NULL;
76 
77     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(cont);
78     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(cont);
79 
80     lv_obj_allocate_ext_attr(cont, sizeof(lv_cont_ext_t));
81     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
82     if(ext == NULL) {
83         lv_obj_del(cont);
84         return NULL;
85     }
86 
87     LV_ASSERT_MEM(ext);
88     ext->fit_left   = LV_FIT_NONE;
89     ext->fit_right  = LV_FIT_NONE;
90     ext->fit_top    = LV_FIT_NONE;
91     ext->fit_bottom = LV_FIT_NONE;
92     ext->layout     = LV_LAYOUT_OFF;
93 
94     lv_obj_set_signal_cb(cont, lv_cont_signal);
95 
96     /*Init the new container*/
97     if(copy == NULL) {
98         /*Set the default styles if it's not screen*/
99         if(par != NULL) {
100             lv_theme_apply(cont, LV_THEME_CONT);
101         }
102 
103 
104     }
105     /*Copy an existing object*/
106     else {
107         lv_cont_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
108         ext->fit_left            = copy_ext->fit_left;
109         ext->fit_right           = copy_ext->fit_right;
110         ext->fit_top             = copy_ext->fit_top;
111         ext->fit_bottom          = copy_ext->fit_bottom;
112         ext->layout              = copy_ext->layout;
113 
114         /*Refresh the style with new signal function*/
115         lv_obj_refresh_style(cont, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
116     }
117 
118     LV_LOG_INFO("container created");
119 
120     return cont;
121 }
122 
123 /*=====================
124  * Setter functions
125  *====================*/
126 
127 /**
128  * Set a layout on a container
129  * @param cont pointer to a container object
130  * @param layout a layout from 'lv_cont_layout_t'
131  */
lv_cont_set_layout(lv_obj_t * cont,lv_layout_t layout)132 void lv_cont_set_layout(lv_obj_t * cont, lv_layout_t layout)
133 {
134     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
135 
136     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
137     if(ext->layout == layout) return;
138 
139     ext->layout = layout;
140 
141     /*Send a signal to refresh the layout*/
142     cont->signal_cb(cont, LV_SIGNAL_CHILD_CHG, NULL);
143 }
144 
145 /**
146  * Set the fit policy in all 4 directions separately.
147  * It tell how to change the container's size automatically.
148  * @param cont pointer to a container object
149  * @param left left fit policy from `lv_fit_t`
150  * @param right right fit policy from `lv_fit_t`
151  * @param top bottom fit policy from `lv_fit_t`
152  * @param bottom bottom fit policy from `lv_fit_t`
153  */
lv_cont_set_fit4(lv_obj_t * cont,lv_fit_t left,lv_fit_t right,lv_fit_t top,lv_fit_t bottom)154 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)
155 {
156     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
157 
158     lv_obj_invalidate(cont);
159     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
160     if(ext->fit_left == left && ext->fit_right == right && ext->fit_top == top && ext->fit_bottom == bottom) {
161         return;
162     }
163 
164     ext->fit_left   = left;
165     ext->fit_right  = right;
166     ext->fit_top    = top;
167     ext->fit_bottom = bottom;
168 
169     /*Send a signal to refresh the layout*/
170     cont->signal_cb(cont, LV_SIGNAL_CHILD_CHG, NULL);
171 }
172 
173 /*=====================
174  * Getter functions
175  *====================*/
176 
177 /**
178  * Get the layout of a container
179  * @param cont pointer to container object
180  * @return the layout from 'lv_cont_layout_t'
181  */
lv_cont_get_layout(const lv_obj_t * cont)182 lv_layout_t lv_cont_get_layout(const lv_obj_t * cont)
183 {
184     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
185 
186     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
187     return ext->layout;
188 }
189 
190 /**
191  * Get left 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_left(const lv_obj_t * cont)195 lv_fit_t lv_cont_get_fit_left(const lv_obj_t * cont)
196 {
197     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
198 
199     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
200     return ext->fit_left;
201 }
202 
203 /**
204  * Get right fit mode of a container
205  * @param cont pointer to a container object
206  * @return an element of `lv_fit_t`
207  */
lv_cont_get_fit_right(const lv_obj_t * cont)208 lv_fit_t lv_cont_get_fit_right(const lv_obj_t * cont)
209 {
210     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
211 
212     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
213     return ext->fit_right;
214 }
215 
216 /**
217  * Get top fit mode of a container
218  * @param cont pointer to a container object
219  * @return an element of `lv_fit_t`
220  */
lv_cont_get_fit_top(const lv_obj_t * cont)221 lv_fit_t lv_cont_get_fit_top(const lv_obj_t * cont)
222 {
223     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
224 
225     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
226     return ext->fit_top;
227 }
228 
229 /**
230  * Get bottom fit mode of a container
231  * @param cont pointer to a container object
232  * @return an element of `lv_fit_t`
233  */
lv_cont_get_fit_bottom(const lv_obj_t * cont)234 lv_fit_t lv_cont_get_fit_bottom(const lv_obj_t * cont)
235 {
236     LV_ASSERT_OBJ(cont, LV_OBJX_NAME);
237 
238     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
239     return ext->fit_bottom;
240 }
241 
242 /**********************
243  *   STATIC FUNCTIONS
244  **********************/
245 
246 /**
247  * Signal function of the container
248  * @param cont pointer to a container object
249  * @param sign a signal type from lv_signal_t enum
250  * @param param pointer to a signal specific variable
251  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
252  */
lv_cont_signal(lv_obj_t * cont,lv_signal_t sign,void * param)253 static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param)
254 {
255     if(sign == LV_SIGNAL_GET_STYLE) {
256         lv_get_style_info_t * info = param;
257         info->result = lv_cont_get_style(cont, info->part);
258         if(info->result != NULL) return LV_RES_OK;
259         else return ancestor_signal(cont, sign, param);
260     }
261 
262     lv_res_t res;
263 
264     /* Include the ancient signal function */
265     res = ancestor_signal(cont, sign, param);
266     if(res != LV_RES_OK) return res;
267     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
268 
269     if(sign == LV_SIGNAL_STYLE_CHG) { /*Recalculate the padding if the style changed*/
270         lv_cont_refr_layout(cont);
271         lv_cont_refr_autofit(cont);
272     }
273     else if(sign == LV_SIGNAL_CHILD_CHG) {
274         lv_cont_refr_layout(cont);
275         lv_cont_refr_autofit(cont);
276     }
277     else if(sign == LV_SIGNAL_COORD_CHG) {
278         if(lv_obj_get_width(cont) != lv_area_get_width(param) || lv_obj_get_height(cont) != lv_area_get_height(param)) {
279             lv_cont_refr_layout(cont);
280             lv_cont_refr_autofit(cont);
281         }
282     }
283     else if(sign == LV_SIGNAL_PARENT_SIZE_CHG) {
284         /*MAX and EDGE fit needs to be refreshed if the parent's size has changed*/
285         lv_cont_refr_autofit(cont);
286     }
287 
288     return res;
289 }
290 
291 
lv_cont_get_style(lv_obj_t * cont,uint8_t type)292 static lv_style_list_t * lv_cont_get_style(lv_obj_t * cont, uint8_t type)
293 {
294     lv_style_list_t * style_dsc_p;
295     switch(type) {
296         case LV_CONT_PART_MAIN:
297             style_dsc_p = &cont->style_list;
298             break;
299         default:
300             style_dsc_p = NULL;
301     }
302 
303     return style_dsc_p;
304 }
305 
306 /**
307  * Refresh the layout of a container
308  * @param cont pointer to an object which layout should be refreshed
309  */
lv_cont_refr_layout(lv_obj_t * cont)310 static void lv_cont_refr_layout(lv_obj_t * cont)
311 {
312     if(lv_obj_is_protected(cont, LV_PROTECT_CHILD_CHG)) return;
313     lv_layout_t type = lv_cont_get_layout(cont);
314 
315     /*'cont' has to be at least 1 child*/
316     if(lv_obj_get_child(cont, NULL) == NULL) return;
317 
318     if(type == LV_LAYOUT_OFF) return;
319 
320     if(type == LV_LAYOUT_CENTER) {
321         lv_cont_layout_center(cont);
322     }
323     else if(type == LV_LAYOUT_COLUMN_LEFT || type == LV_LAYOUT_COLUMN_MID || type == LV_LAYOUT_COLUMN_RIGHT) {
324         lv_cont_layout_col(cont);
325     }
326     else if(type == LV_LAYOUT_ROW_TOP || type == LV_LAYOUT_ROW_MID || type == LV_LAYOUT_ROW_BOTTOM) {
327         lv_cont_layout_row(cont);
328     }
329     else if(type == LV_LAYOUT_PRETTY_MID || type == LV_LAYOUT_PRETTY_TOP || type == LV_LAYOUT_PRETTY_BOTTOM) {
330         lv_cont_layout_pretty(cont);
331     }
332     else if(type == LV_LAYOUT_GRID) {
333         lv_cont_layout_grid(cont);
334     }
335 }
336 
337 /**
338  * Handle column type layouts
339  * @param cont pointer to an object which layout should be handled
340  */
lv_cont_layout_col(lv_obj_t * cont)341 static void lv_cont_layout_col(lv_obj_t * cont)
342 {
343     lv_coord_t left = lv_obj_get_style_pad_left(cont, LV_CONT_PART_MAIN);
344     lv_coord_t right = lv_obj_get_style_pad_right(cont, LV_CONT_PART_MAIN);
345     lv_coord_t top = lv_obj_get_style_pad_top(cont, LV_CONT_PART_MAIN);
346     lv_coord_t inner = lv_obj_get_style_pad_inner(cont, LV_CONT_PART_MAIN);
347 
348     lv_layout_t type = lv_cont_get_layout(cont);
349     lv_obj_t * child;
350 
351     /*Adjust margin and get the alignment type*/
352     lv_align_t align;
353     lv_coord_t hpad_corr;
354 
355     switch(type) {
356         case LV_LAYOUT_COLUMN_LEFT:
357             hpad_corr = left;
358             align     = LV_ALIGN_IN_TOP_LEFT;
359             break;
360         case LV_LAYOUT_COLUMN_MID:
361             hpad_corr = 0;
362             align     = LV_ALIGN_IN_TOP_MID;
363             break;
364         case LV_LAYOUT_COLUMN_RIGHT:
365             hpad_corr = -right;
366             align     = LV_ALIGN_IN_TOP_RIGHT;
367             break;
368         default:
369             hpad_corr = 0;
370             align     = LV_ALIGN_IN_TOP_LEFT;
371             break;
372     }
373 
374     /* Disable child change action because the children will be moved a lot
375      * an unnecessary child change signals could be sent*/
376     lv_obj_add_protect(cont, LV_PROTECT_CHILD_CHG);
377     /* Align the children */
378     lv_coord_t last_cord = top;
379     _LV_LL_READ_BACK(cont->child_ll, child) {
380         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
381         lv_style_int_t mtop = lv_obj_get_style_margin_top(child, LV_OBJ_PART_MAIN);
382         lv_style_int_t mbottom = lv_obj_get_style_margin_bottom(child, LV_OBJ_PART_MAIN);
383         lv_style_int_t mleft = lv_obj_get_style_margin_left(child, LV_OBJ_PART_MAIN);
384         lv_obj_align(child, cont, align, hpad_corr + mleft, last_cord + mtop);
385         last_cord += lv_obj_get_height(child) + inner + mtop + mbottom;
386     }
387 
388     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
389 }
390 
391 /**
392  * Handle row type layouts
393  * @param cont pointer to an object which layout should be handled
394  */
lv_cont_layout_row(lv_obj_t * cont)395 static void lv_cont_layout_row(lv_obj_t * cont)
396 {
397 
398     lv_layout_t type = lv_cont_get_layout(cont);
399     lv_obj_t * child;
400 
401     /*Adjust margin and get the alignment type*/
402     lv_align_t align;
403     lv_coord_t vpad_corr;
404     lv_bidi_dir_t base_dir = lv_obj_get_base_dir(cont);
405     switch(type) {
406         case LV_LAYOUT_ROW_TOP:
407             vpad_corr = lv_obj_get_style_pad_top(cont, LV_CONT_PART_MAIN);
408             align     = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_TOP_RIGHT : LV_ALIGN_IN_TOP_LEFT;
409             break;
410         case LV_LAYOUT_ROW_MID:
411             vpad_corr = 0;
412             align     = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_RIGHT_MID : LV_ALIGN_IN_LEFT_MID;
413             break;
414         case LV_LAYOUT_ROW_BOTTOM:
415             vpad_corr = -lv_obj_get_style_pad_bottom(cont, LV_CONT_PART_MAIN);
416             align     = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_BOTTOM_RIGHT : LV_ALIGN_IN_BOTTOM_LEFT;
417             break;
418         default:
419             vpad_corr = 0;
420             align     = base_dir == LV_BIDI_DIR_RTL ? LV_ALIGN_IN_TOP_RIGHT : LV_ALIGN_IN_TOP_LEFT;
421             break;
422     }
423 
424     /* Disable child change action because the children will be moved a lot
425      * an unnecessary child change signals could be sent*/
426     lv_obj_add_protect(cont, LV_PROTECT_CHILD_CHG);
427 
428     /* Align the children */
429     lv_coord_t last_cord;
430     if(base_dir == LV_BIDI_DIR_RTL) last_cord = lv_obj_get_style_pad_right(cont, LV_CONT_PART_MAIN);
431     else last_cord = lv_obj_get_style_pad_left(cont, LV_CONT_PART_MAIN);
432 
433     lv_coord_t inner = lv_obj_get_style_pad_inner(cont, LV_CONT_PART_MAIN);
434 
435     _LV_LL_READ_BACK(cont->child_ll, child) {
436         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
437 
438         if(base_dir == LV_BIDI_DIR_RTL) lv_obj_align(child, cont, align, -last_cord, vpad_corr);
439         else lv_obj_align(child, cont, align, last_cord, vpad_corr);
440 
441         last_cord += lv_obj_get_width(child) + inner;
442     }
443 
444     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
445 }
446 
447 /**
448  * Handle the center layout
449  * @param cont pointer to an object which layout should be handled
450  */
lv_cont_layout_center(lv_obj_t * cont)451 static void lv_cont_layout_center(lv_obj_t * cont)
452 {
453     lv_obj_t * child;
454     uint32_t obj_num         = 0;
455     lv_coord_t h_tot         = 0;
456 
457     lv_coord_t inner = lv_obj_get_style_pad_inner(cont, LV_CONT_PART_MAIN);
458     _LV_LL_READ(cont->child_ll, child) {
459         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
460         h_tot += lv_obj_get_height(child) + inner;
461         obj_num++;
462     }
463 
464     if(obj_num == 0) return;
465 
466     h_tot -= inner;
467 
468     /* Disable child change action because the children will be moved a lot
469      * an unnecessary child change signals could be sent*/
470     lv_obj_add_protect(cont, LV_PROTECT_CHILD_CHG);
471 
472     /* Align the children */
473     lv_coord_t last_cord = -(h_tot / 2);
474     _LV_LL_READ_BACK(cont->child_ll, child) {
475         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
476 
477         lv_obj_align(child, cont, LV_ALIGN_CENTER, 0, last_cord + lv_obj_get_height(child) / 2);
478         last_cord += lv_obj_get_height(child) + inner;
479     }
480 
481     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
482 }
483 
484 /**
485  * Handle the pretty layout. Put as many object as possible in row
486  * then begin a new row
487  * @param cont pointer to an object which layout should be handled
488  */
lv_cont_layout_pretty(lv_obj_t * cont)489 static void lv_cont_layout_pretty(lv_obj_t * cont)
490 {
491     lv_layout_t type = lv_cont_get_layout(cont);
492 
493     lv_obj_t * child_rs;  /* Row starter child */
494     lv_obj_t * child_rc;  /* Row closer child */
495     lv_obj_t * child_tmp; /* Temporary child */
496     lv_coord_t w_obj         = lv_obj_get_width(cont);
497     lv_coord_t act_y         =  lv_obj_get_style_pad_top(cont, LV_CONT_PART_MAIN);
498     /* Disable child change action because the children will be moved a lot
499      * an unnecessary child change signals could be sent*/
500 
501     child_rs = _lv_ll_get_tail(&cont->child_ll); /*Set the row starter child*/
502     if(child_rs == NULL) return;                /*Return if no child*/
503 
504     lv_obj_add_protect(cont, LV_PROTECT_CHILD_CHG);
505     lv_coord_t pleft          =  lv_obj_get_style_pad_left(cont, LV_CONT_PART_MAIN);
506     lv_coord_t pright         =  lv_obj_get_style_pad_right(cont, LV_CONT_PART_MAIN);
507     lv_coord_t pinner = lv_obj_get_style_pad_inner(cont, LV_CONT_PART_MAIN);
508 
509     child_rc = child_rs; /*Initially the the row starter and closer is the same*/
510     while(child_rs != NULL) {
511         lv_coord_t h_row = 0;
512         lv_coord_t w_row = pleft + pright; /*The width is at least the left+right pad*/
513         uint32_t obj_num = 0;
514 
515         /*Find the row closer object and collect some data*/
516         do {
517             if(lv_obj_get_hidden(child_rc) == false && lv_obj_is_protected(child_rc, LV_PROTECT_POS) == false) {
518                 /*If this object is already not fit then break*/
519                 lv_coord_t w = lv_obj_get_width(child_rc);
520                 w += lv_obj_get_style_margin_left(child_rc, LV_OBJ_PART_MAIN);
521                 w += lv_obj_get_style_margin_right(child_rc, LV_OBJ_PART_MAIN);
522                 if(w_row + w > w_obj) {
523                     /*Step back one child because the last already not fit, so the previous is the
524                      * closer*/
525                     if(child_rc != NULL && obj_num != 0) {
526                         child_rc = _lv_ll_get_next(&cont->child_ll, child_rc);
527                     }
528                     break;
529                 }
530                 w_row += w + pinner; /*Add the object width + inner padding*/
531 
532                 lv_coord_t h = lv_obj_get_height(child_rc);
533                 h += lv_obj_get_style_margin_top(child_rc, LV_OBJ_PART_MAIN);
534                 h += lv_obj_get_style_margin_bottom(child_rc, LV_OBJ_PART_MAIN);
535                 h_row = LV_MATH_MAX(h_row, h);         /*Search the highest object*/
536                 obj_num++;
537                 if(lv_obj_is_protected(child_rc, LV_PROTECT_FOLLOW))
538                     break; /*If can not be followed by an other object then break here*/
539             }
540             child_rc = _lv_ll_get_prev(&cont->child_ll, child_rc); /*Load the next object*/
541             if(obj_num == 0)
542                 child_rs = child_rc; /*If the first object was hidden (or too long) then set the
543                                         next as first */
544         } while(child_rc != NULL);
545 
546         /*If the object is too long then align it to the middle*/
547         if(obj_num == 0) {
548             if(child_rc != NULL) {
549                 lv_style_int_t mtop = lv_obj_get_style_margin_top(child_rc, LV_OBJ_PART_MAIN);
550 
551                 lv_obj_align(child_rc, cont, LV_ALIGN_IN_TOP_MID, 0, act_y + mtop);
552                 h_row = lv_obj_get_height(child_rc); /*Not set previously because of the early break*/
553                 h_row += mtop;
554                 h_row += lv_obj_get_style_margin_bottom(child_rc, LV_OBJ_PART_MAIN);
555             }
556         }
557         /*If there is only one object in the row then align it to the middle*/
558         else if(obj_num == 1) {
559             lv_obj_align(child_rs, cont, LV_ALIGN_IN_TOP_MID,
560                          0,
561                          act_y + lv_obj_get_style_margin_top(child_rs, LV_OBJ_PART_MAIN));
562         }
563         /* Align the children (from child_rs to child_rc)*/
564         else {
565             w_row -= pinner * obj_num;
566             lv_coord_t new_pinner = (w_obj - w_row) / (obj_num - 1);
567             lv_coord_t act_x    = pleft; /*x init*/
568             child_tmp           = child_rs;
569             while(child_tmp != NULL) {
570                 if(lv_obj_get_hidden(child_tmp) == false && lv_obj_is_protected(child_tmp, LV_PROTECT_POS) == false) {
571                     lv_coord_t mleft = lv_obj_get_style_margin_left(child_tmp, LV_OBJ_PART_MAIN);
572                     lv_coord_t mright = lv_obj_get_style_margin_right(child_tmp, LV_OBJ_PART_MAIN);
573                     switch(type) {
574                         case LV_LAYOUT_PRETTY_TOP:
575                             lv_obj_align(child_tmp, cont, LV_ALIGN_IN_TOP_LEFT,
576                                          act_x + mleft,
577                                          act_y + lv_obj_get_style_margin_top(child_tmp, LV_OBJ_PART_MAIN));
578                             break;
579                         case LV_LAYOUT_PRETTY_MID:
580                             lv_obj_align(child_tmp, cont, LV_ALIGN_IN_TOP_LEFT,
581                                          act_x + mleft,
582                                          act_y + (h_row - lv_obj_get_height(child_tmp)) / 2);
583 
584                             break;
585                         case LV_LAYOUT_PRETTY_BOTTOM:
586                             lv_obj_align(child_tmp, cont, LV_ALIGN_IN_TOP_LEFT,
587                                          act_x + mleft,
588                                          act_y + h_row - lv_obj_get_height(child_tmp) - lv_obj_get_style_margin_bottom(child_tmp, LV_OBJ_PART_MAIN));
589                             break;
590                         default:
591                             break;
592                     }
593 
594                     act_x += lv_obj_get_width(child_tmp) + new_pinner + mleft + mright;
595                 }
596                 if(child_tmp == child_rc) break;
597                 child_tmp = _lv_ll_get_prev(&cont->child_ll, child_tmp);
598             }
599         }
600 
601         if(child_rc == NULL) break;
602         act_y += pinner + h_row;           /*y increment*/
603         child_rs = _lv_ll_get_prev(&cont->child_ll, child_rc); /*Go to the next object*/
604         child_rc = child_rs;
605     }
606     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
607 }
608 
609 /**
610  * Handle the grid layout. Align same-sized objects in a grid
611  * @param cont pointer to an object which layout should be handled
612  */
lv_cont_layout_grid(lv_obj_t * cont)613 static void lv_cont_layout_grid(lv_obj_t * cont)
614 {
615     lv_coord_t w_fit         =  lv_obj_get_width_fit(cont);
616     lv_coord_t inner = lv_obj_get_style_pad_inner(cont, LV_CONT_PART_MAIN);
617     lv_coord_t y_ofs = inner + lv_obj_get_height(lv_obj_get_child(cont, NULL));
618 
619     /* Disable child change action because the children will be moved a lot
620      * an unnecessary child change signals could be sent*/
621     lv_obj_add_protect(cont, LV_PROTECT_CHILD_CHG);
622 
623     /* Align the children */
624     lv_coord_t left = lv_obj_get_style_pad_left(cont, LV_CONT_PART_MAIN);
625     lv_coord_t act_x = left;
626     lv_coord_t act_y = lv_obj_get_style_pad_top(cont, LV_CONT_PART_MAIN);
627     lv_obj_t * child;
628     _LV_LL_READ_BACK(cont->child_ll, child) {
629         if(lv_obj_get_hidden(child) != false || lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
630         lv_coord_t obj_w = lv_obj_get_width(child);
631         if(act_x + obj_w > w_fit + left) {
632             act_x = left;
633             act_y += y_ofs;
634         }
635 
636         lv_obj_set_pos(child, act_x, act_y);
637         act_x += inner + obj_w;
638     }
639 
640     lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
641 }
642 
643 /**
644  * Handle auto fit. Set the size of the object to involve all children.
645  * @param cont pointer to an object which size will be modified
646  */
lv_cont_refr_autofit(lv_obj_t * cont)647 static void lv_cont_refr_autofit(lv_obj_t * cont)
648 {
649     if(lv_obj_is_protected(cont, LV_PROTECT_CHILD_CHG)) return;
650     lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
651 
652     if(ext->fit_left == LV_FIT_NONE && ext->fit_right == LV_FIT_NONE && ext->fit_top == LV_FIT_NONE &&
653        ext->fit_bottom == LV_FIT_NONE) {
654         return;
655     }
656 
657     lv_area_t tight_area;
658     lv_area_t ori;
659     lv_obj_t * child_i;
660 
661     lv_obj_t * par               = lv_obj_get_parent(cont);
662     lv_area_t parent_area;
663     lv_area_copy(&parent_area, &par->coords);
664     parent_area.x1 += lv_obj_get_style_pad_left(par, LV_OBJ_PART_MAIN);
665     parent_area.x2 -= lv_obj_get_style_pad_right(par, LV_OBJ_PART_MAIN);
666     parent_area.y1 += lv_obj_get_style_pad_top(par, LV_OBJ_PART_MAIN);
667     parent_area.y2 -= lv_obj_get_style_pad_bottom(par, LV_OBJ_PART_MAIN);
668 
669     /*Search the side coordinates of the children*/
670     lv_obj_get_coords(cont, &ori);
671     lv_obj_get_coords(cont, &tight_area);
672 
673     bool has_children = _lv_ll_is_empty(&cont->child_ll) ? false : true;
674 
675     if(has_children) {
676         tight_area.x1 = LV_COORD_MAX;
677         tight_area.y1 = LV_COORD_MAX;
678         tight_area.x2 = LV_COORD_MIN;
679         tight_area.y2 = LV_COORD_MIN;
680 
681         _LV_LL_READ(cont->child_ll, child_i) {
682             if(lv_obj_get_hidden(child_i) != false) continue;
683 
684             if(ext->fit_left != LV_FIT_PARENT) {
685                 lv_style_int_t mleft = lv_obj_get_style_margin_left(child_i, LV_OBJ_PART_MAIN);
686                 tight_area.x1 = LV_MATH_MIN(tight_area.x1, child_i->coords.x1 - mleft);
687             }
688 
689             if(ext->fit_right != LV_FIT_PARENT) {
690                 lv_style_int_t mright = lv_obj_get_style_margin_right(child_i, LV_OBJ_PART_MAIN);
691                 tight_area.x2 = LV_MATH_MAX(tight_area.x2, child_i->coords.x2 + mright);
692             }
693 
694             if(ext->fit_top != LV_FIT_PARENT) {
695                 lv_style_int_t mtop = lv_obj_get_style_margin_top(child_i, LV_OBJ_PART_MAIN);
696                 tight_area.y1 = LV_MATH_MIN(tight_area.y1, child_i->coords.y1 - mtop);
697             }
698 
699             if(ext->fit_bottom != LV_FIT_PARENT) {
700                 lv_style_int_t mbottom = lv_obj_get_style_margin_bottom(child_i, LV_OBJ_PART_MAIN);
701                 tight_area.y2 = LV_MATH_MAX(tight_area.y2, child_i->coords.y2 + mbottom);
702             }
703         }
704 
705         tight_area.x1 -= lv_obj_get_style_pad_left(cont, LV_CONT_PART_MAIN);
706         tight_area.x2 += lv_obj_get_style_pad_right(cont, LV_CONT_PART_MAIN);
707         tight_area.y1 -= lv_obj_get_style_pad_top(cont, LV_CONT_PART_MAIN);
708         tight_area.y2 += lv_obj_get_style_pad_bottom(cont, LV_CONT_PART_MAIN);
709     }
710 
711     lv_area_t new_area;
712     lv_area_copy(&new_area, &ori);
713 
714     switch(ext->fit_left) {
715         case LV_FIT_TIGHT:
716             new_area.x1 = tight_area.x1;
717             break;
718         case LV_FIT_PARENT:
719             new_area.x1 = parent_area.x1;
720             break;
721         case LV_FIT_MAX:
722             new_area.x1 = has_children ? LV_MATH_MIN(tight_area.x1, parent_area.x1) : parent_area.x1;
723             break;
724         default:
725             break;
726     }
727 
728     switch(ext->fit_right) {
729         case LV_FIT_TIGHT:
730             new_area.x2 = tight_area.x2;
731             break;
732         case LV_FIT_PARENT:
733             new_area.x2 = parent_area.x2;
734             break;
735         case LV_FIT_MAX:
736             new_area.x2 = has_children ? LV_MATH_MAX(tight_area.x2, parent_area.x2) : parent_area.x2;
737             break;
738         default:
739             break;
740     }
741 
742     switch(ext->fit_top) {
743         case LV_FIT_TIGHT:
744             new_area.y1 = tight_area.y1;
745             break;
746         case LV_FIT_PARENT:
747             new_area.y1 = parent_area.y1;
748             break;
749         case LV_FIT_MAX:
750             new_area.y1 = has_children ? LV_MATH_MIN(tight_area.y1, parent_area.y1) : parent_area.y1;
751             break;
752         default:
753             break;
754     }
755 
756     switch(ext->fit_bottom) {
757         case LV_FIT_TIGHT:
758             new_area.y2 = tight_area.y2;
759             break;
760         case LV_FIT_PARENT:
761             new_area.y2 = parent_area.y2;
762             break;
763         case LV_FIT_MAX:
764             new_area.y2 = has_children ? LV_MATH_MAX(tight_area.y2, parent_area.y2) : parent_area.y2;
765             break;
766         default:
767             break;
768     }
769 
770     /*Do nothing if the coordinates are not changed*/
771     if(cont->coords.x1 != new_area.x1 || cont->coords.y1 != new_area.y1 || cont->coords.x2 != new_area.x2 ||
772        cont->coords.y2 != new_area.y2) {
773 
774         lv_obj_invalidate(cont);
775         lv_area_copy(&cont->coords, &new_area);
776         lv_obj_invalidate(cont);
777 
778         /*Notify the object about its new coordinates*/
779         cont->signal_cb(cont, LV_SIGNAL_COORD_CHG, &ori);
780 
781         /*Inform the parent about the new coordinates*/
782         par->signal_cb(par, LV_SIGNAL_CHILD_CHG, cont);
783 
784         if(lv_obj_get_auto_realign(cont)) {
785             lv_obj_realign(cont);
786         }
787 
788         /*Tell the children the parent's size has changed*/
789         _LV_LL_READ(cont->child_ll, child_i) {
790             child_i->signal_cb(child_i, LV_SIGNAL_PARENT_SIZE_CHG, &ori);
791         }
792     }
793 }
794 
795 #endif
796