diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index f9485bb9b7..ed2a629d14 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -179,6 +179,7 @@ elm_private_eolian_files = \ lib/elementary/efl_ui_model_size.eo \ lib/elementary/efl_ui_model_homogeneous.eo \ lib/elementary/efl_ui_model_exact.eo \ + lib/elementary/efl_ui_model_average.eo \ $(NULL) # Legacy classes - not part of public EO API @@ -892,6 +893,7 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/efl_ui_model_size.c \ lib/elementary/efl_ui_model_homogeneous.c \ lib/elementary/efl_ui_model_exact.c \ + lib/elementary/efl_ui_model_average.c \ $(NULL) diff --git a/src/lib/elementary/efl_ui_model_average.c b/src/lib/elementary/efl_ui_model_average.c new file mode 100644 index 0000000000..be4dca59a1 --- /dev/null +++ b/src/lib/elementary/efl_ui_model_average.c @@ -0,0 +1,181 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "elm_priv.h" + +// This class rely on the parent class to do all the individual holding of value, +// it only compute the average size of an item and answer for that property alone. + +// FIXME: handle child being removed +typedef struct _Efl_Ui_Model_Average_Data Efl_Ui_Model_Average_Data; +struct _Efl_Ui_Model_Average_Data +{ + Efl_Ui_Model_Average_Data *parent; + + struct { + unsigned long long width; + unsigned long long height; + unsigned long long wseen; + unsigned long long hseen; + } total; + + Eina_Bool wseen : 1; + Eina_Bool hseen : 1; +}; + +typedef struct _Efl_Ui_Model_Average_Update Efl_Ui_Model_Average_Update; +struct _Efl_Ui_Model_Average_Update +{ + unsigned long long *total; + unsigned long long *seen; + unsigned int previous; +}; + +static Eina_Value +_efl_ui_model_average_update(Eo *obj EINA_UNUSED, void *data, const Eina_Value v) +{ + Efl_Ui_Model_Average_Update *request = data; + unsigned int now; + + if (!eina_value_uint_convert(&v, &now)) + goto on_error; + + *(request->total) += now - request->previous; + if (request->seen) *(request->seen) += 1; + + on_error: + return v; +} + +static void +_efl_ui_model_average_clean(Eo *obj EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED) +{ + free(data); +} + +static Eina_Future * +_efl_ui_model_average_prepare(Eo *obj, + unsigned long long *total, unsigned long long *seen, + const char *property, Eina_Value *value) +{ + Efl_Ui_Model_Average_Update *update; + Eina_Value *previous; + Eina_Future *f; + + update = calloc(1, sizeof (Efl_Ui_Model_Average_Update)); + if (!update) return efl_loop_future_rejected(obj, ENOMEM); + + previous = efl_model_property_get(obj, property); + if (eina_value_type_get(previous) == EINA_VALUE_TYPE_ERROR) + { + Eina_Error err; + + // Check the case when that property hasn't been set before + if (!eina_value_error_convert(previous, &err)) + goto on_error; + if (err != EAGAIN) goto on_error; + } + else if (!eina_value_uint_convert(previous, &update->previous)) + goto on_error; + eina_value_free(previous); + + update->total = total; + update->seen = seen; + + // We have to make the change after we fetch the old value, otherwise, well, no old value left + f = efl_model_property_set(efl_super(obj, EFL_UI_MODEL_AVERAGE_CLASS), property, value); + + return efl_future_then(obj, f, + .success = _efl_ui_model_average_update, + .free = _efl_ui_model_average_clean, + .data = update); + on_error: + eina_value_free(previous); + free(update); + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE); +} + +static Eina_Future * +_efl_ui_model_average_efl_model_property_set(Eo *obj, Efl_Ui_Model_Average_Data *pd, const char *property, Eina_Value *value) +{ + Eina_Future *f = NULL; + + if (!pd->parent) goto end; + + // In vertical list mode we do not need to compute the average width size + /* if (!strcmp(property, _efl_model_property_selfw)) */ + /* { */ + /* f = _efl_ui_model_average_prepare(obj, &pd->parent->total.width, */ + /* pd->wseen ? NULL : &pd->parent->total.wseen, */ + /* property, value, EINA_TRUE); */ + /* pd->wseen = EINA_TRUE; */ + /* } */ + if (!strcmp(property, _efl_model_property_selfh)) + { + f = _efl_ui_model_average_prepare(obj, &pd->parent->total.height, + pd->hseen ? NULL : &pd->parent->total.hseen, + property, value); + pd->hseen = EINA_TRUE; + } + + end: + if (!f) + f = efl_model_property_set(efl_super(obj, EFL_UI_MODEL_AVERAGE_CLASS), property, value); + + return f; +} + +static inline Eina_Value * +_efl_ui_model_average_compute(const Eo *obj, Eina_Value *r, unsigned long long total, unsigned long long seen) +{ + unsigned int count; + + eina_value_free(r); + + // Protection against divide by zero + if (!seen) return eina_value_uint_new(0); + + count = efl_model_children_count_get(obj); + // We are doing the multiply first in an attempt to not reduce the precision to early on. + return eina_value_uint_new((total * count) / seen); +} + +static Eina_Value * +_efl_ui_model_average_efl_model_property_get(const Eo *obj, Efl_Ui_Model_Average_Data *pd, const char *property) +{ + const Eina_Value_Type *t; + Eina_Value *r; + + r = efl_model_property_get(efl_super(obj, EFL_UI_MODEL_AVERAGE_CLASS), property); + if (!r) return r; + + // We are checking that the parent class was able to provide an answer to the request for property "Total.Width" + // or "Total.Height" which means that we are an object that should compute its size. This avoid computing the + // pointer to the parent object. + t = eina_value_type_get(r); + if (t == EINA_VALUE_TYPE_UINT) + { + if (!strcmp(property, _efl_model_property_totalh)) + r = _efl_ui_model_average_compute(obj, r, pd->total.height, pd->total.hseen); + // We do not need to average the width in vertical list mode as this is done by the parent class + /* if (!strcmp(property, _efl_model_property_totalw)) */ + /* r = _efl_ui_model_average_compute(obj, r, pd->total.width, pd->total.wseen); */ + } + + return r; +} + +static Efl_Object * +_efl_ui_model_average_efl_object_constructor(Eo *obj, Efl_Ui_Model_Average_Data *pd) +{ + Eo *parent = efl_parent_get(obj); + + if (parent && efl_isa(parent, EFL_UI_MODEL_AVERAGE_CLASS)) + pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_MODEL_AVERAGE_CLASS); + + return efl_constructor(efl_super(obj, EFL_UI_MODEL_AVERAGE_CLASS)); +} + +#include "efl_ui_model_average.eo.c" diff --git a/src/lib/elementary/efl_ui_model_average.eo b/src/lib/elementary/efl_ui_model_average.eo new file mode 100644 index 0000000000..1af87f986e --- /dev/null +++ b/src/lib/elementary/efl_ui_model_average.eo @@ -0,0 +1,19 @@ +class Efl.Ui.Model_Average extends Efl.Ui.Model_Exact +{ + [[Class to be used to store object item size for List/Grid View. + + This model provide the same feature as @Efl.Ui.Model_Exact except for the + @Efl.Model.property "$total.width" and "$total.height" which reflect an + estimated value of the total size by using the currently know size from its + children as an average size for all its children. As more children fill + "$self.width" and "$self.height", this model will figure out a more precise + answer. Once all children size is known, the result will be exact and the same + as @Efl.Ui.Model_Exact. + + This model only supporting vertical list at this point.]] + + implements { + Efl.Object.constructor; + Efl.Model.property { set; get; } + } +} \ No newline at end of file diff --git a/src/lib/elementary/elm_priv.h b/src/lib/elementary/elm_priv.h index a5f187df17..e0b284b6b6 100644 --- a/src/lib/elementary/elm_priv.h +++ b/src/lib/elementary/elm_priv.h @@ -72,6 +72,7 @@ # include "efl_ui_model_size.eo.h" # include "efl_ui_model_homogeneous.eo.h" # include "efl_ui_model_exact.eo.h" +# include "efl_ui_model_average.eo.h" extern const char *_efl_model_property_itemw; extern const char *_efl_model_property_itemh; diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index c4453b7c9e..f9c508de0b 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -340,6 +340,7 @@ priv_eo_files = [ 'efl_ui_model_size.eo', 'efl_ui_model_homogeneous.eo', 'efl_ui_model_exact.eo', + 'efl_ui_model_average.eo', ] priv_eo_file_target = [] @@ -911,6 +912,7 @@ elementary_src = [ 'efl_ui_model_size.c', 'efl_ui_model_homogeneous.c', 'efl_ui_model_exact.c', + 'efl_ui_model_average.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]