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
This commit is contained in:
Cedric BAIL 2018-12-06 16:38:47 -08:00
parent 7245567fe6
commit 4fe4f76b8c
11 changed files with 294 additions and 2 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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.]]
}
}
}
}

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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; }
}
}

View File

@ -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]