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

Display conditions - Updated predicate VM to move logic to the model

This commit is contained in:
Robert 2020-07-10 16:48:58 +02:00
parent 4dfc61ab7d
commit 16c2b7f7fd
12 changed files with 270 additions and 172 deletions

View File

@ -35,7 +35,7 @@ namespace Artemis.Core.Models.Profile.Conditions
public override void ApplyToEntity() public override void ApplyToEntity()
{ {
DisplayConditionGroupEntity.Id = EntityId; DisplayConditionGroupEntity.Id = EntityId;
DisplayConditionGroupEntity.ParentId = Parent?.EntityId ?? new Guid(); DisplayConditionGroupEntity.ParentId = Parent?.EntityId ?? Guid.Empty;
DisplayConditionGroupEntity.BooleanOperator = (int) BooleanOperator; DisplayConditionGroupEntity.BooleanOperator = (int) BooleanOperator;
foreach (var child in Children) foreach (var child in Children)

View File

@ -6,6 +6,10 @@ namespace Artemis.Core.Models.Profile.Conditions
{ {
public ListOperator ListOperator { get; set; } public ListOperator ListOperator { get; set; }
public override void ApplyToEntity()
{
}
} }
public enum ListOperator public enum ListOperator

View File

@ -11,9 +11,10 @@ namespace Artemis.Core.Models.Profile.Conditions
{ {
public class DisplayConditionPredicate : DisplayConditionPart public class DisplayConditionPredicate : DisplayConditionPart
{ {
public DisplayConditionPredicate(DisplayConditionPart parent) public DisplayConditionPredicate(DisplayConditionPart parent, PredicateType predicateType)
{ {
Parent = parent; Parent = parent;
PredicateType = predicateType;
DisplayConditionPredicateEntity = new DisplayConditionPredicateEntity(); DisplayConditionPredicateEntity = new DisplayConditionPredicateEntity();
} }
@ -30,7 +31,7 @@ namespace Artemis.Core.Models.Profile.Conditions
public DisplayConditionPredicateEntity DisplayConditionPredicateEntity { get; set; } public DisplayConditionPredicateEntity DisplayConditionPredicateEntity { get; set; }
public PredicateType PredicateType { get; set; } public PredicateType PredicateType { get; set; }
public DisplayConditionOperator Operator { get; set; } public DisplayConditionOperator Operator { get; private set; }
public DataModel LeftDataModel { get; private set; } public DataModel LeftDataModel { get; private set; }
public string LeftPropertyPath { get; private set; } public string LeftPropertyPath { get; private set; }
@ -45,20 +46,37 @@ namespace Artemis.Core.Models.Profile.Conditions
public void UpdateLeftSide(DataModel dataModel, string path) public void UpdateLeftSide(DataModel dataModel, string path)
{ {
if (!dataModel.ContainsPath(path)) if (dataModel != null && path == null)
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'"); throw new ArtemisCoreException("If a data model is provided, a path is also required");
if (dataModel == null && path != null)
throw new ArtemisCoreException("If path is provided, a data model is also required");
if (dataModel != null)
{
if (!dataModel.ContainsPath(path))
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
}
LeftDataModel = dataModel; LeftDataModel = dataModel;
LeftPropertyPath = path; LeftPropertyPath = path;
ValidateOperator();
ValidateRightSide(); ValidateRightSide();
CreateExpression(); CreateExpression();
} }
public void UpdateRightSide(DataModel dataModel, string path) public void UpdateRightSide(DataModel dataModel, string path)
{ {
if (!dataModel.ContainsPath(path)) if (dataModel != null && path == null)
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'"); throw new ArtemisCoreException("If a data model is provided, a path is also required");
if (dataModel == null && path != null)
throw new ArtemisCoreException("If path is provided, a data model is also required");
if (dataModel != null)
{
if (!dataModel.ContainsPath(path))
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
}
PredicateType = PredicateType.Dynamic; PredicateType = PredicateType.Dynamic;
RightDataModel = dataModel; RightDataModel = dataModel;
@ -77,6 +95,25 @@ namespace Artemis.Core.Models.Profile.Conditions
CreateExpression(); CreateExpression();
} }
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
{
if (displayConditionOperator == null)
{
Operator = null;
return;
}
if (LeftDataModel == null)
{
Operator = displayConditionOperator;
return;
}
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
if (displayConditionOperator.SupportsType(leftType))
Operator = displayConditionOperator;
}
public void CreateExpression() public void CreateExpression()
{ {
if (PredicateType == PredicateType.Dynamic) if (PredicateType == PredicateType.Dynamic)
@ -89,6 +126,17 @@ namespace Artemis.Core.Models.Profile.Conditions
{ {
} }
private void ValidateOperator()
{
if (LeftDataModel == null)
return;
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
if (!Operator.SupportsType(leftType))
Operator = null;
}
/// <summary> /// <summary>
/// Validates the right side, ensuring it is still compatible with the current left side /// Validates the right side, ensuring it is still compatible with the current left side
/// </summary> /// </summary>
@ -97,14 +145,19 @@ namespace Artemis.Core.Models.Profile.Conditions
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath); var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
if (PredicateType == PredicateType.Dynamic) if (PredicateType == PredicateType.Dynamic)
{ {
if (RightDataModel == null)
return;
var rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath); var rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath);
if (!leftSideType.IsCastableFrom(rightSideType)) if (!leftSideType.IsCastableFrom(rightSideType))
UpdateRightSide(null, null); UpdateRightSide(null, null);
} }
else else
{ {
// Just update the value with itself, it'll validate :) if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType()))
UpdateRightSide(RightStaticValue); UpdateRightSide(RightStaticValue);
else
UpdateRightSide(null);
} }
} }

View File

@ -1,18 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core.Plugins.Abstract.DataModels.Attributes; using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
using Artemis.UI.Shared.Exceptions;
using Stylet; using Stylet;
namespace Artemis.UI.Shared.DataModelVisualization namespace Artemis.UI.Shared.DataModelVisualization
{ {
public abstract class DataModelInputViewModel<T> : DataModelInputViewModel public abstract class DataModelInputViewModel<T> : DataModelInputViewModel
{ {
private bool _closed;
private T _inputValue; private T _inputValue;
protected DataModelInputViewModel(DataModelPropertyAttribute description, T initialValue) protected DataModelInputViewModel(DataModelPropertyAttribute description, T initialValue)
@ -33,6 +32,10 @@ namespace Artemis.UI.Shared.DataModelVisualization
/// <inheritdoc /> /// <inheritdoc />
public sealed override void Submit() public sealed override void Submit()
{ {
if (_closed)
return;
_closed = true;
foreach (var sourceUpdatingBinding in BindingOperations.GetSourceUpdatingBindings(View)) foreach (var sourceUpdatingBinding in BindingOperations.GetSourceUpdatingBindings(View))
sourceUpdatingBinding.UpdateSource(); sourceUpdatingBinding.UpdateSource();
@ -43,6 +46,10 @@ namespace Artemis.UI.Shared.DataModelVisualization
/// <inheritdoc /> /// <inheritdoc />
public sealed override void Cancel() public sealed override void Cancel()
{ {
if (_closed)
return;
_closed = true;
OnCancel(); OnCancel();
UpdateCallback(InputValue, false); UpdateCallback(InputValue, false);
} }
@ -62,7 +69,8 @@ namespace Artemis.UI.Shared.DataModelVisualization
internal Action<object, bool> UpdateCallback { get; set; } internal Action<object, bool> UpdateCallback { get; set; }
/// <summary> /// <summary>
/// Gets the types this input view model can support through type conversion. This list is defined when registering the view model. /// Gets the types this input view model can support through type conversion. This list is defined when registering the
/// view model.
/// </summary> /// </summary>
internal IReadOnlyCollection<Type> CompatibleConversionTypes { get; set; } internal IReadOnlyCollection<Type> CompatibleConversionTypes { get; set; }
@ -84,12 +92,14 @@ namespace Artemis.UI.Shared.DataModelVisualization
public UIElement View { get; set; } public UIElement View { get; set; }
/// <summary> /// <summary>
/// Submits the input value and removes this view model /// Submits the input value and removes this view model.
/// <para>This is called automatically when the user presses enter or clicks outside the view</para>
/// </summary> /// </summary>
public abstract void Submit(); public abstract void Submit();
/// <summary> /// <summary>
/// Discards changes to the input value and removes this view model /// Discards changes to the input value and removes this view model.
/// <para>This is called automatically when the user presses escape</para>
/// </summary> /// </summary>
public abstract void Cancel(); public abstract void Cancel();

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Artemis.Core.Extensions; using Artemis.Core.Extensions;
@ -18,10 +17,10 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
{ {
private BindableCollection<DataModelVisualizationViewModel> _children; private BindableCollection<DataModelVisualizationViewModel> _children;
private DataModel _dataModel; private DataModel _dataModel;
private bool _isMatchingFilteredTypes;
private DataModelVisualizationViewModel _parent; private DataModelVisualizationViewModel _parent;
private DataModelPropertyAttribute _propertyDescription; private DataModelPropertyAttribute _propertyDescription;
private PropertyInfo _propertyInfo; private PropertyInfo _propertyInfo;
private bool _isMatchingFilteredTypes;
internal DataModelVisualizationViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) internal DataModelVisualizationViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo)
{ {
@ -75,6 +74,36 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
set => SetAndNotify(ref _isMatchingFilteredTypes, value); set => SetAndNotify(ref _isMatchingFilteredTypes, value);
} }
public string PropertyPath
{
get
{
if (Parent == null)
return PropertyInfo?.Name;
if (PropertyInfo == null)
return Parent.PropertyPath;
var parentPath = Parent.PropertyPath;
return parentPath != null ? $"{parentPath}.{PropertyInfo.Name}" : PropertyInfo.Name;
}
}
public string DisplayPropertyPath
{
get
{
if (Parent == null)
return PropertyDescription?.Name;
if (PropertyDescription == null)
return Parent.DisplayPropertyPath;
var parentPath = Parent.DisplayPropertyPath;
return parentPath != null ? $"{parentPath} {PropertyDescription.Name}" : PropertyDescription.Name;
}
}
public abstract void Update(IDataModelVisualizationService dataModelVisualizationService); public abstract void Update(IDataModelVisualizationService dataModelVisualizationService);
public virtual object GetCurrentValue() public virtual object GetCurrentValue()
@ -84,6 +113,14 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
public void ApplyTypeFilter(bool looseMatch, params Type[] filteredTypes) public void ApplyTypeFilter(bool looseMatch, params Type[] filteredTypes)
{ {
if (filteredTypes != null)
{
if (filteredTypes.All(t => t == null))
filteredTypes = null;
else
filteredTypes = filteredTypes.Where(t => t != null).ToArray();
}
// If the VM has children, its own type is not relevant // If the VM has children, its own type is not relevant
if (Children.Any()) if (Children.Any())
{ {
@ -114,9 +151,18 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
IsMatchingFilteredTypes = filteredTypes.Any(t => t == PropertyInfo.PropertyType); IsMatchingFilteredTypes = filteredTypes.Any(t => t == PropertyInfo.PropertyType);
} }
public DataModelVisualizationViewModel GetChildForCondition(DisplayConditionPredicate predicate) public DataModelVisualizationViewModel GetChildForCondition(DisplayConditionPredicate predicate, DisplayConditionSide side)
{ {
if (side == DisplayConditionSide.Left)
{
if (predicate.LeftDataModel == null || predicate.LeftPropertyPath == null)
return null;
return GetChildByPath(predicate.LeftDataModel.PluginInfo.Guid, predicate.LeftPropertyPath);
}
if (predicate.RightDataModel == null || predicate.RightPropertyPath == null)
return null;
return GetChildByPath(predicate.RightDataModel.PluginInfo.Guid, predicate.RightPropertyPath);
} }
public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath) public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath)
@ -141,18 +187,6 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
} }
} }
public string GetCurrentPath()
{
if (Parent == null)
return PropertyInfo?.Name;
if (PropertyInfo == null)
return Parent.GetCurrentPath();
var parentPath = Parent.GetCurrentPath();
return parentPath != null ? $"{parentPath}.{PropertyInfo.Name}" : PropertyInfo.Name;
}
protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo) protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo)
{ {
// Skip properties decorated with DataModelIgnore // Skip properties decorated with DataModelIgnore
@ -182,8 +216,10 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
PropertyDescription = DataModel?.DataModelDescription; PropertyDescription = DataModel?.DataModelDescription;
// Rely on property info for the description // Rely on property info for the description
else if (PropertyInfo != null) else if (PropertyInfo != null)
{
PropertyDescription = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(PropertyInfo, typeof(DataModelPropertyAttribute)) ?? PropertyDescription = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(PropertyInfo, typeof(DataModelPropertyAttribute)) ??
new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize()}; new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize()};
}
else else
throw new ArtemisSharedUIException("Failed to get property description because plugin info is null but the parent has a datamodel"); throw new ArtemisSharedUIException("Failed to get property description because plugin info is null but the parent has a datamodel");
@ -192,4 +228,10 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
PropertyDescription.Name = PropertyInfo.Name.Humanize(); PropertyDescription.Name = PropertyInfo.Name.Humanize();
} }
} }
public enum DisplayConditionSide
{
Left,
Right
}
} }

View File

@ -8,7 +8,7 @@
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">
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}"> <TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}">
<b:Interaction.Behaviors> <b:Interaction.Behaviors>
<behaviors:PutCursorAtEndTextBoxBehavior/> <behaviors:PutCursorAtEndTextBoxBehavior/>
</b:Interaction.Behaviors> </b:Interaction.Behaviors>

View File

@ -8,7 +8,7 @@
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">
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}"> <TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}">
<b:Interaction.Behaviors> <b:Interaction.Behaviors>
<behaviors:PutCursorAtEndTextBoxBehavior/> <behaviors:PutCursorAtEndTextBoxBehavior/>
</b:Interaction.Behaviors> </b:Interaction.Behaviors>

View File

@ -5,9 +5,10 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behaviors="clr-namespace:Artemis.UI.Shared.Behaviors;assembly=Artemis.UI.Shared" xmlns:behaviors="clr-namespace:Artemis.UI.Shared.Behaviors;assembly=Artemis.UI.Shared"
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">
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}"> <TextBox VerticalAlignment="Center" Text="{Binding InputValue}" LostFocus="{s:Action Submit}">
<b:Interaction.Behaviors> <b:Interaction.Behaviors>
<behaviors:PutCursorAtEndTextBoxBehavior/> <behaviors:PutCursorAtEndTextBoxBehavior/>
</b:Interaction.Behaviors> </b:Interaction.Behaviors>

View File

@ -38,15 +38,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
public void AddCondition(string type) public void AddCondition(string type)
{ {
if (type == "Static") if (type == "Static")
DisplayConditionGroup.AddChild(new DisplayConditionPredicate {PredicateType = PredicateType.Static}); DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Static));
else if (type == "Dynamic") else if (type == "Dynamic")
DisplayConditionGroup.AddChild(new DisplayConditionPredicate {PredicateType = PredicateType.Dynamic}); DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Dynamic));
Update(); Update();
} }
public void AddGroup() public void AddGroup()
{ {
DisplayConditionGroup.AddChild(new DisplayConditionGroup()); DisplayConditionGroup.AddChild(new DisplayConditionGroup(DisplayConditionGroup));
Update(); Update();
} }

View File

@ -48,7 +48,7 @@
Background="#ab47bc" Background="#ab47bc"
BorderBrush="#ab47bc" BorderBrush="#ab47bc"
Style="{StaticResource DisplayConditionButton}" Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding DisplayConditionPredicate.LeftPropertyPath}" ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick"> Click="PropertyButton_OnClick">
<Button.ContextMenu> <Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}"> <ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}">
@ -80,7 +80,7 @@
Style="{StaticResource DisplayConditionButtonLeftClickMenu}" Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B" Background="#7B7B7B"
BorderBrush="#7B7B7B" BorderBrush="#7B7B7B"
Content="{Binding DisplayConditionPredicate.Operator.Description}" Content="{Binding SelectedOperator.Description}"
Click="PropertyButton_OnClick"> Click="PropertyButton_OnClick">
<Button.ContextMenu> <Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}"> <ContextMenu ItemsSource="{Binding Operators}">
@ -109,7 +109,7 @@
Background="{DynamicResource PrimaryHueMidBrush}" Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}" BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Style="{StaticResource DisplayConditionButton}" Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding DisplayConditionPredicate.RightPropertyPath}" ToolTip="{Binding SelectedRightSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick" Click="PropertyButton_OnClick"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"> Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu> <Button.ContextMenu>
@ -147,12 +147,12 @@
Command="{s:Action ActivateRightSideInputViewModel}" Command="{s:Action ActivateRightSideInputViewModel}"
HorizontalAlignment="Left"> HorizontalAlignment="Left">
<Grid> <Grid>
<StackPanel Visibility="{Binding DisplayConditionPredicate.RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal"> <StackPanel Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
<TextBlock Margin="0 0 3 0" <TextBlock Margin="0 0 3 0"
FontWeight="Light" FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}" Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}"/> Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}"/>
<TextBlock Text="{Binding DisplayConditionPredicate.RightStaticValue}"/> <TextBlock Text="{Binding RightStaticValue}"/>
<TextBlock Margin="3 0 0 0" <TextBlock Margin="3 0 0 0"
FontWeight="Light" FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Affix}" Text="{Binding SelectedLeftSideProperty.PropertyDescription.Affix}"
@ -161,7 +161,7 @@
<TextBlock Text="« Enter a value »" <TextBlock Text="« Enter a value »"
FontStyle="Italic" FontStyle="Italic"
Visibility="{Binding DisplayConditionPredicate.RightStaticValue, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" /> Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid> </Grid>
</Button> </Button>
<Border BorderBrush="{DynamicResource PrimaryHueMidBrush}" <Border BorderBrush="{DynamicResource PrimaryHueMidBrush}"

View File

@ -29,9 +29,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private DataModelInputViewModel _rightSideInputViewModel; private DataModelInputViewModel _rightSideInputViewModel;
private int _rightSideTransitionIndex; private int _rightSideTransitionIndex;
private DataModelVisualizationViewModel _selectedLeftSideProperty; private DataModelVisualizationViewModel _selectedLeftSideProperty;
private DisplayConditionOperator _selectedOperator;
private DataModelVisualizationViewModel _selectedRightSideProperty; private DataModelVisualizationViewModel _selectedRightSideProperty;
private List<Type> _supportedInputTypes; private List<Type> _supportedInputTypes;
private object _rightStaticValue;
public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, IProfileEditorService profileEditorService, public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, IProfileEditorService profileEditorService,
IDataModelVisualizationService dataModelVisualizationService, IDataModelService dataModelService, IEventAggregator eventAggregator) IDataModelVisualizationService dataModelVisualizationService, IDataModelService dataModelService, IEventAggregator eventAggregator)
@ -46,7 +48,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty); SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty);
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Initialize(); // Initialize async, no need to wait for it
Task.Run(Initialize);
} }
public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model; public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model;
@ -83,6 +86,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _selectedRightSideProperty, value); set => SetAndNotify(ref _selectedRightSideProperty, value);
} }
public object RightStaticValue
{
get => _rightStaticValue;
set => SetAndNotify(ref _rightStaticValue, value);
}
public int RightSideTransitionIndex public int RightSideTransitionIndex
{ {
get => _rightSideTransitionIndex; get => _rightSideTransitionIndex;
@ -101,121 +110,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _operators, value); set => SetAndNotify(ref _operators, value);
} }
public void Initialize() public DisplayConditionOperator SelectedOperator
{ {
Task.Run(() => get => _selectedOperator;
{ set => SetAndNotify(ref _selectedOperator, value);
// Get the data models
LeftSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
RightSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
{
LeftSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
RightSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
}
// Determine which types are currently supported
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();
});
} }
public override void Update() public DelegateCommand SelectLeftPropertyCommand { get; }
{ public DelegateCommand SelectRightPropertyCommand { get; }
if (!IsInitialized) public DelegateCommand SelectOperatorCommand { get; }
return;
// If static, only allow selecting properties also supported by input
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
// Determine the left side property first
SelectedLeftSideProperty = DisplayConditionPredicate.LeftPropertyPath != null
? LeftSideDataModel.GetChildByPath(DisplayConditionPredicate.LeftDataModelGuid, DisplayConditionPredicate.LeftPropertyPath)
: null;
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// 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)
return;
RightSideTransitionIndex = 1;
RightSideInputViewModel = _dataModelVisualizationService.GetDataModelInputViewModel(
SelectedLeftSideProperty.PropertyInfo.PropertyType,
SelectedLeftSideProperty.PropertyDescription,
DisplayConditionPredicate.RightStaticValue,
UpdateInputValue
);
_eventAggregator.Subscribe(this);
}
public void UpdateInputValue(object value, bool isSubmitted)
{
if (isSubmitted)
{
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
if (value != null && value.GetType() != leftSideType)
DisplayConditionPredicate.RightStaticValue = Convert.ChangeType(value, leftSideType);
else
DisplayConditionPredicate.RightStaticValue = value;
Update();
}
RightSideTransitionIndex = 0;
RightSideInputViewModel = null;
_eventAggregator.Unsubscribe(this);
}
public void Handle(MainWindowKeyEvent message) public void Handle(MainWindowKeyEvent message)
{ {
@ -237,32 +140,119 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
RightSideInputViewModel.Submit(); RightSideInputViewModel.Submit();
} }
#endregion public void Initialize()
{
// Get the data models
LeftSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
RightSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
{
LeftSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
RightSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
}
#region Commands // Determine which types are currently supported
var editors = _dataModelVisualizationService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
public DelegateCommand SelectLeftPropertyCommand { get; } IsInitialized = true;
public DelegateCommand SelectRightPropertyCommand { get; } Update();
public DelegateCommand SelectOperatorCommand { get; } }
public override void Update()
{
if (!IsInitialized)
return;
// If static, only allow selecting properties also supported by input
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
// Determine the left side property first
SelectedLeftSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Left);
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators = _dataModelService.GetCompatibleConditionOperators(leftSideType);
if (DisplayConditionPredicate.Operator == null)
DisplayConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DisplayConditionPredicate.Operator;
// Determine the right side
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
{
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
else
RightStaticValue = DisplayConditionPredicate.RightStaticValue;
}
public void ApplyLeftSide()
{
DisplayConditionPredicate.UpdateLeftSide(SelectedLeftSideProperty.DataModel, SelectedLeftSideProperty.PropertyPath);
SelectedOperator = DisplayConditionPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DisplayConditionPredicate.UpdateRightSide(SelectedRightSideProperty.DataModel, SelectedRightSideProperty.PropertyPath);
Update();
}
public void ApplyRightSideStatic(object value, bool isSubmitted)
{
if (isSubmitted)
{
DisplayConditionPredicate.UpdateRightSide(value);
Update();
}
RightSideTransitionIndex = 0;
RightSideInputViewModel = null;
RightStaticValue = value;
_eventAggregator.Unsubscribe(this);
}
public void ApplyOperator()
{
DisplayConditionPredicate.UpdateOperator(SelectedOperator);
Update();
}
public void ActivateRightSideInputViewModel()
{
if (SelectedLeftSideProperty?.PropertyInfo == null)
return;
RightSideTransitionIndex = 1;
RightSideInputViewModel = _dataModelVisualizationService.GetDataModelInputViewModel(
SelectedLeftSideProperty.PropertyInfo.PropertyType,
SelectedLeftSideProperty.PropertyDescription,
DisplayConditionPredicate.RightStaticValue,
ApplyRightSideStatic
);
_eventAggregator.Subscribe(this);
}
private void ExecuteSelectLeftProperty(object context) private void ExecuteSelectLeftProperty(object context)
{ {
if (!(context is DataModelVisualizationViewModel vm)) if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return; return;
DisplayConditionPredicate.LeftPropertyPath = vm.GetCurrentPath(); SelectedLeftSideProperty = dataModelVisualizationViewModel;
DisplayConditionPredicate.LeftDataModelGuid = vm.DataModel.PluginInfo.Guid; ApplyLeftSide();
Update();
} }
private void ExecuteSelectRightProperty(object context) private void ExecuteSelectRightProperty(object context)
{ {
if (!(context is DataModelVisualizationViewModel vm)) if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return; return;
DisplayConditionPredicate.RightPropertyPath = vm.GetCurrentPath(); SelectedRightSideProperty = dataModelVisualizationViewModel;
DisplayConditionPredicate.RightDataModelGuid = vm.DataModel.PluginInfo.Guid; ApplyRightSideDynamic();
Update();
} }
private void ExecuteSelectOperatorCommand(object context) private void ExecuteSelectOperatorCommand(object context)
@ -270,10 +260,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
if (!(context is DisplayConditionOperator displayConditionOperator)) if (!(context is DisplayConditionOperator displayConditionOperator))
return; return;
DisplayConditionPredicate.Operator = displayConditionOperator; SelectedOperator = displayConditionOperator;
Update(); ApplyOperator();
} }
#endregion
} }
} }

View File

@ -32,7 +32,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
// Ensure the layer has a root display condition group // Ensure the layer has a root display condition group
if (e.RenderProfileElement.DisplayConditionGroup == null) if (e.RenderProfileElement.DisplayConditionGroup == null)
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(); e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null);
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null); RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
RootGroup.IsRootGroup = true; RootGroup.IsRootGroup = true;