mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
118 lines
5.0 KiB
C#
118 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using Artemis.Core;
|
|
using DryIoc;
|
|
|
|
namespace Artemis.UI.Shared.Services.PropertyInput;
|
|
|
|
internal class PropertyInputService : IPropertyInputService
|
|
{
|
|
private readonly IContainer _container;
|
|
private readonly List<PropertyInputRegistration> _registeredPropertyEditors;
|
|
|
|
public PropertyInputService(IContainer container)
|
|
{
|
|
_container = container;
|
|
_registeredPropertyEditors = new List<PropertyInputRegistration>();
|
|
RegisteredPropertyEditors = new ReadOnlyCollection<PropertyInputRegistration>(_registeredPropertyEditors);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors { get; }
|
|
|
|
public PropertyInputRegistration RegisterPropertyInput<T>(Plugin plugin) where T : PropertyInputViewModel
|
|
{
|
|
return RegisterPropertyInput(typeof(T), plugin);
|
|
}
|
|
|
|
public PropertyInputRegistration RegisterPropertyInput(Type viewModelType, Plugin plugin)
|
|
{
|
|
if (!typeof(PropertyInputViewModel).IsAssignableFrom(viewModelType))
|
|
throw new ArtemisSharedUIException($"Property input VM type must implement {nameof(PropertyInputViewModel)}");
|
|
|
|
lock (_registeredPropertyEditors)
|
|
{
|
|
// Indirectly checked if there's a BaseType above
|
|
Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
|
|
// If the supported type is a generic, assume there is a base type
|
|
if (supportedType.IsGenericParameter)
|
|
{
|
|
if (supportedType.BaseType == null)
|
|
throw new ArtemisSharedUIException("Generic property input VM type must have a type constraint");
|
|
supportedType = supportedType.BaseType;
|
|
}
|
|
|
|
PropertyInputRegistration? existing = _registeredPropertyEditors.FirstOrDefault(r => r.SupportedType == supportedType);
|
|
if (existing != null)
|
|
{
|
|
if (existing.Plugin != plugin)
|
|
throw new ArtemisSharedUIException($"Cannot register property editor for type {supportedType.Name} because an editor was already " +
|
|
$"registered by {existing.Plugin}");
|
|
|
|
return existing;
|
|
}
|
|
|
|
_container.Register(viewModelType, setup: Setup.With(preventDisposal: true));
|
|
PropertyInputRegistration registration = new(this, plugin, supportedType, viewModelType);
|
|
_registeredPropertyEditors.Add(registration);
|
|
return registration;
|
|
}
|
|
}
|
|
|
|
public void RemovePropertyInput(PropertyInputRegistration registration)
|
|
{
|
|
lock (_registeredPropertyEditors)
|
|
{
|
|
if (_registeredPropertyEditors.Contains(registration))
|
|
{
|
|
registration.Unsubscribe();
|
|
_registeredPropertyEditors.Remove(registration);
|
|
|
|
_container.Unregister(registration.ViewModelType);
|
|
_container.ClearCache(registration.ViewModelType);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CanCreatePropertyInputViewModel(ILayerProperty layerProperty)
|
|
{
|
|
PropertyInputRegistration? registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == layerProperty.PropertyType);
|
|
if (registration == null && layerProperty.PropertyType.IsEnum)
|
|
registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(Enum));
|
|
|
|
return registration != null;
|
|
}
|
|
|
|
public PropertyInputViewModel<T>? CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty)
|
|
{
|
|
Type? viewModelType = null;
|
|
PropertyInputRegistration? registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T));
|
|
|
|
// Check for enums if no supported type was found
|
|
if (registration == null && typeof(T).IsEnum)
|
|
{
|
|
// The enum VM will likely be a generic, that requires creating a generic type matching the layer property
|
|
registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(Enum));
|
|
if (registration != null && registration.ViewModelType.IsGenericType)
|
|
viewModelType = registration.ViewModelType.MakeGenericType(layerProperty.GetType().GenericTypeArguments);
|
|
}
|
|
else if (registration != null)
|
|
{
|
|
viewModelType = registration.ViewModelType;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (viewModelType == null)
|
|
return null;
|
|
|
|
// ReSharper disable once InconsistentlySynchronizedField
|
|
// When you've just spent the last 2 hours trying to figure out a deadlock and reach this line, I'm so, so sorry. I thought this would be fine.
|
|
IContainer container = registration?.Plugin.Container ?? _container;
|
|
return (PropertyInputViewModel<T>) container.Resolve(viewModelType, args: new object[] { layerProperty });
|
|
}
|
|
} |