Skip to content

Commit 02e2c14

Browse files
committed
Close #50. Add ReadOnlyProperty support for BindableTextField.
1 parent 2febaa9 commit 02e2c14

File tree

10 files changed

+183
-70
lines changed

10 files changed

+183
-70
lines changed

src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ public IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupTy
7979
return this;
8080
}
8181

82+
public bool TryRentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData,
83+
out IProperty<TValueType> property)
84+
{
85+
EnsureBindingDataValid(bindingData);
86+
87+
if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false)
88+
{
89+
property = default;
90+
return false;
91+
}
92+
93+
var baseProperty = memberInfo.GetMemberValue<IBaseProperty>(context, out _);
94+
95+
if (baseProperty is IProperty<TValueType>)
96+
{
97+
property = _objectWrapperHandler
98+
.GetProperty<IProperty<TValueType>, TValueType>(context, bindingData, memberInfo);
99+
100+
return true;
101+
}
102+
103+
property = default;
104+
return false;
105+
}
106+
82107
public IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData)
83108
{
84109
EnsureBindingDataValid(bindingData);

src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public interface IObjectProvider
1414
IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupType = WarmupType.OnlyByType)
1515
where T : IValueConverter;
1616

17+
bool TryRentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData,
18+
out IProperty<TValueType> property);
19+
1720
IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData);
1821
IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context, PropertyBindingData bindingData);
1922
void ReturnProperty<TValueType>(IProperty<TValueType> property);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Reflection;
3+
using System.Runtime.CompilerServices;
4+
using UnityMvvmToolkit.Core.Interfaces;
5+
6+
namespace UnityMvvmToolkit.Core.Internal.Extensions
7+
{
8+
internal static class MemberInfoExtensions
9+
{
10+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11+
public static T GetMemberValue<T>(this MemberInfo memberInfo, IBindingContext context, out Type memberType)
12+
{
13+
switch (memberInfo.MemberType)
14+
{
15+
case MemberTypes.Field:
16+
{
17+
var fieldInfo = (FieldInfo) memberInfo;
18+
memberType = fieldInfo.FieldType;
19+
20+
return (T) fieldInfo.GetValue(context);
21+
}
22+
23+
case MemberTypes.Property:
24+
{
25+
var propertyInfo = (PropertyInfo) memberInfo;
26+
memberType = propertyInfo.PropertyType;
27+
28+
return (T) propertyInfo.GetValue(context);
29+
}
30+
31+
default:
32+
throw new ArgumentOutOfRangeException();
33+
}
34+
}
35+
}
36+
}

src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void CreateValueConverterInstances<T>(int capacity, WarmupType warmupType
5454
public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, MemberInfo memberInfo)
5555
where TProperty : IBaseProperty
5656
{
57-
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
57+
var property = memberInfo.GetMemberValue<IBaseProperty>(context, out var propertyType);
5858

5959
var targetType = typeof(TValueType);
6060
var sourceType = propertyType.GenericTypeArguments[0];
@@ -102,7 +102,7 @@ public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, M
102102
public TProperty GetProperty<TProperty, TValueType>(IBindingContext context, BindingData bindingData,
103103
MemberInfo memberInfo) where TProperty : IBaseProperty
104104
{
105-
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
105+
var property = memberInfo.GetMemberValue<IBaseProperty>(context, out var propertyType);
106106

107107
var targetType = typeof(TValueType);
108108
var sourceType = propertyType.GenericTypeArguments[0];
@@ -148,13 +148,13 @@ public TProperty GetProperty<TProperty, TValueType>(IBindingContext context, Bin
148148
public TCommand GetCommand<TCommand>(IBindingContext context, MemberInfo memberInfo)
149149
where TCommand : IBaseCommand
150150
{
151-
return GetMemberValue<TCommand>(context, memberInfo, out _);
151+
return memberInfo.GetMemberValue<TCommand>(context, out _);
152152
}
153153

154154
public ICommandWrapper GetCommandWrapper(IBindingContext context, CommandBindingData bindingData,
155155
MemberInfo memberInfo)
156156
{
157-
var command = GetMemberValue<IBaseCommand>(context, memberInfo, out var commandType);
157+
var command = memberInfo.GetMemberValue<IBaseCommand>(context, out var commandType);
158158

159159
if (commandType.IsGenericType == false ||
160160
commandType.GetInterface(nameof(IBaseCommand)) == null)
@@ -353,32 +353,6 @@ private void ReturnWrapper(IObjectWrapper wrapper)
353353
_wrappersByConverter[wrapper.ConverterId].Enqueue(wrapper);
354354
}
355355

356-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
357-
private static T GetMemberValue<T>(IBindingContext context, MemberInfo memberInfo, out Type memberType)
358-
{
359-
switch (memberInfo.MemberType)
360-
{
361-
case MemberTypes.Field:
362-
{
363-
var fieldInfo = (FieldInfo) memberInfo;
364-
memberType = fieldInfo.FieldType;
365-
366-
return (T) fieldInfo.GetValue(context);
367-
}
368-
369-
case MemberTypes.Property:
370-
{
371-
var propertyInfo = (PropertyInfo) memberInfo;
372-
memberType = propertyInfo.PropertyType;
373-
374-
return (T) propertyInfo.GetValue(context);
375-
}
376-
377-
default:
378-
throw new ArgumentOutOfRangeException();
379-
}
380-
}
381-
382356
[MethodImpl(MethodImplOptions.AggressiveInlining)]
383357
private void AssureIsNotDisposed()
384358
{

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ public IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupTy
7979
return this;
8080
}
8181

82+
public bool TryRentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData,
83+
out IProperty<TValueType> property)
84+
{
85+
EnsureBindingDataValid(bindingData);
86+
87+
if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false)
88+
{
89+
property = default;
90+
return false;
91+
}
92+
93+
var baseProperty = memberInfo.GetMemberValue<IBaseProperty>(context, out _);
94+
95+
if (baseProperty is IProperty<TValueType>)
96+
{
97+
property = _objectWrapperHandler
98+
.GetProperty<IProperty<TValueType>, TValueType>(context, bindingData, memberInfo);
99+
100+
return true;
101+
}
102+
103+
property = default;
104+
return false;
105+
}
106+
82107
public IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData)
83108
{
84109
EnsureBindingDataValid(bindingData);

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public interface IObjectProvider
1414
IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupType = WarmupType.OnlyByType)
1515
where T : IValueConverter;
1616

17+
bool TryRentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData,
18+
out IProperty<TValueType> property);
19+
1720
IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData);
1821
IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context, PropertyBindingData bindingData);
1922
void ReturnProperty<TValueType>(IProperty<TValueType> property);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Reflection;
3+
using System.Runtime.CompilerServices;
4+
using UnityMvvmToolkit.Core.Interfaces;
5+
6+
namespace UnityMvvmToolkit.Core.Internal.Extensions
7+
{
8+
internal static class MemberInfoExtensions
9+
{
10+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11+
public static T GetMemberValue<T>(this MemberInfo memberInfo, IBindingContext context, out Type memberType)
12+
{
13+
switch (memberInfo.MemberType)
14+
{
15+
case MemberTypes.Field:
16+
{
17+
var fieldInfo = (FieldInfo) memberInfo;
18+
memberType = fieldInfo.FieldType;
19+
20+
return (T) fieldInfo.GetValue(context);
21+
}
22+
23+
case MemberTypes.Property:
24+
{
25+
var propertyInfo = (PropertyInfo) memberInfo;
26+
memberType = propertyInfo.PropertyType;
27+
28+
return (T) propertyInfo.GetValue(context);
29+
}
30+
31+
default:
32+
throw new ArgumentOutOfRangeException();
33+
}
34+
}
35+
}
36+
}

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void CreateValueConverterInstances<T>(int capacity, WarmupType warmupType
5454
public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, MemberInfo memberInfo)
5555
where TProperty : IBaseProperty
5656
{
57-
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
57+
var property = memberInfo.GetMemberValue<IBaseProperty>(context, out var propertyType);
5858

5959
var targetType = typeof(TValueType);
6060
var sourceType = propertyType.GenericTypeArguments[0];
@@ -102,7 +102,7 @@ public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, M
102102
public TProperty GetProperty<TProperty, TValueType>(IBindingContext context, BindingData bindingData,
103103
MemberInfo memberInfo) where TProperty : IBaseProperty
104104
{
105-
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
105+
var property = memberInfo.GetMemberValue<IBaseProperty>(context, out var propertyType);
106106

107107
var targetType = typeof(TValueType);
108108
var sourceType = propertyType.GenericTypeArguments[0];
@@ -148,13 +148,13 @@ public TProperty GetProperty<TProperty, TValueType>(IBindingContext context, Bin
148148
public TCommand GetCommand<TCommand>(IBindingContext context, MemberInfo memberInfo)
149149
where TCommand : IBaseCommand
150150
{
151-
return GetMemberValue<TCommand>(context, memberInfo, out _);
151+
return memberInfo.GetMemberValue<TCommand>(context, out _);
152152
}
153153

154154
public ICommandWrapper GetCommandWrapper(IBindingContext context, CommandBindingData bindingData,
155155
MemberInfo memberInfo)
156156
{
157-
var command = GetMemberValue<IBaseCommand>(context, memberInfo, out var commandType);
157+
var command = memberInfo.GetMemberValue<IBaseCommand>(context, out var commandType);
158158

159159
if (commandType.IsGenericType == false ||
160160
commandType.GetInterface(nameof(IBaseCommand)) == null)
@@ -353,32 +353,6 @@ private void ReturnWrapper(IObjectWrapper wrapper)
353353
_wrappersByConverter[wrapper.ConverterId].Enqueue(wrapper);
354354
}
355355

356-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
357-
private static T GetMemberValue<T>(IBindingContext context, MemberInfo memberInfo, out Type memberType)
358-
{
359-
switch (memberInfo.MemberType)
360-
{
361-
case MemberTypes.Field:
362-
{
363-
var fieldInfo = (FieldInfo) memberInfo;
364-
memberType = fieldInfo.FieldType;
365-
366-
return (T) fieldInfo.GetValue(context);
367-
}
368-
369-
case MemberTypes.Property:
370-
{
371-
var propertyInfo = (PropertyInfo) memberInfo;
372-
memberType = propertyInfo.PropertyType;
373-
374-
return (T) propertyInfo.GetValue(context);
375-
}
376-
377-
default:
378-
throw new ArgumentOutOfRangeException();
379-
}
380-
}
381-
382356
[MethodImpl(MethodImplOptions.AggressiveInlining)]
383357
private void AssureIsNotDisposed()
384358
{

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Runtime.CompilerServices;
1+
using System;
2+
using System.Runtime.CompilerServices;
23
using UnityEngine.UIElements;
34
using UnityMvvmToolkit.Core;
45
using UnityMvvmToolkit.Core.Extensions;
@@ -8,9 +9,11 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements
89
{
910
public partial class BindableTextField : TextField, IBindableElement
1011
{
11-
private IProperty<string> _valueProperty;
1212
private PropertyBindingData _propertyBindingData;
1313

14+
private IProperty<string> _valueProperty;
15+
private IReadOnlyProperty<string> _valueReadOnlyProperty;
16+
1417
public virtual void SetBindingContext(IBindingContext context, IObjectProvider objectProvider)
1518
{
1619
if (string.IsNullOrWhiteSpace(BindingValuePath))
@@ -20,27 +23,50 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o
2023

2124
_propertyBindingData ??= BindingValuePath.ToPropertyBindingData();
2225

23-
_valueProperty = objectProvider.RentProperty<string>(context, _propertyBindingData);
24-
_valueProperty.ValueChanged += OnPropertyValueChanged;
26+
if (objectProvider.TryRentProperty(context, _propertyBindingData, out _valueProperty))
27+
{
28+
_valueReadOnlyProperty = _valueProperty;
29+
this.RegisterValueChangedCallback(OnControlValueChanged);
30+
}
31+
else if (isReadOnly)
32+
{
33+
_valueReadOnlyProperty = objectProvider.RentReadOnlyProperty<string>(context, _propertyBindingData);
34+
}
35+
else
36+
{
37+
var controlName = string.IsNullOrWhiteSpace(name) ? nameof(BindableTextField) : name;
2538

26-
UpdateControlValue(_valueProperty.Value);
27-
this.RegisterValueChangedCallback(OnControlValueChanged);
39+
throw new InvalidOperationException(
40+
$"The {_propertyBindingData.PropertyName} property is read-only. Mark the {controlName} as read-only or change the property type.");
41+
}
42+
43+
_valueReadOnlyProperty.ValueChanged += OnPropertyValueChanged;
44+
45+
UpdateControlValue(_valueReadOnlyProperty.Value);
2846
}
2947

3048
public virtual void ResetBindingContext(IObjectProvider objectProvider)
3149
{
32-
if (_valueProperty is null)
50+
if (_valueReadOnlyProperty is null)
3351
{
3452
return;
3553
}
3654

37-
_valueProperty.ValueChanged -= OnPropertyValueChanged;
55+
_valueReadOnlyProperty.ValueChanged -= OnPropertyValueChanged;
3856

39-
objectProvider.ReturnProperty(_valueProperty);
57+
if (_valueProperty is null)
58+
{
59+
objectProvider.ReturnReadOnlyProperty(_valueReadOnlyProperty);
60+
}
61+
else
62+
{
63+
objectProvider.ReturnProperty(_valueProperty);
64+
this.UnregisterValueChangedCallback(OnControlValueChanged);
65+
}
4066

4167
_valueProperty = null;
68+
_valueReadOnlyProperty = null;
4269

43-
this.UnregisterValueChangedCallback(OnControlValueChanged);
4470
UpdateControlValue(default);
4571
}
4672

0 commit comments

Comments
 (0)