diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am index 6fe217a956..b69f3b63b0 100644 --- a/src/Makefile_Efl.am +++ b/src/Makefile_Efl.am @@ -78,6 +78,7 @@ efl_eolian_files = \ lib/efl/interfaces/efl_gfx_text_class.eo \ lib/efl/interfaces/efl_gfx_size_class.eo \ lib/efl/interfaces/efl_interpolator.eo \ + lib/efl/interfaces/efl_cached_item.eo \ $(efl_eolian_legacy_files) \ $(NULL) diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index 648a521b3c..48e9e7b897 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -134,6 +134,7 @@ elm_public_eolian_files = \ lib/elementary/efl_ui_tab_page_part_tab.eo \ lib/elementary/efl_ui_widget_focus_manager.eo \ lib/elementary/efl_ui_text_part.eo \ + lib/elementary/efl_ui_caching_factory.eo \ $(NULL) # More public files -- FIXME @@ -884,6 +885,7 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/efl_ui_tab_bar.c \ lib/elementary/efl_ui_tab_page.c \ lib/elementary/efl_ui_widget_focus_manager.c \ + lib/elementary/efl_ui_caching_factory.c \ $(NULL) 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 # include +# 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 # include # include +# include # include # include # include 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 +#endif + +#include +#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]