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, &current, 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