ecore: prevent duplication of children for Efl.Composite_Model and enable dummy object.
authorCedric BAIL <cedric.bail@free.fr>
Thu, 28 Feb 2019 02:17:30 +0000 (18:17 -0800)
committerWonki Kim <wonki_.kim@samsung.com>
Fri, 8 Mar 2019 11:49:37 +0000 (20:49 +0900)
This patch allow Efl.Composite_Model to return always the same object for the same index.
This way, it make it easier for the Model to always be in sync even if there is multiple
user at any time. The support for dummy object allow the Composite_Model to host more
object than what the source model provide. This dummy model will only have the property
of the Composite_Model and none of the Source model ofcourse.

Reviewed-by: Vitor Sousa da Silva <vitorsousa@expertisesolutions.com.br>
Differential Revision: https://phab.enlightenment.org/D8045

src/lib/ecore/efl_composite_model.c
src/lib/ecore/efl_composite_model.eo

index 8c511c6..668b382 100644 (file)
@@ -16,14 +16,36 @@ typedef struct _Efl_Composite_Model_Data Efl_Composite_Model_Data;
 
 struct _Efl_Composite_Model_Data
 {
+   EINA_RBTREE;
+
+   Efl_Composite_Model *self;
    Efl_Model *source;
+   Eina_Rbtree *indexed;
 
    unsigned int index;
 
    Eina_Bool need_index : 1;
    Eina_Bool set_index : 1;
+   Eina_Bool inserted : 1;
 };
 
+static Eina_Rbtree_Direction
+_children_indexed_cmp(const Efl_Composite_Model_Data *left,
+                      const Efl_Composite_Model_Data *right,
+                      void *data EINA_UNUSED)
+{
+   if (left->index < right->index)
+     return EINA_RBTREE_LEFT;
+   return EINA_RBTREE_RIGHT;
+}
+
+static int
+_children_indexed_key(const Efl_Composite_Model_Data *node,
+                      const int *key, int length EINA_UNUSED, void *data EINA_UNUSED)
+{
+   return node->index - *key;
+}
+
 static void
 _efl_composite_model_efl_object_destructor(Eo *obj, Efl_Composite_Model_Data *pd)
 {
@@ -41,15 +63,68 @@ _efl_composite_model_efl_object_destructor(Eo *obj, Efl_Composite_Model_Data *pd
    efl_destructor(efl_super(obj, EFL_COMPOSITE_MODEL_CLASS));
 }
 
+static void
+_efl_composite_model_efl_object_invalidate(Eo *obj, Efl_Composite_Model_Data *pd)
+{
+   if (pd->inserted)
+     {
+        Eo *parent;
+
+        parent = efl_parent_get(obj);
+        if (efl_isa(parent, EFL_COMPOSITE_MODEL_CLASS))
+          {
+             Efl_Composite_Model_Data *ppd;
+
+             ppd = efl_data_scope_get(parent, EFL_COMPOSITE_MODEL_CLASS);
+             ppd->indexed = eina_rbtree_inline_remove(ppd->indexed, EINA_RBTREE_GET(pd),
+                                                      EINA_RBTREE_CMP_NODE_CB(_children_indexed_cmp), NULL);
+             pd->inserted = EINA_FALSE;
+          }
+        else
+          {
+             ERR("Unexpected parent change during the life of object: %s this might lead to crash.", efl_debug_name_get(obj));
+          }
+     }
+
+   efl_invalidate(efl_super(obj, EFL_COMPOSITE_MODEL_CLASS));
+}
+
 static Efl_Object *
 _efl_composite_model_efl_object_finalize(Eo *obj, Efl_Composite_Model_Data *pd)
 {
+   Eo *parent;
+
    if (pd->source == NULL)
      {
         ERR("Source of the composite model wasn't defined at construction time.");
         return NULL;
      }
 
+   pd->self = obj;
+
+   parent = efl_parent_get(obj);
+   if (efl_isa(parent, EFL_COMPOSITE_MODEL_CLASS) && !pd->inserted)
+     {
+        Efl_Composite_Model_Data *ppd = efl_data_scope_get(parent, EFL_COMPOSITE_MODEL_CLASS);
+        Efl_Composite_Model_Data *lookup;
+
+        lookup = (void*) eina_rbtree_inline_lookup(ppd->indexed, &pd->index, sizeof (int),
+                                                   EINA_RBTREE_CMP_KEY_CB(_children_indexed_key), NULL);
+        if (lookup)
+          {
+             // There is already an object at this index, we should not
+             // build anything different than what exist. Returning existing one.
+             return lookup->self;
+          }
+        else
+          {
+             ppd->indexed = eina_rbtree_inline_insert(ppd->indexed, EINA_RBTREE_GET(pd),
+                                                      EINA_RBTREE_CMP_NODE_CB(_children_indexed_cmp), NULL);
+
+             pd->inserted = EINA_TRUE;
+          }
+     }
+
    return obj;
 }
 
@@ -175,6 +250,7 @@ struct _Efl_Composite_Model_Slice_Request
    const Efl_Class *self;
    Eo *parent;
    unsigned int start;
+   unsigned int dummy_need;
 };
 
 static Eina_Value
@@ -203,6 +279,25 @@ _efl_composite_model_then(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
         efl_unref(composite);
      }
 
+   while (req->dummy_need)
+     {
+        Eo *dummy, *dummy_proxy;
+
+        // Create a dummy object and its proxy
+        dummy = efl_add(EFL_GENERIC_MODEL_CLASS, req->parent);
+        dummy_proxy = efl_add_ref(req->self, req->parent,
+                                  efl_ui_view_model_set(efl_added, dummy),
+                                  efl_composite_model_index_set(efl_added, req->start + i),
+                                  efl_loop_model_volatile_make(efl_added));
+        efl_parent_set(dummy, dummy_proxy);
+
+        eina_value_array_append(&r, dummy_proxy);
+        efl_unref(dummy_proxy);
+
+        req->dummy_need--;
+        i++;
+     }
+
    return r;
 }
 
@@ -223,8 +318,47 @@ _efl_composite_model_efl_model_children_slice_get(Eo *obj,
 {
    Efl_Composite_Model_Slice_Request *req;
    Eina_Future *f;
+   unsigned int source_count, self_count;
+   unsigned int req_count;
+
+   source_count = efl_model_children_count_get(pd->source);
+   self_count = efl_model_children_count_get(obj);
+
+   if (start + count > source_count &&
+       start + count > self_count)
+     return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
+
+   if (start > source_count)
+     {
+        Eina_Value r = EINA_VALUE_EMPTY;
+        unsigned int i = 0;
+
+        eina_value_array_setup(&r, EINA_VALUE_TYPE_OBJECT, 4);
+
+        while (count)
+          {
+             Eo *dummy, *dummy_proxy;
+
+             // Create a dummy object and its proxy
+             dummy = efl_add(EFL_GENERIC_MODEL_CLASS, obj);
+             dummy_proxy = efl_add_ref(efl_class_get(obj), obj,
+                                       efl_ui_view_model_set(efl_added, dummy),
+                                       efl_composite_model_index_set(efl_added, start + i),
+                                       efl_loop_model_volatile_make(efl_added));
+             efl_parent_set(dummy, dummy_proxy);
+
+             eina_value_array_append(&r, dummy_proxy);
+             efl_unref(dummy_proxy);
+
+             count--;
+             i++;
+          }
+
+        return efl_loop_future_resolved(obj, r);
+     }
 
-   f = efl_model_children_slice_get(pd->source, start, count);
+   req_count = start + count > source_count ? source_count - start : count;
+   f = efl_model_children_slice_get(pd->source, start, req_count);
    if (!f) return NULL;
 
    req = malloc(sizeof (Efl_Composite_Model_Slice_Request));
@@ -233,6 +367,10 @@ _efl_composite_model_efl_model_children_slice_get(Eo *obj,
    req->self = efl_class_get(obj);
    req->parent = efl_ref(obj);
    req->start = start;
+   if (start + count < source_count)
+     req->dummy_need = 0;
+   else
+     req->dummy_need = count - (source_count - start);
 
    return efl_future_then(obj, f, .success_type = EINA_VALUE_TYPE_ARRAY,
                           .success = _efl_composite_model_then,
index 18a60bc..34a77fa 100644 (file)
@@ -21,6 +21,7 @@ class @beta Efl.Composite_Model extends Efl.Loop_Model implements Efl.Ui.View
    }
    implements {
       Efl.Object.destructor;
+      Efl.Object.invalidate;
       Efl.Object.finalize;
       Efl.Ui.View.model { set; get; }
       Efl.Model.property { set; get; }