diff --git a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPart.cs b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPart.cs index f9954b483..50ddcd6dc 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPart.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPart.cs @@ -62,7 +62,7 @@ namespace Artemis.Core internal abstract bool EvaluateObject(object target); internal abstract void Save(); - internal abstract DisplayConditionPartEntity GetEntity(); + internal abstract DataModelConditionPartEntity GetEntity(); #region IDisposable diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionGroup.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionGroup.cs index 9a2f3c6ef..a3d91df6d 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionGroup.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionGroup.cs @@ -20,7 +20,7 @@ namespace Artemis.Core public DataModelConditionGroup(DataModelConditionPart parent) { Parent = parent; - Entity = new DisplayConditionGroupEntity(); + Entity = new DataModelConditionGroupEntity(); } /// @@ -28,7 +28,7 @@ namespace Artemis.Core /// /// /// - public DataModelConditionGroup(DataModelConditionPart parent, DisplayConditionGroupEntity entity) + public DataModelConditionGroup(DataModelConditionPart parent, DataModelConditionGroupEntity entity) { Parent = parent; Entity = entity; @@ -36,13 +36,13 @@ namespace Artemis.Core foreach (var childEntity in Entity.Children) { - if (childEntity is DisplayConditionGroupEntity groupEntity) + if (childEntity is DataModelConditionGroupEntity groupEntity) AddChild(new DataModelConditionGroup(this, groupEntity)); - else if (childEntity is DisplayConditionListEntity listEntity) + else if (childEntity is DataModelConditionListEntity listEntity) AddChild(new DataModelConditionList(this, listEntity)); - else if (childEntity is DisplayConditionPredicateEntity predicateEntity) + else if (childEntity is DataModelConditionPredicateEntity predicateEntity) AddChild(new DataModelConditionPredicate(this, predicateEntity)); - else if (childEntity is DisplayConditionListPredicateEntity listPredicateEntity) + else if (childEntity is DataModelConditionListPredicateEntity listPredicateEntity) AddChild(new DataModelConditionListPredicate(this, listPredicateEntity)); } } @@ -52,13 +52,13 @@ namespace Artemis.Core /// public BooleanOperator BooleanOperator { get; set; } - internal DisplayConditionGroupEntity Entity { get; set; } + internal DataModelConditionGroupEntity Entity { get; set; } /// public override bool Evaluate() { if (_disposed) - throw new ObjectDisposedException("DisplayConditionGroup"); + throw new ObjectDisposedException("DataModelConditionGroup"); // Empty groups are always true if (Children.Count == 0) @@ -100,7 +100,7 @@ namespace Artemis.Core internal override bool EvaluateObject(object target) { if (_disposed) - throw new ObjectDisposedException("DisplayConditionGroup"); + throw new ObjectDisposedException("DataModelConditionGroup"); // Empty groups are always true if (Children.Count == 0) @@ -129,7 +129,7 @@ namespace Artemis.Core child.Save(); } - internal override DisplayConditionPartEntity GetEntity() + internal override DataModelConditionPartEntity GetEntity() { return Entity; } diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs index eda2ba6ee..e650a9b85 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs @@ -22,12 +22,12 @@ namespace Artemis.Core public DataModelConditionList(DataModelConditionPart parent) { Parent = parent; - Entity = new DisplayConditionListEntity(); + Entity = new DataModelConditionListEntity(); Initialize(); } - internal DataModelConditionList(DataModelConditionPart parent, DisplayConditionListEntity entity) + internal DataModelConditionList(DataModelConditionPart parent, DataModelConditionListEntity entity) { Parent = parent; Entity = entity; @@ -36,7 +36,7 @@ namespace Artemis.Core Initialize(); } - internal DisplayConditionListEntity Entity { get; set; } + internal DataModelConditionListEntity Entity { get; set; } /// /// Gets or sets the list operator @@ -62,7 +62,7 @@ namespace Artemis.Core public override bool Evaluate() { if (_disposed) - throw new ObjectDisposedException("DisplayConditionList"); + throw new ObjectDisposedException("DataModelConditionList"); if (CompiledListAccessor == null) return false; @@ -78,7 +78,7 @@ namespace Artemis.Core public void UpdateList(DataModel dataModel, string path) { if (_disposed) - throw new ObjectDisposedException("DisplayConditionList"); + throw new ObjectDisposedException("DataModelConditionList"); if (dataModel != null && path == null) throw new ArtemisCoreException("If a data model is provided, a path is also required"); @@ -129,7 +129,7 @@ namespace Artemis.Core internal override bool EvaluateObject(object target) { if (_disposed) - throw new ObjectDisposedException("DisplayConditionList"); + throw new ObjectDisposedException("DataModelConditionList"); if (!Children.Any()) return false; @@ -166,7 +166,7 @@ namespace Artemis.Core child.Save(); } - internal override DisplayConditionPartEntity GetEntity() + internal override DataModelConditionPartEntity GetEntity() { return Entity; } @@ -189,7 +189,7 @@ namespace Artemis.Core CreateExpression(); // There should only be one child and it should be a group - if (Entity.Children.SingleOrDefault() is DisplayConditionGroupEntity rootGroup) + if (Entity.Children.SingleOrDefault() is DataModelConditionGroupEntity rootGroup) AddChild(new DataModelConditionGroup(this, rootGroup)); else { @@ -201,12 +201,14 @@ namespace Artemis.Core private void CreateExpression() { if (_disposed) - throw new ObjectDisposedException("DisplayConditionList"); + throw new ObjectDisposedException("DataModelConditionList"); var parameter = Expression.Parameter(typeof(object), "listDataModel"); var accessor = ListPropertyPath.Split('.').Aggregate( Expression.Convert(parameter, ListDataModel.GetType()), - (expression, s) => Expression.Convert(Expression.Property(expression, s), typeof(IList))); + Expression.Property + ); + accessor = Expression.Convert(accessor, typeof(IList)); var lambda = Expression.Lambda>(accessor, parameter); CompiledListAccessor = lambda.Compile(); diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs index 5b6cd275c..6c95c6538 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs @@ -18,32 +18,32 @@ namespace Artemis.Core /// /// /// - public DataModelConditionListPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType) + public DataModelConditionListPredicate(DataModelConditionPart parent, ListRightSideType predicateType) { Parent = parent; PredicateType = predicateType; - Entity = new DisplayConditionListPredicateEntity(); + Entity = new DataModelConditionListPredicateEntity(); ApplyParentList(); Initialize(); } - internal DataModelConditionListPredicate(DataModelConditionPart parent, DisplayConditionListPredicateEntity entity) + internal DataModelConditionListPredicate(DataModelConditionPart parent, DataModelConditionListPredicateEntity entity) { Parent = parent; Entity = entity; - PredicateType = (ProfileRightSideType) entity.PredicateType; + PredicateType = (ListRightSideType) entity.PredicateType; ApplyParentList(); Initialize(); } - internal DisplayConditionListPredicateEntity Entity { get; set; } + internal DataModelConditionListPredicateEntity Entity { get; set; } /// /// Gets or sets the predicate type /// - public ProfileRightSideType PredicateType { get; set; } + public ListRightSideType PredicateType { get; set; } /// /// Gets the operator @@ -55,6 +55,11 @@ namespace Artemis.Core /// public Type ListType { get; private set; } + /// + /// Gets whether the list contains primitives + /// + public bool IsPrimitiveList { get; private set; } + /// /// Gets the currently used instance of the list data model /// @@ -70,6 +75,12 @@ namespace Artemis.Core /// public string LeftPropertyPath { get; private set; } + /// + /// Gets the currently used instance of the right side data model + /// Note: This is null when using a path inside the list + /// + public DataModel RightDataModel { get; set; } + /// /// Gets the path of the right property in the /// @@ -77,21 +88,28 @@ namespace Artemis.Core /// /// Gets the right static value, only used it is - /// + /// /// public object RightStaticValue { get; private set; } /// - /// Gets the compiled function that evaluates this predicate + /// Gets the compiled expression that evaluates this predicate /// public Func CompiledListPredicate { get; private set; } + /// + /// Gets the compiled expression that evaluates this predicate on an external right-side data model + /// + public Func CompiledExternalListPredicate { get; set; } + /// /// Updates the left side of the predicate /// /// The path pointing to the left side value inside the list public void UpdateLeftSide(string path) { + if (IsPrimitiveList) + throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list"); if (!ListContainsInnerPath(path)) throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}"); @@ -103,7 +121,8 @@ namespace Artemis.Core } /// - /// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression + /// Updates the right side of the predicate using a path to a value inside the list item, makes the predicate dynamic + /// and re-compiles the expression /// /// The path pointing to the right side value inside the list public void UpdateRightSideDynamic(string path) @@ -111,7 +130,33 @@ namespace Artemis.Core if (!ListContainsInnerPath(path)) throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}"); - PredicateType = ProfileRightSideType.Dynamic; + PredicateType = ListRightSideType.Dynamic; + RightPropertyPath = path; + + CreateExpression(); + } + + /// + /// Updates the right side of the predicate using path to a value in a data model, makes the predicate dynamic and + /// re-compiles the expression + /// + /// + /// The path pointing to the right side value inside the list + public void UpdateRightSideDynamic(DataModel dataModel, string path) + { + if (dataModel != null && path == null) + 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 = ListRightSideType.DynamicExternal; + RightDataModel = dataModel; RightPropertyPath = path; CreateExpression(); @@ -123,7 +168,7 @@ namespace Artemis.Core /// The right side value to use public void UpdateRightSideStatic(object staticValue) { - PredicateType = ProfileRightSideType.Static; + PredicateType = ListRightSideType.Static; RightPropertyPath = null; SetStaticValue(staticValue); @@ -163,11 +208,36 @@ namespace Artemis.Core return false; } + /// + /// Determines whether the provided path is contained inside the list + /// + /// The path to evaluate + public bool ListContainsInnerPath(string path) + { + if (ListType == null) + return false; + + var parts = path.Split('.'); + var current = ListType; + foreach (var part in parts) + { + var property = current.GetProperty(part); + current = property?.PropertyType; + + if (property == null) + return false; + } + + return true; + } + #region IDisposable /// protected override void Dispose(bool disposing) { + DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; + DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded; ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved; @@ -205,9 +275,14 @@ namespace Artemis.Core throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a list at path '{path}'"); ListType = listType; + IsPrimitiveList = listType.IsPrimitive || listType.IsEnum || listType == typeof(string); } else + { ListType = null; + IsPrimitiveList = true; + } + ListDataModel = dataModel; ListPropertyPath = path; @@ -220,6 +295,7 @@ namespace Artemis.Core CreateExpression(); } + /// /// Evaluates the condition part on the given target /// @@ -229,26 +305,14 @@ namespace Artemis.Core /// internal override bool EvaluateObject(object target) { - return CompiledListPredicate != null && CompiledListPredicate(target); - } + if (PredicateType == ListRightSideType.Static && CompiledListPredicate != null) + return CompiledListPredicate(target); + if (PredicateType == ListRightSideType.Dynamic && CompiledListPredicate != null) + return CompiledListPredicate(target); + if (PredicateType == ListRightSideType.DynamicExternal && CompiledExternalListPredicate != null) + return CompiledExternalListPredicate(target, RightDataModel); - internal bool ListContainsInnerPath(string path) - { - if (ListType == null) - return false; - - var parts = path.Split('.'); - var current = ListType; - foreach (var part in parts) - { - var property = current.GetProperty(part); - current = property?.PropertyType; - - if (property == null) - return false; - } - - return true; + return false; } internal Type GetTypeAtInnerPath(string path) @@ -280,6 +344,8 @@ namespace Artemis.Core } Entity.LeftPropertyPath = LeftPropertyPath; + + Entity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid; Entity.RightPropertyPath = RightPropertyPath; Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue); @@ -290,13 +356,15 @@ namespace Artemis.Core } } - internal override DisplayConditionPartEntity GetEntity() + internal override DataModelConditionPartEntity GetEntity() { return Entity; } private void Initialize() { + DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded; + DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved; ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded; ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved; @@ -313,13 +381,20 @@ namespace Artemis.Core } // Right side dynamic - if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null) + if (PredicateType == ListRightSideType.Dynamic && Entity.RightPropertyPath != null) { if (ListContainsInnerPath(Entity.RightPropertyPath)) UpdateRightSideDynamic(Entity.RightPropertyPath); } + // Right side dynamic using an external data model + else if (PredicateType == ListRightSideType.Dynamic && Entity.RightDataModelGuid != null && Entity.RightPropertyPath != null) + { + var dataModel = DataModelStore.Get(Entity.RightDataModelGuid.Value)?.DataModel; + if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath)) + UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath); + } // Right side static - else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null) + else if (PredicateType == ListRightSideType.Static && Entity.RightStaticValue != null) { try { @@ -363,10 +438,12 @@ namespace Artemis.Core return; // If the operator does not support a right side, create a static expression because the right side will simply be null - if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide) + if (PredicateType == ListRightSideType.Dynamic && Operator.SupportsRightSide) CreateDynamicExpression(); - - CreateStaticExpression(); + else if (PredicateType == ListRightSideType.DynamicExternal && Operator.SupportsRightSide) + CreateDynamicExternalExpression(); + else + CreateStaticExpression(); } private void ValidateOperator() @@ -382,7 +459,7 @@ namespace Artemis.Core private void ValidateRightSide() { var leftSideType = GetTypeAtInnerPath(LeftPropertyPath); - if (PredicateType == ProfileRightSideType.Dynamic) + if (PredicateType == ListRightSideType.Dynamic) { if (RightPropertyPath == null) return; @@ -391,6 +468,15 @@ namespace Artemis.Core if (!leftSideType.IsCastableFrom(rightSideType)) UpdateRightSideDynamic(null); } + else if (PredicateType == ListRightSideType.DynamicExternal) + { + if (RightDataModel == null) + return; + + var rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath); + if (!leftSideType.IsCastableFrom(rightSideType)) + UpdateRightSideDynamic(null, null); + } else { if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType())) @@ -434,7 +520,7 @@ namespace Artemis.Core var rightSideAccessor = CreateListAccessor(RightPropertyPath, leftSideParameter); // 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 + // This can cause issues if the DataModelConditionOperator 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); @@ -443,14 +529,34 @@ namespace Artemis.Core CompiledListPredicate = lambda.Compile(); } - private void CreateStaticExpression() + private void CreateDynamicExternalExpression() { - if (LeftPropertyPath == null || Operator == null) + if (LeftPropertyPath == null || RightPropertyPath == null || RightDataModel == null || Operator == null) return; // List accessors share the same parameter because a list always contains one item per entry var leftSideParameter = Expression.Parameter(typeof(object), "listItem"); var leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter); + var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter); + + // A conversion may be required if the types differ + // This can cause issues if the DataModelConditionOperator 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 conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor); + var lambda = Expression.Lambda>(conditionExpression, leftSideParameter, rightSideParameter); + CompiledExternalListPredicate = lambda.Compile(); + } + + private void CreateStaticExpression() + { + if (!IsPrimitiveList && LeftPropertyPath == null || Operator == null) + return; + + // List accessors share the same parameter because a list always contains one item per entry + var leftSideParameter = Expression.Parameter(typeof(object), "listItem"); + var leftSideAccessor = IsPrimitiveList ? Expression.Convert(leftSideParameter, ListType) : CreateListAccessor(LeftPropertyPath, leftSideParameter); // 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) @@ -458,7 +564,7 @@ namespace Artemis.Core // If the right side value is null, the constant type cannot be inferred and must be provided manually var rightSideConstant = RightStaticValue != null - ? Expression.Constant(RightStaticValue) + ? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type)) : Expression.Constant(null, leftSideAccessor.Type); var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant); @@ -474,8 +580,46 @@ namespace Artemis.Core ); } + private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter) + { + var listType = dataModel.GetListTypeInPath(path); + if (listType != null) + throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list"); + + parameter = Expression.Parameter(typeof(object), parameterName + "DataModel"); + return path.Split('.').Aggregate( + Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type + Expression.Property + ); + } + #region Event handlers + private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e) + { + var dataModel = e.Registration.DataModel; + if (dataModel.PluginInfo.Guid == Entity.ListDataModelGuid && dataModel.ContainsPath(Entity.ListPropertyPath)) + UpdateList(dataModel, Entity.LeftPropertyPath); + if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath)) + UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath); + } + + private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e) + { + if (ListDataModel == e.Registration.DataModel) + { + CompiledListPredicate = null; + CompiledExternalListPredicate = null; + ListDataModel = null; + } + + if (RightDataModel == e.Registration.DataModel) + { + CompiledExternalListPredicate = null; + RightDataModel = null; + } + } + private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e) { var conditionOperator = e.Registration.ConditionOperator; @@ -493,4 +637,25 @@ namespace Artemis.Core #endregion } + + /// + /// An enum defining the right side type of a profile entity + /// + public enum ListRightSideType + { + /// + /// A static right side value + /// + Static, + + /// + /// A dynamic right side value based on a path in the list + /// + Dynamic, + + /// + /// A dynamic right side value based on a path in a data model + /// + DynamicExternal + } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs index 5af53d9cc..6a63cd13f 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs @@ -23,12 +23,12 @@ namespace Artemis.Core { Parent = parent; PredicateType = predicateType; - Entity = new DisplayConditionPredicateEntity(); + Entity = new DataModelConditionPredicateEntity(); Initialize(); } - internal DataModelConditionPredicate(DataModelConditionPart parent, DisplayConditionPredicateEntity entity) + internal DataModelConditionPredicate(DataModelConditionPart parent, DataModelConditionPredicateEntity entity) { Parent = parent; Entity = entity; @@ -83,7 +83,7 @@ namespace Artemis.Core /// public Func CompiledStaticPredicate { get; private set; } - internal DisplayConditionPredicateEntity Entity { get; set; } + internal DataModelConditionPredicateEntity Entity { get; set; } /// /// Updates the left side of the predicate @@ -309,7 +309,7 @@ namespace Artemis.Core } } - internal override DisplayConditionPartEntity GetEntity() + internal override DataModelConditionPartEntity GetEntity() { return Entity; } @@ -369,7 +369,7 @@ namespace Artemis.Core var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter); // 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 + // This can cause issues if the DataModelConditionOperator 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); @@ -391,7 +391,7 @@ namespace Artemis.Core // If the right side value is null, the constant type cannot be inferred and must be provided manually var rightSideConstant = RightStaticValue != null - ? Expression.Constant(RightStaticValue) + ? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type)) : Expression.Constant(null, leftSideAccessor.Type); var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant); diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs index e31296d73..4ed07a17c 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs @@ -72,6 +72,9 @@ namespace Artemis.Core { _disposed = true; + DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; + DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; + foreach (var dataBindingModifier in Modifiers) dataBindingModifier.Dispose(); } @@ -153,8 +156,7 @@ namespace Artemis.Core { return SourceDataModel?.GetTypeAtPath(SourcePropertyPath); } - - + /// /// Updates the source of the data binding and re-compiles the expression /// diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index f2eec3588..06146e6a3 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -49,8 +49,8 @@ namespace Artemis.Core DisplayContinuously = RenderElementEntity.DisplayContinuously; AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline; - DataModelConditionGroup = RenderElementEntity.RootDisplayCondition != null - ? new DataModelConditionGroup(null, RenderElementEntity.RootDisplayCondition) + DisplayCondition = RenderElementEntity.DisplayCondition != null + ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition) : new DataModelConditionGroup(null); ActivateEffects(); @@ -82,8 +82,8 @@ namespace Artemis.Core } // Conditions - RenderElementEntity.RootDisplayCondition = DataModelConditionGroup?.Entity; - DataModelConditionGroup?.Save(); + RenderElementEntity.DisplayCondition = DisplayCondition?.Entity; + DisplayCondition?.Save(); } #region Properties @@ -392,22 +392,22 @@ namespace Artemis.Core private set => SetAndNotify(ref _displayConditionMet, value); } - private DataModelConditionGroup _dataModelConditionGroup; + private DataModelConditionGroup _displayCondition; private TimeSpan _timelinePosition; private bool _displayConditionMet; /// /// Gets or sets the root display condition group /// - public DataModelConditionGroup DataModelConditionGroup + public DataModelConditionGroup DisplayCondition { - get => _dataModelConditionGroup; - set => SetAndNotify(ref _dataModelConditionGroup, value); + get => _displayCondition; + set => SetAndNotify(ref _displayCondition, value); } public void UpdateDisplayCondition() { - var conditionMet = DataModelConditionGroup == null || DataModelConditionGroup.Evaluate(); + var conditionMet = DisplayCondition == null || DisplayCondition.Evaluate(); if (conditionMet && !DisplayConditionMet) TimelinePosition = TimeSpan.Zero; diff --git a/src/Artemis.Core/Services/Registration/DataModelService.cs b/src/Artemis.Core/Services/Registration/DataModelService.cs index f3dcfaeb9..a66816fb6 100644 --- a/src/Artemis.Core/Services/Registration/DataModelService.cs +++ b/src/Artemis.Core/Services/Registration/DataModelService.cs @@ -11,9 +11,9 @@ namespace Artemis.Core.Services public DataModelService(IPluginService pluginService) { // Add data models of already loaded plugins - foreach (var module in pluginService.GetPluginsOfType()) + foreach (var module in pluginService.GetPluginsOfType().Where(p => p.Enabled)) AddModuleDataModel(module); - foreach (var dataModelExpansion in pluginService.GetPluginsOfType()) + foreach (var dataModelExpansion in pluginService.GetPluginsOfType().Where(p => p.Enabled)) AddDataModelExpansionDataModel(dataModelExpansion); // Add data models of new plugins when they get enabled diff --git a/src/Artemis.Core/Services/SettingsService.cs b/src/Artemis.Core/Services/SettingsService.cs index 9e3d76496..24b267e63 100644 --- a/src/Artemis.Core/Services/SettingsService.cs +++ b/src/Artemis.Core/Services/SettingsService.cs @@ -19,7 +19,7 @@ namespace Artemis.Core.Services } /// - /// A wrapper around plugin settings for internal use. + /// A wrapper around plugin settings for miscellaneous use outside plugins /// Do not inject into a plugin, for plugins inject instead. /// public interface ISettingsService : IProtectedArtemisService diff --git a/src/Artemis.Core/Stores/ConditionOperatorStore.cs b/src/Artemis.Core/Stores/ConditionOperatorStore.cs index 194188084..0ee90cdd5 100644 --- a/src/Artemis.Core/Stores/ConditionOperatorStore.cs +++ b/src/Artemis.Core/Stores/ConditionOperatorStore.cs @@ -56,13 +56,13 @@ namespace Artemis.Core var candidates = Registrations.Where(r => r.ConditionOperator.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList(); // If there are multiple operators with the same description, use the closest match - foreach (var displayConditionOperators in candidates.GroupBy(r => r.ConditionOperator.Description).Where(g => g.Count() > 1).ToList()) + foreach (var candidate in candidates.GroupBy(r => r.ConditionOperator.Description).Where(g => g.Count() > 1).ToList()) { - var closest = displayConditionOperators.OrderByDescending(r => r.ConditionOperator.CompatibleTypes.Contains(type)).FirstOrDefault(); - foreach (var displayConditionOperator in displayConditionOperators) + var closest = candidate.OrderByDescending(r => r.ConditionOperator.CompatibleTypes.Contains(type)).FirstOrDefault(); + foreach (var conditionOperator in candidate) { - if (displayConditionOperator != closest) - candidates.Remove(displayConditionOperator); + if (conditionOperator != closest) + candidates.Remove(conditionOperator); } } diff --git a/src/Artemis.Plugins.DataModelExpansions.TestData/DataModels/PluginDataModel.cs b/src/Artemis.Plugins.DataModelExpansions.TestData/DataModels/PluginDataModel.cs index c18ee6513..0e24eac8b 100644 --- a/src/Artemis.Plugins.DataModelExpansions.TestData/DataModels/PluginDataModel.cs +++ b/src/Artemis.Plugins.DataModelExpansions.TestData/DataModels/PluginDataModel.cs @@ -9,6 +9,9 @@ namespace Artemis.Plugins.DataModelExpansions.TestData.DataModels public PluginDataModel() { PluginSubDataModel = new PluginSubDataModel(); + ListItems = new List(); + for (var i = 0; i < 20; i++) + ListItems.Add(new SomeListItem {ItemName = $"Item {i + 1}", Number = i}); } // Your datamodel can have regular properties and you can annotate them if you'd like @@ -24,6 +27,14 @@ namespace Artemis.Plugins.DataModelExpansions.TestData.DataModels public Team Team { get; set; } public bool IsWinning { get; set; } + + public List ListItems { get; set; } + } + + public class SomeListItem + { + public string ItemName { get; set; } + public int Number { get; set; } } public enum Team @@ -36,7 +47,7 @@ namespace Artemis.Plugins.DataModelExpansions.TestData.DataModels { public PluginSubDataModel() { - ListOfInts = new List { 1, 2, 3, 4, 5 }; + ListOfInts = new List {1, 2, 3, 4, 5}; } // You don't need to annotate properties, they will still show up diff --git a/src/Artemis.Storage/Entities/Profile/Abstract/DisplayConditionPartEntity.cs b/src/Artemis.Storage/Entities/Profile/Abstract/DisplayConditionPartEntity.cs index f1397fd35..2db9ce8a5 100644 --- a/src/Artemis.Storage/Entities/Profile/Abstract/DisplayConditionPartEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/Abstract/DisplayConditionPartEntity.cs @@ -2,8 +2,8 @@ namespace Artemis.Storage.Entities.Profile.Abstract { - public abstract class DisplayConditionPartEntity + public abstract class DataModelConditionPartEntity { - public List Children { get; set; } + public List Children { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs index 41af59cd9..7e7556e36 100644 --- a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs @@ -16,6 +16,6 @@ namespace Artemis.Storage.Entities.Profile.Abstract public List PropertyEntities { get; set; } public List ExpandedPropertyGroups { get; set; } - public DisplayConditionGroupEntity RootDisplayCondition { get; set; } + public DataModelConditionGroupEntity DisplayCondition { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionGroupEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionGroupEntity.cs similarity index 54% rename from src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionGroupEntity.cs rename to src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionGroupEntity.cs index a862a79e4..90f84494e 100644 --- a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionGroupEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionGroupEntity.cs @@ -3,11 +3,11 @@ using Artemis.Storage.Entities.Profile.Abstract; namespace Artemis.Storage.Entities.Profile.Conditions { - public class DisplayConditionGroupEntity : DisplayConditionPartEntity + public class DataModelConditionGroupEntity : DataModelConditionPartEntity { - public DisplayConditionGroupEntity() + public DataModelConditionGroupEntity() { - Children = new List(); + Children = new List(); } public int BooleanOperator { get; set; } diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionListEntity.cs similarity index 64% rename from src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListEntity.cs rename to src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionListEntity.cs index 925c9a055..00e6f0e34 100644 --- a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionListEntity.cs @@ -4,11 +4,11 @@ using Artemis.Storage.Entities.Profile.Abstract; namespace Artemis.Storage.Entities.Profile.Conditions { - public class DisplayConditionListEntity : DisplayConditionPartEntity + public class DataModelConditionListEntity : DataModelConditionPartEntity { - public DisplayConditionListEntity() + public DataModelConditionListEntity() { - Children = new List(); + Children = new List(); } public Guid? ListDataModelGuid { get; set; } diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListPredicateEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionListPredicateEntity.cs similarity index 82% rename from src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListPredicateEntity.cs rename to src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionListPredicateEntity.cs index 67c59027f..32429c83c 100644 --- a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListPredicateEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionListPredicateEntity.cs @@ -3,7 +3,7 @@ using Artemis.Storage.Entities.Profile.Abstract; namespace Artemis.Storage.Entities.Profile.Conditions { - public class DisplayConditionListPredicateEntity : DisplayConditionPartEntity + public class DataModelConditionListPredicateEntity : DataModelConditionPartEntity { public int PredicateType { get; set; } @@ -12,6 +12,7 @@ namespace Artemis.Storage.Entities.Profile.Conditions 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 diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionPredicateEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionPredicateEntity.cs similarity index 88% rename from src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionPredicateEntity.cs rename to src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionPredicateEntity.cs index f31c160f9..91f39ccf2 100644 --- a/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionPredicateEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/Conditions/DataModelConditionPredicateEntity.cs @@ -3,7 +3,7 @@ using Artemis.Storage.Entities.Profile.Abstract; namespace Artemis.Storage.Entities.Profile.Conditions { - public class DisplayConditionPredicateEntity : DisplayConditionPartEntity + public class DataModelConditionPredicateEntity : DataModelConditionPartEntity { public int PredicateType { get; set; } public Guid? LeftDataModelGuid { get; set; } diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/ProfileConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/ProfileConditionEntity.cs deleted file mode 100644 index 686300b58..000000000 --- a/src/Artemis.Storage/Entities/Profile/Conditions/ProfileConditionEntity.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Artemis.Storage.Entities.Profile.Conditions -{ - public class ProfileConditionEntity - { - public Guid Id { get; set; } - } -} \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingConditionValueEntity.cs b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingConditionValueEntity.cs index 17bace120..8f1c9004b 100644 --- a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingConditionValueEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingConditionValueEntity.cs @@ -5,6 +5,6 @@ namespace Artemis.Storage.Entities.Profile.DataBindings public class DataBindingConditionValueEntity { public string Value { get; set; } - public DisplayConditionGroupEntity RootGroup { get; set; } + public DataModelConditionGroupEntity RootGroup { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml index ca1dbb905..e1d719218 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml @@ -13,7 +13,7 @@ - + @@ -32,7 +32,7 @@ + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateView.xaml.cs similarity index 69% rename from src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml.cs rename to src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateView.xaml.cs index 396bd3f41..44c02f06b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateView.xaml.cs @@ -1,14 +1,14 @@ using System.Windows; using System.Windows.Controls; -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions +namespace Artemis.UI.Screens.ProfileEditor.Conditions { /// - /// Interaction logic for DisplayConditionListPredicateView.xaml + /// Interaction logic for DataModelConditionListPredicateView.xaml /// - public partial class DisplayConditionListPredicateView : UserControl + public partial class DataModelConditionListPredicateView : UserControl { - public DisplayConditionListPredicateView() + public DataModelConditionListPredicateView() { InitializeComponent(); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs new file mode 100644 index 000000000..25a20d0e3 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using Artemis.Core; +using Artemis.Core.Services; +using Artemis.UI.Exceptions; +using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Input; +using Artemis.UI.Shared.Services; +using Stylet; + +namespace Artemis.UI.Screens.ProfileEditor.Conditions +{ + public class DataModelConditionListPredicateViewModel : DataModelConditionViewModel, IDisposable + { + private readonly IConditionOperatorService _conditionOperatorService; + private readonly IDataModelUIService _dataModelUIService; + private readonly IEventAggregator _eventAggregator; + private readonly IProfileEditorService _profileEditorService; + private bool _isPrimitiveList; + private DataModelDynamicViewModel _leftSideSelectionViewModel; + private BindableCollection _operators; + private DataModelStaticViewModel _rightSideInputViewModel; + private DataModelDynamicViewModel _rightSideSelectionViewModel; + private ConditionOperator _selectedOperator; + + private List _supportedInputTypes; + + public DataModelConditionListPredicateViewModel( + DataModelConditionListPredicate dataModelConditionListPredicate, + IProfileEditorService profileEditorService, + IDataModelUIService dataModelUIService, + IConditionOperatorService conditionOperatorService, + IEventAggregator eventAggregator) : base(dataModelConditionListPredicate) + { + _profileEditorService = profileEditorService; + _dataModelUIService = dataModelUIService; + _conditionOperatorService = conditionOperatorService; + _eventAggregator = eventAggregator; + _supportedInputTypes = new List(); + + SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); + Operators = new BindableCollection(); + + Initialize(); + } + + public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model; + + public BindableCollection Operators + { + get => _operators; + set => SetAndNotify(ref _operators, value); + } + + public DataModelDynamicViewModel LeftSideSelectionViewModel + { + get => _leftSideSelectionViewModel; + set => SetAndNotify(ref _leftSideSelectionViewModel, value); + } + + public DataModelDynamicViewModel RightSideSelectionViewModel + { + get => _rightSideSelectionViewModel; + set => SetAndNotify(ref _rightSideSelectionViewModel, value); + } + + public DataModelStaticViewModel RightSideInputViewModel + { + get => _rightSideInputViewModel; + set => SetAndNotify(ref _rightSideInputViewModel, value); + } + + public ConditionOperator SelectedOperator + { + get => _selectedOperator; + set => SetAndNotify(ref _selectedOperator, value); + } + + public DelegateCommand SelectOperatorCommand { get; } + + public void Dispose() + { + if (!_isPrimitiveList) + { + LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected; + LeftSideSelectionViewModel.Dispose(); + } + + DisposeRightSideDynamic(); + DisposeRightSideStatic(); + } + + public override void Delete() + { + base.Delete(); + _profileEditorService.UpdateSelectedProfileElement(); + } + + public void Initialize() + { + var listDataModel = GetListDataModel(); + if (listDataModel.Children.Count == 1 && listDataModel.Children.First() is DataModelListPropertyViewModel) + _isPrimitiveList = true; + else + _isPrimitiveList = false; + + // Get the data models + if (!_isPrimitiveList) + { + LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); + LeftSideSelectionViewModel.ChangeDataModel((DataModelPropertiesViewModel) listDataModel); + LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected; + } + + // Determine which types are currently supported + var editors = _dataModelUIService.RegisteredDataModelEditors; + _supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); + _supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes)); + + Update(); + } + + public override void Update() + { + var listDataModelGuid = DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid; + if (!_isPrimitiveList) + { + LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray(); + LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188)); + LeftSideSelectionViewModel.SelectedPropertyViewModel = LeftSideSelectionViewModel.DataModelViewModel.GetChildByPath( + listDataModelGuid, DataModelConditionListPredicate.LeftPropertyPath + ); + } + + var leftSideType = _isPrimitiveList + ? DataModelConditionListPredicate.ListType + : LeftSideSelectionViewModel.SelectedPropertyViewModel?.PropertyInfo?.PropertyType; + + // Get the supported operators + Operators.Clear(); + Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType)); + if (DataModelConditionListPredicate.Operator == null) + DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType))); + SelectedOperator = DataModelConditionListPredicate.Operator; + + // Ensure the right side has the proper VM + if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic || DataModelConditionListPredicate.PredicateType == ListRightSideType.DynamicExternal) + { + DisposeRightSideStatic(); + if (RightSideSelectionViewModel == null) + { + RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); + RightSideSelectionViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush"); + RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected; + } + + RightSideSelectionViewModel.FilterTypes = new[] {leftSideType}; + if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic) + { + RightSideSelectionViewModel.SelectedPropertyViewModel = RightSideSelectionViewModel.DataModelViewModel.GetChildByPath( + listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath + ); + } + else + RightSideSelectionViewModel.PopulateSelectedPropertyViewModel(DataModelConditionListPredicate.RightDataModel, DataModelConditionListPredicate.RightPropertyPath); + } + else + { + DisposeRightSideDynamic(); + if (RightSideInputViewModel == null) + { + RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType); + RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue; + RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush"); + RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered; + } + + if (RightSideInputViewModel.TargetType != leftSideType) + RightSideInputViewModel.UpdateTargetType(leftSideType); + } + } + + public void ApplyLeftSide() + { + DataModelConditionListPredicate.UpdateLeftSide(LeftSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath); + _profileEditorService.UpdateSelectedProfileElement(); + + SelectedOperator = DataModelConditionListPredicate.Operator; + Update(); + } + + public void ApplyRightSideDynamic() + { + if (DataModelConditionListPredicate.ListContainsInnerPath(RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath)) + DataModelConditionListPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath); + else + DataModelConditionListPredicate.UpdateRightSideDynamic( + RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel, + RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath + ); + + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + public void ApplyRightSideStatic(object value) + { + DataModelConditionListPredicate.UpdateRightSideStatic(value); + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + public void ApplyOperator() + { + DataModelConditionListPredicate.UpdateOperator(SelectedOperator); + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + private DataModelVisualizationViewModel GetListDataModel() + { + if (DataModelConditionListPredicate.ListDataModel == null || DataModelConditionListPredicate.ListPropertyPath == null) + throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list"); + + var dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); + var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath( + DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid, + DataModelConditionListPredicate.ListPropertyPath + ); + + return listDataModel.GetListTypeViewModel(_dataModelUIService); + } + + private void ExecuteSelectOperatorCommand(object context) + { + if (!(context is ConditionOperator dataModelConditionOperator)) + return; + + SelectedOperator = dataModelConditionOperator; + ApplyOperator(); + } + + private void LeftSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) + { + ApplyLeftSide(); + } + + private void RightSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) + { + ApplyRightSideDynamic(); + } + + private void RightSideOnValueEntered(object sender, DataModelInputStaticEventArgs e) + { + ApplyRightSideStatic(e.Value); + } + + private void DisposeRightSideStatic() + { + if (RightSideInputViewModel != null) + { + RightSideInputViewModel.ValueUpdated -= RightSideOnValueEntered; + RightSideInputViewModel.Dispose(); + RightSideInputViewModel = null; + } + } + + private void DisposeRightSideDynamic() + { + if (RightSideSelectionViewModel != null) + { + RightSideSelectionViewModel.PropertySelected -= RightSideOnPropertySelected; + RightSideSelectionViewModel.Dispose(); + RightSideSelectionViewModel = null; + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListView.xaml b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListView.xaml new file mode 100644 index 000000000..32c09a1c4 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListView.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListView.xaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListView.xaml.cs similarity index 71% rename from src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListView.xaml.cs rename to src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListView.xaml.cs index 897403a3a..23a1fe9e6 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListView.xaml.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListView.xaml.cs @@ -1,14 +1,14 @@ using System.Windows; using System.Windows.Controls; -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions +namespace Artemis.UI.Screens.ProfileEditor.Conditions { /// - /// Interaction logic for DisplayConditionListView.xaml + /// Interaction logic for DataModelConditionListView.xaml /// - public partial class DisplayConditionListView : UserControl + public partial class DataModelConditionListView : UserControl { - public DisplayConditionListView() + public DataModelConditionListView() { InitializeComponent(); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs new file mode 100644 index 000000000..105a314f3 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using System.Linq; +using System.Windows.Media; +using Artemis.Core; +using Artemis.UI.Ninject.Factories; +using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Input; +using Artemis.UI.Shared.Services; +using Humanizer; + +namespace Artemis.UI.Screens.ProfileEditor.Conditions +{ + public class DataModelConditionListViewModel : DataModelConditionViewModel, IDisposable + { + private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory; + private readonly IDataModelUIService _dataModelUIService; + private readonly IProfileEditorService _profileEditorService; + private DataModelDynamicViewModel _targetSelectionViewModel; + + public DataModelConditionListViewModel( + DataModelConditionList dataModelConditionList, + IProfileEditorService profileEditorService, + IDataModelUIService dataModelUIService, + IDataModelConditionsVmFactory dataModelConditionsVmFactory) : base(dataModelConditionList) + { + _profileEditorService = profileEditorService; + _dataModelUIService = dataModelUIService; + _dataModelConditionsVmFactory = dataModelConditionsVmFactory; + + Initialize(); + } + + public DataModelDynamicViewModel TargetSelectionViewModel + { + get => _targetSelectionViewModel; + set => SetAndNotify(ref _targetSelectionViewModel, value); + } + + public DataModelConditionList DataModelConditionList => (DataModelConditionList) Model; + + public string SelectedListOperator => DataModelConditionList.ListOperator.Humanize(); + + public void Dispose() + { + TargetSelectionViewModel.Dispose(); + TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected; + } + + public void SelectListOperator(string type) + { + var enumValue = Enum.Parse(type); + DataModelConditionList.ListOperator = enumValue; + NotifyOfPropertyChange(nameof(SelectedListOperator)); + + _profileEditorService.UpdateSelectedProfileElement(); + } + + public void AddCondition(string type) + { + if (type == "Static") + DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Static)); + else if (type == "Dynamic") + DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Dynamic)); + + Update(); + _profileEditorService.UpdateSelectedProfileElement(); + } + + public void AddGroup() + { + DataModelConditionList.AddChild(new DataModelConditionGroup(DataModelConditionList)); + + Update(); + _profileEditorService.UpdateSelectedProfileElement(); + } + + public override void Delete() + { + base.Delete(); + _profileEditorService.UpdateSelectedProfileElement(); + } + + public void Initialize() + { + TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); + TargetSelectionViewModel.FilterTypes = new[] {typeof(IList)}; + TargetSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188)); + TargetSelectionViewModel.Placeholder = "Select a list"; + TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected; + + Update(); + } + + public void ApplyList() + { + DataModelConditionList.UpdateList( + TargetSelectionViewModel.SelectedPropertyViewModel.DataModel, + TargetSelectionViewModel.SelectedPropertyViewModel.PropertyPath + ); + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + public override void Update() + { + TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DataModelConditionList.ListDataModel, DataModelConditionList.ListPropertyPath); + NotifyOfPropertyChange(nameof(SelectedListOperator)); + + // Remove VMs of effects no longer applied on the layer + var toRemove = Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList(); + // Using RemoveRange breaks our lovely animations + foreach (var conditionViewModel in toRemove) + Items.Remove(conditionViewModel); + + foreach (var childModel in Model.Children) + { + if (Items.Any(c => c.Model == childModel)) + continue; + if (!(childModel is DataModelConditionGroup dataModelConditionGroup)) + continue; + + var viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, true); + viewModel.IsRootGroup = true; + Items.Add(viewModel); + } + + foreach (var childViewModel in Items) + childViewModel.Update(); + } + + private void TargetSelectionViewModelOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e) + { + ApplyList(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateView.xaml b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateView.xaml new file mode 100644 index 000000000..8bdc6a14e --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateView.xaml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateView.xaml.cs similarity index 70% rename from src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml.cs rename to src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateView.xaml.cs index bfef45278..0891cd5fc 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateView.xaml.cs @@ -1,14 +1,14 @@ using System.Windows; using System.Windows.Controls; -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions +namespace Artemis.UI.Screens.ProfileEditor.Conditions { /// - /// Interaction logic for DisplayConditionPredicateView.xaml + /// Interaction logic for DataModelConditionPredicateView.xaml /// - public partial class DisplayConditionPredicateView : UserControl + public partial class DataModelConditionPredicateView : UserControl { - public DisplayConditionPredicateView() + public DataModelConditionPredicateView() { InitializeComponent(); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs new file mode 100644 index 000000000..cc351d7a9 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using Artemis.Core; +using Artemis.Core.Services; +using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Input; +using Artemis.UI.Shared.Services; +using Stylet; + +namespace Artemis.UI.Screens.ProfileEditor.Conditions +{ + public class DataModelConditionPredicateViewModel : DataModelConditionViewModel, IDisposable + { + private readonly IConditionOperatorService _conditionOperatorService; + private readonly IDataModelUIService _dataModelUIService; + private readonly IProfileEditorService _profileEditorService; + private DataModelDynamicViewModel _leftSideSelectionViewModel; + private BindableCollection _operators; + private DataModelStaticViewModel _rightSideInputViewModel; + private DataModelDynamicViewModel _rightSideSelectionViewModel; + private ConditionOperator _selectedOperator; + + private List _supportedInputTypes; + + public DataModelConditionPredicateViewModel( + DataModelConditionPredicate dataModelConditionPredicate, + IProfileEditorService profileEditorService, + IDataModelUIService dataModelUIService, + IConditionOperatorService conditionOperatorService, + ISettingsService settingsService) : base(dataModelConditionPredicate) + { + _profileEditorService = profileEditorService; + _dataModelUIService = dataModelUIService; + _conditionOperatorService = conditionOperatorService; + _supportedInputTypes = new List(); + + SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); + Operators = new BindableCollection(); + + ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues"); + + Initialize(); + } + + public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model; + public PluginSetting ShowDataModelValues { get; } + + + public BindableCollection Operators + { + get => _operators; + set => SetAndNotify(ref _operators, value); + } + + public DataModelDynamicViewModel LeftSideSelectionViewModel + { + get => _leftSideSelectionViewModel; + set => SetAndNotify(ref _leftSideSelectionViewModel, value); + } + + public ConditionOperator SelectedOperator + { + get => _selectedOperator; + set => SetAndNotify(ref _selectedOperator, value); + } + + public DataModelDynamicViewModel RightSideSelectionViewModel + { + get => _rightSideSelectionViewModel; + set => SetAndNotify(ref _rightSideSelectionViewModel, value); + } + + public DataModelStaticViewModel RightSideInputViewModel + { + get => _rightSideInputViewModel; + set => SetAndNotify(ref _rightSideInputViewModel, value); + } + + public DelegateCommand SelectOperatorCommand { get; } + + public void Dispose() + { + if (LeftSideSelectionViewModel != null) + { + LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected; + LeftSideSelectionViewModel.Dispose(); + LeftSideSelectionViewModel = null; + } + + DisposeRightSideStatic(); + DisposeRightSideDynamic(); + } + + public override void Delete() + { + base.Delete(); + _profileEditorService.UpdateSelectedProfileElement(); + } + + public void Initialize() + { + LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); + LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected; + // Determine which types are currently supported + var editors = _dataModelUIService.RegisteredDataModelEditors; + _supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); + _supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes)); + + Update(); + } + + public override void Update() + { + LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray(); + LeftSideSelectionViewModel.PopulateSelectedPropertyViewModel( + DataModelConditionPredicate.LeftDataModel, + DataModelConditionPredicate.LeftPropertyPath + ); + var leftSideType = LeftSideSelectionViewModel.SelectedPropertyViewModel?.PropertyInfo?.PropertyType; + + // Get the supported operators + Operators.Clear(); + Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType)); + if (DataModelConditionPredicate.Operator == null) + DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType))); + SelectedOperator = DataModelConditionPredicate.Operator; + + // Ensure the right side has the proper VM + var targetType = LeftSideSelectionViewModel?.SelectedPropertyViewModel?.PropertyInfo?.PropertyType; + if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic) + { + DisposeRightSideStatic(); + if (RightSideSelectionViewModel == null) + { + RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); + RightSideSelectionViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush"); + RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected; + } + + RightSideSelectionViewModel.PopulateSelectedPropertyViewModel( + DataModelConditionPredicate.RightDataModel, + DataModelConditionPredicate.RightPropertyPath + ); + RightSideSelectionViewModel.FilterTypes = new[] {targetType}; + } + else + { + DisposeRightSideDynamic(); + if (RightSideInputViewModel == null) + { + RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(targetType); + RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush"); + RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered; + } + + if (RightSideInputViewModel.TargetType != targetType) + RightSideInputViewModel.UpdateTargetType(targetType); + } + } + + public void ApplyLeftSide() + { + DataModelConditionPredicate.UpdateLeftSide( + LeftSideSelectionViewModel.SelectedPropertyViewModel.DataModel, + LeftSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath + ); + _profileEditorService.UpdateSelectedProfileElement(); + + SelectedOperator = DataModelConditionPredicate.Operator; + Update(); + } + + public void ApplyRightSideDynamic() + { + DataModelConditionPredicate.UpdateRightSide( + RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel, + RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath + ); + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + public void ApplyRightSideStatic(object value) + { + DataModelConditionPredicate.UpdateRightSide(value); + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + public void ApplyOperator() + { + DataModelConditionPredicate.UpdateOperator(SelectedOperator); + _profileEditorService.UpdateSelectedProfileElement(); + + Update(); + } + + private void LeftSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) + { + ApplyLeftSide(); + } + + private void RightSideOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e) + { + ApplyRightSideDynamic(); + } + + private void RightSideOnValueEntered(object sender, DataModelInputStaticEventArgs e) + { + ApplyRightSideStatic(e.Value); + } + + private void ExecuteSelectOperatorCommand(object context) + { + if (!(context is ConditionOperator DataModelConditionOperator)) + return; + + SelectedOperator = DataModelConditionOperator; + ApplyOperator(); + } + + private void DisposeRightSideStatic() + { + if (RightSideInputViewModel != null) + { + RightSideInputViewModel.ValueUpdated -= RightSideOnValueEntered; + RightSideInputViewModel.Dispose(); + RightSideInputViewModel = null; + } + } + + private void DisposeRightSideDynamic() + { + if (RightSideSelectionViewModel != null) + { + RightSideSelectionViewModel.PropertySelected -= RightSideOnPropertySelected; + RightSideSelectionViewModel.Dispose(); + RightSideSelectionViewModel = null; + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/Abstract/DisplayConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/Abstract/DisplayConditionViewModel.cs deleted file mode 100644 index 2cca9b857..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/Abstract/DisplayConditionViewModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Artemis.Core; -using Stylet; - -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract -{ - public abstract class DisplayConditionViewModel : Conductor.Collection.AllActive - { - protected DisplayConditionViewModel(DataModelConditionPart model) - { - Model = model; - } - - public DataModelConditionPart Model { get; } - - public abstract void Update(); - - public virtual void Delete() - { - Model.Parent.RemoveChild(Model); - ((DisplayConditionViewModel) Parent).Update(); - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml deleted file mode 100644 index 95fb76fea..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs deleted file mode 100644 index 421dcbeb6..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs +++ /dev/null @@ -1,353 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Timers; -using System.Windows; -using System.Windows.Input; -using Artemis.Core; -using Artemis.Core.Services; -using Artemis.UI.Events; -using Artemis.UI.Exceptions; -using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; -using Artemis.UI.Shared; -using Artemis.UI.Shared.Services; -using Artemis.UI.Utilities; -using Stylet; - -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions -{ - public class DisplayConditionListPredicateViewModel : DisplayConditionViewModel, IHandle, IHandle, IDisposable - { - private readonly IConditionOperatorService _conditionOperatorService; - private readonly IDataModelUIService _dataModelUIService; - private readonly IEventAggregator _eventAggregator; - private readonly IProfileEditorService _profileEditorService; - private readonly Timer _updateTimer; - private bool _isInitialized; - private DataModelVisualizationViewModel _leftSideDataModel; - private BindableCollection _operators; - private DataModelVisualizationViewModel _rightSideDataModel; - private DataModelInputViewModel _rightSideInputViewModel; - private int _rightSideTransitionIndex; - private object _rightStaticValue; - private DataModelVisualizationViewModel _selectedLeftSideProperty; - private ConditionOperator _selectedOperator; - private DataModelVisualizationViewModel _selectedRightSideProperty; - - private List _supportedInputTypes; - - public DisplayConditionListPredicateViewModel( - DataModelConditionListPredicate dataModelConditionListPredicate, - IProfileEditorService profileEditorService, - IDataModelUIService dataModelUIService, - IConditionOperatorService conditionOperatorService, - ISettingsService settingsService, - IEventAggregator eventAggregator) : base(dataModelConditionListPredicate) - { - _profileEditorService = profileEditorService; - _dataModelUIService = dataModelUIService; - _conditionOperatorService = conditionOperatorService; - _eventAggregator = eventAggregator; - _updateTimer = new Timer(500); - _supportedInputTypes = new List(); - - SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty); - SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty); - SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); - Operators = new BindableCollection(); - - ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues"); - - // Initialize async, no need to wait for it - Task.Run(Initialize); - } - - public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model; - public bool ShowRightSidePropertySelection => DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic; - public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null; - public PluginSetting ShowDataModelValues { get; } - - public bool IsInitialized - { - get => _isInitialized; - private set => SetAndNotify(ref _isInitialized, value); - } - - public bool LeftSideDataModelOpen { get; set; } - - public DataModelVisualizationViewModel LeftSideDataModel - { - get => _leftSideDataModel; - set => SetAndNotify(ref _leftSideDataModel, value); - } - - public DataModelVisualizationViewModel RightSideDataModel - { - get => _rightSideDataModel; - set => SetAndNotify(ref _rightSideDataModel, value); - } - - public bool RightSideDataModelOpen { get; set; } - - public DataModelVisualizationViewModel SelectedLeftSideProperty - { - get => _selectedLeftSideProperty; - set - { - if (!SetAndNotify(ref _selectedLeftSideProperty, value)) return; - NotifyOfPropertyChange(nameof(CanActivateRightSideInputViewModel)); - } - } - - public DataModelVisualizationViewModel SelectedRightSideProperty - { - get => _selectedRightSideProperty; - set => SetAndNotify(ref _selectedRightSideProperty, value); - } - - public object RightStaticValue - { - get => _rightStaticValue; - set => SetAndNotify(ref _rightStaticValue, value); - } - - public int RightSideTransitionIndex - { - get => _rightSideTransitionIndex; - set => SetAndNotify(ref _rightSideTransitionIndex, value); - } - - public DataModelInputViewModel RightSideInputViewModel - { - get => _rightSideInputViewModel; - set => SetAndNotify(ref _rightSideInputViewModel, value); - } - - public BindableCollection Operators - { - get => _operators; - set => SetAndNotify(ref _operators, value); - } - - public ConditionOperator SelectedOperator - { - get => _selectedOperator; - set => SetAndNotify(ref _selectedOperator, value); - } - - public DelegateCommand SelectLeftPropertyCommand { get; } - public DelegateCommand SelectRightPropertyCommand { get; } - public DelegateCommand SelectOperatorCommand { get; } - - 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 override void Delete() - { - base.Delete(); - _profileEditorService.UpdateSelectedProfileElement(); - } - - public void Initialize() - { - // Get the data models - LeftSideDataModel = GetListDataModel(); - RightSideDataModel = GetListDataModel(); - LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested; - RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested; - - // Determine which types are currently supported - var editors = _dataModelUIService.RegisteredDataModelEditors; - _supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); - _supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes)); - - Update(); - - _updateTimer.Start(); - _updateTimer.Elapsed += OnUpdateTimerOnElapsed; - - IsInitialized = true; - } - - public override void Update() - { - // Not yet initialized if these are null - if (LeftSideDataModel == null || RightSideDataModel == null) - return; - - var listDataModelGuid = DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid; - - // If static, only allow selecting properties also supported by input - if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Static) - LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray()); - - // Determine the left side property first - SelectedLeftSideProperty = LeftSideDataModel.GetChildByPath(listDataModelGuid, DataModelConditionListPredicate.LeftPropertyPath); - var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType; - - // Get the supported operators - Operators.Clear(); - Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType)); - if (DataModelConditionListPredicate.Operator == null) - DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType))); - SelectedOperator = DataModelConditionListPredicate.Operator; - - // Determine the right side - if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic) - { - SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath); - RightSideDataModel.ApplyTypeFilter(true, leftSideType); - } - else - RightStaticValue = DataModelConditionListPredicate.RightStaticValue; - } - - public void ApplyLeftSide() - { - DataModelConditionListPredicate.UpdateLeftSide(SelectedLeftSideProperty.PropertyPath); - _profileEditorService.UpdateSelectedProfileElement(); - - SelectedOperator = DataModelConditionListPredicate.Operator; - Update(); - } - - public void ApplyRightSideDynamic() - { - DataModelConditionListPredicate.UpdateRightSideDynamic(SelectedRightSideProperty.PropertyPath); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - public void ApplyRightSideStatic(object value, bool isSubmitted) - { - if (isSubmitted) - { - DataModelConditionListPredicate.UpdateRightSideStatic(value); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - RightSideTransitionIndex = 0; - RightSideInputViewModel = null; - RightStaticValue = value; - _eventAggregator.Unsubscribe(this); - } - - public void ApplyOperator() - { - DataModelConditionListPredicate.UpdateOperator(SelectedOperator); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - public void ActivateRightSideInputViewModel() - { - if (SelectedLeftSideProperty?.PropertyInfo == null) - return; - - RightSideTransitionIndex = 1; - RightSideInputViewModel = _dataModelUIService.GetDataModelInputViewModel( - SelectedLeftSideProperty.PropertyInfo.PropertyType, - SelectedLeftSideProperty.PropertyDescription, - DataModelConditionListPredicate.RightStaticValue, - ApplyRightSideStatic - ); - _eventAggregator.Subscribe(this); - } - - private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) - { - if (LeftSideDataModelOpen) - LeftSideDataModel.Update(_dataModelUIService); - else if (RightSideDataModelOpen) - RightSideDataModel.Update(_dataModelUIService); - } - - private void RightDataModelUpdateRequested(object sender, EventArgs e) - { - var listDataModelGuid = DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid; - var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType; - - // If the right side property is missing it may be available now that the data model has been updated - if (SelectedRightSideProperty == null && DataModelConditionListPredicate.RightPropertyPath != null) - SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath); - - // With the data model updated, also reapply the filter - RightSideDataModel.ApplyTypeFilter(true, leftSideType); - } - - private void LeftDataModelUpdateRequested(object sender, EventArgs e) - { - if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Static) - LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray()); - } - - private DataModelVisualizationViewModel GetListDataModel() - { - if (DataModelConditionListPredicate.ListDataModel == null || DataModelConditionListPredicate.ListPropertyPath == null) - throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list"); - - var dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); - var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath( - DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid, - DataModelConditionListPredicate.ListPropertyPath - ); - - return listDataModel.GetListTypeViewModel(_dataModelUIService); - } - - private void ExecuteSelectLeftProperty(object context) - { - if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel)) - return; - - SelectedLeftSideProperty = dataModelVisualizationViewModel; - ApplyLeftSide(); - } - - private void ExecuteSelectRightProperty(object context) - { - if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel)) - return; - - SelectedRightSideProperty = dataModelVisualizationViewModel; - ApplyRightSideDynamic(); - } - - private void ExecuteSelectOperatorCommand(object context) - { - if (!(context is ConditionOperator displayConditionOperator)) - return; - - SelectedOperator = displayConditionOperator; - ApplyOperator(); - } - - public void Dispose() - { - _updateTimer.Dispose(); - _updateTimer.Elapsed -= OnUpdateTimerOnElapsed; - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListView.xaml b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListView.xaml deleted file mode 100644 index 634955989..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListView.xaml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs deleted file mode 100644 index 353b86b9f..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Collections; -using System.Linq; -using System.Threading.Tasks; -using System.Timers; -using Artemis.Core; -using Artemis.Core.Services; -using Artemis.UI.Ninject.Factories; -using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; -using Artemis.UI.Shared; -using Artemis.UI.Shared.Services; -using Artemis.UI.Utilities; -using Humanizer; - -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions -{ - public class DisplayConditionListViewModel : DisplayConditionViewModel, IDisposable - { - private readonly IDataModelUIService _dataModelUIService; - private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; - private readonly IProfileEditorService _profileEditorService; - private readonly Timer _updateTimer; - private bool _isInitialized; - private DataModelListViewModel _selectedListProperty; - private DataModelPropertiesViewModel _targetDataModel; - - public DisplayConditionListViewModel( - DataModelConditionList dataModelConditionList, - DisplayConditionViewModel parent, - IProfileEditorService profileEditorService, - IDataModelUIService dataModelUIService, - IDisplayConditionsVmFactory displayConditionsVmFactory, - ISettingsService settingsService) : base(dataModelConditionList) - { - _profileEditorService = profileEditorService; - _dataModelUIService = dataModelUIService; - _displayConditionsVmFactory = displayConditionsVmFactory; - _updateTimer = new Timer(500); - - SelectListPropertyCommand = new DelegateCommand(ExecuteSelectListProperty); - - ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues"); - - // Initialize async, no need to wait for it - Task.Run(Initialize); - } - - public DelegateCommand SelectListPropertyCommand { get; } - public PluginSetting ShowDataModelValues { get; } - - public DataModelConditionList DataModelConditionList => (DataModelConditionList) Model; - - public bool IsInitialized - { - get => _isInitialized; - set => SetAndNotify(ref _isInitialized, value); - } - - public bool TargetDataModelOpen { get; set; } - - public DataModelPropertiesViewModel TargetDataModel - { - get => _targetDataModel; - set => SetAndNotify(ref _targetDataModel, value); - } - - public DataModelListViewModel SelectedListProperty - { - get => _selectedListProperty; - set => SetAndNotify(ref _selectedListProperty, value); - } - - public string SelectedListOperator => DataModelConditionList.ListOperator.Humanize(); - - public void SelectListOperator(string type) - { - var enumValue = Enum.Parse(type); - DataModelConditionList.ListOperator = enumValue; - NotifyOfPropertyChange(nameof(SelectedListOperator)); - - _profileEditorService.UpdateSelectedProfileElement(); - } - - public void AddCondition(string type) - { - if (type == "Static") - DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Static)); - else if (type == "Dynamic") - DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Dynamic)); - - Update(); - _profileEditorService.UpdateSelectedProfileElement(); - } - - public void AddGroup() - { - DataModelConditionList.AddChild(new DataModelConditionGroup(DataModelConditionList)); - - Update(); - _profileEditorService.UpdateSelectedProfileElement(); - } - - public override void Delete() - { - base.Delete(); - _profileEditorService.UpdateSelectedProfileElement(); - } - - public void Initialize() - { - // Get the data models - TargetDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); - TargetDataModel.UpdateRequested += TargetDataModelUpdateRequested; - - Update(); - - _updateTimer.Start(); - _updateTimer.Elapsed += OnUpdateTimerOnElapsed; - - IsInitialized = true; - } - - public void ApplyList() - { - DataModelConditionList.UpdateList(SelectedListProperty.DataModel, SelectedListProperty.PropertyPath); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - public override void Update() - { - if (TargetDataModel == null) - return; - - NotifyOfPropertyChange(nameof(SelectedListOperator)); - - // Update the selected list property - if (DataModelConditionList.ListDataModel != null && DataModelConditionList.ListPropertyPath != null) - { - var child = TargetDataModel.GetChildByPath( - DataModelConditionList.ListDataModel.PluginInfo.Guid, - DataModelConditionList.ListPropertyPath - ); - SelectedListProperty = child as DataModelListViewModel; - } - - // Ensure filtering is applied to include Enumerables only - TargetDataModel.ApplyTypeFilter(true, typeof(IList)); - - // Remove VMs of effects no longer applied on the layer - var toRemove = Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList(); - // Using RemoveRange breaks our lovely animations - foreach (var displayConditionViewModel in toRemove) - Items.Remove(displayConditionViewModel); - - foreach (var childModel in Model.Children) - { - if (Items.Any(c => c.Model == childModel)) - continue; - if (!(childModel is DataModelConditionGroup displayConditionGroup)) - continue; - - var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, true); - viewModel.IsRootGroup = true; - Items.Add(viewModel); - } - - foreach (var childViewModel in Items) - childViewModel.Update(); - } - - private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) - { - if (TargetDataModelOpen) - { - TargetDataModel?.Update(_dataModelUIService); - SelectedListProperty?.Update(_dataModelUIService); - } - } - - private void TargetDataModelUpdateRequested(object sender, EventArgs e) - { - TargetDataModel.ApplyTypeFilter(true, typeof(IList)); - } - - - private void ExecuteSelectListProperty(object context) - { - if (!(context is DataModelListViewModel dataModelListViewModel)) - return; - - SelectedListProperty = dataModelListViewModel; - ApplyList(); - } - - public void Dispose() - { - _updateTimer.Dispose(); - _updateTimer.Elapsed -= OnUpdateTimerOnElapsed; - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml deleted file mode 100644 index 760d4cbcb..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs deleted file mode 100644 index aa86f1d6d..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs +++ /dev/null @@ -1,332 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Timers; -using System.Windows; -using System.Windows.Input; -using Artemis.Core; -using Artemis.Core.Services; -using Artemis.UI.Events; -using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; -using Artemis.UI.Shared; -using Artemis.UI.Shared.Services; -using Artemis.UI.Utilities; -using Stylet; - -namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions -{ - public class DisplayConditionPredicateViewModel : DisplayConditionViewModel, IHandle, IHandle, IDisposable - { - private readonly IConditionOperatorService _conditionOperatorService; - private readonly IDataModelUIService _dataModelUIService; - private readonly IEventAggregator _eventAggregator; - private readonly IProfileEditorService _profileEditorService; - private readonly Timer _updateTimer; - private bool _isInitialized; - private DataModelPropertiesViewModel _leftSideDataModel; - private BindableCollection _operators; - private DataModelPropertiesViewModel _rightSideDataModel; - private DataModelInputViewModel _rightSideInputViewModel; - private int _rightSideTransitionIndex; - private object _rightStaticValue; - private DataModelVisualizationViewModel _selectedLeftSideProperty; - private ConditionOperator _selectedOperator; - private DataModelVisualizationViewModel _selectedRightSideProperty; - - private List _supportedInputTypes; - - public DisplayConditionPredicateViewModel( - DataModelConditionPredicate dataModelConditionPredicate, - IProfileEditorService profileEditorService, - IDataModelUIService dataModelUIService, - IConditionOperatorService conditionOperatorService, - ISettingsService settingsService, - IEventAggregator eventAggregator) : base(dataModelConditionPredicate) - { - _profileEditorService = profileEditorService; - _dataModelUIService = dataModelUIService; - _conditionOperatorService = conditionOperatorService; - _eventAggregator = eventAggregator; - _updateTimer = new Timer(500); - _supportedInputTypes = new List(); - - SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty); - SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty); - SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); - Operators = new BindableCollection(); - - ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues"); - - Initialize(); - } - - public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model; - public bool ShowRightSidePropertySelection => DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic; - public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null; - public PluginSetting ShowDataModelValues { get; } - - public bool IsInitialized - { - get => _isInitialized; - private set => SetAndNotify(ref _isInitialized, value); - } - - public bool LeftSideDataModelOpen { get; set; } - - public DataModelPropertiesViewModel LeftSideDataModel - { - get => _leftSideDataModel; - set => SetAndNotify(ref _leftSideDataModel, value); - } - - public DataModelPropertiesViewModel RightSideDataModel - { - get => _rightSideDataModel; - set => SetAndNotify(ref _rightSideDataModel, value); - } - - public bool RightSideDataModelOpen { get; set; } - - public DataModelVisualizationViewModel SelectedLeftSideProperty - { - get => _selectedLeftSideProperty; - set - { - if (!SetAndNotify(ref _selectedLeftSideProperty, value)) return; - NotifyOfPropertyChange(nameof(CanActivateRightSideInputViewModel)); - } - } - - public DataModelVisualizationViewModel SelectedRightSideProperty - { - get => _selectedRightSideProperty; - set => SetAndNotify(ref _selectedRightSideProperty, value); - } - - public object RightStaticValue - { - get => _rightStaticValue; - set => SetAndNotify(ref _rightStaticValue, value); - } - - public int RightSideTransitionIndex - { - get => _rightSideTransitionIndex; - set => SetAndNotify(ref _rightSideTransitionIndex, value); - } - - public DataModelInputViewModel RightSideInputViewModel - { - get => _rightSideInputViewModel; - set => SetAndNotify(ref _rightSideInputViewModel, value); - } - - public BindableCollection Operators - { - get => _operators; - set => SetAndNotify(ref _operators, value); - } - - public ConditionOperator SelectedOperator - { - get => _selectedOperator; - set => SetAndNotify(ref _selectedOperator, value); - } - - public DelegateCommand SelectLeftPropertyCommand { get; } - public DelegateCommand SelectRightPropertyCommand { get; } - public DelegateCommand SelectOperatorCommand { get; } - - 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 override void Delete() - { - base.Delete(); - _profileEditorService.UpdateSelectedProfileElement(); - } - - public void Initialize() - { - // Get the data models - LeftSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); - RightSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); - - // Determine which types are currently supported - var editors = _dataModelUIService.RegisteredDataModelEditors; - _supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); - _supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes)); - - LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested; - RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested; - - Update(); - - _updateTimer.Start(); - _updateTimer.Elapsed += OnUpdateTimerOnElapsed; - - IsInitialized = true; - } - - public override void Update() - { - if (LeftSideDataModel == null || DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && RightSideDataModel == null) - return; - - // If static, only allow selecting properties also supported by input - if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Static) - LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray()); - - // Determine the left side property first - SelectedLeftSideProperty = LeftSideDataModel.GetChildForCondition(DataModelConditionPredicate, DisplayConditionSide.Left); - var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType; - - // Get the supported operators - Operators.Clear(); - Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType)); - if (DataModelConditionPredicate.Operator == null) - DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType))); - SelectedOperator = DataModelConditionPredicate.Operator; - - // Determine the right side - if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic) - { - SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DataModelConditionPredicate, DisplayConditionSide.Right); - RightSideDataModel.ApplyTypeFilter(true, leftSideType); - } - else - RightStaticValue = DataModelConditionPredicate.RightStaticValue; - } - - public void ApplyLeftSide() - { - DataModelConditionPredicate.UpdateLeftSide(SelectedLeftSideProperty.DataModel, SelectedLeftSideProperty.PropertyPath); - _profileEditorService.UpdateSelectedProfileElement(); - - SelectedOperator = DataModelConditionPredicate.Operator; - Update(); - } - - public void ApplyRightSideDynamic() - { - DataModelConditionPredicate.UpdateRightSide(SelectedRightSideProperty.DataModel, SelectedRightSideProperty.PropertyPath); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - public void ApplyRightSideStatic(object value, bool isSubmitted) - { - if (isSubmitted) - { - DataModelConditionPredicate.UpdateRightSide(value); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - RightSideTransitionIndex = 0; - RightSideInputViewModel = null; - RightStaticValue = value; - _eventAggregator.Unsubscribe(this); - } - - public void ApplyOperator() - { - DataModelConditionPredicate.UpdateOperator(SelectedOperator); - _profileEditorService.UpdateSelectedProfileElement(); - - Update(); - } - - public void ActivateRightSideInputViewModel() - { - if (SelectedLeftSideProperty?.PropertyInfo == null) - return; - - RightSideTransitionIndex = 1; - RightSideInputViewModel = _dataModelUIService.GetDataModelInputViewModel( - SelectedLeftSideProperty.PropertyInfo.PropertyType, - SelectedLeftSideProperty.PropertyDescription, - DataModelConditionPredicate.RightStaticValue, - ApplyRightSideStatic - ); - _eventAggregator.Subscribe(this); - } - - private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) - { - if (LeftSideDataModelOpen) - LeftSideDataModel.Update(_dataModelUIService); - else if (RightSideDataModelOpen) - RightSideDataModel.Update(_dataModelUIService); - } - - private void RightDataModelUpdateRequested(object sender, EventArgs e) - { - var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType; - if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic) - SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DataModelConditionPredicate, DisplayConditionSide.Right); - - // With the data model updated, also reapply the filter - RightSideDataModel.ApplyTypeFilter(true, leftSideType); - } - - private void LeftDataModelUpdateRequested(object sender, EventArgs e) - { - if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Static) - LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray()); - } - - private void ExecuteSelectLeftProperty(object context) - { - if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel)) - return; - - SelectedLeftSideProperty = dataModelVisualizationViewModel; - ApplyLeftSide(); - } - - private void ExecuteSelectRightProperty(object context) - { - if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel)) - return; - - SelectedRightSideProperty = dataModelVisualizationViewModel; - ApplyRightSideDynamic(); - } - - private void ExecuteSelectOperatorCommand(object context) - { - if (!(context is ConditionOperator displayConditionOperator)) - return; - - SelectedOperator = displayConditionOperator; - ApplyOperator(); - } - - public void Dispose() - { - _updateTimer.Dispose(); - _updateTimer.Elapsed -= OnUpdateTimerOnElapsed; - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs index 112aba813..b5f75e0c9 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs @@ -1,23 +1,24 @@ using System.Linq; using Artemis.Core; using Artemis.UI.Ninject.Factories; +using Artemis.UI.Screens.ProfileEditor.Conditions; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using Stylet; namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions { - public class DisplayConditionsViewModel : Conductor, IProfileEditorPanelViewModel + public class DisplayConditionsViewModel : Conductor, IProfileEditorPanelViewModel { - private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; + private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory; private readonly IProfileEditorService _profileEditorService; private RenderProfileElement _renderProfileElement; private bool _displayStartHint; - public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) + public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory) { _profileEditorService = profileEditorService; - _displayConditionsVmFactory = displayConditionsVmFactory; + _dataModelConditionsVmFactory = dataModelConditionsVmFactory; } public bool DisplayStartHint @@ -82,10 +83,10 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions } // Ensure the layer has a root display condition group - if (e.RenderProfileElement.DataModelConditionGroup == null) - e.RenderProfileElement.DataModelConditionGroup = new DataModelConditionGroup(null); + if (e.RenderProfileElement.DisplayCondition == null) + e.RenderProfileElement.DisplayCondition = new DataModelConditionGroup(null); - ActiveItem = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DataModelConditionGroup, false); + ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(e.RenderProfileElement.DisplayCondition, false); ActiveItem.IsRootGroup = true; ActiveItem.Update(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml index 86c5d3f85..8f6668f59 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml @@ -11,7 +11,7 @@ - + diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml index 554ed1a01..6f09ca838 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml @@ -14,7 +14,7 @@ - + @@ -51,7 +51,7 @@