efl: add a Efl.Model_Provider that every widget will look up for in their parent tree.

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
This commit is contained in:
Cedric BAIL 2019-07-10 18:03:46 -07:00 committed by Marcel Hollerbach
parent a68e18a903
commit 2d481d8593
6 changed files with 121 additions and 8 deletions

View 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 */

View File

@ -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"

View File

@ -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; }
}
}

View 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([

View File

@ -5786,20 +5786,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
@ -5815,6 +5866,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,
@ -5822,6 +5876,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;
@ -5830,7 +5889,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;
@ -5864,7 +5922,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);

View File

@ -386,6 +386,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;