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
This commit is contained in:
Lauro Moura 2019-08-05 10:17:52 -04:00 committed by Mike Blumenkrantz
parent 7c72f10153
commit 40def3eac9
16 changed files with 807 additions and 12 deletions

View File

@ -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 <>

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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;

View File

@ -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'

View File

@ -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 {

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -15,7 +15,7 @@ class TestMain
static int Main(string[] args)
{
Efl.All.Init();
Efl.All.Init(Efl.Csharp.Components.Ui);
bool pass = true;

View File

@ -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

View File

@ -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