#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_Average_Model_Data Efl_Ui_Average_Model_Data; struct _Efl_Ui_Average_Model_Data { Efl_Ui_Average_Model_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_Average_Model_Update Efl_Ui_Average_Model_Update; struct _Efl_Ui_Average_Model_Update { unsigned long long *total; unsigned long long *seen; unsigned int previous; }; static Eina_Value _efl_ui_average_model_update(Eo *obj EINA_UNUSED, void *data, const Eina_Value v) { Efl_Ui_Average_Model_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_average_model_clean(Eo *obj, void *data, const Eina_Future *dead_future EINA_UNUSED) { free(data); efl_unref(obj); } static Eina_Future * _efl_ui_average_model_prepare(Eo *obj, unsigned long long *total, unsigned long long *seen, const char *property, Eina_Value *value) { Efl_Ui_Average_Model_Update *update; Eina_Value *previous; Eina_Future *f; update = calloc(1, sizeof (Efl_Ui_Average_Model_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; // As we are operating asynchronously and we want to make sure that the object // survive until the transaction is commited, we will ref the object here // and unref on clean. This is necessary so that a nested call of property_set // on a model returned by children_slice_get, the user doesn't have to keep a // reference around to do the same. It shouldn't create any problem as this // future would be cancelled automatically when the parent object get destroyed. efl_ref(obj); // 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_AVERAGE_MODEL_CLASS), property, value); return efl_future_then(obj, f, .success = _efl_ui_average_model_update, .free = _efl_ui_average_model_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_average_model_efl_model_property_set(Eo *obj, Efl_Ui_Average_Model_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 (eina_streq(property, _efl_model_property_selfw)) */ /* { */ /* f = _efl_ui_average_model_prepare(obj, &pd->parent->total.width, */ /* pd->wseen ? NULL : &pd->parent->total.wseen, */ /* property, value, EINA_TRUE); */ /* pd->wseen = EINA_TRUE; */ /* } */ if (eina_streq(property, _efl_model_property_selfh)) { f = _efl_ui_average_model_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_AVERAGE_MODEL_CLASS), property, value); return f; } static inline Eina_Value * _efl_ui_average_model_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_average_model_efl_model_property_get(const Eo *obj, Efl_Ui_Average_Model_Data *pd, const char *property) { const Eina_Value_Type *t; Eina_Value *r; r = efl_model_property_get(efl_super(obj, EFL_UI_AVERAGE_MODEL_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 (eina_streq(property, _efl_model_property_totalh)) r = _efl_ui_average_model_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 (eina_streq(property, _efl_model_property_totalw)) */ /* r = _efl_ui_average_model_compute(obj, r, pd->total.width, pd->total.wseen); */ } return r; } static Efl_Object * _efl_ui_average_model_efl_object_constructor(Eo *obj, Efl_Ui_Average_Model_Data *pd) { Eo *parent = efl_parent_get(obj); if (parent && efl_isa(parent, EFL_UI_AVERAGE_MODEL_CLASS)) pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_AVERAGE_MODEL_CLASS); return efl_constructor(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS)); } #include "efl_ui_average_model.eo.c"