elementary: update Efl.Ui.Caching_Factory to rely on Efl.Ui.Widget_Factory for Efl.Ui.Widget.

I am not sure we really need Efl.Ui.Caching_Factory after this, but in case we want a Caching_Factory
for non Efl.Ui.Widget, this is supported by this patch (And is the reason why most of the complexity).
The benefit from inheriting from Efl.Ui.Widget_Factory allow to get the style of an Efl.Ui.Widget
defined by an Efl.Model properly done at creation time.

Reviewed-by: SangHyeon Jade Lee <sh10233.lee@samsung.com>
Reviewed-by: Larry Lira <larry@expertisesolutions.com.br>
Differential Revision: https://phab.enlightenment.org/D7705
This commit is contained in:
Cedric BAIL 2019-01-18 18:01:23 -08:00
parent aa0e89a590
commit c910679917
4 changed files with 160 additions and 46 deletions

View File

@ -6,18 +6,33 @@
#include "elm_priv.h"
typedef struct _Efl_Ui_Caching_Factory_Data Efl_Ui_Caching_Factory_Data;
typedef struct _Efl_Ui_Caching_Factory_Request Efl_Ui_Caching_Factory_Request;
struct _Efl_Ui_Caching_Factory_Data
{
const Efl_Class *klass;
Eina_Stringshare *style;
// 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;
Eina_Hash *lookup;
struct {
unsigned int memory;
unsigned int items;
} limit, current;
Eina_Bool invalidated : 1;
};
struct _Efl_Ui_Caching_Factory_Request
{
Efl_Ui_Caching_Factory_Data *pd;
Eo *parent;
Efl_Model *model;
};
// Clear the cache until it meet the constraint
@ -33,7 +48,15 @@ _efl_ui_caching_factory_remove(Efl_Ui_Caching_Factory_Data *pd, Eina_List *l, Ef
}
static void
_efl_ui_caching_factory_flush(Efl_Ui_Caching_Factory_Data *pd)
_efl_ui_caching_factory_item_del(Eo *obj, Efl_Ui_Caching_Factory_Data *pd,
Efl_Gfx_Entity *entity)
{
if (pd->klass) efl_del(entity);
else efl_ui_factory_release(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS), entity);
}
static void
_efl_ui_caching_factory_flush(Eo *obj, Efl_Ui_Caching_Factory_Data *pd)
{
while (pd->limit.items != 0 &&
pd->current.items > pd->limit.items)
@ -43,8 +66,8 @@ _efl_ui_caching_factory_flush(Efl_Ui_Caching_Factory_Data *pd)
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);
if (pd->lookup) eina_hash_del(pd->lookup, efl_ui_widget_style_get(entity), entity);
_efl_ui_caching_factory_item_del(obj, pd, entity);
}
while (pd->limit.memory != 0 &&
@ -55,69 +78,138 @@ _efl_ui_caching_factory_flush(Efl_Ui_Caching_Factory_Data *pd)
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);
if (pd->lookup) eina_hash_del(pd->lookup, efl_ui_widget_style_get(entity), entity);
_efl_ui_caching_factory_item_del(obj, pd, entity);
}
}
static Eina_Value
_efl_ui_caching_factory_create_then(Eo *obj EINA_UNUSED, void *data, const Eina_Value v)
{
Efl_Ui_Caching_Factory_Request *r = data;
Efl_Ui_Widget *w;
const char *string = NULL;
if (!eina_value_string_get(&v, &string))
return eina_value_error_init(EFL_MODEL_ERROR_NOT_SUPPORTED);
w = eina_hash_find(r->pd->lookup, string);
if (!w)
{
Eina_Future *f;
// No object of that style in the cache, need to create a new one
f = efl_ui_factory_create(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS),
r->model, r->parent);
return eina_future_as_value(f);
}
eina_hash_del(r->pd->lookup, string, w);
_efl_ui_caching_factory_remove(r->pd, r->pd->cache, w);
efl_parent_set(w, r->parent);
efl_ui_view_model_set(w, r->model);
return eina_value_object_init(w);
}
static void
_efl_ui_caching_factory_create_cleanup(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
{
Efl_Ui_Caching_Factory_Request *r = data;
efl_unref(r->model);
efl_unref(r->parent);
free(r);
}
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;
Efl_Gfx_Entity *w = NULL;
if (pd->cache)
{
r = eina_list_data_get(pd->cache);
if (pd->style && !pd->klass)
{
Efl_Ui_Caching_Factory_Request *r;
_efl_ui_caching_factory_remove(pd, pd->cache, r);
r = calloc(1, sizeof (Efl_Ui_Caching_Factory_Request));
if (!r) return efl_loop_future_rejected(obj, ENOMEM);
efl_parent_set(r, parent);
r->pd = pd;
r->parent = efl_ref(parent);
r->model = efl_ref(model);
return efl_future_then(obj, efl_model_property_ready_get(obj, pd->style),
.success = _efl_ui_caching_factory_create_then,
.data = r,
.free = _efl_ui_caching_factory_create_cleanup);
}
w = eina_list_data_get(pd->cache);
_efl_ui_caching_factory_remove(pd, pd->cache, w);
efl_parent_set(w, parent);
}
else
if (!w)
{
r = efl_add(pd->klass, parent);
if (pd->klass) w = efl_add(pd->klass, parent);
else return efl_ui_factory_create(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS),
model, parent);
}
efl_ui_view_model_set(r, model);
efl_ui_view_model_set(w, model);
return efl_loop_future_resolved(obj, eina_value_object_init(r));
return efl_loop_future_resolved(obj, eina_value_object_init(w));
}
static void
_efl_ui_caching_factory_item_class_set(Eo *obj,
Efl_Ui_Caching_Factory_Data *pd,
const Efl_Object *klass)
_efl_ui_caching_factory_efl_ui_widget_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))
if (efl_isa(klass, EFL_UI_VIEW_INTERFACE) &&
!efl_isa(klass, EFL_UI_WIDGET_CLASS))
{
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 ;
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 nor '%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),
efl_class_name_get(EFL_UI_WIDGET_CLASS),
efl_class_name_get(EFL_UI_VIEW_INTERFACE));
return ;
}
pd->klass = klass;
return;
}
pd->klass = klass;
efl_ui_widget_factory_item_class_set(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS), klass);
}
static const Efl_Object *
_efl_ui_caching_factory_item_class_get(const Eo *obj EINA_UNUSED,
Efl_Ui_Caching_Factory_Data *pd)
_efl_ui_caching_factory_efl_ui_widget_factory_item_class_get(const Eo *obj,
Efl_Ui_Caching_Factory_Data *pd)
{
return pd->klass;
if (pd->klass) return pd->klass;
return efl_ui_widget_factory_item_class_get(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS));
}
static void
_efl_ui_caching_factory_memory_limit_set(Eo *obj EINA_UNUSED,
_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);
_efl_ui_caching_factory_flush(obj, pd);
}
static unsigned int
@ -128,13 +220,13 @@ _efl_ui_caching_factory_memory_limit_get(const Eo *obj EINA_UNUSED,
}
static void
_efl_ui_caching_factory_items_limit_set(Eo *obj EINA_UNUSED,
_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);
_efl_ui_caching_factory_flush(obj, pd);
}
static unsigned int
@ -149,6 +241,13 @@ _efl_ui_caching_factory_efl_ui_factory_release(Eo *obj,
Efl_Ui_Caching_Factory_Data *pd,
Efl_Gfx_Entity *ui_view)
{
// Are we invalidated ?
if (pd->invalidated)
{
_efl_ui_caching_factory_item_del(obj, pd, ui_view);
return;
}
// Change parent, disconnect the object and make it invisible
efl_parent_set(ui_view, obj);
efl_gfx_entity_visible_set(ui_view, EINA_FALSE);
@ -161,8 +260,15 @@ _efl_ui_caching_factory_efl_ui_factory_release(Eo *obj,
if (efl_isa(ui_view, EFL_CACHED_ITEM_INTERFACE))
pd->current.memory += efl_cached_item_memory_size_get(ui_view);
// Fill lookup
if (!pd->klass && efl_ui_widget_style_get(ui_view))
{
if (!pd->lookup) pd->lookup = eina_hash_string_djb2_new(NULL);
eina_hash_direct_add(pd->lookup, efl_ui_widget_style_get(ui_view), ui_view);
}
// And check if the cache need some triming
_efl_ui_caching_factory_flush(pd);
_efl_ui_caching_factory_flush(obj, pd);
}
static void
@ -171,6 +277,9 @@ _efl_ui_caching_factory_efl_object_invalidate(Eo *obj EINA_UNUSED,
{
// 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);
eina_hash_free(pd->lookup);
pd->lookup = NULL;
pd->invalidated = EINA_TRUE;
}
static Efl_App *
@ -215,4 +324,13 @@ _efl_ui_caching_factory_efl_object_parent_set(Eo *obj, Efl_Ui_Caching_Factory_Da
if (a) efl_event_callback_add(a, EFL_APP_EVENT_PAUSE, _efl_ui_caching_factory_pause, pd);
}
static void
_efl_ui_caching_factory_efl_ui_model_connect_connect(Eo *obj, Efl_Ui_Caching_Factory_Data *pd,
const char *name, const char *property)
{
if (!strcmp(name, "style"))
eina_stringshare_replace(&pd->style, property);
efl_ui_model_connect(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS), name, property);
}
#include "efl_ui_caching_factory.eo.c"

View File

@ -1,9 +1,11 @@
class Efl.Ui.Caching_Factory extends Efl.Loop_Consumer implements Efl.Ui.Factory
class Efl.Ui.Caching_Factory extends Efl.Ui.Widget_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.
This factory handles caching of one type of object that must be an @Efl.Gfx.Entity with an @Efl.Ui.View interface defined.
This factory will rely on its parent class @Efl.Ui.Widget_Factory for creating the subset of class that match @Efl.Ui.Widget
interface.
The factory will 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
@ -13,14 +15,6 @@ class Efl.Ui.Caching_Factory extends Efl.Loop_Consumer implements Efl.Ui.Factory
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 {}
@ -42,6 +36,8 @@ class Efl.Ui.Caching_Factory extends Efl.Loop_Consumer implements Efl.Ui.Factory
implements {
Efl.Ui.Factory.create;
Efl.Ui.Factory.release;
Efl.Ui.Model.Connect.connect;
Efl.Ui.Widget_Factory.item_class { get; set; }
Efl.Object.invalidate;
Efl.Object.parent { set; }
}

View File

@ -17,7 +17,7 @@ EOLIAN static Eo *
_efl_ui_image_factory_efl_object_constructor(Eo *obj, Efl_Ui_Image_Factory_Data *pd)
{
obj = efl_constructor(efl_super(obj, MY_CLASS));
efl_ui_caching_factory_item_class_set(obj, EFL_UI_IMAGE_CLASS);
efl_ui_widget_factory_item_class_set(obj, EFL_UI_IMAGE_CLASS);
pd->property = NULL;

View File

@ -45,7 +45,7 @@ _efl_ui_layout_factory_efl_object_constructor(Eo *obj, Efl_Ui_Layout_Factory_Dat
{
obj = efl_constructor(efl_super(obj, MY_CLASS));
efl_ui_caching_factory_item_class_set(obj, EFL_UI_LAYOUT_CLASS);
efl_ui_widget_factory_item_class_set(obj, EFL_UI_LAYOUT_CLASS);
pd->connects = eina_hash_stringshared_new(EINA_FREE_CB(eina_stringshare_del));
pd->factory_connects = eina_hash_stringshared_new(EINA_FREE_CB(efl_unref));