efl: add a Efl.Model_Provider that every widget will look up for in their parent...
authorCedric BAIL <cedric.bail@free.fr>
Thu, 11 Jul 2019 01:03:46 +0000 (18:03 -0700)
committerSangHyeon Jade Lee <sh10233.lee@samsung.com>
Tue, 23 Jul 2019 05:02:34 +0000 (14:02 +0900)
This is done to simplify code as you only need to set the model on the
provider and all the widget that are using it as a provider will automatically be
updated. The child will find a provider during at the time the first property binding
is set on the widget by checking if the parent have an Efl.Model_Provider set. It is
not necessary to set a model to have a valid lookup on a Efl.Model_Provider. To disable
a widget lookup, you can just force set a model on it (even NULL) and it will disable
the lookup.

Reviewed-by: Marcel Hollerbach <mail@marcel-hollerbach.de>
Differential Revision: https://phab.enlightenment.org/D9290

src/lib/efl/Efl.h
src/lib/efl/interfaces/efl_model_provider.c [new file with mode: 0644]
src/lib/efl/interfaces/efl_model_provider.eo [new file with mode: 0644]
src/lib/efl/interfaces/meson.build
src/lib/elementary/efl_ui_widget.c
src/lib/elementary/elm_widget.h

index d2746ee..97c21ec 100644 (file)
@@ -147,6 +147,7 @@ typedef Efl_Gfx_Path_Command_Type Efl_Gfx_Path_Command;
 #include "interfaces/efl_ui_property_bind.eo.h"
 #include "interfaces/efl_ui_factory.eo.h"
 #include "interfaces/efl_ui_factory_bind.eo.h"
+#include "interfaces/efl_model_provider.eo.h"
 #include "interfaces/efl_cached_item.eo.h"
 
 /* Observable interface */
diff --git a/src/lib/efl/interfaces/efl_model_provider.c b/src/lib/efl/interfaces/efl_model_provider.c
new file mode 100644 (file)
index 0000000..d60bb44
--- /dev/null
@@ -0,0 +1,36 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Efl.h"
+
+typedef struct _Efl_Model_Provider_Data Efl_Model_Provider_Data;
+struct _Efl_Model_Provider_Data
+{
+   Efl_Model *model;
+};
+
+static void
+_efl_model_provider_efl_ui_view_model_set(Eo *obj, Efl_Model_Provider_Data *pd,
+                                          Efl_Model *model)
+{
+   Efl_Model_Changed_Event ev;
+
+   ev.previous = efl_ref(pd->model);
+   ev.current = efl_ref(model);
+   efl_replace(&pd->model, model);
+
+   efl_event_callback_call(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, &ev);
+
+   efl_unref(ev.previous);
+   efl_unref(ev.current);
+}
+
+static Efl_Model *
+_efl_model_provider_efl_ui_view_model_get(const Eo *obj EINA_UNUSED,
+                                          Efl_Model_Provider_Data *pd)
+{
+   return pd->model;
+}
+
+#include "efl_model_provider.eo.c"
diff --git a/src/lib/efl/interfaces/efl_model_provider.eo b/src/lib/efl/interfaces/efl_model_provider.eo
new file mode 100644 (file)
index 0000000..6fa68f5
--- /dev/null
@@ -0,0 +1,12 @@
+class @beta Efl.Model_Provider extends Efl.Object implements Efl.Ui.View
+{
+   [[EFL object that provide a model to all.
+
+   You can use this when you would otherwise have to call @Efl.Ui.View.model.set
+   on multiple widgets by registering this object using @Efl.Object.provider_register
+   on a parent that they all depends on.
+   ]]
+   implements {
+      Efl.Ui.View.model { get; set; }
+   }
+}
index d09a24c..abfe721 100644 (file)
@@ -63,6 +63,7 @@ pub_eo_files = [
   'efl_gfx_blur.eo',
   'efl_gfx_hint.eo',
   'efl_model.eo',
+  'efl_model_provider.eo',
   'efl_interpolator.eo',
   'efl_gfx_image_orientable.eo',
   'efl_container.eo',
@@ -167,6 +168,7 @@ efl_src += files([
   'efl_file.c',
   'efl_ui_layout_orientable_readonly.c',
   'efl_text_markup_util.c',
+  'efl_model_provider.c',
 ])
 
 #efl_header_src += files([
index df9e6c4..00c60c0 100644 (file)
@@ -6767,20 +6767,71 @@ _efl_ui_widget_model_update(Efl_Ui_Widget_Data *pd)
      _efl_ui_property_bind_get(pd, property);
 }
 
+static void _efl_ui_widget_model_provider_model_change(void *data, const Efl_Event *event EINA_UNUSED);
+static void _efl_ui_widget_model_provider_invalidate(void *data, const Efl_Event *event EINA_UNUSED);
+
+EFL_CALLBACKS_ARRAY_DEFINE(efl_ui_widget_model_provider_callbacks,
+                           { EFL_EVENT_INVALIDATE, _efl_ui_widget_model_provider_invalidate },
+                           { EFL_UI_VIEW_EVENT_MODEL_CHANGED, _efl_ui_widget_model_provider_model_change });
+
+static void
+_efl_ui_widget_model_provider_model_change(void *data, const Efl_Event *event)
+{
+   Efl_Ui_Widget_Data *pd = data;
+
+   efl_replace(&pd->properties.model,
+               efl_ui_view_model_get(pd->properties.provider));
+   _efl_ui_widget_model_update(pd);
+
+   efl_event_callback_call(pd->obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, event->info);
+}
+
+static void
+_efl_ui_widget_model_provider_invalidate(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Efl_Ui_Widget_Data *pd = data;
+
+   efl_event_callback_array_del(pd->properties.provider,
+                                efl_ui_widget_model_provider_callbacks(),
+                                pd);
+   efl_replace(&pd->properties.provider, NULL);
+   efl_replace(&pd->properties.model, NULL);
+}
+
 static void
 _efl_ui_widget_model_register(Eo *obj, Efl_Ui_Widget_Data *pd)
 {
    if (pd->properties.registered) return ;
-   if (pd->properties.model_lookup)
+
+   if (!pd->properties.model)
      {
+        Efl_Model_Changed_Event ev;
+
+        efl_replace(&pd->properties.provider,
+                    efl_provider_find(obj, EFL_MODEL_PROVIDER_CLASS));
+        if (!pd->properties.provider) return ;
+        efl_event_callback_array_add(pd->properties.provider,
+                                     efl_ui_widget_model_provider_callbacks(),
+                                     pd);
+
+        efl_replace(&pd->properties.model,
+                    efl_ui_view_model_get(pd->properties.provider));
+
         if (!pd->properties.model) return ;
 
-        efl_event_callback_add(pd->properties.model, EFL_MODEL_EVENT_PROPERTIES_CHANGED,
-                               _efl_ui_model_property_bind_changed, pd);
-        efl_event_callback_add(obj, EFL_UI_PROPERTY_BIND_EVENT_PROPERTIES_CHANGED,
-                               _efl_ui_view_property_bind_changed, pd);
-        pd->properties.registered = EINA_TRUE;
+        ev.current = pd->properties.model;
+        ev.previous = NULL;
+        efl_event_callback_call(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, &ev);
      }
+
+   if (!pd->properties.model) return ;
+   if (!pd->properties.model_lookup) return ;
+
+   efl_event_callback_add(pd->properties.model, EFL_MODEL_EVENT_PROPERTIES_CHANGED,
+                          _efl_ui_model_property_bind_changed, pd);
+   efl_event_callback_add(obj, EFL_UI_PROPERTY_BIND_EVENT_PROPERTIES_CHANGED,
+                          _efl_ui_view_property_bind_changed, pd);
+   pd->properties.registered = EINA_TRUE;
 }
 
 static void
@@ -6796,6 +6847,9 @@ _efl_ui_widget_model_unregister(Eo *obj, Efl_Ui_Widget_Data *pd)
 
         pd->properties.registered = EINA_FALSE;
      }
+   // Invalidate must be called before setting a new model and even if no model is registered
+   if (pd->properties.provider)
+     _efl_ui_widget_model_provider_invalidate(pd, NULL);
 }
 static Eina_Error
 _efl_ui_widget_efl_ui_property_bind_property_bind(Eo *obj, Efl_Ui_Widget_Data *pd,
@@ -6803,6 +6857,11 @@ _efl_ui_widget_efl_ui_property_bind_property_bind(Eo *obj, Efl_Ui_Widget_Data *p
 {
    Efl_Ui_Property_Bound *prop;
 
+   // Always check for a model and fetch a provider in case a binded property
+   // is provided by a class down the hierarchy, but they still need to be notified
+   // when a model change
+   _efl_ui_widget_model_register(obj, pd);
+
    // Check if the property is available from the reflection table of the object.
    if (!efl_property_reflection_exist(obj, key)) return EFL_PROPERTY_ERROR_INVALID_KEY;
 
@@ -6811,7 +6870,6 @@ _efl_ui_widget_efl_ui_property_bind_property_bind(Eo *obj, Efl_Ui_Widget_Data *p
         pd->properties.model_lookup = eina_hash_stringshared_new(_efl_ui_property_bind_free);
         pd->properties.view_lookup = eina_hash_stringshared_new(NULL);
      }
-   _efl_ui_widget_model_register(obj, pd);
 
    prop = calloc(1, sizeof (Efl_Ui_Property_Bound));
    if (!prop) return ENOMEM;
@@ -6845,7 +6903,10 @@ _efl_ui_widget_efl_ui_view_model_set(Eo *obj,
    // Set the properties handler just in case
    _efl_ui_widget_model_register(obj, pd);
 
-   efl_event_callback_call(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, &ev);
+   // In case the model set was NULL, but we did found a model provider
+   // we shouldn't emit a second event. Otherwise we should.
+   if (ev.current == pd->properties.model)
+     efl_event_callback_call(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, &ev);
 
    if (pd->properties.model) _efl_ui_widget_model_update(pd);
 
index 1089e6a..fe90492 100644 (file)
@@ -414,6 +414,7 @@ typedef struct _Elm_Widget_Smart_Data
    } legacy_focus;
    struct {
       Efl_Model *model;
+      Efl_Model_Provider *provider;
       Eina_Hash *model_lookup;
       Eina_Hash *view_lookup;
       Eina_Bool  registered : 1;