--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+
+typedef struct _Efl_Ui_Caching_Factory_Data Efl_Ui_Caching_Factory_Data;
+struct _Efl_Ui_Caching_Factory_Data
+{
+ const Efl_Class *klass;
+
+ // Simple list of ready-to-use objects. They are all equal so it does not matter from which
+ // end of the list objects are added and removed.
+ Eina_List *cache;
+
+ struct {
+ unsigned int memory;
+ unsigned int items;
+ } limit, current;
+};
+
+// Clear the cache until it meet the constraint
+static void
+_efl_ui_caching_factory_remove(Efl_Ui_Caching_Factory_Data *pd, Eina_List *l, Efl_Gfx_Entity *entity)
+{
+ pd->cache = eina_list_remove_list(pd->cache, l);
+ pd->current.items--;
+
+ pd->current.memory -= efl_class_memory_size_get(entity);
+ if (efl_isa(entity, EFL_CACHED_ITEM_INTERFACE))
+ pd->current.memory -= efl_cached_item_memory_size_get(entity);
+}
+
+static void
+_efl_ui_caching_factory_flush(Efl_Ui_Caching_Factory_Data *pd)
+{
+ while (pd->limit.items != 0 &&
+ pd->current.items > pd->limit.items)
+ {
+ Efl_Gfx_Entity *entity;
+
+ entity = eina_list_data_get(eina_list_last(pd->cache));
+
+ _efl_ui_caching_factory_remove(pd, eina_list_last(pd->cache), entity);
+
+ efl_del(entity);
+ }
+
+ while (pd->limit.memory != 0 &&
+ pd->current.memory > pd->limit.memory)
+ {
+ Efl_Gfx_Entity *entity;
+
+ entity = eina_list_data_get(eina_list_last(pd->cache));
+
+ _efl_ui_caching_factory_remove(pd, eina_list_last(pd->cache), entity);
+
+ efl_del(entity);
+ }
+}
+
+static Eina_Future *
+_efl_ui_caching_factory_efl_ui_factory_create(Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd,
+ Efl_Model *model, Efl_Gfx_Entity *parent)
+{
+ Efl_Gfx_Entity *r;
+
+ if (pd->cache)
+ {
+ r = eina_list_data_get(pd->cache);
+
+ _efl_ui_caching_factory_remove(pd, pd->cache, r);
+
+ efl_parent_set(r, parent);
+ }
+ else
+ {
+ r = efl_add(pd->klass, parent);
+ }
+
+ efl_ui_view_model_set(r, model);
+
+ return efl_loop_future_resolved(obj, eina_value_object_init(r));
+}
+
+static void
+_efl_ui_caching_factory_item_class_set(Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd,
+ const Efl_Object *klass)
+{
+ if (!efl_isa(klass, EFL_GFX_ENTITY_INTERFACE) ||
+ !efl_isa(klass, EFL_UI_VIEW_INTERFACE))
+ {
+ ERR("Provided class '%s' for factory '%s' doesn't implement '%s' and '%s' interfaces.",
+ efl_class_name_get(klass),
+ efl_class_name_get(obj),
+ efl_class_name_get(EFL_GFX_ENTITY_INTERFACE),
+ efl_class_name_get(EFL_UI_VIEW_INTERFACE));
+ return ;
+ }
+ pd->klass = klass;
+}
+
+static const Efl_Object *
+_efl_ui_caching_factory_item_class_get(const Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd)
+{
+ return pd->klass;
+}
+
+static void
+_efl_ui_caching_factory_memory_limit_set(Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd,
+ unsigned int limit)
+{
+ pd->limit.memory = limit;
+
+ _efl_ui_caching_factory_flush(pd);
+}
+
+static unsigned int
+_efl_ui_caching_factory_memory_limit_get(const Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd)
+{
+ return pd->limit.memory;
+}
+
+static void
+_efl_ui_caching_factory_items_limit_set(Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd,
+ unsigned int limit)
+{
+ pd->limit.items = limit;
+
+ _efl_ui_caching_factory_flush(pd);
+}
+
+static unsigned int
+_efl_ui_caching_factory_items_limit_get(const Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd)
+{
+ return pd->limit.items;
+}
+
+static void
+_efl_ui_caching_factory_efl_ui_factory_release(Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd,
+ Efl_Gfx_Entity *ui_view)
+{
+ // Change parent, disconnect the object and make it invisible
+ efl_parent_set(ui_view, obj);
+ efl_gfx_entity_visible_set(ui_view, EINA_FALSE);
+ efl_ui_view_model_set(ui_view, NULL);
+
+ // Add to the cache
+ pd->cache = eina_list_prepend(pd->cache, ui_view);
+ pd->current.items++;
+ pd->current.memory += efl_class_memory_size_get(ui_view);
+ if (efl_isa(ui_view, EFL_CACHED_ITEM_INTERFACE))
+ pd->current.memory += efl_cached_item_memory_size_get(ui_view);
+
+ // And check if the cache need some triming
+ _efl_ui_caching_factory_flush(pd);
+}
+
+static void
+_efl_ui_caching_factory_efl_object_invalidate(Eo *obj,
+ Efl_Ui_Caching_Factory_Data *pd)
+{
+ // As all the objects in the cache have the factory as parent, there's no need to unparent them
+ pd->cache = eina_list_free(pd->cache);
+}
+
+static Efl_App *
+_efl_ui_caching_factory_app_get(Eo *obj)
+{
+ Efl_Object *p;
+
+ p = efl_parent_get(obj);
+ if (!p) return NULL;
+
+ // It is acceptable to just have a loop as parent and not an app
+ return efl_provider_find(obj, EFL_APP_CLASS);
+}
+
+static void
+_efl_ui_caching_factory_pause(void *data, const Efl_Event *event)
+{
+ Efl_Ui_Caching_Factory_Data *pd = data;
+ Efl_Gfx_Entity *entity;
+
+ // Application is going into background, let's free ressource
+ // Possible improvement would be to delay that by a few second.
+ EINA_LIST_FREE(pd->cache, entity)
+ efl_del(entity);
+
+ pd->current.items = 0;
+ pd->current.memory = 0;
+}
+
+static void
+_efl_ui_caching_factory_efl_object_parent_set(Eo *obj, Efl_Ui_Caching_Factory_Data *pd, Efl_Object *parent)
+{
+ Efl_App *a;
+
+ a = _efl_ui_caching_factory_app_get(obj);
+ if (a) efl_event_callback_del(a, EFL_APP_EVENT_PAUSE, _efl_ui_caching_factory_pause, pd);
+
+ efl_parent_set(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS), parent);
+
+ // We are fetching the parent again, just in case the update was denied
+ a = _efl_ui_caching_factory_app_get(obj);
+ if (a) efl_event_callback_add(a, EFL_APP_EVENT_PAUSE, _efl_ui_caching_factory_pause, pd);
+}
+
+#include "efl_ui_caching_factory.eo.c"
--- /dev/null
+class Efl.Ui.Caching_Factory (Efl.Loop_Consumer, Efl.Ui.Factory)
+{
+ [[Efl Ui Factory that provides object caching.
+
+ This factory handles caching of one type of object and automatically empties the cache
+ when the application goes into pause.
+
+ Creating objects is costly and time consuming, keeping a few on hand for when you next will need them helps a lot.
+ This is what this factory caching infrastructure provides. It will create the object from the class defined on it and
+ set the parent and the model as needed for all created items. The View has to release the Item using the
+ release function of the Factory interface for all of this to work properly.
+
+ The cache might decide to flush itself when the application event pause is triggered.
+ ]]
+ methods {
+ @property item_class {
+ [[Define the class of the item returned by this factory.]]
+ get {}
+ set {}
+ values {
+ klass: const(Efl.Class); [[The class identifier to create item from.]]
+ }
+ }
+ @property memory_limit {
+ [[Define the maxium size in Bytes that all the object waiting on standby in the cache take. They must provide the @Efl.Cached.Item interface for an accurate accounting.]]
+ get {}
+ set {}
+ values {
+ limit: uint; [[When set to zero, there is no limit on the amount of memory the cache will use.]]
+ }
+ }
+ @property items_limit {
+ [[Define how many maximum number of items are waiting on standby in the cache.]]
+ get {}
+ set {}
+ values {
+ limit: uint; [[When set to zero, there is no limit to the amount of items stored in the cache.]]
+ }
+ }
+ }
+
+ implements {
+ Efl.Ui.Factory.create;
+ Efl.Ui.Factory.release;
+ Efl.Object.invalidate;
+ Efl.Object.parent { set; }
+ }
+}