felipealmeida pushed a commit to branch master.

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

commit cc49c1034b1114e1ce9d10d8651ec6d24c1faaa1
Author: Lauro Moura <[email protected]>
Date:   Mon Aug 19 21:31:20 2019 -0300

    csharp: MVVM parts support
    
    Summary:
    Parts binding will follow a similar approach to regular property
    binding:
    
    `var error = factory.PartName().PropertyName().Bind(modelProperty);`
    
    * Changed both `Bind()` overloads to return the error code from
      `efl_ui_property_bind`
    * Also properties from interfaces implemented didn't have their `Bindable`
      wrapper methds available.
    
    Depends on D9563
    
    Reviewers: felipealmeida, cedric, SanghyeonLee, woohyun
    
    Reviewed By: cedric
    
    Tags: #efl
    
    Differential Revision: https://phab.enlightenment.org/D9564
---
 .../eolian_mono/eolian/mono/function_definition.hh | 22 +++++-
 src/bin/eolian_mono/eolian/mono/helpers.hh         | 12 ++++
 src/bin/eolian_mono/eolian/mono/klass.hh           | 12 +++-
 src/bin/eolian_mono/eolian/mono/part_definition.hh | 40 +++++++++++
 src/bindings/mono/efl_mono/Bind.cs                 | 82 +++++++++++++++++++---
 src/tests/efl_mono/Parts.cs                        | 13 ++++
 6 files changed, 170 insertions(+), 11 deletions(-)

diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh 
b/src/bin/eolian_mono/eolian/mono/function_definition.hh
index cf85f6844d..f010f6dc2f 100644
--- a/src/bin/eolian_mono/eolian/mono/function_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh
@@ -251,8 +251,26 @@ struct property_extension_method_definition_generator
       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"
+          if (!as_generator("public static Efl.BindableProperty<" << 
type(true) << "> " << managed_name << "<T>(this Efl.Ui.ItemFactory<T> fac, 
Efl.Csharp.ExtensionTag<"
+                                        << 
name_helpers::klass_full_concrete_or_interface_name(cls)
+                                        << ", T>magic = null) where T : " << 
name_helpers::klass_full_concrete_or_interface_name(cls) <<  " {\n"
+                            << scope_tab << scope_tab << "return new 
Efl.BindableProperty<" << type(true) << ">(\"" << property.name << "\", fac);\n"
+                            << scope_tab << "}\n"
+                            ).generate(sink, std::make_tuple(prop_type, 
prop_type), context))
+            return false;
+        }
+
+      // Do we need BindablePart extensions for this class?
+      if (!helpers::inherits_from(cls, "Efl.Ui.LayoutPart"))
+        return true;
+
+      if (property.setter.is_engaged())
+        {
+          attributes::type_def prop_type = property.setter->parameters[0].type;
+          if (!as_generator("public static Efl.BindableProperty<" << 
type(true) << "> " << managed_name << "<T>(this Efl.BindablePart<T> part, 
Efl.Csharp.ExtensionTag<"
+                                        << 
name_helpers::klass_full_concrete_or_interface_name(cls)
+                                        << ", T>magic = null) where T : " << 
name_helpers::klass_full_concrete_or_interface_name(cls) <<  " {\n"
+                            << scope_tab << scope_tab << "return new 
Efl.BindableProperty<" << type(true) << ">(part.PartName, \"" << property.name 
<< "\", part.Binder);\n"
                             << scope_tab << "}\n"
                             ).generate(sink, std::make_tuple(prop_type, 
prop_type), context))
             return false;
diff --git a/src/bin/eolian_mono/eolian/mono/helpers.hh 
b/src/bin/eolian_mono/eolian/mono/helpers.hh
index c3e1114c98..a7c27c4809 100644
--- a/src/bin/eolian_mono/eolian/mono/helpers.hh
+++ b/src/bin/eolian_mono/eolian/mono/helpers.hh
@@ -163,6 +163,18 @@ bool has_regular_ancestor(attributes::klass_def const& cls)
    return std::any_of(inherits.begin(), inherits.end(), is_regular);
 }
 
+/*
+ * Sugar for checking if a given class in in the inheritance tree
+ */
+bool inherits_from(attributes::klass_def const& cls, std::string const& name)
+{
+   return std::any_of(cls.inherits.begin(), cls.inherits.end(),
+           [&](attributes::klass_name const& inherit)
+           {
+                return 
name_helpers::klass_full_concrete_or_interface_name(inherit) == name;
+           });
+}
+
 /*
  * Gets all methods that this class should implement (i.e. that come from an 
unimplemented interface/mixin and the class itself)
  */
diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh 
b/src/bin/eolian_mono/eolian/mono/klass.hh
index f118dea619..9c466d7c24 100644
--- a/src/bin/eolian_mono/eolian/mono/klass.hh
+++ b/src/bin/eolian_mono/eolian/mono/klass.hh
@@ -348,14 +348,24 @@ struct klass
      if(!name_helpers::close_namespaces(sink, cls.namespaces, context))
        return false;
 
+     std::vector<attributes::property_def> implementable_properties;
+     std::copy(cls.properties.begin(), cls.properties.end(), 
std::back_inserter(implementable_properties));
+
+     for (auto&& klass : helpers::non_implemented_interfaces(cls, context))
+     {
+         attributes::klass_def c(get_klass(klass, cls.unit), cls.unit);
+         std::copy(c.properties.begin(), c.properties.end(), 
std::back_inserter(implementable_properties));
+     }
+
      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")
+         << *((scope_tab << part_extension_method_definition(cls)) << "\n")
          << "}\n"
          << lit("#pragma warning restore CS1591\n"))
-        .generate(sink, std::make_tuple(cls.namespaces, cls.properties), 
context))
+        .generate(sink, std::make_tuple(cls.namespaces, 
implementable_properties, cls.parts), context))
      return false;
 
      return true;
diff --git a/src/bin/eolian_mono/eolian/mono/part_definition.hh 
b/src/bin/eolian_mono/eolian/mono/part_definition.hh
index 484cd0d65a..ec6ef61551 100644
--- a/src/bin/eolian_mono/eolian/mono/part_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/part_definition.hh
@@ -35,6 +35,39 @@ struct part_definition_generator
 
 } const part_definition {};
 
+struct part_extension_method_definition_generator
+{
+   template<typename OutputIterator, typename Context>
+   bool generate(OutputIterator sink, attributes::part_def const& part, 
Context context) const
+   {
+      if (blacklist::is_class_blacklisted(part.klass, context))
+        return true;
+
+      auto part_klass_name = 
name_helpers::klass_full_concrete_or_interface_name(part.klass);
+      /* auto unit = (const Eolian_Unit*) 
context_find_tag<eolian_state_context>(context).state; */
+      /* auto klass = get_klass(part.klass, unit); */
+
+      if (!as_generator(
+                scope_tab << "public static Efl.BindablePart<" << 
part_klass_name << "> " << name_helpers::managed_part_name(part) << "<T>(this 
Efl.Ui.ItemFactory<T> fac, Efl.Csharp.ExtensionTag<"
+                            << 
name_helpers::klass_full_concrete_or_interface_name(cls)
+                            << ", T> x=null) where T : " << 
name_helpers::klass_full_concrete_or_interface_name(cls) << "\n"
+                << scope_tab << "{\n"
+                << scope_tab << scope_tab << "return new Efl.BindablePart<" << 
part_klass_name << ">(\"" << part.name << "\" ,fac);\n"
+                << scope_tab << "}\n"
+            ).generate(sink, attributes::unused, context))
+        return false;
+
+      return true;
+   }
+
+   grammar::attributes::klass_def const& cls;
+};
+
+part_extension_method_definition_generator part_extension_method_definition 
(grammar::attributes::klass_def const& cls)
+{
+  return {cls};
+}
+
 }
 
 namespace efl { namespace eolian { namespace grammar {
@@ -44,10 +77,17 @@ struct is_eager_generator< 
::eolian_mono::part_definition_generator> : std::true
 template <>
 struct is_generator< ::eolian_mono::part_definition_generator> : 
std::true_type {};
 
+template <>
+struct is_eager_generator< 
::eolian_mono::part_extension_method_definition_generator> : std::true_type {};
+template <>
+struct is_generator< 
::eolian_mono::part_extension_method_definition_generator> : std::true_type {};
+
 namespace type_traits {
 template <>
 struct attributes_needed< ::eolian_mono::part_definition_generator> : 
std::integral_constant<int, 1> {};
 
+template <>
+struct attributes_needed< 
::eolian_mono::part_extension_method_definition_generator> : 
std::integral_constant<int, 1> {};
 }
 } } }
 
diff --git a/src/bindings/mono/efl_mono/Bind.cs 
b/src/bindings/mono/efl_mono/Bind.cs
index 5a10f780f5..c1e2a660d7 100644
--- a/src/bindings/mono/efl_mono/Bind.cs
+++ b/src/bindings/mono/efl_mono/Bind.cs
@@ -8,29 +8,95 @@ using System.ComponentModel;
 
 namespace Efl {
 
-/// <summary>Representas a bindable property as used by <see 
cref="Efl.Ui.ItemFactory&lt;T&gt;" /> instances.
+/// <summary>Represents 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>
+public class BindableProperty<T>
 {
+
     /// <summary>Creates a new bindable property with the source name 
<c>name</c>.</summary>
-    public Bindable(string name, Efl.Ui.IPropertyBind binder)
+    public BindableProperty(string name, Efl.Ui.IPropertyBind binder)
     {
-        this.name = name;
+        this.propertyName = name;
+        this.partName = null;
         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)
+    /// <summary>Creates a new bindable property for part 
<c>part</c>.</summary>
+    public BindableProperty(string partName, string partProperty, 
Efl.Ui.IPropertyBind binder)
     {
-        binder.PropertyBind(name, model_property);
+        this.partName = partName;
+        this.propertyName = partProperty;
+        this.binder = binder;
     }
 
-    string name;
+    /// <summary>Binds the model property <c>modelProperty</c> to the property 
<c>name</c> set in the constructor.</summary>
+    public Eina.Error Bind(string modelProperty)
+    {
+        if (this.partName == null)
+        {
+            return this.binder.PropertyBind(this.propertyName, modelProperty);
+        }
+        else
+        {
+            var partHolder = this.binder as Efl.IPart;
+
+            if (partHolder == null)
+            {
+                throw new InvalidOperationException($"Failed to cast binder 
{binder} to IPart");
+            }
+
+            var partBinder = partHolder.GetPart(this.partName) as 
Efl.Ui.IPropertyBind;
+
+            if (partBinder != null)
+            {
+                return partBinder.PropertyBind(this.propertyName, 
modelProperty);
+            }
+            else
+            {
+                throw new InvalidOperationException($"Failed to get part 
{this.partName}");
+            }
+        }
+    }
+
+    string propertyName;
+    string partName;
     Efl.Ui.IPropertyBind binder;
 }
 
+/// <summary>Represents bindable parts 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 BindablePart<T>
+{
+    /// <summary>Creates a new bindable property with the binder 
<c>binder</c>.</summary>
+    public BindablePart(string partName, Efl.Ui.IPropertyBind binder)
+    {
+        this.PartName = partName;
+        this.Binder = binder;
+    }
+
+    /// <summary>The name of the part this instance wraps.</summary>
+    public string PartName { get; private set; }
+    /// <summary>The binder that will be used to bind the properties.</summary>
+    public Efl.Ui.IPropertyBind Binder { get; private set; }
+}
+
+namespace Csharp
+{
+
+/// <summary>Helper class to differentiate between factory extension methods.
+///
+/// For internal use only.</summary>
+public class ExtensionTag<TBase, TInherited>
+    where TInherited : TBase
+{
+}
+
+}
+
 }
 
 #endif
diff --git a/src/tests/efl_mono/Parts.cs b/src/tests/efl_mono/Parts.cs
index 3de0b6c94b..00a67bae56 100644
--- a/src/tests/efl_mono/Parts.cs
+++ b/src/tests/efl_mono/Parts.cs
@@ -39,6 +39,19 @@ public static class TestParts
     }
 }
 
+public static class TestMVVMParts
+{
+    public static void mvvm_dynamic_parts()
+    {
+        var factory = new Efl.Ui.ItemFactory<Efl.Ui.ListDefaultItem>();
+
+        var bindablePart = factory.TextPart();
+        var error = bindablePart.Markup().Bind("name");
+
+        Test.AssertEquals(error, Eina.Error.NO_ERROR);
+    }
+}
+
 #endif
 
 }

-- 


Reply via email to