1 /**
2  * @file lv_grid.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../lv_layouts.h"
10 
11 #if LV_USE_GRID
12 
13 /*********************
14  *      DEFINES
15  *********************/
16 /**
17  * Some helper defines
18  */
19 #define IS_FR(x)       (x >= LV_COORD_MAX - 100)
20 #define IS_CONTENT(x)  (x == LV_COORD_MAX - 101)
21 #define GET_FR(x)      (x - (LV_COORD_MAX - 100))
22 
23 /**********************
24  *      TYPEDEFS
25  **********************/
26 typedef struct {
27     uint32_t col;
28     uint32_t row;
29     lv_point_t grid_abs;
30 } item_repos_hint_t;
31 
32 typedef struct {
33     lv_coord_t * x;
34     lv_coord_t * y;
35     lv_coord_t * w;
36     lv_coord_t * h;
37     uint32_t col_num;
38     uint32_t row_num;
39     lv_coord_t grid_w;
40     lv_coord_t grid_h;
41 } _lv_grid_calc_t;
42 
43 
44 /**********************
45  *  GLOBAL PROTOTYPES
46  **********************/
47 
48 /**********************
49  *  STATIC PROTOTYPES
50  **********************/
51 static void grid_update(lv_obj_t * cont, void * user_data);
52 static void calc(lv_obj_t * obj, _lv_grid_calc_t * calc);
53 static void calc_free(_lv_grid_calc_t * calc);
54 static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c);
55 static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c);
56 static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint);
57 static lv_coord_t grid_align(lv_coord_t cont_size,  bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num,
58                              lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse);
59 static uint32_t count_tracks(const lv_coord_t * templ);
60 
get_col_dsc(lv_obj_t * obj)61 static inline const lv_coord_t * get_col_dsc(lv_obj_t * obj)
62 {
63     return lv_obj_get_style_grid_column_dsc_array(obj, 0);
64 }
get_row_dsc(lv_obj_t * obj)65 static inline const lv_coord_t * get_row_dsc(lv_obj_t * obj)
66 {
67     return lv_obj_get_style_grid_row_dsc_array(obj, 0);
68 }
get_col_pos(lv_obj_t * obj)69 static inline uint8_t get_col_pos(lv_obj_t * obj)
70 {
71     return lv_obj_get_style_grid_cell_column_pos(obj, 0);
72 }
get_row_pos(lv_obj_t * obj)73 static inline uint8_t get_row_pos(lv_obj_t * obj)
74 {
75     return lv_obj_get_style_grid_cell_row_pos(obj, 0);
76 }
get_col_span(lv_obj_t * obj)77 static inline uint8_t get_col_span(lv_obj_t * obj)
78 {
79     return lv_obj_get_style_grid_cell_column_span(obj, 0);
80 }
get_row_span(lv_obj_t * obj)81 static inline uint8_t get_row_span(lv_obj_t * obj)
82 {
83     return lv_obj_get_style_grid_cell_row_span(obj, 0);
84 }
get_cell_col_align(lv_obj_t * obj)85 static inline uint8_t get_cell_col_align(lv_obj_t * obj)
86 {
87     return lv_obj_get_style_grid_cell_x_align(obj, 0);
88 }
get_cell_row_align(lv_obj_t * obj)89 static inline uint8_t get_cell_row_align(lv_obj_t * obj)
90 {
91     return lv_obj_get_style_grid_cell_y_align(obj, 0);
92 }
get_grid_col_align(lv_obj_t * obj)93 static inline uint8_t get_grid_col_align(lv_obj_t * obj)
94 {
95     return lv_obj_get_style_grid_column_align(obj, 0);
96 }
get_grid_row_align(lv_obj_t * obj)97 static inline uint8_t get_grid_row_align(lv_obj_t * obj)
98 {
99     return lv_obj_get_style_grid_row_align(obj, 0);
100 }
101 
102 /**********************
103  *  GLOBAL VARIABLES
104  **********************/
105 uint32_t LV_LAYOUT_GRID;
106 lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
107 lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
108 lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
109 lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
110 lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
111 lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
112 lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
113 lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
114 lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
115 lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
116 
117 /**********************
118  *  STATIC VARIABLES
119  **********************/
120 
121 /**********************
122  *      MACROS
123  **********************/
124 
125 /**********************
126  *   GLOBAL FUNCTIONS
127  **********************/
128 
129 
lv_grid_init(void)130 void lv_grid_init(void)
131 {
132     LV_LAYOUT_GRID = lv_layout_register(grid_update, NULL);
133 
134     LV_STYLE_GRID_COLUMN_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
135     LV_STYLE_GRID_ROW_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
136     LV_STYLE_GRID_COLUMN_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
137     LV_STYLE_GRID_ROW_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
138 
139     LV_STYLE_GRID_CELL_ROW_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
140     LV_STYLE_GRID_CELL_ROW_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
141     LV_STYLE_GRID_CELL_COLUMN_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
142     LV_STYLE_GRID_CELL_COLUMN_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
143     LV_STYLE_GRID_CELL_X_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
144     LV_STYLE_GRID_CELL_Y_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
145 }
146 
lv_obj_set_grid_dsc_array(lv_obj_t * obj,const lv_coord_t col_dsc[],const lv_coord_t row_dsc[])147 void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[])
148 {
149     lv_obj_set_style_grid_column_dsc_array(obj, col_dsc, 0);
150     lv_obj_set_style_grid_row_dsc_array(obj, row_dsc, 0);
151     lv_obj_set_style_layout(obj, LV_LAYOUT_GRID, 0);
152 }
153 
lv_obj_set_grid_align(lv_obj_t * obj,lv_grid_align_t column_align,lv_grid_align_t row_align)154 void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align)
155 {
156     lv_obj_set_style_grid_column_align(obj, column_align, 0);
157     lv_obj_set_style_grid_row_align(obj, row_align, 0);
158 
159 }
160 
lv_obj_set_grid_cell(lv_obj_t * obj,lv_grid_align_t x_align,uint8_t col_pos,uint8_t col_span,lv_grid_align_t y_align,uint8_t row_pos,uint8_t row_span)161 void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t x_align, uint8_t col_pos, uint8_t col_span,
162                           lv_grid_align_t y_align, uint8_t row_pos, uint8_t row_span)
163 
164 {
165     lv_obj_set_style_grid_cell_column_pos(obj, col_pos, 0);
166     lv_obj_set_style_grid_cell_row_pos(obj, row_pos, 0);
167     lv_obj_set_style_grid_cell_x_align(obj, x_align, 0);
168     lv_obj_set_style_grid_cell_column_span(obj, col_span, 0);
169     lv_obj_set_style_grid_cell_row_span(obj, row_span, 0);
170     lv_obj_set_style_grid_cell_y_align(obj, y_align, 0);
171 
172     lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
173 }
174 
175 
lv_style_set_grid_row_dsc_array(lv_style_t * style,const lv_coord_t value[])176 void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[])
177 {
178     lv_style_value_t v = {
179         .ptr = (const void *)value
180     };
181     lv_style_set_prop(style, LV_STYLE_GRID_ROW_DSC_ARRAY, v);
182 }
183 
lv_style_set_grid_column_dsc_array(lv_style_t * style,const lv_coord_t value[])184 void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[])
185 {
186     lv_style_value_t v = {
187         .ptr = (const void *)value
188     };
189     lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v);
190 }
191 
lv_style_set_grid_row_align(lv_style_t * style,lv_grid_align_t value)192 void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value)
193 {
194     lv_style_value_t v = {
195         .num = (lv_grid_align_t)value
196     };
197     lv_style_set_prop(style, LV_STYLE_GRID_ROW_ALIGN, v);
198 }
199 
lv_style_set_grid_column_align(lv_style_t * style,lv_grid_align_t value)200 void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value)
201 {
202     lv_style_value_t v = {
203         .num = (lv_grid_align_t)value
204     };
205     lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_ALIGN, v);
206 }
207 
208 
lv_style_set_grid_cell_column_pos(lv_style_t * style,lv_coord_t value)209 void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value)
210 {
211     lv_style_value_t v = {
212         .num = value
213     };
214     lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_POS, v);
215 }
216 
lv_style_set_grid_cell_column_span(lv_style_t * style,lv_coord_t value)217 void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value)
218 {
219     lv_style_value_t v = {
220         .num = value
221     };
222     lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_SPAN, v);
223 }
224 
lv_style_set_grid_cell_row_pos(lv_style_t * style,lv_coord_t value)225 void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value)
226 {
227     lv_style_value_t v = {
228         .num = value
229     };
230     lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_POS, v);
231 }
232 
lv_style_set_grid_cell_row_span(lv_style_t * style,lv_coord_t value)233 void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value)
234 {
235     lv_style_value_t v = {
236         .num = value
237     };
238     lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_SPAN, v);
239 }
240 
lv_style_set_grid_cell_x_align(lv_style_t * style,lv_coord_t value)241 void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value)
242 {
243     lv_style_value_t v = {
244         .num = value
245     };
246     lv_style_set_prop(style, LV_STYLE_GRID_CELL_X_ALIGN, v);
247 }
248 
lv_style_set_grid_cell_y_align(lv_style_t * style,lv_coord_t value)249 void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value)
250 {
251     lv_style_value_t v = {
252         .num = value
253     };
254     lv_style_set_prop(style, LV_STYLE_GRID_CELL_Y_ALIGN, v);
255 }
256 
lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj,const lv_coord_t value[],lv_style_selector_t selector)257 void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
258 {
259     lv_style_value_t v = {
260         .ptr = (const void *)value
261     };
262     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_DSC_ARRAY, v, selector);
263 }
264 
lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj,const lv_coord_t value[],lv_style_selector_t selector)265 void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
266 {
267     lv_style_value_t v = {
268         .ptr = (const void *)value
269     };
270     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v, selector);
271 }
272 
273 
lv_obj_set_style_grid_row_align(lv_obj_t * obj,lv_grid_align_t value,lv_style_selector_t selector)274 void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
275 {
276     lv_style_value_t v = {
277         .num = (int32_t) value
278     };
279     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_ALIGN, v, selector);
280 }
281 
lv_obj_set_style_grid_column_align(lv_obj_t * obj,lv_grid_align_t value,lv_style_selector_t selector)282 void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
283 {
284     lv_style_value_t v = {
285         .num = (int32_t) value
286     };
287     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_ALIGN, v, selector);
288 }
289 
290 
lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj,lv_coord_t value,lv_style_selector_t selector)291 void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
292 {
293     lv_style_value_t v = {
294         .num = value
295     };
296     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_POS, v, selector);
297 }
298 
lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj,lv_coord_t value,lv_style_selector_t selector)299 void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
300 {
301     lv_style_value_t v = {
302         .num = value
303     };
304     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_SPAN, v, selector);
305 }
306 
lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj,lv_coord_t value,lv_style_selector_t selector)307 void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
308 {
309     lv_style_value_t v = {
310         .num = value
311     };
312     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_POS, v, selector);
313 }
314 
lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj,lv_coord_t value,lv_style_selector_t selector)315 void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
316 {
317     lv_style_value_t v = {
318         .num = value
319     };
320     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_SPAN, v, selector);
321 }
322 
lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj,lv_coord_t value,lv_style_selector_t selector)323 void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
324 {
325     lv_style_value_t v = {
326         .num = value
327     };
328     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_X_ALIGN, v, selector);
329 }
330 
lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj,lv_coord_t value,lv_style_selector_t selector)331 void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
332 {
333     lv_style_value_t v = {
334         .num = value
335     };
336     lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_Y_ALIGN, v, selector);
337 }
338 
339 
340 /**********************
341  *   STATIC FUNCTIONS
342  **********************/
343 
grid_update(lv_obj_t * cont,void * user_data)344 static void grid_update(lv_obj_t * cont, void * user_data)
345 {
346     LV_LOG_INFO("update %p container", (void *)cont);
347     LV_UNUSED(user_data);
348 
349     const lv_coord_t * col_templ = get_col_dsc(cont);
350     const lv_coord_t * row_templ = get_row_dsc(cont);
351     if(col_templ == NULL || row_templ == NULL) return;
352 
353     _lv_grid_calc_t c;
354     calc(cont, &c);
355 
356     item_repos_hint_t hint;
357     lv_memset_00(&hint, sizeof(hint));
358 
359     /*Calculate the grids absolute x and y coordinates.
360      *It will be used as helper during item repositioning to avoid calculating this value for every children*/
361     lv_coord_t border_widt = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
362     lv_coord_t pad_left = lv_obj_get_style_pad_left(cont, LV_PART_MAIN) + border_widt;
363     lv_coord_t pad_top = lv_obj_get_style_pad_top(cont, LV_PART_MAIN) + border_widt;
364     hint.grid_abs.x = pad_left + cont->coords.x1 - lv_obj_get_scroll_x(cont);
365     hint.grid_abs.y = pad_top + cont->coords.y1 - lv_obj_get_scroll_y(cont);
366 
367     uint32_t i;
368     for(i = 0; i < cont->spec_attr->child_cnt; i++) {
369         lv_obj_t * item = cont->spec_attr->children[i];
370         item_repos(item, &c, &hint);
371     }
372     calc_free(&c);
373 
374     lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
375     lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
376     if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
377         lv_obj_refr_size(cont);
378     }
379 
380     lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
381 
382     LV_TRACE_LAYOUT("finished");
383 }
384 
385 /**
386  * Calculate the grid cells coordinates
387  * @param cont an object that has a grid
388  * @param calc store the calculated cells sizes here
389  * @note `_lv_grid_calc_free(calc_out)` needs to be called when `calc_out` is not needed anymore
390  */
calc(lv_obj_t * cont,_lv_grid_calc_t * calc_out)391 static void calc(lv_obj_t * cont, _lv_grid_calc_t * calc_out)
392 {
393     if(lv_obj_get_child(cont, 0) == NULL) {
394         lv_memset_00(calc_out, sizeof(_lv_grid_calc_t));
395         return;
396     }
397 
398     calc_rows(cont, calc_out);
399     calc_cols(cont, calc_out);
400 
401     lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
402     lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
403 
404     bool rev = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
405 
406     lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
407     lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
408     bool auto_w = (w_set == LV_SIZE_CONTENT && !cont->w_layout) ? true : false;
409     lv_coord_t cont_w = lv_obj_get_content_width(cont);
410     calc_out->grid_w = grid_align(cont_w, auto_w, get_grid_col_align(cont), col_gap, calc_out->col_num, calc_out->w,
411                                   calc_out->x, rev);
412 
413     bool auto_h = (h_set == LV_SIZE_CONTENT && !cont->h_layout) ? true : false;
414     lv_coord_t cont_h = lv_obj_get_content_height(cont);
415     calc_out->grid_h = grid_align(cont_h, auto_h, get_grid_row_align(cont), row_gap, calc_out->row_num, calc_out->h,
416                                   calc_out->y, false);
417 
418     LV_ASSERT_MEM_INTEGRITY();
419 }
420 
421 /**
422  * Free the a grid calculation's data
423  * @param calc pointer to the calculated grid cell coordinates
424  */
calc_free(_lv_grid_calc_t * calc)425 static void calc_free(_lv_grid_calc_t * calc)
426 {
427     lv_mem_buf_release(calc->x);
428     lv_mem_buf_release(calc->y);
429     lv_mem_buf_release(calc->w);
430     lv_mem_buf_release(calc->h);
431 }
432 
calc_cols(lv_obj_t * cont,_lv_grid_calc_t * c)433 static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c)
434 {
435     const lv_coord_t * col_templ = get_col_dsc(cont);
436     lv_coord_t cont_w = lv_obj_get_content_width(cont);
437 
438     c->col_num = count_tracks(col_templ);
439     c->x = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
440     c->w = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
441 
442     /*Set sizes for CONTENT cells*/
443     uint32_t i;
444     for(i = 0; i < c->col_num; i++) {
445         lv_coord_t size = LV_COORD_MIN;
446         if(IS_CONTENT(col_templ[i])) {
447             /*Check the size of children of this cell*/
448             uint32_t ci;
449             for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
450                 lv_obj_t * item = lv_obj_get_child(cont, ci);
451                 if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
452                 uint32_t col_span = get_col_span(item);
453                 if(col_span != 1) continue;
454 
455                 uint32_t col_pos = get_col_pos(item);
456                 if(col_pos != i) continue;
457 
458                 size = LV_MAX(size, lv_obj_get_width(item));
459             }
460             if(size >= 0) c->w[i] = size;
461             else c->w[i] = 0;
462         }
463     }
464 
465     uint32_t col_fr_cnt = 0;
466     lv_coord_t grid_w = 0;
467 
468     for(i = 0; i < c->col_num; i++) {
469         lv_coord_t x = col_templ[i];
470         if(IS_FR(x)) {
471             col_fr_cnt += GET_FR(x);
472         }
473         else if(IS_CONTENT(x)) {
474             grid_w += c->w[i];
475         }
476         else {
477             c->w[i] = x;
478             grid_w += x;
479         }
480     }
481 
482     lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
483     cont_w -= col_gap * (c->col_num - 1);
484     lv_coord_t free_w = cont_w - grid_w;
485     if(free_w < 0) free_w = 0;
486 
487     int32_t last_fr_i = -1;
488     int32_t last_fr_x = 0;
489     for(i = 0; i < c->col_num; i++) {
490         lv_coord_t x = col_templ[i];
491         if(IS_FR(x)) {
492             lv_coord_t f = GET_FR(x);
493             c->w[i] = (free_w * f) / col_fr_cnt;
494             last_fr_i = i;
495             last_fr_x = f;
496         }
497     }
498 
499     /*To avoid rounding errors set the last FR track to the remaining size */
500     if(last_fr_i >= 0) {
501         c->w[last_fr_i] = free_w - ((free_w * (col_fr_cnt - last_fr_x)) / col_fr_cnt);
502     }
503 }
504 
calc_rows(lv_obj_t * cont,_lv_grid_calc_t * c)505 static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c)
506 {
507     uint32_t i;
508     const lv_coord_t * row_templ = get_row_dsc(cont);
509     c->row_num = count_tracks(row_templ);
510     c->y = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
511     c->h = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
512     /*Set sizes for CONTENT cells*/
513     for(i = 0; i < c->row_num; i++) {
514         lv_coord_t size = LV_COORD_MIN;
515         if(IS_CONTENT(row_templ[i])) {
516             /*Check the size of children of this cell*/
517             uint32_t ci;
518             for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
519                 lv_obj_t * item = lv_obj_get_child(cont, ci);
520                 if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
521                 uint32_t row_span = get_row_span(item);
522                 if(row_span != 1) continue;
523 
524                 uint32_t row_pos = get_row_pos(item);
525                 if(row_pos != i) continue;
526 
527                 size = LV_MAX(size, lv_obj_get_height(item));
528             }
529             if(size >= 0) c->h[i] = size;
530             else c->h[i] = 0;
531         }
532     }
533 
534     uint32_t row_fr_cnt = 0;
535     lv_coord_t grid_h = 0;
536 
537     for(i = 0; i < c->row_num; i++) {
538         lv_coord_t x = row_templ[i];
539         if(IS_FR(x)) {
540             row_fr_cnt += GET_FR(x);
541         }
542         else if(IS_CONTENT(x)) {
543             grid_h += c->h[i];
544         }
545         else {
546             c->h[i] = x;
547             grid_h += x;
548         }
549     }
550 
551 
552     lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
553     lv_coord_t cont_h = lv_obj_get_content_height(cont) - row_gap * (c->row_num - 1);
554     lv_coord_t free_h = cont_h - grid_h;
555     if(free_h < 0) free_h = 0;
556 
557     int32_t last_fr_i = -1;
558     int32_t last_fr_x = 0;
559     for(i = 0; i < c->row_num; i++) {
560         lv_coord_t x = row_templ[i];
561         if(IS_FR(x)) {
562             lv_coord_t f = GET_FR(x);
563             c->h[i] = (free_h * f) / row_fr_cnt;
564         }
565     }
566 
567     /*To avoid rounding errors set the last FR track to the remaining size */
568     if(last_fr_i >= 0) {
569         c->h[last_fr_i] = free_h - ((free_h * (row_fr_cnt - last_fr_x)) / row_fr_cnt);
570     }
571 }
572 
573 /**
574  * Reposition a grid item in its cell
575  * @param item a grid item to reposition
576  * @param calc the calculated grid of `cont`
577  * @param child_id_ext helper value if the ID of the child is know (order from the oldest) else -1
578  * @param grid_abs helper value, the absolute position of the grid, NULL if unknown
579  */
item_repos(lv_obj_t * item,_lv_grid_calc_t * c,item_repos_hint_t * hint)580 static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint)
581 {
582     if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) return;
583     uint32_t col_span = get_col_span(item);
584     uint32_t row_span = get_row_span(item);
585     if(row_span == 0 || col_span == 0) return;
586 
587     uint32_t col_pos = get_col_pos(item);
588     uint32_t row_pos = get_row_pos(item);
589     lv_grid_align_t col_align = get_cell_col_align(item);
590     lv_grid_align_t row_align = get_cell_row_align(item);
591 
592 
593     lv_coord_t col_x1 = c->x[col_pos];
594     lv_coord_t col_x2 = c->x[col_pos + col_span - 1] + c->w[col_pos + col_span - 1];
595     lv_coord_t col_w = col_x2 - col_x1;
596 
597     lv_coord_t row_y1 = c->y[row_pos];
598     lv_coord_t row_y2 = c->y[row_pos + row_span - 1] + c->h[row_pos + row_span - 1];
599     lv_coord_t row_h = row_y2 - row_y1;
600 
601 
602     /*If the item has RTL base dir switch start and end*/
603     if(lv_obj_get_style_base_dir(item, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
604         if(col_align == LV_GRID_ALIGN_START) col_align = LV_GRID_ALIGN_END;
605         else if(col_align == LV_GRID_ALIGN_END) col_align = LV_GRID_ALIGN_START;
606     }
607 
608     lv_coord_t x;
609     lv_coord_t y;
610     lv_coord_t item_w = lv_area_get_width(&item->coords);
611     lv_coord_t item_h = lv_area_get_height(&item->coords);
612 
613     switch(col_align) {
614         default:
615         case LV_GRID_ALIGN_START:
616             x = c->x[col_pos];
617             item->w_layout = 0;
618             break;
619         case LV_GRID_ALIGN_STRETCH:
620             x = c->x[col_pos];
621             item_w = col_w;
622             item->w_layout = 1;
623             break;
624         case LV_GRID_ALIGN_CENTER:
625             x = c->x[col_pos] + (col_w - item_w) / 2;
626             item->w_layout = 0;
627             break;
628         case LV_GRID_ALIGN_END:
629             x = c->x[col_pos] + col_w - lv_obj_get_width(item);
630             item->w_layout = 0;
631             break;
632     }
633 
634     switch(row_align) {
635         default:
636         case LV_GRID_ALIGN_START:
637             y = c->y[row_pos];
638             item->h_layout = 0;
639             break;
640         case LV_GRID_ALIGN_STRETCH:
641             y = c->y[row_pos];
642             item_h = row_h;
643             item->h_layout = 1;
644             break;
645         case LV_GRID_ALIGN_CENTER:
646             y = c->y[row_pos] + (row_h - item_h) / 2;
647             item->h_layout = 0;
648             break;
649         case LV_GRID_ALIGN_END:
650             y = c->y[row_pos] + row_h - lv_obj_get_height(item);
651             item->h_layout = 0;
652             break;
653     }
654 
655     /*Set a new size if required*/
656     if(lv_obj_get_width(item) != item_w || lv_obj_get_height(item) != item_h) {
657         lv_area_t old_coords;
658         lv_area_copy(&old_coords, &item->coords);
659         lv_obj_invalidate(item);
660         lv_area_set_width(&item->coords, item_w);
661         lv_area_set_height(&item->coords, item_h);
662         lv_obj_invalidate(item);
663         lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
664         lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
665 
666     }
667 
668     /*Handle percentage value of translate*/
669     lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
670     lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
671     lv_coord_t w = lv_obj_get_width(item);
672     lv_coord_t h = lv_obj_get_height(item);
673     if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
674     if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
675 
676     x += tr_x;
677     y += tr_y;
678 
679     lv_coord_t diff_x = hint->grid_abs.x + x - item->coords.x1;
680     lv_coord_t diff_y = hint->grid_abs.y + y - item->coords.y1;
681     if(diff_x || diff_y) {
682         lv_obj_invalidate(item);
683         item->coords.x1 += diff_x;
684         item->coords.x2 += diff_x;
685         item->coords.y1 += diff_y;
686         item->coords.y2 += diff_y;
687         lv_obj_invalidate(item);
688         lv_obj_move_children_by(item, diff_x, diff_y, true);
689     }
690 }
691 
692 /**
693  * Place the grid track according to align methods. It keeps the track sizes but sets their position.
694  * It can process both columns or rows according to the passed parameters.
695  * @param cont_size size of the containers content area (width/height)
696  * @param auto_size true: the container has auto size in the current direction
697  * @param align align method
698  * @param gap grid gap
699  * @param track_num number of tracks
700  * @param size_array array with the track sizes
701  * @param pos_array write the positions of the tracks here
702  * @return the total size of the grid
703  */
grid_align(lv_coord_t cont_size,bool auto_size,uint8_t align,lv_coord_t gap,uint32_t track_num,lv_coord_t * size_array,lv_coord_t * pos_array,bool reverse)704 static lv_coord_t grid_align(lv_coord_t cont_size,  bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num,
705                              lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse)
706 {
707     lv_coord_t grid_size = 0;
708     uint32_t i;
709 
710     if(auto_size) {
711         pos_array[0] = 0;
712     }
713     else {
714         /*With spaced alignment gap will be calculated from the remaining space*/
715         if(align == LV_GRID_ALIGN_SPACE_AROUND || align == LV_GRID_ALIGN_SPACE_BETWEEN || align == LV_GRID_ALIGN_SPACE_EVENLY) {
716             gap = 0;
717             if(track_num == 1) align = LV_GRID_ALIGN_CENTER;
718         }
719 
720         /*Get the full grid size with gap*/
721         for(i = 0; i < track_num; i++) {
722             grid_size += size_array[i] + gap;
723         }
724         grid_size -= gap;
725 
726         /*Calculate the position of the first item and set gap is necessary*/
727         switch(align) {
728             case LV_GRID_ALIGN_START:
729                 pos_array[0] = 0;
730                 break;
731             case LV_GRID_ALIGN_CENTER:
732                 pos_array[0] = (cont_size - grid_size) / 2;
733                 break;
734             case LV_GRID_ALIGN_END:
735                 pos_array[0] = cont_size - grid_size;
736                 break;
737             case LV_GRID_ALIGN_SPACE_BETWEEN:
738                 pos_array[0] = 0;
739                 gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num - 1);
740                 break;
741             case LV_GRID_ALIGN_SPACE_AROUND:
742                 gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num);
743                 pos_array[0] = gap / 2;
744                 break;
745             case LV_GRID_ALIGN_SPACE_EVENLY:
746                 gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num + 1);
747                 pos_array[0] = gap;
748                 break;
749 
750         }
751     }
752 
753     /*Set the position of all tracks from the start position, gaps and track sizes*/
754     for(i = 0; i < track_num - 1; i++) {
755         pos_array[i + 1] = pos_array[i] + size_array[i] + gap;
756     }
757 
758     lv_coord_t total_gird_size = pos_array[track_num - 1] + size_array[track_num - 1] - pos_array[0];
759 
760     if(reverse) {
761         for(i = 0; i < track_num; i++) {
762             pos_array[i] = cont_size - pos_array[i] - size_array[i];
763         }
764 
765     }
766 
767     /*Return the full size of the grid*/
768     return total_gird_size;
769 }
770 
count_tracks(const lv_coord_t * templ)771 static uint32_t count_tracks(const lv_coord_t * templ)
772 {
773     uint32_t i;
774     for(i = 0; templ[i] != LV_GRID_TEMPLATE_LAST; i++);
775 
776     return i;
777 }
778 
779 
780 #endif /*LV_USE_GRID*/
781