1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <Elementary.h>
6 #include "elm_priv.h"
7
8 // This class rely on the parent class to do all the individual holding of value,
9 // it only compute the average size of an item and answer for that property alone.
10
11 // FIXME: handle child being removed
12 typedef struct _Efl_Ui_Average_Model_Data Efl_Ui_Average_Model_Data;
13 struct _Efl_Ui_Average_Model_Data
14 {
15 Efl_Ui_Average_Model_Data *parent;
16
17 struct {
18 unsigned long long width;
19 unsigned long long height;
20 unsigned long long wseen;
21 unsigned long long hseen;
22 } total;
23
24 Eina_Bool wseen : 1;
25 Eina_Bool hseen : 1;
26 };
27
28 typedef struct _Efl_Ui_Average_Model_Update Efl_Ui_Average_Model_Update;
29 struct _Efl_Ui_Average_Model_Update
30 {
31 unsigned long long *total;
32 unsigned long long *seen;
33 unsigned int previous;
34 };
35
36 static Eina_Value
_efl_ui_average_model_update(Eo * obj EINA_UNUSED,void * data,const Eina_Value v)37 _efl_ui_average_model_update(Eo *obj EINA_UNUSED, void *data, const Eina_Value v)
38 {
39 Efl_Ui_Average_Model_Update *request = data;
40 unsigned int now;
41
42 if (!eina_value_uint_convert(&v, &now))
43 goto on_error;
44
45 *(request->total) += now - request->previous;
46 if (request->seen) *(request->seen) += 1;
47
48 on_error:
49 return v;
50 }
51
52 static void
_efl_ui_average_model_clean(Eo * obj,void * data,const Eina_Future * dead_future EINA_UNUSED)53 _efl_ui_average_model_clean(Eo *obj, void *data, const Eina_Future *dead_future EINA_UNUSED)
54 {
55 free(data);
56
57 efl_unref(obj);
58 }
59
60 static Eina_Future *
_efl_ui_average_model_prepare(Eo * obj,unsigned long long * total,unsigned long long * seen,const char * property,Eina_Value * value)61 _efl_ui_average_model_prepare(Eo *obj,
62 unsigned long long *total, unsigned long long *seen,
63 const char *property, Eina_Value *value)
64 {
65 Efl_Ui_Average_Model_Update *update;
66 Eina_Value *previous;
67 Eina_Future *f;
68
69 update = calloc(1, sizeof (Efl_Ui_Average_Model_Update));
70 if (!update) return efl_loop_future_rejected(obj, ENOMEM);
71
72 previous = efl_model_property_get(obj, property);
73 if (eina_value_type_get(previous) == EINA_VALUE_TYPE_ERROR)
74 {
75 Eina_Error err;
76
77 // Check the case when that property hasn't been set before
78 if (!eina_value_error_convert(previous, &err))
79 goto on_error;
80 if (err != EAGAIN) goto on_error;
81 }
82 else if (!eina_value_uint_convert(previous, &update->previous))
83 goto on_error;
84 eina_value_free(previous);
85
86 update->total = total;
87 update->seen = seen;
88
89 // As we are operating asynchronously and we want to make sure that the object
90 // survive until the transaction is commited, we will ref the object here
91 // and unref on clean. This is necessary so that a nested call of property_set
92 // on a model returned by children_slice_get, the user doesn't have to keep a
93 // reference around to do the same. It shouldn't create any problem as this
94 // future would be cancelled automatically when the parent object get destroyed.
95 efl_ref(obj);
96
97 // We have to make the change after we fetch the old value, otherwise, well, no old value left
98 f = efl_model_property_set(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS), property, value);
99
100 return efl_future_then(obj, f,
101 .success = _efl_ui_average_model_update,
102 .free = _efl_ui_average_model_clean,
103 .data = update);
104 on_error:
105 eina_value_free(previous);
106 free(update);
107 return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
108 }
109
110 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)111 _efl_ui_average_model_efl_model_property_set(Eo *obj, Efl_Ui_Average_Model_Data *pd, const char *property, Eina_Value *value)
112 {
113 Eina_Future *f = NULL;
114
115 if (!pd->parent) goto end;
116
117 // In vertical list mode we do not need to compute the average width size
118 /* if (eina_streq(property, _efl_model_property_selfw)) */
119 /* { */
120 /* f = _efl_ui_average_model_prepare(obj, &pd->parent->total.width, */
121 /* pd->wseen ? NULL : &pd->parent->total.wseen, */
122 /* property, value, EINA_TRUE); */
123 /* pd->wseen = EINA_TRUE; */
124 /* } */
125 if (eina_streq(property, _efl_model_property_selfh))
126 {
127 f = _efl_ui_average_model_prepare(obj, &pd->parent->total.height,
128 pd->hseen ? NULL : &pd->parent->total.hseen,
129 property, value);
130 pd->hseen = EINA_TRUE;
131 }
132
133 end:
134 if (!f)
135 f = efl_model_property_set(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS), property, value);
136
137 return f;
138 }
139
140 static inline Eina_Value *
_efl_ui_average_model_compute(const Eo * obj,Eina_Value * r,unsigned long long total,unsigned long long seen)141 _efl_ui_average_model_compute(const Eo *obj, Eina_Value *r, unsigned long long total, unsigned long long seen)
142 {
143 unsigned int count;
144
145 eina_value_free(r);
146
147 // Protection against divide by zero
148 if (!seen) return eina_value_uint_new(0);
149
150 count = efl_model_children_count_get(obj);
151 // We are doing the multiply first in an attempt to not reduce the precision to early on.
152 return eina_value_uint_new((total * count) / seen);
153 }
154
155 static Eina_Value *
_efl_ui_average_model_efl_model_property_get(const Eo * obj,Efl_Ui_Average_Model_Data * pd,const char * property)156 _efl_ui_average_model_efl_model_property_get(const Eo *obj, Efl_Ui_Average_Model_Data *pd, const char *property)
157 {
158 const Eina_Value_Type *t;
159 Eina_Value *r;
160
161 r = efl_model_property_get(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS), property);
162 if (!r) return r;
163
164 // We are checking that the parent class was able to provide an answer to the request for property "Total.Width"
165 // or "Total.Height" which means that we are an object that should compute its size. This avoid computing the
166 // pointer to the parent object.
167 t = eina_value_type_get(r);
168 if (t == EINA_VALUE_TYPE_UINT)
169 {
170 if (eina_streq(property, _efl_model_property_totalh))
171 r = _efl_ui_average_model_compute(obj, r, pd->total.height, pd->total.hseen);
172 // We do not need to average the width in vertical list mode as this is done by the parent class
173 /* if (eina_streq(property, _efl_model_property_totalw)) */
174 /* r = _efl_ui_average_model_compute(obj, r, pd->total.width, pd->total.wseen); */
175 }
176
177 return r;
178 }
179
180 static Efl_Object *
_efl_ui_average_model_efl_object_constructor(Eo * obj,Efl_Ui_Average_Model_Data * pd)181 _efl_ui_average_model_efl_object_constructor(Eo *obj, Efl_Ui_Average_Model_Data *pd)
182 {
183 Eo *parent = efl_parent_get(obj);
184
185 if (parent && efl_isa(parent, EFL_UI_AVERAGE_MODEL_CLASS))
186 pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_AVERAGE_MODEL_CLASS);
187
188 return efl_constructor(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS));
189 }
190
191 #include "efl_ui_average_model.eo.c"
192