aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorCedric BAIL <cedric.bail@free.fr>2018-12-06 16:38:47 -0800
committerCedric BAIL <cedric.bail@free.fr>2019-01-09 11:02:11 -0800
commit4fe4f76b8c8d0bdcacc5c045a95b1431dda964ff (patch)
tree3cdad6250c6a02c43b7f8dc9e8f293ed50189e16 /src/lib
parentefl: add a factory Eina_Error and rename the file to be more on point with it... (diff)
downloadefl-4fe4f76b8c8d0bdcacc5c045a95b1431dda964ff.tar.gz
elementary: add a factory that handle caching for you.
This factory handle caching of one type of object and automatically empty the cache when the application goes into pause. Creating object is costly and time consuming, keeping a few on hands for when you next will need them help a lot. This is what this factory caching infrastructure provide. It will create the object from the class defined on it, set the parent and the model as needed for all items a Factory create. The View has to release the Item using the release function of the Factory interface for all of this to work properly. This is copying what Elm_Genlist was doing for you in the background and bring Efl interface to parity. Reviewed-by: Felipe Magno de Almeida <felipe@expertisesolutions.com.br> Differential Revision: https://phab.enlightenment.org/D7443
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/efl/Efl.h1
-rw-r--r--src/lib/efl/interfaces/efl_cached_item.eo14
-rw-r--r--src/lib/efl/interfaces/efl_interfaces_main.c2
-rw-r--r--src/lib/efl/interfaces/meson.build1
-rw-r--r--src/lib/elementary/Efl_Ui.h2
-rw-r--r--src/lib/elementary/Elementary.h1
-rw-r--r--src/lib/elementary/efl_ui_caching_factory.c218
-rw-r--r--src/lib/elementary/efl_ui_caching_factory.eo48
-rw-r--r--src/lib/elementary/meson.build6
9 files changed, 291 insertions, 2 deletions
diff --git a/src/lib/efl/Efl.h b/src/lib/efl/Efl.h
index 7da806c779..af6154b766 100644
--- a/src/lib/efl/Efl.h
+++ b/src/lib/efl/Efl.h
@@ -149,6 +149,7 @@ typedef Efl_Gfx_Path_Command_Type Efl_Gfx_Path_Command;
#include "interfaces/efl_ui_model_connect.eo.h"
#include "interfaces/efl_ui_factory.eo.h"
#include "interfaces/efl_ui_format.eo.h"
+#include "interfaces/efl_cached_item.eo.h"
/* Observable interface */
#include "interfaces/efl_observer.eo.h"
diff --git a/src/lib/efl/interfaces/efl_cached_item.eo b/src/lib/efl/interfaces/efl_cached_item.eo
new file mode 100644
index 0000000000..ec1cd0ce1a
--- /dev/null
+++ b/src/lib/efl/interfaces/efl_cached_item.eo
@@ -0,0 +1,14 @@
+interface Efl.Cached.Item
+{
+ [[Efl Cached Item interface]]
+ methods {
+ @property memory_size {
+ get {
+ [[Get the memory size associated with an object.]]
+ }
+ values {
+ consumed: uint; [[Size of memory consumed by this object.]]
+ }
+ }
+ }
+}
diff --git a/src/lib/efl/interfaces/efl_interfaces_main.c b/src/lib/efl/interfaces/efl_interfaces_main.c
index 5133def96b..d1f9b80394 100644
--- a/src/lib/efl/interfaces/efl_interfaces_main.c
+++ b/src/lib/efl/interfaces/efl_interfaces_main.c
@@ -81,6 +81,8 @@
#include "interfaces/efl_ui_multi_selectable.eo.c"
#include "interfaces/efl_ui_zoom.eo.c"
+#include "interfaces/efl_cached_item.eo.c"
+
static void
_noref_death(void *data EINA_UNUSED, const Efl_Event *event)
{
diff --git a/src/lib/efl/interfaces/meson.build b/src/lib/efl/interfaces/meson.build
index a78cf4a209..13f71e4408 100644
--- a/src/lib/efl/interfaces/meson.build
+++ b/src/lib/efl/interfaces/meson.build
@@ -104,6 +104,7 @@ pub_eo_files = [
'efl_gfx_color_class.eo',
'efl_gfx_text_class.eo',
'efl_gfx_size_class.eo',
+ 'efl_cached_item.eo',
]
foreach eo_file : pub_eo_files
diff --git a/src/lib/elementary/Efl_Ui.h b/src/lib/elementary/Efl_Ui.h
index f4219975b8..d2e2297091 100644
--- a/src/lib/elementary/Efl_Ui.h
+++ b/src/lib/elementary/Efl_Ui.h
@@ -207,6 +207,8 @@ typedef Eo Efl_Ui_Focus_Manager;
# include <efl_ui_calendar.h>
# include <efl_ui_button_eo.h>
+# include "efl_ui_caching_factory.eo.h"
+
/* FIXME: Multibuttonentry must not use elm_widget_item */
# warning Efl.Ui.Multibutton is not available yet without Elementary.h
# if 0
diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h
index 9ae22e8d54..6050df643a 100644
--- a/src/lib/elementary/Elementary.h
+++ b/src/lib/elementary/Elementary.h
@@ -347,6 +347,7 @@ typedef Eo Efl_Ui_Focus_Manager;
# include <efl_ui_list_view_relayout.eo.h>
# include <efl_ui_list_view.eo.h>
# include <efl_ui_list_view_pan.eo.h>
+# include <efl_ui_caching_factory.eo.h>
# include <efl_ui_pan.eo.h>
# include <efl_ui_scroll_manager.eo.h>
# include <efl_ui_scroller.eo.h>
diff --git a/src/lib/elementary/efl_ui_caching_factory.c b/src/lib/elementary/efl_ui_caching_factory.c
new file mode 100644
index 0000000000..fe69e4209d
--- /dev/null
+++ b/src/lib/elementary/efl_ui_caching_factory.c
@@ -0,0 +1,218 @@
+#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"
diff --git a/src/lib/elementary/efl_ui_caching_factory.eo b/src/lib/elementary/efl_ui_caching_factory.eo
new file mode 100644
index 0000000000..c894e2cf22
--- /dev/null
+++ b/src/lib/elementary/efl_ui_caching_factory.eo
@@ -0,0 +1,48 @@
+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; }
+ }
+}
diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build
index ca6d1a4c3a..5e92402c06 100644
--- a/src/lib/elementary/meson.build
+++ b/src/lib/elementary/meson.build
@@ -280,7 +280,8 @@ pub_eo_files = [
'elm_view_form.eo',
'elm_web.eo',
'elm_widget_item.eo',
- 'efl_ui_text_part.eo'
+ 'efl_ui_text_part.eo',
+ 'efl_ui_caching_factory.eo',
]
foreach eo_file : pub_eo_files
@@ -903,7 +904,8 @@ elementary_src = [
'efl_ui_tab_pager.c',
'efl_ui_tab_bar.c',
'efl_ui_tab_page.c',
- 'efl_ui_widget_focus_manager.c'
+ 'efl_ui_widget_focus_manager.c',
+ 'efl_ui_caching_factory.c'
]
elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl]