Profile editor - Many runtime fixes, UI is back to usable!

This commit is contained in:
SpoinkyNL committed 2020-09-12 23:11:08 +02:00
1 parent db9d9fb4e6
commit 7fff1a593f
38 files changed
+309 -352

No files matched your search

+1
View File
@@ -37,6 +37,7 @@
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
<PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
<PackageReference Include="LiteDB" Version="5.0.8" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
@@ -41,13 +41,13 @@ namespace Artemis.Core
if (DataBinding.LayerProperty.PropertyDescription.MinInputValue is float min)
value = Math.Max(value, min);
ValueSetter?.Invoke(value);
SetExpression?.Invoke(value);
}
/// <inheritdoc />
public override float GetValue()
{
return ValueGetter?.Invoke() ?? 0f;
return GetExpression(DataBinding.LayerProperty.CurrentValue);
}
}
}
@@ -26,13 +26,13 @@ namespace Artemis.Core
/// <inheritdoc />
public override void ApplyValue(object value)
{
ValueSetter?.Invoke(value);
SetExpression?.Invoke(value);
}
/// <inheritdoc />
public override object GetValue()
{
return ValueGetter?.Invoke();
return GetExpression(DataBinding.LayerProperty.CurrentValue);
}
}
}
@@ -3,12 +3,12 @@
namespace Artemis.Core
{
/// <inheritdoc />
public class IntDataBindingConverter : FloatDataBindingConverter<int>
public class IntDataBindingConverter : IntDataBindingConverter<int>
{
}
/// <inheritdoc />
public class IntDataBindingConverter<T> : DataBindingConverter<T, int> where T : ILayerProperty
public class IntDataBindingConverter<T> : DataBindingConverter<T, int>
{
/// <summary>
/// Creates a new instance of the <see cref="IntDataBindingConverter{T}" /> class
@@ -41,13 +41,13 @@ namespace Artemis.Core
/// <inheritdoc />
public override void ApplyValue(int value)
{
ValueSetter?.Invoke(value);
SetExpression?.Invoke(value);
}
/// <inheritdoc />
public override int GetValue()
{
return ValueGetter?.Invoke() ?? 0;
return GetExpression(DataBinding.LayerProperty.CurrentValue);
}
}
}
@@ -13,12 +13,12 @@ namespace Artemis.Core
/// <summary>
/// A dynamically compiled getter pointing to the data bound property
/// </summary>
public Func<TProperty> ValueGetter { get; private set; }
public Func<TLayerProperty, TProperty> GetExpression { get; private set; }
/// <summary>
/// A dynamically compiled setter pointing to the data bound property
/// </summary>
public Action<TProperty> ValueSetter { get; private set; }
public Action<TProperty> SetExpression { get; private set; }
/// <summary>
/// Gets the data binding this converter is applied to
@@ -75,32 +75,12 @@ namespace Artemis.Core
internal void Initialize(DataBinding<TLayerProperty, TProperty> dataBinding)
{
DataBinding = dataBinding;
ValueGetter = CreateValueGetter();
ValueSetter = CreateValueSetter();
GetExpression = dataBinding.Registration.PropertyExpression.Compile();
SetExpression = CreateValueSetter();
OnInitialized();
}
private Func<TProperty> CreateValueGetter()
{
if (DataBinding.TargetProperty?.DeclaringType == null)
return null;
var getterMethod = DataBinding.TargetProperty.GetGetMethod();
if (getterMethod == null)
return null;
var constant = Expression.Constant(DataBinding.LayerProperty);
// The path is null if the registration is applied to the root (LayerProperty.CurrentValue)
var property = DataBinding.Registration.Path == null
? Expression.Property(constant, DataBinding.TargetProperty)
: (MemberExpression) DataBinding.Registration.Path.Split('.').Aggregate<string, Expression>(constant, Expression.Property);
var lambda = Expression.Lambda<Func<TProperty>>(property);
return lambda.Compile();
}
private Action<TProperty> CreateValueSetter()
{
if (DataBinding.TargetProperty?.DeclaringType == null)
@@ -110,10 +90,9 @@ namespace Artemis.Core
if (setterMethod == null)
return null;
var constant = Expression.Constant(DataBinding.LayerProperty);
var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
var body = Expression.Call(constant, setterMethod, propertyValue);
var body = DataBinding.Registration.PropertyExpression;
var lambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
return lambda.Compile();
}
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Artemis.Core
@@ -7,16 +8,13 @@ namespace Artemis.Core
/// <inheritdoc />
public class DataBindingRegistration<TLayerProperty, TProperty> : IDataBindingRegistration
{
internal DataBindingRegistration(
LayerProperty<TLayerProperty> layerProperty,
DataBindingConverter<TLayerProperty, TProperty> converter,
PropertyInfo property,
string path)
internal DataBindingRegistration(LayerProperty<TLayerProperty> layerProperty,
DataBindingConverter<TLayerProperty, TProperty> converter,
Expression<Func<TLayerProperty, TProperty>> propertyExpression)
{
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
Converter = converter ?? throw new ArgumentNullException(nameof(converter));
Property = property ?? throw new ArgumentNullException(nameof(property));
Path = path ?? throw new ArgumentNullException(nameof(path));
PropertyExpression = propertyExpression ?? throw new ArgumentNullException(nameof(propertyExpression));
}
/// <summary>
@@ -24,22 +22,21 @@ namespace Artemis.Core
/// </summary>
public LayerProperty<TLayerProperty> LayerProperty { get; }
/// <summary>
/// Gets the converter that's used by the data binding
/// </summary>
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; }
/// <summary>
/// Gets the expression that that accesses the property
/// </summary>
public Expression<Func<TLayerProperty, TProperty>> PropertyExpression { get; }
/// <summary>
/// Gets the registered property
/// </summary>
public PropertyInfo Property { get; }
/// <summary>
/// Gets the path of the registered property on the layer property
/// </summary>
public string Path { get; }
/// <summary>
/// Gets the data binding created using this registration
/// </summary>
@@ -51,7 +48,7 @@ namespace Artemis.Core
if (DataBinding != null)
return DataBinding;
var dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetProperty == Path);
var dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetProperty == PropertyExpression.ToString());
if (dataBinding == null)
return null;
+16 -4
View File
@@ -64,8 +64,8 @@ namespace Artemis.Core
_leds = new List<ArtemisLed>();
_expandedPropertyGroups = new List<string>();
Initialize();
Load();
Initialize();
}
internal LayerEntity LayerEntity { get; set; }
@@ -148,11 +148,22 @@ namespace Artemis.Core
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
// Layers have two hardcoded property groups, instantiate them
var generalAttribute = Attribute.GetCustomAttribute(
GetType().GetProperty(nameof(General)),
typeof(PropertyGroupDescriptionAttribute)
);
var transformAttribute = Attribute.GetCustomAttribute(
GetType().GetProperty(nameof(Transform)),
typeof(PropertyGroupDescriptionAttribute)
);
General.GroupDescription = (PropertyGroupDescriptionAttribute) generalAttribute;
General.Initialize(this, "General.", Constants.CorePluginInfo);
Transform.GroupDescription = (PropertyGroupDescriptionAttribute) transformAttribute;
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo);
General.ShapeType.BaseValueChanged += ShapeTypeOnBaseValueChanged;
ApplyShapeType();
ActivateLayerBrush();
}
#region Storage
@@ -165,8 +176,6 @@ namespace Artemis.Core
Order = LayerEntity.Order;
_expandedPropertyGroups.AddRange(LayerEntity.ExpandedPropertyGroups);
ActivateLayerBrush();
LoadRenderElement();
}
@@ -710,6 +719,9 @@ namespace Artemis.Core
internal void ActivateLayerBrush()
{
var current = General.BrushReference.CurrentValue;
if (current == null)
return;
var descriptor = LayerBrushStore.Get(current.BrushPluginGuid, current.BrushType)?.LayerBrushDescriptor;
descriptor?.CreateInstance(this);
@@ -729,7 +741,7 @@ namespace Artemis.Core
}
#endregion
#region Event handlers
private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e)
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile;
namespace Artemis.Core
@@ -20,5 +21,15 @@ namespace Artemis.Core
/// </para>
/// </summary>
void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description);
/// <summary>
/// Returns a list off all data binding registrations
/// </summary>
List<IDataBindingRegistration> GetAllDataBindingRegistrations();
/// <summary>
/// Gets or sets whether the property is hidden in the UI
/// </summary>
bool IsHidden { get; set; }
}
}
@@ -61,9 +61,7 @@ namespace Artemis.Core
private bool _isHidden;
/// <summary>
/// Gets or sets whether the property is hidden in the UI
/// </summary>
/// <inheritdoc />
public bool IsHidden
{
get => _isHidden;
@@ -335,36 +333,21 @@ namespace Artemis.Core
return _dataBindingRegistrations;
}
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda, DataBindingConverter<T, TProperty> converter)
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyExpression, DataBindingConverter<T, TProperty> converter)
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
// If the lambda references to itself, use the property info of public new T CurrentValue
PropertyInfo propertyInfo;
string path = null;
if (propertyLambda.Parameters[0] == propertyLambda.Body)
propertyInfo = GetType().GetProperties().FirstOrDefault(p => p.Name == nameof(CurrentValue) && p.PropertyType == typeof(T));
else
{
propertyInfo = ReflectionUtilities.GetPropertyInfo(CurrentValue, propertyLambda);
// Deconstruct the lambda
var current = (MemberExpression) propertyLambda.Body;
path = current.Member.Name;
while (current.Expression is MemberExpression memberExpression)
{
path = current.Member.Name + "." + path;
current = memberExpression;
}
}
if (propertyExpression.Body.NodeType != ExpressionType.MemberAccess && propertyExpression.Body.NodeType != ExpressionType.Parameter)
throw new ArtemisCoreException("Provided expression is invalid, it must be 'value => value' or 'value => value.Property'");
if (converter.SupportedType != propertyInfo.PropertyType)
if (converter.SupportedType != propertyExpression.ReturnType)
{
throw new ArtemisCoreException($"Cannot register data binding property for property {propertyInfo.Name} " +
throw new ArtemisCoreException($"Cannot register data binding property for property {PropertyDescription.Name} " +
"because the provided converter does not support the property's type");
}
_dataBindingRegistrations.Add(new DataBindingRegistration<T, TProperty>(this, converter, propertyInfo, path));
_dataBindingRegistrations.Add(new DataBindingRegistration<T, TProperty>(this, converter, propertyExpression));
}
/// <summary>
@@ -432,7 +415,6 @@ namespace Artemis.Core
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
IsLoadedFromStorage = fromStorage;
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime);
}
@@ -7,6 +7,7 @@ using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile;
using Humanizer;
namespace Artemis.Core
{
@@ -215,6 +216,10 @@ namespace Artemis.Core
if (instance == null)
throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
// Ensure the description has a name, if not this is a good point to set it based on the property info
if (string.IsNullOrWhiteSpace(propertyDescription.Name))
propertyDescription.Name = propertyInfo.Name.Humanize();
var entity = GetPropertyEntity(ProfileElement, path + propertyInfo.Name, out var fromStorage);
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription);
propertyInfo.SetValue(this, instance);
@@ -35,6 +35,7 @@ namespace Artemis.Core.LayerBrushes
internal void InitializeProperties()
{
Properties = Activator.CreateInstance<T>();
Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description};
Properties.LayerBrush = this;
Properties.Initialize(Layer, "LayerBrush.", PluginInfo);
PropertiesInitialized = true;
@@ -1,4 +1,5 @@
using System;
using Artemis.Core.Services;
using Ninject;
namespace Artemis.Core.LayerBrushes
@@ -43,11 +44,6 @@ namespace Artemis.Core.LayerBrushes
/// </summary>
public LayerBrushProvider LayerBrushProvider { get; }
/// <summary>
/// Gets or sets the kernel used to instantiate the described layer brush
/// </summary>
internal IKernel Kernel { get; set; }
/// <summary>
/// Creates an instance of the described brush and applies it to the layer
/// </summary>
@@ -56,7 +52,7 @@ namespace Artemis.Core.LayerBrushes
if (layer.LayerBrush != null)
throw new ArtemisCoreException("Layer already has an instantiated layer brush");
var brush = (BaseLayerBrush) Kernel.Get(LayerBrushType);
var brush = (BaseLayerBrush) CoreService.Kernel.Get(LayerBrushType);
brush.Layer = layer;
brush.Descriptor = this;
brush.Initialize();
@@ -41,11 +41,14 @@ namespace Artemis.Core.LayerBrushes
if (!Enabled)
throw new ArtemisPluginException(PluginInfo, "Can only add a layer brush descriptor when the plugin is enabled");
_layerBrushDescriptors.Add(new LayerBrushDescriptor(displayName, description, icon, typeof(T), this));
var descriptor = new LayerBrushDescriptor(displayName, description, icon, typeof(T), this);
_layerBrushDescriptors.Add(descriptor);
LayerBrushStore.Add(descriptor);
}
private void OnPluginDisabled(object sender, EventArgs e)
{
// The store will clean up the registrations by itself, the plugin just needs to clear its own list
_layerBrushDescriptors.Clear();
}
}
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
using Ninject;
@@ -44,12 +45,7 @@ namespace Artemis.Core.LayerEffects
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
/// </summary>
public LayerEffectProvider LayerEffectProvider { get; }
/// <summary>
/// Gets or sets the kernel used to instantiate the described layer effect
/// </summary>
internal IKernel Kernel { get; set; }
/// <summary>
/// Gets a boolean indicating if this descriptor is a placeholder for a missing plugin
/// </summary>
@@ -70,7 +66,7 @@ namespace Artemis.Core.LayerEffects
return;
}
var effect = (BaseLayerEffect) Kernel.Get(LayerEffectType);
var effect = (BaseLayerEffect)CoreService.Kernel.Get(LayerEffectType);
effect.ProfileElement = renderElement;
effect.EntityId = entity.Id;
effect.Order = entity.Order;
@@ -41,11 +41,14 @@ namespace Artemis.Core.LayerEffects
if (!Enabled)
throw new ArtemisPluginException(PluginInfo, "Can only add a layer effect descriptor when the plugin is enabled");
_layerEffectDescriptors.Add(new LayerEffectDescriptor(displayName, description, icon, typeof(T), this));
var descriptor = new LayerEffectDescriptor(displayName, description, icon, typeof(T), this);
_layerEffectDescriptors.Add(descriptor);
LayerEffectStore.Add(descriptor);
}
private void OnPluginDisabled(object sender, EventArgs e)
{
// The store will clean up the registrations by itself, the plugin just needs to clear its own list
_layerEffectDescriptors.Clear();
}
}
+5 -4
View File
@@ -23,8 +23,9 @@ namespace Artemis.Core.Services
/// </summary>
internal class CoreService : ICoreService
{
internal static IKernel Kernel;
private readonly Stopwatch _frameStopWatch;
private readonly IKernel _kernel;
private readonly ILogger _logger;
private readonly PluginSetting<LogEventLevel> _loggingLevel;
private readonly IPluginService _pluginService;
@@ -37,9 +38,9 @@ namespace Artemis.Core.Services
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService)
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
{
_kernel = kernel;
Kernel = kernel;
_logger = logger;
_pluginService = pluginService;
_rgbService = rgbService;
@@ -80,7 +81,7 @@ namespace Artemis.Core.Services
_logger.Information("Initializing Artemis Core version {version}", versionAttribute?.InformationalVersion);
ApplyLoggingLevel();
DeserializationLogger.Initialize(_kernel);
DeserializationLogger.Initialize(Kernel);
// Initialize the services
_pluginService.CopyBuiltInPlugins();
@@ -2,24 +2,16 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.LayerBrushes;
using Ninject;
namespace Artemis.Core.Services
{
internal class LayerBrushService : ILayerBrushService
{
private readonly IKernel _kernel;
public LayerBrushService(IKernel kernel)
{
_kernel = kernel;
}
public LayerBrushRegistration RegisterLayerBrush(LayerBrushDescriptor descriptor)
{
if (descriptor == null)
throw new ArgumentNullException(nameof(descriptor));
descriptor.Kernel = _kernel;
return LayerBrushStore.Add(descriptor);
}
@@ -35,4 +27,4 @@ namespace Artemis.Core.Services
return LayerBrushStore.GetAll().Select(r => r.LayerBrushDescriptor).ToList();
}
}
}
}
@@ -2,25 +2,16 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.LayerEffects;
using Ninject;
namespace Artemis.Core.Services
{
internal class LayerEffectService : ILayerEffectService
{
private readonly IKernel _kernel;
public LayerEffectService(IKernel kernel)
{
_kernel = kernel;
}
public LayerEffectRegistration RegisterLayerEffect(LayerEffectDescriptor descriptor)
{
if (descriptor == null)
throw new ArgumentNullException(nameof(descriptor));
descriptor.Kernel = _kernel;
return LayerEffectStore.Add(descriptor);
}