diff options
author | Lauro Moura <lauromoura@expertisesolutions.com.br> | 2019-08-19 21:31:20 -0300 |
---|---|---|
committer | Felipe Magno de Almeida <felipe@expertisesolutions.com.br> | 2019-08-19 21:41:29 -0300 |
commit | cc49c1034b1114e1ce9d10d8651ec6d24c1faaa1 (patch) | |
tree | fe13ff532336c143320afa99dcf7e937ee21d28e /src | |
parent | 2a0eeba4e5fdd0de0c46000fa783cb59f7d716e7 (diff) | |
download | efl-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.hh | 22 | ||||
-rw-r--r-- | src/bin/eolian_mono/eolian/mono/helpers.hh | 12 | ||||
-rw-r--r-- | src/bin/eolian_mono/eolian/mono/klass.hh | 12 | ||||
-rw-r--r-- | src/bin/eolian_mono/eolian/mono/part_definition.hh | 40 | ||||
-rw-r--r-- | src/bindings/mono/efl_mono/Bind.cs | 82 | ||||
-rw-r--r-- | 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 @@ -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<T>" /> instances. +/// <summary>Represents a bindable property as used by <see cref="Efl.Ui.ItemFactory<T>" /> 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<T>" /> 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 } |