forked from enlightenment/efl
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
This commit is contained in:
parent
2a0eeba4e5
commit
cc49c1034b
|
@ -251,8 +251,26 @@ struct property_extension_method_definition_generator
|
||||||
if (property.setter.is_engaged())
|
if (property.setter.is_engaged())
|
||||||
{
|
{
|
||||||
attributes::type_def prop_type = property.setter->parameters[0].type;
|
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"
|
if (!as_generator("public static Efl.BindableProperty<" << type(true) << "> " << managed_name << "<T>(this Efl.Ui.ItemFactory<T> fac, Efl.Csharp.ExtensionTag<"
|
||||||
<< scope_tab << scope_tab << "return new Efl.Bindable<" << type(true) << ">(\"" << property.name << "\", fac);\n"
|
<< 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"
|
<< scope_tab << "}\n"
|
||||||
).generate(sink, std::make_tuple(prop_type, prop_type), context))
|
).generate(sink, std::make_tuple(prop_type, prop_type), context))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -163,6 +163,18 @@ bool has_regular_ancestor(attributes::klass_def const& cls)
|
||||||
return std::any_of(inherits.begin(), inherits.end(), is_regular);
|
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)
|
* Gets all methods that this class should implement (i.e. that come from an unimplemented interface/mixin and the class itself)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -348,14 +348,24 @@ struct klass
|
||||||
if(!name_helpers::close_namespaces(sink, cls.namespaces, context))
|
if(!name_helpers::close_namespaces(sink, cls.namespaces, context))
|
||||||
return false;
|
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
|
if(!as_generator
|
||||||
(lit("#pragma warning disable CS1591\n") // Disabling warnings as DocFx will hide these classes
|
(lit("#pragma warning disable CS1591\n") // Disabling warnings as DocFx will hide these classes
|
||||||
<<"public static class " << (string % "_") << name_helpers::klass_inherit_name(cls)
|
<<"public static class " << (string % "_") << name_helpers::klass_inherit_name(cls)
|
||||||
<< "_ExtensionMethods {\n"
|
<< "_ExtensionMethods {\n"
|
||||||
<< *((scope_tab << property_extension_method_definition(cls)) << "\n")
|
<< *((scope_tab << property_extension_method_definition(cls)) << "\n")
|
||||||
|
<< *((scope_tab << part_extension_method_definition(cls)) << "\n")
|
||||||
<< "}\n"
|
<< "}\n"
|
||||||
<< lit("#pragma warning restore CS1591\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 false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -35,6 +35,39 @@ struct part_definition_generator
|
||||||
|
|
||||||
} const part_definition {};
|
} 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 {
|
namespace efl { namespace eolian { namespace grammar {
|
||||||
|
@ -44,10 +77,17 @@ struct is_eager_generator< ::eolian_mono::part_definition_generator> : std::true
|
||||||
template <>
|
template <>
|
||||||
struct is_generator< ::eolian_mono::part_definition_generator> : std::true_type {};
|
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 {
|
namespace type_traits {
|
||||||
template <>
|
template <>
|
||||||
struct attributes_needed< ::eolian_mono::part_definition_generator> : std::integral_constant<int, 1> {};
|
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> {};
|
||||||
}
|
}
|
||||||
} } }
|
} } }
|
||||||
|
|
||||||
|
|
|
@ -8,29 +8,95 @@ using System.ComponentModel;
|
||||||
|
|
||||||
namespace Efl {
|
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>
|
/// <para>It is internally instantiated and returned by generated extension methods.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Bindable<T>
|
public class BindableProperty<T>
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>Creates a new bindable property with the source name <c>name</c>.</summary>
|
/// <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;
|
this.binder = binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Binds the model property <c>model_property</c> to the property <c>name</c> set in the constructor.</summary>
|
/// <summary>Creates a new bindable property for part <c>part</c>.</summary>
|
||||||
public void Bind(string model_property)
|
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;
|
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
|
#endif
|
||||||
|
|
|
@ -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
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue