forked from sim0n00ps/OF-DL
252 lines
6.8 KiB
C#
252 lines
6.8 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Reflection;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using Newtonsoft.Json;
|
|
using OF_DL.Models.Config;
|
|
|
|
namespace OF_DL.Gui.ViewModels;
|
|
|
|
public partial class ConfigFieldViewModel : ViewModelBase
|
|
{
|
|
public ConfigFieldViewModel(PropertyInfo propertyInfo, object? initialValue)
|
|
{
|
|
PropertyInfo = propertyInfo;
|
|
PropertyName = propertyInfo.Name;
|
|
DisplayName = ToDisplayName(propertyInfo.Name);
|
|
PropertyType = propertyInfo.PropertyType;
|
|
|
|
IsBoolean = PropertyType == typeof(bool);
|
|
IsEnum = PropertyType.IsEnum;
|
|
IsDate = PropertyType == typeof(DateTime?);
|
|
IsNumeric = PropertyType == typeof(int) || PropertyType == typeof(int?);
|
|
IsMultiline = PropertyType == typeof(Dictionary<string, CreatorConfig>);
|
|
IsTextInput = !IsBoolean && !IsEnum && !IsDate && !IsNumeric;
|
|
|
|
if (IsEnum)
|
|
{
|
|
foreach (string enumName in Enum.GetNames(PropertyType))
|
|
{
|
|
EnumOptions.Add(enumName);
|
|
}
|
|
}
|
|
|
|
LoadInitialValue(initialValue);
|
|
}
|
|
|
|
public PropertyInfo PropertyInfo { get; }
|
|
|
|
public string PropertyName { get; }
|
|
|
|
public string DisplayName { get; }
|
|
|
|
public Type PropertyType { get; }
|
|
|
|
public bool IsBoolean { get; }
|
|
|
|
public bool IsEnum { get; }
|
|
|
|
public bool IsDate { get; }
|
|
|
|
public bool IsNumeric { get; }
|
|
|
|
public bool IsTextInput { get; }
|
|
|
|
public bool IsMultiline { get; }
|
|
|
|
public double TextBoxMinHeight => IsMultiline ? 150 : 36;
|
|
|
|
public ObservableCollection<string> EnumOptions { get; } = [];
|
|
|
|
[ObservableProperty] private bool _boolValue;
|
|
|
|
[ObservableProperty] private string? _enumValue;
|
|
|
|
[ObservableProperty] private DateTimeOffset? _dateValue;
|
|
|
|
[ObservableProperty] private decimal? _numericValue;
|
|
|
|
[ObservableProperty] private string _textValue = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasError))]
|
|
private string _errorMessage = string.Empty;
|
|
|
|
public bool HasError => !string.IsNullOrWhiteSpace(ErrorMessage);
|
|
|
|
public bool TryGetTypedValue(out object? value, out string? error)
|
|
{
|
|
value = null;
|
|
error = null;
|
|
|
|
if (IsBoolean)
|
|
{
|
|
value = BoolValue;
|
|
return true;
|
|
}
|
|
|
|
if (IsEnum)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(EnumValue))
|
|
{
|
|
error = $"{DisplayName} is required.";
|
|
return false;
|
|
}
|
|
|
|
if (!Enum.TryParse(PropertyType, EnumValue, true, out object? enumResult))
|
|
{
|
|
error = $"{DisplayName} must be one of: {string.Join(", ", EnumOptions)}.";
|
|
return false;
|
|
}
|
|
|
|
value = enumResult;
|
|
return true;
|
|
}
|
|
|
|
if (PropertyType == typeof(string))
|
|
{
|
|
value = TextValue.Trim();
|
|
return true;
|
|
}
|
|
|
|
if (PropertyType == typeof(int))
|
|
{
|
|
if (!NumericValue.HasValue)
|
|
{
|
|
error = $"{DisplayName} must be a whole number.";
|
|
return false;
|
|
}
|
|
|
|
if (decimal.Truncate(NumericValue.Value) != NumericValue.Value)
|
|
{
|
|
error = $"{DisplayName} must be a whole number.";
|
|
return false;
|
|
}
|
|
|
|
value = (int)NumericValue.Value;
|
|
return true;
|
|
}
|
|
|
|
if (PropertyType == typeof(int?))
|
|
{
|
|
if (!NumericValue.HasValue)
|
|
{
|
|
value = null;
|
|
return true;
|
|
}
|
|
|
|
if (decimal.Truncate(NumericValue.Value) != NumericValue.Value)
|
|
{
|
|
error = $"{DisplayName} must be a whole number.";
|
|
return false;
|
|
}
|
|
|
|
value = (int)NumericValue.Value;
|
|
return true;
|
|
}
|
|
|
|
if (PropertyType == typeof(DateTime?))
|
|
{
|
|
if (!DateValue.HasValue)
|
|
{
|
|
value = null;
|
|
return true;
|
|
}
|
|
|
|
value = DateValue.Value.DateTime;
|
|
return true;
|
|
}
|
|
|
|
if (PropertyType == typeof(Dictionary<string, CreatorConfig>))
|
|
{
|
|
if (string.IsNullOrWhiteSpace(TextValue))
|
|
{
|
|
value = new Dictionary<string, CreatorConfig>();
|
|
return true;
|
|
}
|
|
|
|
try
|
|
{
|
|
Dictionary<string, CreatorConfig>? parsed =
|
|
JsonConvert.DeserializeObject<Dictionary<string, CreatorConfig>>(TextValue);
|
|
value = parsed ?? new Dictionary<string, CreatorConfig>();
|
|
return true;
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
error = $"{DisplayName} must be valid JSON.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
error = $"{DisplayName} has an unsupported field type.";
|
|
return false;
|
|
}
|
|
|
|
public void ClearError()
|
|
{
|
|
ErrorMessage = string.Empty;
|
|
}
|
|
|
|
public void SetError(string message)
|
|
{
|
|
ErrorMessage = message;
|
|
}
|
|
|
|
private void LoadInitialValue(object? initialValue)
|
|
{
|
|
if (IsBoolean)
|
|
{
|
|
BoolValue = initialValue is bool boolValue && boolValue;
|
|
return;
|
|
}
|
|
|
|
if (IsEnum)
|
|
{
|
|
EnumValue = initialValue?.ToString() ?? EnumOptions.FirstOrDefault();
|
|
return;
|
|
}
|
|
|
|
if (PropertyType == typeof(Dictionary<string, CreatorConfig>))
|
|
{
|
|
Dictionary<string, CreatorConfig> creatorConfigs =
|
|
initialValue as Dictionary<string, CreatorConfig> ?? new Dictionary<string, CreatorConfig>();
|
|
TextValue = JsonConvert.SerializeObject(creatorConfigs, Formatting.Indented);
|
|
return;
|
|
}
|
|
|
|
if (PropertyType == typeof(DateTime?))
|
|
{
|
|
DateTime? date = initialValue is DateTime dt ? dt : null;
|
|
DateValue = date.HasValue ? new DateTimeOffset(date.Value) : null;
|
|
return;
|
|
}
|
|
|
|
if (PropertyType == typeof(int))
|
|
{
|
|
NumericValue = initialValue is int intValue ? intValue : 0;
|
|
return;
|
|
}
|
|
|
|
if (PropertyType == typeof(int?))
|
|
{
|
|
NumericValue = initialValue is int nullableIntValue ? nullableIntValue : null;
|
|
return;
|
|
}
|
|
|
|
TextValue = initialValue?.ToString() ?? string.Empty;
|
|
}
|
|
|
|
private static string ToDisplayName(string propertyName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(propertyName))
|
|
{
|
|
return propertyName;
|
|
}
|
|
|
|
return string.Concat(propertyName.Select((character, index) =>
|
|
index > 0 && char.IsUpper(character) && !char.IsUpper(propertyName[index - 1])
|
|
? $" {character}"
|
|
: character.ToString()));
|
|
}
|
|
}
|