forked from enlightenment/efl
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:
parent
7c72f10153
commit
40def3eac9
|
@ -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 <>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T>" /> 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
|
|
@ -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<Efl.Ui.Button>();
|
||||
/// 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
|
|
@ -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<T>" /></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
|
|
@ -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<T>" /> 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<PersonModel>(parent);
|
||||
/// modelData.Add(new PersonModel { Name = "John", Age = 30 };
|
||||
/// var model = new Efl.GenericModel<PersonModel>(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
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ class TestMain
|
|||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
Efl.All.Init();
|
||||
Efl.All.Init(Efl.Csharp.Components.Ui);
|
||||
|
||||
bool pass = true;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue