1 // Note: @1.23 Initial release has infrastructure to support more mode than homogeneous, but isn't exposed in the API nor supported.
2
3 #ifdef HAVE_CONFIG_H
4 # include "elementary_config.h"
5 #endif
6
7 #define ELM_LAYOUT_PROTECTED
8 #define EFL_UI_SCROLL_MANAGER_PROTECTED
9 #define EFL_UI_SCROLLBAR_PROTECTED
10 #define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
11
12 #include <Efl_Ui.h>
13 #include <Elementary.h>
14 #include "elm_widget.h"
15 #include "elm_priv.h"
16 #include "inttypes.h"
17
18 #include "efl_ui_collection_view_focus_manager.eo.h"
19
20 #ifndef VIEWPORT_ENABLE
21 # undef VIEWPORT_ENABLE
22 #endif
23
24 typedef struct _Efl_Ui_Collection_View_Data Efl_Ui_Collection_View_Data;
25 typedef struct _Efl_Ui_Collection_Viewport Efl_Ui_Collection_Viewport;
26 typedef struct _Efl_Ui_Collection_View_Focus_Manager_Data Efl_Ui_Collection_View_Focus_Manager_Data;
27 typedef struct _Efl_Ui_Collection_Item Efl_Ui_Collection_Item;
28 typedef struct _Efl_Ui_Collection_Item_Lookup Efl_Ui_Collection_Item_Lookup;
29 typedef struct _Efl_Ui_Collection_Request Efl_Ui_Collection_Request;
30
31 struct _Efl_Ui_Collection_Item
32 {
33 Efl_Gfx_Entity *entity;
34 Efl_Model *model;
35 };
36
37 struct _Efl_Ui_Collection_Item_Lookup
38 {
39 EINA_RBTREE;
40
41 unsigned int index;
42 Efl_Ui_Collection_Item item;
43 };
44
45 struct _Efl_Ui_Collection_Viewport
46 {
47 Efl_Ui_Collection_Item *items;
48
49 unsigned int offset;
50 uint16_t count;
51 };
52
53 struct _Efl_Ui_Collection_Request
54 {
55 Eina_Future *f;
56
57 unsigned int offset;
58 unsigned int length;
59
60 Eina_Bool need_size : 1;
61 Eina_Bool need_entity : 1;
62 Eina_Bool entity_requested : 1;
63 };
64
65 struct _Efl_Ui_Collection_View_Data
66 {
67 Efl_Ui_Factory *factory;
68 Efl_Ui_Position_Manager_Entity *manager;
69 Efl_Ui_Scroll_Manager *scroller;
70 Efl_Ui_Pan *pan;
71 Efl_Gfx_Entity *sizer;
72 Efl_Model *model;
73 Efl_Model *multi_selectable_async_model;
74
75 #ifdef VIEWPORT_ENABLE
76 Efl_Ui_Collection_Viewport *viewport[3];
77 #endif
78 Eina_Rbtree *cache;
79
80 Eina_List *requests; // Array of Efl_Ui_Collection_Request in progress
81
82 struct {
83 Efl_Gfx_Entity *last; // The last item of the collection, so focus can start by the end if necessary.
84 Efl_Gfx_Entity *previously; // The previously selected item in the collection, so focus can come back to it.
85 } focus;
86
87 unsigned int start_id;
88 unsigned int end_id;
89
90 Eina_Size2D content_min_size;
91
92 Efl_Ui_Layout_Orientation direction;
93 Efl_Ui_Select_Mode mode;
94
95 struct {
96 Eina_Bool w : 1;
97 Eina_Bool h : 1;
98 } match_content;
99
100 Efl_Ui_Position_Manager_Request_Range current_range;
101 };
102
103 struct _Efl_Ui_Collection_View_Focus_Manager_Data
104 {
105 Efl_Ui_Collection_View *collection;
106 };
107
108 static const char *COLLECTION_VIEW_MANAGED = "_collection_view.managed";
109 static const char *COLLECTION_VIEW_MANAGED_YES = "yes";
110
111 #define MY_CLASS EFL_UI_COLLECTION_VIEW_CLASS
112
113 #define MY_DATA_GET(obj, pd) \
114 Efl_Ui_Collection_View_Data *pd = efl_data_scope_get(obj, MY_CLASS);
115
116 static Eina_Bool _entity_request(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_Request *request);
117 static void _idle_cb(void *data, const Efl_Event *event);
118
119 static int
_cache_tree_lookup(const Eina_Rbtree * node,const void * key,int length EINA_UNUSED,void * data EINA_UNUSED)120 _cache_tree_lookup(const Eina_Rbtree *node, const void *key,
121 int length EINA_UNUSED, void *data EINA_UNUSED)
122 {
123 const Efl_Ui_Collection_Item_Lookup *n = (Efl_Ui_Collection_Item_Lookup *)node;
124 const unsigned int *index = key;
125
126 if (n->index > *index)
127 return 1;
128 if (n->index < *index)
129 return -1;
130 return 0;
131 }
132
133 static Eina_Rbtree_Direction
_cache_tree_cmp(const Eina_Rbtree * left,const Eina_Rbtree * right,void * data EINA_UNUSED)134 _cache_tree_cmp(const Eina_Rbtree *left, const Eina_Rbtree *right, void *data EINA_UNUSED)
135 {
136 Efl_Ui_Collection_Item_Lookup *l = (Efl_Ui_Collection_Item_Lookup *)left;
137 Efl_Ui_Collection_Item_Lookup *r = (Efl_Ui_Collection_Item_Lookup *)right;
138
139 return l->index < r->index ? EINA_RBTREE_LEFT : EINA_RBTREE_RIGHT;
140 }
141
142 static Eina_Value
_undo_item_selected_then(Eo * item,void * data EINA_UNUSED,Eina_Error err)143 _undo_item_selected_then(Eo *item, void *data EINA_UNUSED, Eina_Error err)
144 {
145 Eina_Value *get;
146 Eina_Bool item_selected = efl_ui_selectable_selected_get(item);
147 Eina_Bool model_selected = EINA_FALSE;
148
149 get = efl_model_property_get(efl_ui_view_model_get(item), "self.selected");
150 eina_value_bool_get(get, &model_selected);
151 eina_value_free(get);
152
153 if ((!!model_selected) != (!!item_selected))
154 efl_ui_selectable_selected_set(item, model_selected);
155
156 return eina_value_error_init(err);
157 }
158
159 static void
_selected_item_cb(void * data EINA_UNUSED,const Efl_Event * ev)160 _selected_item_cb(void *data EINA_UNUSED, const Efl_Event *ev)
161 {
162 // Link back property to model, maybe just trigger event on the item should be enough
163 Eina_Value *get;
164 Eina_Bool item_selected = efl_ui_selectable_selected_get(ev->object);
165 Eina_Bool model_selected = EINA_FALSE;
166 Eina_Value set = eina_value_bool_init(!!item_selected);
167
168 get = efl_model_property_get(efl_ui_view_model_get(ev->object), "self.selected");
169 eina_value_bool_get(get, &model_selected);
170 eina_value_free(get);
171
172 if ((!!model_selected) != (!!item_selected))
173 {
174 Eina_Future *f;
175
176 f = efl_model_property_set(efl_ui_view_model_get(ev->object), "self.selected", &set);
177
178 // In case the mode is preventing the change, we need to update the UI back. So handle error case
179 efl_future_then(ev->object, f,
180 .error = _undo_item_selected_then);
181 }
182
183 eina_value_flush(&set);
184 }
185
186 static void
_redirect_item_cb(void * data,const Efl_Event * ev)187 _redirect_item_cb(void *data, const Efl_Event *ev)
188 {
189 Eo *obj = data;
190
191 #define REDIRECT_EVT(Desc, Item_Desc) \
192 if (Desc == ev->desc) \
193 { \
194 Efl_Ui_Item_Clickable_Clicked item_clicked; \
195 Efl_Input_Clickable_Clicked *clicked = ev->info; \
196 \
197 item_clicked.clicked = *clicked; \
198 item_clicked.item = ev->object; \
199 \
200 efl_event_callback_call(obj, Item_Desc, &item_clicked); \
201 }
202 #define REDIRECT_EVT_PRESS(Desc, Item_Desc) \
203 if (Desc == ev->desc) \
204 { \
205 Efl_Ui_Item_Clickable_Pressed item_pressed; \
206 int *button = ev->info; \
207 \
208 item_pressed.button = *button; \
209 item_pressed.item = ev->object; \
210 \
211 efl_event_callback_call(obj, Item_Desc, &item_pressed); \
212 }
213
214 REDIRECT_EVT_PRESS(EFL_INPUT_EVENT_PRESSED, EFL_UI_EVENT_ITEM_PRESSED);
215 REDIRECT_EVT_PRESS(EFL_INPUT_EVENT_UNPRESSED, EFL_UI_EVENT_ITEM_UNPRESSED);
216 REDIRECT_EVT_PRESS(EFL_INPUT_EVENT_LONGPRESSED, EFL_UI_EVENT_ITEM_LONGPRESSED);
217 REDIRECT_EVT(EFL_INPUT_EVENT_CLICKED_ANY, EFL_UI_EVENT_ITEM_CLICKED_ANY);
218 REDIRECT_EVT(EFL_INPUT_EVENT_CLICKED, EFL_UI_EVENT_ITEM_CLICKED);
219 #undef REDIRECT_EVT
220 #undef REDIRECT_EVT_PRESS
221 }
222
223 EFL_CALLBACKS_ARRAY_DEFINE(active_item_cbs,
224 { EFL_UI_EVENT_SELECTED_CHANGED, _selected_item_cb },
225 { EFL_INPUT_EVENT_PRESSED, _redirect_item_cb },
226 { EFL_INPUT_EVENT_UNPRESSED, _redirect_item_cb },
227 { EFL_INPUT_EVENT_LONGPRESSED, _redirect_item_cb },
228 { EFL_INPUT_EVENT_CLICKED, _redirect_item_cb },
229 { EFL_INPUT_EVENT_CLICKED_ANY, _redirect_item_cb });
230
231 static void
_entity_cleanup(Efl_Ui_Collection_View * obj,Efl_Ui_Factory * factory,Efl_Ui_Collection_Item * item,Eina_Array * scheduled_release)232 _entity_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Factory *factory,
233 Efl_Ui_Collection_Item *item, Eina_Array *scheduled_release)
234 {
235 Efl_Gfx_Entity *entities[1];
236
237 entities[0] = item->entity;
238 if (!entities[0]) return ;
239
240 efl_event_callback_array_del(entities[0], active_item_cbs(), obj);
241 efl_replace(&item->entity, NULL);
242 efl_event_callback_call(obj, EFL_UI_COLLECTION_VIEW_EVENT_ITEM_UNREALIZED, entities[0]);
243 if (!scheduled_release)
244 {
245 efl_ui_factory_release(factory, EINA_C_ARRAY_ITERATOR_NEW(entities));
246 }
247 else
248 {
249 eina_array_push(scheduled_release, entities[0]);
250 }
251 }
252
253 static void
_item_cleanup(Efl_Ui_Collection_View * obj,Efl_Ui_Factory * factory,Efl_Ui_Collection_Item * item,Eina_Array * scheduled_release)254 _item_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Factory *factory,
255 Efl_Ui_Collection_Item *item, Eina_Array *scheduled_release)
256 {
257 efl_replace(&item->model, NULL);
258
259 _entity_cleanup(obj, factory, item, scheduled_release);
260 }
261
262 static void
_cache_item_free(Eina_Rbtree * node,void * data)263 _cache_item_free(Eina_Rbtree *node, void *data)
264 {
265 Efl_Ui_Collection_Item_Lookup *n = (void*) node;
266 MY_DATA_GET(data, pd);
267
268 _item_cleanup(data, pd->factory, &n->item, NULL);
269 free(n);
270 }
271
272 static void
_cache_cleanup(Efl_Ui_Collection_View * obj,Efl_Ui_Collection_View_Data * pd)273 _cache_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd)
274 {
275 eina_rbtree_delete(pd->cache, _cache_item_free, obj);
276 pd->cache = NULL;
277 }
278
279 static void
_all_cleanup(Efl_Ui_Collection_View * obj,Efl_Ui_Collection_View_Data * pd)280 _all_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd)
281 {
282 Efl_Ui_Collection_Request *request;
283 Eina_List *l, *ll;
284 #ifdef VIEWPORT_ENABLE
285 unsigned int i;
286 #endif
287
288 _cache_cleanup(obj, pd);
289 #ifdef VIEWPORT_ENABLE
290 for (i = 0; i < 3; i++)
291 {
292 unsigned int j;
293
294 if (!pd->viewport[i]) continue;
295
296 for (j = 0; j < pd->viewport[i]->count; j++)
297 _item_cleanup(obj, pd->factory, &(pd->viewport[i]->items[j]));
298 }
299 #endif
300
301 efl_replace(&pd->focus.previously, NULL);
302 efl_replace(&pd->focus.last, NULL);
303
304 EINA_LIST_FOREACH_SAFE(pd->requests, l, ll, request)
305 eina_future_cancel(request->f);
306 }
307
308 static inline Eina_Bool
_size_from_model(Efl_Model * model,Eina_Size2D * r,const char * width,const char * height)309 _size_from_model(Efl_Model *model, Eina_Size2D *r, const char *width, const char *height)
310 {
311 Eina_Value *vw, *vh;
312 Eina_Bool success = EINA_FALSE;
313
314 EINA_SAFETY_ON_NULL_RETURN_VAL(model, EINA_FALSE);
315
316 vw = efl_model_property_get(model, width);
317 vh = efl_model_property_get(model, height);
318
319 if (eina_value_type_get(vw) == EINA_VALUE_TYPE_ERROR ||
320 eina_value_type_get(vh) == EINA_VALUE_TYPE_ERROR)
321 goto on_error;
322
323 if (!eina_value_int_convert(vw, &(r->w))) r->w = 0;
324 if (!eina_value_int_convert(vh, &(r->h))) r->h = 0;
325
326 success = EINA_TRUE;
327
328 on_error:
329 eina_value_free(vw);
330 eina_value_free(vh);
331
332 return success;
333 }
334
335 static inline void
_size_to_model(Efl_Model * model,Eina_Size2D state)336 _size_to_model(Efl_Model *model, Eina_Size2D state)
337 {
338 Eina_Value vw, vh;
339
340 vw = eina_value_int_init(state.w);
341 vh = eina_value_int_init(state.h);
342
343 efl_model_property_set(model, "self.width", &vw);
344 efl_model_property_set(model, "self.height", &vh);
345
346 eina_value_flush(&vw);
347 eina_value_flush(&vh);
348 }
349
350 #define ITEM_BASE_SIZE_FROM_MODEL(Model, Size) _size_from_model(Model, &Size, "item.width", "item.height")
351 #define ITEM_SIZE_FROM_MODEL(Model, Size) _size_from_model(Model, &Size, "self.width", "self.height")
352
353 static Eina_List *
_request_add(Eina_List * requests,Efl_Ui_Collection_Request ** request,unsigned int index,Eina_Bool need_entity)354 _request_add(Eina_List *requests, Efl_Ui_Collection_Request **request,
355 unsigned int index, Eina_Bool need_entity)
356 {
357 if (!(*request)) goto create;
358
359 if ((*request)->offset + (*request)->length == index)
360 {
361 if (need_entity) (*request)->need_entity = EINA_TRUE;
362 if (!need_entity) (*request)->need_size = EINA_TRUE;
363 (*request)->length += 1;
364 return requests;
365 }
366
367 requests = eina_list_append(requests, *request);
368
369 create:
370 *request = calloc(1, sizeof (Efl_Ui_Collection_Request));
371 if (!(*request)) return requests;
372 (*request)->offset = index;
373 (*request)->length = 1;
374 // At this point, we rely on the model caching ability to avoid recreating model
375 (*request)->need_entity = !!need_entity;
376 (*request)->need_size = EINA_TRUE;
377
378 return requests;
379 }
380
381 static Eina_Value
_model_fetched_cb(Eo * obj,void * data,const Eina_Value v)382 _model_fetched_cb(Eo *obj, void *data, const Eina_Value v)
383 {
384 MY_DATA_GET(obj, pd);
385 Efl_Ui_Collection_Request *request = data;
386 Efl_Model *child;
387 unsigned int i, len;
388 Eina_Bool request_entity = EINA_FALSE;
389
390 EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
391 {
392 Efl_Ui_Collection_Item_Lookup *insert;
393 Eina_Size2D item_size;
394 #ifdef VIEWPORT_ENABLE
395 unsigned int v;
396
397 for (v = 0; v < 3; ++v)
398 {
399 if (!pd->viewport[v]) continue;
400
401 if ((pd->viewport[v]->offset <= request->offset + i) &&
402 (request->offset + i < pd->viewport[v]->offset + pd->viewport[v]->count))
403 {
404 unsigned int index = request->offset + i - pd->viewport[v]->offset;
405
406 efl_replace(&pd->viewport[v]->items[index].model, child);
407 child = NULL;
408 break;
409 }
410 }
411 #endif
412
413 // When requesting a model, it should not be in the cache prior to the request
414 if (!child) continue;
415
416 unsigned int search_index = request->offset + i;
417
418 insert = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
419 sizeof (search_index), _cache_tree_lookup,
420 NULL);
421 if (insert)
422 {
423 if (!insert->item.entity && request->need_entity)
424 {
425 //drop the old model here, overwrite with model + view
426 efl_replace(&insert->item.model, child);
427 }
428 else
429 ERR("Inserting a model that was already fetched, dropping new model %u", search_index);
430 }
431 else
432 {
433 insert = calloc(1, sizeof (Efl_Ui_Collection_Item_Lookup));
434 if (!insert) continue;
435 insert->index = request->offset + i;
436 insert->item.model = efl_ref(child);
437 pd->cache = eina_rbtree_inline_insert(pd->cache, EINA_RBTREE_GET(insert), _cache_tree_cmp, NULL);
438 }
439
440 if (!ITEM_SIZE_FROM_MODEL(insert->item.model, item_size))
441 request_entity = EINA_TRUE;
442 }
443
444 if (request_entity)
445 {
446 request->need_entity = EINA_TRUE;
447
448 if (!request->entity_requested)
449 _entity_request(obj, request);
450 }
451 else if (request->need_size)
452 {
453 efl_ui_position_manager_entity_item_size_changed(pd->manager, request->offset,
454 request->offset + len);
455 }
456
457 return v;
458 }
459
460 static void
_model_free_cb(Eo * o,void * data,const Eina_Future * dead_future EINA_UNUSED)461 _model_free_cb(Eo *o, void *data, const Eina_Future *dead_future EINA_UNUSED)
462 {
463 MY_DATA_GET(o, pd);
464 Efl_Ui_Collection_Request *request = data;
465
466 if (!request->entity_requested)
467 {
468 pd->requests = eina_list_remove(pd->requests, request);
469 free(request);
470 }
471 }
472
473 static Eina_Value
_entity_fetch_cb(Eo * obj,void * data EINA_UNUSED,const Eina_Value v)474 _entity_fetch_cb(Eo *obj, void *data EINA_UNUSED, const Eina_Value v)
475 {
476 MY_DATA_GET(obj, pd);
477 Efl_Model *child;
478 Eina_Future *r;
479 Eina_Array tmp;
480 unsigned int i, len;
481
482 eina_array_step_set(&tmp, sizeof (Eina_Array), 4);
483
484 EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
485 {
486 eina_array_push(&tmp, child);
487 }
488
489 r = efl_ui_view_factory_create_with_event(pd->factory, eina_array_iterator_new(&tmp));
490
491 eina_array_flush(&tmp);
492
493 return eina_future_as_value(r);
494 }
495
496 static inline unsigned int
_lookup_entity_index(Efl_Gfx_Entity * entity,Efl_Model ** model)497 _lookup_entity_index(Efl_Gfx_Entity *entity, Efl_Model **model)
498 {
499 Efl_Model *fetch;
500
501 fetch = efl_ui_view_model_get(entity);
502 if (model) *model = fetch;
503 return efl_composite_model_index_get(fetch);
504 }
505
506 static void
_last_entity_update(Efl_Ui_Collection_View_Data * pd,Efl_Gfx_Entity * entity)507 _last_entity_update(Efl_Ui_Collection_View_Data *pd, Efl_Gfx_Entity *entity)
508 {
509 Efl_Model *new_model, *old_model;
510 unsigned int new_index, old_index;
511
512 if (!pd->focus.last) goto replace;
513
514 new_index = _lookup_entity_index(entity, &new_model);
515 old_index = _lookup_entity_index(pd->focus.last, &old_model);
516
517 if (new_index <= old_index) return;
518
519 replace:
520 efl_replace(&pd->focus.last, entity);
521 }
522
523 static inline Eina_Bool
_entity_propagate(Efl_Model * model,Efl_Gfx_Entity * entity)524 _entity_propagate(Efl_Model *model, Efl_Gfx_Entity *entity)
525 {
526 Eina_Size2D item_size;
527
528 if (efl_key_data_get(entity, "efl.ui.widget.factory.size_set"))
529 {
530 return EINA_FALSE;
531 }
532
533 if (ITEM_SIZE_FROM_MODEL(model, item_size))
534 {
535 efl_gfx_hint_size_min_set(entity, item_size);
536 efl_canvas_group_need_recalculate_set(entity, EINA_FALSE);
537 if (efl_isa(entity, EFL_UI_ITEM_CLASS)) efl_ui_item_calc_locked_set(entity, EINA_TRUE);
538 return EINA_FALSE;
539 }
540
541 efl_canvas_group_calculate(entity);
542 item_size = efl_gfx_hint_size_combined_min_get(entity);
543 efl_canvas_group_need_recalculate_set(entity, EINA_FALSE);
544
545 _size_to_model(model, item_size);
546 return EINA_TRUE;
547 }
548
549 static Eina_Value
_entity_fetched_cb(Eo * obj,void * data,const Eina_Value v)550 _entity_fetched_cb(Eo *obj, void *data, const Eina_Value v)
551 {
552 MY_DATA_GET(obj, pd);
553 Efl_Ui_Collection_Request *request = data;
554 Efl_Gfx_Entity *child;
555 unsigned int i, len;
556 unsigned int updated_size_start_id = 0, updated_entity_start_id = 0;
557 Eina_Bool updated_size = EINA_FALSE, updated_entity = EINA_FALSE;
558 Evas *e;
559
560 e = evas_object_evas_get(obj);
561 evas_event_freeze(e);
562
563 EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
564 {
565 Efl_Ui_Collection_Item_Lookup *lookup;
566 unsigned int search_index;
567 //unsigned int v;
568
569 efl_key_data_set(child, COLLECTION_VIEW_MANAGED, COLLECTION_VIEW_MANAGED_YES);
570 /* fix eventing in scroller by ensuring collection items are in the scroller hierarchy */
571 efl_ui_item_container_set(child, obj);
572 efl_ui_widget_sub_object_add(obj, child);
573 efl_canvas_group_member_add(pd->pan, child);
574 efl_gfx_entity_visible_set(child, EINA_FALSE);
575
576 #ifdef VIEWPORT_ENABLE
577 for (v = 0; v < 3; ++v)
578 {
579 if (!pd->viewport[v]) continue;
580
581 if ((pd->viewport[v]->offset <= request->offset + i) &&
582 (request->offset + i < pd->viewport[v]->offset + pd->viewport[v]->count))
583 {
584 unsigned int index = request->offset + i - pd->viewport[v]->offset;
585
586 if (pd->viewport[v]->items[index].entity)
587 {
588 ERR("Entity already existing for id %d", i);
589 efl_unref(pd->viewport[v]->items[index].entity);
590 efl_del(pd->viewport[v]->items[index].entity);
591 pd->viewport[v]->items[index].entity = NULL;
592 }
593
594 efl_replace(&pd->viewport[v]->items[index].entity, child);
595 if (_entity_propagate(pd->viewport[v]->items[index].model, child))
596 {
597 if (!updated_size)
598 {
599 updated_size = EINA_TRUE;
600 updated_size_start_id = index;
601 }
602 }
603 else
604 {
605 if (updated_size)
606 {
607 efl_ui_position_manager_entity_item_size_changed(pd->manager,
608 updated_size_start_id,
609 index - 1);
610 updated_size = EINA_FALSE;
611 }
612 }
613 child = NULL;
614 break;
615 }
616 }
617 #endif
618 // When requesting an entity, the model should already be in the cache
619 if (!child) continue;
620
621 search_index = request->offset + i;
622
623 lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
624 sizeof (search_index), _cache_tree_lookup,
625 NULL);
626
627 if (!lookup)
628 {
629 Efl_Gfx_Entity *entities[1] = { child };
630 efl_ui_factory_release(pd->factory, EINA_C_ARRAY_ITERATOR_NEW(entities));
631 continue;
632 }
633 if (lookup->item.entity)
634 {
635 ERR("Entity already existing for id %u", search_index);
636 _entity_cleanup(obj, pd->factory, &lookup->item, NULL);
637 }
638
639 lookup->item.entity = efl_ref(child);
640 efl_event_callback_array_add(child, active_item_cbs(), obj);
641 efl_event_callback_call(obj, EFL_UI_COLLECTION_VIEW_EVENT_ITEM_REALIZED, child);
642
643 if (!updated_entity)
644 {
645 updated_entity = EINA_TRUE;
646 updated_entity_start_id = search_index;
647 }
648
649 if (_entity_propagate(lookup->item.model, child))
650 {
651 if (!updated_size)
652 {
653 updated_size = EINA_TRUE;
654 updated_size_start_id = search_index;
655 }
656 }
657 else
658 {
659 if (updated_size)
660 {
661 efl_ui_position_manager_entity_item_size_changed(pd->manager,
662 updated_size_start_id,
663 search_index - 1);
664 updated_size = EINA_FALSE;
665 }
666 }
667 }
668
669 evas_event_thaw(e);
670 evas_event_thaw_eval(e);
671
672 // Check if the last child is also the list item in the list
673 _last_entity_update(pd, child);
674
675 // Currently position manager will flush its entire size cache on update, so only do
676 // it when necessary to improve performance.
677 if (updated_size || request->need_size)
678 {
679 efl_ui_position_manager_entity_item_size_changed(pd->manager,
680 updated_size_start_id,
681 request->offset + i - 1);
682 updated_size = EINA_FALSE;
683 }
684
685 // Notify the position manager that new entity are ready to display
686 if (updated_entity)
687 {
688 efl_ui_position_manager_entity_entities_ready(pd->manager,
689 updated_entity_start_id,
690 request->offset + i - 1);
691
692 efl_event_callback_del(efl_main_loop_get(), EFL_LOOP_EVENT_IDLE, _idle_cb, obj);
693 efl_event_callback_add(efl_main_loop_get(), EFL_LOOP_EVENT_IDLE, _idle_cb, obj);
694 }
695 return v;
696 }
697
698 static void
_entity_free_cb(Eo * o,void * data,const Eina_Future * dead_future EINA_UNUSED)699 _entity_free_cb(Eo *o, void *data, const Eina_Future *dead_future EINA_UNUSED)
700 {
701 MY_DATA_GET(o, pd);
702 Efl_Ui_Collection_Request *request = data;
703
704 pd->requests = eina_list_remove(pd->requests, request);
705 free(request);
706 }
707
708 static Eina_Bool
_focus_lookup(Efl_Ui_Collection_View_Data * pd,unsigned int search_index,Efl_Gfx_Entity ** entity,Efl_Model ** model)709 _focus_lookup(Efl_Ui_Collection_View_Data *pd, unsigned int search_index,
710 Efl_Gfx_Entity **entity, Efl_Model **model)
711 {
712 unsigned int idx;
713
714 if (entity) *entity = pd->focus.last;
715 if (pd->focus.last)
716 {
717 idx = _lookup_entity_index(pd->focus.last, model);
718 if (idx == search_index) return EINA_TRUE;
719 }
720 if (entity) *entity = pd->focus.previously;
721 if (pd->focus.previously)
722 {
723 idx = _lookup_entity_index(pd->focus.previously, model);
724 if (idx == search_index) return EINA_TRUE;
725 }
726
727 if (entity) *entity = NULL;
728 if (model) *model = NULL;
729 return EINA_FALSE;
730 }
731
732 static Efl_Ui_Collection_Item_Lookup *
_build_from_focus(Efl_Ui_Collection_View_Data * pd,unsigned int search_index,Efl_Model ** model)733 _build_from_focus(Efl_Ui_Collection_View_Data *pd, unsigned int search_index,
734 Efl_Model **model)
735 {
736 Efl_Ui_Collection_Item_Lookup *insert;
737 Efl_Gfx_Entity *entity = NULL;
738
739 // Not found in the cache lookup, but just maybe
740 if (!_focus_lookup(pd, search_index, &entity, model)) return NULL;
741
742 // Lucky us, let's add it to the cache
743 insert = calloc(1, sizeof (Efl_Ui_Collection_Item_Lookup));
744 if (!insert) return NULL;
745
746 insert->index = search_index;
747 insert->item.model = efl_ref(*model);
748 insert->item.entity = efl_ref(entity);
749
750 pd->cache = eina_rbtree_inline_insert(pd->cache, EINA_RBTREE_GET(insert),
751 _cache_tree_cmp, NULL);
752
753 return insert;
754 }
755
756 static Eina_List *
_cache_size_fetch(Eina_List * requests,Efl_Ui_Collection_Request ** request,Efl_Ui_Collection_View_Data * pd,unsigned int search_index,Efl_Ui_Position_Manager_Size_Batch_Entity * target,Eina_Size2D item_base)757 _cache_size_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
758 Efl_Ui_Collection_View_Data *pd,
759 unsigned int search_index,
760 Efl_Ui_Position_Manager_Size_Batch_Entity *target,
761 Eina_Size2D item_base)
762 {
763 Efl_Ui_Collection_Item_Lookup *lookup;
764 Efl_Model *model = NULL;
765 Eina_Size2D item_size = item_base;
766
767 if (!pd->cache) goto not_found;
768
769 lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
770 sizeof (search_index), _cache_tree_lookup,
771 NULL);
772 // In the cache we should always have model, so no need to check for it
773 if (lookup)
774 {
775 model = lookup->item.model;
776 }
777 else
778 {
779 lookup = _build_from_focus(pd, search_index, &model);
780 if (!lookup) goto not_found;
781 }
782
783 // If we do not know the size
784 if (!ITEM_SIZE_FROM_MODEL(model, item_size))
785 {
786 if (lookup->item.entity)
787 {
788 ERR("Got a model '%s' and an item '%s', but no size. Recalculating.",
789 efl_debug_name_get(model), efl_debug_name_get(lookup->item.entity));
790 _entity_propagate(model, lookup->item.entity);
791 if (!ITEM_SIZE_FROM_MODEL(model, item_size))
792 {
793 CRI("No size for itme '%s' after recalculating. This is bad.",
794 efl_debug_name_get(lookup->item.entity));
795 }
796 }
797 else if (!ITEM_BASE_SIZE_FROM_MODEL(pd->model, item_size))
798 {
799 INF("No base size yet available. Making things up.");
800 item_size.w = 1;
801 item_size.h = 1;
802 }
803 }
804
805 target->size = item_size;
806 target->element_depth = 0;
807 target->depth_leader = EINA_FALSE;
808 return requests;
809
810 not_found:
811 requests = _request_add(requests, request, search_index, EINA_FALSE);
812
813 target->size = item_size;
814 target->element_depth = 0;
815 target->depth_leader = EINA_FALSE;
816 return requests;
817 }
818
819 static Eina_List *
_cache_entity_fetch(Eina_List * requests,Efl_Ui_Collection_Request ** request,Efl_Ui_Collection_View_Data * pd,unsigned int search_index,Efl_Ui_Position_Manager_Object_Batch_Entity * target)820 _cache_entity_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
821 Efl_Ui_Collection_View_Data *pd,
822 unsigned int search_index,
823 Efl_Ui_Position_Manager_Object_Batch_Entity *target)
824 {
825 Efl_Ui_Collection_Item_Lookup *lookup;
826 Efl_Model *model = NULL;
827
828 if (!pd->cache) goto not_found;
829
830 lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
831 sizeof (search_index), _cache_tree_lookup,
832 NULL);
833 if (!lookup) lookup = _build_from_focus(pd, search_index, &model);
834 if (!lookup) goto not_found;
835 if (!lookup->item.entity) goto not_found;
836
837 if (target) target->entity = lookup->item.entity;
838 goto finish;
839
840 not_found:
841 requests = _request_add(requests, request, search_index, EINA_TRUE);
842
843 if (target) target->entity = NULL;
844 finish:
845 if (!target) return requests;
846
847 target->element_depth = 0;
848 target->depth_leader = EINA_FALSE;
849
850 return requests;
851 }
852
853 static Eina_Bool
_entity_request(Efl_Ui_Collection_View * obj,Efl_Ui_Collection_Request * request)854 _entity_request(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_Request *request)
855 {
856 if (request->entity_requested) return EINA_TRUE;
857 request->f = efl_future_then(obj, request->f,
858 .success_type = EINA_VALUE_TYPE_ARRAY,
859 .success = _entity_fetch_cb);
860 request->f = efl_future_then(obj, request->f,
861 .success_type = EINA_VALUE_TYPE_ARRAY,
862 .success = _entity_fetched_cb,
863 .data = request,
864 .free = _entity_free_cb);
865 request->entity_requested = EINA_TRUE;
866 request->need_entity = EINA_TRUE;
867
868 return EINA_TRUE;
869 }
870
871 static inline Eina_Bool
_entity_inflight_request(Efl_Ui_Collection_View * obj,Efl_Ui_Collection_Request * request,Efl_Ui_Collection_Request * inflight)872 _entity_inflight_request(Efl_Ui_Collection_View *obj,
873 Efl_Ui_Collection_Request *request,
874 Efl_Ui_Collection_Request *inflight)
875 {
876 inflight->need_size |= request->need_size;
877 if (request->need_entity == EINA_FALSE) return EINA_TRUE;
878
879 return _entity_request(obj, inflight);
880 }
881
882 static Eina_List *
_batch_request_flush(Eina_List * requests,Efl_Ui_Collection_View * obj,Efl_Ui_Collection_View_Data * pd)883 _batch_request_flush(Eina_List *requests,
884 Efl_Ui_Collection_View *obj,
885 Efl_Ui_Collection_View_Data *pd)
886 {
887 Efl_Ui_Collection_Request *request;
888 Eina_List *ll, *next_list_item;
889
890 EINA_LIST_FOREACH_SAFE(requests, ll, next_list_item, request)
891 {
892 // Check request intersection with all pending request
893 Efl_Ui_Collection_Request *inflight;
894 Efl_Model *model;
895 Eina_List *l;
896
897 EINA_LIST_FOREACH(pd->requests, l, inflight)
898 {
899 unsigned int istart = inflight->offset;
900 unsigned int iend = inflight->offset + inflight->length;
901 unsigned int rstart = request->offset;
902 unsigned int rend = request->offset + request->length;
903
904 // Way before
905 if (rend < istart) continue;
906 // Way after
907 if (rstart >= iend) continue;
908
909 // request included in current inflight request
910 if (rstart >= istart && rend <= iend)
911 {
912 if (!_entity_inflight_request(obj, request, inflight)) continue;
913
914 // In this case no need to start a request
915 requests = eina_list_remove_list(requests, ll);
916 free(request);
917 request = NULL;
918 break;
919 }
920
921 // request overflow left and right
922 if (rstart < istart && iend < rend)
923 {
924 if (!_entity_inflight_request(obj, request, inflight)) continue;
925
926 // Remove the center portion of the request by emitting a new one
927 Efl_Ui_Collection_Request *rn;
928
929 rn = calloc(1, sizeof (Efl_Ui_Collection_Request));
930 if (!rn) break;
931
932 rn->offset = iend;
933 rn->length = rend - iend;
934 rn->need_entity = request->need_entity;
935 rn->need_size = request->need_size;
936
937 requests = eina_list_append(requests, rn);
938
939 request->length = istart - rstart;
940 continue;
941 }
942
943 // request overflow left
944 if (rstart < istart && rend > istart && rend <= iend)
945 {
946 if (!_entity_inflight_request(obj, request, inflight)) continue;
947 request->length = istart - rstart;
948 continue;
949 }
950
951 // request overflow right
952 if (rstart >= istart && rstart < iend && iend <= rend)
953 {
954 if (!_entity_inflight_request(obj, request, inflight)) continue;
955 request->offset = iend;
956 request->length = rend - iend;
957 continue;
958 }
959 }
960
961 if (!request) continue;
962
963 model = pd->model;
964 // Are we ready yet
965 if (!model)
966 {
967 requests = eina_list_remove_list(requests, ll);
968 free(request);
969 continue;
970 }
971 // Is the request inside the limit of the model?
972 if (request->offset >= efl_model_children_count_get(model))
973 {
974 requests = eina_list_remove_list(requests, ll);
975 free(request);
976 continue;
977 }
978 // Is its limit outside the model limit?
979 if (request->offset + request->length >= efl_model_children_count_get(model))
980 {
981 request->length = efl_model_children_count_get(model) - request->offset;
982 }
983
984 // We now have a request, time to trigger a fetch
985 // We assume here that we are always fetching the model (model_requested must be true)
986 request->f = efl_model_children_slice_get(model, request->offset, request->length);
987 request->f = efl_future_then(obj, request->f,
988 .success = _model_fetched_cb,
989 .data = request,
990 .free = _model_free_cb);
991
992 eina_list_move_list(&pd->requests, &requests, ll);
993 }
994 return eina_list_free(requests);
995 }
996
997 static Efl_Ui_Position_Manager_Size_Batch_Result
_batch_size_cb(void * data,Efl_Ui_Position_Manager_Size_Call_Config conf,Eina_Rw_Slice memory)998 _batch_size_cb(void *data, Efl_Ui_Position_Manager_Size_Call_Config conf, Eina_Rw_Slice memory)
999 {
1000 MY_DATA_GET(data, pd);
1001 Efl_Ui_Position_Manager_Size_Batch_Entity *sizes;
1002 Efl_Ui_Collection_Request *request = NULL;
1003 Efl_Ui_Position_Manager_Size_Batch_Result result = {0};
1004 Efl_Model *parent;
1005 Eina_List *requests = NULL;
1006 Eina_Size2D item_base = {0};
1007 unsigned int limit;
1008 unsigned int idx = 0;
1009
1010 // get the approximate value from the tree node
1011 parent = pd->model;
1012
1013 sizes = memory.mem;
1014 //count = efl_model_children_count_get(parent);
1015 limit = conf.range.end_id - conf.range.start_id;
1016 ITEM_BASE_SIZE_FROM_MODEL(parent, item_base);
1017
1018 // Look in the temporary cache now for the beginning of the buffer
1019 #ifdef VIEWPORT_ENABLE
1020 if (pd->viewport[0] && ((unsigned int)(conf.range.start_id + idx) < pd->viewport[0]->offset))
1021 {
1022 while ((unsigned int)(conf.range.start_id + idx) < pd->viewport[0]->offset && idx < limit)
1023 {
1024 unsigned int search_index = conf.range.start_id + idx;
1025 requests = _cache_size_fetch(requests, &request, pd,
1026 search_index, &sizes[idx], item_base);
1027 idx++;
1028 }
1029 }
1030
1031 // Then look in our buffer view if the needed information can be found there
1032 for (i = 0; i < 3; ++i)
1033 {
1034 if (!pd->viewport[i]) continue;
1035
1036 while (idx < limit &&
1037 (pd->viewport[i]->offset <= conf.range.start_id + idx) &&
1038 (conf.range.start_id + idx < (pd->viewport[i]->offset + pd->viewport[i]->count)))
1039 {
1040 unsigned int offset = conf.range.start_id + idx - pd->viewport[i]->offset;
1041 Efl_Model *model = pd->viewport[i]->items[offset].model;
1042 Efl_Gfx_Entity *entity = pd->viewport[i]->items[offset].entity;
1043 Eina_Bool entity_request = EINA_FALSE;
1044
1045 if (model)
1046 {
1047 Eina_Size2D item_size;
1048 Eina_Bool found = EINA_FALSE;
1049
1050 if (ITEM_SIZE_FROM_MODEL(model, item_size))
1051 found = EINA_TRUE;
1052 if (!found && entity)
1053 {
1054 item_size = efl_gfx_hint_size_combined_min_get(entity);
1055 //if the size is 0 here, then we are running into trouble,
1056 //fetch size from the parent model, where some fallback is defined
1057 if (item_size.h == 0 && item_size.w == 0)
1058 {
1059 item_size = item_base;
1060 found = EINA_TRUE;
1061 }
1062 else
1063 {
1064 _size_to_model(model, item_size);
1065 found = EINA_TRUE;
1066 }
1067
1068 }
1069
1070 if (found)
1071 {
1072 sizes[idx].size = item_size;
1073 sizes[idx].element_depth = 0;
1074 sizes[idx].depth_leader = EINA_FALSE;
1075 goto done;
1076 }
1077
1078 // We will need an entity to calculate this size
1079 entity_request = EINA_TRUE;
1080 }
1081 // No data, add to the requests
1082 requests = _request_add(requests, &request, conf.range.start_id + idx, entity_request);
1083
1084 sizes[idx].size = item_base;
1085 sizes[idx].element_depth = 0;
1086 sizes[idx].depth_leader = EINA_FALSE;
1087
1088 done:
1089 idx++;
1090 }
1091 }
1092
1093 // Look in the temporary cache now for the end of the buffer
1094 while (idx < limit)
1095 {
1096 unsigned int search_index = conf.range.start_id + idx;
1097 requests = _cache_size_fetch(requests, &request, pd,
1098 search_index, &sizes[idx], item_base);
1099 idx++;
1100 }
1101 #endif
1102
1103 /* if (conf.cache_request) */
1104 /* { */
1105 /* printf("CACHING SIZE CALL\n"); */
1106 /* while (idx < limit) */
1107 /* { */
1108 /* sizes[idx].depth_leader = EINA_FALSE; */
1109 /* sizes[idx].element_depth = 0; */
1110 /* sizes[idx].size = pd->last_base; */
1111 /* idx++; */
1112 /* } */
1113 /* fprintf(stderr, "read with no fetch\n"); */
1114 /* } */
1115 /* else */
1116 {
1117 while (idx < limit)
1118 {
1119 unsigned int search_index = conf.range.start_id + idx;
1120 requests = _cache_size_fetch(requests, &request, pd,
1121 search_index, &sizes[idx], item_base);
1122 idx++;
1123 }
1124
1125
1126 // Done, but flush request first
1127 if (request) requests = eina_list_append(requests, request);
1128
1129 requests = _batch_request_flush(requests, data, pd);
1130 }
1131
1132 // Get the amount of filled item
1133 result.filled_items = limit;
1134
1135 return result;
1136 }
1137
1138 static Efl_Ui_Position_Manager_Object_Batch_Result
_batch_entity_cb(void * data,Efl_Ui_Position_Manager_Request_Range range,Eina_Rw_Slice memory)1139 _batch_entity_cb(void *data, Efl_Ui_Position_Manager_Request_Range range, Eina_Rw_Slice memory)
1140 {
1141 MY_DATA_GET(data, pd);
1142 Efl_Ui_Position_Manager_Object_Batch_Entity *entities;
1143 Efl_Ui_Collection_Request *request = NULL;
1144 Efl_Ui_Position_Manager_Object_Batch_Result result = {0};
1145 Eina_List *requests = NULL;
1146 #ifdef VIEWPORT_ENABLE
1147 Efl_Model *parent;
1148 #endif
1149 unsigned int limit;
1150 unsigned int idx = 0;
1151
1152 //parent = pd->model;
1153
1154 entities = memory.mem;
1155 //count = efl_model_children_count_get(parent);
1156 limit = range.end_id - range.start_id;
1157
1158 // Look in the temporary cache now for the beginning of the buffer
1159 #ifdef VIEWPORT_ENABLE
1160 if (pd->viewport[0] && ((unsigned int)(range.start_id + idx) < pd->viewport[0]->offset))
1161 {
1162 while (idx < limit && (unsigned int)(range.start_id + idx) < pd->viewport[0]->offset)
1163 {
1164 unsigned int search_index = range.start_id + idx;
1165
1166 requests = _cache_entity_fetch(requests, &request, pd,
1167 search_index, &entities[idx]);
1168
1169 idx++;
1170 }
1171 }
1172
1173 // Then look in our buffer view if the needed information can be found there
1174 for (i = 0; i < 3; ++i)
1175 {
1176 if (!pd->viewport[i]) continue;
1177
1178 while (idx < limit &&
1179 (pd->viewport[i]->offset <= range.start_id + idx) &&
1180 (range.start_id + idx < (pd->viewport[i]->offset + pd->viewport[i]->count)))
1181 {
1182 unsigned int offset = range.start_id + idx - pd->viewport[i]->offset;
1183 Efl_Gfx_Entity *entity = pd->viewport[i]->items[offset].entity;
1184
1185 if (!entity)
1186 {
1187 // No data, add to the requests
1188 requests = _request_add(requests, &request, range.start_id + idx, EINA_TRUE);
1189
1190 entities[idx].entity = NULL;
1191 entities[idx].depth_leader = EINA_FALSE;
1192 entities[idx].element_depth = 0;
1193 }
1194 else
1195 {
1196 entities[idx].entity = entity;
1197 entities[idx].depth_leader = EINA_FALSE;
1198 entities[idx].element_depth = 0;
1199 }
1200
1201 idx++;
1202 }
1203 }
1204 #endif
1205
1206 // Look in the temporary cache now for the end of the buffer
1207 while (idx < limit)
1208 {
1209 unsigned int search_index = range.start_id + idx;
1210
1211 requests = _cache_entity_fetch(requests, &request, pd,
1212 search_index, &entities[idx]);
1213 idx++;
1214 }
1215 // Done, but flush request first
1216 if (request)
1217 {
1218 requests = eina_list_append(requests, request);
1219 }
1220
1221 requests = _batch_request_flush(requests, data, pd);
1222
1223 // Get the amount of filled item
1224 result.filled_items = limit;
1225
1226 return result;
1227 }
1228
1229
1230 #if 0
1231 static void
1232 _batch_free_cb(void *data)
1233 {
1234 efl_unref(data);
1235 }
1236 #endif
1237
1238 static void
flush_min_size(Eo * obj,Efl_Ui_Collection_View_Data * pd)1239 flush_min_size(Eo *obj, Efl_Ui_Collection_View_Data *pd)
1240 {
1241 Eina_Size2D tmp = pd->content_min_size;
1242
1243 if (!pd->match_content.w)
1244 tmp.w = -1;
1245
1246 if (!pd->match_content.h)
1247 tmp.h = -1;
1248
1249 efl_gfx_hint_size_restricted_min_set(obj, tmp);
1250 }
1251
1252 static void
_manager_content_size_changed_cb(void * data,const Efl_Event * ev)1253 _manager_content_size_changed_cb(void *data, const Efl_Event *ev)
1254 {
1255 Eina_Size2D *size = ev->info;
1256 MY_DATA_GET(data, pd);
1257
1258 efl_gfx_entity_size_set(pd->sizer, *size);
1259 }
1260
1261 static void
_manager_content_min_size_changed_cb(void * data,const Efl_Event * ev)1262 _manager_content_min_size_changed_cb(void *data, const Efl_Event *ev)
1263 {
1264 Eina_Size2D *size = ev->info;
1265 MY_DATA_GET(data, pd);
1266
1267 pd->content_min_size = *size;
1268
1269 flush_min_size(data, pd);
1270 }
1271
1272 #ifdef VIEWPORT_ENABLE
1273 static Eina_List *
_viewport_walk_fill(Eina_List * requests,Efl_Ui_Collection_View * obj,Efl_Ui_Collection_View_Data * pd,Efl_Ui_Collection_Viewport * viewport)1274 _viewport_walk_fill(Eina_List *requests,
1275 Efl_Ui_Collection_View *obj,
1276 Efl_Ui_Collection_View_Data *pd,
1277 Efl_Ui_Collection_Viewport *viewport)
1278 {
1279 Efl_Ui_Collection_Request *current = NULL;
1280 unsigned int j;
1281
1282 for (j = 0; j < viewport->count; j++)
1283 {
1284 Efl_Ui_Collection_Item_Lookup *lookup;
1285 unsigned int index = viewport->offset + j;
1286
1287 if (viewport->items[j].model) goto check_entity;
1288
1289 lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &index,
1290 sizeof (index), _cache_tree_lookup,
1291 NULL);
1292
1293 if (lookup)
1294 {
1295 efl_replace(&viewport->items[j].model, lookup->item.model);
1296 efl_replace(&viewport->items[j].entity, lookup->item.entity);
1297 efl_replace(&lookup->item.entity, NULL); // Necessary to avoid premature release
1298
1299 pd->cache = eina_rbtree_inline_remove(pd->cache, EINA_RBTREE_GET(lookup),
1300 _cache_tree_cmp, NULL);
1301 _cache_item_free(EINA_RBTREE_GET(lookup), obj);
1302 }
1303
1304 check_entity:
1305 if (viewport->items[j].entity) continue ;
1306 requests = _request_add(requests, ¤t, index, EINA_TRUE);
1307 }
1308
1309 // We do break request per viewport, just in case we generate to big batch at once
1310 if (current) requests = eina_list_append(requests, current);
1311
1312 return requests;
1313 }
1314
1315 #endif
1316
1317 // An RbTree has the nice property of sorting content. The smaller than the root being in
1318 // son[1] and the greater than the root in son[0]. Using this we can efficiently walk the
1319 // tree once to take note of all the item that need cleaning.
1320 static void
_mark_lesser(Efl_Ui_Collection_Item_Lookup * root,Eina_Array * mark,const unsigned int lower)1321 _mark_lesser(Efl_Ui_Collection_Item_Lookup *root, Eina_Array *mark, const unsigned int lower)
1322 {
1323 if (!root) return ;
1324
1325 if (root->index < lower)
1326 {
1327 eina_array_push(mark, root);
1328 _mark_lesser((void*) EINA_RBTREE_GET(root)->son[1], mark, lower);
1329 }
1330 else
1331 {
1332 _mark_lesser((void*) EINA_RBTREE_GET(root)->son[0], mark, lower);
1333 _mark_lesser((void*) EINA_RBTREE_GET(root)->son[1], mark, lower);
1334 }
1335 }
1336
1337 static void
_mark_ge(Efl_Ui_Collection_Item_Lookup * root,Eina_Array * mark,const unsigned int upper)1338 _mark_ge(Efl_Ui_Collection_Item_Lookup *root, Eina_Array *mark, const unsigned int upper)
1339 {
1340 if (!root) return ;
1341
1342 if (root->index >= upper)
1343 {
1344 eina_array_push(mark, root);
1345 _mark_ge((void*) EINA_RBTREE_GET(root)->son[0], mark, upper);
1346 _mark_ge((void*) EINA_RBTREE_GET(root)->son[1], mark, upper);
1347 }
1348 else
1349 {
1350 _mark_ge((void*) EINA_RBTREE_GET(root)->son[0], mark, upper);
1351 }
1352 }
1353
1354 // we walk the tree twice, once for everything below the limit and once for everything above
1355 // then we do free each item individually.
1356 static void
_idle_cb(void * data,const Efl_Event * event EINA_UNUSED)1357 _idle_cb(void *data, const Efl_Event *event EINA_UNUSED)
1358 {
1359 Efl_Ui_Collection_Item_Lookup *lookup;
1360 Eina_Array mark;
1361 Eina_Array scheduled_release;
1362 MY_DATA_GET(data, pd);
1363 const unsigned int length = pd->current_range.end_id - pd->current_range.start_id;
1364 const unsigned int lower_end = MAX((long)pd->current_range.start_id - (long)length/2, 0);
1365 const unsigned int upper_end = pd->current_range.end_id + length/2;
1366 Eina_Array_Iterator iterator;
1367 unsigned int i;
1368
1369 eina_array_step_set(&mark, sizeof (Eina_Array), 16);
1370 eina_array_step_set(&scheduled_release, sizeof (Eina_Array), 16);
1371
1372 _mark_lesser((void*) pd->cache, &mark, lower_end);
1373 _mark_ge((void*) pd->cache, &mark, upper_end);
1374
1375 EINA_ARRAY_ITER_NEXT(&mark, i, lookup, iterator)
1376 {
1377 pd->cache = (void*) eina_rbtree_inline_remove(pd->cache,
1378 EINA_RBTREE_GET(lookup),
1379 _cache_tree_cmp, NULL);
1380 _item_cleanup(data, pd->factory, &lookup->item, &scheduled_release);
1381 free(lookup);
1382 }
1383 eina_array_flush(&mark);
1384
1385 efl_ui_factory_release(pd->factory, eina_array_iterator_new(&scheduled_release));
1386 eina_array_flush(&scheduled_release);
1387
1388 efl_event_callback_del(efl_main_loop_get(), EFL_LOOP_EVENT_IDLE, _idle_cb, data);
1389 }
1390
1391 #ifndef VIEWPORT_ENABLE
1392 static void
_manager_content_visible_range_changed_cb(void * data,const Efl_Event * ev)1393 _manager_content_visible_range_changed_cb(void *data, const Efl_Event *ev)
1394 {
1395 Efl_Ui_Position_Manager_Range_Update *event = ev->info;
1396 unsigned int count;
1397 unsigned int lower_end;
1398 unsigned int upper_end;
1399 long length;
1400 Efl_Ui_Collection_Request *request = NULL;
1401 Eina_List *requests = NULL;
1402 unsigned int idx;
1403 MY_DATA_GET(data, pd);
1404
1405 pd->current_range.start_id = event->start_id;
1406 pd->current_range.end_id = event->end_id;
1407
1408 count = efl_model_children_count_get(efl_ui_view_model_get(data));
1409
1410 length = pd->current_range.end_id - pd->current_range.start_id;
1411 lower_end = MAX((long)pd->current_range.start_id - (length / 2), 0);
1412 upper_end = MIN(pd->current_range.end_id + (length / 2), count);
1413
1414 idx = lower_end;
1415 while (idx < upper_end)
1416 {
1417 unsigned int search_index = idx;
1418
1419 requests = _cache_entity_fetch(requests, &request, pd,
1420 search_index, NULL);
1421
1422 idx++;
1423 }
1424 // Done, but flush request first
1425 if (request) requests = eina_list_append(requests, request);
1426
1427 requests = _batch_request_flush(requests, data, pd);
1428 }
1429 #endif
1430
1431 #ifdef VIEWPORT_ENABLE
1432 static void
_manager_content_visible_range_changed_cb(void * data,const Efl_Event * ev)1433 _manager_content_visible_range_changed_cb(void *data, const Efl_Event *ev)
1434 {
1435 Efl_Ui_Position_Manager_Range_Update *event = ev->info;
1436 MY_DATA_GET(data, pd);
1437 Eina_List *requests = NULL;
1438 long baseid;
1439 unsigned int delta, marginup, margindown;
1440 unsigned int upperlimit_offset, lowerlimit_offset;
1441 unsigned int i;
1442
1443 pd->start_id = event->start_id;
1444 pd->end_id = event->end_id;
1445
1446 delta = pd->end_id - pd->start_id;
1447
1448 // First time setting up the viewport, so trigger request as we see fit
1449 if (!pd->viewport[0])
1450 {
1451 baseid = pd->start_id - delta;
1452
1453 for (i = 0; i < 3; i++)
1454 {
1455 pd->viewport[i] = calloc(1, sizeof (Efl_Ui_Collection_Viewport));
1456 if (!pd->viewport[i]) continue;
1457
1458 pd->viewport[i]->offset = MAX(baseid + delta * i, 0);
1459 pd->viewport[i]->count = delta;
1460 pd->viewport[i]->items = calloc(delta, sizeof (Efl_Ui_Collection_Item));
1461 if (!pd->viewport[i]->items) continue ;
1462
1463 requests = _viewport_walk_fill(requests, data, pd, pd->viewport[i]);
1464 }
1465
1466 goto flush_requests;
1467 }
1468
1469 // Compute limit offset
1470 upperlimit_offset = delta * 3 + pd->viewport[0]->offset;
1471 lowerlimit_offset = 0;
1472
1473 // Adjust the viewport for size or to much offset change in two step
1474
1475 // Trying to resize first if there size is in bigger/smaller than 25% of the original size
1476 margindown = delta * 75 / 100;
1477 marginup = delta * 125 / 100;
1478 if (margindown < pd->viewport[0]->count &&
1479 pd->viewport[0]->count < marginup)
1480 {
1481 // Trying to do the resize in an optimized way is complex, let's do it simple
1482 Efl_Ui_Collection_Item *items[3];
1483 unsigned int j = 0, t = 1;
1484
1485 for (i = 0; i < 3; i++)
1486 {
1487 unsigned int m;
1488
1489 items[i] = calloc(delta, sizeof (Efl_Ui_Collection_Item));
1490 if (!items[i]) continue;
1491
1492 for (m = 0; m < delta && t < 3; m++)
1493 {
1494 items[i][m] = pd->viewport[t]->items[j];
1495
1496 j++;
1497 if (j < pd->viewport[t]->count) continue;
1498
1499 j = 0;
1500 t++;
1501 if (t == 3) break;
1502 }
1503
1504 // Preserve last updated index to later build a request
1505 if (t == 3)
1506 {
1507 upperlimit_offset = pd->viewport[0]->offset + i * delta + m;
1508
1509 t = 4; // So that we never come back here again
1510 }
1511 }
1512
1513 // For now destroy leftover object, could be cached
1514 for (i = t; i < 3; i++)
1515 {
1516 for (; j < pd->viewport[i]->count; j++)
1517 {
1518 _item_cleanup(pd->factory, &pd->viewport[i]->items[j]);
1519 }
1520 j = 0;
1521 }
1522
1523 // And now define viewport back
1524 for (i = 0; i < 3; i++)
1525 {
1526 free(pd->viewport[i]->items);
1527 pd->viewport[i]->items = items[i];
1528 pd->viewport[i]->count = delta;
1529 pd->viewport[i]->offset = pd->viewport[0]->offset + delta * i;
1530 }
1531 }
1532
1533 // We decided that resizing was unecessary
1534 delta = pd->viewport[0]->count;
1535
1536 // Try to keep the visual viewport in between half of the first and last viewport
1537
1538 // start_id is in the first half of the first viewport, assume upward move
1539 // start_id + delta is in the second half of the last viewport, assume upward move
1540 if (pd->viewport[0]->offset + delta / 2 < pd->start_id ||
1541 pd->start_id + delta > pd->viewport[2]->offset + delta / 2)
1542 {
1543 // We could optimize this to actually just move viewport around in most cases
1544 Efl_Ui_Collection_Item *items[3];
1545 unsigned int j = 0, t = 0;
1546 unsigned int target, current;
1547
1548 // Case where are at the top
1549 if (pd->start_id < delta && pd->viewport[0]->offset == 0) goto build_request;
1550
1551 // Trying to adjust the offset to maintain it in the center viewport +/- delta/2
1552 baseid = (pd->start_id < delta) ? 0 : pd->start_id - delta;
1553
1554 // Lookup for starting point
1555 lowerlimit_offset = pd->viewport[0]->offset;
1556 target = baseid;
1557
1558 // cleanup before target
1559 for (current = pd->viewport[t]->offset; current < target; current++)
1560 {
1561 _item_cleanup(pd->factory, &pd->viewport[t]->items[j]);
1562
1563 j++;
1564 if (j < pd->viewport[t]->count) continue;
1565
1566 j = 0;
1567 t++;
1568 if (t == 3) break;
1569 }
1570
1571 // Allocation and copy
1572 for (i = 0; i < 3; i++)
1573 {
1574 unsigned int m;
1575
1576 items[i] = calloc(delta, sizeof (Efl_Ui_Collection_Item));
1577 if (!items[i]) continue;
1578
1579 for (m = 0; m < delta && t < 3; m++, target++)
1580 {
1581 if (target < pd->viewport[t]->offset) continue ;
1582 items[i][m] = pd->viewport[t]->items[j];
1583
1584 j++;
1585 if (j < pd->viewport[t]->count) continue;
1586
1587 j = 0;
1588 t++;
1589 if (t == 3) break;
1590 }
1591
1592 // Preserve last updated index to later build a request
1593 if (t == 3)
1594 {
1595 if (upperlimit_offset > pd->viewport[0]->offset + i * delta + m)
1596 {
1597 upperlimit_offset = pd->viewport[0]->offset + i * delta + m;
1598 }
1599
1600 t = 4; // So that we never come back here again
1601 }
1602 }
1603
1604 // For now destroy leftover object, could be cached
1605 for (i = t; i < 3; i++)
1606 {
1607 for (; j < pd->viewport[i]->count; j++)
1608 {
1609 _item_cleanup(pd->factory, &pd->viewport[i]->items[j]);
1610 }
1611 j = 0;
1612 }
1613
1614 // And now define viewport back
1615 for (i = 0; i < 3; i++)
1616 {
1617 free(pd->viewport[i]->items);
1618 pd->viewport[i]->items = items[i];
1619 pd->viewport[i]->offset = baseid + delta * i;
1620 }
1621 }
1622
1623 build_request:
1624 // Check if the first viewport has all the lower part of it filled with objects
1625 if (pd->viewport[0]->offset < lowerlimit_offset)
1626 {
1627 Efl_Ui_Collection_Request *request;
1628
1629 request = calloc(1, sizeof (Efl_Ui_Collection_Request));
1630 if (request) return ;
1631
1632 request->offset = lowerlimit_offset;
1633 // This length work over multiple viewport as they are contiguous
1634 request->length = lowerlimit_offset - pd->viewport[0]->offset;
1635 request->need_size = EINA_TRUE;
1636 request->need_entity = EINA_TRUE;
1637
1638 requests = eina_list_append(requests, request);
1639 }
1640
1641 // Check if the last viewport has all the upper part of it filler with objects
1642 if (pd->viewport[2]->offset + pd->viewport[2]->count > upperlimit_offset)
1643 {
1644 Efl_Ui_Collection_Request *request;
1645
1646 request = calloc(1, sizeof (Efl_Ui_Collection_Request));
1647 if (request) return ;
1648
1649 request->offset = upperlimit_offset;
1650 // This length work over multiple viewport as they are contiguous
1651 request->length = pd->viewport[2]->offset + pd->viewport[2]->count - upperlimit_offset;
1652 request->need_size = EINA_TRUE;
1653 request->need_entity = EINA_TRUE;
1654
1655 requests = eina_list_append(requests, request);
1656 }
1657
1658 flush_requests:
1659 requests = _batch_request_flush(requests, data, pd);
1660 }
1661 #endif
1662
1663 EFL_CALLBACKS_ARRAY_DEFINE(manager_cbs,
1664 { EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, _manager_content_size_changed_cb },
1665 { EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, _manager_content_min_size_changed_cb },
1666 { EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, _manager_content_visible_range_changed_cb }
1667 )
1668
1669 static void
_item_scroll_internal(Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd,unsigned int index,double align EINA_UNUSED,Eina_Bool anim)1670 _item_scroll_internal(Eo *obj EINA_UNUSED,
1671 Efl_Ui_Collection_View_Data *pd,
1672 unsigned int index,
1673 double align EINA_UNUSED,
1674 Eina_Bool anim)
1675 {
1676 Eina_Rect ipos, view;
1677 Eina_Position2D vpos;
1678
1679 if (!pd->scroller) return;
1680
1681 ipos = efl_ui_position_manager_entity_position_single_item(pd->manager, index);
1682 view = efl_ui_scrollable_viewport_geometry_get(pd->scroller);
1683 vpos = efl_ui_scrollable_content_pos_get(pd->scroller);
1684
1685 ipos.x = ipos.x + vpos.x - view.x;
1686 ipos.y = ipos.y + vpos.y - view.y;
1687
1688 //FIXME scrollable needs some sort of align, the docs do not even garantee to completely move in the element
1689 efl_ui_scrollable_scroll(pd->scroller, ipos, anim);
1690 }
1691
1692 // Exported function
1693
1694 EOLIAN static void
_efl_ui_collection_view_factory_set(Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd,Efl_Ui_Factory * factory)1695 _efl_ui_collection_view_factory_set(Eo *obj EINA_UNUSED, Efl_Ui_Collection_View_Data *pd,
1696 Efl_Ui_Factory *factory)
1697 {
1698 if (pd->factory) efl_ui_property_bind(pd->factory, "selected", NULL);
1699 efl_replace(&pd->factory, factory);
1700 if (pd->factory) efl_ui_property_bind(pd->factory, "selected", "self.selected");
1701 }
1702
1703 EOLIAN static Efl_Ui_Factory *
_efl_ui_collection_view_factory_get(const Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd)1704 _efl_ui_collection_view_factory_get(const Eo *obj EINA_UNUSED, Efl_Ui_Collection_View_Data *pd)
1705 {
1706 return pd->factory;
1707 }
1708
1709 static void
_unref_cb(void * data)1710 _unref_cb(void *data)
1711 {
1712 Eo *obj = data;
1713
1714 efl_unref(obj);
1715 }
1716
1717 EOLIAN static void
_efl_ui_collection_view_position_manager_set(Eo * obj,Efl_Ui_Collection_View_Data * pd,Efl_Ui_Position_Manager_Entity * manager)1718 _efl_ui_collection_view_position_manager_set(Eo *obj, Efl_Ui_Collection_View_Data *pd,
1719 Efl_Ui_Position_Manager_Entity *manager)
1720 {
1721 Efl_Model *model;
1722 unsigned int count;
1723
1724 if (manager)
1725 EINA_SAFETY_ON_FALSE_RETURN(efl_isa(manager, EFL_UI_POSITION_MANAGER_ENTITY_INTERFACE));
1726
1727 if (pd->manager)
1728 {
1729 efl_event_callback_array_del(pd->manager, manager_cbs(), obj);
1730 efl_del(pd->manager);
1731 }
1732 pd->manager = manager;
1733 if (!pd->manager) return;
1734
1735 // Start watching change on model from here on
1736 model = pd->model;
1737 count = model ? efl_model_children_count_get(model) : 0;
1738
1739 efl_parent_set(pd->manager, obj);
1740 efl_event_callback_array_add(pd->manager, manager_cbs(), obj);
1741 switch(efl_ui_position_manager_entity_version(pd->manager, 1))
1742 {
1743 case 1:
1744 efl_ui_position_manager_data_access_v1_data_access_set(pd->manager,
1745 efl_provider_find(obj, EFL_UI_WIN_CLASS),
1746 efl_ref(obj), _batch_entity_cb, _unref_cb,
1747 efl_ref(obj), _batch_size_cb, _unref_cb,
1748 count);
1749 break;
1750 }
1751
1752 if (efl_finalized_get(obj))
1753 efl_ui_position_manager_entity_viewport_set(pd->manager, efl_ui_scrollable_viewport_geometry_get(obj));
1754 efl_ui_layout_orientation_set(pd->manager, pd->direction);
1755 }
1756
1757 EOLIAN static Efl_Ui_Position_Manager_Entity *
_efl_ui_collection_view_position_manager_get(const Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd)1758 _efl_ui_collection_view_position_manager_get(const Eo *obj EINA_UNUSED,
1759 Efl_Ui_Collection_View_Data *pd)
1760 {
1761 return pd->manager;
1762 }
1763
1764 static void
_efl_model_count_changed(void * data,const Efl_Event * event EINA_UNUSED)1765 _efl_model_count_changed(void *data, const Efl_Event *event EINA_UNUSED)
1766 {
1767 Efl_Ui_Collection_Request *request = NULL;
1768 Eina_List *requests = NULL;
1769 MY_DATA_GET(data, pd);
1770 unsigned int index;
1771 unsigned int count = 0;
1772
1773 count = efl_model_children_count_get(pd->model);
1774 if (pd->focus.last)
1775 {
1776 index = _lookup_entity_index(pd->focus.last, NULL);
1777
1778 if (index + 1 == count)
1779 return ;
1780 }
1781
1782 // The last item is not the last item anymore
1783 requests = _request_add(requests, &request, count, EINA_TRUE);
1784 requests = _batch_request_flush(requests, data, pd);
1785
1786 // We are not triggering efl_ui_position_manager_entity_data_access_set as it is can
1787 // only be slow, we rely on child added/removed instead (If we were to not rely on
1788 // child added/removed we could maybe use count changed)
1789 }
1790
1791 static void
_efl_model_properties_changed(void * data EINA_UNUSED,const Efl_Event * event EINA_UNUSED)1792 _efl_model_properties_changed(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
1793 {
1794 // We could here watch if the global base size item change and notify of a global change
1795 // But I can not find a proper way to do it for the object that are not visible, which
1796 // is kind of the point...
1797 }
1798
1799 static void
_cache_cleanup_above(Efl_Ui_Collection_View * obj,Efl_Ui_Collection_View_Data * pd,unsigned int index)1800 _cache_cleanup_above(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd, unsigned int index)
1801 {
1802 Efl_Ui_Collection_Item_Lookup *lookup;
1803 Eina_Array scheduled_release;
1804 Eina_Array mark;
1805 Eina_Array_Iterator iterator;
1806 unsigned int i;
1807
1808 eina_array_step_set(&mark, sizeof (Eina_Array), 16);
1809 eina_array_step_set(&scheduled_release, sizeof (Eina_Array), 16);
1810
1811 _mark_ge((void*) pd->cache, &mark, index);
1812
1813 EINA_ARRAY_ITER_NEXT(&mark, i, lookup, iterator)
1814 {
1815 pd->cache = (void*) eina_rbtree_inline_remove(pd->cache,
1816 EINA_RBTREE_GET(lookup),
1817 _cache_tree_cmp, NULL);
1818 _item_cleanup(obj, pd->factory, &lookup->item, &scheduled_release);
1819 free(lookup);
1820 }
1821 eina_array_flush(&mark);
1822
1823 efl_ui_factory_release(pd->factory, eina_array_iterator_new(&scheduled_release));
1824 eina_array_flush(&scheduled_release);
1825 }
1826
1827 static void
_efl_model_child_added(void * data,const Efl_Event * event)1828 _efl_model_child_added(void *data, const Efl_Event *event)
1829 {
1830 // At the moment model only append child, but let's try to handle it theorically correct
1831 Efl_Model_Children_Event *ev = event->info;
1832 MY_DATA_GET(data, pd);
1833 #ifdef VIEWPORT_ENABLE
1834 Eina_List *requests = NULL;
1835 unsigned int i;
1836 #endif
1837
1838 _cache_cleanup_above(data, pd, ev->index);
1839
1840 // Check if we really have something to do
1841 #ifdef VIEWPORT_ENABLE
1842 if (!pd->viewport[0]) goto notify_manager;
1843
1844 // Insert the child in the viewport if necessary
1845 for (i = 0; i < 3; i++)
1846 {
1847 Efl_Ui_Collection_Request *request;
1848 unsigned int o;
1849 unsigned int j;
1850
1851 if (ev->index < pd->viewport[i]->offset)
1852 {
1853 pd->viewport[i]->offset++;
1854 continue;
1855 }
1856 if (pd->viewport[i]->offset + pd->viewport[i]->count < ev->index)
1857 {
1858 continue;
1859 }
1860
1861 for (j = 2; j > i; j--)
1862 {
1863 _item_cleanup(pd->factory, &pd->viewport[j]->items[pd->viewport[j]->count - 1]);
1864 memmove(&pd->viewport[j]->items[1],
1865 &pd->viewport[j]->items[0],
1866 (pd->viewport[j]->count - 1) * sizeof (Efl_Ui_Collection_Item));
1867 pd->viewport[j]->items[0] = pd->viewport[j - 1]->items[pd->viewport[j - 1]->count - 1];
1868 pd->viewport[j - 1]->items[pd->viewport[j - 1]->count - 1].entity = NULL;
1869 pd->viewport[j - 1]->items[pd->viewport[j - 1]->count - 1].model = NULL;
1870 }
1871 o = ev->index - pd->viewport[i]->offset;
1872 memmove(&pd->viewport[j]->items[o],
1873 &pd->viewport[j]->items[o + 1],
1874 (pd->viewport[j]->count - 1 - o) * sizeof (Efl_Ui_Collection_Item));
1875 pd->viewport[j]->items[o].entity = NULL;
1876 pd->viewport[j]->items[o].model = efl_ref(ev->child);
1877
1878 request = calloc(1, sizeof (Efl_Ui_Collection_Request));
1879 if (!request) break;
1880 request->offset = ev->index;
1881 request->length = 1;
1882 request->need_size = EINA_TRUE;
1883 request->need_entity = EINA_TRUE;
1884
1885 requests = eina_list_append(requests, request);
1886
1887 requests = _batch_request_flush(requests, data, pd);
1888
1889 break;
1890 }
1891
1892 notify_manager:
1893 #endif
1894 efl_ui_position_manager_entity_item_added(pd->manager, ev->index, NULL);
1895 }
1896
1897 static void
_efl_model_child_removed(void * data,const Efl_Event * event)1898 _efl_model_child_removed(void *data, const Efl_Event *event)
1899 {
1900 Efl_Model_Children_Event *ev = event->info;
1901 MY_DATA_GET(data, pd);
1902 Eina_List *requests = NULL;
1903 #ifdef VIEWPORT_ENABLE
1904 unsigned int i;
1905 #endif
1906 unsigned int upper_end;
1907 long length;
1908 unsigned int count;
1909 unsigned int request_length;
1910
1911 // FIXME: later optimization, instead of reloading everyone, we could actually track index and self
1912 // update would be more efficient, but it is also more tricky
1913 _cache_cleanup_above(data, pd, ev->index);
1914
1915 count = efl_model_children_count_get(event->object);
1916 length = pd->current_range.end_id - pd->current_range.start_id;
1917 upper_end = MIN(pd->current_range.end_id + (length / 2), count);
1918
1919 // Check if we really have something to do
1920 #ifdef VIEWPORT_ENABLE
1921 if (!pd->viewport[0]) goto notify_manager;
1922
1923 // Insert the child in the viewport if necessary
1924 for (i = 0; i < 3; i++)
1925 {
1926 Efl_Ui_Collection_Request *request;
1927 unsigned int o;
1928
1929 if (ev->index < pd->viewport[i]->offset)
1930 {
1931 pd->viewport[i]->offset--;
1932 continue;
1933 }
1934 if (pd->viewport[i]->offset + pd->viewport[i]->count < ev->index)
1935 {
1936 continue;
1937 }
1938
1939 o = ev->index - pd->viewport[i]->offset;
1940 _item_cleanup(pd->factory, &pd->viewport[i]->items[o]);
1941 for (; i < 3; i++)
1942 {
1943 memmove(&pd->viewport[i]->items[o],
1944 &pd->viewport[i]->items[o + 1],
1945 (pd->viewport[i]->count - 1 - o) * sizeof (Efl_Ui_Collection_Item));
1946 if (i + 1 < 3)
1947 {
1948 pd->viewport[i]->items[pd->viewport[i]->count - 1] = pd->viewport[i + 1]->items[0];
1949 }
1950 else
1951 {
1952 pd->viewport[i]->items[pd->viewport[i]->count - 1].entity = NULL;
1953 pd->viewport[i]->items[pd->viewport[i]->count - 1].model = NULL;
1954 }
1955 o = 0;
1956 }
1957
1958 request = calloc(1, sizeof (Efl_Ui_Collection_Request));
1959 if (!request) break;
1960 request->offset = pd->viewport[2]->offset + pd->viewport[i]->count - 1;
1961 request->length = 1;
1962 request->need_size = EINA_TRUE;
1963 request->need_entity = EINA_TRUE;
1964
1965 requests = eina_list_append(requests, request);
1966
1967 requests = _batch_request_flush(requests, data, pd);
1968
1969 break;
1970 }
1971
1972 notify_manager:
1973 #endif
1974 request_length = upper_end - ev->index;
1975
1976 if (request_length > 0)
1977 {
1978 Efl_Ui_Collection_Request *request = NULL;
1979
1980 requests = _request_add(requests, &request, ev->index, EINA_TRUE);
1981 request->length = request_length;
1982 requests = eina_list_append(requests, request);
1983 requests = _batch_request_flush(requests, data, pd);
1984 }
1985
1986 efl_ui_position_manager_entity_item_removed(pd->manager, ev->index, NULL);
1987 }
1988
1989 EFL_CALLBACKS_ARRAY_DEFINE(model_cbs,
1990 { EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, _efl_model_count_changed },
1991 { EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_properties_changed },
1992 { EFL_MODEL_EVENT_CHILD_ADDED, _efl_model_child_added },
1993 { EFL_MODEL_EVENT_CHILD_REMOVED, _efl_model_child_removed })
1994
1995 static void
_efl_ui_collection_view_model_changed(void * data,const Efl_Event * event)1996 _efl_ui_collection_view_model_changed(void *data, const Efl_Event *event)
1997 {
1998 Efl_Model_Changed_Event *ev = event->info;
1999 Eina_List *requests = NULL;
2000 MY_DATA_GET(data, pd);
2001 Eina_Iterator *it;
2002 const char *property;
2003 Efl_Model *model = NULL;
2004 unsigned int count;
2005 Efl_Model *mselect = NULL;
2006 Eina_Bool selection = EINA_FALSE, sizing = EINA_FALSE;
2007
2008 // Cleanup all object, pending request to prepare refetching everything
2009 _all_cleanup(data, pd);
2010 if (pd->model) efl_event_callback_array_del(pd->model, model_cbs(), data);
2011 if (pd->multi_selectable_async_model)
2012 {
2013 efl_event_callback_forwarder_del(pd->multi_selectable_async_model,
2014 EFL_UI_SELECTABLE_EVENT_SELECTION_CHANGED,
2015 data);
2016 efl_composite_detach(data, pd->multi_selectable_async_model);
2017 }
2018
2019 if (!ev->current)
2020 {
2021 efl_replace(&pd->model, NULL);
2022 efl_replace(&pd->multi_selectable_async_model, NULL);
2023 return ;
2024 }
2025
2026 it = efl_model_properties_get(ev->current);
2027 EINA_ITERATOR_FOREACH(it, property)
2028 {
2029 // Check if the model provide selection
2030 if (eina_streq(property, "child.selected"))
2031 selection = EINA_TRUE;
2032 // Check if the model provide sizing logic
2033 else if (eina_streq(property, _efl_model_property_itemw) ||
2034 eina_streq(property, _efl_model_property_itemh))
2035 sizing = EINA_TRUE;
2036 }
2037 eina_iterator_free(it);
2038
2039 if (selection)
2040 {
2041 // Search the composition of model for the one providing MULTI_SELECTABLE_ASYNC
2042 mselect = ev->current;
2043 while (mselect &&
2044 !efl_isa(mselect, EFL_UI_MULTI_SELECTABLE_INDEX_RANGE_INTERFACE) &&
2045 efl_isa(mselect, EFL_COMPOSITE_MODEL_CLASS))
2046 mselect = efl_ui_view_model_get(mselect);
2047
2048 if (!efl_isa(mselect, EFL_UI_MULTI_SELECTABLE_INDEX_RANGE_INTERFACE))
2049 {
2050 mselect = NULL;
2051 selection = EINA_FALSE;
2052 }
2053 }
2054
2055 // Try to build the minimal chain of necessary model for collection view
2056 model = ev->current;
2057
2058 // Build and connect the selection model properly
2059 if (!mselect)
2060 {
2061 mselect = model = efl_add_ref(EFL_UI_SELECT_MODEL_CLASS, data,
2062 efl_ui_view_model_set(efl_added, model),
2063 efl_loop_model_volatile_make(efl_added));
2064 }
2065 efl_replace(&pd->multi_selectable_async_model, mselect);
2066 efl_composite_attach(data, pd->multi_selectable_async_model);
2067 efl_event_callback_forwarder_add(pd->multi_selectable_async_model,
2068 EFL_UI_SELECTABLE_EVENT_SELECTION_CHANGED,
2069 data);
2070
2071 if (!sizing) model = efl_add_ref(EFL_UI_HOMOGENEOUS_MODEL_CLASS, data,
2072 efl_ui_view_model_set(efl_added, model),
2073 efl_loop_model_volatile_make(efl_added));
2074
2075 efl_replace(&pd->model, model);
2076 efl_event_callback_array_add(pd->model, model_cbs(), data);
2077
2078 if (mselect) efl_unref(mselect);
2079 if (!sizing) efl_unref(model);
2080
2081 count = efl_model_children_count_get(model);
2082
2083 #ifdef VIEWPORT_ENABLE
2084 for (i = 0; i < 3; i++)
2085 {
2086
2087 if (!pd->viewport[i]) continue ;
2088 if (pd->viewport[i]->count == 0) continue ;
2089
2090 request = calloc(1, sizeof (Efl_Ui_Collection_Request));
2091 if (!request) continue ;
2092
2093 request->offset = pd->viewport[i]->offset;
2094 request->length = pd->viewport[i]->count;
2095 request->need_size = EINA_TRUE;
2096 request->need_entity = EINA_TRUE;
2097
2098 requests = eina_list_append(requests, request);
2099 }
2100 #endif
2101
2102 // Fetch last item if necessary for later focus
2103 if (efl_model_children_count_get(model))
2104 {
2105 Efl_Ui_Collection_Request *request = NULL;
2106 uint64_t index = efl_model_children_count_get(model) - 1;
2107
2108 requests = _request_add(requests, &request, index, EINA_TRUE);
2109 }
2110
2111 // Flush all pending request
2112 requests = _batch_request_flush(requests, data, pd);
2113
2114 switch (efl_ui_position_manager_entity_version(pd->manager, 1))
2115 {
2116 case 1:
2117 efl_ui_position_manager_data_access_v1_data_access_set(pd->manager,
2118 efl_provider_find(data, EFL_UI_WIN_CLASS),
2119 efl_ref(data), _batch_entity_cb, _unref_cb,
2120 efl_ref(data), _batch_size_cb, _unref_cb,
2121 count);
2122 break;
2123 }
2124 efl_ui_position_manager_entity_item_size_changed(pd->manager, 0, count - 1);
2125 }
2126
2127 static void
_pan_viewport_changed_cb(void * data,const Efl_Event * ev EINA_UNUSED)2128 _pan_viewport_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
2129 {
2130 MY_DATA_GET(data, pd);
2131 Eina_Rect rect = efl_ui_scrollable_viewport_geometry_get(data);
2132
2133 efl_ui_position_manager_entity_viewport_set(pd->manager, rect);
2134 }
2135
2136 static void
_pan_position_changed_cb(void * data,const Efl_Event * ev EINA_UNUSED)2137 _pan_position_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
2138 {
2139 MY_DATA_GET(data, pd);
2140 Eina_Position2D pos = efl_ui_pan_position_get(pd->pan);
2141 Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
2142 Eina_Vector2 rpos = {0.0, 0.0};
2143
2144 if (max.x > 0.0)
2145 rpos.x = (double)pos.x/(double)max.x;
2146 if (max.y > 0.0)
2147 rpos.y = (double)pos.y/(double)max.y;
2148
2149 efl_ui_position_manager_entity_scroll_position_set(pd->manager, rpos.x, rpos.y);
2150 }
2151
2152 EFL_CALLBACKS_ARRAY_DEFINE(pan_events_cb,
2153 {EFL_UI_PAN_EVENT_PAN_CONTENT_POSITION_CHANGED, _pan_position_changed_cb},
2154 {EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _pan_viewport_changed_cb},
2155 {EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _pan_viewport_changed_cb},
2156 )
2157
2158 EOLIAN static Efl_Object *
_efl_ui_collection_view_efl_object_constructor(Eo * obj,Efl_Ui_Collection_View_Data * pd)2159 _efl_ui_collection_view_efl_object_constructor(Eo *obj, Efl_Ui_Collection_View_Data *pd)
2160 {
2161 pd->direction = EFL_UI_LAYOUT_ORIENTATION_VERTICAL;
2162 obj = efl_constructor(efl_super(obj, EFL_UI_COLLECTION_VIEW_CLASS));
2163
2164 if (!elm_widget_theme_klass_get(obj))
2165 elm_widget_theme_klass_set(obj, "collection");
2166
2167 efl_wref_add(efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(obj)), &pd->sizer);
2168 efl_gfx_color_set(pd->sizer, 0, 0, 0, 0);
2169
2170 efl_wref_add(efl_add(EFL_UI_PAN_CLASS, obj), &pd->pan);
2171 efl_content_set(pd->pan, pd->sizer);
2172 efl_event_callback_array_add(pd->pan, pan_events_cb(), obj);
2173
2174 efl_wref_add(efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj), &pd->scroller);
2175 efl_composite_attach(obj, pd->scroller);
2176 efl_ui_mirrored_set(pd->scroller, efl_ui_mirrored_get(obj));
2177 efl_ui_scroll_manager_pan_set(pd->scroller, pd->pan);
2178
2179 efl_ui_scroll_connector_bind(obj, pd->scroller);
2180
2181 efl_event_callback_add(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED,
2182 _efl_ui_collection_view_model_changed, obj);
2183
2184 return obj;
2185 }
2186
2187 EOLIAN static void
_efl_ui_collection_view_efl_object_invalidate(Eo * obj,Efl_Ui_Collection_View_Data * pd)2188 _efl_ui_collection_view_efl_object_invalidate(Eo *obj,
2189 Efl_Ui_Collection_View_Data *pd)
2190 {
2191 efl_ui_collection_view_position_manager_set(obj, NULL);
2192 efl_event_callback_del(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED,
2193 _efl_ui_collection_view_model_changed, obj);
2194
2195 _all_cleanup(obj, pd);
2196
2197 //pd pan is given to edje, which reparents it, which forces us to manually deleting it
2198 if (pd->pan)
2199 efl_del(pd->pan);
2200
2201 efl_invalidate(efl_super(obj, EFL_UI_COLLECTION_VIEW_CLASS));
2202 }
2203
2204 EOLIAN static void
_efl_ui_collection_view_efl_ui_layout_orientable_orientation_set(Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd,Efl_Ui_Layout_Orientation dir)2205 _efl_ui_collection_view_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED,
2206 Efl_Ui_Collection_View_Data *pd,
2207 Efl_Ui_Layout_Orientation dir)
2208 {
2209 if (pd->direction == dir) return;
2210
2211 pd->direction = dir;
2212 if (pd->manager) efl_ui_layout_orientation_set(pd->manager, dir);
2213 }
2214
2215 EOLIAN static Efl_Ui_Layout_Orientation
_efl_ui_collection_view_efl_ui_layout_orientable_orientation_get(const Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd)2216 _efl_ui_collection_view_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED,
2217 Efl_Ui_Collection_View_Data *pd)
2218 {
2219 return pd->direction;
2220 }
2221
2222 EOLIAN static Eina_Error
_efl_ui_collection_view_efl_ui_widget_theme_apply(Eo * obj,Efl_Ui_Collection_View_Data * pd)2223 _efl_ui_collection_view_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Collection_View_Data *pd)
2224 {
2225 Eina_Error res;
2226
2227 ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC);
2228 res = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
2229 if (res == EFL_UI_THEME_APPLY_ERROR_GENERIC) return res;
2230 efl_ui_mirrored_set(pd->scroller, efl_ui_mirrored_get(obj));
2231 efl_content_set(efl_part(wd->resize_obj, "efl.content"), pd->pan);
2232
2233 return res;
2234 }
2235
2236 EOLIAN static void
_efl_ui_collection_view_efl_ui_scrollable_match_content_set(Eo * obj,Efl_Ui_Collection_View_Data * pd,Eina_Bool w,Eina_Bool h)2237 _efl_ui_collection_view_efl_ui_scrollable_match_content_set(Eo *obj, Efl_Ui_Collection_View_Data *pd, Eina_Bool w, Eina_Bool h)
2238 {
2239 if (pd->match_content.w == w && pd->match_content.h == h)
2240 return;
2241
2242 pd->match_content.w = w;
2243 pd->match_content.h = h;
2244
2245 efl_ui_scrollable_match_content_set(pd->scroller, w, h);
2246 flush_min_size(obj, pd);
2247 }
2248
2249 EOLIAN static Efl_Ui_Focus_Manager *
_efl_ui_collection_view_efl_ui_widget_focus_manager_focus_manager_create(Eo * obj,Efl_Ui_Collection_View_Data * pd EINA_UNUSED,Efl_Ui_Focus_Object * root)2250 _efl_ui_collection_view_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj, Efl_Ui_Collection_View_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root)
2251 {
2252 Efl_Ui_Collection_View_Focus_Manager_Data *mpd;
2253 Eo *manager = efl_add(EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS, obj,
2254 efl_ui_focus_manager_root_set(efl_added, root));
2255
2256 mpd = efl_data_scope_get(manager, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS);
2257 mpd->collection = obj;
2258
2259 return manager;
2260 }
2261
2262 EOLIAN static Efl_Ui_Focus_Object *
_efl_ui_collection_view_efl_ui_focus_manager_move(Eo * obj,Efl_Ui_Collection_View_Data * pd,Efl_Ui_Focus_Direction direction)2263 _efl_ui_collection_view_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Collection_View_Data *pd, Efl_Ui_Focus_Direction direction)
2264 {
2265 Eo *new_obj, *focus;
2266 Eina_Size2D step;
2267
2268 new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
2269 focus = efl_ui_focus_manager_focus_get(obj);
2270 step = efl_gfx_hint_size_combined_min_get(focus);
2271 if (!new_obj)
2272 {
2273 Eina_Rect pos = efl_gfx_entity_geometry_get(focus);
2274 Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(pd->scroller);
2275 Eina_Position2D vpos = efl_ui_scrollable_content_pos_get(pd->scroller);
2276
2277 pos.x = pos.x + vpos.x - view.x;
2278 pos.y = pos.y + vpos.y - view.y;
2279 Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
2280
2281 if (direction == EFL_UI_FOCUS_DIRECTION_RIGHT)
2282 {
2283 if (pos.x < max.x)
2284 {
2285 pos.x = MIN(max.x, pos.x + step.w);
2286 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
2287 new_obj = focus;
2288 }
2289 }
2290 else if (direction == EFL_UI_FOCUS_DIRECTION_LEFT)
2291 {
2292 if (pos.x > 0)
2293 {
2294 pos.x = MAX(0, pos.x - step.w);
2295 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
2296 new_obj = focus;
2297 }
2298 }
2299 else if (direction == EFL_UI_FOCUS_DIRECTION_UP)
2300 {
2301 if (pos.y > 0)
2302 {
2303 pos.y = MAX(0, pos.y - step.h);
2304 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
2305 new_obj = focus;
2306 }
2307 }
2308 else if (direction == EFL_UI_FOCUS_DIRECTION_DOWN)
2309 {
2310 if (pos.y < max.y)
2311 {
2312 pos.y = MAX(0, pos.y + step.h);
2313 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
2314 new_obj = focus;
2315 }
2316 }
2317 }
2318 else
2319 {
2320 Efl_Model *model;
2321 Eina_Value *vindex;
2322 unsigned int index;
2323
2324 model = efl_ui_view_model_get(new_obj);
2325 vindex = efl_model_property_get(model, "child.index");
2326 if (eina_value_uint_convert(vindex, &index))
2327 _item_scroll_internal(obj, pd, index, .0, EINA_TRUE);
2328 eina_value_free(vindex);
2329 }
2330
2331 return new_obj;
2332 }
2333
2334 EOLIAN static Eina_Bool
_efl_ui_collection_view_efl_ui_widget_focus_state_apply(Eo * obj,Efl_Ui_Collection_View_Data * pd EINA_UNUSED,Efl_Ui_Widget_Focus_State current_state,Efl_Ui_Widget_Focus_State * configured_state,Efl_Ui_Widget * redirect EINA_UNUSED)2335 _efl_ui_collection_view_efl_ui_widget_focus_state_apply(Eo *obj, Efl_Ui_Collection_View_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect EINA_UNUSED)
2336 {
2337 return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj);
2338 }
2339
2340 #include "efl_ui_collection_view.eo.c"
2341
2342 #define ITEM_IS_OUTSIDE_VISIBLE(id) id < cpd->start_id || id > cpd->end_id
2343
2344 static Efl_Ui_Item *
_find_item(Eo * obj EINA_UNUSED,Efl_Ui_Collection_View_Data * pd EINA_UNUSED,Eo * focused_element)2345 _find_item(Eo *obj EINA_UNUSED, Efl_Ui_Collection_View_Data *pd EINA_UNUSED, Eo *focused_element)
2346 {
2347 if (!focused_element) return NULL;
2348
2349 while (focused_element &&
2350 efl_key_data_get(focused_element, COLLECTION_VIEW_MANAGED) != COLLECTION_VIEW_MANAGED_YES)
2351 {
2352 focused_element = efl_ui_widget_parent_get(focused_element);
2353 }
2354
2355 return focused_element;
2356 }
2357
2358 static inline void
_assert_item_available(Eo * item,int new_id,Efl_Ui_Collection_View_Data * pd)2359 _assert_item_available(Eo *item, int new_id, Efl_Ui_Collection_View_Data *pd)
2360 {
2361 efl_gfx_entity_visible_set(item, EINA_TRUE);
2362 efl_gfx_entity_geometry_set(item, efl_ui_position_manager_entity_position_single_item(pd->manager, new_id));
2363 }
2364 EOLIAN static void
_efl_ui_collection_view_focus_manager_efl_ui_focus_manager_manager_focus_set(Eo * obj,Efl_Ui_Collection_View_Focus_Manager_Data * pd,Efl_Ui_Focus_Object * focus)2365 _efl_ui_collection_view_focus_manager_efl_ui_focus_manager_manager_focus_set(Eo *obj, Efl_Ui_Collection_View_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *focus)
2366 {
2367 MY_DATA_GET(pd->collection, cpd);
2368 Efl_Ui_Item *item = NULL;
2369 unsigned int item_id;
2370
2371 if (focus == efl_ui_focus_manager_root_get(obj))
2372 {
2373 // Find last item
2374 item = cpd->focus.previously;
2375 if (!item) item = cpd->focus.last;
2376 if (item) item_id = _lookup_entity_index(item, NULL);
2377 else item_id = efl_model_children_count_get(cpd->model) - 1;
2378 }
2379 else
2380 {
2381 item = _find_item(obj, cpd, focus);
2382 if (!item) return ;
2383
2384 item_id = _lookup_entity_index(item, NULL);
2385 }
2386
2387 // If this is NULL then we are before finalize, we cannot serve any sane value here
2388 if (!cpd->manager) return ;
2389
2390 if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
2391 {
2392 _assert_item_available(item, item_id, cpd);
2393 }
2394 efl_ui_focus_manager_focus_set(efl_super(obj, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS), focus);
2395 }
2396
2397 static int
_id_from_item(Efl_Ui_Item * item,unsigned int * index)2398 _id_from_item(Efl_Ui_Item *item, unsigned int *index)
2399 {
2400 Eina_Value *vindex;
2401 Efl_Model *model;
2402
2403 model = efl_ui_view_model_get(item);
2404
2405 vindex = efl_model_property_get(model, "child.index");
2406 EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_uint_convert(vindex, index), EINA_FALSE);
2407 eina_value_free(vindex);
2408 return EINA_TRUE;
2409 }
2410
2411 EOLIAN static Efl_Ui_Focus_Object *
_efl_ui_collection_view_focus_manager_efl_ui_focus_manager_request_move(Eo * obj,Efl_Ui_Collection_View_Focus_Manager_Data * pd,Efl_Ui_Focus_Direction direction,Efl_Ui_Focus_Object * child,Eina_Bool logical)2412 _efl_ui_collection_view_focus_manager_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Collection_View_Focus_Manager_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical)
2413 {
2414 MY_DATA_GET(pd->collection, cpd);
2415 Efl_Ui_Item *new_item = NULL;
2416 Efl_Ui_Item *item;
2417 unsigned int item_id;
2418
2419 if (!child)
2420 child = efl_ui_focus_manager_focus_get(obj);
2421
2422 item = _find_item(obj, cpd, child);
2423
2424 //if this is NULL then we are before finalize, we cannot serve any sane value here
2425 if (!cpd->manager) goto end;
2426 if (!item) goto end;
2427
2428 if (!_id_from_item(item, &item_id))
2429 goto end;
2430
2431 if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
2432 {
2433 unsigned int new_id;
2434
2435 if (!efl_ui_position_manager_entity_relative_item(cpd->manager,
2436 item_id,
2437 direction,
2438 &new_id))
2439 {
2440 new_item = NULL;
2441 }
2442 else
2443 {
2444 Efl_Ui_Collection_Item_Lookup *lookup;
2445 #ifdef VIEWPORT_ENABLE
2446 unsigned int i;
2447
2448 for (i = 0; i < 3; i++)
2449 {
2450 if (!cpd->viewport[i]) continue;
2451
2452 if (!((cpd->viewport[i]->offset <= (unsigned int) new_id) &&
2453 ((unsigned int) new_id < cpd->viewport[i]->offset + cpd->viewport[i]->count)))
2454 continue;
2455
2456 new_item = cpd->viewport[i]->items[new_id - cpd->viewport[i]->offset].entity;
2457 // We shouldn't get in a case where the available item is NULL
2458 if (!new_item) break; // Just in case
2459 _assert_item_available(new_item, new_id, cpd);
2460 }
2461 #else
2462 unsigned int search_index = new_id;
2463 lookup = (void*) eina_rbtree_inline_lookup(cpd->cache, &search_index,
2464 sizeof (search_index), _cache_tree_lookup,
2465 NULL);
2466 if (lookup)
2467 {
2468 _assert_item_available(lookup->item.entity, new_id, cpd);
2469 new_item = lookup->item.entity;
2470 }
2471 else
2472 {
2473 ERR("This item cannot get focus right now. It should be visible first.");
2474 new_item = NULL;
2475 }
2476 #endif
2477 }
2478 }
2479 else
2480 {
2481 new_item = efl_ui_focus_manager_request_move(efl_super(obj, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS), direction, child, logical);
2482 }
2483
2484 end:
2485 efl_replace(&cpd->focus.previously, new_item);
2486 return new_item;
2487 }
2488
2489 #include "efl_ui_collection_view_focus_manager.eo.c"
2490