summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLauro Moura <lauromoura@expertisesolutions.com.br>2019-08-19 21:31:20 -0300
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2019-08-19 21:41:29 -0300
commitcc49c1034b1114e1ce9d10d8651ec6d24c1faaa1 (patch)
treefe13ff532336c143320afa99dcf7e937ee21d28e /src
parent2a0eeba4e5fdd0de0c46000fa783cb59f7d716e7 (diff)
downloadefl-cc49c1034b1114e1ce9d10d8651ec6d24c1faaa1.tar.gz
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
Diffstat (limited to 'src')
-rw-r--r--src/bin/eolian_mono/eolian/mono/function_definition.hh22
-rw-r--r--src/bin/eolian_mono/eolian/mono/helpers.hh12
-rw-r--r--src/bin/eolian_mono/eolian/mono/klass.hh12
-rw-r--r--src/bin/eolian_mono/eolian/mono/part_definition.hh40
-rw-r--r--src/bindings/mono/efl_mono/Bind.cs82
-rw-r--r--src/tests/efl_mono/Parts.cs13
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
@@ -164,6 +164,18 @@ bool has_regular_ancestor(attributes::klass_def const& cls)
}
/*
+ * 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)
*/
template<typename Context>
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
}