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