From 40def3eac914138189163a895527705a1ffefcfd Mon Sep 17 00:00:00 2001 From: Lauro Moura Date: Mon, 5 Aug 2019 10:17:52 -0400 Subject: [PATCH] efl-mono: Add Model manual implementation to C# and MVVM factories Summary: Depends on D9273, D9270 Test Plan: Run added testcases. Reviewers: cedric, bu5hm4n, zmike, SanghyeonLee, felipealmeida, segfaultxavi Reviewed By: cedric Subscribers: cedric Tags: #expertise_solutions, #efl_language_bindings Differential Revision: https://phab.enlightenment.org/D8080 --- .../eolian/mono/function_definition.hh | 53 ++++ src/bin/eolian_mono/eolian/mono/klass.hh | 10 + src/bindings/mono/efl_mono/Bind.cs | 36 +++ src/bindings/mono/efl_mono/Factory.cs | 30 +++ src/bindings/mono/efl_mono/GenericModel.cs | 174 +++++++++++++ src/bindings/mono/efl_mono/UserModel.cs | 104 ++++++++ src/bindings/mono/efl_mono/meson.build | 55 ++++- src/bindings/mono/eo_mono/FunctionWrapper.cs | 2 +- src/bindings/mono/meson.build | 7 - src/lib/efl/interfaces/efl_model.eo | 2 +- src/lib/efl_mono/efl_mono_model_internal.c | 229 ++++++++++++++++++ src/lib/efl_mono/efl_mono_model_internal.eo | 19 ++ .../efl_mono/efl_mono_model_internal_child.eo | 10 + src/tests/efl_mono/Main.cs | 2 +- src/tests/efl_mono/Model.cs | 83 +++++++ src/tests/efl_mono/meson.build | 3 +- 16 files changed, 807 insertions(+), 12 deletions(-) create mode 100644 src/bindings/mono/efl_mono/Bind.cs create mode 100644 src/bindings/mono/efl_mono/Factory.cs create mode 100644 src/bindings/mono/efl_mono/GenericModel.cs create mode 100644 src/bindings/mono/efl_mono/UserModel.cs create mode 100644 src/lib/efl_mono/efl_mono_model_internal.c create mode 100644 src/lib/efl_mono/efl_mono_model_internal.eo create mode 100644 src/lib/efl_mono/efl_mono_model_internal_child.eo create mode 100644 src/tests/efl_mono/Model.cs diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh b/src/bin/eolian_mono/eolian/mono/function_definition.hh index 8c43008c53..cf85f6844d 100644 --- a/src/bin/eolian_mono/eolian/mono/function_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh @@ -223,6 +223,52 @@ struct native_function_definition_parameterized } } const native_function_definition; +struct property_extension_method_definition_generator +{ + template + bool generate(OutputIterator sink, attributes::property_def const& property, Context context) const + { + if (blacklist::is_property_blacklisted(property, context)) + return true; + + auto options = efl::eolian::grammar::context_find_tag(context); + + if (!options.want_beta) + return true; // Bindable is a beta feature for now. + + auto get_params = property.getter.is_engaged() ? property.getter->parameters.size() : 0; + auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0; + + std::string managed_name = name_helpers::property_managed_name(property); + + if (get_params > 0 || set_params > 1) + return true; + + std::string dir_mod; + if (property.setter.is_engaged()) + dir_mod = direction_modifier(property.setter->parameters[0]); + + if (property.setter.is_engaged()) + { + attributes::type_def prop_type = property.setter->parameters[0].type; + if (!as_generator("public static Efl.Bindable<" << type(true) << "> " << managed_name << "(this Efl.Ui.ItemFactory fac) where T : " << name_helpers::klass_full_concrete_or_interface_name(cls) << " {\n" + << scope_tab << scope_tab << "return new Efl.Bindable<" << type(true) << ">(\"" << property.name << "\", fac);\n" + << scope_tab << "}\n" + ).generate(sink, std::make_tuple(prop_type, prop_type), context)) + return false; + } + + return true; + } + + grammar::attributes::klass_def const& cls; +}; + +property_extension_method_definition_generator property_extension_method_definition (grammar::attributes::klass_def const& cls) +{ + return {cls}; +} + struct property_wrapper_definition_generator { template @@ -345,6 +391,8 @@ struct is_eager_generator< ::eolian_mono::function_definition_generator> : std:: template <> struct is_eager_generator< ::eolian_mono::native_function_definition_generator> : std::true_type {}; template <> +struct is_eager_generator< ::eolian_mono::property_extension_method_definition_generator> : std::true_type {}; +template <> struct is_eager_generator< ::eolian_mono::property_wrapper_definition_generator> : std::true_type {}; template <> struct is_eager_generator< ::eolian_mono::property_wrapper_definition_parameterized> : std::true_type {}; @@ -355,6 +403,8 @@ struct is_generator< ::eolian_mono::native_function_definition_generator> : std: template <> struct is_generator< ::eolian_mono::function_definition_parameterized> : std::true_type {}; template <> +struct is_generator< ::eolian_mono::property_extension_method_definition_generator> : std::true_type {}; +template <> struct is_generator< ::eolian_mono::property_wrapper_definition_generator> : std::true_type {}; template <> struct is_generator< ::eolian_mono::property_wrapper_definition_parameterized> : std::true_type {}; @@ -369,6 +419,9 @@ struct attributes_needed< ::eolian_mono::function_definition_parameterized> : st template <> struct attributes_needed< ::eolian_mono::native_function_definition_generator> : std::integral_constant {}; +template <> +struct attributes_needed< ::eolian_mono::property_extension_method_definition_generator> : std::integral_constant {}; + template <> struct attributes_needed< ::eolian_mono::property_wrapper_definition_generator> : std::integral_constant {}; template <> diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh index 80d4debc99..f118dea619 100644 --- a/src/bin/eolian_mono/eolian/mono/klass.hh +++ b/src/bin/eolian_mono/eolian/mono/klass.hh @@ -348,6 +348,16 @@ struct klass if(!name_helpers::close_namespaces(sink, cls.namespaces, context)) return false; + if(!as_generator + (lit("#pragma warning disable CS1591\n") // Disabling warnings as DocFx will hide these classes + <<"public static class " << (string % "_") << name_helpers::klass_inherit_name(cls) + << "_ExtensionMethods {\n" + << *((scope_tab << property_extension_method_definition(cls)) << "\n") + << "}\n" + << lit("#pragma warning restore CS1591\n")) + .generate(sink, std::make_tuple(cls.namespaces, cls.properties), context)) + return false; + return true; } diff --git a/src/bindings/mono/efl_mono/Bind.cs b/src/bindings/mono/efl_mono/Bind.cs new file mode 100644 index 0000000000..5a10f780f5 --- /dev/null +++ b/src/bindings/mono/efl_mono/Bind.cs @@ -0,0 +1,36 @@ +#if EFL_BETA + +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; + +namespace Efl { + +/// Representas a bindable property as used by instances. +/// +/// It is internally instantiated and returned by generated extension methods. +/// +public class Bindable +{ + /// Creates a new bindable property with the source name name. + public Bindable(string name, Efl.Ui.IPropertyBind binder) + { + this.name = name; + this.binder = binder; + } + + /// Binds the model property model_property to the property name set in the constructor. + public void Bind(string model_property) + { + binder.PropertyBind(name, model_property); + } + + string name; + Efl.Ui.IPropertyBind binder; +} + +} + +#endif diff --git a/src/bindings/mono/efl_mono/Factory.cs b/src/bindings/mono/efl_mono/Factory.cs new file mode 100644 index 0000000000..d3c9c13e6d --- /dev/null +++ b/src/bindings/mono/efl_mono/Factory.cs @@ -0,0 +1,30 @@ +#if EFL_BETA + +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; + +namespace Efl { namespace Ui { + +/// Helper factory class. Makes use of C# extension methods for easier property binding. +/// +/// +/// var factory = Efl.Ui.Factory<Efl.Ui.Button>(); +/// factory.Style().Bind("Name"); // The factory Style property is bound to the Name property for the given model. +/// +/// +/// +public class ItemFactory : Efl.Ui.CachingFactory, IDisposable +{ + /// Creates a new factory. + public ItemFactory(Efl.Object parent = null) + : base (parent, typeof(T)) + { + } +} + +} } + +#endif diff --git a/src/bindings/mono/efl_mono/GenericModel.cs b/src/bindings/mono/efl_mono/GenericModel.cs new file mode 100644 index 0000000000..1bfa91e53d --- /dev/null +++ b/src/bindings/mono/efl_mono/GenericModel.cs @@ -0,0 +1,174 @@ +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; +using Eina; + +#if EFL_BETA + +namespace Efl { + +/// Generic implementation for MVVM models based on +public class GenericModel : Efl.Object, Efl.IModel, IDisposable +{ + private Efl.IModel model; + + /// Creates a new model wrapping model. + public GenericModel (Efl.IModel model, Efl.Object parent = null) : base(parent) + { + this.model = model; + } + + /// The list of properties available in the wrapped model. + public Eina.Iterator< System.String> Properties + { + get { return GetProperties(); } + } + + /// The number of children in the wrapped model. + public uint ChildrenCount + { + get { return GetChildrenCount(); } + } + + /// The list of properties available in the wrapped model. + public Eina.Iterator GetProperties() + { + return model.GetProperties(); + } + + /// Gets the value of the given property in the wrapped model. + public Eina.Value GetProperty( System.String property) + { + return model.GetProperty(property); + } + + /// Sets the value of the given property in the given model. + public Eina.Future SetProperty( System.String property, Eina.Value value) + { + return model.SetProperty(property, value); + } + + /// Returns the number of children in the wrapped model. + public uint GetChildrenCount() + { + return model.GetChildrenCount(); + } + + /// Returns an that will resolve when the property is ready to be read. + public Eina.Future GetPropertyReady( System.String property) + { + return model.GetPropertyReady(property); + } + + /// Gets a number of children from the wrapped model. + public Eina.Future GetChildrenSlice( uint start, uint count) + { + return model.GetChildrenSlice(start, count); + } + + /// Adds a new object to the wrapper model. + public void Add(T o) + { + Efl.IModel child = (Efl.IModel)this.AddChild(); + ModelHelper.SetProperties(o, child); + } + + /// Adds a new childs to the model and returns it. + public Efl.Object AddChild() + { + return model.AddChild(); + } + + /// Deletes the given child from the wrapped model. + public void DelChild( Efl.Object child) + { + model.DelChild(child); + } + + /// Gets the element at the specified index. + async public System.Threading.Tasks.Task GetAtAsync(uint index) + { + using (Eina.Value v = await GetChildrenSliceAsync(index, 1)) + { + if (v.GetValueType().IsContainer()) + { + var child = (Efl.IModel)v[0]; + T obj = (T)Activator.CreateInstance(typeof(T), new System.Object[] {}); + ModelHelper.GetProperties(obj, child); + return obj; + } + else + { + throw new System.InvalidOperationException("GetChildrenSlice must have returned a container"); + } + } + } + + /// Async wrapper around . + public System.Threading.Tasks.Task SetPropertyAsync( System.String property, Eina.Value value, System.Threading.CancellationToken token=default(System.Threading.CancellationToken)) + { + return model.SetPropertyAsync(property, value, token); + } + + /// Async wrapper around . + public System.Threading.Tasks.Task GetPropertyReadyAsync( System.String property, System.Threading.CancellationToken token=default(System.Threading.CancellationToken)) + { + return model.GetPropertyReadyAsync(property, token); + } + + /// Async wrapper around . + public System.Threading.Tasks.Task GetChildrenSliceAsync( uint start, uint count, System.Threading.CancellationToken token=default(System.Threading.CancellationToken)) + { + return model.GetChildrenSliceAsync(start, count, token); + } + + /// Event triggered when properties on the wrapped model changes. + public event EventHandler PropertiesChangedEvt + { + add { + model.PropertiesChangedEvt += value; + } + remove { + model.PropertiesChangedEvt -= value; + } + } + + /// Event triggered when a child is added from the wrapped model. + public event EventHandler ChildAddedEvt + { + add { + model.ChildAddedEvt += value; + } + remove { + model.ChildAddedEvt -= value; + } + } + + /// Event triggered when a child is removed from the wrapped model. + public event EventHandler ChildRemovedEvt + { + add { + model.ChildRemovedEvt += value; + } + remove { + model.ChildRemovedEvt -= value; + } + } + + /// Event triggered when the number of children changes. + public event EventHandler ChildrenCountChangedEvt + { + add { + model.ChildrenCountChangedEvt += value; + } + remove { + model.ChildrenCountChangedEvt -= value; + } + } +} + +} + +#endif diff --git a/src/bindings/mono/efl_mono/UserModel.cs b/src/bindings/mono/efl_mono/UserModel.cs new file mode 100644 index 0000000000..33d2a36be6 --- /dev/null +++ b/src/bindings/mono/efl_mono/UserModel.cs @@ -0,0 +1,104 @@ +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; +using System.Reflection; + +namespace Efl { + +#if EFL_BETA + +internal class ModelHelper +{ + /// FIXME Move this to eina_value.cs and be a static method of Value? + static internal Eina.Value ValueFromProperty(T source, PropertyInfo property) + { + return new Eina.Value(property.GetValue(source)); + } + + static internal void SetPropertyFromValue(T target, PropertyInfo property, Eina.Value v) + { + property.SetValue(target, v.Unwrap()); + } + + /// Sets the properties of the from the properties of the given object + /// . + static internal void SetProperties(T o, Efl.IModel child) + { + var properties = typeof(T).GetProperties(); + foreach(var prop in properties) + { + child.SetProperty(prop.Name, ValueFromProperty(o, prop)); + } + } + + /// Sets the properties of from the properties of . + static internal void GetProperties(T o, Efl.IModel child) + { + var properties = typeof(T).GetProperties(); + foreach(var prop in properties) + { + using (var v = child.GetProperty(prop.Name)) + { + SetPropertyFromValue(o, prop, v); + } + } + } +} + +/// Helper class to simplify the creation of MVVM Models based on . +/// +/// This class works together with to wrap user defined classes as MVVM models. +/// Example: +/// +/// +/// public class PersonModel +/// { +/// public string Name { get; set; } +/// public int Age { get; set; } +/// } +/// // Instantiating the model +/// var modelData = new Efl.UserModel<PersonModel>(parent); +/// modelData.Add(new PersonModel { Name = "John", Age = 30 }; +/// var model = new Efl.GenericModel<PersonModel>(modelData, parent); +/// PersonModel p = await model.GetAtAsync(0); +/// +/// +[Efl.Eo.BindingEntity] +public class UserModel : Efl.MonoModelInternal, IDisposable +{ + /// Creates a new model. + /// The parent of the model. + public UserModel (Efl.Object parent = null) : base(Efl.MonoModelInternal.efl_mono_model_internal_class_get(), parent) + { + var properties = typeof(T).GetProperties(); + foreach(var prop in properties) + { + AddProperty(prop.Name, Eina.ValueTypeBridge.GetManaged(prop.PropertyType)); + } + } + + /// Disposes of this instance. + ~UserModel() + { + Dispose(false); + } + + /// Adds a new child to the model wrapping the properites of o + /// + /// Reflection is used to instantiate a new for this child and + /// set the mirroring properties correctly. + /// + /// + /// The user model instance to be added to this model. + public void Add (T o) + { + Efl.IModel child = (Efl.IModel) this.AddChild(); + ModelHelper.SetProperties(o, child); + } +} + +#endif + +} diff --git a/src/bindings/mono/efl_mono/meson.build b/src/bindings/mono/efl_mono/meson.build index 3edefb3c0c..fb2b2c0431 100644 --- a/src/bindings/mono/efl_mono/meson.build +++ b/src/bindings/mono/efl_mono/meson.build @@ -1,6 +1,10 @@ mono_files += files( 'efl_all.cs', - 'efl_csharp_application.cs' + 'efl_csharp_application.cs', + 'UserModel.cs', + 'GenericModel.cs', + 'Factory.cs', + 'Bind.cs' ) bash = find_program('bash') @@ -29,3 +33,52 @@ efl_src = configure_file( output: 'efl_libs.cs', configuration: efl_libs ) + +mono_eo_files = [ + 'efl_mono_model_internal.eo', + 'efl_mono_model_internal_child.eo' +] + +# mono_eo_c_files = [] + +pub_eo_file_target = [] + +foreach eo_file : mono_eo_files + pub_eo_file_target += custom_target('eolian_gen_' + eo_file, + input : join_paths('..', '..', '..', 'lib', 'efl_mono', eo_file), + output : [eo_file + '.h'], + depfile : eo_file + '.d', + install : false, + command : eolian_gen + [ '-I', meson.current_source_dir(), eolian_include_directories, + '-o', 'h:' + join_paths(meson.current_build_dir(), eo_file + '.h'), + '-o', 'c:' + join_paths(meson.current_build_dir(), eo_file + '.c'), + '-o', 'd:' + join_paths(meson.current_build_dir(), eo_file + '.d'), + '-gchd', '@INPUT@']) + # mono_eo_c_files += join_paths(meson.current_build_dir(), eo_file + '.c') +endforeach + +efl_mono_lib = library('eflcustomexportsmono', + [ + join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_custom_exports_mono.c'), + join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_mono_model_internal.c'), + ], + pub_eo_file_target, + install : true, + include_directories : config_dir + [include_directories(join_paths('.'))], + dependencies : [eo, eina, ecore], + version : meson.project_version() +) + +foreach eo_file : mono_eo_files + if not blacklisted_files.contains(eo_file) + mono_generator_target += custom_target('eolian_mono_gen_'+eo_file.underscorify()+'', + input : join_paths('..', '..', '..', 'lib', 'efl_mono', eo_file), + output : [eo_file + '.cs'], + command : [eolian_mono_gen, beta_option, '-I', meson.current_source_dir(), eolian_include_directories, + '--dllimport', 'eflcustomexportsmono', + '-o', join_paths(meson.current_build_dir(), eo_file + '.cs'), + '-e', get_option('mono-examples-dir'), + '@INPUT@']) + + endif +endforeach diff --git a/src/bindings/mono/eo_mono/FunctionWrapper.cs b/src/bindings/mono/eo_mono/FunctionWrapper.cs index bdbca77d93..04a5f05614 100644 --- a/src/bindings/mono/eo_mono/FunctionWrapper.cs +++ b/src/bindings/mono/eo_mono/FunctionWrapper.cs @@ -131,7 +131,7 @@ public class FunctionLoadResult { if (_Delegate == null) { - throw new InvalidOperationException($"Trying to get Delegate while not loaded. Load result: {Kind}"); + throw new InvalidOperationException($"Trying to get Delegate of type {typeof(T).FullName} while not loaded. Load result: {Kind}"); } return _Delegate; diff --git a/src/bindings/mono/meson.build b/src/bindings/mono/meson.build index 5637595f80..8a87da9572 100644 --- a/src/bindings/mono/meson.build +++ b/src/bindings/mono/meson.build @@ -82,13 +82,6 @@ blacklisted_files = [ 'elm_atspi_app_object.eo', ] -efl_mono_lib = library('eflcustomexportsmono', - join_paths('..', '..', 'lib', 'efl_mono', 'efl_custom_exports_mono.c'), - install : true, - dependencies : [eo, eina, ecore], - version : meson.project_version() -) - beta_option = [] if (get_option('mono-beta')) beta_option = '-b' diff --git a/src/lib/efl/interfaces/efl_model.eo b/src/lib/efl/interfaces/efl_model.eo index 40790a5153..b6e78cc04a 100644 --- a/src/lib/efl/interfaces/efl_model.eo +++ b/src/lib/efl/interfaces/efl_model.eo @@ -121,7 +121,7 @@ interface @beta Efl.Model ignored.]] } /* XXX: is this right? */ - return: future>; [[Array of children]] + return: future>; [[Array of children]] } @property children_count { get { diff --git a/src/lib/efl_mono/efl_mono_model_internal.c b/src/lib/efl_mono/efl_mono_model_internal.c new file mode 100644 index 0000000000..cda6d66e14 --- /dev/null +++ b/src/lib/efl_mono/efl_mono_model_internal.c @@ -0,0 +1,229 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Efl.h" +#include "Ecore.h" +#include + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# define EAPI __declspec(dllexport) +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + + +#include "efl_mono_model_internal.eo.h" +#include "efl_mono_model_internal_child.eo.h" + +#include "assert.h" + +typedef struct _Efl_Mono_Model_Internal_Data Efl_Mono_Model_Internal_Data; + +typedef struct _Properties_Info Properties_Info; +struct _Properties_Info +{ + const char* name; + const Eina_Value_Type* type; +}; + +typedef struct _Efl_Mono_Model_Internal_Data +{ + Eina_Array *properties_info; + Eina_Array *properties_names; + Eina_Array *items; +} _Efl_Mono_Model_Internal_Data; + + +#define MY_CLASS EFL_MONO_MODEL_INTERNAL_CLASS + +typedef struct _Efl_Mono_Model_Internal_Child_Data +{ + Efl_Mono_Model_Internal_Data* model_pd; + size_t index; + Eina_Array *values; + Eo* child; + //Eina_Array *items; +} Efl_Mono_Model_Internal_Child_Data; + +static int _find_property_index (const char* name, Eina_Array* properties_name) +{ + int i, size = eina_array_count_get(properties_name); + for (i = 0; i != size; ++i) + { + if (!strcmp(properties_name->data[i], name)) + { + return i; + } + } + return -1; +} + +static Eo * +_efl_mono_model_internal_efl_object_constructor(Eo *obj, Efl_Mono_Model_Internal_Data *pd) +{ + obj = efl_constructor(efl_super(obj, MY_CLASS)); + + pd->properties_info = eina_array_new(5); + pd->properties_names = eina_array_new(5); + pd->items = eina_array_new(5); + + if (!obj) return NULL; + + return obj; +} + +static void +_efl_mono_model_internal_efl_object_destructor(Eo *obj, Efl_Mono_Model_Internal_Data *pd EINA_UNUSED) +{ + efl_destructor(efl_super(obj, MY_CLASS)); +} + +static void +_efl_mono_model_internal_add_property(Eo *obj EINA_UNUSED, Efl_Mono_Model_Internal_Data *pd, const char *name, const Eina_Value_Type *type) +{ + Properties_Info* info = malloc(sizeof(Properties_Info)); + info->name = eina_stringshare_add(name); + info->type = type; + eina_array_push (pd->properties_info, info); + eina_array_push (pd->properties_names, eina_stringshare_add(info->name)); +} + + +static Eina_Iterator * +_efl_mono_model_internal_efl_model_properties_get(const Eo *obj EINA_UNUSED, Efl_Mono_Model_Internal_Data *pd EINA_UNUSED) +{ + return eina_array_iterator_new (NULL); +} + +static Efl_Object* +_efl_mono_model_internal_efl_model_child_add(Eo *obj, Efl_Mono_Model_Internal_Data *pd) +{ + Efl_Mono_Model_Internal_Child* child = efl_add (EFL_MONO_MODEL_INTERNAL_CHILD_CLASS, obj); + assert (child != NULL); + Efl_Mono_Model_Internal_Child_Data* pcd = efl_data_xref (child, EFL_MONO_MODEL_INTERNAL_CHILD_CLASS, obj); + pcd->model_pd = pd; + pcd->index = eina_array_count_get(pd->items); + pcd->child = child; + pcd->values = eina_array_new(5); + eina_array_push (pd->items, pcd); + + return child; +} + +static unsigned int +_efl_mono_model_internal_efl_model_children_count_get(const Eo *obj EINA_UNUSED, Efl_Mono_Model_Internal_Data *pd) +{ + return eina_array_count_get(pd->items); +} + +static Eina_Future * +_efl_mono_model_internal_child_efl_model_property_set(Eo *obj, Efl_Mono_Model_Internal_Child_Data *pd, const char *property, Eina_Value *value) +{ + int i = _find_property_index (property, pd->model_pd->properties_names); + int j; + Eina_Value* old_value; + Eina_Value* new_value; + Eina_Value tmp_value; + + if (i >= 0) + { + for (j = i - eina_array_count_get (pd->values); j >= 0; --j) + { + eina_array_push (pd->values, (void*)1); + pd->values->data[pd->values->count-1] = NULL; + } + + old_value = eina_array_data_get (pd->values, i); + if (old_value) + eina_value_free (old_value); + new_value = malloc (sizeof(Eina_Value)); + eina_value_copy (value, new_value); + eina_value_copy (value, &tmp_value); + eina_array_data_set (pd->values, i, new_value); + + + return efl_loop_future_resolved(obj, tmp_value); + } + else + { + // not found property + return efl_loop_future_rejected(obj, EAGAIN); + } +} + +static Eina_Value * +_efl_mono_model_internal_child_efl_model_property_get(const Eo *obj EINA_UNUSED, Efl_Mono_Model_Internal_Child_Data *pd EINA_UNUSED, const char *property EINA_UNUSED) +{ + unsigned int i = _find_property_index (property, pd->model_pd->properties_names); + if(eina_array_count_get (pd->values) <= i + || eina_array_data_get (pd->values, i) == NULL) + return eina_value_error_new(EAGAIN); + else + { + Eina_Value* src = eina_array_data_get(pd->values, i); + Eina_Value* clone = malloc (sizeof(Eina_Value)); + eina_value_copy (src, clone); + return clone; + } +} + +static Eina_Future * +_efl_mono_model_internal_efl_model_children_slice_get(Eo *obj, Efl_Mono_Model_Internal_Data *pd, unsigned int start, unsigned int count EINA_UNUSED) +{ + unsigned int i; + Eina_Value array = EINA_VALUE_EMPTY; + Efl_Mono_Model_Internal_Child_Data* pcd; + + eina_value_array_setup(&array, EINA_VALUE_TYPE_OBJECT, count % 8); + + for (i = start; i != start + count; ++i) + { + pcd = eina_array_data_get(pd->items, i); + eina_value_array_append (&array, pcd->child); + } + + return efl_loop_future_resolved(obj, array); +} + +static Eo * +_efl_mono_model_internal_child_efl_object_constructor(Eo *obj, Efl_Mono_Model_Internal_Child_Data *pd EINA_UNUSED) +{ + obj = efl_constructor(efl_super(obj, EFL_MONO_MODEL_INTERNAL_CHILD_CLASS)); + + return obj; +} + +static void +_efl_mono_model_internal_child_efl_object_destructor(Eo *obj, Efl_Mono_Model_Internal_Child_Data *pd EINA_UNUSED) +{ + efl_destructor(efl_super(obj, EFL_MONO_MODEL_INTERNAL_CHILD_CLASS)); +} + +static Efl_Object* +_efl_mono_model_internal_child_efl_model_child_add(Eo *obj EINA_UNUSED, Efl_Mono_Model_Internal_Child_Data *pd EINA_UNUSED) +{ + abort(); + return NULL; +} + +static Eina_Iterator * +_efl_mono_model_internal_child_efl_model_properties_get(const Eo *obj EINA_UNUSED, Efl_Mono_Model_Internal_Child_Data *pd) +{ + return eina_array_iterator_new (pd->model_pd->properties_names); +} + +#include "efl_mono_model_internal.eo.c" +#include "efl_mono_model_internal_child.eo.c" diff --git a/src/lib/efl_mono/efl_mono_model_internal.eo b/src/lib/efl_mono/efl_mono_model_internal.eo new file mode 100644 index 0000000000..3a639defe7 --- /dev/null +++ b/src/lib/efl_mono/efl_mono_model_internal.eo @@ -0,0 +1,19 @@ +class @beta Efl.Mono_Model_Internal extends Efl.Loop_Consumer implements Efl.Model +{ + methods { + add_property { + params { + @in name: string; + @in type: ptr(const(Eina.Value_Type)); + } + } + } + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Model.properties { get; } + Efl.Model.child_add; + Efl.Model.children_count { get; } + Efl.Model.children_slice_get; + } +} diff --git a/src/lib/efl_mono/efl_mono_model_internal_child.eo b/src/lib/efl_mono/efl_mono_model_internal_child.eo new file mode 100644 index 0000000000..ec8d657ca4 --- /dev/null +++ b/src/lib/efl_mono/efl_mono_model_internal_child.eo @@ -0,0 +1,10 @@ +class Efl.Mono_Model_Internal_Child extends Efl.Loop_Consumer implements Efl.Model +{ + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Model.properties { get; } + Efl.Model.property { get; set; } + Efl.Model.child_add; + } +} diff --git a/src/tests/efl_mono/Main.cs b/src/tests/efl_mono/Main.cs index f154f935eb..06add7edd2 100644 --- a/src/tests/efl_mono/Main.cs +++ b/src/tests/efl_mono/Main.cs @@ -15,7 +15,7 @@ class TestMain static int Main(string[] args) { - Efl.All.Init(); + Efl.All.Init(Efl.Csharp.Components.Ui); bool pass = true; diff --git a/src/tests/efl_mono/Model.cs b/src/tests/efl_mono/Model.cs new file mode 100644 index 0000000000..a43d9da51c --- /dev/null +++ b/src/tests/efl_mono/Model.cs @@ -0,0 +1,83 @@ +#define CODE_ANALYSIS + +using System; +using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + +#if EFL_BETA + +namespace TestSuite { + +[SuppressMessage("Gendarme.Rules.Portability", "DoNotHardcodePathsRule")] +public class TestModel { + + public class VeggieViewModel + { + public string Name { get; set; } + public string Type { get; set; } + public string Image { get; set; } + } + + private static Efl.UserModel CreateModel(Efl.Loop loop) + { + Efl.UserModel veggies = new Efl.UserModel(loop); + veggies.Add (new VeggieViewModel{ Name="Tomato", Type="Fruit", Image="tomato.png"}); + veggies.Add (new VeggieViewModel{ Name="Romaine Lettuce", Type="Vegetable", Image="lettuce.png"}); + veggies.Add (new VeggieViewModel{ Name="Zucchini", Type="Vegetable", Image="zucchini.png"}); + + return veggies; + } + + public static void reflection_test () + { + Efl.Loop loop = Efl.App.AppMain; + + var veggies = CreateModel(loop); + } + + internal static async Task EasyModelExtractionAsync (Efl.Loop loop) + { + var veggies = CreateModel(loop); + + var model = new Efl.GenericModel(veggies, loop); + Test.AssertEquals(3, (int)model.GetChildrenCount()); + + VeggieViewModel r2 = await model.GetAtAsync(1); + Test.AssertEquals(r2.Name, "Romaine Lettuce"); + + VeggieViewModel r = await model.GetAtAsync(0); + Test.AssertEquals(r.Name, "Tomato"); + + loop.End(); + } + + public static void easy_model_extraction () + { + Efl.Loop loop = Efl.App.AppMain; + Task task = EasyModelExtractionAsync(loop); + + loop.Begin(); + + task.Wait(); + } + + public static void factory_test () + { + string propertyBound = null; + bool callbackCalled = false; + var factory = new Efl.Ui.ItemFactory(); + factory.PropertyBoundEvt += (object sender, Efl.Ui.IPropertyBindPropertyBoundEvt_Args args) => { + propertyBound = args.arg; + callbackCalled = true; + }; + + factory.Style().Bind("first name"); + + Test.Assert(callbackCalled, "Property bound callback must have been called."); + Test.AssertEquals(propertyBound, "style"); + } +} + +} + +#endif diff --git a/src/tests/efl_mono/meson.build b/src/tests/efl_mono/meson.build index e464cdd2a8..445c823caa 100644 --- a/src/tests/efl_mono/meson.build +++ b/src/tests/efl_mono/meson.build @@ -72,6 +72,7 @@ efl_mono_src = [ 'Events.cs', 'FunctionPointers.cs', 'FunctionPointerMarshalling.cs', + 'Model.cs', 'Parts.cs', 'Promises.cs', 'Strbuf.cs', @@ -111,7 +112,7 @@ custom_target('copy_efl_mono_lib_dll', command : [copy_prog, '@INPUT@', '@OUTPUT@']) endif -config_libs = ['eina', 'ecore', 'eo', 'efl', 'evas', 'eldbus', 'elementary'] +config_libs = ['eina', 'ecore', 'eo', 'efl', 'evas', 'eldbus', 'elementary', 'efl_mono'] load_lib = efl_mono_test_suite_path + ':' foreach config : config_libs