elementary: introduce Efl.Ui.CollectionView a generic listing View.
authorCedric BAIL <cedric.bail@free.fr>
Fri, 5 Jul 2019 21:03:13 +0000 (14:03 -0700)
committerJongmin Lee <jm105.lee@samsung.com>
Tue, 24 Sep 2019 21:47:58 +0000 (06:47 +0900)
The idea of this widget is to provide to MVVM what Efl.Ui.Collection provide and
leverage the same shared logic for layout.

Co-authored-by: Mike Blumenkrantz <zmike@samsung.com>
Co-authored-by: Marcel Hollerbach <mail@marcel-hollerbach.de>
Differential Revision: https://phab.enlightenment.org/D9958

15 files changed:
src/examples/elementary/efl_ui_collection_view_example_1.c [new file with mode: 0644]
src/examples/elementary/meson.build
src/lib/elementary/Efl_Ui.h
src/lib/elementary/efl_ui_collection_events.eo
src/lib/elementary/efl_ui_collection_view.c [new file with mode: 0644]
src/lib/elementary/efl_ui_collection_view.eo [new file with mode: 0644]
src/lib/elementary/efl_ui_collection_view_focus_manager.eo [new file with mode: 0644]
src/lib/elementary/efl_ui_item.c
src/lib/elementary/efl_ui_item.eo
src/lib/elementary/efl_ui_item_private.h
src/lib/elementary/efl_ui_position_manager_list.c
src/lib/elementary/efl_ui_widget.c
src/lib/elementary/efl_ui_widget_factory.c
src/lib/elementary/elm_priv.h
src/lib/elementary/meson.build

diff --git a/src/examples/elementary/efl_ui_collection_view_example_1.c b/src/examples/elementary/efl_ui_collection_view_example_1.c
new file mode 100644 (file)
index 0000000..7296065
--- /dev/null
@@ -0,0 +1,107 @@
+//  gcc -o efl_ui_collection_view_example_1 efl_ui_collection_view_example_1.c `pkg-config --cflags --libs efl-ui
+
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#else
+# define EFL_BETA_API_SUPPORT 1
+#endif
+
+#include <Efl_Ui.h>
+#include <Elementary.h>
+#include <Efl.h>
+#include <Eio.h>
+#include <stdio.h>
+
+#define NUM_ITEMS 400
+
+static Efl_Model*
+_make_model(Evas_Object *win)
+{
+   Eina_Value vtext;
+   Efl_Generic_Model *model, *child;
+   unsigned int i;
+   char buf[256];
+
+   model = efl_add(EFL_GENERIC_MODEL_CLASS, win);
+   eina_value_setup(&vtext, EINA_VALUE_TYPE_STRING);
+
+   for (i = 0; i < (NUM_ITEMS); i++)
+     {
+        child = efl_model_child_add(model);
+        snprintf(buf, sizeof(buf), "Item # %i", i);
+        eina_value_set(&vtext, buf);
+        efl_model_property_set(child, "title", &vtext);
+     }
+
+   eina_value_flush(&vtext);
+   return model;
+}
+
+static void
+_item_constructing(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   Efl_Gfx_Entity *item = ev->info;
+
+   if (!efl_ui_item_calc_locked_get(item))
+     efl_gfx_hint_size_min_set(item, EINA_SIZE2D(50, 50));
+}
+
+EAPI_MAIN void
+efl_main(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   Efl_Ui_Factory *factory;
+   Evas_Object *win, *li;
+   Eo *model;
+   Eo *position_manager;
+   Efl_App *app = ev->object;
+   Eina_Accessor *ac;
+   Eina_Bool list = EINA_TRUE, multi = EINA_FALSE, none = EINA_FALSE;
+   Efl_Ui_Select_Mode mode = EFL_UI_SELECT_MODE_SINGLE;
+   const char *arg;
+   unsigned int i;
+
+   ac = efl_core_command_line_command_access(app);
+   EINA_ACCESSOR_FOREACH(ac, i, arg)
+     {
+        if (eina_streq(arg, "grid")) list = EINA_FALSE;
+        if (eina_streq(arg, "multi")) multi = EINA_TRUE;
+        if (eina_streq(arg, "none")) none = EINA_TRUE;
+     }
+   eina_accessor_free(ac);
+
+   if (multi) mode = EFL_UI_SELECT_MODE_MULTI;
+   if (none) mode = EFL_UI_SELECT_MODE_NONE;
+
+   win = efl_add(EFL_UI_WIN_CLASS, app,
+                 efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC),
+                 efl_ui_win_autohide_set(efl_added, EINA_TRUE));
+   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_HIDDEN);
+   model = _make_model(win);
+
+   factory = efl_add(EFL_UI_LAYOUT_FACTORY_CLASS, win);
+   efl_ui_property_bind(factory, "text", "title");
+
+   if (list)
+     {
+        position_manager = efl_new(EFL_UI_POSITION_MANAGER_LIST_CLASS);
+        efl_ui_widget_factory_item_class_set(factory, EFL_UI_LIST_DEFAULT_ITEM_CLASS);
+     }
+   else
+     {
+        position_manager = efl_new(EFL_UI_POSITION_MANAGER_GRID_CLASS);
+        efl_ui_widget_factory_item_class_set(factory, EFL_UI_GRID_DEFAULT_ITEM_CLASS);
+        efl_event_callback_add(factory, EFL_UI_FACTORY_EVENT_ITEM_CONSTRUCTING, _item_constructing, NULL);
+     }
+
+   li = efl_add(EFL_UI_COLLECTION_VIEW_CLASS, win,
+                efl_ui_collection_view_position_manager_set(efl_added, position_manager),
+                efl_ui_view_model_set(efl_added, model),
+                efl_ui_multi_selectable_async_select_mode_set(efl_added, mode),
+                efl_ui_collection_view_factory_set(efl_added, factory));
+
+   efl_content_set(win, li);
+
+   //showall
+   efl_gfx_entity_size_set(win, EINA_SIZE2D(320, 320));
+}
+EFL_MAIN()
index 53d1213..5348929 100644 (file)
@@ -114,6 +114,7 @@ examples = [
   'efl_ui_list_view_example_1',
   'efl_ui_list_view_example_2',
   'efl_ui_list_view_example_3',
+  'efl_ui_collection_view_example_1',
   'efl_canvas_layout_text',
   'efl_ui_theme_example_01',
   'efl_ui_theme_example_02',
index 07affe3..4b82c0f 100644 (file)
@@ -335,7 +335,7 @@ typedef Eo Efl_Ui_Spotlight_Indicator;
 # include <efl_ui_list_view.eo.h>
 # include <efl_ui_list_view_model.eo.h>
 # include <efl_ui_view_model.eo.h>
-
+# include <efl_ui_collection_view.eo.h>
 
 # include <efl_ui_scroller.eo.h>
 # include <efl_ui_pan.eo.h>
index 9ef6a99..6531725 100644 (file)
@@ -1,6 +1,6 @@
 interface @beta Efl.Ui.Collection_Events
 {
-   [[Shared sets of events between @Efl.Ui.Collection and Efl.Ui.Collection_View.]]
+   [[Shared sets of events between @Efl.Ui.Collection and @Efl.Ui.Collection_View.]]
    event_prefix: efl_ui;
    events {
       item,pressed : Efl.Ui.Item; [[A $press event occurred over an item.]]
diff --git a/src/lib/elementary/efl_ui_collection_view.c b/src/lib/elementary/efl_ui_collection_view.c
new file mode 100644 (file)
index 0000000..d597c46
--- /dev/null
@@ -0,0 +1,2301 @@
+// Note: @1.23 Initial release has infrastructure to support more mode than homogeneous, but isn't exposed in the API nor supported.
+
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#define ELM_LAYOUT_PROTECTED
+#define EFL_UI_SCROLL_MANAGER_PROTECTED
+#define EFL_UI_SCROLLBAR_PROTECTED
+#define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
+
+#include <Efl_Ui.h>
+#include <Elementary.h>
+#include "elm_widget.h"
+#include "elm_priv.h"
+
+#include "efl_ui_collection_view_focus_manager.eo.h"
+
+#ifndef VIEWPORT_ENABLE
+# undef VIEWPORT_ENABLE
+#endif
+
+typedef struct _Efl_Ui_Collection_View_Data Efl_Ui_Collection_View_Data;
+typedef struct _Efl_Ui_Collection_Viewport Efl_Ui_Collection_Viewport;
+typedef struct _Efl_Ui_Collection_View_Focus_Manager_Data Efl_Ui_Collection_View_Focus_Manager_Data;
+typedef struct _Efl_Ui_Collection_Item Efl_Ui_Collection_Item;
+typedef struct _Efl_Ui_Collection_Item_Lookup Efl_Ui_Collection_Item_Lookup;
+typedef struct _Efl_Ui_Collection_Request Efl_Ui_Collection_Request;
+
+struct _Efl_Ui_Collection_Item
+{
+   Efl_Gfx_Entity *entity;
+   Efl_Model *model;
+};
+
+struct _Efl_Ui_Collection_Item_Lookup
+{
+   EINA_RBTREE;
+
+   uint64_t index;
+   Efl_Ui_Collection_Item item;
+};
+
+struct _Efl_Ui_Collection_Viewport
+{
+   Efl_Ui_Collection_Item *items;
+
+   uint64_t offset;
+   uint16_t count;
+};
+
+struct _Efl_Ui_Collection_Request
+{
+   Eina_Future *f;
+
+   uint64_t offset;
+   uint64_t length;
+
+   Eina_Bool model_requested : 1;
+   Eina_Bool model_fetched : 1;
+   Eina_Bool need_entity : 1;
+   Eina_Bool entity_requested : 1;
+};
+
+struct _Efl_Ui_Collection_View_Data
+{
+   Efl_Ui_Factory *factory;
+   Efl_Ui_Position_Manager_Entity *manager;
+   Efl_Ui_Scroll_Manager *scroller;
+   Efl_Ui_Pan *pan;
+   Efl_Gfx_Entity *sizer;
+   Efl_Model *model;
+   Efl_Model *multi_selectable_async_model;
+
+#ifdef VIEWPORT_ENABLE
+   Efl_Ui_Collection_Viewport *viewport[3];
+#endif
+   Eina_Rbtree *cache;
+
+   Eina_List *requests; // Array of Efl_Ui_Collection_Request in progress
+
+   uint64_t start_id;
+   uint64_t end_id;
+
+   Eina_Size2D content_min_size;
+
+   Efl_Ui_Layout_Orientation direction;
+   Efl_Ui_Select_Mode mode;
+
+   struct {
+      Eina_Bool w : 1;
+      Eina_Bool h : 1;
+   } match_content;
+
+   Efl_Ui_Position_Manager_Request_Range current_range;
+};
+
+struct _Efl_Ui_Collection_View_Focus_Manager_Data
+{
+   Efl_Ui_Collection_View *collection;
+};
+
+static const char *COLLECTION_VIEW_MANAGED = "_collection_view.managed";
+static const char *COLLECTION_VIEW_MANAGED_YES = "yes";
+
+#define MY_CLASS EFL_UI_COLLECTION_VIEW_CLASS
+
+#define MY_DATA_GET(obj, pd)                                            \
+  Efl_Ui_Collection_View_Data *pd = efl_data_scope_get(obj, MY_CLASS);
+
+static void _entity_request(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_Request *request);
+static void _idle_cb(void *data, const Efl_Event *event);
+
+static int
+_cache_tree_lookup(const Eina_Rbtree *node, const void *key,
+                   int length EINA_UNUSED, void *data EINA_UNUSED)
+{
+   const Efl_Ui_Collection_Item_Lookup *n = (Efl_Ui_Collection_Item_Lookup *)node;
+   const uint64_t *index = key;
+
+   if (n->index > *index)
+     return 1;
+   if (n->index < *index)
+     return -1;
+   return 0;
+}
+
+static Eina_Rbtree_Direction
+_cache_tree_cmp(const Eina_Rbtree *left, const Eina_Rbtree *right, void *data EINA_UNUSED)
+{
+   Efl_Ui_Collection_Item_Lookup *l = (Efl_Ui_Collection_Item_Lookup *)left;
+   Efl_Ui_Collection_Item_Lookup *r = (Efl_Ui_Collection_Item_Lookup *)right;
+
+   return l->index < r->index ? EINA_RBTREE_LEFT : EINA_RBTREE_RIGHT;
+}
+
+static Eina_Value
+_undo_item_selected_then(Eo *item, void *data EINA_UNUSED, Eina_Error err)
+{
+   Eina_Value *get;
+   Eina_Bool item_selected = efl_ui_selectable_selected_get(item);
+   Eina_Bool model_selected = EINA_FALSE;
+
+   get = efl_model_property_get(efl_ui_view_model_get(item), "self.selected");
+   eina_value_bool_get(get, &model_selected);
+   eina_value_free(get);
+
+   if ((!!model_selected) != (!!item_selected))
+     efl_ui_selectable_selected_set(item, model_selected);
+
+   return eina_value_error_init(err);
+}
+
+static void
+_selected_item_cb(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   // Link back property to model, maybe just trigger event on the item should be enough
+   Eina_Value *get;
+   Eina_Bool item_selected = efl_ui_selectable_selected_get(ev->object);
+   Eina_Bool model_selected = EINA_FALSE;
+   Eina_Value set = eina_value_bool_init(!!item_selected);
+
+   get = efl_model_property_get(efl_ui_view_model_get(ev->object), "self.selected");
+   eina_value_bool_get(get, &model_selected);
+   eina_value_free(get);
+
+   if ((!!model_selected) != (!!item_selected))
+     {
+        Eina_Future *f;
+
+        f = efl_model_property_set(efl_ui_view_model_get(ev->object), "self.selected", &set);
+
+        // In case the mode is preventing the change, we need to update the UI back. So handle error case
+        efl_future_then(ev->object, f,
+                        .error = _undo_item_selected_then);
+     }
+
+   eina_value_flush(&set);
+}
+
+static void
+_redirect_item_cb(void *data, const Efl_Event *ev)
+{
+   Eo *obj = data;
+
+#define REDIRECT_EVT(item_evt, item)                                    \
+   if (item_evt == ev->desc) efl_event_callback_call(obj, item, ev->object);
+   REDIRECT_EVT(EFL_INPUT_EVENT_PRESSED, EFL_UI_EVENT_ITEM_PRESSED);
+   REDIRECT_EVT(EFL_INPUT_EVENT_UNPRESSED, EFL_UI_EVENT_ITEM_UNPRESSED);
+   REDIRECT_EVT(EFL_INPUT_EVENT_LONGPRESSED, EFL_UI_EVENT_ITEM_LONGPRESSED);
+   REDIRECT_EVT(EFL_INPUT_EVENT_CLICKED_ANY, EFL_UI_EVENT_ITEM_CLICKED_ANY);
+   REDIRECT_EVT(EFL_INPUT_EVENT_CLICKED, EFL_UI_EVENT_ITEM_CLICKED);
+#undef REDIRECT_EVT
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(active_item_cbs,
+  { EFL_UI_EVENT_SELECTED_CHANGED, _selected_item_cb },
+  { EFL_INPUT_EVENT_PRESSED, _redirect_item_cb },
+  { EFL_INPUT_EVENT_UNPRESSED, _redirect_item_cb },
+  { EFL_INPUT_EVENT_LONGPRESSED, _redirect_item_cb },
+  { EFL_INPUT_EVENT_CLICKED, _redirect_item_cb },
+  { EFL_INPUT_EVENT_CLICKED_ANY, _redirect_item_cb });
+
+static void
+_entity_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Factory *factory,
+                Efl_Ui_Collection_Item *item, Eina_Array *scheduled_release)
+{
+   Efl_Gfx_Entity *entities[1];
+
+   entities[0] = item->entity;
+   if (!entities[0]) return ;
+
+   efl_event_callback_array_del(entities[0], active_item_cbs(), obj);
+   efl_replace(&item->entity, NULL);
+   efl_event_callback_call(obj, EFL_UI_COLLECTION_VIEW_EVENT_ITEM_UNREALIZED, entities[0]);
+   if (!scheduled_release)
+     {
+        efl_ui_factory_release(factory, EINA_C_ARRAY_ITERATOR_NEW(entities));
+     }
+   else
+     {
+        eina_array_push(scheduled_release, entities[0]);
+     }
+}
+
+static void
+_item_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Factory *factory,
+              Efl_Ui_Collection_Item *item, Eina_Array *scheduled_release)
+{
+   efl_replace(&item->model, NULL);
+
+   _entity_cleanup(obj, factory, item, scheduled_release);
+}
+
+static void
+_cache_item_free(Eina_Rbtree *node, void *data)
+{
+   Efl_Ui_Collection_Item_Lookup *n = (void*) node;
+   MY_DATA_GET(data, pd);
+
+   _item_cleanup(data, pd->factory, &n->item, NULL);
+   free(n);
+}
+
+static void
+_cache_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd)
+{
+   eina_rbtree_delete(pd->cache, _cache_item_free, obj);
+   pd->cache = NULL;
+}
+
+static void
+_all_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd)
+{
+   Efl_Ui_Collection_Request *request;
+   Eina_List *l, *ll;
+#ifdef VIEWPORT_ENABLE
+   unsigned int i;
+#endif
+
+   _cache_cleanup(obj, pd);
+#ifdef VIEWPORT_ENABLE
+   for (i = 0; i < 3; i++)
+     {
+        unsigned int j;
+
+        if (!pd->viewport[i]) continue;
+
+        for (j = 0; j < pd->viewport[i]->count; j++)
+          _item_cleanup(obj, pd->factory, &(pd->viewport[i]->items[j]));
+     }
+#endif
+
+   EINA_LIST_FOREACH_SAFE(pd->requests, l, ll, request)
+     eina_future_cancel(request->f);
+}
+
+static inline Eina_Bool
+_size_from_model(Efl_Model *model, Eina_Size2D *r, const char *width, const char *height)
+{
+   Eina_Value *vw, *vh;
+   Eina_Bool success = EINA_FALSE;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(model, EINA_FALSE);
+
+   vw = efl_model_property_get(model, width);
+   vh = efl_model_property_get(model, height);
+
+   if (eina_value_type_get(vw) == EINA_VALUE_TYPE_ERROR ||
+       eina_value_type_get(vh) == EINA_VALUE_TYPE_ERROR)
+     goto on_error;
+
+   if (!eina_value_int_convert(vw, &(r->w))) r->w = 0;
+   if (!eina_value_int_convert(vh, &(r->h))) r->h = 0;
+
+   success = EINA_TRUE;
+
+ on_error:
+   eina_value_free(vw);
+   eina_value_free(vh);
+
+   return success;
+}
+
+static inline void
+_size_to_model(Efl_Model *model, Eina_Size2D state)
+{
+   Eina_Value vw, vh;
+
+   vw = eina_value_int_init(state.w);
+   vh = eina_value_int_init(state.h);
+
+   efl_model_property_set(model, "self.width", &vw);
+   efl_model_property_set(model, "self.height", &vh);
+
+   eina_value_flush(&vw);
+   eina_value_flush(&vh);
+}
+
+#define ITEM_BASE_SIZE_FROM_MODEL(Model, Size) _size_from_model(Model, &Size, "item.width", "item.height")
+#define ITEM_SIZE_FROM_MODEL(Model, Size) _size_from_model(Model, &Size, "self.width", "self.height")
+
+static Eina_List *
+_request_add(Eina_List *requests, Efl_Ui_Collection_Request **request,
+             uint64_t index, Eina_Bool need_entity)
+{
+   if (!(*request)) goto create;
+
+   if ((*request)->offset + (*request)->length == index)
+     {
+        if (need_entity) (*request)->need_entity = EINA_TRUE;
+        (*request)->length += 1;
+        return requests;
+     }
+
+   requests = eina_list_append(requests, *request);
+
+ create:
+   *request = calloc(1, sizeof (Efl_Ui_Collection_Request));
+   if (!(*request)) return requests;
+   (*request)->offset = index;
+   (*request)->length = 1;
+   // At this point, we rely on the model caching ability to avoid recreating model
+   (*request)->model_requested = EINA_TRUE;
+   (*request)->need_entity = !!need_entity;
+
+   return requests;
+}
+
+static Eina_Value
+_model_fetched_cb(Eo *obj, void *data, const Eina_Value v)
+{
+   MY_DATA_GET(obj, pd);
+   Efl_Ui_Collection_Request *request = data;
+   Efl_Model *child;
+   unsigned int i, len;
+   Eina_Bool request_entity = EINA_FALSE;
+
+   request->model_fetched = EINA_TRUE;
+   EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
+     {
+        Efl_Ui_Collection_Item_Lookup *insert;
+        Eina_Size2D item_size;
+#ifdef VIEWPORT_ENABLE
+        unsigned int v;
+
+        for (v = 0; v < 3; ++v)
+          {
+             if (!pd->viewport[v]) continue;
+
+             if ((pd->viewport[v]->offset <= request->offset + i) &&
+                 (request->offset + i < pd->viewport[v]->offset + pd->viewport[v]->count))
+               {
+                  uint64_t index = request->offset + i - pd->viewport[v]->offset;
+
+                  efl_replace(&pd->viewport[v]->items[index].model, child);
+                  child = NULL;
+                  break;
+               }
+          }
+#endif
+
+        // When requesting a model, it should not be in the cache prior to the request
+        if (!child) continue;
+
+        uint64_t search_index = request->offset + i;
+
+        insert = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
+                                                   sizeof (search_index), _cache_tree_lookup,
+                                                   NULL);
+        if (insert)
+          {
+             if (!insert->item.entity && request->need_entity)
+               {
+                  //drop the old model here, overwrite with model + view
+                  efl_replace(&insert->item.model, child);
+               }
+             else
+               ERR("Inserting a model that was already fetched, dropping new model %lu", search_index);
+          }
+        else
+          {
+             insert = calloc(1, sizeof (Efl_Ui_Collection_Item_Lookup));
+             if (!insert) continue;
+             insert->index = request->offset + i;
+             insert->item.model = efl_ref(child);
+             pd->cache = eina_rbtree_inline_insert(pd->cache, EINA_RBTREE_GET(insert), _cache_tree_cmp, NULL);
+          }
+
+        if (!ITEM_SIZE_FROM_MODEL(insert->item.model, item_size))
+          request_entity = EINA_TRUE;
+     }
+
+   if (request_entity)
+     {
+        request->need_entity = EINA_TRUE;
+        _entity_request(obj, request);
+     }
+
+   return v;
+}
+
+static void
+_model_free_cb(Eo *o, void *data, const Eina_Future *dead_future EINA_UNUSED)
+{
+   MY_DATA_GET(o, pd);
+   Efl_Ui_Collection_Request *request = data;
+
+   if (request->need_entity)
+     {
+        if (!request->entity_requested)
+          _entity_request(o, request);
+     }
+   else
+     {
+        pd->requests = eina_list_remove(pd->requests, request);
+        free(request);
+     }
+}
+
+static Eina_Value
+_entity_fetch_cb(Eo *obj, void *data EINA_UNUSED, const Eina_Value v)
+{
+   MY_DATA_GET(obj, pd);
+   Efl_Model *child;
+   Eina_Future *r;
+   Eina_Array tmp;
+   unsigned int i, len;
+
+   eina_array_step_set(&tmp, sizeof (Eina_Array), 4);
+
+   EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
+     {
+        eina_array_push(&tmp, child);
+     }
+
+   r = efl_ui_view_factory_create_with_event(pd->factory, eina_array_iterator_new(&tmp));
+
+   eina_array_flush(&tmp);
+
+   return eina_future_as_value(r);
+}
+
+static inline Eina_Bool
+_entity_propagate(Efl_Model *model, Efl_Gfx_Entity *entity)
+{
+   Eina_Size2D item_size;
+
+   if (efl_key_data_get(entity, "efl.ui.widget.factory.size_set"))
+     {
+        return EINA_FALSE;
+     }
+
+   if (ITEM_SIZE_FROM_MODEL(model, item_size))
+     {
+        efl_gfx_hint_size_min_set(entity, item_size);
+        efl_canvas_group_need_recalculate_set(entity, EINA_FALSE);
+        if (efl_isa(entity, EFL_UI_ITEM_CLASS)) efl_ui_item_calc_locked_set(entity, EINA_TRUE);
+        return EINA_FALSE;
+     }
+
+   efl_canvas_group_calculate(entity);
+   item_size = efl_gfx_hint_size_combined_min_get(entity);
+   efl_canvas_group_need_recalculate_set(entity, EINA_FALSE);
+
+   _size_to_model(model, item_size);
+   return EINA_TRUE;
+}
+
+static Eina_Value
+_entity_fetched_cb(Eo *obj, void *data, const Eina_Value v)
+{
+   MY_DATA_GET(obj, pd);
+   Efl_Ui_Collection_Request *request = data;
+   Efl_Gfx_Entity *child;
+   unsigned int i, len;
+   uint64_t updated_size_start_id, updated_entity_start_id;
+   Eina_Bool updated_size = EINA_FALSE, updated_entity = EINA_FALSE;
+
+   EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
+     {
+        Efl_Ui_Collection_Item_Lookup *lookup;
+        uint64_t search_index;
+        //unsigned int v;
+
+        efl_key_data_set(child, COLLECTION_VIEW_MANAGED, COLLECTION_VIEW_MANAGED_YES);
+        /* fix eventing in scroller by ensuring collection items are in the scroller hierarchy */
+        efl_ui_item_container_set(child, obj);
+        efl_ui_widget_sub_object_add(obj, child);
+        efl_canvas_group_member_add(pd->pan, child);
+        efl_ui_widget_focus_allow_set(child, EINA_FALSE);
+        efl_gfx_entity_visible_set(child, EINA_FALSE);
+
+#ifdef VIEWPORT_ENABLE
+        for (v = 0; v < 3; ++v)
+          {
+             if (!pd->viewport[v]) continue;
+
+             if ((pd->viewport[v]->offset <= request->offset + i) &&
+                 (request->offset + i < pd->viewport[v]->offset + pd->viewport[v]->count))
+               {
+                  uint64_t index = request->offset + i - pd->viewport[v]->offset;
+
+                  if (pd->viewport[v]->items[index].entity)
+                    {
+                       ERR("Entity already existing for id %d", i);
+                       efl_unref(pd->viewport[v]->items[index].entity);
+                       efl_del(pd->viewport[v]->items[index].entity);
+                       pd->viewport[v]->items[index].entity = NULL;
+                    }
+
+                  efl_replace(&pd->viewport[v]->items[index].entity, child);
+                  if (_entity_propagate(pd->viewport[v]->items[index].model, child))
+                    {
+                       if (!updated_size)
+                         {
+                            updated_size = EINA_TRUE;
+                            updated_size_start_id = index;
+                         }
+                    }
+                  else
+                    {
+                       if (updated_size)
+                         {
+                            efl_ui_position_manager_entity_item_size_changed(pd->manager,
+                                                                             updated_size_start_id,
+                                                                             index - 1);
+                            updated_size = EINA_FALSE;
+                         }
+                    }
+                  child = NULL;
+                  break;
+               }
+          }
+#endif
+
+        // When requesting an entity, the model should already be in the cache
+        if (!child) continue;
+
+        search_index = request->offset + i;
+
+        lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
+                                                   sizeof (search_index), _cache_tree_lookup,
+                                                   NULL);
+
+        if (!lookup)
+          {
+             Efl_Gfx_Entity *entities[1] = { child };
+             efl_ui_factory_release(pd->factory, EINA_C_ARRAY_ITERATOR_NEW(entities));
+             continue;
+          }
+        if (lookup->item.entity)
+          {
+             ERR("Entity already existing for id %lu", search_index);
+             _entity_cleanup(obj, pd->factory, &lookup->item, NULL);
+          }
+
+        lookup->item.entity = efl_ref(child);
+        efl_event_callback_array_add(child, active_item_cbs(), obj);
+        efl_event_callback_call(obj, EFL_UI_COLLECTION_VIEW_EVENT_ITEM_REALIZED, child);
+
+        if (!updated_entity)
+          {
+             updated_entity = EINA_TRUE;
+             updated_entity_start_id = search_index;
+          }
+
+        if (_entity_propagate(lookup->item.model, child))
+          {
+             if (!updated_size)
+               {
+                  updated_size = EINA_TRUE;
+                  updated_size_start_id = search_index;
+               }
+          }
+        else
+          {
+             if (updated_size)
+               {
+                  efl_ui_position_manager_entity_item_size_changed(pd->manager,
+                                                                   updated_size_start_id,
+                                                                   search_index - 1);
+                  updated_size = EINA_FALSE;
+               }
+          }}
+   ;
+
+   // Currently position manager will flush its entire size cache on update, so only do
+   // it when necessary to improve performance.
+   if (updated_size)
+     {
+        efl_ui_position_manager_entity_item_size_changed(pd->manager,
+                                                         updated_size_start_id,
+                                                         i - 1);
+        updated_size = EINA_FALSE;
+     }
+
+   // Notify the position manager that new entity are ready to display
+   if (updated_entity)
+     {
+        efl_ui_position_manager_entity_entities_ready(pd->manager,
+                                                      updated_entity_start_id,
+                                                      i - 1);
+
+        efl_event_callback_del(efl_main_loop_get(), EFL_LOOP_EVENT_IDLE, _idle_cb, obj);
+        efl_event_callback_add(efl_main_loop_get(), EFL_LOOP_EVENT_IDLE, _idle_cb, obj);
+     }
+   return v;
+}
+
+static void
+_entity_free_cb(Eo *o, void *data, const Eina_Future *dead_future EINA_UNUSED)
+{
+   MY_DATA_GET(o, pd);
+   Efl_Ui_Collection_Request *request = data;
+
+   pd->requests = eina_list_remove(pd->requests, request);
+   free(request);
+}
+
+static Eina_List *
+_cache_size_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
+                  Efl_Ui_Collection_View_Data *pd,
+                  uint64_t search_index,
+                  Efl_Ui_Position_Manager_Size_Batch_Entity *target,
+                  Eina_Size2D item_base)
+{
+   Efl_Ui_Collection_Item_Lookup *lookup;
+   Efl_Model *model;
+   Eina_Size2D item_size = item_base;
+
+   if (!pd->cache) goto not_found;
+
+   lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
+                                              sizeof (search_index), _cache_tree_lookup,
+                                              NULL);
+   if (!lookup) goto not_found;
+
+   // In the cache we should always have model, so no need to check for it
+   model = lookup->item.model;
+
+   // If we do not know the size
+   if (!ITEM_SIZE_FROM_MODEL(model, item_size))
+     {
+        if (lookup->item.entity)
+          {
+             ERR("Got a model '%s' and an item '%s', but no size. Recalculating.",
+                 efl_debug_name_get(model), efl_debug_name_get(lookup->item.entity));
+             _entity_propagate(model, lookup->item.entity);
+             if (!ITEM_SIZE_FROM_MODEL(model, item_size))
+               {
+                  CRI("No size for itme '%s' after recalculating. This is bad.",
+                      efl_debug_name_get(lookup->item.entity));
+               }
+          }
+        else if (!ITEM_BASE_SIZE_FROM_MODEL(pd->model, item_size))
+          {
+             INF("No base size yet available. Making things up.");
+             item_size.w = 1;
+             item_size.h = 1;
+          }
+     }
+
+   target->size = item_size;
+   target->element_depth = 0;
+   target->depth_leader = EINA_FALSE;
+   return requests;
+
+ not_found:
+   requests = _request_add(requests, request, search_index, EINA_FALSE);
+
+   target->size = item_size;
+   target->element_depth = 0;
+   target->depth_leader = EINA_FALSE;
+   return requests;
+}
+
+static Eina_List *
+_cache_entity_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
+                    Efl_Ui_Collection_View_Data *pd,
+                    uint64_t search_index,
+                    Efl_Ui_Position_Manager_Object_Batch_Entity *target)
+{
+   Efl_Ui_Collection_Item_Lookup *lookup;
+
+   if (!pd->cache) goto not_found;
+
+   lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
+                                              sizeof (search_index), _cache_tree_lookup,
+                                              NULL);
+   if (!lookup) goto not_found;
+   if (!lookup->item.entity) goto not_found;
+
+   if (target) target->entity = lookup->item.entity;
+   goto finish;
+
+ not_found:
+   requests = _request_add(requests, request, search_index, EINA_TRUE);
+
+   if (target) target->entity = NULL;
+ finish:
+   if (!target) return requests;
+
+   target->element_depth = 0;
+   target->depth_leader = EINA_FALSE;
+
+   return requests;
+}
+
+static void
+_entity_request(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_Request *request)
+{
+   if (request->model_requested && (!request->model_fetched)) return;
+   request->f = efl_future_then(obj, request->f,
+                                .success_type = EINA_VALUE_TYPE_ARRAY,
+                                .success = _entity_fetch_cb);
+   request->f = efl_future_then(obj, request->f,
+                                .success_type = EINA_VALUE_TYPE_ARRAY,
+                                .success = _entity_fetched_cb,
+                                .data = request,
+                                .free = _entity_free_cb);
+   request->entity_requested = EINA_TRUE;
+}
+
+static inline void
+_entity_inflight_request(Efl_Ui_Collection_View *obj,
+                         Efl_Ui_Collection_Request *request,
+                         Efl_Ui_Collection_Request *inflight)
+{
+   if (request->need_entity == EINA_FALSE) return ;
+   if (inflight->entity_requested) return ;
+
+   _entity_request(obj, inflight);
+}
+
+static Eina_List *
+_batch_request_flush(Eina_List *requests,
+                     Efl_Ui_Collection_View *obj,
+                     Efl_Ui_Collection_View_Data *pd)
+{
+   Efl_Ui_Collection_Request *request;
+   Eina_List *ll, *next_list_item;
+
+   EINA_LIST_FOREACH_SAFE(requests, ll, next_list_item, request)
+     {
+        // Check request intersection with all pending request
+        Efl_Ui_Collection_Request *inflight;
+        Efl_Model *model;
+        Eina_List *l;
+
+        EINA_LIST_FOREACH(pd->requests, l, inflight)
+          {
+             uint64_t istart = inflight->offset;
+             uint64_t iend = inflight->offset + inflight->length;
+             uint64_t rstart = request->offset;
+             uint64_t rend = request->offset + request->length;
+
+             // Way before
+             if (rend < istart) continue;
+             // Way after
+             if (rstart >= iend) continue;
+
+             // request included in current inflight request
+             if (rstart >= istart && rend <= iend)
+               {
+                  _entity_inflight_request(obj, request, inflight);
+
+                  // In this case no need to start a request
+                  requests = eina_list_remove_list(requests, ll);
+                  free(request);
+                  request = NULL;
+                  break;
+               }
+
+             // request overflow left and right
+             if (rstart < istart && iend < rend)
+               {
+                  // Remove the center portion of the request by emitting a new one
+                  Efl_Ui_Collection_Request *rn;
+
+                  rn = calloc(1, sizeof (Efl_Ui_Collection_Request));
+                  if (!rn) break;
+
+                  rn->offset = iend;
+                  rn->length = rend - iend;
+                  rn->model_requested = request->model_requested;
+                  rn->need_entity = request->need_entity;
+
+                  requests = eina_list_append(requests, rn);
+
+                  request->length = istart - rstart;
+                  _entity_inflight_request(obj, request, inflight);
+                  continue;
+               }
+
+             // request overflow left
+             if (rstart < istart && rend > istart && rend <= iend)
+               {
+                  request->length = istart - rstart;
+                  _entity_inflight_request(obj, request, inflight);
+                  continue;
+               }
+
+             // request overflow right
+             if (rstart >= istart && rstart < iend && iend <= rend)
+               {
+                  request->offset = iend;
+                  request->length = rend - iend;
+                  _entity_inflight_request(obj, request, inflight);
+                  continue;
+               }
+          }
+
+        if (!request) continue;
+
+        model = pd->model;
+        // Are we ready yet
+        if (!model)
+          {
+             requests = eina_list_remove_list(requests, ll);
+             free(request);
+             continue;
+          }
+        // Is the request inside the limit of the model?
+        if (request->offset >= efl_model_children_count_get(model))
+          {
+             requests = eina_list_remove_list(requests, ll);
+             free(request);
+             continue;
+          }
+        // Is its limit outside the model limit?
+        if (request->offset + request->length >= efl_model_children_count_get(model))
+          {
+             request->length = efl_model_children_count_get(model) - request->offset;
+          }
+
+        // We now have a request, time to trigger a fetch
+        // We assume here that we are always fetching the model (model_requested must be true)
+        if (!request->model_requested)
+          {
+             CRI("Someone forgot to set model_requested for %lu to %lu.",
+                 request->offset, request->offset + request->length);
+             request->model_requested = EINA_TRUE;
+          }
+        request->f = efl_model_children_slice_get(model, request->offset, request->length);
+        request->f = efl_future_then(obj, request->f,
+                                     .success = _model_fetched_cb,
+                                     .data = request,
+                                     .free = _model_free_cb);
+
+        eina_list_move_list(&pd->requests, &requests, ll);
+     }
+   return NULL;
+}
+
+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)
+{
+   MY_DATA_GET(data, pd);
+   Efl_Ui_Position_Manager_Size_Batch_Entity *sizes;
+   Efl_Ui_Collection_Request *request = NULL;
+   Efl_Ui_Position_Manager_Size_Batch_Result result = {0};
+   Efl_Model *parent;
+   Eina_List *requests = NULL;
+   Eina_Size2D item_base = {0};
+   unsigned int limit;
+   unsigned int idx = 0;
+
+   // get the approximate value from the tree node
+   parent = pd->model;
+
+   sizes = memory.mem;
+   //count = efl_model_children_count_get(parent);
+   limit = conf.range.end_id - conf.range.start_id;
+   ITEM_BASE_SIZE_FROM_MODEL(parent, item_base);
+
+   // Look in the temporary cache now for the beginning of the buffer
+#ifdef VIEWPORT_ENABLE
+   if (pd->viewport[0] && ((uint64_t)(conf.range.start_id + idx) < pd->viewport[0]->offset))
+     {
+        while ((uint64_t)(conf.range.start_id + idx) < pd->viewport[0]->offset && idx < limit)
+          {
+             uint64_t search_index = conf.range.start_id + idx;
+             requests = _cache_size_fetch(requests, &request, pd,
+                                          search_index, &sizes[idx], item_base);
+             idx++;
+          }
+     }
+
+   // Then look in our buffer view if the needed information can be found there
+   for (i = 0; i < 3; ++i)
+     {
+        if (!pd->viewport[i]) continue;
+
+        while (idx < limit &&
+               (pd->viewport[i]->offset <= conf.range.start_id + idx) &&
+               (conf.range.start_id + idx < (pd->viewport[i]->offset + pd->viewport[i]->count)))
+          {
+             uint64_t offset = conf.range.start_id + idx - pd->viewport[i]->offset;
+             Efl_Model *model = pd->viewport[i]->items[offset].model;
+             Efl_Gfx_Entity *entity = pd->viewport[i]->items[offset].entity;
+             Eina_Bool entity_request = EINA_FALSE;
+
+             if (model)
+               {
+                  Eina_Size2D item_size;
+                  Eina_Bool found = EINA_FALSE;
+
+                  if (ITEM_SIZE_FROM_MODEL(model, item_size))
+                    found = EINA_TRUE;
+                  if (!found && entity)
+                    {
+                       item_size = efl_gfx_hint_size_combined_min_get(entity);
+                       //if the size is 0 here, then we are running into trouble,
+                       //fetch size from the parent model, where some fallback is defined
+                       if (item_size.h == 0 && item_size.w == 0)
+                         {
+                            item_size = item_base;
+                            found = EINA_TRUE;
+                         }
+                       else
+                         {
+                            _size_to_model(model, item_size);
+                            found = EINA_TRUE;
+                         }
+
+                    }
+
+                  if (found)
+                    {
+                       sizes[idx].size = item_size;
+                       sizes[idx].element_depth = 0;
+                       sizes[idx].depth_leader = EINA_FALSE;
+                       goto done;
+                    }
+
+                  // We will need an entity to calculate this size
+                  entity_request = EINA_TRUE;
+               }
+             // No data, add to the requests
+             requests = _request_add(requests, &request, conf.range.start_id + idx, entity_request);
+
+             sizes[idx].size = item_base;
+             sizes[idx].element_depth = 0;
+             sizes[idx].depth_leader = EINA_FALSE;
+
+          done:
+             idx++;
+          }
+     }
+
+   // Look in the temporary cache now for the end of the buffer
+   while (idx < limit)
+     {
+        uint64_t search_index = conf.range.start_id + idx;
+        requests = _cache_size_fetch(requests, &request, pd,
+                                     search_index, &sizes[idx], item_base);
+        idx++;
+     }
+#endif
+
+   /* if (conf.cache_request) */
+   /*   { */
+   /*      printf("CACHING SIZE CALL\n"); */
+   /*      while (idx < limit) */
+   /*          { */
+   /*             sizes[idx].depth_leader = EINA_FALSE; */
+   /*             sizes[idx].element_depth = 0; */
+   /*             sizes[idx].size = pd->last_base; */
+   /*             idx++; */
+   /*        } */
+   /*      fprintf(stderr, "read with no fetch\n"); */
+   /*   } */
+   /* else */
+     {
+        while (idx < limit)
+          {
+             uint64_t search_index = conf.range.start_id + idx;
+             requests = _cache_size_fetch(requests, &request, pd,
+                                          search_index, &sizes[idx], item_base);
+             idx++;
+          }
+
+
+        // Done, but flush request first
+        if (request) requests = eina_list_append(requests, request);
+
+        requests = _batch_request_flush(requests, data, pd);
+     }
+
+   // Get the amount of filled item
+   result.filled_items = limit;
+
+   return result;
+}
+
+static Efl_Ui_Position_Manager_Object_Batch_Result
+_batch_entity_cb(void *data, Efl_Ui_Position_Manager_Request_Range range, Eina_Rw_Slice memory)
+{
+   MY_DATA_GET(data, pd);
+   Efl_Ui_Position_Manager_Object_Batch_Entity *entities;
+   Efl_Ui_Collection_Request *request = NULL;
+   Efl_Ui_Position_Manager_Object_Batch_Result result = {0};
+   Eina_List *requests = NULL;
+#ifdef VIEWPORT_ENABLE
+   Efl_Model *parent;
+#endif
+   unsigned int limit;
+   unsigned int idx = 0;
+
+   //parent = pd->model;
+
+   entities = memory.mem;
+   //count = efl_model_children_count_get(parent);
+   limit = range.end_id - range.start_id;;
+
+   // Look in the temporary cache now for the beginning of the buffer
+#ifdef VIEWPORT_ENABLE
+   if (pd->viewport[0] && ((uint64_t)(range.start_id + idx) < pd->viewport[0]->offset))
+     {
+        while (idx < limit && (uint64_t)(range.start_id + idx) < pd->viewport[0]->offset)
+          {
+             uint64_t search_index = range.start_id + idx;
+
+             requests = _cache_entity_fetch(requests, &request, pd,
+                                            search_index, &entities[idx]);
+
+             idx++;
+          }
+     }
+
+   // Then look in our buffer view if the needed information can be found there
+   for (i = 0; i < 3; ++i)
+     {
+        if (!pd->viewport[i]) continue;
+
+        while (idx < limit &&
+               (pd->viewport[i]->offset <= range.start_id + idx) &&
+               (range.start_id + idx < (pd->viewport[i]->offset + pd->viewport[i]->count)))
+          {
+             uint64_t offset = range.start_id + idx - pd->viewport[i]->offset;
+             Efl_Gfx_Entity *entity = pd->viewport[i]->items[offset].entity;
+
+             if (!entity)
+               {
+                  // No data, add to the requests
+                  requests = _request_add(requests, &request, range.start_id + idx, EINA_TRUE);
+
+                  entities[idx].entity = NULL;
+                  entities[idx].depth_leader = EINA_FALSE;
+                  entities[idx].element_depth = 0;
+               }
+             else
+               {
+                  entities[idx].entity = entity;
+                  entities[idx].depth_leader = EINA_FALSE;
+                  entities[idx].element_depth = 0;
+               }
+
+             idx++;
+          }
+     }
+#endif
+
+   // Look in the temporary cache now for the end of the buffer
+   while (idx < limit)
+     {
+        uint64_t search_index = range.start_id + idx;
+
+        requests = _cache_entity_fetch(requests, &request, pd,
+                                       search_index, &entities[idx]);
+
+        idx++;
+     }
+   // Done, but flush request first
+   if (request)
+     {
+        requests = eina_list_append(requests, request);
+     }
+
+   requests = _batch_request_flush(requests, data, pd);
+
+   // Get the amount of filled item
+   result.filled_items = limit;
+
+   return result;
+}
+
+
+#if 0
+static void
+_batch_free_cb(void *data)
+{
+   efl_unref(data);
+}
+#endif
+
+static void
+flush_min_size(Eo *obj, Efl_Ui_Collection_View_Data *pd)
+{
+   Eina_Size2D tmp = pd->content_min_size;
+
+   if (!pd->match_content.w)
+     tmp.w = -1;
+
+   if (!pd->match_content.h)
+     tmp.h = -1;
+
+   efl_gfx_hint_size_restricted_min_set(obj, tmp);
+}
+
+static void
+_manager_content_size_changed_cb(void *data, const Efl_Event *ev)
+{
+   Eina_Size2D *size = ev->info;
+   MY_DATA_GET(data, pd);
+
+   efl_gfx_entity_size_set(pd->sizer, *size);
+}
+
+static void
+_manager_content_min_size_changed_cb(void *data, const Efl_Event *ev)
+{
+   Eina_Size2D *size = ev->info;
+   MY_DATA_GET(data, pd);
+
+   pd->content_min_size = *size;
+
+   flush_min_size(data, pd);
+}
+
+#ifdef VIEWPORT_ENABLE
+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)
+{
+   Efl_Ui_Collection_Request *current = NULL;
+   unsigned int j;
+
+   for (j = 0; j < viewport->count; j++)
+     {
+        Efl_Ui_Collection_Item_Lookup *lookup;
+        uint64_t index = viewport->offset + j;
+
+        if (viewport->items[j].model) goto check_entity;
+
+        lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &index,
+                                                   sizeof (index), _cache_tree_lookup,
+                                                   NULL);
+
+        if (lookup)
+          {
+             efl_replace(&viewport->items[j].model, lookup->item.model);
+             efl_replace(&viewport->items[j].entity, lookup->item.entity);
+             efl_replace(&lookup->item.entity, NULL); // Necessary to avoid premature release
+
+             pd->cache = eina_rbtree_inline_remove(pd->cache, EINA_RBTREE_GET(lookup),
+                                                   _cache_tree_cmp, NULL);
+             _cache_item_free(EINA_RBTREE_GET(lookup), obj);
+          }
+
+     check_entity:
+        if (viewport->items[j].entity) continue ;
+        requests = _request_add(requests, &current, index, EINA_TRUE);
+     }
+
+   // We do break request per viewport, just in case we generate to big batch at once
+   if (current) requests = eina_list_append(requests, current);
+
+   return requests;
+}
+
+#endif
+
+// An RbTree has the nice property of sorting content. The smaller than the root being in
+// son[1] and the greater than the root in son[0]. Using this we can efficiently walk the
+// tree once to take note of all the item that need cleaning.
+static void
+_mark_lesser(Efl_Ui_Collection_Item_Lookup *root, Eina_Array *mark, const unsigned int lower)
+{
+   if (!root) return ;
+
+   if (root->index < lower)
+     {
+        eina_array_push(mark, root);
+        _mark_lesser((void*) EINA_RBTREE_GET(root)->son[1], mark, lower);
+     }
+   else
+     {
+        _mark_lesser((void*) EINA_RBTREE_GET(root)->son[0], mark, lower);
+        _mark_lesser((void*) EINA_RBTREE_GET(root)->son[1], mark, lower);
+     }
+}
+
+static void
+_mark_ge(Efl_Ui_Collection_Item_Lookup *root, Eina_Array *mark, const unsigned int upper)
+{
+   if (!root) return ;
+
+   if (root->index >= upper)
+     {
+        eina_array_push(mark, root);
+        _mark_ge((void*) EINA_RBTREE_GET(root)->son[0], mark, upper);
+        _mark_ge((void*) EINA_RBTREE_GET(root)->son[1], mark, upper);
+     }
+   else
+     {
+        _mark_ge((void*) EINA_RBTREE_GET(root)->son[0], mark, upper);
+     }
+}
+
+// we walk the tree twice, once for everything below the limit and once for everything above
+// then we do free each item individually.
+static void
+_idle_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Efl_Ui_Collection_Item_Lookup *lookup;
+   Eina_Array mark;
+   Eina_Array scheduled_release;
+   MY_DATA_GET(data, pd);
+   const unsigned int length = pd->current_range.end_id - pd->current_range.start_id;
+   const unsigned int lower_end = MAX((long)pd->current_range.start_id - (long)length/2, 0);
+   const unsigned int upper_end = pd->current_range.end_id + length/2;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+
+   eina_array_step_set(&mark, sizeof (Eina_Array), 16);
+   eina_array_step_set(&scheduled_release, sizeof (Eina_Array), 16);
+
+   _mark_lesser((void*) pd->cache, &mark, lower_end);
+   _mark_ge((void*) pd->cache, &mark, upper_end);
+
+   EINA_ARRAY_ITER_NEXT(&mark, i, lookup, iterator)
+     {
+        pd->cache = (void*) eina_rbtree_inline_remove(pd->cache,
+                                                      EINA_RBTREE_GET(lookup),
+                                                      _cache_tree_cmp, NULL);
+        _item_cleanup(data, pd->factory, &lookup->item, &scheduled_release);
+        free(lookup);
+     }
+   eina_array_flush(&mark);
+
+   efl_ui_factory_release(pd->factory, eina_array_iterator_new(&scheduled_release));
+   eina_array_flush(&scheduled_release);
+
+   efl_event_callback_del(efl_main_loop_get(), EFL_LOOP_EVENT_IDLE, _idle_cb, data);
+}
+
+#ifndef VIEWPORT_ENABLE
+static void
+_manager_content_visible_range_changed_cb(void *data, const Efl_Event *ev)
+{
+   Efl_Ui_Position_Manager_Range_Update *event = ev->info;
+   unsigned int count;
+   unsigned int lower_end;
+   unsigned int upper_end;
+   long length;
+   Efl_Ui_Collection_Request *request = NULL;
+   Eina_List *requests = NULL;
+   unsigned int idx;
+   MY_DATA_GET(data, pd);
+
+   pd->current_range.start_id = event->start_id;
+   pd->current_range.end_id = event->end_id;
+
+   count = efl_model_children_count_get(efl_ui_view_model_get(data));
+
+   length = pd->current_range.end_id - pd->current_range.start_id;
+   lower_end = MAX((long)pd->current_range.start_id - (length / 2), 0);
+   upper_end = MIN(pd->current_range.end_id + (length / 2), count);
+
+   idx = lower_end;
+   while (idx < upper_end)
+     {
+        uint64_t search_index = idx;
+
+        requests = _cache_entity_fetch(requests, &request, pd,
+                                       search_index, NULL);
+
+        idx++;
+     }
+   // Done, but flush request first
+   if (request) requests = eina_list_append(requests, request);
+
+   requests = _batch_request_flush(requests, data, pd);
+}
+#endif
+
+#ifdef VIEWPORT_ENABLE
+static void
+_manager_content_visible_range_changed_cb(void *data, const Efl_Event *ev)
+{
+   Efl_Ui_Position_Manager_Range_Update *event = ev->info;
+   MY_DATA_GET(data, pd);
+   Eina_List *requests = NULL;
+   long baseid;
+   unsigned int delta, marginup, margindown;
+   uint64_t upperlimit_offset, lowerlimit_offset;
+   unsigned int i;
+
+   pd->start_id = event->start_id;
+   pd->end_id = event->end_id;
+
+   delta = pd->end_id - pd->start_id;
+
+   // First time setting up the viewport, so trigger request as we see fit
+   if (!pd->viewport[0])
+     {
+        baseid = pd->start_id - delta;
+
+        for (i = 0; i < 3; i++)
+          {
+             pd->viewport[i] = calloc(1, sizeof (Efl_Ui_Collection_Viewport));
+             if (!pd->viewport[i]) continue;
+
+             pd->viewport[i]->offset = MAX(baseid + delta * i, 0);
+             pd->viewport[i]->count = delta;
+             pd->viewport[i]->items = calloc(delta, sizeof (Efl_Ui_Collection_Item));
+             if (!pd->viewport[i]->items) continue ;
+
+             requests = _viewport_walk_fill(requests, data, pd, pd->viewport[i]);
+          }
+
+        goto flush_requests;
+     }
+
+   // Compute limit offset
+   upperlimit_offset = delta * 3 + pd->viewport[0]->offset;
+   lowerlimit_offset = 0;
+
+   // Adjust the viewport for size or to much offset change in two step
+
+   // Trying to resize first if there size is in bigger/smaller than 25% of the original size
+   margindown = delta * 75 / 100;
+   marginup = delta * 125 / 100;
+   if (margindown < pd->viewport[0]->count &&
+       pd->viewport[0]->count < marginup)
+     {
+        // Trying to do the resize in an optimized way is complex, let's do it simple
+        Efl_Ui_Collection_Item *items[3];
+        unsigned int j = 0, t = 1;
+
+        for (i = 0; i < 3; i++)
+          {
+             unsigned int m;
+
+             items[i] = calloc(delta, sizeof (Efl_Ui_Collection_Item));
+             if (!items[i]) continue;
+
+             for (m = 0; m < delta && t < 3; m++)
+               {
+                  items[i][m] = pd->viewport[t]->items[j];
+
+                  j++;
+                  if (j < pd->viewport[t]->count) continue;
+
+                  j = 0;
+                  t++;
+                  if (t == 3) break;
+               }
+
+             // Preserve last updated index to later build a request
+             if (t == 3)
+               {
+                  upperlimit_offset = pd->viewport[0]->offset + i * delta + m;
+
+                  t = 4; // So that we never come back here again
+               }
+          }
+
+        // For now destroy leftover object, could be cached
+        for (i = t; i < 3; i++)
+          {
+             for (; j < pd->viewport[i]->count; j++)
+               {
+                  _item_cleanup(pd->factory, &pd->viewport[i]->items[j]);
+               }
+             j = 0;
+          }
+
+        // And now define viewport back
+        for (i = 0; i < 3; i++)
+          {
+             free(pd->viewport[i]->items);
+             pd->viewport[i]->items = items[i];
+             pd->viewport[i]->count = delta;
+             pd->viewport[i]->offset = pd->viewport[0]->offset + delta * i;
+          }
+     }
+
+   // We decided that resizing was unecessary
+   delta = pd->viewport[0]->count;
+
+   // Try to keep the visual viewport in between half of the first and last viewport
+
+   // start_id is in the first half of the first viewport, assume upward move
+   // start_id + delta is in the second half of the last viewport, assume upward move
+   if (pd->viewport[0]->offset + delta / 2 < pd->start_id ||
+       pd->start_id + delta > pd->viewport[2]->offset + delta / 2)
+     {
+        // We could optimize this to actually just move viewport around in most cases
+        Efl_Ui_Collection_Item *items[3];
+        unsigned int j = 0, t = 0;
+        uint64_t target, current;
+
+        // Case where are at the top
+        if (pd->start_id < delta && pd->viewport[0]->offset == 0) goto build_request;
+
+        // Trying to adjust the offset to maintain it in the center viewport +/- delta/2
+        baseid = (pd->start_id < delta) ? 0 : pd->start_id - delta;
+
+        // Lookup for starting point
+        lowerlimit_offset = pd->viewport[0]->offset;
+        target = baseid;
+
+        // cleanup before target
+        for (current = pd->viewport[t]->offset; current < target; current++)
+          {
+             _item_cleanup(pd->factory, &pd->viewport[t]->items[j]);
+
+             j++;
+             if (j < pd->viewport[t]->count) continue;
+
+             j = 0;
+             t++;
+             if (t == 3) break;
+          }
+
+        // Allocation and copy
+        for (i = 0; i < 3; i++)
+          {
+             unsigned int m;
+
+             items[i] = calloc(delta, sizeof (Efl_Ui_Collection_Item));
+             if (!items[i]) continue;
+
+             for (m = 0; m < delta && t < 3; m++, target++)
+               {
+                  if (target < pd->viewport[t]->offset) continue ;
+                  items[i][m] = pd->viewport[t]->items[j];
+
+                  j++;
+                  if (j < pd->viewport[t]->count) continue;
+
+                  j = 0;
+                  t++;
+                  if (t == 3) break;
+               }
+
+             // Preserve last updated index to later build a request
+             if (t == 3)
+               {
+                  if (upperlimit_offset > pd->viewport[0]->offset + i * delta + m)
+                    {
+                       upperlimit_offset = pd->viewport[0]->offset + i * delta + m;
+                    }
+
+                  t = 4; // So that we never come back here again
+               }
+          }
+
+        // For now destroy leftover object, could be cached
+        for (i = t; i < 3; i++)
+          {
+             for (; j < pd->viewport[i]->count; j++)
+               {
+                  _item_cleanup(pd->factory, &pd->viewport[i]->items[j]);
+               }
+             j = 0;
+          }
+
+        // And now define viewport back
+        for (i = 0; i < 3; i++)
+          {
+             free(pd->viewport[i]->items);
+             pd->viewport[i]->items = items[i];
+             pd->viewport[i]->offset = baseid + delta * i;
+          }
+     }
+
+ build_request:
+   // Check if the first viewport has all the lower part of it filled with objects
+   if (pd->viewport[0]->offset < lowerlimit_offset)
+     {
+        Efl_Ui_Collection_Request *request;
+
+        request = calloc(1, sizeof (Efl_Ui_Collection_Request));
+        if (request) return ;
+
+        request->offset = lowerlimit_offset;
+        // This length work over multiple viewport as they are contiguous
+        request->length = lowerlimit_offset - pd->viewport[0]->offset;
+        request->model_requested = EINA_TRUE;
+        request->need_entity = EINA_TRUE;
+
+        requests = eina_list_append(requests, request);
+     }
+
+   // Check if the last viewport has all the upper part of it filler with objects
+   if (pd->viewport[2]->offset + pd->viewport[2]->count > upperlimit_offset)
+     {
+        Efl_Ui_Collection_Request *request;
+
+        request = calloc(1, sizeof (Efl_Ui_Collection_Request));
+        if (request) return ;
+
+        request->offset = upperlimit_offset;
+        // This length work over multiple viewport as they are contiguous
+        request->length = pd->viewport[2]->offset + pd->viewport[2]->count - upperlimit_offset;
+        request->model_requested = EINA_TRUE;
+        request->need_entity = EINA_TRUE;
+
+        requests = eina_list_append(requests, request);
+     }
+
+ flush_requests:
+   requests = _batch_request_flush(requests, data, pd);
+}
+#endif
+
+EFL_CALLBACKS_ARRAY_DEFINE(manager_cbs,
+ { EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, _manager_content_size_changed_cb },
+ { EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, _manager_content_min_size_changed_cb },
+ { EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, _manager_content_visible_range_changed_cb }
+)
+
+static void
+_item_scroll_internal(Eo *obj EINA_UNUSED,
+                      Efl_Ui_Collection_View_Data *pd,
+                      uint64_t index,
+                      double align EINA_UNUSED,
+                      Eina_Bool anim)
+{
+   Eina_Rect ipos, view;
+   Eina_Position2D vpos;
+
+   if (!pd->scroller) return;
+
+   ipos = efl_ui_position_manager_entity_position_single_item(pd->manager, index);
+   view = efl_ui_scrollable_viewport_geometry_get(pd->scroller);
+   vpos = efl_ui_scrollable_content_pos_get(pd->scroller);
+
+   ipos.x = ipos.x + vpos.x - view.x;
+   ipos.y = ipos.y + vpos.y - view.y;
+
+   //FIXME scrollable needs some sort of align, the docs do not even garantee to completly move in the element
+   efl_ui_scrollable_scroll(pd->scroller, ipos, anim);
+}
+
+// Exported function
+
+EOLIAN static void
+_efl_ui_collection_view_factory_set(Eo *obj EINA_UNUSED, Efl_Ui_Collection_View_Data *pd,
+                                  Efl_Ui_Factory *factory)
+{
+   if (pd->factory) efl_ui_property_bind(pd->factory, "selected", NULL);
+   efl_replace(&pd->factory, factory);
+   if (pd->factory) efl_ui_property_bind(pd->factory, "selected", "self.selected");
+}
+
+EOLIAN static Efl_Ui_Factory *
+_efl_ui_collection_view_factory_get(const Eo *obj EINA_UNUSED, Efl_Ui_Collection_View_Data *pd)
+{
+   return pd->factory;
+}
+
+static void
+_unref_cb(void *data)
+{
+   Eo *obj = data;
+
+   efl_unref(obj);
+}
+
+EOLIAN static void
+_efl_ui_collection_view_position_manager_set(Eo *obj, Efl_Ui_Collection_View_Data *pd,
+                                             Efl_Ui_Position_Manager_Entity *manager)
+{
+   Efl_Model *model;
+   unsigned int count;
+
+   if (manager)
+     EINA_SAFETY_ON_FALSE_RETURN(efl_isa(manager, EFL_UI_POSITION_MANAGER_ENTITY_INTERFACE));
+
+   if (pd->manager)
+     {
+        efl_event_callback_array_del(pd->manager, manager_cbs(), obj);
+        efl_del(pd->manager);
+     }
+   pd->manager = manager;
+   if (!pd->manager) return;
+
+   // Start watching change on model from here on
+   model = pd->model;
+   count = model ? efl_model_children_count_get(model) : 0;
+
+   efl_parent_set(pd->manager, obj);
+   efl_event_callback_array_add(pd->manager, manager_cbs(), obj);
+        switch(efl_ui_position_manager_entity_version(pd->manager, 1))
+          {
+            case 1:
+              efl_ui_position_manager_data_access_v1_data_access_set(pd->manager,
+                efl_ref(obj), _batch_entity_cb, _unref_cb,
+                efl_ref(obj), _batch_size_cb, _unref_cb,
+                count);
+            break;
+          }
+
+   if (efl_finalized_get(obj))
+     efl_ui_position_manager_entity_viewport_set(pd->manager, efl_ui_scrollable_viewport_geometry_get(obj));
+   efl_ui_layout_orientation_set(pd->manager, pd->direction);
+}
+
+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)
+{
+   return pd->manager;
+}
+
+static void
+_efl_model_count_changed(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+   // We are not triggering efl_ui_position_manager_entity_data_access_set as it is can
+   // only be slow, we rely on child added/removed instead (If we were to not rely on
+   // child added/removed we could maybe use count changed)
+}
+
+static void
+_efl_model_properties_changed(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+   // We could here watch if the global base size item change and notify of a global change
+   // But I can not find a proper way to do it for the object that are not visible, which
+   // is kind of the point...
+}
+
+static void
+_cache_cleanup_above(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd, unsigned int index)
+{
+   Efl_Ui_Collection_Item_Lookup *lookup;
+   Eina_Array scheduled_release;
+   Eina_Array mark;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+
+   eina_array_step_set(&mark, sizeof (Eina_Array), 16);
+   eina_array_step_set(&scheduled_release, sizeof (Eina_Array), 16);
+
+   _mark_ge((void*) pd->cache, &mark, index);
+
+   EINA_ARRAY_ITER_NEXT(&mark, i, lookup, iterator)
+     {
+        pd->cache = (void*) eina_rbtree_inline_remove(pd->cache,
+                                                      EINA_RBTREE_GET(lookup),
+                                                      _cache_tree_cmp, NULL);
+        _item_cleanup(obj, pd->factory, &lookup->item, &scheduled_release);
+        free(lookup);
+     }
+   eina_array_flush(&mark);
+
+   efl_ui_factory_release(pd->factory, eina_array_iterator_new(&scheduled_release));
+   eina_array_flush(&scheduled_release);
+}
+
+static void
+_efl_model_child_added(void *data, const Efl_Event *event)
+{
+   // At the moment model only append child, but let's try to handle it theorically correct
+   Efl_Model_Children_Event *ev = event->info;
+   MY_DATA_GET(data, pd);
+#ifdef VIEWPORT_ENABLE
+   Eina_List *requests = NULL;
+   unsigned int i;
+#endif
+
+   _cache_cleanup_above(data, pd, ev->index);
+
+   // Check if we really have something to do
+#ifdef VIEWPORT_ENABLE
+   if (!pd->viewport[0]) goto notify_manager;
+
+   // Insert the child in the viewport if necessary
+   for (i = 0; i < 3; i++)
+     {
+        Efl_Ui_Collection_Request *request;
+        unsigned int o;
+        unsigned int j;
+
+        if (ev->index < pd->viewport[i]->offset)
+          {
+             pd->viewport[i]->offset++;
+             continue;
+          }
+        if (pd->viewport[i]->offset + pd->viewport[i]->count < ev->index)
+          {
+             continue;
+          }
+
+        for (j = 2; j > i; j--)
+          {
+             _item_cleanup(pd->factory, &pd->viewport[j]->items[pd->viewport[j]->count - 1]);
+             memmove(&pd->viewport[j]->items[1],
+                     &pd->viewport[j]->items[0],
+                     (pd->viewport[j]->count - 1) * sizeof (Efl_Ui_Collection_Item));
+             pd->viewport[j]->items[0] = pd->viewport[j - 1]->items[pd->viewport[j - 1]->count - 1];
+             pd->viewport[j - 1]->items[pd->viewport[j - 1]->count - 1].entity = NULL;
+             pd->viewport[j - 1]->items[pd->viewport[j - 1]->count - 1].model = NULL;
+          }
+        o = ev->index - pd->viewport[i]->offset;
+        memmove(&pd->viewport[j]->items[o],
+                &pd->viewport[j]->items[o + 1],
+                (pd->viewport[j]->count - 1 - o) * sizeof (Efl_Ui_Collection_Item));
+        pd->viewport[j]->items[o].entity = NULL;
+        pd->viewport[j]->items[o].model = efl_ref(ev->child);
+
+        request = calloc(1, sizeof (Efl_Ui_Collection_Request));
+        if (!request) break;
+        request->offset = ev->index;
+        request->length = 1;
+        request->model_requested = EINA_TRUE;
+        request->need_entity = EINA_TRUE;
+
+        requests = eina_list_append(requests, request);
+
+        requests = _batch_request_flush(requests, data, pd);
+
+        break;
+     }
+
+ notify_manager:
+#endif
+   // FIXME this function must be called with an entity
+   efl_ui_position_manager_entity_item_added(pd->manager, ev->index, NULL);
+}
+
+static void
+_efl_model_child_removed(void *data, const Efl_Event *event)
+{
+   Efl_Model_Children_Event *ev = event->info;
+   MY_DATA_GET(data, pd);
+   Eina_List *requests = NULL;
+   Efl_Ui_Collection_Request *request = NULL;
+#ifdef VIEWPORT_ENABLE
+   Eina_List *requests = NULL;
+   unsigned int i;
+#endif
+   unsigned int upper_end;
+   long length;
+   unsigned int count;
+
+   // FIXME: later optimization, instead of reloading everyone, we could actually track index and self
+   // update would be more efficient, but it is also more tricky
+   _cache_cleanup_above(data, pd, ev->index);
+
+   count = efl_model_children_count_get(event->object);
+   length = pd->current_range.end_id - pd->current_range.start_id;
+   upper_end = MIN(pd->current_range.end_id + (length / 2), count);
+
+   // Check if we really have something to do
+#ifdef VIEWPORT_ENABLE
+   if (!pd->viewport[0]) goto notify_manager;
+
+   // Insert the child in the viewport if necessary
+   for (i = 0; i < 3; i++)
+     {
+        Efl_Ui_Collection_Request *request;
+        unsigned int o;
+
+        if (ev->index < pd->viewport[i]->offset)
+          {
+             pd->viewport[i]->offset--;
+             continue;
+          }
+        if (pd->viewport[i]->offset + pd->viewport[i]->count < ev->index)
+          {
+             continue;
+          }
+
+        o = ev->index - pd->viewport[i]->offset;
+        _item_cleanup(pd->factory, &pd->viewport[i]->items[o]);
+        for (; i < 3; i++)
+          {
+             memmove(&pd->viewport[i]->items[o],
+                     &pd->viewport[i]->items[o + 1],
+                     (pd->viewport[i]->count - 1 - o) * sizeof (Efl_Ui_Collection_Item));
+             if (i + 1 < 3)
+               {
+                  pd->viewport[i]->items[pd->viewport[i]->count - 1] = pd->viewport[i + 1]->items[0];
+               }
+             else
+               {
+                  pd->viewport[i]->items[pd->viewport[i]->count - 1].entity = NULL;
+                  pd->viewport[i]->items[pd->viewport[i]->count - 1].model = NULL;
+               }
+             o = 0;
+          }
+
+        request = calloc(1, sizeof (Efl_Ui_Collection_Request));
+        if (!request) break;
+        request->offset = pd->viewport[2]->offset + pd->viewport[i]->count - 1;
+        request->length = 1;
+        request->model_requested = EINA_TRUE;
+        request->need_entity = EINA_TRUE;
+
+        requests = eina_list_append(requests, request);
+
+        requests = _batch_request_flush(requests, data, pd);
+
+        break;
+     }
+
+ notify_manager:
+#endif
+   requests = _request_add(requests, &request, ev->index, EINA_TRUE);
+   request->length = upper_end - ev->index;
+
+   if (request->length > 0)
+     {
+        requests = eina_list_append(requests, request);
+        requests = _batch_request_flush(requests, data, pd);
+     }
+
+   efl_ui_position_manager_entity_item_removed(pd->manager, ev->index, NULL);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(model_cbs,
+                           { EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, _efl_model_count_changed },
+                           { EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_properties_changed },
+                           { EFL_MODEL_EVENT_CHILD_ADDED, _efl_model_child_added },
+                           { EFL_MODEL_EVENT_CHILD_REMOVED, _efl_model_child_removed })
+
+static void
+_efl_ui_collection_view_model_changed(void *data, const Efl_Event *event)
+{
+   Efl_Model_Changed_Event *ev = event->info;
+   Eina_List *requests = NULL;
+   MY_DATA_GET(data, pd);
+   Eina_Iterator *it;
+   const char *property;
+   Efl_Model *model = NULL;
+   unsigned int count;
+   Efl_Model *mselect = NULL;
+   Eina_Bool selection = EINA_FALSE, sizing = EINA_FALSE;
+
+   if (ev->previous) efl_event_callback_array_del(ev->previous, model_cbs(), data);
+   if (ev->current) efl_event_callback_array_add(ev->current, model_cbs(), data);
+
+   // Cleanup all object, pending request and refetch everything
+   _all_cleanup(data, pd);
+
+   efl_replace(&pd->model, NULL);
+
+   if (!ev->current)
+     {
+        if (pd->multi_selectable_async_model)
+          {
+             efl_event_callback_forwarder_del(pd->multi_selectable_async_model,
+                                              EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED,
+                                              data);
+             efl_composite_detach(data, pd->multi_selectable_async_model);
+             efl_replace(&pd->multi_selectable_async_model, NULL);
+          }
+        return ;
+     }
+
+   it = efl_model_properties_get(ev->current);
+   EINA_ITERATOR_FOREACH(it, property)
+     {
+        // Check if the model provide selection
+        if (eina_streq(property, "child.selected"))
+          selection = EINA_TRUE;
+        // Check if the model provide sizing logic
+        else if (eina_streq(property, _efl_model_property_itemw) ||
+                 eina_streq(property, _efl_model_property_itemh))
+          sizing = EINA_TRUE;
+     }
+   eina_iterator_free(it);
+
+   if (selection)
+     {
+        // Search the composition of model for the one providing MULTI_SELECTABLE_ASYNC
+        mselect = ev->current;
+        while (mselect &&
+               !efl_isa(mselect, EFL_UI_MULTI_SELECTABLE_ASYNC_INTERFACE) &&
+               efl_isa(mselect, EFL_COMPOSITE_MODEL_CLASS))
+          mselect = efl_ui_view_model_get(mselect);
+
+        if (!efl_isa(mselect, EFL_UI_MULTI_SELECTABLE_ASYNC_INTERFACE))
+          {
+             mselect = NULL;
+             selection = EINA_FALSE;
+          }
+     }
+
+   // Try to build the minimal chain of necessary model for collection view
+   model = ev->current;
+
+   // Build and connect the selection model properly
+   if (!mselect)
+     {
+        mselect = model = efl_add(EFL_UI_SELECT_MODEL_CLASS, data,
+                                  efl_ui_view_model_set(efl_added, model));
+     }
+   if (pd->multi_selectable_async_model)
+     {
+        efl_event_callback_forwarder_del(pd->multi_selectable_async_model,
+                                         EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED,
+                                         data);
+        efl_composite_detach(data, pd->multi_selectable_async_model);
+     }
+   efl_replace(&pd->multi_selectable_async_model, mselect);
+   efl_composite_attach(data, pd->multi_selectable_async_model);
+   efl_event_callback_forwarder_add(pd->multi_selectable_async_model,
+                                    EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED,
+                                    data);
+
+   if (!sizing) model = efl_add(EFL_UI_HOMOGENEOUS_MODEL_CLASS, data,
+                                efl_ui_view_model_set(efl_added, model));
+
+   count = efl_model_children_count_get(model);
+
+#ifdef VIEWPORT_ENABLE
+   for (i = 0; i < 3; i++)
+     {
+        Efl_Ui_Collection_Request *request;
+
+        if (!pd->viewport[i]) continue ;
+        if (pd->viewport[i]->count == 0) continue ;
+
+        request = calloc(1, sizeof (Efl_Ui_Collection_Request));
+        if (!request) continue ;
+
+        request->offset = pd->viewport[i]->offset;
+        request->length = pd->viewport[i]->count;
+        request->model_requested = EINA_TRUE;
+        request->need_entity = EINA_TRUE;
+
+        requests = eina_list_append(requests, request);
+     }
+#endif
+   requests = _batch_request_flush(requests, data, pd);
+
+   pd->model = model;
+   efl_ui_position_manager_entity_item_size_changed(pd->manager, 0, count - 1);
+   switch(efl_ui_position_manager_entity_version(pd->manager, 1))
+     {
+       case 1:
+         efl_ui_position_manager_data_access_v1_data_access_set(pd->manager,
+           efl_ref(data), _batch_entity_cb, _unref_cb,
+           efl_ref(data), _batch_size_cb, _unref_cb,
+           count);
+       break;
+     }
+
+
+}
+
+static void
+_pan_viewport_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   MY_DATA_GET(data, pd);
+   Eina_Rect rect = efl_ui_scrollable_viewport_geometry_get(data);
+
+   efl_ui_position_manager_entity_viewport_set(pd->manager, rect);
+}
+
+static void
+_pan_position_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   MY_DATA_GET(data, pd);
+   Eina_Position2D pos = efl_ui_pan_position_get(pd->pan);
+   Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
+   Eina_Vector2 rpos = {0.0, 0.0};
+
+   if (max.x > 0.0)
+     rpos.x = (double)pos.x/(double)max.x;
+   if (max.y > 0.0)
+     rpos.y = (double)pos.y/(double)max.y;
+
+   efl_ui_position_manager_entity_scroll_position_set(pd->manager, rpos.x, rpos.y);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(pan_events_cb,
+  {EFL_UI_PAN_EVENT_PAN_CONTENT_POSITION_CHANGED, _pan_position_changed_cb},
+  {EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _pan_viewport_changed_cb},
+  {EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _pan_viewport_changed_cb},
+)
+
+EOLIAN static Efl_Object *
+_efl_ui_collection_view_efl_object_constructor(Eo *obj, Efl_Ui_Collection_View_Data *pd)
+{
+   pd->direction = EFL_UI_LAYOUT_ORIENTATION_VERTICAL;
+   obj = efl_constructor(efl_super(obj, EFL_UI_COLLECTION_VIEW_CLASS));
+
+   if (!elm_widget_theme_klass_get(obj))
+     elm_widget_theme_klass_set(obj, "collection");
+
+   efl_wref_add(efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(obj)), &pd->sizer);
+   efl_gfx_color_set(pd->sizer, 0, 0, 0, 0);
+
+   efl_wref_add(efl_add(EFL_UI_PAN_CLASS, obj), &pd->pan);
+   efl_content_set(pd->pan, pd->sizer);
+   efl_event_callback_array_add(pd->pan, pan_events_cb(), obj);
+
+   efl_wref_add(efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj), &pd->scroller);
+   efl_composite_attach(obj, pd->scroller);
+   efl_ui_mirrored_set(pd->scroller, efl_ui_mirrored_get(obj));
+   efl_ui_scroll_manager_pan_set(pd->scroller, pd->pan);
+
+   efl_ui_scroll_connector_bind(obj, pd->scroller);
+
+   efl_event_callback_add(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED,
+                          _efl_ui_collection_view_model_changed, obj);
+
+   return obj;
+}
+
+EOLIAN static void
+_efl_ui_collection_view_efl_object_invalidate(Eo *obj,
+                                              Efl_Ui_Collection_View_Data *pd)
+{
+   efl_ui_collection_view_position_manager_set(obj, NULL);
+   efl_event_callback_del(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED,
+                          _efl_ui_collection_view_model_changed, obj);
+
+   _all_cleanup(obj, pd);
+
+   efl_invalidate(efl_super(obj, EFL_UI_COLLECTION_VIEW_CLASS));
+}
+
+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)
+{
+   if (pd->direction == dir) return;
+
+   pd->direction = dir;
+   if (pd->manager) efl_ui_layout_orientation_set(pd->manager, dir);
+}
+
+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)
+{
+   return pd->direction;
+}
+
+EOLIAN static Eina_Error
+_efl_ui_collection_view_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Collection_View_Data *pd)
+{
+   Eina_Error res;
+
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC);
+   res = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
+   if (res == EFL_UI_THEME_APPLY_ERROR_GENERIC) return res;
+   efl_ui_mirrored_set(pd->scroller, efl_ui_mirrored_get(obj));
+   efl_content_set(efl_part(wd->resize_obj, "efl.content"), pd->pan);
+
+   return res;
+}
+
+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)
+{
+   if (pd->match_content.w == w && pd->match_content.h == h)
+     return;
+
+   pd->match_content.w = w;
+   pd->match_content.h = h;
+
+   efl_ui_scrollable_match_content_set(pd->scroller, w, h);
+   flush_min_size(obj, pd);
+}
+
+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)
+{
+   Efl_Ui_Collection_View_Focus_Manager_Data *mpd;
+   Eo *manager = efl_add(EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS, obj,
+                         efl_ui_focus_manager_root_set(efl_added, root));
+
+   mpd = efl_data_scope_get(manager, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS);
+   mpd->collection = obj;
+
+   return manager;
+}
+
+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)
+{
+   Eo *new_obj, *focus;
+   Eina_Size2D step;
+
+   new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
+   focus = efl_ui_focus_manager_focus_get(obj);
+   step = efl_gfx_hint_size_combined_min_get(focus);
+   if (!new_obj)
+     {
+        Eina_Rect pos = efl_gfx_entity_geometry_get(focus);
+        Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(pd->scroller);
+        Eina_Position2D vpos = efl_ui_scrollable_content_pos_get(pd->scroller);
+
+        pos.x = pos.x + vpos.x - view.x;
+        pos.y = pos.y + vpos.y - view.y;
+        Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
+
+        if (direction == EFL_UI_FOCUS_DIRECTION_RIGHT)
+          {
+             if (pos.x < max.x)
+               {
+                  pos.x = MIN(max.x, pos.x + step.w);
+                  efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
+                  new_obj = focus;
+               }
+          }
+        else if (direction == EFL_UI_FOCUS_DIRECTION_LEFT)
+          {
+             if (pos.x > 0)
+               {
+                  pos.x = MAX(0, pos.x - step.w);
+                  efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
+                  new_obj = focus;
+               }
+          }
+        else if (direction == EFL_UI_FOCUS_DIRECTION_UP)
+          {
+             if (pos.y > 0)
+               {
+                  pos.y = MAX(0, pos.y - step.h);
+                  efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
+                  new_obj = focus;
+               }
+          }
+        else if (direction == EFL_UI_FOCUS_DIRECTION_DOWN)
+          {
+             if (pos.y < max.y)
+               {
+                  pos.y = MAX(0, pos.y + step.h);
+                  efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
+                  new_obj = focus;
+               }
+          }
+     }
+   else
+     {
+        Efl_Model *model;
+        Eina_Value *vindex;
+        uint64_t index;
+
+        model = efl_ui_view_model_get(new_obj);
+        vindex = efl_model_property_get(model, "child.index");
+        if (eina_value_uint64_convert(vindex, &index))
+          _item_scroll_internal(obj, pd, index, .0, EINA_TRUE);
+        eina_value_free(vindex);
+     }
+
+   return new_obj;
+}
+
+#include "efl_ui_collection_view.eo.c"
+
+#define ITEM_IS_OUTSIDE_VISIBLE(id) id < cpd->start_id || id > cpd->end_id
+
+static Efl_Ui_Item *
+_find_item(Eo *obj EINA_UNUSED, Efl_Ui_Collection_View_Data *pd EINA_UNUSED, Eo *focused_element)
+{
+   if (!focused_element) return NULL;
+
+   while (focused_element &&
+          efl_key_data_get(focused_element, COLLECTION_VIEW_MANAGED) != COLLECTION_VIEW_MANAGED_YES)
+     {
+        focused_element = efl_ui_widget_parent_get(focused_element);
+     }
+
+   return focused_element;
+}
+
+static inline void
+_assert_item_available(Eo *item, int new_id, Efl_Ui_Collection_View_Data *pd)
+{
+   efl_gfx_entity_visible_set(item, EINA_TRUE);
+   efl_gfx_entity_geometry_set(item, efl_ui_position_manager_entity_position_single_item(pd->manager, new_id));
+}
+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)
+{
+   MY_DATA_GET(pd->collection, cpd);
+   Efl_Ui_Item *item = NULL;
+   uint64_t item_id;
+
+   if (focus == efl_ui_focus_manager_root_get(obj))
+     {
+        // Find last item
+        item_id = efl_model_children_count_get(cpd->model) - 1;
+     }
+   else
+     {
+        Efl_Model *model;
+        Eina_Value *vindex;
+
+        item = _find_item(obj, cpd, focus);
+        if (!item) return ;
+
+        model = efl_ui_view_model_get(item);
+        vindex = efl_model_property_get(model, "child.index");
+        if (!eina_value_uint64_convert(vindex, &item_id)) return;
+        eina_value_free(vindex);
+     }
+
+   // If this is NULL then we are before finalize, we cannot serve any sane value here
+   if (!cpd->manager) return ;
+
+   if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
+     {
+        _assert_item_available(item, item_id, cpd);
+     }
+   efl_ui_focus_manager_focus_set(efl_super(obj, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS), focus);
+}
+
+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)
+{
+   MY_DATA_GET(pd->collection, cpd);
+   Efl_Ui_Item *new_item, *item;
+   unsigned int item_id;
+
+   if (!child)
+     child = efl_ui_focus_manager_focus_get(obj);
+
+   item = _find_item(obj, cpd, child);
+
+   //if this is NULL then we are before finalize, we cannot serve any sane value here
+   if (!cpd->manager) return NULL;
+   if (!item) return NULL;
+
+   item_id = efl_ui_item_index_get(item);
+
+   if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
+     {
+        int new_id;
+
+        new_id = efl_ui_position_manager_entity_relative_item(cpd->manager,
+                                                              efl_ui_item_index_get(item),
+                                                              direction);
+        if (new_id == -1)
+          {
+             new_item = NULL;
+          }
+        else
+          {
+#ifdef VIEWPORT_ENABLE
+             unsigned int i;
+
+             for (i = 0; i < 3; i++)
+               {
+                  if (!cpd->viewport[i]) continue;
+
+                  if (!((cpd->viewport[i]->offset <= (unsigned int) new_id) &&
+                        ((unsigned int) new_id < cpd->viewport[i]->offset + cpd->viewport[i]->count)))
+                    continue;
+
+                  new_item = cpd->viewport[i]->items[new_id - cpd->viewport[i]->offset].entity;
+                  // We shouldn't get in a case where the available item is NULL
+                  if (!new_item) break; // Just in case
+                  _assert_item_available(new_item, new_id, cpd);
+               }
+#endif
+          }
+     }
+   else
+     {
+        new_item = efl_ui_focus_manager_request_move(efl_super(obj, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS), direction, child, logical);
+     }
+
+   return new_item;
+}
+
+#include "efl_ui_collection_view_focus_manager.eo.c"
diff --git a/src/lib/elementary/efl_ui_collection_view.eo b/src/lib/elementary/efl_ui_collection_view.eo
new file mode 100644 (file)
index 0000000..d0efbf3
--- /dev/null
@@ -0,0 +1,61 @@
+class @beta Efl.Ui.Collection_View extends Efl.Ui.Layout_Base implements
+                Efl.Ui.Layout_Orientable,
+                Efl.Ui.Selectable,
+                Efl.Ui.Multi_Selectable_Async,
+                Efl.Ui.Focus.Manager_Sub,
+                Efl.Ui.Widget_Focus_Manager,
+                Efl.Ui.Collection_Events
+    composites Efl.Ui.Scrollable, Efl.Ui.Scrollbar, Efl.Ui.Multi_Selectable_Async
+{
+   [[This widget displays a list of items in an arrangement controlled by an external @.position_manager
+     object. By using different @.position_manager objects this widget can show unidimensional lists or
+     two-dimensional grids of items, for example.
+
+     This class is intended to act as a base for widgets like List_View or Grid_View,
+     which hide this complexity from the user.
+
+     Items are generated by the @Efl.Ui.Factory defined with .factory.set to match the content of the
+     @Efl.Model defined with @Efl.Ui.View.model.set. They are dynamically created/destroyed to only have
+     the one that are necessary to display all the one that are to far out of the viewport will not be
+     created to lighten the usage for very large list.
+
+     The direction of the arrangement can be controlled through @Efl.Ui.Layout_Orientable.orientation.
+
+     If all items do not fit in the current widget size scrolling facilities are provided.
+
+     Items inside this widget can be selected according to the @Efl.Ui.Multi_Selectable_Async.select_mode
+     policy, and the selection can be retrieved with @Efl.Ui.Multi_Selectable_Async.selected_iterator_new.
+   ]]
+   methods {
+      @property factory {
+         [[Define the factory used to create all the items.]]
+         get {}
+         set {}
+         values {
+            factory: Efl.Ui.Factory; [[The factory.]]
+         }
+      }
+      @property position_manager {
+        [[Position manager object that handles placement of items.]]
+        values {
+           position_manager : Efl.Ui.Position_Manager.Entity @move; [[The objects ownership is passed to the item container.]]
+        }
+      }
+   }
+   implements {
+      Efl.Object.constructor;
+      Efl.Object.invalidate;
+
+      Efl.Ui.Layout_Orientable.orientation { get; set; }
+
+      Efl.Ui.Widget.theme_apply;
+
+      Efl.Ui.Scrollable.match_content { set; }
+      Efl.Ui.Widget_Focus_Manager.focus_manager_create;
+      Efl.Ui.Focus.Manager.move;
+   }
+   events {
+      item,realized : Efl.Ui.Item; [[Event triggered when an @Efl.Ui.Item has been provided by the @Efl.Ui.Factory and is about to be used.]]
+      item,unrealized : Efl.Ui.Item; [[Event triggered when the @Efl.Ui.Collection_View is about to give an @Efl.Ui.Item back to the @Efl.Ui.Factory.]]
+   }
+}
diff --git a/src/lib/elementary/efl_ui_collection_view_focus_manager.eo b/src/lib/elementary/efl_ui_collection_view_focus_manager.eo
new file mode 100644 (file)
index 0000000..bd4e277
--- /dev/null
@@ -0,0 +1,7 @@
+class @beta Efl.Ui.Collection_View_Focus_Manager extends Efl.Ui.Focus.Manager_Calc {
+  [[Internal class which implements collection specific behaviour, cannot be used outside of collection]]
+  implements {
+      Efl.Ui.Focus.Manager.manager_focus { set; }
+      Efl.Ui.Focus.Manager.request_move;
+  }
+}
index b454555..7cdfe16 100644 (file)
@@ -208,6 +208,29 @@ _efl_ui_item_item_parent_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Data *pd)
    return pd->parent;
 }
 
+EOLIAN static void
+_efl_ui_item_calc_locked_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Data *pd, Eina_Bool locked)
+{
+   pd->locked = !!locked;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_item_calc_locked_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Data *pd)
+{
+   return pd->locked;
+}
+
+EOLIAN static void
+_efl_ui_item_efl_canvas_group_group_need_recalculate_set(Eo *obj, Efl_Ui_Item_Data *pd EINA_UNUSED, Eina_Bool value)
+{
+   // Prevent recalc when the item are stored in the cache
+   // As due to async behavior, we can still have text updated from future that just finished after
+   // we have left the releasing stage of factories. This is the simplest way to prevent those later
+   // update.
+   if (pd->locked) return;
+   efl_canvas_group_need_recalculate_set(efl_super(obj, EFL_UI_ITEM_CLASS), value);
+}
+
 ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_item, Efl_Ui_Item_Data)
 
 #include "efl_ui_item.eo.c"
index 0e841f5..9f05b1e 100644 (file)
@@ -54,6 +54,17 @@ abstract Efl.Ui.Item extends Efl.Ui.Layout_Base implements Efl.Ui.Selectable, Ef
             parent : Efl.Ui.Item;
          }
       }
+      @property calc_locked {
+         [[If the item has its calc locked it will not trigger @Efl.Canvas.Group.group_need_recalculate.set done.
+
+           This is done automatically by @Efl.Ui.Widget_Factory, but you can use this information to meaningfully set the hint when items are not @.calc_locked.
+         ]]
+         set {}
+         get {}
+         values {
+            locked: bool; [[If set to $true, no more @Efl.Canvas.Group.group_need_recalculate.set]]
+         }
+      }
    }
    implements {
       Efl.Object.constructor;
@@ -61,5 +72,6 @@ abstract Efl.Ui.Item extends Efl.Ui.Layout_Base implements Efl.Ui.Selectable, Ef
       Efl.Object.destructor;
       Efl.Ui.Selectable.selected {get; set;}
       Efl.Ui.Widget.widget_input_event_handler;
+      Efl.Canvas.Group.group_need_recalculate { set; }
    }
 }
index cd8300b..5c1ef26 100644 (file)
@@ -11,6 +11,7 @@ typedef struct _Efl_Ui_Item_Data
 
    // Boolean Data
    Eina_Bool  selected : 1; /* State for item selected */
+   Eina_Bool  locked : 1;
 } Efl_Ui_Item_Data;
 
 
index cee6730..0adb498 100644 (file)
@@ -37,6 +37,14 @@ typedef struct {
  */
 
 static void
+cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
+{
+   if (pd->size_cache)
+     free(pd->size_cache);
+   pd->size_cache = NULL;
+}
+
+static void
 cache_require(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
 {
    unsigned int i;
@@ -82,19 +90,15 @@ cache_require(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
           }
         pd->size_cache[i + 1] = pd->size_cache[i] + step;
         pd->maximum_min_size = MAX(pd->maximum_min_size, min);
+        /* no point iterating further if size calc can't be done yet */
+        //if ((!i) && (!pd->maximum_min_size)) break;
      }
    pd->average_item_size = pd->size_cache[pd->size]/pd->size;
+   if ((!pd->average_item_size) && (!pd->maximum_min_size))
+     cache_invalidate(obj, pd);
 }
 
-static void
-cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
-{
-   if (pd->size_cache)
-     free(pd->size_cache);
-   pd->size_cache = NULL;
-}
-
-static inline int
+static int
 cache_access(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd, unsigned int idx)
 {
    EINA_SAFETY_ON_FALSE_RETURN_VAL(idx <= pd->size, 0);
@@ -105,7 +109,12 @@ static void
 recalc_absolut_size(Eo *obj, Efl_Ui_Position_Manager_List_Data *pd)
 {
    Eina_Size2D min_size = EINA_SIZE2D(-1, -1);
+   Eina_Size2D pabs_size = pd->abs_size;
+   int pmin_size = pd->maximum_min_size;
+
    cache_require(obj, pd);
+   /* deferred */
+   if (!pd->size_cache) return;
 
    pd->abs_size = pd->viewport.size;
 
@@ -116,8 +125,8 @@ recalc_absolut_size(Eo *obj, Efl_Ui_Position_Manager_List_Data *pd)
         else
           pd->abs_size.w = MAX(cache_access(obj, pd, pd->size), pd->abs_size.w);
      }
-
-   efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, &pd->abs_size);
+   if ((pabs_size.w != pd->abs_size.w) || (pabs_size.h != pd->abs_size.h))
+     efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, &pd->abs_size);
 
    if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
      {
@@ -127,8 +136,8 @@ recalc_absolut_size(Eo *obj, Efl_Ui_Position_Manager_List_Data *pd)
      {
         min_size.h = pd->maximum_min_size;
      }
-
-   efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
+   if ((pd->maximum_min_size > 0) && (pmin_size > 0) && (pd->maximum_min_size != pmin_size))
+     efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
 }
 
 static inline Vis_Segment
index a18baf4..976966b 100644 (file)
@@ -6816,8 +6816,6 @@ _efl_ui_property_bind_get(Eo *obj, Efl_Ui_Widget_Data *pd, Efl_Ui_Property_Bound
    value = efl_model_property_get(pd->properties.model, prop->property);
    target = prop->part ? efl_part(obj, prop->part) : obj;
 
-   fprintf(stderr, "setting: %s for %s from %s\n",
-           eina_value_to_string(value), prop->property, efl_debug_name_get(pd->properties.model));
    err = efl_property_reflection_set(target, prop->key, eina_value_reference_copy(value));
    eina_value_free(value);
 
index 0be3e30..b1c339d 100644 (file)
@@ -95,6 +95,9 @@ _efl_ui_widget_factory_constructing(void *data EINA_UNUSED, const Efl_Event *ev)
 
    model = efl_ui_view_model_get(ui_view);
 
+   // Enable recalculate in case we do not know the size of the item
+   efl_canvas_group_need_recalculate_set(ui_view, EINA_TRUE);
+
    // Fetch min size from model if available to avoid recalculcating it
    width = efl_model_property_get(model, "self.width");
    height = efl_model_property_get(model, "self.height");
@@ -106,12 +109,14 @@ _efl_ui_widget_factory_constructing(void *data EINA_UNUSED, const Efl_Event *ev)
         if (!eina_value_int_convert(width, &s.w)) s.w = 0;
         if (!eina_value_int_convert(height, &s.h)) s.h = 0;
 
-        /* efl_event_freeze(ui_view); */
-        efl_key_data_set(ui_view, "efl.ui.widget.factory.size_set", (void*)EINA_TRUE);
         efl_gfx_hint_size_min_set(ui_view, s);
+        efl_canvas_group_need_recalculate_set(ui_view, EINA_FALSE);
+        if (efl_isa(ui_view, EFL_UI_ITEM_CLASS)) efl_ui_item_calc_locked_set(ui_view, EINA_TRUE);
      }
    eina_value_free(width);
    eina_value_free(height);
+
+   efl_key_data_set(ui_view, "efl.ui.widget.factory.size_check", (void*)EINA_TRUE);
 }
 
 
@@ -121,7 +126,7 @@ _efl_ui_widget_factory_building(void *data, const Efl_Event *ev)
    Efl_Gfx_Entity *ui_view = ev->info;
    Efl_Ui_Widget_Factory_Data *pd = data;
    const Efl_Model *model;
-   Eina_Value *property, *width, *height;
+   Eina_Value *property;
    Efl_Ui_Bind_Part_Data *bpd;
    Eina_Iterator *it;
    char *style;
@@ -146,22 +151,6 @@ _efl_ui_widget_factory_building(void *data, const Efl_Event *ev)
      }
    eina_iterator_free(it);
 
-   // Fetch min size from model if available to avoid recalculcating it
-   width = efl_model_property_get(model, "self.width");
-   height = efl_model_property_get(model, "self.height");
-   if (eina_value_type_get(width) != EINA_VALUE_TYPE_ERROR &&
-       eina_value_type_get(height) != EINA_VALUE_TYPE_ERROR)
-     {
-        Eina_Size2D s;
-
-        if (!eina_value_int_convert(width, &s.w)) s.w = 0;
-        if (!eina_value_int_convert(height, &s.h)) s.h = 0;
-
-        efl_gfx_hint_size_min_set(ui_view, s);
-     }
-   eina_value_free(width);
-   eina_value_free(height);
-
    // As we have already waited for the property to be ready, we should get the right style now
    if (!pd->style) return ;
 
@@ -173,6 +162,8 @@ _efl_ui_widget_factory_building(void *data, const Efl_Event *ev)
    free(style);
 
    eina_value_free(property);
+
+   efl_key_data_set(ui_view, "efl.ui.widget.factory.cached", NULL);
 }
 
 static void
@@ -185,6 +176,7 @@ _efl_ui_widget_factory_releasing(void *data, const Efl_Event *ev)
 
    efl_key_data_set(ui_view, "efl.ui.widget.factory.size_set", NULL);
    efl_key_data_set(ui_view, "efl.ui.widget.factory.size_check", NULL);
+   if (efl_isa(ui_view, EFL_UI_ITEM_CLASS)) efl_ui_item_calc_locked_set(ui_view, EINA_TRUE);
 
    // Bind all property before the object is finalize
    it = eina_hash_iterator_data_new(pd->parts);
@@ -200,6 +192,9 @@ _efl_ui_widget_factory_releasing(void *data, const Efl_Event *ev)
    eina_iterator_free(it);
 
    efl_ui_view_model_set(ui_view, NULL);
+
+   // Prevent any recalc to happen when an object is in the cache or during shutdown of the object
+   efl_canvas_group_need_recalculate_set(ui_view, EINA_FALSE);
 }
 
 EFL_CALLBACKS_ARRAY_DEFINE(item_callbacks,
index f6e84d0..d6f5ba4 100644 (file)
 # include "efl_ui_focus_parent_provider_standard.eo.h"
 # include "efl_ui_selection_manager.eo.h"
 # include "efl_datetime_manager.eo.h"
+
 extern const char *_efl_model_property_itemw;
 extern const char *_efl_model_property_itemh;
 extern const char *_efl_model_property_selfw;
index a22e188..517db3e 100644 (file)
@@ -191,6 +191,8 @@ pub_eo_files = [
   'efl_ui_tab_bar_default_item.eo',
   'efl_ui_select_model.eo',
   'efl_ui_view_model.eo',
+  'efl_ui_collection_view.eo',
+  'efl_ui_collection_view_focus_manager.eo',
 ]
 
 foreach eo_file : pub_eo_files
@@ -1007,6 +1009,7 @@ elementary_src = [
   'efl_ui_tab_bar_default_item.c',
   'efl_ui_select_model.c',
   'efl_ui_view_model.c',
+  'efl_ui_collection_view.c',
 ]
 
 #<--TIZEN_ONLY