forked from amkovkov/GranuSightSoftware2
feat: Control value converter
This commit is contained in:
@@ -6,22 +6,22 @@ namespace GSS2.Core.Helpers;
|
||||
|
||||
public static class ControlValueHelper
|
||||
{
|
||||
private static readonly Dictionary<Type, Func<object, ControlId, object?>> _valueParsers = new()
|
||||
private static readonly Dictionary<Type, Func<object, ControlId, object?>> _parsers = new()
|
||||
{
|
||||
{ typeof(bool ), (obj, _) => ParseValue<bool >(obj, bool .TryParse) },
|
||||
{ typeof(byte ), (obj, _) => ParseValue<byte >(obj, byte .TryParse) },
|
||||
{ typeof(float ), (obj, _) => ParseValue<float >(obj, float .TryParse) },
|
||||
{ typeof(long ), (obj, _) => ParseValue<long >(obj, long .TryParse) },
|
||||
{ typeof(ushort ), (obj, _) => ParseValue<ushort>(obj, ushort.TryParse) },
|
||||
{ typeof(uint ), (obj, _) => ParseValue<uint >(obj, uint .TryParse) },
|
||||
{ typeof(int ), ParseValueInt },
|
||||
{ typeof(bool ), (obj, _) => Parse<bool >(obj, bool .TryParse) },
|
||||
{ typeof(byte ), (obj, _) => Parse<byte >(obj, byte .TryParse) },
|
||||
{ typeof(float ), (obj, _) => Parse<float >(obj, float .TryParse) },
|
||||
{ typeof(long ), (obj, _) => Parse<long >(obj, long .TryParse) },
|
||||
{ typeof(ushort ), (obj, _) => Parse<ushort>(obj, ushort.TryParse) },
|
||||
{ typeof(uint ), (obj, _) => Parse<uint >(obj, uint .TryParse) },
|
||||
{ typeof(int ), ParseInt },
|
||||
// int - особый случай потому что он может использовать ControlId.Enumerators для прообразования стокового "enum" в значение
|
||||
{ typeof(Rectangle), ParseValueRectangle },
|
||||
{ typeof(Size ), ParseValueSize },
|
||||
{ typeof(Point ), ParseValuePoint }
|
||||
{ typeof(Rectangle), ParseRectangle },
|
||||
{ typeof(Size ), ParseSize },
|
||||
{ typeof(Point ), ParsePoint }
|
||||
};
|
||||
private delegate bool ParseValueDelegate<T>(string s, out T value);
|
||||
private static object? ParseValue<T>(object obj, ParseValueDelegate<T> parser) where T : struct
|
||||
private static object? Parse<T>(object obj, ParseValueDelegate<T> parser) where T : struct
|
||||
{
|
||||
if (obj is T value)
|
||||
return value;
|
||||
@@ -29,7 +29,7 @@ public static class ControlValueHelper
|
||||
return parsed;
|
||||
return null;
|
||||
}
|
||||
private static object? ParseValueInt(object obj, ControlId id)
|
||||
private static object? ParseInt(object obj, ControlId id)
|
||||
{
|
||||
if (obj is int value)
|
||||
return value;
|
||||
@@ -44,7 +44,7 @@ public static class ControlValueHelper
|
||||
|
||||
return null;
|
||||
}
|
||||
private static object? ParseValueRectangle(object obj, ControlId id)
|
||||
private static object? ParseRectangle(object obj, ControlId id)
|
||||
{
|
||||
if (obj is Rectangle value)
|
||||
return value;
|
||||
@@ -65,7 +65,7 @@ public static class ControlValueHelper
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static object? ParseValueSize(object obj, ControlId id)
|
||||
private static object? ParseSize(object obj, ControlId id)
|
||||
{
|
||||
if (obj is Size value)
|
||||
return value;
|
||||
@@ -82,7 +82,7 @@ public static class ControlValueHelper
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static object? ParseValuePoint(object obj, ControlId id)
|
||||
private static object? ParsePoint(object obj, ControlId id)
|
||||
{
|
||||
if (obj is Point value)
|
||||
return value;
|
||||
@@ -99,152 +99,112 @@ public static class ControlValueHelper
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static T? ConvertValueStruct<T>(object obj, ControlId id) where T : struct
|
||||
|
||||
private static T ConvertValue<T>(object obj, ControlId id)
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (!_parsers.TryGetValue(typeof(T), out var parser))
|
||||
throw new Exception();
|
||||
|
||||
if (!_valueParsers.ContainsKey(type))
|
||||
return null;
|
||||
|
||||
var parsed = _valueParsers[type](obj, id);
|
||||
var result = (T?)parsed;
|
||||
return result;
|
||||
var parsed = parser(obj, id);
|
||||
if (parsed is null || parsed is not T typed)
|
||||
throw new Exception();
|
||||
return typed;
|
||||
}
|
||||
private static T? ConvertValueClass<T>(object obj, ControlId id) where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (!_valueParsers.ContainsKey(type))
|
||||
return null;
|
||||
|
||||
var parsed = _valueParsers[type](obj, id);
|
||||
var result = (T?)parsed;
|
||||
return result;
|
||||
}
|
||||
private static readonly Dictionary<ControlTypeEnum, Func<object, ControlId, ControlValue?>> _controlValueConverters = new()
|
||||
{
|
||||
{ ControlTypeEnum.Bool , (obj, id) => { var v = ConvertValueStruct<bool >(obj, id); return v is null ? null : new ControlValueBool (v.Value); } },
|
||||
{ ControlTypeEnum.Byte , (obj, id) => { var v = ConvertValueStruct<byte >(obj, id); return v is null ? null : new ControlValueByte (v.Value); } },
|
||||
{ ControlTypeEnum.Float , (obj, id) => { var v = ConvertValueStruct<float >(obj, id); return v is null ? null : new ControlValueFloat (v.Value); } },
|
||||
{ ControlTypeEnum.Integer32 , (obj, id) => { var v = ConvertValueStruct<int >(obj, id); return v is null ? null : new ControlValueInteger32 (v.Value); } },
|
||||
{ ControlTypeEnum.Integer64 , (obj, id) => { var v = ConvertValueStruct<long >(obj, id); return v is null ? null : new ControlValueInteger64 (v.Value); } },
|
||||
{ ControlTypeEnum.Unsigned16, (obj, id) => { var v = ConvertValueStruct<ushort >(obj, id); return v is null ? null : new ControlValueUnsigned16(v.Value); } },
|
||||
{ ControlTypeEnum.Unsigned32, (obj, id) => { var v = ConvertValueStruct<uint >(obj, id); return v is null ? null : new ControlValueUnsigned32(v.Value); } },
|
||||
{ ControlTypeEnum.Rectangle , (obj, id) => { var v = ConvertValueClass <Rectangle>(obj, id); return v is null ? null : new ControlValueRectangle (v); } },
|
||||
{ ControlTypeEnum.Size , (obj, id) => { var v = ConvertValueClass <Size >(obj, id); return v is null ? null : new ControlValueSize (v); } },
|
||||
{ ControlTypeEnum.Point , (obj, id) => { var v = ConvertValueClass <Point >(obj, id); return v is null ? null : new ControlValuePoint (v); } }
|
||||
};
|
||||
private static readonly Dictionary<ControlTypeEnum, Action<ControlValue, ControlId, ControlInfo>> _controlValueClapers = new()
|
||||
{
|
||||
{ ControlTypeEnum.Bool , (value, id, info) => { } },
|
||||
{ ControlTypeEnum.Byte , (value, id, info) => { if (value is ControlValueByte typed && info.Min is ControlValueByte typedMin && info.Max is ControlValueByte typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||
{ ControlTypeEnum.Float , (value, id, info) => { if (value is ControlValueFloat typed && info.Min is ControlValueFloat typedMin && info.Max is ControlValueFloat typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||
{ ControlTypeEnum.Integer32 , (value, id, info) => { if (value is ControlValueInteger32 typed && info.Min is ControlValueInteger32 typedMin && info.Max is ControlValueInteger32 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||
{ ControlTypeEnum.Integer64 , (value, id, info) => { if (value is ControlValueInteger64 typed && info.Min is ControlValueInteger64 typedMin && info.Max is ControlValueInteger64 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||
{ ControlTypeEnum.Unsigned16, (value, id, info) => { if (value is ControlValueUnsigned16 typed && info.Min is ControlValueUnsigned16 typedMin && info.Max is ControlValueUnsigned16 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||
{ ControlTypeEnum.Unsigned32, (value, id, info) => { if (value is ControlValueUnsigned32 typed && info.Min is ControlValueUnsigned32 typedMin && info.Max is ControlValueUnsigned32 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }
|
||||
};
|
||||
public static ControlValue? ConvertToControlValue(object obj, ControlId id, ControlInfo info)
|
||||
{
|
||||
if (!_controlValueConverters.TryGetValue(id.Type, out var converter))
|
||||
return null;
|
||||
var controlValue = converter(obj, id);
|
||||
if (controlValue is null)
|
||||
return null;
|
||||
if (!_controlValueClapers.TryGetValue(id.Type, out var clamper))
|
||||
return controlValue;
|
||||
clamper(controlValue, id, info);
|
||||
return controlValue;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, Func<object, ControlId, object?>> _arrayParsers = new()
|
||||
{
|
||||
{ typeof(bool ), ParseArray<bool> },
|
||||
{ typeof(byte ), ParseArray<byte> },
|
||||
{ typeof(float ), ParseArray<float> },
|
||||
{ typeof(long ), ParseArray<long> },
|
||||
{ typeof(ushort ), ParseArray<ushort> },
|
||||
{ typeof(uint ), ParseArray<uint> },
|
||||
{ typeof(int ), ParseArray<int> },
|
||||
{ typeof(Rectangle), ParseArray<Rectangle>},
|
||||
{ typeof(Size ), ParseArray<Size> },
|
||||
{ typeof(Point ), ParseArray<Point> }
|
||||
};
|
||||
private static object? ParseArray<T>(object obj, ControlId id)
|
||||
private static IEnumerable<T> ConvertArray<T>(object obj, ControlId id)
|
||||
{
|
||||
if (obj is not IEnumerable enumerable)
|
||||
return null;
|
||||
throw new Exception();
|
||||
|
||||
var list = enumerable.Cast<object>().ToList();
|
||||
|
||||
if ((ulong)list.Count != id.Size)
|
||||
return null;
|
||||
if ((ulong)list.Count() != id.Size)
|
||||
throw new Exception();
|
||||
|
||||
var array = new T[list.Count];
|
||||
var array = new T[list.Count()];
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var parsed = _valueParsers[typeof(T)](list[i], id);
|
||||
if (parsed is null)
|
||||
return null;
|
||||
for (int i = 0; i < list.Count(); i++)
|
||||
array[i] = ConvertValue<T>(list.Skip(i).First(), id);
|
||||
|
||||
array[i] = (T)parsed;
|
||||
}
|
||||
|
||||
return array;
|
||||
return array.AsEnumerable();
|
||||
}
|
||||
private static IEnumerable<T>? ConvertArray<T>(object obj, ControlId id)
|
||||
|
||||
private static ControlValue ConvertControlValue(object obj, ControlId id, ControlInfo info)
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (!ControlValue.ValueTypes.TryGetValue(id.Type, out var valueType))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (!_arrayParsers.ContainsKey(type))
|
||||
return null;
|
||||
var convertValue = typeof(ControlValueHelper)
|
||||
.GetMethod(nameof(ConvertValue), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)?
|
||||
.MakeGenericMethod([valueType]);
|
||||
if (convertValue is null)
|
||||
throw new Exception();
|
||||
|
||||
var parsed = _arrayParsers[type](obj, id);
|
||||
var result = (T[]?)parsed;
|
||||
return result;
|
||||
var value = convertValue.Invoke(null, [obj, id]);
|
||||
|
||||
var controlValueType = AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.FirstOrDefault(t =>
|
||||
!t.IsAbstract &&
|
||||
t.BaseType is { IsGenericType: true } baseType &&
|
||||
baseType.GetGenericTypeDefinition() == typeof(ControlValue<>) &&
|
||||
baseType.GetGenericArguments()[0] == valueType);
|
||||
|
||||
if (controlValueType is null)
|
||||
throw new Exception();
|
||||
|
||||
var constructor = controlValueType.GetConstructor([valueType]);
|
||||
if (constructor is null)
|
||||
throw new Exception();
|
||||
|
||||
var controlValue = constructor.Invoke([value]);
|
||||
if (controlValue is null || controlValue is not ControlValue typed)
|
||||
throw new Exception();
|
||||
|
||||
return typed;
|
||||
}
|
||||
private static readonly Dictionary<ControlTypeEnum, Func<object, ControlId, ControlValue?>> _controlArrayConverters = new()
|
||||
private static ControlValue ConvertControlArray(object obj, ControlId id, ControlInfo info)
|
||||
{
|
||||
{ ControlTypeEnum.Bool , (obj, id) => { var v = ConvertArray<bool >(obj, id); return v is null ? null : new ControlValueBoolSpan (v); } },
|
||||
{ ControlTypeEnum.Byte , (obj, id) => { var v = ConvertArray<byte >(obj, id); return v is null ? null : new ControlValueByteSpan (v); } },
|
||||
{ ControlTypeEnum.Float , (obj, id) => { var v = ConvertArray<float >(obj, id); return v is null ? null : new ControlValueFloatSpan (v); } },
|
||||
{ ControlTypeEnum.Integer32 , (obj, id) => { var v = ConvertArray<int >(obj, id); return v is null ? null : new ControlValueInteger32Span (v); } },
|
||||
{ ControlTypeEnum.Integer64 , (obj, id) => { var v = ConvertArray<long >(obj, id); return v is null ? null : new ControlValueInteger64Span (v); } },
|
||||
{ ControlTypeEnum.Unsigned16, (obj, id) => { var v = ConvertArray<ushort >(obj, id); return v is null ? null : new ControlValueUnsigned16Span(v); } },
|
||||
{ ControlTypeEnum.Unsigned32, (obj, id) => { var v = ConvertArray<uint >(obj, id); return v is null ? null : new ControlValueUnsigned32Span(v); } },
|
||||
{ ControlTypeEnum.Rectangle , (obj, id) => { var v = ConvertArray<Rectangle>(obj, id); return v is null ? null : new ControlValueRectangleSpan (v); } },
|
||||
{ ControlTypeEnum.Size , (obj, id) => { var v = ConvertArray<Size >(obj, id); return v is null ? null : new ControlValueSizeSpan (v); } },
|
||||
{ ControlTypeEnum.Point , (obj, id) => { var v = ConvertArray<Point >(obj, id); return v is null ? null : new ControlValuePointSpan (v); } }
|
||||
};
|
||||
private static readonly Dictionary<ControlTypeEnum, Action<ControlValue, ControlId, ControlInfo>> _controlArrayClapers = new()
|
||||
if (!ControlValue.ArrayTypes.TryGetValue(id.Type, out var arrayType))
|
||||
throw new Exception();
|
||||
if (!ControlValue.ValueTypes.TryGetValue(id.Type, out var valueType))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
var convertArray = typeof(ControlValueHelper)
|
||||
.GetMethod(nameof(ConvertArray), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)?
|
||||
.MakeGenericMethod([valueType]);
|
||||
if (convertArray is null)
|
||||
throw new Exception();
|
||||
|
||||
var value = convertArray.Invoke(null, [obj, id]);
|
||||
|
||||
var controlValueType = AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.FirstOrDefault(t =>
|
||||
!t.IsAbstract &&
|
||||
t.BaseType is { IsGenericType: true } baseType &&
|
||||
baseType.GetGenericTypeDefinition() == typeof(ControlValue<>) &&
|
||||
baseType.GetGenericArguments()[0] == arrayType);
|
||||
|
||||
if (controlValueType is null)
|
||||
throw new Exception();
|
||||
|
||||
var constructor = controlValueType.GetConstructor([arrayType]);
|
||||
if (constructor is null)
|
||||
throw new Exception();
|
||||
|
||||
var controlValue = constructor.Invoke([value]);
|
||||
if (controlValue is null || controlValue is not ControlValue typed)
|
||||
throw new Exception();
|
||||
|
||||
return typed;
|
||||
}
|
||||
|
||||
public static ControlValue Convert(object obj, ControlId id, ControlInfo info)
|
||||
{
|
||||
{ ControlTypeEnum.Bool , (value, id, info) => { } },
|
||||
{ ControlTypeEnum.Byte , (value, id, info) => { if (value is ControlValueByteSpan typed && info.Min is ControlValueByte typedMin && info.Max is ControlValueByte typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||
{ ControlTypeEnum.Float , (value, id, info) => { if (value is ControlValueFloatSpan typed && info.Min is ControlValueFloat typedMin && info.Max is ControlValueFloat typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||
{ ControlTypeEnum.Integer32 , (value, id, info) => { if (value is ControlValueInteger32Span typed && info.Min is ControlValueInteger32 typedMin && info.Max is ControlValueInteger32 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||
{ ControlTypeEnum.Integer64 , (value, id, info) => { if (value is ControlValueInteger64Span typed && info.Min is ControlValueInteger64 typedMin && info.Max is ControlValueInteger64 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||
{ ControlTypeEnum.Unsigned16, (value, id, info) => { if (value is ControlValueUnsigned16Span typed && info.Min is ControlValueUnsigned16 typedMin && info.Max is ControlValueUnsigned16 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||
{ ControlTypeEnum.Unsigned32, (value, id, info) => { if (value is ControlValueUnsigned32Span typed && info.Min is ControlValueUnsigned32 typedMin && info.Max is ControlValueUnsigned32 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||
{ ControlTypeEnum.Rectangle , (value, id, info) => { if (value is ControlValueRectangleSpan typed && info.Min is ControlValueRectangle typedMin && info.Max is ControlValueRectangle typedMax) typed.Set(typed.Get().Select(v => new Rectangle(Math.Clamp(v.X, typedMin.Get().X, typedMax.Get().X ),
|
||||
Math.Clamp(v.Y, typedMin.Get().Y, typedMax.Get().Y ),
|
||||
Math.Clamp(v.Width, typedMin.Get().Width, typedMax.Get().Width ),
|
||||
Math.Clamp(v.Height, typedMin.Get().Height, typedMax.Get().Height)))); } },
|
||||
{ ControlTypeEnum.Size , (value, id, info) => { if (value is ControlValueSizeSpan typed && info.Min is ControlValueSize typedMin && info.Max is ControlValueSize typedMax) typed.Set(typed.Get().Select(v => new Size( Math.Clamp(v.Width, typedMin.Get().Width, typedMax.Get().Width ),
|
||||
Math.Clamp(v.Height, typedMin.Get().Height, typedMax.Get().Height)))); } },
|
||||
{ ControlTypeEnum.Point , (value, id, info) => { if (value is ControlValuePointSpan typed && info.Min is ControlValuePoint typedMin && info.Max is ControlValuePoint typedMax) typed.Set(typed.Get().Select(v => new Point( Math.Clamp(v.X, typedMin.Get().X, typedMax.Get().X ),
|
||||
Math.Clamp(v.Y, typedMin.Get().Y, typedMax.Get().Y )))); } },
|
||||
};
|
||||
public static ControlValue? ConvertToControlArray(object obj, ControlId id, ControlInfo info)
|
||||
{
|
||||
if (!_controlArrayConverters.ContainsKey(id.Type))
|
||||
return null;
|
||||
var controlValue = _controlArrayConverters[id.Type](obj, id);
|
||||
if (controlValue is null)
|
||||
return null;
|
||||
if (!_controlArrayClapers.TryGetValue(id.Type, out var clamper))
|
||||
return controlValue;
|
||||
clamper(controlValue, id, info);
|
||||
return controlValue;
|
||||
if (!id.IsArray)
|
||||
return ConvertControlValue(obj, id, info);
|
||||
return ConvertControlArray(obj, id, info);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user