elementary: add internal Efl_Ui_Model_Average.
authorCedric BAIL <cedric.bail@free.fr>
Fri, 11 Jan 2019 00:40:25 +0000 (16:40 -0800)
committerTaehyub Kim <taehyub.kim@samsung.com>
Thu, 31 Jan 2019 03:45:42 +0000 (12:45 +0900)
This model enable View that require to compute the size of their item
to rely on its logic to store all items size independently. It has the
same interface as the Homogeneous and should be exchangeable for a View.
It provide an average total size at all time. It use Efl.Ui.Model_Exact
to do most of the work and is a good example on how to alter a Model
logic.

Reviewed-by: SangHyeon Jade Lee <sh10233.lee@samsung.com>
Differential Revision: https://phab.enlightenment.org/D7661

src/Makefile_Elementary.am
src/lib/elementary/efl_ui_model_average.c [new file with mode: 0644]
src/lib/elementary/efl_ui_model_average.eo [new file with mode: 0644]
src/lib/elementary/elm_priv.h
src/lib/elementary/meson.build

index a1a9586..03296e8 100644 (file)
@@ -181,6 +181,7 @@ elm_private_eolian_files = \
        lib/elementary/efl_ui_model_size.eo \
        lib/elementary/efl_ui_model_homogeneous.eo \
        lib/elementary/efl_ui_model_exact.eo \
+       lib/elementary/efl_ui_model_average.eo \
        $(NULL)
 
 ## TIZEN_ONLY(20180509): support elementary optional build
@@ -918,6 +919,7 @@ lib_elementary_libelementary_la_SOURCES = \
        lib/elementary/efl_ui_model_size.c \
        lib/elementary/efl_ui_model_homogeneous.c \
        lib/elementary/efl_ui_model_exact.c \
+       lib/elementary/efl_ui_model_average.c \
        $(NULL)
 
 
diff --git a/src/lib/elementary/efl_ui_model_average.c b/src/lib/elementary/efl_ui_model_average.c
new file mode 100644 (file)
index 0000000..be4dca5
--- /dev/null
@@ -0,0 +1,181 @@
+#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_Model_Average_Data Efl_Ui_Model_Average_Data;
+struct _Efl_Ui_Model_Average_Data
+{
+   Efl_Ui_Model_Average_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_Model_Average_Update Efl_Ui_Model_Average_Update;
+struct _Efl_Ui_Model_Average_Update
+{
+   unsigned long long *total;
+   unsigned long long *seen;
+   unsigned int previous;
+};
+
+static Eina_Value
+_efl_ui_model_average_update(Eo *obj EINA_UNUSED, void *data, const Eina_Value v)
+{
+   Efl_Ui_Model_Average_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_model_average_clean(Eo *obj EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
+{
+   free(data);
+}
+
+static Eina_Future *
+_efl_ui_model_average_prepare(Eo *obj,
+                              unsigned long long *total, unsigned long long *seen,
+                              const char *property, Eina_Value *value)
+{
+   Efl_Ui_Model_Average_Update *update;
+   Eina_Value *previous;
+   Eina_Future *f;
+
+   update = calloc(1, sizeof (Efl_Ui_Model_Average_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;
+
+   // 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_MODEL_AVERAGE_CLASS), property, value);
+
+   return efl_future_then(obj, f,
+                          .success = _efl_ui_model_average_update,
+                          .free = _efl_ui_model_average_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_model_average_efl_model_property_set(Eo *obj, Efl_Ui_Model_Average_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 (!strcmp(property, _efl_model_property_selfw)) */
+   /*   { */
+   /*      f = _efl_ui_model_average_prepare(obj, &pd->parent->total.width, */
+   /*                                        pd->wseen ? NULL : &pd->parent->total.wseen, */
+   /*                                        property, value, EINA_TRUE); */
+   /*      pd->wseen = EINA_TRUE; */
+   /*   } */
+   if (!strcmp(property, _efl_model_property_selfh))
+     {
+        f = _efl_ui_model_average_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_MODEL_AVERAGE_CLASS), property, value);
+
+   return f;
+}
+
+static inline Eina_Value *
+_efl_ui_model_average_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_model_average_efl_model_property_get(const Eo *obj, Efl_Ui_Model_Average_Data *pd, const char *property)
+{
+   const Eina_Value_Type *t;
+   Eina_Value *r;
+
+   r = efl_model_property_get(efl_super(obj, EFL_UI_MODEL_AVERAGE_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 (!strcmp(property, _efl_model_property_totalh))
+          r = _efl_ui_model_average_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 (!strcmp(property, _efl_model_property_totalw)) */
+        /*   r = _efl_ui_model_average_compute(obj, r, pd->total.width, pd->total.wseen); */
+     }
+
+   return r;
+}
+
+static Efl_Object *
+_efl_ui_model_average_efl_object_constructor(Eo *obj, Efl_Ui_Model_Average_Data *pd)
+{
+   Eo *parent = efl_parent_get(obj);
+
+   if (parent && efl_isa(parent, EFL_UI_MODEL_AVERAGE_CLASS))
+     pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_MODEL_AVERAGE_CLASS);
+
+   return efl_constructor(efl_super(obj, EFL_UI_MODEL_AVERAGE_CLASS));
+}
+
+#include "efl_ui_model_average.eo.c"
diff --git a/src/lib/elementary/efl_ui_model_average.eo b/src/lib/elementary/efl_ui_model_average.eo
new file mode 100644 (file)
index 0000000..1af87f9
--- /dev/null
@@ -0,0 +1,19 @@
+class Efl.Ui.Model_Average extends Efl.Ui.Model_Exact
+{
+   [[Class to be used to store object item size for List/Grid View.
+
+   This model provide the same feature as @Efl.Ui.Model_Exact except for the
+   @Efl.Model.property "$total.width" and "$total.height" which reflect an
+   estimated value of the total size by using the currently know size from its
+   children as an average size for all its children. As more children fill
+   "$self.width" and "$self.height", this model will figure out a more precise
+   answer. Once all children size is known, the result will be exact and the same
+   as @Efl.Ui.Model_Exact.
+
+   This model only supporting vertical list at this point.]]
+
+   implements {
+      Efl.Object.constructor;
+      Efl.Model.property { set; get; }
+   }
+}
\ No newline at end of file
index 373a1e5..416d21e 100644 (file)
@@ -75,6 +75,7 @@
 # include "efl_ui_model_size.eo.h"
 # include "efl_ui_model_homogeneous.eo.h"
 # include "efl_ui_model_exact.eo.h"
+# include "efl_ui_model_average.eo.h"
 
 extern const char *_efl_model_property_itemw;
 extern const char *_efl_model_property_itemh;
index ba99b22..d1edd7e 100644 (file)
@@ -341,6 +341,7 @@ priv_eo_files = [
   'efl_ui_model_size.eo',
   'efl_ui_model_homogeneous.eo',
   'efl_ui_model_exact.eo',
+  'efl_ui_model_average.eo',
 ]
 
 priv_eo_file_target = []
@@ -916,6 +917,7 @@ elementary_src = [
   'efl_ui_model_size.c',
   'efl_ui_model_homogeneous.c',
   'efl_ui_model_exact.c',
+  'efl_ui_model_average.c',
 ]
 
 elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl]