Skip to content

Commit 781a4fd

Browse files
author
TimGameDev
committed
Main goal: make it able to use external dlls which contains abstract UI logic without referencing UnityWeld.
Benefits: - no tight coupling with unityweld - external dlls keep its abstraction levels and can be used as you want (i.e. in console application) This is very good practice and should be done in every single plugin. For example Zenject has it by default. Done: * registration of custom Binding attribute (default one is preregistered) * registration of custom ViewModelProvider (default one is preregistered)
1 parent d93b1a6 commit 781a4fd

File tree

3 files changed

+142
-20
lines changed

3 files changed

+142
-20
lines changed

UnityWeld/Binding/AbstractMemberBinding.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,18 @@ private object FindViewModel(string viewModelName)
3939
}
4040

4141
var providedViewModel = components
42-
.Select(component => component as IViewModelProvider)
42+
.Select(component => component.GetViewModelData())
4343
.Where(component => component != null)
4444
.FirstOrDefault(
45-
viewModelBinding => viewModelBinding.GetViewModelTypeName() == viewModelName &&
45+
viewModelData => viewModelData.TypeName == viewModelName &&
4646
#pragma warning disable 252,253 // Warning says unintended reference comparison, but we do want to compare references
47-
(object)viewModelBinding != this
47+
(object)viewModelData.Model != this
4848
#pragma warning restore 252,253
4949
);
5050

5151
if (providedViewModel != null)
5252
{
53-
return providedViewModel.GetViewModel();
53+
return providedViewModel.Model;
5454
}
5555

5656
trans = trans.parent;

UnityWeld/Binding/Internal/TypeResolver.cs

+137-13
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,43 @@
99

1010
namespace UnityWeld.Binding.Internal
1111
{
12+
/// <summary>
13+
/// ViewModel provider delegate
14+
/// </summary>
15+
/// <param name="component"></param>
16+
/// <returns>Tuple with ViewModel name and ViewModel itself</returns>
17+
public delegate ViewModelProviderData ViewModelProviderDelegate(Component component);
18+
19+
/// <summary>
20+
/// Contains ViewModel data
21+
/// </summary>
22+
public class ViewModelProviderData
23+
{
24+
public readonly object Model;
25+
public readonly string TypeName;
26+
27+
public ViewModelProviderData(object model, string typeName)
28+
{
29+
Model = model;
30+
TypeName = typeName;
31+
}
32+
}
33+
1234
/// <summary>
1335
/// Helper class for setting up the factory for use in the editor.
1436
/// </summary>
1537
public static class TypeResolver
1638
{
39+
private static readonly IList<Type> BindingAttributeTypes = new List<Type>(2)
40+
{
41+
typeof(BindingAttribute) //this should be the only ref to default binding attribute
42+
};
43+
44+
private static readonly IList<ViewModelProviderDelegate> ViewModelProviders = new List<ViewModelProviderDelegate>(2)
45+
{
46+
DefaultViewModelProvider
47+
};
48+
1749
private static Type[] typesWithBindingAttribute;
1850

1951
/// <summary>
@@ -28,7 +60,7 @@ public static IEnumerable<Type> TypesWithBindingAttribute
2860
{
2961
if (typesWithBindingAttribute == null)
3062
{
31-
typesWithBindingAttribute = FindTypesMarkedByAttribute(typeof(BindingAttribute));
63+
typesWithBindingAttribute = FindTypesMarkedByBindingAttribute();
3264
}
3365

3466
return typesWithBindingAttribute;
@@ -58,6 +90,20 @@ public static IEnumerable<Type> TypesWithAdapterAttribute
5890

5991
private static Type[] typesWithWeldContainerAttribute;
6092

93+
/// <summary>
94+
/// Impliments default view model provider
95+
/// </summary>
96+
private static ViewModelProviderData DefaultViewModelProvider(Component component)
97+
{
98+
var provider = component as IViewModelProvider;
99+
if (provider == null)
100+
{
101+
return null;
102+
}
103+
104+
return new ViewModelProviderData(provider.GetViewModel(), provider.GetViewModelTypeName());
105+
}
106+
61107
/// <summary>
62108
/// Find all types with WeldContainerAttribute. This works in the same way as
63109
/// TypesWithAdapterAttribute and TypesWithBindingAttribute in that it finds it
@@ -101,6 +147,21 @@ private static Type[] FindTypesMarkedByAttribute(Type attributeType)
101147
return typesFound.ToArray();
102148
}
103149

150+
/// <summary>
151+
/// Find all types marked with binding attribute
152+
/// </summary>
153+
/// <returns></returns>
154+
private static Type[] FindTypesMarkedByBindingAttribute()
155+
{
156+
var result = new List<Type>();
157+
foreach (var attributeType in BindingAttributeTypes)
158+
{
159+
result.AddRange(FindTypesMarkedByAttribute(attributeType));
160+
}
161+
162+
return result.ToArray();
163+
}
164+
104165
/// <summary>
105166
/// Returns an enumerable of all known types.
106167
/// </summary>
@@ -205,21 +266,18 @@ private static IEnumerable<Type> FindAvailableViewModelTypes(AbstractMemberBindi
205266
}
206267

207268
// Case where a ViewModelBinding is used to bind a non-MonoBehaviour class.
208-
var viewModelBinding = component as IViewModelProvider;
209-
if (viewModelBinding != null)
269+
var viewModelData = component.GetViewModelData();
270+
if (viewModelData != null)
210271
{
211-
var viewModelTypeName = viewModelBinding.GetViewModelTypeName();
212272
// Ignore view model bindings that haven't been set up yet.
213-
if (string.IsNullOrEmpty(viewModelTypeName))
214-
{
273+
if (string.IsNullOrEmpty(viewModelData.TypeName))
215274
continue;
216-
}
217275

218276
foundAtLeastOneBinding = true;
219277

220-
yield return GetViewModelType(viewModelBinding.GetViewModelTypeName());
278+
yield return GetViewModelType(viewModelData.TypeName);
221279
}
222-
else if (component.GetType().GetCustomAttributes(typeof(BindingAttribute), false).Any())
280+
else if (component.GetType().HasBindingAttribute())
223281
{
224282
// Case where we are binding to an existing MonoBehaviour.
225283
foundAtLeastOneBinding = true;
@@ -246,9 +304,7 @@ public static BindableMember<PropertyInfo>[] FindBindableProperties(AbstractMemb
246304
.SelectMany(type => GetPublicProperties(type)
247305
.Select(p => new BindableMember<PropertyInfo>(p, type))
248306
)
249-
.Where(p => p.Member
250-
.GetCustomAttributes(typeof(BindingAttribute), false)
251-
.Any() // Filter out properties that don't have [Binding].
307+
.Where(p => p.Member.HasBindingAttribute() // Filter out properties that don't have [Binding].
252308
)
253309
.ToArray();
254310
}
@@ -295,7 +351,7 @@ public static BindableMember<MethodInfo>[] FindBindableMethods(EventBinding targ
295351
.Select(m => new BindableMember<MethodInfo>(m, type))
296352
)
297353
.Where(m => m.Member.GetParameters().Length == 0)
298-
.Where(m => m.Member.GetCustomAttributes(typeof(BindingAttribute), false).Any()
354+
.Where(m => m.Member.HasBindingAttribute()
299355
&& !m.MemberName.StartsWith("get_")) // Exclude property getters, since we aren't doing anything with the return value of the bound method anyway.
300356
.ToArray();
301357
}
@@ -310,5 +366,73 @@ public static BindableMember<PropertyInfo>[] FindBindableCollectionProperties(Co
310366
.Where(p => !typeof(string).IsAssignableFrom(p.Member.PropertyType))
311367
.ToArray();
312368
}
369+
370+
/// <summary>
371+
/// Register custom binding attribute.
372+
/// This will allow to mark bindable properties in external dlls without referencing UnityWeld.
373+
/// </summary>
374+
/// <typeparam name="T"></typeparam>
375+
public static void RegisterCustomBindingAttribute<T>() where T : Attribute
376+
{
377+
var type = typeof(T);
378+
if (BindingAttributeTypes.Contains(type))
379+
{
380+
return;
381+
}
382+
383+
BindingAttributeTypes.Add(type);
384+
}
385+
386+
/// <summary>
387+
/// Register custom ViewModel provider.
388+
/// This will allow to use custom ViewModel providers in external dlls without referencing UnityWeld.
389+
/// </summary>
390+
/// <param name="provider"></param>
391+
public static void RegisterCustomViewModelProvider(ViewModelProviderDelegate provider)
392+
{
393+
if (ViewModelProviders.Contains(provider))
394+
{
395+
return;
396+
}
397+
398+
ViewModelProviders.Add(provider);
399+
}
400+
401+
/// <summary>
402+
/// Check if type has binding attribute
403+
/// </summary>
404+
public static bool HasBindingAttribute(this MemberInfo type)
405+
{
406+
//for to avoid GC
407+
for (var index = 0; index < BindingAttributeTypes.Count; index++)
408+
{
409+
if (type.GetCustomAttributes(BindingAttributeTypes[index], false).Any())
410+
{
411+
return true;
412+
}
413+
}
414+
415+
return false;
416+
}
417+
418+
/// <summary>
419+
/// Get ViewModel data from component
420+
/// </summary>
421+
public static ViewModelProviderData GetViewModelData(this Component component)
422+
{
423+
if (component == null)
424+
return null;
425+
426+
//for to avoid GC
427+
for (var i = 0; i < ViewModelProviders.Count; i++)
428+
{
429+
var data = ViewModelProviders[i](component);
430+
431+
if (data != null)
432+
return data;
433+
}
434+
435+
return null;
436+
}
313437
}
314438
}

UnityWeld_Editor/SubViewModelBindingEditor.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@ public override void OnInspectorGUI()
6565
private BindableMember<PropertyInfo>[] FindBindableProperties()
6666
{
6767
return TypeResolver.FindBindableProperties(targetScript)
68-
.Where(prop => prop.Member.PropertyType
69-
.GetCustomAttributes(typeof(BindingAttribute), false)
70-
.Any()
68+
.Where(prop => prop.Member.PropertyType.HasBindingAttribute()
7169
)
7270
.ToArray();
7371
}

0 commit comments

Comments
 (0)