diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs
index f6226c541..67ac8df30 100644
--- a/src/Artemis.Core/Constants.cs
+++ b/src/Artemis.Core/Constants.cs
@@ -28,7 +28,7 @@ namespace Artemis.Core
public static readonly PluginInfo CorePluginInfo = new PluginInfo {Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"};
///
- /// A read-only collection containing all primitive number types
+ /// A read-only collection containing all primitive numeric types
///
public static IReadOnlyCollection NumberTypes = new List
{
@@ -44,5 +44,30 @@ namespace Artemis.Core
typeof(double),
typeof(decimal)
};
+
+ ///
+ /// A read-only collection containing all primitive integral numeric types
+ ///
+ public static IReadOnlyCollection IntegralNumberTypes = new List
+ {
+ typeof(sbyte),
+ typeof(byte),
+ typeof(short),
+ typeof(ushort),
+ typeof(int),
+ typeof(uint),
+ typeof(long),
+ typeof(ulong)
+ };
+
+ ///
+ /// A read-only collection containing all primitive floating-point numeric types
+ ///
+ public static IReadOnlyCollection FloatNumberTypes = new List
+ {
+ typeof(float),
+ typeof(double),
+ typeof(decimal)
+ };
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs
index e8eeb4ee5..dba5e36f8 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs
@@ -34,14 +34,6 @@ namespace Artemis.Core.Models.Profile.Conditions
///
public abstract string Icon { get; }
- ///
- /// Creates a binary expression comparing two types
- ///
- /// The type of parameter passed to the left side of the expression
- /// The type of parameter passed to the right side of the expression
- ///
- public abstract BinaryExpression CreateExpression(Type leftSideType, Type rightSideType);
-
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
{
if (_registered)
@@ -78,5 +70,13 @@ namespace Artemis.Core.Models.Profile.Conditions
return true;
return CompatibleTypes.Any(t => t.IsCastableFrom(type));
}
+
+ ///
+ /// Creates a binary expression comparing two types
+ ///
+ /// The parameter on the left side of the expression
+ /// The parameter on the right side of the expression
+ ///
+ public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
index 3d54a64fc..b260f3449 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
@@ -1,5 +1,9 @@
using System;
+using System.Linq;
+using System.Linq.Expressions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
+using Artemis.Core.Plugins.Abstract.DataModels;
+using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Models.Profile.Conditions
{
@@ -10,10 +14,88 @@ namespace Artemis.Core.Models.Profile.Conditions
public Guid LeftDataModelGuid { get; set; }
public string LeftPropertyPath { get; set; }
-
+
public Guid RightDataModelGuid { get; set; }
public string RightPropertyPath { get; set; }
+
+ // TODO: Implement type-checking or perhaps convert it here
public object RightStaticValue { get; set; }
+
+ public Expression> DynamicConditionLambda { get; private set; }
+ public Func CompiledDynamicConditionLambda { get; private set; }
+ public Expression> StaticConditionLambda { get; private set; }
+ public Func CompiledStaticConditionLambda { get; private set; }
+
+ public void CreateExpression(IDataModelService dataModelService)
+ {
+ if (PredicateType == PredicateType.Dynamic)
+ CreateDynamicExpression(dataModelService);
+ else
+ CreateStaticExpression(dataModelService);
+ }
+
+ private void CreateDynamicExpression(IDataModelService dataModelService)
+ {
+ if (LeftDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(LeftPropertyPath))
+ return;
+ if (RightDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(RightPropertyPath))
+ return;
+
+ var leftDataModel = dataModelService.GetPluginDataModelByGuid(LeftDataModelGuid);
+ if (leftDataModel == null)
+ return;
+
+ var rightDataModel = dataModelService.GetPluginDataModelByGuid(RightDataModelGuid);
+ if (rightDataModel == null)
+ return;
+
+ var leftSideParameter = Expression.Parameter(typeof(DataModel), "leftDataModel");
+ var leftSideAccessor = LeftPropertyPath.Split('.').Aggregate(
+ Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type
+ Expression.Property
+ );
+ var rightSideParameter = Expression.Parameter(typeof(DataModel), "rightDataModel");
+ var rightSideAccessor = RightPropertyPath.Split('.').Aggregate(
+ Expression.Convert(rightSideParameter, rightDataModel.GetType()), // Cast to the appropriate type
+ Expression.Property
+ );
+
+ // A conversion may be required if the types differ
+ // This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here
+ if (rightSideAccessor.Type != leftSideAccessor.Type)
+ rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
+
+ var dynamicConditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
+
+ DynamicConditionLambda = Expression.Lambda>(dynamicConditionExpression, leftSideParameter, rightSideParameter);
+ CompiledDynamicConditionLambda = DynamicConditionLambda.Compile();
+ }
+
+ private void CreateStaticExpression(IDataModelService dataModelService)
+ {
+ if (LeftDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(LeftPropertyPath))
+ return;
+
+ var leftDataModel = dataModelService.GetPluginDataModelByGuid(LeftDataModelGuid);
+ if (leftDataModel == null)
+ return;
+
+ var leftSideParameter = Expression.Parameter(typeof(DataModel), "leftDataModel");
+ var leftSideAccessor = LeftPropertyPath.Split('.').Aggregate(
+ Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type
+ Expression.Property
+ );
+
+ // If the left side is a value type but the input is empty, this isn't a valid expression
+ if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
+ return;
+
+ var rightSideConstant = Expression.Constant(RightStaticValue);
+ var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
+
+ StaticConditionLambda = Expression.Lambda>(conditionExpression, leftSideParameter);
+ CompiledStaticConditionLambda = StaticConditionLambda.Compile();
+ }
}
public enum PredicateType
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs
index 9f2b3cee9..4868ad996 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Equals";
public override string Icon => "Equal";
- public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
+ public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
- var leftSideParameter = Expression.Parameter(leftSideType, "a");
- var rightSideParameter = Expression.Parameter(rightSideType, "b");
- return Expression.Equal(leftSideParameter, rightSideParameter);
+ return Expression.Equal(leftSide, rightSide);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs
index a8fa3adb4..b8618a2b6 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is greater than";
public override string Icon => "GreaterThan";
- public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
+ public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
- var leftSideParameter = Expression.Parameter(leftSideType, "a");
- var rightSideParameter = Expression.Parameter(rightSideType, "b");
- return Expression.GreaterThan(leftSideParameter, rightSideParameter);
+ return Expression.GreaterThan(leftSide, rightSide);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs
index 9b428c0cf..d4955ee34 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is greater than or equal to";
public override string Icon => "GreaterThanOrEqual";
- public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
+ public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
- var leftSideParameter = Expression.Parameter(leftSideType, "a");
- var rightSideParameter = Expression.Parameter(rightSideType, "b");
- return Expression.GreaterThanOrEqual(leftSideParameter, rightSideParameter);
+ return Expression.GreaterThanOrEqual(leftSide, rightSide);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs
index 2a9ba1310..fc083f400 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is less than";
public override string Icon => "LessThan";
- public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
+ public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
- var leftSideParameter = Expression.Parameter(leftSideType, "a");
- var rightSideParameter = Expression.Parameter(rightSideType, "b");
- return Expression.LessThan(leftSideParameter, rightSideParameter);
+ return Expression.LessThan(leftSide, rightSide);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs
index d01fe5d1b..ebc9dd871 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is less than or equal to";
public override string Icon => "LessThanOrEqual";
- public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
+ public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
- var leftSideParameter = Expression.Parameter(leftSideType, "a");
- var rightSideParameter = Expression.Parameter(rightSideType, "b");
- return Expression.LessThanOrEqual(leftSideParameter, rightSideParameter);
+ return Expression.LessThanOrEqual(leftSide, rightSide);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs
index 00d83e516..f4dc78c71 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Does not equal";
public override string Icon => "NotEqualVariant";
- public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
+ public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
- var leftSideParameter = Expression.Parameter(leftSideType, "a");
- var rightSideParameter = Expression.Parameter(rightSideType, "b");
- return Expression.NotEqual(leftSideParameter, rightSideParameter);
+ return Expression.NotEqual(leftSide, rightSide);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/DataModelService.cs b/src/Artemis.Core/Services/DataModelService.cs
index 548257500..60b8b48c2 100644
--- a/src/Artemis.Core/Services/DataModelService.cs
+++ b/src/Artemis.Core/Services/DataModelService.cs
@@ -93,6 +93,15 @@ namespace Artemis.Core.Services
return null;
}
+ public DataModel GetPluginDataModelByGuid(Guid pluginGuid)
+ {
+ var pluginInfo = _pluginService.GetAllPluginInfo().FirstOrDefault(i => i.Guid == pluginGuid);
+ if (pluginInfo == null || !pluginInfo.Enabled)
+ return null;
+
+ return GetPluginDataModel(pluginInfo.Instance);
+ }
+
public bool GetPluginExtendsDataModel(Plugin plugin)
{
if (plugin is Module module)
diff --git a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs
index 24b6d5ab4..e6190ba40 100644
--- a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs
@@ -31,6 +31,12 @@ namespace Artemis.Core.Services.Interfaces
/// Should be a module with a data model or a data model expansion
DataModel GetPluginDataModel(Plugin plugin);
+ ///
+ /// If found, returns the data model of the provided plugin
+ ///
+ /// Should be a module with a data model or a data model expansion
+ DataModel GetPluginDataModelByGuid(Guid pluginGuid);
+
///
/// Determines whether the given plugin expands the main data model
///
diff --git a/src/Artemis.Storage/Entities/Profile/DisplayConditionPredicateEntity.cs b/src/Artemis.Storage/Entities/Profile/DisplayConditionPredicateEntity.cs
new file mode 100644
index 000000000..eb4e70d0c
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/DisplayConditionPredicateEntity.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Artemis.Storage.Entities.Profile
+{
+ public class DisplayConditionPredicateEntity
+ {
+ public Guid LeftDataModelGuid { get; set; }
+ public string LeftPropertyPath { get; set; }
+
+ public Guid RightDataModelGuid { get; set; }
+ public string RightPropertyPath { get; set; }
+
+ // Stored as a string to be able to control serialization and deserialization ourselves
+ public string RightStaticValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml
index 7e3811d08..a1944d9a1 100644
--- a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml
+++ b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml
@@ -49,6 +49,7 @@
LostFocus="InputLostFocus"
KeyDown="InputKeyDown"
Visibility="Collapsed"
- RequestBringIntoView="Input_OnRequestBringIntoView" />
+ RequestBringIntoView="Input_OnRequestBringIntoView"
+ PreviewTextInput="Input_PreviewTextInput"/>
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs
index 7d2ba3c10..f1bd0b456 100644
--- a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs
+++ b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs
@@ -1,6 +1,8 @@
using System;
using System.ComponentModel;
+using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -81,7 +83,7 @@ namespace Artemis.UI.Shared.Controls
private void InputMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
-
+
_startValue = Value;
((IInputElement) sender).CaptureMouse();
_mouseDragStartPoint = e.GetPosition((IInputElement) sender);
@@ -109,7 +111,7 @@ namespace Artemis.UI.Shared.Controls
return;
e.Handled = true;
-
+
if (!_calledDragStarted)
{
OnDragStarted();
@@ -162,6 +164,13 @@ namespace Artemis.UI.Shared.Controls
e.Handled = true;
}
+ private void Input_PreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ var seperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
+ var regex = new Regex("^[" + seperator + "][0-9]+$|^[0-9]*[" + seperator + "]{0,1}[0-9]*$");
+ e.Handled = !regex.IsMatch(e.Text);
+ }
+
private static decimal UltimateRoundingFunction(decimal amountToRound, decimal nearstOf, decimal fairness)
{
return Math.Floor(amountToRound / nearstOf + fairness) * nearstOf;
diff --git a/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs
index fd4fe4b38..24931edc3 100644
--- a/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs
+++ b/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs
@@ -46,24 +46,6 @@ namespace Artemis.UI.Shared.DataModelVisualization
OnCancel();
UpdateCallback(InputValue, false);
}
-
- ///
- /// May be called to convert an object to one of the types defined in CompatibleConversionTypes
- ///
- /// The object to convert, will always be of a type contained in CompatibleConversionTypes
- /// The converted value
- protected virtual T ConvertToSupportedType(object source)
- {
- return default;
- }
-
- internal override object InternalConvertToSupportedType(object source)
- {
- if (CompatibleConversionTypes != null && CompatibleConversionTypes.Contains(source.GetType()))
- return ConvertToSupportedType(source);
-
- throw new ArtemisSharedUIException($"Cannot convert source of type {source.GetType().Name} because the data model input view model does not support it.");
- }
}
///
@@ -80,7 +62,7 @@ namespace Artemis.UI.Shared.DataModelVisualization
internal Action