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