summaryrefslogtreecommitdiff
path: root/src/lib/ecore
diff options
context:
space:
mode:
authorCedric BAIL <cedric.bail@free.fr>2019-04-28 10:59:57 -0700
committerCedric BAIL <cedric.bail@free.fr>2019-05-29 15:53:24 -0700
commitc84c4212671ff1155ab044def41326667bab90f5 (patch)
tree79c3af0f414d8639fde6361e1cf09e28eae6378b /src/lib/ecore
parenta86a0931f16f6a9d5afbff71364048dc859e2dff (diff)
ecore: add simple logic to generate synthetic string property assembling value of other properties.
I have choosen to use ${} as the property marker, but I am open to other suggestion. Let me know if you have other suggestion. This patch also fix recursion on properties changed to properly explore parents dependencies, avoid infinite loop and emit properties changed by added property logic or property text on the Efl.ViewModel. Reviewed-by: Lauro Neto <Lauro Moura <lauromoura@expertisesolutions.com.br>> Differential Revision: https://phab.enlightenment.org/D8755
Diffstat (limited to 'src/lib/ecore')
-rw-r--r--src/lib/ecore/efl_view_model.c315
-rw-r--r--src/lib/ecore/efl_view_model.eo28
2 files changed, 326 insertions, 17 deletions
diff --git a/src/lib/ecore/efl_view_model.c b/src/lib/ecore/efl_view_model.c
index dd3fefaba1..a19050e9be 100644
--- a/src/lib/ecore/efl_view_model.c
+++ b/src/lib/ecore/efl_view_model.c
@@ -13,6 +13,7 @@
13 13
14typedef struct _Efl_View_Model_Data Efl_View_Model_Data; 14typedef struct _Efl_View_Model_Data Efl_View_Model_Data;
15typedef struct _Efl_View_Model_Bind Efl_View_Model_Bind; 15typedef struct _Efl_View_Model_Bind Efl_View_Model_Bind;
16typedef struct _Efl_View_Model_Text Efl_View_Model_Text;
16typedef struct _Efl_View_Model_Logic Efl_View_Model_Logic; 17typedef struct _Efl_View_Model_Logic Efl_View_Model_Logic;
17typedef struct _Efl_View_Model_Property_Ref Efl_View_Model_Property_Ref; 18typedef struct _Efl_View_Model_Property_Ref Efl_View_Model_Property_Ref;
18 19
@@ -23,6 +24,7 @@ struct _Efl_View_Model_Data
23 24
24 Eina_Hash *bound; // Stringhash of Efl_View_Model_Bind 25 Eina_Hash *bound; // Stringhash of Efl_View_Model_Bind
25 Eina_Hash *logics; // Stringhash of Efl_View_Model_Logic 26 Eina_Hash *logics; // Stringhash of Efl_View_Model_Logic
27 Eina_Hash *texts; // Stringhash of Efl_View_Model_Text
26 28
27 Eina_Hash *deduplication; // Stringhash of Efl_View_Model_Property_Ref 29 Eina_Hash *deduplication; // Stringhash of Efl_View_Model_Property_Ref
28 30
@@ -35,6 +37,15 @@ struct _Efl_View_Model_Data
35 Eina_Bool children_bind : 1; // Define if child object should be automatically binded 37 Eina_Bool children_bind : 1; // Define if child object should be automatically binded
36}; 38};
37 39
40struct _Efl_View_Model_Text
41{
42 Eina_Stringshare *name;
43 Eina_Stringshare *definition;
44 Eina_Stringshare *not_ready;
45 Eina_Stringshare *on_error;
46 Efl_Model *self;
47};
48
38struct _Efl_View_Model_Bind 49struct _Efl_View_Model_Bind
39{ 50{
40 Eina_Stringshare *source; 51 Eina_Stringshare *source;
@@ -192,6 +203,163 @@ _efl_view_model_property_logic_del(Eo *obj EINA_UNUSED, Efl_View_Model_Data *pd,
192 return 0; 203 return 0;
193} 204}
194 205
206static int
207_lookup_next_token(const char *definition,
208 Eina_Slstr **text,
209 Eina_Slstr **property)
210{
211 const char *lookup_text;
212 const char *lookup_property;
213
214 if (!definition) return 0;
215
216 *text = NULL;
217 *property = NULL;
218
219 lookup_text = strstr(definition, "${");
220 if (!lookup_text) goto on_error;
221 lookup_text += 2;
222
223 lookup_property = strchr(lookup_text, '}');
224 if (!lookup_property) goto on_error;
225
226 *text = eina_slstr_copy_new_length(definition, lookup_text - definition - 2);
227 *property = eina_slstr_copy_new_length(lookup_text, lookup_property - lookup_text);
228
229 return lookup_property + 1 - definition;
230
231 on_error:
232 if (strlen(definition) == 0) return 0;
233 *text = eina_slstr_copy_new(definition);
234 return strlen(definition);
235}
236
237static Eina_Error
238_efl_view_model_property_string_add(Eo *obj, Efl_View_Model_Data *pd,
239 const char *name,
240 const char *definition,
241 const char *not_ready,
242 const char *on_error)
243{
244 Efl_View_Model_Text *text;
245 Eina_Stringshare *sn;
246 Eina_Slstr *st = NULL;
247 Eina_Slstr *sp = NULL;
248 int lookup;
249 Eina_Error err = ENOMEM;
250
251 if (!name || !definition) return EFL_MODEL_ERROR_INCORRECT_VALUE;
252 if (!strlen(name)) return EFL_MODEL_ERROR_INCORRECT_VALUE;
253 if (!strlen(definition)) return EFL_MODEL_ERROR_INCORRECT_VALUE;
254 sn = eina_stringshare_add(name);
255
256 // Lookup if there is an existing property defined and undo it first
257 text = eina_hash_find(pd->texts, sn);
258 if (text) efl_view_model_property_string_del(obj, sn);
259
260 text = calloc(1, sizeof (Efl_View_Model_Text));
261 if (!text) goto on_error;
262
263 err = EFL_MODEL_ERROR_INCORRECT_VALUE;
264
265 text->name = eina_stringshare_add(name);
266 text->definition = eina_stringshare_add(definition);
267 text->not_ready = not_ready ? eina_stringshare_add(not_ready) : NULL;
268 text->on_error = on_error ? eina_stringshare_add(on_error) : NULL;
269 text->self = obj;
270
271 for (lookup = _lookup_next_token(definition, &st, &sp);
272 lookup;
273 definition += lookup, lookup = _lookup_next_token(definition, &st, &sp))
274 {
275 if (sp) efl_view_model_property_bind(obj, sp, name);
276 }
277
278 for (lookup = _lookup_next_token(not_ready, &st, &sp);
279 lookup;
280 not_ready += lookup, lookup = _lookup_next_token(not_ready, &st, &sp))
281 {
282 if (sp) efl_view_model_property_bind(obj, sp, name);
283 }
284
285 for (lookup = _lookup_next_token(on_error, &st, &sp);
286 lookup;
287 on_error += lookup, lookup = _lookup_next_token(on_error, &st, &sp))
288 {
289 if (sp) efl_view_model_property_bind(obj, sp, name);
290 }
291
292 eina_hash_direct_add(pd->texts, text->name, text);
293
294 return 0;
295
296 on_error:
297 eina_stringshare_del(sn);
298 free(text);
299 return err;
300}
301
302static void
303_text_free(void *data)
304{
305 Efl_View_Model_Text *text = data;
306 Eina_Stringshare *st;
307 Eina_Stringshare *sp;
308 int lookup;
309 const char *tmp;
310
311 tmp = text->definition;
312 for (lookup = _lookup_next_token(tmp, &st, &sp);
313 lookup;
314 tmp += lookup, lookup = _lookup_next_token(tmp, &st, &sp))
315 {
316 if (sp) efl_view_model_property_unbind(text->self, sp, text->name);
317 }
318
319 tmp = text->not_ready;
320 for (lookup = _lookup_next_token(tmp, &st, &sp);
321 lookup;
322 tmp += lookup, lookup = _lookup_next_token(tmp, &st, &sp))
323 {
324 if (sp) efl_view_model_property_unbind(text->self, sp, text->name);
325 }
326
327 tmp = text->on_error;
328 for (lookup = _lookup_next_token(tmp, &st, &sp);
329 lookup;
330 tmp += lookup, lookup = _lookup_next_token(tmp, &st, &sp))
331 {
332 if (sp) efl_view_model_property_unbind(text->self, sp, text->name);
333 }
334
335 eina_stringshare_del(text->name);
336 eina_stringshare_del(text->not_ready);
337 eina_stringshare_del(text->on_error);
338 free(text);
339}
340
341static Eina_Error
342_efl_view_model_property_string_del(Eo *obj EINA_UNUSED,
343 Efl_View_Model_Data *pd,
344 const char *name)
345{
346 Efl_View_Model_Text *text;
347 Eina_Stringshare *sn;
348 Eina_Error err = EFL_MODEL_ERROR_INCORRECT_VALUE;
349
350 if (!name) return EFL_MODEL_ERROR_INCORRECT_VALUE;
351
352 sn = eina_stringshare_add(name);
353 text = eina_hash_find(pd->texts, sn);
354 if (!text) goto on_error;
355 eina_hash_del(pd->texts, sn, text);
356 err = 0;
357
358 on_error:
359 eina_stringshare_del(sn);
360 return err;
361}
362
195static void 363static void
196_efl_view_model_property_bind(Eo *obj EINA_UNUSED, Efl_View_Model_Data *pd, 364_efl_view_model_property_bind(Eo *obj EINA_UNUSED, Efl_View_Model_Data *pd,
197 const char *source, const char *destination) 365 const char *source, const char *destination)
@@ -269,14 +437,36 @@ _bind_free(void *data)
269 free(bind); 437 free(bind);
270} 438}
271 439
272static Efl_View_Model_Bind * 440static void
273_efl_view_model_property_bind_lookup(Efl_View_Model_Data *pd, Eina_Stringshare *src) 441_efl_view_model_property_bind_lookup(Eina_Array *changed_properties,
442 Efl_View_Model_Data *pd,
443 Eina_Stringshare *src)
274{ 444{
275 Efl_View_Model_Bind *bind; 445 Efl_View_Model_Bind *bind;
276 446
447 if (!pd) return ;
277 bind = eina_hash_find(pd->bound, src); 448 bind = eina_hash_find(pd->bound, src);
278 if (!bind && pd->parent) return _efl_view_model_property_bind_lookup(pd->parent, src); 449 if (bind)
279 return bind; 450 {
451 Eina_Stringshare *dest;
452 Eina_List *l;
453
454 EINA_LIST_FOREACH(bind->destinations, l, dest)
455 {
456 // Check for duplicated entry first to avoid infinite recursion
457 Eina_Stringshare *dup = NULL;
458 Eina_Array_Iterator iterator;
459 unsigned int i;
460
461 EINA_ARRAY_ITER_NEXT(changed_properties, i, dup, iterator)
462 if (dup == dest) break;
463 if (dup == dest) continue ;
464
465 eina_array_push(changed_properties, dest);
466 _efl_view_model_property_bind_lookup(changed_properties, pd, dest);
467 }
468 }
469 _efl_view_model_property_bind_lookup(changed_properties, pd->parent, src);
280} 470}
281 471
282static void 472static void
@@ -300,20 +490,10 @@ _efl_view_model_property_changed(void *data, const Efl_Event *event)
300 490
301 EINA_ARRAY_ITER_NEXT(ev->changed_properties, i, property, iterator) 491 EINA_ARRAY_ITER_NEXT(ev->changed_properties, i, property, iterator)
302 { 492 {
303 Efl_View_Model_Bind *bind;
304
305 eina_array_push(nev.changed_properties, property); 493 eina_array_push(nev.changed_properties, property);
306 494
307 src = eina_stringshare_ref(property); 495 src = eina_stringshare_ref(property);
308 bind = _efl_view_model_property_bind_lookup(pd, src); 496 _efl_view_model_property_bind_lookup(nev.changed_properties, pd, src);
309 if (bind)
310 {
311 Eina_Stringshare *dest;
312 Eina_List *l;
313
314 EINA_LIST_FOREACH(bind->destinations, l, dest)
315 eina_array_push(nev.changed_properties, dest);
316 }
317 } 497 }
318 498
319 efl_event_callback_call(event->object, EFL_MODEL_EVENT_PROPERTIES_CHANGED, &nev); 499 efl_event_callback_call(event->object, EFL_MODEL_EVENT_PROPERTIES_CHANGED, &nev);
@@ -433,6 +613,7 @@ _efl_view_model_efl_object_constructor(Eo *obj, Efl_View_Model_Data *pd)
433 pd->bound = eina_hash_stringshared_new(_bind_free); 613 pd->bound = eina_hash_stringshared_new(_bind_free);
434 pd->logics = eina_hash_stringshared_new(_logic_free); 614 pd->logics = eina_hash_stringshared_new(_logic_free);
435 pd->deduplication = eina_hash_stringshared_new(_ref_free); 615 pd->deduplication = eina_hash_stringshared_new(_ref_free);
616 pd->texts = eina_hash_stringshared_new(_text_free);
436 617
437 efl_event_callback_array_priority_add(obj, efl_view_model_intercept(), EFL_CALLBACK_PRIORITY_BEFORE, pd); 618 efl_event_callback_array_priority_add(obj, efl_view_model_intercept(), EFL_CALLBACK_PRIORITY_BEFORE, pd);
438 619
@@ -458,6 +639,12 @@ _efl_view_model_efl_object_destructor(Eo *obj, Efl_View_Model_Data *pd)
458 eina_hash_free(pd->logics); 639 eina_hash_free(pd->logics);
459 pd->logics = NULL; 640 pd->logics = NULL;
460 641
642 eina_hash_free(pd->texts);
643 pd->texts = NULL;
644
645 eina_hash_free(pd->deduplication);
646 pd->deduplication = NULL;
647
461 efl_destructor(efl_super(obj, EFL_VIEW_MODEL_CLASS)); 648 efl_destructor(efl_super(obj, EFL_VIEW_MODEL_CLASS));
462} 649}
463 650
@@ -485,13 +672,104 @@ _efl_view_model_efl_model_property_set(Eo *obj, Efl_View_Model_Data *pd,
485 if (logic) 672 if (logic)
486 f = logic->set.fct(logic->get.data, obj, prop, value); 673 f = logic->set.fct(logic->get.data, obj, prop, value);
487 else 674 else
488 f = efl_model_property_set(efl_super(obj, EFL_VIEW_MODEL_CLASS), property, value); 675 {
676 if (eina_hash_find(pd->texts, prop))
677 f = efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
678 else
679 f = efl_model_property_set(efl_super(obj, EFL_VIEW_MODEL_CLASS), property, value);
680 }
489 681
490 eina_stringshare_del(prop); 682 eina_stringshare_del(prop);
491 return f; 683 return f;
492} 684}
493 685
494static Eina_Value * 686static Eina_Value *
687_efl_view_model_text_generate(const Eo *obj,
688 Eina_Strbuf *out,
689 Eina_Stringshare *pattern,
690 Eina_Bool stop_on_error)
691{
692 Eina_Stringshare *st;
693 Eina_Stringshare *sp;
694 int lookup;
695
696 for (lookup = _lookup_next_token(pattern, &st, &sp);
697 lookup;
698 pattern += lookup, lookup = _lookup_next_token(pattern, &st, &sp))
699 {
700 Eina_Value *request;
701 char *sr;
702
703 eina_strbuf_append(out, st);
704
705 if (!sp) continue;
706
707 request = efl_model_property_get(obj, sp);
708 if (!request)
709 {
710 if (stop_on_error)
711 return eina_value_error_new(EFL_MODEL_ERROR_NOT_SUPPORTED);
712 eina_strbuf_append(out, "Unknown property");
713 continue;
714 }
715 if (eina_value_type_get(request) == EINA_VALUE_TYPE_ERROR && stop_on_error)
716 return request;
717
718 sr = eina_value_to_string(request);
719 eina_strbuf_append(out, sr);
720
721 free(sr);
722 eina_value_free(request);
723 }
724
725 return eina_value_string_new(eina_strbuf_string_get(out));
726}
727
728static Eina_Value *
729_efl_view_model_text_property_get(const Eo *obj, Efl_View_Model_Data *pd, Eina_Stringshare *prop)
730{
731 Efl_View_Model_Text *lookup;
732 Eina_Strbuf *buf;
733 Eina_Value *r;
734 Eina_Error err = 0;
735
736 if (!pd) return NULL;
737 lookup = eina_hash_find(pd->texts, prop);
738 // Lookup for property definition in the parent, but property value will be fetched on
739 // the child object doing the request.
740 if (!lookup) return _efl_view_model_text_property_get(obj, pd->parent, prop);
741
742 buf = eina_strbuf_new();
743
744 r = _efl_view_model_text_generate(obj, buf,
745 lookup->definition,
746 !!(lookup->on_error || lookup->not_ready));
747 if (eina_value_type_get(r) != EINA_VALUE_TYPE_ERROR)
748 goto done;
749 if (eina_value_error_get(r, &err) && err == EAGAIN && lookup->not_ready)
750 {
751 eina_strbuf_reset(buf);
752 eina_value_free(r);
753
754 r = _efl_view_model_text_generate(obj, buf, lookup->not_ready, !!lookup->on_error);
755 if (eina_value_type_get(r) != EINA_VALUE_TYPE_ERROR)
756 goto done;
757 }
758 if (lookup->on_error)
759 {
760 eina_strbuf_reset(buf);
761 eina_value_free(r);
762
763 r = _efl_view_model_text_generate(obj, buf, lookup->on_error, 0);
764 }
765
766 done:
767 eina_strbuf_free(buf);
768
769 return r;
770}
771
772static Eina_Value *
495_efl_view_model_efl_model_property_get(const Eo *obj, Efl_View_Model_Data *pd, 773_efl_view_model_efl_model_property_get(const Eo *obj, Efl_View_Model_Data *pd,
496 const char *property) 774 const char *property)
497{ 775{
@@ -504,7 +782,10 @@ _efl_view_model_efl_model_property_get(const Eo *obj, Efl_View_Model_Data *pd,
504 if (logic) 782 if (logic)
505 r = logic->get.fct(logic->get.data, obj, prop); 783 r = logic->get.fct(logic->get.data, obj, prop);
506 else 784 else
507 r = efl_model_property_get(efl_super(obj, EFL_VIEW_MODEL_CLASS), property); 785 {
786 r = _efl_view_model_text_property_get(obj, pd, prop);
787 if (!r) r = efl_model_property_get(efl_super(obj, EFL_VIEW_MODEL_CLASS), property);
788 }
508 789
509 eina_stringshare_del(prop); 790 eina_stringshare_del(prop);
510 return r; 791 return r;
diff --git a/src/lib/ecore/efl_view_model.eo b/src/lib/ecore/efl_view_model.eo
index d7092b3b7a..f429c67b7b 100644
--- a/src/lib/ecore/efl_view_model.eo
+++ b/src/lib/ecore/efl_view_model.eo
@@ -25,6 +25,34 @@ class @beta Efl.View_Model extends Efl.Composite_Model
25 it and manually define your property on it via callbacks. 25 it and manually define your property on it via callbacks.
26 ]] 26 ]]
27 methods { 27 methods {
28 property_string_add {
29 [[Adds a synthetic string property, generated from a $definition string and other properties in the model.
30
31 The $definition string, similar to how $printf works, contains ${} placeholders that are replaced by the actual value of the property inside the placeholder tags when the synthetic property is retrieved.
32 For example, a numeric property $length might be strange to use as a label, since it will only display a number. However, a synthetic string can be generated with the definition "Length ${length}." which renders more nicely and does not require any more code by the user of the property.
33
34 $not_ready and $on_error strings can be given to be used when the data is not ready or there is some error, respectively. These strings do accept placeholder tags.
35
36 See @.property_string_del
37 ]]
38 params {
39 name: string; [[The name for the new synthetic property.]]
40 definition: string; [[The definition string for the new synthetic property.]]
41 not_ready: string; [[The text to be used if any of the properties used in $definition is not ready yet. If set to $null, no check against EAGAIN will be done.]]
42 on_error: string; [[The text to be used if any of the properties used in $definition is in error. It takes precedence over $not_ready. If set to $null, no error checks are performed.]]
43 }
44 return: Eina.Error;
45 }
46 property_string_del {
47 [[Delete a synthetic property previously defined by @.property_string_add.
48
49 See @.property_string_add
50 ]]
51 params {
52 name: string; [[The name of the synthetic property to delete.]]
53 }
54 return: Eina.Error;
55 }
28 property_logic_add { 56 property_logic_add {
29 [[Add callbacks that will be triggered when someone ask for the specified property name when getting or setting a property. 57 [[Add callbacks that will be triggered when someone ask for the specified property name when getting or setting a property.
30 58