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