1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #define EFL_UI_WIDGET_PROTECTED
6 #define EFL_PART_PROTECTED
7 #define EFL_UI_FACTORY_PROTECTED
8 
9 #include <Efl_Ui.h>
10 #include "elm_priv.h"
11 #include "efl_ui_property_bind_part.eo.h"
12 
13 typedef struct _Efl_Ui_Widget_Factory_Data Efl_Ui_Widget_Factory_Data;
14 typedef struct _Efl_Ui_Widget_Factory_Request Efl_Ui_Widget_Factory_Request;
15 typedef struct _Efl_Ui_Bind_Part_Data Efl_Ui_Bind_Part_Data;
16 typedef struct _Efl_Ui_Property_Bind_Data Efl_Ui_Property_Bind_Data;
17 
18 struct _Efl_Ui_Property_Bind_Data
19 {
20    Eina_Stringshare *part_property;
21    Eina_Stringshare *model_property;
22 };
23 
24 struct _Efl_Ui_Bind_Part_Data
25 {
26    Eina_Stringshare *part;
27 
28    Eina_List *properties;
29 };
30 
31 struct _Efl_Ui_Widget_Factory_Data
32 {
33    const Efl_Class *klass;
34 
35    Efl_Ui_Widget *parenting_widget;
36 
37    Eina_Hash *parts;
38 
39    Eina_Stringshare *default_property;
40 
41    Eina_Stringshare *style;
42 };
43 
44 struct _Efl_Ui_Widget_Factory_Request
45 {
46    Efl_Ui_Widget_Factory_Data *pd;
47    Efl_Ui_Factory *factory;
48 };
49 
50 static Efl_Object *
_efl_ui_widget_factory_efl_object_finalize(Eo * obj,Efl_Ui_Widget_Factory_Data * pd)51 _efl_ui_widget_factory_efl_object_finalize(Eo *obj, Efl_Ui_Widget_Factory_Data *pd)
52 {
53    pd->parenting_widget = efl_provider_find(obj, EFL_UI_WIDGET_CLASS);
54    if (!pd->parenting_widget)
55      {
56         ERR("Widget_Factory requires a Widget as parent.");
57         return NULL;
58      }
59 
60    return efl_finalize(efl_super(obj, EFL_UI_WIDGET_FACTORY_CLASS));
61 }
62 
63 Efl_Ui_Win *
efl_ui_widget_factory_widget_get(Efl_Ui_Widget_Factory * factory)64 efl_ui_widget_factory_widget_get(Efl_Ui_Widget_Factory *factory)
65 {
66    Efl_Ui_Widget_Factory_Data *pd = efl_data_scope_get(factory, EFL_UI_WIDGET_FACTORY_CLASS);
67 
68    return pd->parenting_widget;
69 }
70 
71 static void
_efl_ui_widget_factory_item_class_set(Eo * obj,Efl_Ui_Widget_Factory_Data * pd,const Efl_Class * klass)72 _efl_ui_widget_factory_item_class_set(Eo *obj, Efl_Ui_Widget_Factory_Data *pd,
73                                       const Efl_Class *klass)
74 {
75    if (!efl_isa(klass, EFL_UI_VIEW_INTERFACE) ||
76        !efl_isa(klass, EFL_UI_WIDGET_CLASS))
77      {
78         ERR("Provided class '%s' for factory '%s' doesn't implement '%s' and '%s' interfaces.",
79             efl_class_name_get(klass),
80             efl_class_name_get(obj),
81             efl_class_name_get(EFL_UI_WIDGET_CLASS),
82             efl_class_name_get(EFL_UI_VIEW_INTERFACE));
83         return ;
84      }
85    pd->klass = klass;
86 }
87 
88 static const Efl_Class *
_efl_ui_widget_factory_item_class_get(const Eo * obj EINA_UNUSED,Efl_Ui_Widget_Factory_Data * pd)89 _efl_ui_widget_factory_item_class_get(const Eo *obj EINA_UNUSED,
90                                       Efl_Ui_Widget_Factory_Data *pd)
91 {
92    return pd->klass;
93 }
94 
95 static void
_efl_ui_widget_factory_constructing(void * data EINA_UNUSED,const Efl_Event * ev)96 _efl_ui_widget_factory_constructing(void *data EINA_UNUSED, const Efl_Event *ev)
97 {
98    Efl_Gfx_Entity *ui_view = ev->info;
99    const Efl_Model *model;
100    Eina_Value *width, *height;
101 
102    model = efl_ui_view_model_get(ui_view);
103 
104    // Enable recalculate in case we do not know the size of the item
105    efl_canvas_group_need_recalculate_set(ui_view, EINA_TRUE);
106 
107    // Fetch min size from model if available to avoid recalculcating it
108    width = efl_model_property_get(model, "self.width");
109    height = efl_model_property_get(model, "self.height");
110    if (eina_value_type_get(width) != EINA_VALUE_TYPE_ERROR &&
111        eina_value_type_get(height) != EINA_VALUE_TYPE_ERROR)
112      {
113         Eina_Size2D s;
114 
115         if (!eina_value_int_convert(width, &s.w)) s.w = 0;
116         if (!eina_value_int_convert(height, &s.h)) s.h = 0;
117 
118         efl_gfx_hint_size_min_set(ui_view, s);
119         efl_canvas_group_need_recalculate_set(ui_view, EINA_FALSE);
120         if (efl_isa(ui_view, EFL_UI_ITEM_CLASS)) efl_ui_item_calc_locked_set(ui_view, EINA_TRUE);
121      }
122    eina_value_free(width);
123    eina_value_free(height);
124 
125    efl_key_data_set(ui_view, "efl.ui.widget.factory.size_check", (void*)EINA_TRUE);
126 }
127 
128 
129 static void
_efl_ui_widget_factory_building(void * data,const Efl_Event * ev)130 _efl_ui_widget_factory_building(void *data, const Efl_Event *ev)
131 {
132    Efl_Gfx_Entity *ui_view = ev->info;
133    Efl_Ui_Widget_Factory_Data *pd = data;
134    const Efl_Model *model;
135    Eina_Value *property;
136    Efl_Ui_Bind_Part_Data *bpd;
137    Eina_Iterator *it;
138    char *style;
139 
140    model = efl_ui_view_model_get(ui_view);
141 
142    // Check property size only if not checked yet
143    if (!efl_key_data_get(ui_view, "efl.ui.widget.factory.size_check"))
144      _efl_ui_widget_factory_constructing(data, ev);
145 
146    // Bind all property before the object is finalize
147    it = eina_hash_iterator_data_new(pd->parts);
148    EINA_ITERATOR_FOREACH(it, bpd)
149      {
150         Efl_Ui_Property_Bind_Data *bppd;
151         Eina_List *l;
152 
153         EINA_LIST_FOREACH(bpd->properties, l, bppd)
154           efl_ui_property_bind(efl_part(ui_view, bpd->part),
155                                bppd->part_property,
156                                bppd->model_property);
157      }
158    eina_iterator_free(it);
159 
160    // As we have already waited for the property to be ready, we should get the right style now
161    if (!pd->style) return ;
162 
163    property = efl_model_property_get(model, pd->style);
164    if (!property) return ;
165 
166    style = eina_value_to_string(property);
167    if (style) efl_ui_widget_style_set(ui_view, style);
168    free(style);
169 
170    eina_value_free(property);
171 
172    efl_key_data_set(ui_view, "efl.ui.widget.factory.cached", NULL);
173 }
174 
175 static void
_efl_ui_widget_factory_releasing(void * data,const Efl_Event * ev)176 _efl_ui_widget_factory_releasing(void *data, const Efl_Event *ev)
177 {
178    Efl_Ui_Widget_Factory_Data *pd = data;
179    Efl_Gfx_Entity *ui_view = ev->info;
180    Efl_Ui_Bind_Part_Data *bpd;
181    Eina_Iterator *it;
182 
183    efl_key_data_set(ui_view, "efl.ui.widget.factory.size_set", NULL);
184    efl_key_data_set(ui_view, "efl.ui.widget.factory.size_check", NULL);
185    if (efl_isa(ui_view, EFL_UI_ITEM_CLASS)) efl_ui_item_calc_locked_set(ui_view, EINA_TRUE);
186 
187    // Bind default property
188    if (pd->default_property) efl_ui_property_bind(ui_view, NULL, pd->default_property);
189 
190    // Bind all property before the object is finalize
191    it = eina_hash_iterator_data_new(pd->parts);
192    EINA_ITERATOR_FOREACH(it, bpd)
193      {
194         Efl_Ui_Property_Bind_Data *bppd;
195         Eina_List *l;
196 
197         EINA_LIST_FOREACH(bpd->properties, l, bppd)
198           efl_ui_property_bind(efl_part(ui_view, bpd->part),
199                                bppd->part_property, NULL);
200      }
201    eina_iterator_free(it);
202 
203    efl_ui_view_model_set(ui_view, NULL);
204 
205    // Prevent any recalc to happen when an object is in the cache or during shutdown of the object
206    efl_canvas_group_need_recalculate_set(ui_view, EINA_FALSE);
207 }
208 
209 EFL_CALLBACKS_ARRAY_DEFINE(item_callbacks,
210                            { EFL_UI_FACTORY_EVENT_ITEM_CONSTRUCTING, _efl_ui_widget_factory_constructing },
211                            { EFL_UI_FACTORY_EVENT_ITEM_BUILDING, _efl_ui_widget_factory_building },
212                            { EFL_UI_FACTORY_EVENT_ITEM_RELEASING, _efl_ui_widget_factory_releasing })
213 
214 static Eo *
_efl_ui_widget_factory_efl_object_constructor(Efl_Ui_Widget_Factory * obj,Efl_Ui_Widget_Factory_Data * pd)215 _efl_ui_widget_factory_efl_object_constructor(Efl_Ui_Widget_Factory *obj,
216                                               Efl_Ui_Widget_Factory_Data *pd)
217 {
218    obj = efl_constructor(efl_super(obj, EFL_UI_WIDGET_FACTORY_CLASS));
219 
220    efl_event_callback_array_add(obj, item_callbacks(), pd);
221 
222    return obj;
223 }
224 
225 static Efl_Ui_Widget *
_efl_ui_widget_create(const Efl_Ui_Factory * factory,const Efl_Class * klass,Efl_Ui_Widget * parent,Efl_Model * model)226 _efl_ui_widget_create(const Efl_Ui_Factory *factory,
227                       const Efl_Class *klass,
228                       Efl_Ui_Widget *parent,
229                       Efl_Model *model)
230 {
231    Efl_Ui_Widget *w;
232 
233    w = efl_add(klass, parent,
234                efl_ui_view_model_set(efl_added, model),
235                efl_event_callback_call((Efl_Ui_Factory *) factory, EFL_UI_FACTORY_EVENT_ITEM_CONSTRUCTING, efl_added));
236    efl_event_callback_call((Efl_Ui_Factory *) factory, EFL_UI_FACTORY_EVENT_ITEM_BUILDING, w);
237    return w;
238 }
239 
240 static Eina_Value
_efl_ui_widget_factory_create_then(Eo * model,void * data,const Eina_Value v EINA_UNUSED)241 _efl_ui_widget_factory_create_then(Eo *model, void *data, const Eina_Value v EINA_UNUSED)
242 {
243    Efl_Ui_Widget_Factory_Request *r = data;
244    Efl_Ui_Widget *w;
245 
246    w = _efl_ui_widget_create(r->factory, r->pd->klass, r->pd->parenting_widget, model);
247    if (!w) return eina_value_error_init(ENOMEM);
248    return eina_value_object_init(w);
249 }
250 
251 static void
_efl_ui_widget_factory_single_cleanup(Eo * model,void * data EINA_UNUSED,const Eina_Future * dead_future EINA_UNUSED)252 _efl_ui_widget_factory_single_cleanup(Eo *model, void *data EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
253 {
254    efl_unref(model);
255 }
256 
257 static void
_efl_ui_widget_factory_create_cleanup(Eo * o EINA_UNUSED,void * data,const Eina_Future * dead_future EINA_UNUSED)258 _efl_ui_widget_factory_create_cleanup(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
259 {
260    Efl_Ui_Widget_Factory_Request *r = data;
261 
262    efl_unref(r->factory);
263    free(r);
264 }
265 
266 static Eina_Future *
_efl_ui_widget_factory_efl_ui_factory_create(Eo * obj,Efl_Ui_Widget_Factory_Data * pd,Eina_Iterator * models)267 _efl_ui_widget_factory_efl_ui_factory_create(Eo *obj, Efl_Ui_Widget_Factory_Data *pd,
268                                              Eina_Iterator *models)
269 {
270    Efl_Ui_Widget_Factory_Request *r = NULL;
271    Eina_Future **f = NULL;
272    Efl_Model *model = NULL;
273    int count = 0;
274 
275    if (!pd->klass)
276      return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
277 
278    if (!pd->style)
279      {
280         Efl_Ui_Widget *w = NULL;
281         Eina_Value r;
282         Evas *e;
283 
284         e = evas_object_evas_get(obj);
285         evas_event_freeze(e);
286 
287         eina_value_array_setup(&r, EINA_VALUE_TYPE_OBJECT, 4);
288 
289         EINA_ITERATOR_FOREACH(models, model)
290           {
291              w = _efl_ui_widget_create(obj, pd->klass, pd->parenting_widget, model);
292 
293              if (!w)
294                {
295                   evas_event_thaw(e);
296                   evas_event_thaw_eval(e);
297                   return efl_loop_future_rejected(obj, ENOMEM);
298                }
299              eina_value_array_append(&r, w);
300           }
301         eina_iterator_free(models);
302 
303         evas_event_thaw(e);
304         evas_event_thaw_eval(e);
305 
306         return efl_loop_future_resolved(obj, r);
307      }
308 
309    r = calloc(1, sizeof (Efl_Ui_Widget_Factory_Request));
310    if (!r) return efl_loop_future_rejected(obj, ENOMEM);
311 
312    r->pd = pd;
313    r->factory = efl_ref(obj);
314 
315    f = calloc(count + 1, sizeof (Eina_Future *));
316    if (!f) goto alloc_array_error;
317 
318    EINA_ITERATOR_FOREACH(models, model)
319      {
320         f[count++] = efl_future_then(efl_ref(model), efl_model_property_ready_get(model, pd->style),
321                                      .success = _efl_ui_widget_factory_create_then,
322                                      .free = _efl_ui_widget_factory_single_cleanup);
323 
324         Eina_Future** tmp = realloc(f, (count + 1) * sizeof (Eina_Future *));
325         if (!tmp)
326           {
327             free(f);
328             goto alloc_array_error;
329           }
330         f = tmp;
331      }
332    eina_iterator_free(models);
333 
334    f[count] = EINA_FUTURE_SENTINEL;
335 
336    return efl_future_then(obj, eina_future_all_array(f),
337                           .data = r,
338                           .free = _efl_ui_widget_factory_create_cleanup);
339 
340 alloc_array_error:
341    efl_unref(r->factory);
342    free(r);
343    eina_iterator_free(models);
344    return efl_loop_future_rejected(obj, ENOMEM);
345 }
346 
347 static void
_efl_ui_widget_factory_efl_ui_factory_release(Eo * obj,Efl_Ui_Widget_Factory_Data * pd EINA_UNUSED,Eina_Iterator * ui_views)348 _efl_ui_widget_factory_efl_ui_factory_release(Eo *obj,
349                                               Efl_Ui_Widget_Factory_Data *pd EINA_UNUSED,
350                                               Eina_Iterator *ui_views)
351 {
352    Efl_Gfx_Entity *ui_view;
353 
354    EINA_ITERATOR_FOREACH(ui_views, ui_view)
355      {
356         // There might be multiple call to releasing on the same object as every factory in the
357         // inheritance chain can decide to keep it for a time
358         efl_event_callback_call(obj, EFL_UI_FACTORY_EVENT_ITEM_RELEASING, ui_view);
359         // We do not cache or track this item, just get rid of them asap
360         efl_del(ui_view);
361      }
362    eina_iterator_free(ui_views);
363 }
364 
365 Eina_Stringshare *_property_style_ss = NULL;
366 
367 static Eina_Error
_efl_ui_widget_factory_efl_ui_property_bind_property_bind(Eo * obj,Efl_Ui_Widget_Factory_Data * pd,const char * target,const char * property)368 _efl_ui_widget_factory_efl_ui_property_bind_property_bind(Eo *obj, Efl_Ui_Widget_Factory_Data *pd,
369                                                           const char *target, const char *property)
370 {
371    if (_property_style_ss == target || !strcmp(target, _property_style_ss))
372      {
373         eina_stringshare_replace(&pd->style, property);
374         efl_event_callback_call(obj, EFL_UI_PROPERTY_BIND_EVENT_PROPERTY_BOUND, (void*) _property_style_ss);
375         return 0;
376      }
377 
378    return EINVAL;
379 }
380 
381 
382 typedef struct _Efl_Ui_Property_Bind_Part_Data Efl_Ui_Property_Bind_Part_Data;
383 struct _Efl_Ui_Property_Bind_Part_Data
384 {
385    Efl_Ui_Widget_Factory_Data *pd;
386    Eina_Stringshare *name;
387 };
388 
389 static Efl_Object *
_efl_ui_widget_factory_efl_part_part_get(const Eo * obj,Efl_Ui_Widget_Factory_Data * pd,const char * name)390 _efl_ui_widget_factory_efl_part_part_get(const Eo *obj,
391                                          Efl_Ui_Widget_Factory_Data *pd,
392                                          const char *name)
393 {
394    Efl_Ui_Property_Bind_Part_Data *ppd;
395    Efl_Object *part;
396 
397    part = efl_add(EFL_UI_PROPERTY_BIND_PART_CLASS, (Eo*) obj);
398    if (!part) return NULL;
399 
400    ppd = efl_data_scope_get(part, EFL_UI_PROPERTY_BIND_PART_CLASS);
401    ppd->name = eina_stringshare_add(name);
402    ppd->pd = pd;
403 
404    return part;
405 }
406 
407 static void
_efl_ui_property_bind_part_efl_object_destructor(Eo * obj,Efl_Ui_Property_Bind_Part_Data * pd)408 _efl_ui_property_bind_part_efl_object_destructor(Eo *obj, Efl_Ui_Property_Bind_Part_Data *pd)
409 {
410    eina_stringshare_replace(&pd->name, NULL);
411 
412    efl_destructor(efl_super(obj, EFL_UI_PROPERTY_BIND_PART_CLASS));
413 }
414 
415 static Eina_Error
_efl_ui_property_bind_part_efl_ui_property_bind_property_bind(Eo * obj EINA_UNUSED,Efl_Ui_Property_Bind_Part_Data * pd,const char * key,const char * property)416 _efl_ui_property_bind_part_efl_ui_property_bind_property_bind(Eo *obj EINA_UNUSED,
417                                                               Efl_Ui_Property_Bind_Part_Data *pd,
418                                                               const char *key,
419                                                               const char *property)
420 {
421    Efl_Ui_Bind_Part_Data *bpd;
422    Efl_Ui_Property_Bind_Data *bppd;
423 
424    if (!pd->pd)
425      {
426         ERR("Trying to bind part property without specifying which part");
427         return ENOENT;
428      }
429 
430    if (!key)
431      {
432         eina_stringshare_replace(&pd->pd->default_property, property);
433         return 0;
434      }
435 
436    if (!pd->pd->parts)
437      pd->pd->parts = eina_hash_stringshared_new(NULL);
438 
439    bpd = eina_hash_find(pd->pd->parts, pd->name);
440    if (!bpd)
441      {
442         bpd = calloc(1, sizeof (Efl_Ui_Bind_Part_Data));
443         if (!bpd) return ENOMEM;
444 
445         bpd->part = eina_stringshare_ref(pd->name);
446 
447         eina_hash_direct_add(pd->pd->parts, bpd->part, bpd);
448      }
449 
450    bppd = calloc(1, sizeof (Efl_Ui_Property_Bind_Data));
451    if (!bppd) return ENOMEM;
452 
453    bppd->part_property = eina_stringshare_add(key);
454    bppd->model_property = eina_stringshare_add(property);
455 
456    bpd->properties = eina_list_append(bpd->properties, bppd);
457 
458    efl_event_callback_call(obj, EFL_UI_PROPERTY_BIND_EVENT_PROPERTY_BOUND, (void*) key);
459 
460    return 0;
461 }
462 
463 static Eina_Error
_efl_ui_widget_factory_efl_ui_factory_bind_factory_bind(Eo * obj EINA_UNUSED,Efl_Ui_Widget_Factory_Data * pd EINA_UNUSED,const char * key EINA_UNUSED,Efl_Ui_Factory * factory EINA_UNUSED)464 _efl_ui_widget_factory_efl_ui_factory_bind_factory_bind(Eo *obj EINA_UNUSED,
465                                                         Efl_Ui_Widget_Factory_Data *pd EINA_UNUSED,
466                                                         const char *key EINA_UNUSED,
467                                                         Efl_Ui_Factory *factory EINA_UNUSED)
468 {
469    ERR("Efl.Ui.Widget_Factory doesn't support efl.ui.factory_bind.\n");
470    return EINA_ERROR_NOT_IMPLEMENTED;
471 }
472 
473 #include "efl_ui_property_bind_part.eo.c"
474 #include "efl_ui_widget_factory.eo.c"
475