From 33c6c135b1e584a3ac077595fd3e58f0371e58eb Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 18 Jan 2019 18:01:23 -0800 Subject: [PATCH] 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 Reviewed-by: Larry Lira Differential Revision: https://phab.enlightenment.org/D7705 --- src/lib/elementary/efl_ui_caching_factory.c | 182 ++++++++++++++++++++++----- src/lib/elementary/efl_ui_caching_factory.eo | 18 ++- src/lib/elementary/efl_ui_image_factory.c | 2 +- src/lib/elementary/efl_ui_layout_factory.c | 2 +- 4 files changed, 159 insertions(+), 45 deletions(-) diff --git a/src/lib/elementary/efl_ui_caching_factory.c b/src/lib/elementary/efl_ui_caching_factory.c index b25d6ba..49a316e 100644 --- a/src/lib/elementary/efl_ui_caching_factory.c +++ b/src/lib/elementary/efl_ui_caching_factory.c @@ -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,9 +78,49 @@ _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); + 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; - efl_del(entity); + 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 * @@ -65,59 +128,88 @@ _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" diff --git a/src/lib/elementary/efl_ui_caching_factory.eo b/src/lib/elementary/efl_ui_caching_factory.eo index c7e2f2b..ad97a12 100644 --- a/src/lib/elementary/efl_ui_caching_factory.eo +++ b/src/lib/elementary/efl_ui_caching_factory.eo @@ -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; } } diff --git a/src/lib/elementary/efl_ui_image_factory.c b/src/lib/elementary/efl_ui_image_factory.c index ce4d892..4cd08cd 100644 --- a/src/lib/elementary/efl_ui_image_factory.c +++ b/src/lib/elementary/efl_ui_image_factory.c @@ -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; diff --git a/src/lib/elementary/efl_ui_layout_factory.c b/src/lib/elementary/efl_ui_layout_factory.c index 150fd67..0d81136 100644 --- a/src/lib/elementary/efl_ui_layout_factory.c +++ b/src/lib/elementary/efl_ui_layout_factory.c @@ -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)); -- 2.7.4