mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Display conditions - Implemented expression tree creation
Display conditions - Use the most appropriate number input UI general - Use current localisation for decimal seperators and enfore in inputs
This commit is contained in:
parent
92dfbb354a
commit
4d616beffb
@ -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"};
|
||||
|
||||
/// <summary>
|
||||
/// A read-only collection containing all primitive number types
|
||||
/// A read-only collection containing all primitive numeric types
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Type> NumberTypes = new List<Type>
|
||||
{
|
||||
@ -44,5 +44,30 @@ namespace Artemis.Core
|
||||
typeof(double),
|
||||
typeof(decimal)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A read-only collection containing all primitive integral numeric types
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Type> IntegralNumberTypes = new List<Type>
|
||||
{
|
||||
typeof(sbyte),
|
||||
typeof(byte),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(long),
|
||||
typeof(ulong)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A read-only collection containing all primitive floating-point numeric types
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Type> FloatNumberTypes = new List<Type>
|
||||
{
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(decimal)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -34,14 +34,6 @@ namespace Artemis.Core.Models.Profile.Conditions
|
||||
/// </summary>
|
||||
public abstract string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a binary expression comparing two types
|
||||
/// </summary>
|
||||
/// <param name="leftSideType">The type of parameter passed to the left side of the expression</param>
|
||||
/// <param name="rightSideType">The type of parameter passed to the right side of the expression</param>
|
||||
/// <returns></returns>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a binary expression comparing two types
|
||||
/// </summary>
|
||||
/// <param name="leftSide">The parameter on the left side of the expression</param>
|
||||
/// <param name="rightSide">The parameter on the right side of the expression</param>
|
||||
/// <returns></returns>
|
||||
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
|
||||
}
|
||||
}
|
||||
@ -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<Func<DataModel, DataModel, bool>> DynamicConditionLambda { get; private set; }
|
||||
public Func<DataModel, DataModel, bool> CompiledDynamicConditionLambda { get; private set; }
|
||||
public Expression<Func<DataModel, bool>> StaticConditionLambda { get; private set; }
|
||||
public Func<DataModel, bool> 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<string, Expression>(
|
||||
Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type
|
||||
Expression.Property
|
||||
);
|
||||
var rightSideParameter = Expression.Parameter(typeof(DataModel), "rightDataModel");
|
||||
var rightSideAccessor = RightPropertyPath.Split('.').Aggregate<string, Expression>(
|
||||
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<Func<DataModel, DataModel, bool>>(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<string, Expression>(
|
||||
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<Func<DataModel, bool>>(conditionExpression, leftSideParameter);
|
||||
CompiledStaticConditionLambda = StaticConditionLambda.Compile();
|
||||
}
|
||||
}
|
||||
|
||||
public enum PredicateType
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -31,6 +31,12 @@ namespace Artemis.Core.Services.Interfaces
|
||||
/// <param name="plugin">Should be a module with a data model or a data model expansion</param>
|
||||
DataModel GetPluginDataModel(Plugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// If found, returns the data model of the provided plugin
|
||||
/// </summary>
|
||||
/// <param name="pluginGuid">Should be a module with a data model or a data model expansion</param>
|
||||
DataModel GetPluginDataModelByGuid(Guid pluginGuid);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given plugin expands the main data model
|
||||
/// </summary>
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,7 @@
|
||||
LostFocus="InputLostFocus"
|
||||
KeyDown="InputKeyDown"
|
||||
Visibility="Collapsed"
|
||||
RequestBringIntoView="Input_OnRequestBringIntoView" />
|
||||
RequestBringIntoView="Input_OnRequestBringIntoView"
|
||||
PreviewTextInput="Input_PreviewTextInput"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -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;
|
||||
|
||||
@ -46,24 +46,6 @@ namespace Artemis.UI.Shared.DataModelVisualization
|
||||
OnCancel();
|
||||
UpdateCallback(InputValue, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// May be called to convert an object to one of the types defined in CompatibleConversionTypes
|
||||
/// </summary>
|
||||
/// <param name="source">The object to convert, will always be of a type contained in CompatibleConversionTypes</param>
|
||||
/// <returns>The converted value</returns>
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -80,7 +62,7 @@ namespace Artemis.UI.Shared.DataModelVisualization
|
||||
internal Action<object, bool> UpdateCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types this input view model can support through type conversion
|
||||
/// Gets the types this input view model can support through type conversion. This list is defined when registering the view model.
|
||||
/// </summary>
|
||||
internal IReadOnlyCollection<Type> CompatibleConversionTypes { get; set; }
|
||||
|
||||
@ -124,7 +106,5 @@ namespace Artemis.UI.Shared.DataModelVisualization
|
||||
protected virtual void OnCancel()
|
||||
{
|
||||
}
|
||||
|
||||
internal abstract object InternalConvertToSupportedType(object source);
|
||||
}
|
||||
}
|
||||
@ -64,8 +64,10 @@ namespace Artemis.UI.Shared.Services
|
||||
return _dataModelService.GetPluginExtendsDataModel(plugin);
|
||||
}
|
||||
|
||||
public DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes) where T : DataModelInputViewModel
|
||||
public DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes = null) where T : DataModelInputViewModel
|
||||
{
|
||||
if (compatibleConversionTypes == null)
|
||||
compatibleConversionTypes = new List<Type>();
|
||||
var viewModelType = typeof(T);
|
||||
lock (_registeredDataModelEditors)
|
||||
{
|
||||
@ -158,7 +160,12 @@ namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
lock (_registeredDataModelEditors)
|
||||
{
|
||||
// Prefer a VM that natively supports the type
|
||||
var match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == propertyType);
|
||||
// Fall back on a VM that supports the type through conversion
|
||||
if (match == null)
|
||||
match = _registeredDataModelEditors.FirstOrDefault(d => d.CompatibleConversionTypes.Contains(propertyType));
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
var viewModel = InstantiateDataModelInputViewModel(match, description, initialValue);
|
||||
@ -173,8 +180,14 @@ namespace Artemis.UI.Shared.Services
|
||||
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute description, object initialValue)
|
||||
{
|
||||
// The view models expecting value types shouldn't be given null, avoid that
|
||||
if (initialValue == null && registration.SupportedType.IsValueType)
|
||||
initialValue = Activator.CreateInstance(registration.SupportedType);
|
||||
if (registration.SupportedType.IsValueType)
|
||||
{
|
||||
if (initialValue == null)
|
||||
initialValue = Activator.CreateInstance(registration.SupportedType);
|
||||
}
|
||||
// This assumes the type can be converted, that has been checked when the VM was created
|
||||
if (initialValue != null && initialValue.GetType() != registration.SupportedType)
|
||||
initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
|
||||
|
||||
var parameters = new IParameter[]
|
||||
{
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Threading;
|
||||
using Artemis.Core.Models.Profile.Conditions;
|
||||
using Artemis.Core.Ninject;
|
||||
@ -34,8 +37,6 @@ namespace Artemis.UI
|
||||
|
||||
protected override void Launch()
|
||||
{
|
||||
var test = new DisplayCondition();
|
||||
|
||||
StartupArguments = Args.ToList();
|
||||
|
||||
var logger = Kernel.Get<ILogger>();
|
||||
@ -52,6 +53,9 @@ namespace Artemis.UI
|
||||
throw;
|
||||
}
|
||||
|
||||
// Ensure WPF uses the right culture in binding converters
|
||||
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
|
||||
|
||||
// Create and bind the root view, this is a tray icon so don't show it with the window manager
|
||||
Execute.OnUIThread(() => viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel));
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
using FluentValidation.TestHelper;
|
||||
|
||||
namespace Artemis.UI.DataModelVisualization.Input
|
||||
{
|
||||
@ -14,7 +14,8 @@ namespace Artemis.UI.DataModelVisualization.Input
|
||||
|
||||
public void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
var regex = new Regex("^[.|,][0-9]+$|^[0-9]*[.|,]{0,1}[0-9]*$");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
@ -17,10 +16,5 @@ namespace Artemis.UI.DataModelVisualization.Input
|
||||
var regex = new Regex("[^0-9]+");
|
||||
e.Handled = regex.IsMatch(e.Text);
|
||||
}
|
||||
|
||||
protected override int ConvertToSupportedType(object source)
|
||||
{
|
||||
return Convert.ToInt32(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,12 +33,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
|
||||
private List<Type> _supportedInputTypes;
|
||||
|
||||
public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate,
|
||||
DisplayConditionViewModel parent,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDataModelVisualizationService dataModelVisualizationService,
|
||||
IDataModelService dataModelService,
|
||||
IEventAggregator eventAggregator)
|
||||
public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, IProfileEditorService profileEditorService,
|
||||
IDataModelVisualizationService dataModelVisualizationService, IDataModelService dataModelService, IEventAggregator eventAggregator)
|
||||
: base(displayConditionPredicate, parent)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
@ -54,9 +50,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
}
|
||||
|
||||
public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model;
|
||||
public DelegateCommand SelectLeftPropertyCommand { get; }
|
||||
public DelegateCommand SelectRightPropertyCommand { get; }
|
||||
public DelegateCommand SelectOperatorCommand { get; }
|
||||
public bool ShowRightSidePropertySelection => DisplayConditionPredicate.PredicateType == PredicateType.Dynamic;
|
||||
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
|
||||
|
||||
@ -108,26 +101,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
set => SetAndNotify(ref _operators, value);
|
||||
}
|
||||
|
||||
public void Handle(MainWindowKeyEvent message)
|
||||
{
|
||||
if (RightSideInputViewModel == null)
|
||||
return;
|
||||
|
||||
if (!message.KeyDown && message.EventArgs.Key == Key.Escape)
|
||||
RightSideInputViewModel.Cancel();
|
||||
if (!message.KeyDown && message.EventArgs.Key == Key.Enter)
|
||||
RightSideInputViewModel.Submit();
|
||||
}
|
||||
|
||||
public void Handle(MainWindowMouseEvent message)
|
||||
{
|
||||
if (RightSideInputViewModel == null)
|
||||
return;
|
||||
|
||||
if (message.Sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(RightSideInputViewModel.View))
|
||||
RightSideInputViewModel.Submit();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Task.Run(() =>
|
||||
@ -142,7 +115,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
}
|
||||
|
||||
// Determine which types are currently supported
|
||||
_supportedInputTypes = _dataModelVisualizationService.RegisteredDataModelEditors.Select(e => e.SupportedType).ToList();
|
||||
var editors = _dataModelVisualizationService.RegisteredDataModelEditors;
|
||||
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
|
||||
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
|
||||
|
||||
IsInitialized = true;
|
||||
Update();
|
||||
@ -164,38 +139,51 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
: null;
|
||||
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
|
||||
|
||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
|
||||
{
|
||||
// Right side may only select properties matching the left side
|
||||
if (SelectedLeftSideProperty != null)
|
||||
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
|
||||
else
|
||||
RightSideDataModel.ApplyTypeFilter(true);
|
||||
|
||||
// Determine the right side property
|
||||
if (DisplayConditionPredicate.RightPropertyPath != null)
|
||||
{
|
||||
// Ensure the right side property still matches the left side type, else set it to null
|
||||
var selectedProperty = RightSideDataModel.GetChildByPath(DisplayConditionPredicate.RightDataModelGuid, DisplayConditionPredicate.RightPropertyPath);
|
||||
SelectedRightSideProperty = selectedProperty.IsMatchingFilteredTypes ? selectedProperty : null;
|
||||
}
|
||||
else
|
||||
SelectedRightSideProperty = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DisplayConditionPredicate.RightStaticValue != null && DisplayConditionPredicate.RightStaticValue.GetType() != leftSideType)
|
||||
DisplayConditionPredicate.RightStaticValue = null;
|
||||
}
|
||||
|
||||
// Get the supported operators
|
||||
Operators = _dataModelService.GetCompatibleConditionOperators(leftSideType);
|
||||
if (DisplayConditionPredicate.Operator == null || !DisplayConditionPredicate.Operator.SupportsType(leftSideType))
|
||||
DisplayConditionPredicate.Operator = Operators.FirstOrDefault(o => o.SupportsType(leftSideType));
|
||||
|
||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
|
||||
UpdateRightSideDynamic(leftSideType);
|
||||
else
|
||||
UpdateRightSideStatic(leftSideType);
|
||||
|
||||
DisplayConditionPredicate.CreateExpression(_dataModelService);
|
||||
NotifyOfPropertyChange(nameof(DisplayConditionPredicate));
|
||||
}
|
||||
|
||||
#region Dynamic input
|
||||
|
||||
private void UpdateRightSideDynamic(Type leftSideType)
|
||||
{
|
||||
// Right side may only select properties matching the left side
|
||||
if (SelectedLeftSideProperty != null)
|
||||
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
|
||||
else
|
||||
RightSideDataModel.ApplyTypeFilter(true);
|
||||
|
||||
// Determine the right side property
|
||||
if (DisplayConditionPredicate.RightPropertyPath != null)
|
||||
{
|
||||
// Ensure the right side property still matches the left side type, else set it to null
|
||||
var selectedProperty = RightSideDataModel.GetChildByPath(DisplayConditionPredicate.RightDataModelGuid, DisplayConditionPredicate.RightPropertyPath);
|
||||
SelectedRightSideProperty = selectedProperty.IsMatchingFilteredTypes ? selectedProperty : null;
|
||||
}
|
||||
else
|
||||
SelectedRightSideProperty = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static input
|
||||
|
||||
private void UpdateRightSideStatic(Type leftSideType)
|
||||
{
|
||||
if (DisplayConditionPredicate.RightStaticValue != null && DisplayConditionPredicate.RightStaticValue.GetType() != leftSideType)
|
||||
DisplayConditionPredicate.RightStaticValue = null;
|
||||
}
|
||||
|
||||
public void ActivateRightSideInputViewModel()
|
||||
{
|
||||
if (SelectedLeftSideProperty?.PropertyInfo == null)
|
||||
@ -215,7 +203,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
{
|
||||
if (isSubmitted)
|
||||
{
|
||||
DisplayConditionPredicate.RightStaticValue = value;
|
||||
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
|
||||
|
||||
if (value != null && value.GetType() != leftSideType)
|
||||
DisplayConditionPredicate.RightStaticValue = Convert.ChangeType(value, leftSideType);
|
||||
else
|
||||
DisplayConditionPredicate.RightStaticValue = value;
|
||||
Update();
|
||||
}
|
||||
|
||||
@ -224,6 +217,34 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
_eventAggregator.Unsubscribe(this);
|
||||
}
|
||||
|
||||
public void Handle(MainWindowKeyEvent message)
|
||||
{
|
||||
if (RightSideInputViewModel == null)
|
||||
return;
|
||||
|
||||
if (!message.KeyDown && message.EventArgs.Key == Key.Escape)
|
||||
RightSideInputViewModel.Cancel();
|
||||
if (!message.KeyDown && message.EventArgs.Key == Key.Enter)
|
||||
RightSideInputViewModel.Submit();
|
||||
}
|
||||
|
||||
public void Handle(MainWindowMouseEvent message)
|
||||
{
|
||||
if (RightSideInputViewModel == null)
|
||||
return;
|
||||
|
||||
if (message.Sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(RightSideInputViewModel.View))
|
||||
RightSideInputViewModel.Submit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
public DelegateCommand SelectLeftPropertyCommand { get; }
|
||||
public DelegateCommand SelectRightPropertyCommand { get; }
|
||||
public DelegateCommand SelectOperatorCommand { get; }
|
||||
|
||||
private void ExecuteSelectLeftProperty(object context)
|
||||
{
|
||||
if (!(context is DataModelVisualizationViewModel vm))
|
||||
@ -252,5 +273,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
DisplayConditionPredicate.Operator = displayConditionOperator;
|
||||
Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -37,9 +37,9 @@ namespace Artemis.UI.Services
|
||||
if (_registeredBuiltInDataModelInputs)
|
||||
return;
|
||||
|
||||
_dataModelVisualizationService.RegisterDataModelInput<StringDataModelInputViewModel>(Constants.CorePluginInfo, Constants.NumberTypes);
|
||||
_dataModelVisualizationService.RegisterDataModelInput<IntDataModelInputViewModel>(Constants.CorePluginInfo, Constants.NumberTypes);
|
||||
_dataModelVisualizationService.RegisterDataModelInput<DoubleDataModelInputViewModel>(Constants.CorePluginInfo, Constants.NumberTypes);
|
||||
_dataModelVisualizationService.RegisterDataModelInput<StringDataModelInputViewModel>(Constants.CorePluginInfo, null);
|
||||
_dataModelVisualizationService.RegisterDataModelInput<IntDataModelInputViewModel>(Constants.CorePluginInfo, Constants.IntegralNumberTypes);
|
||||
_dataModelVisualizationService.RegisterDataModelInput<DoubleDataModelInputViewModel>(Constants.CorePluginInfo, Constants.FloatNumberTypes);
|
||||
|
||||
_registeredBuiltInDataModelInputs = true;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user