efl/src/lib/elementary/efl_ui_average_model.c

192 lines
6.2 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Elementary.h>
#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"