1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge branch 'better-databindings' into development

This commit is contained in:
Robert 2021-03-04 20:26:18 +01:00
commit bb1ace0941
47 changed files with 331 additions and 217 deletions

View File

@ -15,7 +15,8 @@ Artemis 1 is no longer supported and Artemis 2 is in active development. This en
**Pre-release download**: https://github.com/SpoinkyNL/Artemis/releases (pre-release means your profiles may break at any given time!) **Pre-release download**: https://github.com/SpoinkyNL/Artemis/releases (pre-release means your profiles may break at any given time!)
**Plugin documentation**: https://artemis-rgb.com/docs/ **Plugin documentation**: https://artemis-rgb.com/docs/
**Please note that even though we have plugins for each brand supported by RGB.NET, they have not been thoroughly tested. If you run into any issues please let us know on Discord.** **Please note that even though we have plugins for each brand supported by RGB.NET, they have not been thoroughly tested. If you run into any issues please let us know on Discord.**
A full list of supported devices can be found on the wiki [here](https://wiki.artemis-rgb.com/en/guides/user/devices).
#### Want to build? Follow these instructions #### Want to build? Follow these instructions
1. Create a central folder like ```C:\Repos``` 1. Create a central folder like ```C:\Repos```

View File

@ -37,6 +37,7 @@ steps:
inputs: inputs:
command: 'build' command: 'build'
projects: '$(rgbSolution)' projects: '$(rgbSolution)'
arguments: '--configuration Release'
- task: PublishPipelineArtifact@1 - task: PublishPipelineArtifact@1
displayName: 'Upload build to Azure Pipelines' displayName: 'Upload build to Azure Pipelines'

View File

@ -36,9 +36,6 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public override void ApplyValue(float value) public override void ApplyValue(float value)
{ {
if (ValueTypeSetExpression == null)
return;
if (DataBinding!.LayerProperty.PropertyDescription.MaxInputValue is float max) if (DataBinding!.LayerProperty.PropertyDescription.MaxInputValue is float max)
value = Math.Min(value, max); value = Math.Min(value, max);
if (DataBinding!.LayerProperty.PropertyDescription.MinInputValue is float min) if (DataBinding!.LayerProperty.PropertyDescription.MinInputValue is float min)

View File

@ -6,7 +6,7 @@
internal BoolLayerProperty() internal BoolLayerProperty()
{ {
KeyframesSupported = false; KeyframesSupported = false;
RegisterDataBindingProperty(b => b, new GeneralDataBindingConverter<bool>()); RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new GeneralDataBindingConverter<bool>(), "Value");
} }
/// <summary> /// <summary>

View File

@ -1,17 +1,43 @@
namespace Artemis.Core using System.ComponentModel;
using SkiaSharp;
namespace Artemis.Core
{ {
/// <inheritdoc /> /// <inheritdoc />
public class ColorGradientLayerProperty : LayerProperty<ColorGradient> public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
{ {
private ColorGradient? _subscribedGradient;
internal ColorGradientLayerProperty() internal ColorGradientLayerProperty()
{ {
KeyframesSupported = false; KeyframesSupported = false;
DataBindingsSupported = false; DataBindingsSupported = true;
DefaultValue = new ColorGradient(); DefaultValue = new ColorGradient();
CurrentValueSet += OnCurrentValueSet; CurrentValueSet += OnCurrentValueSet;
} }
private void CreateDataBindingRegistrations()
{
ClearDataBindingProperties();
if (CurrentValue == null)
return;
for (int index = 0; index < CurrentValue.Stops.Count; index++)
{
int stopIndex = index;
void Setter(SKColor value)
{
CurrentValue.Stops[stopIndex].Color = value;
CurrentValue.OnColorValuesUpdated();
}
RegisterDataBindingProperty(() => CurrentValue.Stops[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}");
}
}
/// <summary> /// <summary>
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient" /> /// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient" />
/// </summary> /// </summary>
@ -31,6 +57,22 @@
// Don't allow color gradients to be null // Don't allow color gradients to be null
if (BaseValue == null) if (BaseValue == null)
BaseValue = DefaultValue ?? new ColorGradient(); BaseValue = DefaultValue ?? new ColorGradient();
if (_subscribedGradient != BaseValue)
{
if (_subscribedGradient != null)
_subscribedGradient.PropertyChanged -= SubscribedGradientOnPropertyChanged;
_subscribedGradient = BaseValue;
_subscribedGradient.PropertyChanged += SubscribedGradientOnPropertyChanged;
}
CreateDataBindingRegistrations();
}
private void SubscribedGradientOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (CurrentValue.Stops.Count != GetAllDataBindingRegistrations().Count)
CreateDataBindingRegistrations();
} }
#region Overrides of LayerProperty<ColorGradient> #region Overrides of LayerProperty<ColorGradient>
@ -41,10 +83,31 @@
// Don't allow color gradients to be null // Don't allow color gradients to be null
if (BaseValue == null) if (BaseValue == null)
BaseValue = DefaultValue ?? new ColorGradient(); BaseValue = DefaultValue ?? new ColorGradient();
base.OnInitialize(); base.OnInitialize();
} }
#endregion #endregion
} }
internal class ColorStopDataBindingConverter : DataBindingConverter<ColorGradient, SKColor>
{
public ColorStopDataBindingConverter()
{
SupportsInterpolate = true;
SupportsSum = true;
}
/// <inheritdoc />
public override SKColor Sum(SKColor a, SKColor b)
{
return a.Sum(b);
}
/// <inheritdoc />
public override SKColor Interpolate(SKColor a, SKColor b, double progress)
{
return a.Interpolate(b, (float) progress);
}
}
} }

View File

@ -5,7 +5,7 @@
{ {
internal FloatLayerProperty() internal FloatLayerProperty()
{ {
RegisterDataBindingProperty(value => value, new FloatDataBindingConverter()); RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new FloatDataBindingConverter(), "Value");
} }
/// <summary> /// <summary>

View File

@ -5,8 +5,8 @@
{ {
internal FloatRangeLayerProperty() internal FloatRangeLayerProperty()
{ {
RegisterDataBindingProperty(range => range.Start, new FloatDataBindingConverter<FloatRange>()); RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new FloatDataBindingConverter<FloatRange>(), "Start");
RegisterDataBindingProperty(range => range.End, new FloatDataBindingConverter<FloatRange>()); RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new FloatDataBindingConverter<FloatRange>(), "End");
CurrentValueSet += OnCurrentValueSet; CurrentValueSet += OnCurrentValueSet;
} }

View File

@ -7,7 +7,7 @@ namespace Artemis.Core
{ {
internal IntLayerProperty() internal IntLayerProperty()
{ {
RegisterDataBindingProperty(value => value, new IntDataBindingConverter()); RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new IntDataBindingConverter(), "Value");
} }
/// <summary> /// <summary>

View File

@ -5,8 +5,8 @@
{ {
internal IntRangeLayerProperty() internal IntRangeLayerProperty()
{ {
RegisterDataBindingProperty(range => range.Start, new IntDataBindingConverter<IntRange>()); RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new IntDataBindingConverter<IntRange>(), "Start");
RegisterDataBindingProperty(range => range.End, new IntDataBindingConverter<IntRange>()); RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new IntDataBindingConverter<IntRange>(), "End");
CurrentValueSet += OnCurrentValueSet; CurrentValueSet += OnCurrentValueSet;
} }

View File

@ -7,7 +7,7 @@ namespace Artemis.Core
{ {
internal SKColorLayerProperty() internal SKColorLayerProperty()
{ {
RegisterDataBindingProperty(value => value, new SKColorDataBindingConverter()); RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new SKColorDataBindingConverter(), "Value");
} }
/// <summary> /// <summary>

View File

@ -7,8 +7,8 @@ namespace Artemis.Core
{ {
internal SKPointLayerProperty() internal SKPointLayerProperty()
{ {
RegisterDataBindingProperty(point => point.X, new FloatDataBindingConverter<SKPoint>()); RegisterDataBindingProperty(() => CurrentValue.X, value => CurrentValue = new SKPoint(value, CurrentValue.Y), new FloatDataBindingConverter<SKPoint>(), "X");
RegisterDataBindingProperty(point => point.Y, new FloatDataBindingConverter<SKPoint>()); RegisterDataBindingProperty(() => CurrentValue.Y, value => CurrentValue = new SKPoint(CurrentValue.X, value), new FloatDataBindingConverter<SKPoint>(), "Y");
} }
/// <summary> /// <summary>

View File

@ -7,8 +7,8 @@ namespace Artemis.Core
{ {
internal SKSizeLayerProperty() internal SKSizeLayerProperty()
{ {
RegisterDataBindingProperty(size => size.Height, new FloatDataBindingConverter<SKSize>()); RegisterDataBindingProperty(() => CurrentValue.Width, (value) => CurrentValue = new SKSize(value, CurrentValue.Height), new FloatDataBindingConverter<SKSize>(), "Width");
RegisterDataBindingProperty(size => size.Width, new FloatDataBindingConverter<SKSize>()); RegisterDataBindingProperty(() => CurrentValue.Height, (value) => CurrentValue = new SKSize(CurrentValue.Width, value), new FloatDataBindingConverter<SKSize>(), "Height");
} }
/// <summary> /// <summary>

View File

@ -97,7 +97,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public Type? GetTargetType() public Type? GetTargetType()
{ {
return Registration?.PropertyExpression.ReturnType; return Registration?.Getter.Method.ReturnType;
} }
private void ResetEasing(TProperty value) private void ResetEasing(TProperty value)
@ -272,7 +272,7 @@ namespace Artemis.Core
throw new ObjectDisposedException("DataBinding"); throw new ObjectDisposedException("DataBinding");
// General // General
DataBindingRegistration<TLayerProperty, TProperty>? registration = LayerProperty.GetDataBindingRegistration<TProperty>(Entity.TargetExpression); DataBindingRegistration<TLayerProperty, TProperty>? registration = LayerProperty.GetDataBindingRegistration<TProperty>(Entity.Identifier);
if (registration != null) if (registration != null)
ApplyRegistration(registration); ApplyRegistration(registration);
@ -293,7 +293,7 @@ namespace Artemis.Core
// Don't save an invalid state // Don't save an invalid state
if (Registration != null) if (Registration != null)
Entity.TargetExpression = Registration.PropertyExpression.ToString(); Entity.Identifier = Registration.DisplayName;
Entity.EasingTime = EasingTime; Entity.EasingTime = EasingTime;
Entity.EasingFunction = (int) EasingFunction; Entity.EasingFunction = (int) EasingFunction;

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -10,21 +8,6 @@ namespace Artemis.Core
/// </summary> /// </summary>
public abstract class DataBindingConverter<TLayerProperty, TProperty> : IDataBindingConverter public abstract class DataBindingConverter<TLayerProperty, TProperty> : IDataBindingConverter
{ {
/// <summary>
/// Gets a dynamically compiled getter pointing to the data bound property
/// </summary>
public Func<TLayerProperty, TProperty>? GetExpression { get; private set; }
/// <summary>
/// Gets a dynamically compiled setter pointing to the data bound property used for value types
/// </summary>
public Action<TProperty>? ValueTypeSetExpression { get; private set; }
/// <summary>
/// Gets a dynamically compiled setter pointing to the data bound property used for reference types
/// </summary>
public Action<TLayerProperty, TProperty>? ReferenceTypeSetExpression { get; private set; }
/// <summary> /// <summary>
/// Gets the data binding this converter is applied to /// Gets the data binding this converter is applied to
/// </summary> /// </summary>
@ -40,9 +23,6 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool SupportsInterpolate { get; protected set; } public bool SupportsInterpolate { get; protected set; }
/// <inheritdoc />
public Type SupportedType => typeof(TProperty);
/// <summary> /// <summary>
/// Returns the sum of <paramref name="a" /> and <paramref name="b" /> /// Returns the sum of <paramref name="a" /> and <paramref name="b" />
/// </summary> /// </summary>
@ -65,12 +45,9 @@ namespace Artemis.Core
/// <param name="value"></param> /// <param name="value"></param>
public virtual void ApplyValue(TProperty value) public virtual void ApplyValue(TProperty value)
{ {
if (DataBinding == null) if (DataBinding?.Registration == null)
throw new ArtemisCoreException("Data binding converter is not yet initialized"); throw new ArtemisCoreException("Data binding converter is not yet initialized");
if (ReferenceTypeSetExpression != null) DataBinding.Registration.Setter(value);
ReferenceTypeSetExpression(DataBinding.LayerProperty.CurrentValue, value);
else if (ValueTypeSetExpression != null)
ValueTypeSetExpression(value);
} }
/// <summary> /// <summary>
@ -78,9 +55,9 @@ namespace Artemis.Core
/// </summary> /// </summary>
public virtual TProperty GetValue() public virtual TProperty GetValue()
{ {
if (DataBinding == null || GetExpression == null) if (DataBinding?.Registration == null)
throw new ArtemisCoreException("Data binding converter is not yet initialized"); throw new ArtemisCoreException("Data binding converter is not yet initialized");
return GetExpression(DataBinding.LayerProperty.CurrentValue); return DataBinding.Registration.Getter();
} }
/// <summary> /// <summary>
@ -104,83 +81,10 @@ namespace Artemis.Core
throw new ArtemisCoreException("Cannot initialize a data binding converter for a data binding without a registration"); throw new ArtemisCoreException("Cannot initialize a data binding converter for a data binding without a registration");
DataBinding = dataBinding; DataBinding = dataBinding;
GetExpression = dataBinding.Registration.PropertyExpression.Compile();
CreateSetExpression();
OnInitialized(); OnInitialized();
} }
private void CreateSetExpression() /// <inheritdoc />
{ public Type SupportedType => typeof(TProperty);
// If the registration does not point towards a member of LayerProperty<T>.CurrentValue, assign directly to LayerProperty<T>.CurrentValue
if (DataBinding!.Registration?.Member == null)
{
CreateSetCurrentValueExpression();
return;
}
// Ensure the member of LayerProperty<T>.CurrentValue has a setter
MethodInfo? setterMethod = null;
if (DataBinding.Registration.Member is PropertyInfo propertyInfo)
setterMethod = propertyInfo.GetSetMethod();
// If there is no setter, the built-in data binding cannot do its job, stay null
if (setterMethod == null)
return;
// If LayerProperty<T>.CurrentValue is a value type, assign it directly to LayerProperty<T>.CurrentValue after applying the changes
if (typeof(TLayerProperty).IsValueType)
CreateSetValueTypeExpression();
// If it is a reference type it can safely be updated by its reference
else
CreateSetReferenceTypeExpression();
}
private void CreateSetReferenceTypeExpression()
{
if (DataBinding!.Registration?.Member == null)
throw new ArtemisCoreException("Cannot create value setter for data binding without a registration");
ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
ParameterExpression parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue");
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, DataBinding.Registration.Member);
BinaryExpression assignment = Expression.Assign(memberAccess, propertyValue);
Expression<Action<TLayerProperty, TProperty>> referenceTypeLambda = Expression.Lambda<Action<TLayerProperty, TProperty>>(assignment, parameter, propertyValue);
ReferenceTypeSetExpression = referenceTypeLambda.Compile();
}
private void CreateSetValueTypeExpression()
{
if (DataBinding!.Registration?.Member == null)
throw new ArtemisCoreException("Cannot create value setter for data binding without a registration");
ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
ParameterExpression variableCurrent = Expression.Variable(typeof(TLayerProperty), "current");
ConstantExpression layerProperty = Expression.Constant(DataBinding.LayerProperty);
MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty,
DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]);
BlockExpression body = Expression.Block(
new[] {variableCurrent},
Expression.Assign(variableCurrent, layerPropertyMemberAccess),
Expression.Assign(Expression.MakeMemberAccess(variableCurrent, DataBinding.Registration.Member), propertyValue),
Expression.Assign(layerPropertyMemberAccess, variableCurrent)
);
Expression<Action<TProperty>> valueTypeLambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
ValueTypeSetExpression = valueTypeLambda.Compile();
}
private void CreateSetCurrentValueExpression()
{
ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
ConstantExpression layerProperty = Expression.Constant(DataBinding!.LayerProperty);
MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty,
DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]);
BinaryExpression body = Expression.Assign(layerPropertyMemberAccess, propertyValue);
Expression<Action<TProperty>> lambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
ValueTypeSetExpression = lambda.Compile();
}
} }
} }

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Artemis.Storage.Entities.Profile.DataBindings; using Artemis.Storage.Entities.Profile.DataBindings;
namespace Artemis.Core namespace Artemis.Core
@ -9,16 +7,14 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public class DataBindingRegistration<TLayerProperty, TProperty> : IDataBindingRegistration public class DataBindingRegistration<TLayerProperty, TProperty> : IDataBindingRegistration
{ {
internal DataBindingRegistration(LayerProperty<TLayerProperty> layerProperty, internal DataBindingRegistration(LayerProperty<TLayerProperty> layerProperty, DataBindingConverter<TLayerProperty, TProperty> converter,
DataBindingConverter<TLayerProperty, TProperty> converter, Func<TProperty> getter, Action<TProperty> setter, string displayName)
Expression<Func<TLayerProperty, TProperty>> propertyExpression)
{ {
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty)); LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
Converter = converter ?? throw new ArgumentNullException(nameof(converter)); Converter = converter ?? throw new ArgumentNullException(nameof(converter));
PropertyExpression = propertyExpression ?? throw new ArgumentNullException(nameof(propertyExpression)); Getter = getter ?? throw new ArgumentNullException(nameof(getter));
Setter = setter ?? throw new ArgumentNullException(nameof(setter));
if (propertyExpression.Body is MemberExpression memberExpression) DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName));
Member = memberExpression.Member;
} }
/// <summary> /// <summary>
@ -32,15 +28,17 @@ namespace Artemis.Core
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; } public DataBindingConverter<TLayerProperty, TProperty> Converter { get; }
/// <summary> /// <summary>
/// Gets the expression that that accesses the property /// Gets the function to call to get the value of the property
/// </summary> /// </summary>
public Expression<Func<TLayerProperty, TProperty>> PropertyExpression { get; } public Func<TProperty> Getter { get; }
/// <summary> /// <summary>
/// Gets the member the <see cref="PropertyExpression" /> targets /// Gets the action to call to set the value of the property
/// <para><see langword="null"/> if the <see cref="PropertyExpression" /> is not a member expression</para>
/// </summary> /// </summary>
public MemberInfo? Member { get; } public Action<TProperty> Setter { get; }
/// <inheritdoc />
public string DisplayName { get; }
/// <summary> /// <summary>
/// Gets the data binding created using this registration /// Gets the data binding created using this registration
@ -59,7 +57,7 @@ namespace Artemis.Core
if (DataBinding != null) if (DataBinding != null)
return DataBinding; return DataBinding;
DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetExpression == PropertyExpression.ToString()); DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.Identifier == DisplayName);
if (dataBinding == null) if (dataBinding == null)
return null; return null;

View File

@ -5,6 +5,11 @@
/// </summary> /// </summary>
public interface IDataBindingRegistration public interface IDataBindingRegistration
{ {
/// <summary>
/// Gets or sets the display name of the data binding registration
/// </summary>
string DisplayName { get; }
/// <summary> /// <summary>
/// Returns the data binding applied using this registration /// Returns the data binding applied using this registration
/// </summary> /// </summary>

View File

@ -97,6 +97,16 @@ namespace Artemis.Core
/// </summary> /// </summary>
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved; public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
/// <summary>
/// Occurs when a data binding property has been added
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertyRegistered;
/// <summary>
/// Occurs when all data binding properties have been removed
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertiesCleared;
/// <summary> /// <summary>
/// Occurs when a data binding has been enabled /// Occurs when a data binding has been enabled
/// </summary> /// </summary>

View File

@ -379,21 +379,16 @@ namespace Artemis.Core
public bool HasDataBinding => GetAllDataBindingRegistrations().Any(r => r.GetDataBinding() != null); public bool HasDataBinding => GetAllDataBindingRegistrations().Any(r => r.GetDataBinding() != null);
/// <summary> /// <summary>
/// Gets a data binding registration by the expression used to register it /// Gets a data binding registration by the display name used to register it
/// <para>Note: The expression must exactly match the one used to register the data binding</para> /// <para>Note: The expression must exactly match the one used to register the data binding</para>
/// </summary> /// </summary>
public DataBindingRegistration<T, TProperty>? GetDataBindingRegistration<TProperty>(Expression<Func<T, TProperty>> propertyExpression) public DataBindingRegistration<T, TProperty>? GetDataBindingRegistration<TProperty>(string identifier)
{
return GetDataBindingRegistration<TProperty>(propertyExpression.ToString());
}
internal DataBindingRegistration<T, TProperty>? GetDataBindingRegistration<TProperty>(string expression)
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("LayerProperty"); throw new ObjectDisposedException("LayerProperty");
IDataBindingRegistration? match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration<T, TProperty> registration && IDataBindingRegistration? match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration<T, TProperty> registration &&
registration.PropertyExpression.ToString() == expression); registration.DisplayName == identifier);
return (DataBindingRegistration<T, TProperty>?) match; return (DataBindingRegistration<T, TProperty>?) match;
} }
@ -410,21 +405,35 @@ namespace Artemis.Core
/// Registers a data binding property so that is available to the data binding system /// Registers a data binding property so that is available to the data binding system
/// </summary> /// </summary>
/// <typeparam name="TProperty">The type of the layer property</typeparam> /// <typeparam name="TProperty">The type of the layer property</typeparam>
/// <param name="propertyExpression">The expression pointing to the value to register</param> /// <param name="getter">The function to call to get the value of the property</param>
/// <param name="setter">The action to call to set the value of the property</param>
/// <param name="converter">The converter to use while applying the data binding</param> /// <param name="converter">The converter to use while applying the data binding</param>
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyExpression, DataBindingConverter<T, TProperty> converter) /// <param name="displayName">The display name of the data binding property</param>
public DataBindingRegistration<T, TProperty> RegisterDataBindingProperty<TProperty>(Func<TProperty> getter, Action<TProperty> setter, DataBindingConverter<T, TProperty> converter,
string displayName)
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
if (_dataBindingRegistrations.Any(d => d.DisplayName == displayName))
throw new ArtemisCoreException($"A databinding property named '{displayName}' is already registered.");
DataBindingRegistration<T, TProperty> registration = new(this, converter, getter, setter, displayName);
_dataBindingRegistrations.Add(registration);
OnDataBindingPropertyRegistered();
return registration;
}
/// <summary>
/// Removes all data binding properties so they are no longer available to the data binding system
/// </summary>
public void ClearDataBindingProperties()
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("LayerProperty"); throw new ObjectDisposedException("LayerProperty");
if (propertyExpression.Body.NodeType != ExpressionType.MemberAccess && propertyExpression.Body.NodeType != ExpressionType.Parameter) _dataBindingRegistrations.Clear();
throw new ArtemisCoreException("Provided expression is invalid, it must be 'value => value' or 'value => value.Property'"); OnDataBindingPropertiesCleared();
if (converter.SupportedType != propertyExpression.ReturnType)
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, propertyExpression));
} }
/// <summary> /// <summary>
@ -661,6 +670,12 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved; public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertyRegistered;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertiesCleared;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled; public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled;
@ -716,6 +731,22 @@ namespace Artemis.Core
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this)); KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
} }
/// <summary>
/// Invokes the <see cref="DataBindingPropertyRegistered" /> event
/// </summary>
protected virtual void OnDataBindingPropertyRegistered()
{
DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary>
/// Invokes the <see cref="DataBindingDisabled" /> event
/// </summary>
protected virtual void OnDataBindingPropertiesCleared()
{
DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary> /// <summary>
/// Invokes the <see cref="DataBindingEnabled" /> event /// Invokes the <see cref="DataBindingEnabled" /> event
/// </summary> /// </summary>

View File

@ -4,7 +4,7 @@ namespace Artemis.Storage.Entities.Profile.DataBindings
{ {
public class DataBindingEntity public class DataBindingEntity
{ {
public string TargetExpression { get; set; } public string Identifier { get; set; }
public TimeSpan EasingTime { get; set; } public TimeSpan EasingTime { get; set; }
public int EasingFunction { get; set; } public int EasingFunction { get; set; }

View File

@ -0,0 +1,55 @@
using Artemis.Storage.Migrations.Interfaces;
using LiteDB;
namespace Artemis.Storage.Migrations
{
public class M10BetterDataBindings : IStorageMigration
{
private void Migrate(BsonValue bsonValue)
{
if (!bsonValue.IsDocument || !bsonValue.AsDocument.TryGetValue("PropertyEntities", out BsonValue propertyEntities))
return;
foreach (BsonValue propertyEntity in propertyEntities.AsArray)
{
if (!propertyEntity.AsDocument.TryGetValue("DataBindingEntities", out BsonValue dataBindingEntities))
continue;
foreach (BsonValue dataBindingEntity in dataBindingEntities.AsArray)
{
if (!dataBindingEntity.AsDocument.TryGetValue("TargetExpression", out BsonValue targetExpression))
continue;
string value = targetExpression.AsString;
if (value == "value => value" || value == "b => b")
{
dataBindingEntity.AsDocument["Identifier"] = "Value";
}
else
{
string selector = value.Split("=>")[1];
string property = selector.Split(".")[1];
dataBindingEntity.AsDocument["Identifier"] = property;
}
dataBindingEntity.AsDocument.Remove("TargetExpression");
}
}
}
public int UserVersion => 10;
public void Apply(LiteRepository repository)
{
ILiteCollection<BsonDocument> collection = repository.Database.GetCollection("ProfileEntity");
foreach (BsonDocument bsonDocument in collection.FindAll())
{
foreach (BsonValue bsonLayer in bsonDocument["Layers"].AsArray)
Migrate(bsonLayer);
foreach (BsonValue bsonLayer in bsonDocument["Folders"].AsArray)
Migrate(bsonLayer);
collection.Update(bsonDocument);
}
}
}
}

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Artemis.UI.PropertyInput.BoolPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.BoolPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View File

@ -1,15 +1,25 @@
using Artemis.Core; using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class BoolPropertyInputViewModel : PropertyInputViewModel<bool> public class BoolPropertyInputViewModel : PropertyInputViewModel<bool>
{ {
private List<IDataBindingRegistration> _registrations;
public BoolPropertyInputViewModel(LayerProperty<bool> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService) public BoolPropertyInputViewModel(LayerProperty<bool> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService)
{ {
_registrations = layerProperty.GetAllDataBindingRegistrations();
} }
public bool IsEnabled => true; public bool IsEnabled => _registrations.Any(r => r.GetDataBinding() != null);
protected override void OnDataBindingsChanged()
{
NotifyOfPropertyChange(nameof(IsEnabled));
}
} }
} }

View File

@ -1,12 +1,12 @@
<UserControl x:Class="Artemis.UI.PropertyInput.BrushPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.BrushPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors" xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors"
xmlns:layerBrush="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core" xmlns:layerBrush="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type propertyInput:BrushPropertyInputViewModel}}"> d:DataContext="{d:DesignInstance {x:Type propertyInput:BrushPropertyInputViewModel}}">

View File

@ -7,7 +7,7 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference> public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
{ {

View File

@ -1,9 +1,8 @@
<UserControl x:Class="Artemis.UI.PropertyInput.ColorGradientPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.ColorGradientPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"

View File

@ -3,7 +3,7 @@ using Artemis.Core;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class ColorGradientPropertyInputViewModel : PropertyInputViewModel<ColorGradient> public class ColorGradientPropertyInputViewModel : PropertyInputViewModel<ColorGradient>
{ {

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Artemis.UI.PropertyInput.EnumPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.EnumPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View File

@ -4,7 +4,7 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class EnumPropertyInputViewModel<T> : PropertyInputViewModel<T> where T : Enum public class EnumPropertyInputViewModel<T> : PropertyInputViewModel<T> where T : Enum
{ {

View File

@ -1,11 +1,10 @@
<UserControl x:Class="Artemis.UI.PropertyInput.FloatPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.FloatPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"

View File

@ -4,7 +4,7 @@ using Artemis.UI.Shared.Services;
using FluentValidation; using FluentValidation;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class FloatPropertyInputViewModel : PropertyInputViewModel<float> public class FloatPropertyInputViewModel : PropertyInputViewModel<float>
{ {
@ -13,7 +13,7 @@ namespace Artemis.UI.PropertyInput
public FloatPropertyInputViewModel(LayerProperty<float> layerProperty, IProfileEditorService profileEditorService, IModelValidator<FloatPropertyInputViewModel> validator) public FloatPropertyInputViewModel(LayerProperty<float> layerProperty, IProfileEditorService profileEditorService, IModelValidator<FloatPropertyInputViewModel> validator)
: base(layerProperty, profileEditorService, validator) : base(layerProperty, profileEditorService, validator)
{ {
_registration = layerProperty.GetDataBindingRegistration(value => value); _registration = layerProperty.GetDataBindingRegistration<float>("Value");
} }
public bool IsEnabled => _registration.DataBinding == null; public bool IsEnabled => _registration.DataBinding == null;

View File

@ -1,9 +1,8 @@
<UserControl x:Class="Artemis.UI.PropertyInput.FloatRangePropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.FloatRangePropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"

View File

@ -5,7 +5,7 @@ using Artemis.UI.Shared.Services;
using FluentValidation; using FluentValidation;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class FloatRangePropertyInputViewModel : PropertyInputViewModel<FloatRange> public class FloatRangePropertyInputViewModel : PropertyInputViewModel<FloatRange>
{ {
@ -16,8 +16,8 @@ namespace Artemis.UI.PropertyInput
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IModelValidator<FloatRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator) IModelValidator<FloatRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
{ {
_startRegistration = layerProperty.GetDataBindingRegistration(range => range.Start); _startRegistration = layerProperty.GetDataBindingRegistration<float>("Start");
_endRegistration = layerProperty.GetDataBindingRegistration(range => range.End); _endRegistration = layerProperty.GetDataBindingRegistration<float>("End");
} }
public float Start public float Start

View File

@ -1,11 +1,10 @@
<UserControl x:Class="Artemis.UI.PropertyInput.IntPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.IntPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"

View File

@ -4,7 +4,7 @@ using Artemis.UI.Shared.Services;
using FluentValidation; using FluentValidation;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class IntPropertyInputViewModel : PropertyInputViewModel<int> public class IntPropertyInputViewModel : PropertyInputViewModel<int>
{ {
@ -13,7 +13,7 @@ namespace Artemis.UI.PropertyInput
public IntPropertyInputViewModel(LayerProperty<int> layerProperty, IProfileEditorService profileEditorService, IModelValidator<IntPropertyInputViewModel> validator) public IntPropertyInputViewModel(LayerProperty<int> layerProperty, IProfileEditorService profileEditorService, IModelValidator<IntPropertyInputViewModel> validator)
: base(layerProperty, profileEditorService, validator) : base(layerProperty, profileEditorService, validator)
{ {
_registration = layerProperty.GetDataBindingRegistration(value => value); _registration = layerProperty.GetDataBindingRegistration<int>("Value");
} }
public bool IsEnabled => _registration.DataBinding == null; public bool IsEnabled => _registration.DataBinding == null;

View File

@ -1,9 +1,8 @@
<UserControl x:Class="Artemis.UI.PropertyInput.IntRangePropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.IntRangePropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"

View File

@ -5,7 +5,7 @@ using Artemis.UI.Shared.Services;
using FluentValidation; using FluentValidation;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class IntRangePropertyInputViewModel : PropertyInputViewModel<IntRange> public class IntRangePropertyInputViewModel : PropertyInputViewModel<IntRange>
{ {
@ -16,8 +16,8 @@ namespace Artemis.UI.PropertyInput
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IModelValidator<IntRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator) IModelValidator<IntRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
{ {
_startRegistration = layerProperty.GetDataBindingRegistration(range => range.Start); _startRegistration = layerProperty.GetDataBindingRegistration<int>("Start");
_endRegistration = layerProperty.GetDataBindingRegistration(range => range.End); _endRegistration = layerProperty.GetDataBindingRegistration<int>("End");
} }
public int Start public int Start

View File

@ -1,10 +1,9 @@
<UserControl x:Class="Artemis.UI.PropertyInput.SKColorPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKColorPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="800" d:DesignHeight="25" d:DesignWidth="800"

View File

@ -3,7 +3,7 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using SkiaSharp; using SkiaSharp;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class SKColorPropertyInputViewModel : PropertyInputViewModel<SKColor> public class SKColorPropertyInputViewModel : PropertyInputViewModel<SKColor>
{ {
@ -11,7 +11,7 @@ namespace Artemis.UI.PropertyInput
public SKColorPropertyInputViewModel(LayerProperty<SKColor> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService) public SKColorPropertyInputViewModel(LayerProperty<SKColor> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService)
{ {
_registration = layerProperty.GetDataBindingRegistration(value => value); _registration = layerProperty.GetDataBindingRegistration<SKColor>("Value");
} }
public bool IsEnabled => _registration.DataBinding == null; public bool IsEnabled => _registration.DataBinding == null;

View File

@ -1,11 +1,10 @@
<UserControl x:Class="Artemis.UI.PropertyInput.SKPointPropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKPointPropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="800" d:DesignHeight="25" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:SKPointPropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:SKPointPropertyInputViewModel}">

View File

@ -6,7 +6,7 @@ using FluentValidation;
using SkiaSharp; using SkiaSharp;
using Stylet; using Stylet;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class SKPointPropertyInputViewModel : PropertyInputViewModel<SKPoint> public class SKPointPropertyInputViewModel : PropertyInputViewModel<SKPoint>
{ {
@ -16,8 +16,8 @@ namespace Artemis.UI.PropertyInput
public SKPointPropertyInputViewModel(LayerProperty<SKPoint> layerProperty, IProfileEditorService profileEditorService, public SKPointPropertyInputViewModel(LayerProperty<SKPoint> layerProperty, IProfileEditorService profileEditorService,
IModelValidator<SKPointPropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator) IModelValidator<SKPointPropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
{ {
_xRegistration = layerProperty.GetDataBindingRegistration(point => point.X); _xRegistration = layerProperty.GetDataBindingRegistration<float>("X");
_yRegistration = layerProperty.GetDataBindingRegistration(point => point.Y); _yRegistration = layerProperty.GetDataBindingRegistration<float>("Y");
} }
public float X public float X

View File

@ -1,9 +1,8 @@
<UserControl x:Class="Artemis.UI.PropertyInput.SKSizePropertyInputView" <UserControl x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKSizePropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"

View File

@ -8,7 +8,7 @@ using Stylet;
// using PropertyChanged; // using PropertyChanged;
namespace Artemis.UI.PropertyInput namespace Artemis.UI.DefaultTypes.PropertyInput
{ {
public class SKSizePropertyInputViewModel : PropertyInputViewModel<SKSize> public class SKSizePropertyInputViewModel : PropertyInputViewModel<SKSize>
{ {
@ -18,8 +18,8 @@ namespace Artemis.UI.PropertyInput
public SKSizePropertyInputViewModel(LayerProperty<SKSize> layerProperty, IProfileEditorService profileEditorService, public SKSizePropertyInputViewModel(LayerProperty<SKSize> layerProperty, IProfileEditorService profileEditorService,
IModelValidator<SKSizePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator) IModelValidator<SKSizePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
{ {
_widthRegistration = layerProperty.GetDataBindingRegistration(size => size.Width); _widthRegistration = layerProperty.GetDataBindingRegistration<float>("Width");
_heightRegistration = layerProperty.GetDataBindingRegistration(size => size.Height); _heightRegistration = layerProperty.GetDataBindingRegistration<float>("Height");
} }
// Since SKSize is immutable we need to create properties that replace the SKSize entirely // Since SKSize is immutable we need to create properties that replace the SKSize entirely

View File

@ -38,11 +38,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_dataBindingsVmFactory = dataBindingsVmFactory; _dataBindingsVmFactory = dataBindingsVmFactory;
if (Registration.Member != null) DisplayName = Registration.DisplayName.ToUpper();
DisplayName = Registration.Member.Name.ToUpper();
else
DisplayName = Registration.LayerProperty.PropertyDescription.Name.ToUpper();
AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true); AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType))); DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType)));
EasingViewModels = new BindableCollection<TimelineEasingViewModel>(); EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
@ -106,8 +102,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
protected override void OnInitialActivate() protected override void OnInitialActivate()
{ {
base.OnInitialActivate();
Initialize(); Initialize();
base.OnInitialActivate();
} }
private void Initialize() private void Initialize()

View File

@ -6,11 +6,18 @@
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<TabControl ItemsSource="{Binding Items}" SelectedIndex="{Binding SelectedItemIndex}" DisplayMemberPath="DisplayName" Style="{StaticResource MaterialDesignTabControl}"> <TabControl ItemsSource="{Binding Items}"
SelectedIndex="{Binding SelectedItemIndex}"
Style="{StaticResource MaterialDesignTabControl}" >
<TabControl.ContentTemplate> <TabControl.ContentTemplate>
<DataTemplate> <DataTemplate>
<ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" /> <ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
</DataTemplate> </DataTemplate>
</TabControl.ContentTemplate> </TabControl.ContentTemplate>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="-12 0" Text="{Binding DisplayName}"></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl> </TabControl>
</UserControl> </UserControl>

View File

@ -1,5 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -11,7 +14,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private ILayerProperty? _selectedDataBinding;
private int _selectedItemIndex; private int _selectedItemIndex;
private bool _updating;
public DataBindingsViewModel(IProfileEditorService profileEditorService, IDataBindingsVmFactory dataBindingsVmFactory) public DataBindingsViewModel(IProfileEditorService profileEditorService, IDataBindingsVmFactory dataBindingsVmFactory)
{ {
@ -24,9 +29,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
get => _selectedItemIndex; get => _selectedItemIndex;
set => SetAndNotify(ref _selectedItemIndex, value); set => SetAndNotify(ref _selectedItemIndex, value);
} }
private void CreateDataBindingViewModels() private void CreateDataBindingViewModels()
{ {
int oldIndex = SelectedItemIndex;
Items.Clear(); Items.Clear();
ILayerProperty layerProperty = _profileEditorService.SelectedDataBinding; ILayerProperty layerProperty = _profileEditorService.SelectedDataBinding;
@ -37,26 +43,64 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
// Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving // Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving
// and creating the actual data bindings // and creating the actual data bindings
foreach (IDataBindingRegistration registration in registrations) Items.AddRange(registrations.Select(registration => _dataBindingsVmFactory.DataBindingViewModel(registration)));
Items.Add(_dataBindingsVmFactory.DataBindingViewModel(registration));
SelectedItemIndex = 0; SelectedItemIndex = Items.Count < oldIndex ? 0 : oldIndex;
} }
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e) private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
{ {
CreateDataBindingViewModels(); CreateDataBindingViewModels();
SubscribeToSelectedDataBinding();
SelectedItemIndex = 0;
}
private void SubscribeToSelectedDataBinding()
{
if (_selectedDataBinding != null)
{
_selectedDataBinding.DataBindingPropertyRegistered -= DataBindingRegistrationsChanged;
_selectedDataBinding.DataBindingPropertiesCleared -= DataBindingRegistrationsChanged;
}
_selectedDataBinding = _profileEditorService.SelectedDataBinding;
if (_selectedDataBinding != null)
{
_selectedDataBinding.DataBindingPropertyRegistered += DataBindingRegistrationsChanged;
_selectedDataBinding.DataBindingPropertiesCleared += DataBindingRegistrationsChanged;
}
}
private void DataBindingRegistrationsChanged(object sender, LayerPropertyEventArgs e)
{
if (_updating)
return;
_updating = true;
Execute.PostToUIThread(async () =>
{
await Task.Delay(200);
CreateDataBindingViewModels();
_updating = false;
});
} }
#region Overrides of Screen #region Overrides of Screen
/// <inheritdoc />
protected override void OnInitialActivate() protected override void OnInitialActivate()
{ {
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged; _profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
CreateDataBindingViewModels(); CreateDataBindingViewModels();
SubscribeToSelectedDataBinding();
base.OnInitialActivate(); base.OnInitialActivate();
} }
protected override void OnActivate()
{
SelectedItemIndex = 0;
base.OnActivate();
}
protected override void OnClose() protected override void OnClose()
{ {

View File

@ -302,7 +302,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
TimeSpan delta = DateTime.Now - _lastUpdate; TimeSpan delta = DateTime.Now - _lastUpdate;
_lastUpdate = DateTime.Now; _lastUpdate = DateTime.Now;
if (!AlwaysApplyDataBindings.Value || _profileEditorService.SelectedProfile == null || _profileEditorService.Playing) if (!AlwaysApplyDataBindings.Value || _profileEditorService.SelectedProfile == null)
return; return;
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders() foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders()

View File

@ -4,9 +4,9 @@ using Artemis.Core.Services;
using Artemis.UI.Controllers; using Artemis.UI.Controllers;
using Artemis.UI.DefaultTypes.DataModel.Display; using Artemis.UI.DefaultTypes.DataModel.Display;
using Artemis.UI.DefaultTypes.DataModel.Input; using Artemis.UI.DefaultTypes.DataModel.Input;
using Artemis.UI.DefaultTypes.PropertyInput;
using Artemis.UI.InputProviders; using Artemis.UI.InputProviders;
using Artemis.UI.Ninject; using Artemis.UI.Ninject;
using Artemis.UI.PropertyInput;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Serilog; using Serilog;