mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Databinding modifiers - Redesigned API to leverage generics
Databinding modifiers - Updated default modifiers to use new API
This commit is contained in:
1 parent
2bb663db15
commit
fd9f3afef5
50 files changed
+487
-407
No files matched your search
@@ -33,7 +33,7 @@ namespace Artemis.Core
|
||||
public abstract Type LeftSideType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right side type of this condition operator. May be null if the operator does not support a left side type
|
||||
/// Gets the right side type of this condition operator. May be null if the operator does not support a right side
|
||||
/// </summary>
|
||||
public abstract Type? RightSideType { get; }
|
||||
|
||||
@@ -42,7 +42,14 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check for, must be either the same or be castable to the target type</param>
|
||||
/// <param name="side">Which side of the operator to check, left or right</param>
|
||||
public abstract bool SupportsType(Type type, ConditionParameterSide side);
|
||||
public bool SupportsType(Type type, ConditionParameterSide side)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
if (side == ConditionParameterSide.Left)
|
||||
return LeftSideType.IsCastableFrom(type);
|
||||
return RightSideType != null && RightSideType.IsCastableFrom(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the condition with the input types being provided as objects
|
||||
|
||||
@@ -13,17 +13,7 @@ namespace Artemis.Core
|
||||
/// <param name="a">The parameter on the left side of the expression</param>
|
||||
/// <param name="b">The parameter on the right side of the expression</param>
|
||||
public abstract bool Evaluate(TLeftSide a, TRightSide b);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool SupportsType(Type type, ConditionParameterSide side)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
if (side == ConditionParameterSide.Left)
|
||||
return LeftSideType.IsCastableFrom(type);
|
||||
return RightSideType.IsCastableFrom(type);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override bool InternalEvaluate(object? leftSideValue, object? rightSideValue)
|
||||
{
|
||||
@@ -40,7 +30,7 @@ namespace Artemis.Core
|
||||
else
|
||||
rightSide = default;
|
||||
|
||||
return Evaluate(leftSide, rightSide);
|
||||
return Evaluate(leftSide!, rightSide!);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -61,16 +51,6 @@ namespace Artemis.Core
|
||||
/// <param name="a">The parameter on the left side of the expression</param>
|
||||
public abstract bool Evaluate(TLeftSide a);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool SupportsType(Type type, ConditionParameterSide side)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
if (side == ConditionParameterSide.Left)
|
||||
return LeftSideType.IsCastableFrom(type);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override bool InternalEvaluate(object? leftSideValue, object? rightSideValue)
|
||||
{
|
||||
@@ -81,7 +61,7 @@ namespace Artemis.Core
|
||||
else
|
||||
leftSide = default;
|
||||
|
||||
return Evaluate(leftSide);
|
||||
return Evaluate(leftSide!);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -388,7 +388,7 @@ namespace Artemis.Core
|
||||
return;
|
||||
|
||||
if (!Operator.SupportsType(RightStaticValue.GetType(), ConditionParameterSide.Right))
|
||||
UpdateRightSideDynamic(null);
|
||||
UpdateRightSideStatic(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Artemis.Core
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to clear compiled expressions, without a left data model they are already null
|
||||
// No need to check for compatibility without a left side, when left site does get set it will be checked again
|
||||
if (LeftPath == null || !LeftPath.IsValid)
|
||||
{
|
||||
Operator = conditionOperator;
|
||||
@@ -323,7 +323,7 @@ namespace Artemis.Core
|
||||
return;
|
||||
|
||||
if (!Operator.SupportsType(RightStaticValue.GetType(), ConditionParameterSide.Right))
|
||||
UpdateRightSideDynamic(null);
|
||||
UpdateRightSideStatic(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way
|
||||
/// </summary>
|
||||
public abstract class DataBindingModifierType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the plugin info this data binding modifier belongs to
|
||||
/// <para>Note: Not set until after registering</para>
|
||||
/// </summary>
|
||||
public PluginInfo PluginInfo { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types this modifier supports
|
||||
/// </summary>
|
||||
public abstract IReadOnlyCollection<Type> CompatibleTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the supported parameter type
|
||||
/// <para>If <c>null</c>, the parameter type will match the source property</para>
|
||||
/// </summary>
|
||||
public virtual Type ParameterType => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this modifier supports a parameter, defaults to <c>true</c>
|
||||
/// </summary>
|
||||
public virtual bool SupportsParameter => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this modifier
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon of this modifier
|
||||
/// </summary>
|
||||
public abstract string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of this modifier
|
||||
/// </summary>
|
||||
public virtual string Description => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category of this modifier
|
||||
/// </summary>
|
||||
public virtual string Category => null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the given type is supported by the modifier
|
||||
/// </summary>
|
||||
public bool SupportsType(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
return CompatibleTypes.Any(t => t.IsCastableFrom(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the modifier must apply to a specific value, must be a value of a type contained in
|
||||
/// <see cref="CompatibleTypes" />
|
||||
/// </summary>
|
||||
/// <param name="currentValue">
|
||||
/// The current value before modification, is always of a type contained in
|
||||
/// <see cref="CompatibleTypes" />
|
||||
/// </param>
|
||||
/// <param name="parameterValue">
|
||||
/// The parameter to use for the modification, is always of a type contained in
|
||||
/// <see cref="CompatibleTypes" />
|
||||
/// </param>
|
||||
/// <returns>The modified value, must be a value of a type contained in <see cref="CompatibleTypes" /></returns>
|
||||
public abstract object Apply(object currentValue, object parameterValue);
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way
|
||||
/// <para>
|
||||
/// To implement your own condition operator, inherit <see cref="DataBindingModifierType{TValue, TParameter}" /> or
|
||||
/// <see cref="DataBindingModifierType{TValue}" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract class BaseDataBindingModifierType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the plugin info this data binding modifier belongs to
|
||||
/// <para>Note: Not set until after registering</para>
|
||||
/// </summary>
|
||||
public PluginInfo PluginInfo { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value type of this modifier type
|
||||
/// </summary>
|
||||
public abstract Type ValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter type of this modifier type. May be null if the modifier type does not support a parameter
|
||||
/// </summary>
|
||||
public abstract Type? ParameterType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this modifier
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon of this modifier
|
||||
/// </summary>
|
||||
public abstract string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of this modifier
|
||||
/// </summary>
|
||||
public virtual string Description => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category of this modifier
|
||||
/// </summary>
|
||||
public virtual string Category => null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the given type is supported by the modifier
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check for, must be either the same or be castable to the target type</param>
|
||||
/// <param name="part">Which part of the modifier to check, the value or the parameter</param>
|
||||
public bool SupportsType(Type type, ModifierTypePart part)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
if (part == ModifierTypePart.Value)
|
||||
return ValueType.IsCastableFrom(type);
|
||||
return ParameterType != null && ParameterType.IsCastableFrom(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the modifier to the provided current value
|
||||
/// <para>
|
||||
/// This leaves the caller responsible for the types matching <see cref="ValueType" /> and
|
||||
/// <see cref="ParameterType" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="currentValue">The current value before modification, type should match <see cref="ValueType" /></param>
|
||||
/// <param name="parameterValue">The parameter to use for the modification, type should match <see cref="ParameterType" /></param>
|
||||
/// <returns>The modified value, with a type of <see cref="ValueType" /></returns>
|
||||
internal abstract object? InternalApply(object? currentValue, object? parameterValue);
|
||||
}
|
||||
|
||||
public enum ModifierTypePart
|
||||
{
|
||||
Value,
|
||||
Parameter
|
||||
}
|
||||
}
|
||||
+37
-13
@@ -30,7 +30,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets the type of modifier that is being applied
|
||||
/// </summary>
|
||||
public DataBindingModifierType ModifierType { get; private set; }
|
||||
public BaseDataBindingModifierType? ModifierType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direct data binding this modifier is applied to
|
||||
@@ -40,7 +40,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets the type of the parameter, can either be dynamic (based on a data model value) or static
|
||||
/// </summary>
|
||||
public ProfileRightSideType ParameterType { get; private set; }
|
||||
public ProfileRightSideType ParameterType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position at which the modifier appears on the data binding
|
||||
@@ -56,7 +56,7 @@ namespace Artemis.Core
|
||||
/// Gets the parameter static value, only used it <see cref="ParameterType" /> is
|
||||
/// <see cref="ProfileRightSideType.Static" />
|
||||
/// </summary>
|
||||
public object ParameterStaticValue { get; private set; }
|
||||
public object? ParameterStaticValue { get; private set; }
|
||||
|
||||
internal DataBindingModifierEntity Entity { get; set; }
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
/// <param name="currentValue">The value to apply the modifier to, should be of the same type as the data binding target</param>
|
||||
/// <returns>The modified value</returns>
|
||||
public object Apply(object? currentValue)
|
||||
public object? Apply(object? currentValue)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
@@ -73,17 +73,17 @@ namespace Artemis.Core
|
||||
if (ModifierType == null)
|
||||
return currentValue;
|
||||
|
||||
if (!ModifierType.SupportsParameter)
|
||||
return ModifierType.Apply(currentValue, null);
|
||||
if (ModifierType.ParameterType == null)
|
||||
return ModifierType.InternalApply(currentValue, null);
|
||||
|
||||
if (ParameterType == ProfileRightSideType.Dynamic && ParameterPath != null && ParameterPath.IsValid)
|
||||
{
|
||||
object? value = ParameterPath.GetValue();
|
||||
return ModifierType.Apply(currentValue, value);
|
||||
return ModifierType.InternalApply(currentValue, value);
|
||||
}
|
||||
|
||||
if (ParameterType == ProfileRightSideType.Static)
|
||||
return ModifierType.Apply(currentValue, ParameterStaticValue);
|
||||
return ModifierType.InternalApply(currentValue, ParameterStaticValue);
|
||||
|
||||
return currentValue;
|
||||
}
|
||||
@@ -92,12 +92,11 @@ namespace Artemis.Core
|
||||
/// Updates the modifier type of the modifier and re-compiles the expression
|
||||
/// </summary>
|
||||
/// <param name="modifierType"></param>
|
||||
public void UpdateModifierType(DataBindingModifierType modifierType)
|
||||
public void UpdateModifierType(BaseDataBindingModifierType? modifierType)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
// Calling CreateExpression will clear compiled expressions
|
||||
if (modifierType == null)
|
||||
{
|
||||
ModifierType = null;
|
||||
@@ -105,11 +104,36 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
Type targetType = DirectDataBinding.DataBinding.GetTargetType();
|
||||
if (!modifierType.SupportsType(targetType))
|
||||
if (!modifierType.SupportsType(targetType, ModifierTypePart.Value))
|
||||
throw new ArtemisCoreException($"Cannot apply modifier type {modifierType.GetType().Name} to this modifier because " +
|
||||
$"it does not support this data binding's type {targetType.Name}");
|
||||
|
||||
ModifierType = modifierType;
|
||||
ValidateParameter();
|
||||
}
|
||||
|
||||
private void ValidateParameter()
|
||||
{
|
||||
if (ModifierType == null)
|
||||
return;
|
||||
|
||||
if (ParameterType == ProfileRightSideType.Dynamic)
|
||||
{
|
||||
if (ParameterPath == null || !ParameterPath.IsValid)
|
||||
return;
|
||||
|
||||
Type parameterType = ParameterPath.GetPropertyType()!;
|
||||
if (!ModifierType.SupportsType(parameterType, ModifierTypePart.Parameter))
|
||||
UpdateParameterDynamic(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ParameterStaticValue == null)
|
||||
return;
|
||||
|
||||
if (!ModifierType.SupportsType(ParameterStaticValue.GetType(), ModifierTypePart.Parameter))
|
||||
UpdateParameterStatic(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -165,7 +189,7 @@ namespace Artemis.Core
|
||||
// Modifier type
|
||||
if (Entity.ModifierTypePluginGuid != null && ModifierType == null)
|
||||
{
|
||||
DataBindingModifierType modifierType = DataBindingModifierTypeStore.Get(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType)?.DataBindingModifierType;
|
||||
BaseDataBindingModifierType modifierType = DataBindingModifierTypeStore.Get(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType)?.DataBindingModifierType;
|
||||
if (modifierType != null)
|
||||
UpdateModifierType(modifierType);
|
||||
}
|
||||
@@ -261,7 +285,7 @@ namespace Artemis.Core
|
||||
if (ModifierType != null)
|
||||
return;
|
||||
|
||||
DataBindingModifierType modifierType = e.TypeRegistration.DataBindingModifierType;
|
||||
BaseDataBindingModifierType modifierType = e.TypeRegistration.DataBindingModifierType;
|
||||
if (modifierType.PluginInfo.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType)
|
||||
UpdateModifierType(modifierType);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way using a parameter
|
||||
/// </summary>
|
||||
public abstract class DataBindingModifierType<TValue, TParameter> : BaseDataBindingModifierType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type ValueType => typeof(TValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type ParameterType => typeof(TParameter);
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the modifier must apply to a specific value
|
||||
/// </summary>
|
||||
/// <param name="currentValue">
|
||||
/// The current value before modification
|
||||
/// </param>
|
||||
/// <param name="parameterValue">
|
||||
/// The parameter to use for the modification
|
||||
/// </param>
|
||||
/// <returns>The modified value></returns>
|
||||
public abstract TValue Apply(TValue currentValue, TParameter parameterValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override object? InternalApply(object? currentValue, object? parameterValue)
|
||||
{
|
||||
// TODO: Can we avoid boxing/unboxing?
|
||||
TValue current;
|
||||
if (currentValue != null)
|
||||
current = (TValue) Convert.ChangeType(currentValue, typeof(TValue));
|
||||
else
|
||||
current = default;
|
||||
|
||||
TParameter parameter;
|
||||
if (parameterValue != null)
|
||||
parameter = (TParameter) Convert.ChangeType(parameterValue, typeof(TParameter));
|
||||
else
|
||||
parameter = default;
|
||||
|
||||
return Apply(current!, parameter!);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way
|
||||
/// </summary>
|
||||
public abstract class DataBindingModifierType<TValue> : BaseDataBindingModifierType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type ValueType => typeof(TValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type? ParameterType => null;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the modifier must apply to a specific value
|
||||
/// </summary>
|
||||
/// <param name="currentValue">
|
||||
/// The current value before modification
|
||||
/// </param>
|
||||
/// <returns>The modified value</returns>
|
||||
public abstract TValue Apply(TValue currentValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override object? InternalApply(object? currentValue, object? parameterValue)
|
||||
{
|
||||
// TODO: Can we avoid boxing/unboxing?
|
||||
TValue current;
|
||||
if (currentValue != null)
|
||||
current = (TValue) Convert.ChangeType(currentValue, typeof(TValue));
|
||||
else
|
||||
current = default;
|
||||
|
||||
return Apply(current!);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user