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