zmike pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=40def3eac914138189163a895527705a1ffefcfd

commit 40def3eac914138189163a895527705a1ffefcfd
Author: Lauro Moura <lauromo...@expertisesolutions.com.br>
Date:   Mon Aug 5 10:17:52 2019 -0400

    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/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 ++
 src/lib/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(-)

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<typename OutputIterator, typename Context>
+   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<options_context>(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 << "<T>(this Efl.Ui.ItemFactory<T> 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<typename OutputIterator, typename Context>
@@ -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<int, 1> {};
 
+template <>
+struct attributes_needed< 
::eolian_mono::property_extension_method_definition_generator> : 
std::integral_constant<int, 1> {};
+
 template <>
 struct attributes_needed< 
::eolian_mono::property_wrapper_definition_generator> : 
std::integral_constant<int, 1> {};
 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 {
+
+/// <summary>Representas a bindable property as used by <see 
cref="Efl.Ui.ItemFactory&lt;T&gt;" /> instances.
+///
+/// <para>It is internally instantiated and returned by generated extension 
methods.</para>
+/// </summary>
+public class Bindable<T>
+{
+    /// <summary>Creates a new bindable property with the source name 
<c>name</c>.</summary>
+    public Bindable(string name, Efl.Ui.IPropertyBind binder)
+    {
+        this.name = name;
+        this.binder = binder;
+    }
+
+    /// <summary>Binds the model property <c>model_property</c> to the 
property <c>name</c> set in the constructor.</summary>
+    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 {
+
+/// <summary>Helper factory class. Makes use of C# extension methods for 
easier property binding.
+///
+/// <code>
+/// var factory = Efl.Ui.Factory&lt;Efl.Ui.Button&gt;();
+/// factory.Style().Bind("Name"); // The factory Style property is bound to 
the Name property for the given model.
+/// </code>
+///
+/// </summary>
+public class ItemFactory<T> : Efl.Ui.CachingFactory, IDisposable
+{
+    /// <summary>Creates a new factory.</summary>
+    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 {
+
+/// <summary>Generic <see cref="Efl.IModel" /> implementation for MVVM models 
based on <see cref="Efl.UserModel&lt;T&gt;" /></summary>
+public class GenericModel<T> : Efl.Object, Efl.IModel, IDisposable
+{
+   private Efl.IModel model;
+
+   /// <summary>Creates a new model wrapping <c>model</c>.</summary>
+   public GenericModel (Efl.IModel model, Efl.Object parent = null) : 
base(parent)
+   {
+       this.model = model;
+   }
+
+   /// <summary>The list of properties available in the wrapped 
model.</summary>
+   public Eina.Iterator< System.String> Properties
+   {
+      get { return GetProperties(); }
+   }
+
+   /// <summary>The number of children in the wrapped model.</summary>
+   public  uint ChildrenCount
+   {
+      get { return GetChildrenCount(); }
+   }
+
+   /// <summary>The list of properties available in the wrapped 
model.</summary>
+   public Eina.Iterator<System.String> GetProperties()
+   {
+       return model.GetProperties();
+   }
+
+   /// <summary>Gets the value of the given property in the wrapped 
model.</summary>
+   public Eina.Value GetProperty(  System.String property)
+   {
+       return model.GetProperty(property);
+   }
+
+   /// <summary>Sets the value of the given property in the given 
model.</summary>
+   public Eina.Future SetProperty(  System.String property,   Eina.Value value)
+   {
+       return model.SetProperty(property, value);
+   }
+
+   /// <summary>Returns the number of children in the wrapped model.</summary>
+   public uint GetChildrenCount()
+   {
+       return model.GetChildrenCount();
+   }
+
+   /// <summary>Returns an <see cref="Eina.Future" /> that will resolve when 
the property is ready to be read.</summary>
+   public Eina.Future GetPropertyReady(  System.String property)
+   {
+       return model.GetPropertyReady(property);
+   }
+
+   /// <summary>Gets a number of children from the wrapped model.</summary>
+   public Eina.Future GetChildrenSlice(  uint start,   uint count)
+   {
+       return model.GetChildrenSlice(start, count);
+   }
+
+   /// <summary>Adds a new object to the wrapper model.</summary>
+   public void Add(T o)
+   {
+      Efl.IModel child = (Efl.IModel)this.AddChild();
+      ModelHelper.SetProperties(o, child);
+   }
+
+   /// <summary>Adds a new childs to the model and returns it.</summary>
+   public Efl.Object AddChild()
+   {
+       return model.AddChild();
+   }
+
+   /// <summary>Deletes the given <c>child</c> from the wrapped 
model.</summary>
+   public void DelChild( Efl.Object child)
+   {
+       model.DelChild(child);
+   }
+
+   /// <summary>Gets the element at the specified <c>index</c>.</summary>
+   async public System.Threading.Tasks.Task<T> 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");
+           }
+       }
+   }
+
+   /// <summary>Async wrapper around <see cref="SetProperty(System.String, 
Eina.Value)" />.</summary>
+   public System.Threading.Tasks.Task<Eina.Value> SetPropertyAsync(  
System.String property,  Eina.Value value, System.Threading.CancellationToken 
token=default(System.Threading.CancellationToken))
+   {
+       return model.SetPropertyAsync(property, value, token);
+   }
+
+   /// <summary>Async wrapper around <see 
cref="GetPropertyReady(System.String)" />.</summary>
+   public System.Threading.Tasks.Task<Eina.Value> GetPropertyReadyAsync(  
System.String property, System.Threading.CancellationToken 
token=default(System.Threading.CancellationToken))
+   {
+       return model.GetPropertyReadyAsync(property, token);
+   }
+
+   /// <summary>Async wrapper around <see cref="GetChildrenSlice(uint, uint)" 
/>.</summary>
+   public System.Threading.Tasks.Task<Eina.Value> GetChildrenSliceAsync(  uint 
start,  uint count, System.Threading.CancellationToken 
token=default(System.Threading.CancellationToken))
+   {
+       return model.GetChildrenSliceAsync(start, count, token);
+   }
+
+   /// <summary>Event triggered when properties on the wrapped model 
changes.</summary>
+   public event EventHandler<Efl.IModelPropertiesChangedEvt_Args> 
PropertiesChangedEvt
+   {
+      add {
+          model.PropertiesChangedEvt += value;
+      }
+      remove {
+          model.PropertiesChangedEvt -= value;
+      }
+   }
+
+   /// <summary>Event triggered when a child is added from the wrapped 
model.</summary>
+   public event EventHandler<Efl.IModelChildAddedEvt_Args> ChildAddedEvt
+   {
+      add {
+          model.ChildAddedEvt += value;
+      }
+      remove {
+          model.ChildAddedEvt -= value;
+      }
+   }
+
+   /// <summary>Event triggered when a child is removed from the wrapped 
model.</summary>
+   public event EventHandler<Efl.IModelChildRemovedEvt_Args> ChildRemovedEvt
+   {
+      add {
+          model.ChildRemovedEvt += value;
+      }
+      remove {
+          model.ChildRemovedEvt -= value;
+      }
+   }
+
+   /// <summary>Event triggered when the number of children changes.</summary>
+   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>(T source, PropertyInfo 
property)
+    {
+        return new Eina.Value(property.GetValue(source));
+    }
+
+    static internal void SetPropertyFromValue<T>(T target, PropertyInfo 
property, Eina.Value v)
+    {
+        property.SetValue(target, v.Unwrap());
+    }
+
+    /// <summary>Sets the properties of the <paramref name="child"/> from the 
properties of the given object
+    /// <paramref name="o"/>.</summary>
+    static internal void SetProperties<T>(T o, Efl.IModel child)
+    {
+        var properties = typeof(T).GetProperties();
+        foreach(var prop in properties)
+        {
+            child.SetProperty(prop.Name, ValueFromProperty(o, prop));
+        }
+    }
+
+    /// <summary>Sets the properties of <paramref name="o"/> from the 
properties of <paramref name="child"/>.</summary>
+    static internal void GetProperties<T>(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);
+            }
+        }
+    }
+}
+
+/// <summary>Helper class to simplify the creation of MVVM Models based on 
<see cref="Efl.IModel" />.
+///
+/// <para>This class works together with <see cref="Efl.GenericModel&lt;T&gt;" 
/> to wrap user defined classes as MVVM models.
+/// Example:</para>
+///
+/// <code>
+/// public class PersonModel
+/// {
+///     public string Name { get; set; }
+///     public int Age { get; set; }
+/// }
+/// // Instantiating the model
+/// var modelData = new Efl.UserModel&lt;PersonModel&gt;(parent);
+/// modelData.Add(new PersonModel { Name = "John", Age = 30 };
+/// var model = new Efl.GenericModel&lt;PersonModel&gt;(modelData, parent);
+/// PersonModel p = await model.GetAtAsync(0);
+/// </code>
+/// </summary>
+[Efl.Eo.BindingEntity]
+public class UserModel<T> : Efl.MonoModelInternal, IDisposable
+{
+   /// <summary>Creates a new model.</summary>
+   /// <param name="parent">The parent of the model.</param>
+   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));
+     }
+   }
+
+   /// <summary>Disposes of this instance.</summary>
+   ~UserModel()
+   {
+       Dispose(false);
+   }
+
+   /// <summary>Adds a new child to the model wrapping the properites of 
<c>o</c>
+   ///
+   /// <para>Reflection is used to instantiate a new <see cref="Efl.IModel" /> 
for this child and
+   /// set the mirroring properties correctly.</para>
+   /// </summary>
+   ///
+   /// <param name="o">The user model instance to be added to this 
model.</param>
+   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<T>
         {
             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<accessor<Efl.Object>>; [[Array of children]]
+            return: future<array<Efl.Object>>; [[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 <config.h>
+#endif
+
+#include "Efl.h"
+#include "Ecore.h"
+#include <Eo.h>
+
+#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<VeggieViewModel> CreateModel(Efl.Loop loop)
+    {
+        Efl.UserModel<VeggieViewModel> veggies = new 
Efl.UserModel<VeggieViewModel>(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<VeggieViewModel>(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<Efl.Ui.Button>();
+        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

-- 


Reply via email to