1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Artemis/src/Artemis.UI.Shared/Services/DataModelUIService.cs
2021-09-04 19:41:59 +01:00

267 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Shared.DefaultTypes.DataModel.Display;
using Artemis.UI.Shared.Input;
using Ninject;
using Ninject.Parameters;
namespace Artemis.UI.Shared.Services
{
internal class DataModelUIService : IDataModelUIService
{
private readonly IDataModelService _dataModelService;
private readonly IDataModelVmFactory _dataModelVmFactory;
private readonly IKernel _kernel;
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
public DataModelUIService(IDataModelService dataModelService, IDataModelVmFactory dataModelVmFactory, IKernel kernel)
{
_dataModelService = dataModelService;
_dataModelVmFactory = dataModelVmFactory;
_kernel = kernel;
_registeredDataModelEditors = new List<DataModelVisualizationRegistration>();
_registeredDataModelDisplays = new List<DataModelVisualizationRegistration>();
RegisteredDataModelEditors = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelEditors);
RegisteredDataModelDisplays = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelDisplays);
}
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; }
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; }
public DataModelPropertiesViewModel GetMainDataModelVisualization()
{
DataModelPropertiesViewModel viewModel = new(null, null, null);
foreach (DataModel dataModelExpansion in _dataModelService.GetDataModels().Where(d => d.IsExpansion).OrderBy(d => d.DataModelDescription.Name))
viewModel.Children.Add(new DataModelPropertiesViewModel(dataModelExpansion, viewModel, new DataModelPath(dataModelExpansion)));
// Update to populate children
viewModel.Update(this, null);
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this, null);
return viewModel;
}
public void UpdateModules(DataModelPropertiesViewModel mainDataModelVisualization)
{
List<DataModelVisualizationViewModel> disabledChildren = mainDataModelVisualization.Children
.Where(d => d.DataModel != null && !d.DataModel.Module.IsEnabled)
.ToList();
foreach (DataModelVisualizationViewModel child in disabledChildren)
mainDataModelVisualization.Children.Remove(child);
foreach (DataModel dataModelExpansion in _dataModelService.GetDataModels().OrderBy(d => d.DataModelDescription.Name))
{
if (mainDataModelVisualization.Children.All(c => c.DataModel != dataModelExpansion))
{
mainDataModelVisualization.Children.Add(
new DataModelPropertiesViewModel(dataModelExpansion, mainDataModelVisualization, new DataModelPath(dataModelExpansion))
);
}
}
mainDataModelVisualization.Update(this, null);
}
public DataModelPropertiesViewModel? GetPluginDataModelVisualization(List<Module> modules, bool includeMainDataModel)
{
DataModelPropertiesViewModel root;
// This will contain any modules that are always available
if (includeMainDataModel)
root = GetMainDataModelVisualization();
else
{
root = new DataModelPropertiesViewModel(null, null, null);
root.UpdateRequested += (sender, args) => root.Update(this, null);
}
foreach (Module module in modules)
{
DataModel? dataModel = _dataModelService.GetPluginDataModel(module);
if (dataModel == null)
continue;
root.Children.Add(new DataModelPropertiesViewModel(dataModel, root, new DataModelPath(dataModel)));
}
if (!root.Children.Any())
return null;
// Update to populate children
root.Update(this, null);
return root;
}
public DataModelVisualizationRegistration RegisterDataModelInput<T>(Plugin plugin, IReadOnlyCollection<Type>? compatibleConversionTypes = null) where T : DataModelInputViewModel
{
compatibleConversionTypes ??= new List<Type>();
Type viewModelType = typeof(T);
lock (_registeredDataModelEditors)
{
Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
DataModelVisualizationRegistration? existing = _registeredDataModelEditors.FirstOrDefault(r => r.SupportedType == supportedType);
if (existing != null)
{
if (existing.Plugin != plugin)
throw new ArtemisSharedUIException($"Cannot register data model input for type {supportedType.Name} because an editor was already" +
$" registered by {existing.Plugin}");
return existing;
}
_kernel.Bind(viewModelType).ToSelf();
// Create the registration
DataModelVisualizationRegistration registration = new(this, RegistrationType.Input, plugin, supportedType, viewModelType)
{
// Apply the compatible conversion types to the registration
CompatibleConversionTypes = compatibleConversionTypes
};
_registeredDataModelEditors.Add(registration);
return registration;
}
}
public DataModelVisualizationRegistration RegisterDataModelDisplay<T>(Plugin plugin) where T : DataModelDisplayViewModel
{
Type viewModelType = typeof(T);
lock (_registeredDataModelDisplays)
{
Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
DataModelVisualizationRegistration? existing = _registeredDataModelDisplays.FirstOrDefault(r => r.SupportedType == supportedType);
if (existing != null)
{
if (existing.Plugin != plugin)
throw new ArtemisSharedUIException($"Cannot register data model display for type {supportedType.Name} because an editor was already" +
$" registered by {existing.Plugin}");
return existing;
}
_kernel.Bind(viewModelType).ToSelf();
DataModelVisualizationRegistration registration = new(this, RegistrationType.Display, plugin, supportedType, viewModelType);
_registeredDataModelDisplays.Add(registration);
return registration;
}
}
public void RemoveDataModelInput(DataModelVisualizationRegistration registration)
{
lock (_registeredDataModelEditors)
{
if (_registeredDataModelEditors.Contains(registration))
{
registration.Unsubscribe();
_registeredDataModelEditors.Remove(registration);
_kernel.Unbind(registration.ViewModelType);
}
}
}
public void RemoveDataModelDisplay(DataModelVisualizationRegistration registration)
{
lock (_registeredDataModelDisplays)
{
if (_registeredDataModelDisplays.Contains(registration))
{
registration.Unsubscribe();
_registeredDataModelDisplays.Remove(registration);
_kernel.Unbind(registration.ViewModelType);
}
}
}
public DataModelDisplayViewModel? GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute? description, bool fallBackToDefault)
{
lock (_registeredDataModelDisplays)
{
DataModelDisplayViewModel? result;
DataModelVisualizationRegistration? match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
if (match != null)
{
// If this ever happens something is likely wrong with the plugin unload detection
if (match.Plugin.Kernel == null)
throw new ArtemisSharedUIException("Cannot GetDataModelDisplayViewModel for a registration by an uninitialized plugin");
result = (DataModelDisplayViewModel) match.Plugin.Kernel.Get(match.ViewModelType);
}
else if (!fallBackToDefault)
result = null;
else
result = _kernel.Get<DefaultDataModelDisplayViewModel>();
if (result != null)
result.PropertyDescription = description;
return result;
}
}
public DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback)
{
lock (_registeredDataModelEditors)
{
// Prefer a VM that natively supports the type
DataModelVisualizationRegistration? match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == propertyType);
// Fall back on a VM that supports the type through conversion
if (match == null)
match = _registeredDataModelEditors.FirstOrDefault(d => d.CompatibleConversionTypes != null && d.CompatibleConversionTypes.Contains(propertyType));
// Lastly try getting an enum VM if the provided type is an enum
if (match == null && propertyType.IsEnum)
match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == typeof(Enum));
if (match != null)
{
DataModelInputViewModel viewModel = InstantiateDataModelInputViewModel(match, description, initialValue);
viewModel.UpdateCallback = updateCallback;
return viewModel;
}
return null;
}
}
public DataModelDynamicViewModel GetDynamicSelectionViewModel(Module? module)
{
return _dataModelVmFactory.DataModelDynamicViewModel(module == null ? new List<Module>() : new List<Module> {module});
}
public DataModelDynamicViewModel GetDynamicSelectionViewModel(List<Module> modules)
{
return _dataModelVmFactory.DataModelDynamicViewModel(modules);
}
public DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription)
{
return _dataModelVmFactory.DataModelStaticViewModel(targetType, targetDescription);
}
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
{
// This assumes the type can be converted, that has been checked when the VM was created
if (initialValue != null && initialValue.GetType() != registration.SupportedType)
initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
IParameter[] parameters =
{
new ConstructorArgument("targetDescription", description),
new ConstructorArgument("initialValue", initialValue)
};
// If this ever happens something is likely wrong with the plugin unload detection
if (registration.Plugin.Kernel == null)
throw new ArtemisSharedUIException("Cannot InstantiateDataModelInputViewModel for a registration by an uninitialized plugin");
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Kernel.Get(registration.ViewModelType, parameters);
viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes;
return viewModel;
}
}
}