1 #ifdef HAVE_CONFIG_H
2 #include "elementary_config.h"
3 #endif
4 
5 #include <Efl_Ui.h>
6 #include <Elementary.h>
7 #include "elm_widget.h"
8 #include "elm_priv.h"
9 #include "efl_ui_position_manager_common.h"
10 
11 #define MY_CLASS      EFL_UI_POSITION_MANAGER_GRID_CLASS
12 #define MY_DATA_GET(obj, pd) \
13   Efl_Ui_Position_Manager_Grid_Data *pd = efl_data_scope_get(obj, MY_CLASS);
14 
15 typedef struct {
16    Api_Callbacks callbacks;
17 
18    Eina_Inarray *group_cache;
19    int *size_cache;
20    Eo *last_group;
21    Eina_Future *rebuild_absolut_size;
22    Efl_Ui_Win *window;
23    Evas *canvas;
24 
25    Vis_Segment prev_run;
26 
27    Eina_Rect viewport;
28    Eina_Vector2 scroll_position;
29    Eina_Size2D max_min_size;
30    Eina_Size2D last_viewport_size;
31    Eina_Size2D prev_min_size;
32 
33    Efl_Ui_Layout_Orientation dir;
34 
35    unsigned int size;
36    unsigned int groups;
37    unsigned int prev_consumed_space;
38 
39    Eina_Bool group_cache_dirty;
40    Eina_Bool size_cache_dirty;
41 } Efl_Ui_Position_Manager_Grid_Data;
42 
43 typedef struct {
44    Eina_Size2D group_header_size;
45    int items;
46    Eina_Bool real_group;
47 } Group_Cache_Line;
48 
49 static inline void
_update_min_size(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,int added_index EINA_UNUSED,Eina_Size2D min_size)50 _update_min_size(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int added_index EINA_UNUSED, Eina_Size2D min_size)
51 {
52    pd->max_min_size.w = MAX(pd->max_min_size.w, min_size.w);
53    pd->max_min_size.h = MAX(pd->max_min_size.h, min_size.h);
54 }
55 
56 static void
_group_cache_require(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd)57 _group_cache_require(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
58 {
59    unsigned int i;
60    const int len = 100;
61    Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[len];
62    Efl_Ui_Position_Manager_Size_Batch_Result size_result;
63    Group_Cache_Line line = { 0 };
64 
65    if (!pd->group_cache_dirty)
66      return;
67 
68    pd->max_min_size = EINA_SIZE2D(0, 0);
69 
70    pd->group_cache_dirty = EINA_FALSE;
71    if (pd->group_cache)
72      eina_inarray_free(pd->group_cache);
73    pd->group_cache = eina_inarray_new(sizeof(Group_Cache_Line), 10);
74 
75    for (i = 0; i < pd->size; ++i)
76      {
77         int buffer_id = i % len;
78 
79         if (buffer_id == 0)
80           {
81              BATCH_ACCESS_SIZE(pd->callbacks, i, pd->size, MIN(len, pd->size - i), EINA_TRUE, size_buffer);
82           }
83 
84         if (size_buffer[buffer_id].depth_leader)
85           {
86              eina_inarray_push(pd->group_cache, &line);
87              line.real_group = EINA_TRUE;
88              line.group_header_size = size_buffer[buffer_id].size;
89              line.items = 1;
90           }
91         else if (size_buffer[buffer_id].element_depth > 0 ||
92                 (!line.real_group && size_buffer[buffer_id].element_depth == 0))
93           {
94              line.items ++;
95           }
96         else if (size_buffer[buffer_id].element_depth == 0 && line.real_group)
97           {
98              eina_inarray_push(pd->group_cache, &line);
99              line.real_group = EINA_FALSE;
100              line.group_header_size = EINA_SIZE2D(0, 0);
101              line.items = 0;
102           }
103         _update_min_size(obj, pd, i, size_buffer[buffer_id].size);
104      }
105    eina_inarray_push(pd->group_cache, &line);
106 }
107 
108 static inline void
_group_cache_invalidate(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd)109 _group_cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
110 {
111   pd->group_cache_dirty = EINA_TRUE;
112   pd->size_cache_dirty = EINA_TRUE;
113 }
114 
115 static void
_size_cache_require(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd)116 _size_cache_require(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
117 {
118    if (!pd->size_cache_dirty) return;
119 
120    _group_cache_require(obj, pd);
121 
122    pd->size_cache_dirty = EINA_FALSE;
123    if (pd->size_cache)
124      free(pd->size_cache);
125    pd->size_cache = calloc(sizeof(int), eina_inarray_count(pd->group_cache));
126 
127    for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
128      {
129          Group_Cache_Line *line = eina_inarray_nth(pd->group_cache, i);
130          int header_out = 0;
131          if (line->real_group)
132            header_out = 1;
133 
134          if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
135            pd->size_cache[i] = line->group_header_size.h +
136                         (ceil(
137                           (double)(line->items - header_out)/ /* the number of real items in the group (- the group item) */
138                           (int)(pd->viewport.w/pd->max_min_size.w))) /* devided by the number of items per row */
139                         *pd->max_min_size.h;
140          else
141            pd->size_cache[i] = (ceil((double)(line->items - header_out)/
142                                   (int)((pd->viewport.h-line->group_header_size.h)/pd->max_min_size.h)))*pd->max_min_size.w;
143      }
144 }
145 
146 static inline void
_size_cache_invalidate(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd)147 _size_cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
148 {
149   pd->size_cache_dirty = EINA_TRUE;
150 }
151 
152 typedef struct {
153   int resulting_id;
154   int consumed_space;
155 } Search_Result;
156 
157 static inline Search_Result
_search_id(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,int relevant_space_size)158 _search_id(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int relevant_space_size)
159 {
160    int consumed_space = 0;
161    int consumed_groups = -1;
162    int consumed_ids = 0;
163    int sub_ids = 0;
164    Search_Result res;
165 
166    //first we search how many blocks we can skip
167    for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
168      {
169         Group_Cache_Line *line = eina_inarray_nth(pd->group_cache, i);
170         if (consumed_space + pd->size_cache[i] > relevant_space_size)
171           break;
172         consumed_space += pd->size_cache[i];
173         consumed_groups = i;
174         consumed_ids += line->items;
175      }
176    Group_Cache_Line *line = NULL;
177    if (consumed_groups > -1 && consumed_groups + 1 < (int)eina_inarray_count(pd->group_cache))
178      line = eina_inarray_nth(pd->group_cache, consumed_groups + 1);
179    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
180      {
181         //now we have relevant_space_size - consumed_space left maybe we are searching the group item
182 
183         if (line && line->real_group)
184           {
185              if (consumed_space + line->group_header_size.h > relevant_space_size)
186                {
187                   res.resulting_id = consumed_ids;
188                   res.consumed_space = consumed_space;
189                   return res;
190                }
191              else
192                {
193                    consumed_space += line->group_header_size.h;
194                    consumed_ids += 1;
195                }
196           }
197         //now we need to locate at which id we are starting
198         int space_top = relevant_space_size - consumed_space;
199         consumed_space += floor(space_top/pd->max_min_size.h)*pd->max_min_size.h;
200         sub_ids = floor(space_top/pd->max_min_size.h)*(pd->viewport.w/pd->max_min_size.w);
201      }
202    else
203      {
204         int header_height = 0;
205         if (line && line->real_group)
206           {
207              header_height = line->group_header_size.h;
208           }
209         //now we need to locate at which id we are starting
210         const int space_left = relevant_space_size - consumed_space;
211         consumed_space += floor(space_left/pd->max_min_size.w)*pd->max_min_size.w;
212         sub_ids = floor(space_left/pd->max_min_size.w)*((pd->viewport.h-header_height)/pd->max_min_size.h);
213         if (line && line->real_group &&
214             sub_ids > 0) /* if we are in the first row, we need the group item to be visible, otherwise, we need to add that to the consumed ids */
215           {
216              sub_ids += 1;
217           }
218      }
219    res.resulting_id = consumed_ids + sub_ids;
220    res.consumed_space = consumed_space;
221    return res;
222 }
223 
224 static inline Eina_Bool
_search_start_end(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,int relevant_viewport,int relevant_space_size,unsigned int step,Vis_Segment * cur,int * consumed_space)225 _search_start_end(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int relevant_viewport, int relevant_space_size, unsigned int step, Vis_Segment *cur, int *consumed_space)
226 {
227    Search_Result start = _search_id(obj, pd, MAX(relevant_space_size, 0));
228    Search_Result end = _search_id(obj, pd, MAX(relevant_space_size, 0)+relevant_viewport+step*2);
229    cur->start_id = MIN(MAX(start.resulting_id, 0), (int)pd->size);
230    cur->end_id = MAX(MIN(end.resulting_id, (int)pd->size), 0);
231 
232    *consumed_space = start.consumed_space;
233 
234    return EINA_TRUE;
235 }
236 
237 typedef struct {
238    int relevant_space_size;
239    int consumed_space;
240    Vis_Segment new;
241    Eo *floating_group;
242    Eina_Size2D floating_size;
243    Eo *placed_item;
244 } Item_Position_Context;
245 
246 
247 static inline void
_place_grid_item(Eina_Rect * geom,Efl_Ui_Position_Manager_Grid_Data * pd,int x,int y)248 _place_grid_item(Eina_Rect *geom, Efl_Ui_Position_Manager_Grid_Data *pd, int x, int y)
249 {
250    geom->x += x*pd->max_min_size.w;
251    geom->y += y*pd->max_min_size.h;
252    geom->size = pd->max_min_size;
253 }
254 
255 static inline void
_position_items_vertical(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,Item_Position_Context * ctx)256 _position_items_vertical(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Item_Position_Context *ctx)
257 {
258    Eina_Position2D start = pd->viewport.pos;
259    unsigned int i;
260    const int len = 100;
261    int columns, last_block_start = ctx->new.start_id;
262    Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[len];
263    Efl_Ui_Position_Manager_Size_Batch_Result size_result;
264    Efl_Ui_Position_Manager_Object_Batch_Entity obj_buffer[len];
265    Efl_Ui_Position_Manager_Object_Batch_Result object_result;
266 
267    if (!pd->viewport.w || !pd->viewport.h) return;
268 
269    start.y -= (ctx->relevant_space_size - ctx->consumed_space);
270    columns = pd->viewport.w/pd->max_min_size.w;
271 
272    for (i = ctx->new.start_id; i < ctx->new.end_id; ++i)
273      {
274         int buffer_id = (i-ctx->new.start_id) % len;
275         if (buffer_id == 0)
276           {
277              BATCH_ACCESS_SIZE(pd->callbacks, i, ctx->new.end_id, len, EINA_FALSE, size_buffer);
278              BATCH_ACCESS_OBJECT(pd->callbacks, i, ctx->new.end_id, len, obj_buffer);
279 
280              if (i == ctx->new.start_id)
281                {
282                   ctx->floating_group = object_result.group;
283                   ctx->floating_size = size_result.parent_size;
284                   ctx->floating_size.w = pd->viewport.w;
285                }
286           }
287         Eina_Rect geom;
288         geom.pos = start;
289         int x = (i - last_block_start)%columns;
290         int y = (i - last_block_start)/columns;
291 
292         if (obj_buffer[buffer_id].entity == pd->last_group)
293           pd->last_group = NULL;
294 
295         if (obj_buffer[buffer_id].depth_leader)
296           {
297              if (x != 0)
298                y += 1;
299 
300              last_block_start = i + 1;
301              start.y += size_buffer[buffer_id].size.h + y*pd->max_min_size.h;
302 
303              geom.size = pd->viewport.size;
304              geom.h = size_buffer[buffer_id].size.h;
305              geom.y += y*pd->max_min_size.h;
306              if (!ctx->placed_item)
307                ctx->placed_item = obj_buffer[buffer_id].entity;
308           }
309         else
310           {
311              _place_grid_item(&geom, pd, x, y);
312           }
313         Efl_Gfx_Entity *item = obj_buffer[buffer_id].entity;
314         if (item)
315           {
316              efl_gfx_entity_geometry_set(item, geom);
317              efl_gfx_entity_visible_set(item, EINA_TRUE);
318           }
319      }
320 }
321 
322 static inline void
_position_items_horizontal(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,Item_Position_Context * ctx)323 _position_items_horizontal(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Item_Position_Context *ctx)
324 {
325    Eina_Position2D start = pd->viewport.pos;
326    unsigned int i;
327    const int len = 100;
328    int columns, last_block_start = ctx->new.start_id;
329    Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[len];
330    Efl_Ui_Position_Manager_Size_Batch_Result size_result;
331    Efl_Ui_Position_Manager_Object_Batch_Entity obj_buffer[len];
332    Efl_Ui_Position_Manager_Object_Batch_Result object_result;
333 
334    if (!pd->viewport.w || !pd->viewport.h) return;
335 
336    start.x -= (ctx->relevant_space_size - ctx->consumed_space);
337    columns = (pd->viewport.h)/pd->max_min_size.h;
338 
339    for (i = ctx->new.start_id; i < ctx->new.end_id; ++i)
340      {
341         int buffer_id = (i-ctx->new.start_id) % len;
342         if (buffer_id == 0)
343           {
344              BATCH_ACCESS_SIZE(pd->callbacks, i, ctx->new.end_id, len, EINA_FALSE, size_buffer);
345              BATCH_ACCESS_OBJECT(pd->callbacks, i, ctx->new.end_id, len, obj_buffer);
346 
347              if (i == ctx->new.start_id)
348                {
349                   ctx->floating_group = object_result.group;
350                   ctx->floating_size = size_result.parent_size;
351                   ctx->floating_size.w = pd->viewport.w;
352                   start.y += size_result.parent_size.h;
353                   columns = (pd->viewport.h - size_result.parent_size.h)/pd->max_min_size.h;
354                }
355           }
356         Eina_Rect geom;
357         geom.pos = start;
358 
359         int x = (i - last_block_start)/columns;
360         int y = (i - last_block_start)%columns;
361 
362         if (obj_buffer[buffer_id].entity == pd->last_group)
363           pd->last_group = NULL;
364 
365         if (obj_buffer[buffer_id].depth_leader)
366           {
367              last_block_start = i + 1;
368              start.y = pd->viewport.y + size_buffer[buffer_id].size.h;
369              start.x += x*pd->max_min_size.w;
370 
371              geom.size.h = size_buffer[buffer_id].size.h;
372              geom.size.w = pd->viewport.w;
373              geom.x += x*pd->max_min_size.w;
374              geom.y = pd->viewport.y;
375 
376              columns = (pd->viewport.h - size_buffer[buffer_id].size.h)/pd->max_min_size.h;
377              if (!ctx->placed_item)
378                ctx->placed_item = obj_buffer[buffer_id].entity;
379           }
380         else
381           {
382              _place_grid_item(&geom, pd, x, y);
383           }
384         Efl_Gfx_Entity *item = obj_buffer[buffer_id].entity;
385         if (item)
386           {
387              efl_gfx_entity_geometry_set(item, geom);
388              efl_gfx_entity_visible_set(item, EINA_TRUE);
389           }
390      }
391 }
392 
393 static inline void
_position_group_items(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,Item_Position_Context * ctx)394 _position_group_items(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Item_Position_Context *ctx)
395 {
396    //floating group is not yet positioned, in case it is there, we need to position it there
397    Eina_Rect geom;
398 
399    if (!ctx->floating_group && pd->last_group)
400      {
401         efl_gfx_entity_visible_set(pd->last_group, EINA_FALSE);
402         pd->last_group = NULL;
403      }
404 
405    if (ctx->floating_group)
406      {
407         geom.pos = pd->viewport.pos;
408         geom.size = ctx->floating_size;
409 
410         if (ctx->placed_item)
411           {
412             Eina_Rect placed = efl_gfx_entity_geometry_get(ctx->placed_item);
413              if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
414                {
415                   geom.y = MIN(geom.y, placed.y-geom.h);
416               }
417              else
418                {
419                   geom.x = MIN(geom.x, placed.x-geom.w);
420                }
421           }
422 
423         if (pd->last_group != ctx->floating_group)
424           {
425              efl_gfx_entity_visible_set(pd->last_group, EINA_FALSE);
426              pd->last_group = ctx->floating_group;
427           }
428 
429         efl_gfx_entity_visible_set(ctx->floating_group, EINA_TRUE);
430         efl_gfx_stack_raise_to_top(ctx->floating_group);
431         efl_gfx_entity_geometry_set(ctx->floating_group, geom);
432      }
433    else if (ctx->placed_item)
434      {
435         Eina_Rect placed = efl_gfx_entity_geometry_get(ctx->placed_item);
436 
437         placed.x = MAX(placed.x, pd->viewport.x);
438         placed.y = MAX(placed.y, pd->viewport.y);
439         efl_gfx_entity_geometry_set(ctx->placed_item, placed);
440         efl_gfx_stack_raise_to_top(ctx->placed_item);
441      }
442 }
443 
444 static void
_reposition_content(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd)445 _reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
446 {
447    Eina_Size2D space_size;
448    int relevant_space_size, relevant_viewport, consumed_space;
449    Vis_Segment cur;
450    unsigned int step;
451    Efl_Ui_Position_Manager_Range_Update ev;
452    Item_Position_Context ctx;
453 
454    if (!pd->size) return;
455    if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
456    if (!eina_inarray_count(pd->group_cache)) return;
457 
458    _size_cache_require(obj, pd);
459 
460    //space size contains the amount of space that is outside the viewport (either to the top or to the left)
461    space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
462    space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
463 
464    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
465      {
466         relevant_space_size = space_size.h;
467         relevant_viewport = pd->viewport.h;
468         step = pd->max_min_size.h;
469      }
470    else
471      {
472         relevant_space_size = space_size.w;
473         relevant_viewport = pd->viewport.w;
474         step = pd->max_min_size.w;
475      }
476    if (!_search_start_end(obj, pd, relevant_viewport, relevant_space_size, step, &cur, &consumed_space))
477      return;
478 
479    //to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
480    //The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
481    vis_segment_swap(pd->callbacks, cur, pd->prev_run);
482 
483    ctx.new = cur;
484    ctx.consumed_space = consumed_space;
485    ctx.relevant_space_size = relevant_space_size;
486    ctx.floating_group = NULL;
487    ctx.placed_item = NULL;
488 
489    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
490      {
491         _position_items_vertical(obj, pd, &ctx);
492         _position_group_items(obj, pd, &ctx);
493      }
494    else
495      {
496         _position_items_horizontal(obj, pd, &ctx);
497         _position_group_items(obj, pd, &ctx);
498      }
499 
500    if (pd->prev_run.start_id != cur.start_id || pd->prev_run.end_id != cur.end_id)
501      {
502         ev.start_id = pd->prev_run.start_id = cur.start_id;
503         ev.end_id = pd->prev_run.end_id = cur.end_id;
504         pd->prev_consumed_space = consumed_space;
505         efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, &ev);
506      }
507 }
508 
509 static inline void
_flush_abs_size(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd)510 _flush_abs_size(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd)
511 {
512    Eina_Size2D vp_size;
513    int sum_of_cache = 0;
514 
515    if (!pd->size) return;
516    if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
517 
518    _size_cache_require(obj, pd);
519    for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
520      {
521         sum_of_cache += pd->size_cache[i];
522      }
523 
524    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
525      {
526         vp_size.w = pd->viewport.w;
527         vp_size.h = sum_of_cache;
528      }
529    else
530      {
531         vp_size.h = pd->viewport.h;
532         vp_size.w = sum_of_cache;
533      }
534    if (vp_size.h != pd->last_viewport_size.h || vp_size.w != pd->last_viewport_size.w)
535      {
536         pd->last_viewport_size = vp_size;
537         efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, &vp_size);
538      }
539 }
540 
541 static inline void
_flush_min_size(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd)542 _flush_min_size(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd)
543 {
544    Eina_Size2D min_size = pd->max_min_size;
545 
546    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
547      min_size.h = -1;
548    else
549      min_size.w = -1;
550 
551    if (pd->prev_min_size.w != min_size.w || pd->prev_min_size.h != min_size.h)
552      {
553         pd->prev_min_size = min_size;
554         efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
555      }
556 }
557 
558 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_viewport_set(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,Eina_Rect viewport)559 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_viewport_set(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Eina_Rect viewport)
560 {
561    _size_cache_invalidate(obj, pd);
562    pd->viewport = viewport;
563    _flush_abs_size(obj, pd);
564    _reposition_content(obj, pd);
565 }
566 
567 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_scroll_position_set(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,double x,double y)568 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_scroll_position_set(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, double x, double y)
569 {
570    pd->scroll_position.x = x;
571    pd->scroll_position.y = y;
572    _reposition_content(obj, pd);
573 }
574 
575 static Eina_Value
_rebuild_job_cb(void * data,Eina_Value v EINA_UNUSED,const Eina_Future * f EINA_UNUSED)576 _rebuild_job_cb(void *data, Eina_Value v EINA_UNUSED, const Eina_Future *f EINA_UNUSED)
577 {
578    MY_DATA_GET(data, pd);
579 
580    if (!efl_alive_get(data)) return EINA_VALUE_EMPTY;
581 
582    _flush_abs_size(data, pd);
583    _reposition_content(data, pd);
584    pd->rebuild_absolut_size = NULL;
585 
586    return EINA_VALUE_EMPTY;
587 }
588 
589 static void
_schedule_recalc_abs_size(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd)590 _schedule_recalc_abs_size(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd)
591 {
592    if (pd->rebuild_absolut_size) return;
593 
594    pd->rebuild_absolut_size = efl_loop_job(efl_app_main_get());
595    eina_future_then(pd->rebuild_absolut_size, _rebuild_job_cb, obj);
596 }
597 
598 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_added(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd,int added_index,Efl_Gfx_Entity * subobj EINA_UNUSED)599 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_added(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd, int added_index, Efl_Gfx_Entity *subobj EINA_UNUSED)
600 {
601    Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[1];
602    Efl_Ui_Position_Manager_Size_Batch_Result size_result;
603    pd->size ++;
604 
605    efl_gfx_entity_visible_set(subobj, EINA_FALSE);
606    _group_cache_invalidate(obj, pd);
607    BATCH_ACCESS_SIZE(pd->callbacks, added_index, added_index + 1, 1, EINA_TRUE, size_buffer);
608    _update_min_size(obj, pd, added_index, size_buffer[0].size);
609    _flush_min_size(obj, pd);
610    _schedule_recalc_abs_size(obj, pd);
611 }
612 
613 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_removed(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,int removed_index EINA_UNUSED,Efl_Gfx_Entity * subobj EINA_UNUSED)614 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_removed(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int removed_index EINA_UNUSED, Efl_Gfx_Entity *subobj EINA_UNUSED)
615 {
616    //we ignore here that we might loose the item giving the current max min size
617    EINA_SAFETY_ON_FALSE_RETURN(pd->size > 0);
618    pd->size --;
619    _group_cache_invalidate(obj, pd);
620    pd->prev_run.start_id = MIN(pd->prev_run.start_id, pd->size);
621    pd->prev_run.end_id = MIN(pd->prev_run.end_id, pd->size);
622    _schedule_recalc_abs_size(obj, pd);
623    efl_gfx_entity_visible_set(subobj, EINA_TRUE);
624 }
625 
626 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_size_changed(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd,int start_id,int end_id)627 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_size_changed(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd, int start_id, int end_id)
628 {
629    const int len = 50;
630    Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[len];
631    Efl_Ui_Position_Manager_Size_Batch_Result size_result;
632 
633    for (int i = start_id; i <= end_id; ++i)
634      {
635         int buffer_id = (i-start_id) % len;
636         if (buffer_id == 0)
637           {
638              BATCH_ACCESS_SIZE(pd->callbacks, i, end_id + 1, len, EINA_TRUE, size_buffer);
639           }
640         _update_min_size(obj, pd, i, size_buffer[buffer_id].size);
641      }
642    _size_cache_invalidate(obj, pd);
643    _flush_min_size(obj, pd);
644    _schedule_recalc_abs_size(obj, pd);
645 }
646 
647 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_layout_orientable_orientation_set(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,Efl_Ui_Layout_Orientation dir)648 _efl_ui_position_manager_grid_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Efl_Ui_Layout_Orientation dir)
649 {
650    pd->dir = dir;
651    _flush_min_size(obj, pd);
652    _flush_abs_size(obj, pd);
653    _reposition_content(obj, pd); //FIXME we could check if this is needed or not
654 }
655 
656 
657 EOLIAN static Efl_Ui_Layout_Orientation
_efl_ui_position_manager_grid_efl_ui_layout_orientable_orientation_get(const Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd)658 _efl_ui_position_manager_grid_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
659 {
660    return pd->dir;
661 }
662 
663 EOLIAN static Eina_Rect
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_position_single_item(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,int idx)664 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_position_single_item(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int idx)
665 {
666    Eina_Rect geom;
667    Eina_Size2D space_size;
668    unsigned int relevant_space_size;
669    unsigned int group_consumed_size = 0;
670    unsigned int group_consumed_ids = 0;
671    Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[1];
672    Efl_Ui_Position_Manager_Size_Batch_Result size_result;
673 
674    if (!pd->size) return EINA_RECT(0, 0, 0, 0);
675    if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return EINA_RECT(0, 0, 0, 0);
676    BATCH_ACCESS_SIZE_VAL(pd->callbacks, idx, idx + 1, 1, EINA_TRUE, size_buffer, EINA_RECT_EMPTY());
677 
678    _size_cache_require(obj, pd);
679    _flush_abs_size(obj, pd);
680 
681    space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
682    space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
683    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
684      relevant_space_size = space_size.h;
685    else
686      relevant_space_size = space_size.w;
687 
688    geom.size = pd->max_min_size;
689    geom.pos = pd->viewport.pos;
690 
691    for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
692      {
693         Group_Cache_Line *line = eina_inarray_nth(pd->group_cache, i);
694         if ((int)group_consumed_ids + line->items > idx)
695           break;
696 
697         group_consumed_size += pd->size_cache[i];
698         group_consumed_ids += line->items;
699         if (line->real_group && idx == (int)group_consumed_ids + 1)
700           {
701              geom.y = (relevant_space_size - group_consumed_size);
702              geom.size = size_buffer[0].size;
703 
704              return geom;
705           }
706         else if (line->real_group)
707           group_consumed_size += line->group_header_size.h;
708      }
709 
710    if (idx > 0)
711      EINA_SAFETY_ON_FALSE_RETURN_VAL(group_consumed_ids < (unsigned int)idx, EINA_RECT(0, 0, 0, 0));
712    else if (idx == 0)
713      EINA_SAFETY_ON_FALSE_RETURN_VAL(group_consumed_ids == 0, EINA_RECT(0, 0, 0, 0));
714 
715    int columns = pd->viewport.w/pd->max_min_size.w;
716    if (columns == 0) return EINA_RECT(0, 0, 0, 0);
717    int sub_pos_id = idx - group_consumed_ids;
718 
719    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
720      {
721         const int x = (sub_pos_id)%columns;
722         const int y = (sub_pos_id)/columns;
723 
724         geom.y -= relevant_space_size;
725         geom.x += pd->max_min_size.w*x;
726         geom.y += group_consumed_size + pd->max_min_size.h*y;
727      }
728    else
729      {
730         const int x = (sub_pos_id)/columns;
731         const int y = (sub_pos_id)%columns;
732 
733         geom.x -= relevant_space_size;
734         geom.y += pd->max_min_size.h*y;
735         geom.x += group_consumed_size + pd->max_min_size.w*x;
736      }
737 
738    return geom;
739 }
740 
741 EOLIAN static Eina_Bool
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_relative_item(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd,unsigned int current_id,Efl_Ui_Focus_Direction direction,unsigned int * index)742 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_relative_item(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, unsigned int current_id, Efl_Ui_Focus_Direction direction, unsigned int *index)
743 {
744    switch(direction)
745      {
746         case EFL_UI_FOCUS_DIRECTION_RIGHT:
747         case EFL_UI_FOCUS_DIRECTION_NEXT:
748            if (current_id + 1 >= pd->size) return EINA_FALSE;
749            current_id += 1;
750            break;
751         case EFL_UI_FOCUS_DIRECTION_LEFT:
752         case EFL_UI_FOCUS_DIRECTION_PREVIOUS:
753            if (current_id == 0) return EINA_FALSE;
754            current_id -= 1;
755            break;
756         case EFL_UI_FOCUS_DIRECTION_UP:
757            //FIXME
758            break;
759         case EFL_UI_FOCUS_DIRECTION_DOWN:
760            //FIXME
761            break;
762         default:
763            ERR("Uncaught case!");
764            return EINA_FALSE;
765      }
766 
767    if (index) *index = current_id;
768    return EINA_TRUE;
769 }
770 
771 EOLIAN static int
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_version(Eo * obj EINA_UNUSED,Efl_Ui_Position_Manager_Grid_Data * pd EINA_UNUSED,int max EINA_UNUSED)772 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_version(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd EINA_UNUSED, int max EINA_UNUSED)
773 {
774    return 1;
775 }
776 
777 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_data_access_v1_data_access_set(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd,Efl_Ui_Win * canvas,void * obj_access_data,Efl_Ui_Position_Manager_Object_Batch_Callback obj_access,Eina_Free_Cb obj_access_free_cb,void * size_access_data,Efl_Ui_Position_Manager_Size_Batch_Callback size_access,Eina_Free_Cb size_access_free_cb,int size)778 _efl_ui_position_manager_grid_efl_ui_position_manager_data_access_v1_data_access_set(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd, Efl_Ui_Win *canvas, void *obj_access_data, Efl_Ui_Position_Manager_Object_Batch_Callback obj_access, Eina_Free_Cb obj_access_free_cb, void *size_access_data, Efl_Ui_Position_Manager_Size_Batch_Callback size_access, Eina_Free_Cb size_access_free_cb, int size)
779 {
780    // Cleanup cache first
781    _group_cache_invalidate(obj, pd);
782 
783    // Clean callback if they were set
784    if (pd->callbacks.object.free_cb)
785      pd->callbacks.object.free_cb(pd->callbacks.object.data);
786    if (pd->callbacks.size.free_cb)
787      pd->callbacks.size.free_cb(pd->callbacks.size.data);
788 
789    // Set them
790    efl_replace(&pd->window, canvas);
791    efl_replace(&pd->canvas, canvas ? evas_object_evas_get(canvas) : NULL);
792 
793    pd->callbacks.object.data = obj_access_data;
794    pd->callbacks.object.access = obj_access;
795    pd->callbacks.object.free_cb = obj_access_free_cb;
796    pd->callbacks.size.data = size_access_data;
797    pd->callbacks.size.access = size_access;
798    pd->callbacks.size.free_cb = size_access_free_cb;
799    pd->size = size;
800    _group_cache_require(obj, pd);
801    _schedule_recalc_abs_size(obj, pd);
802 
803 }
804 
805 EOLIAN static void
_efl_ui_position_manager_grid_efl_object_invalidate(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd EINA_UNUSED)806 _efl_ui_position_manager_grid_efl_object_invalidate(Eo *obj,
807                                                     Efl_Ui_Position_Manager_Grid_Data *pd EINA_UNUSED)
808 {
809    efl_ui_position_manager_data_access_v1_data_access_set(obj, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0);
810 
811    efl_invalidate(efl_super(obj, EFL_UI_POSITION_MANAGER_GRID_CLASS));
812 }
813 
814 EOLIAN static Efl_Object*
_efl_ui_position_manager_grid_efl_object_finalize(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd)815 _efl_ui_position_manager_grid_efl_object_finalize(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd)
816 {
817    obj = efl_finalize(efl_super(obj, MY_CLASS));
818 
819    pd->group_cache_dirty = EINA_TRUE;
820    _group_cache_require(obj, pd);
821    _schedule_recalc_abs_size(obj, pd);
822 
823    return obj;
824 }
825 
826 EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_entities_ready(Eo * obj,Efl_Ui_Position_Manager_Grid_Data * pd,unsigned int start_id,unsigned int end_id)827 _efl_ui_position_manager_grid_efl_ui_position_manager_entity_entities_ready(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd, unsigned int start_id, unsigned int end_id)
828 {
829    Eina_Size2D space_size;
830    int relevant_space_size;
831    Item_Position_Context ctx;
832 
833    if (end_id < pd->prev_run.start_id || start_id > pd->prev_run.end_id)
834      return;
835 
836    space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
837    space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
838 
839    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
840      {
841         relevant_space_size = space_size.h;
842      }
843    else
844      {
845         relevant_space_size = space_size.w;
846      }
847 
848    ctx.new = pd->prev_run;
849    ctx.consumed_space = pd->prev_consumed_space;
850    ctx.relevant_space_size = relevant_space_size;
851    ctx.floating_group = NULL;
852    ctx.placed_item = NULL;
853 
854    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
855      {
856         _position_items_vertical(obj, pd, &ctx);
857         _position_group_items(obj, pd, &ctx);
858      }
859    else
860      {
861         _position_items_horizontal(obj, pd, &ctx);
862         _position_group_items(obj, pd, &ctx);
863      }
864 }
865 
866 
867 
868 #include "efl_ui_position_manager_grid.eo.c"
869