diff --git a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs new file mode 100644 index 000000000..c862d90e1 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Artemis.Core.Models.Profile.Conditions.Abstract +{ + public abstract class DisplayConditionPart + { + private readonly List _children; + + protected DisplayConditionPart() + { + _children = new List(); + } + + public DisplayConditionPart Parent { get; set; } + public IReadOnlyList Children => _children.AsReadOnly(); + + public void AddChild(DisplayConditionPart displayConditionPart) + { + if (!_children.Contains(displayConditionPart)) + { + displayConditionPart.Parent = this; + _children.Add(displayConditionPart); + } + } + + public void RemoveChild(DisplayConditionPart displayConditionPart) + { + if (_children.Contains(displayConditionPart)) + { + displayConditionPart.Parent = null; + _children.Remove(displayConditionPart); + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/Abstract/LayerConditionPart.cs b/src/Artemis.Core/Models/Profile/Conditions/Abstract/LayerConditionPart.cs deleted file mode 100644 index 7293b8c5f..000000000 --- a/src/Artemis.Core/Models/Profile/Conditions/Abstract/LayerConditionPart.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Artemis.Core.Models.Profile.Conditions.Abstract -{ - public abstract class LayerConditionPart - { - protected LayerConditionPart() - { - Children = new List(); - } - - public List Children { get; set; } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/LayerCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayCondition.cs similarity index 71% rename from src/Artemis.Core/Models/Profile/Conditions/LayerCondition.cs rename to src/Artemis.Core/Models/Profile/Conditions/DisplayCondition.cs index 08236fd68..cf8f02ef5 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/LayerCondition.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayCondition.cs @@ -4,9 +4,9 @@ using Artemis.Core.Plugins.Abstract.DataModels; namespace Artemis.Core.Models.Profile.Conditions { - public class LayerCondition + public class DisplayCondition { public Expression> ExpressionTree { get; set; } - public LayerConditionGroup RootGroup { get; set; } + public DisplayConditionGroup RootGroup { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionGroup.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs similarity index 82% rename from src/Artemis.Core/Models/Profile/Conditions/LayerConditionGroup.cs rename to src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs index dbcc19ad4..6249df7fe 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionGroup.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs @@ -2,7 +2,7 @@ namespace Artemis.Core.Models.Profile.Conditions { - public class LayerConditionGroup : LayerConditionPart + public class DisplayConditionGroup : DisplayConditionPart { public BooleanOperator BooleanOperator { get; set; } } diff --git a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionListStatement.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs similarity index 79% rename from src/Artemis.Core/Models/Profile/Conditions/LayerConditionListStatement.cs rename to src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs index c50eeb815..38e8e4705 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionListStatement.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs @@ -2,7 +2,7 @@ namespace Artemis.Core.Models.Profile.Conditions { - public class LayerConditionListStatement : LayerConditionPart + public class DisplayConditionListPredicate : DisplayConditionPart { public ListOperator ListOperator { get; set; } diff --git a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs similarity index 97% rename from src/Artemis.Core/Models/Profile/Conditions/LayerConditionOperator.cs rename to src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs index 78f7630b1..e6301860a 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionOperator.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs @@ -6,7 +6,7 @@ using Artemis.Core.Services.Interfaces; namespace Artemis.Core.Models.Profile.Conditions { - public abstract class LayerConditionOperator + public abstract class DisplayConditionOperator { private IDataModelService _dataModelService; private bool _registered; diff --git a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionStatement.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs similarity index 68% rename from src/Artemis.Core/Models/Profile/Conditions/LayerConditionStatement.cs rename to src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs index 522dce6a5..c989d3602 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/LayerConditionStatement.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs @@ -3,12 +3,12 @@ using Artemis.Core.Models.Profile.Conditions.Abstract; namespace Artemis.Core.Models.Profile.Conditions { - public class LayerConditionStatement : LayerConditionPart + public class DisplayConditionPredicate : DisplayConditionPart { public Guid DataModelGuid { get; set; } public string PropertyPath { get; set; } - public LayerConditionOperator Operator { get; set; } + public DisplayConditionOperator Operator { get; set; } public object Value { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs index d98b8d19c..2d78469b0 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs @@ -4,7 +4,7 @@ using System.Linq.Expressions; namespace Artemis.Core.Models.Profile.Conditions.Operators { - public class GreaterThanConditionOperator : LayerConditionOperator + public class GreaterThanConditionOperator : DisplayConditionOperator { public override IReadOnlyCollection CompatibleTypes => new List { diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index e353f3852..ea58f3550 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Artemis.Core.Extensions; +using Artemis.Core.Models.Profile.Conditions; using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Models.Profile.LayerProperties.Attributes; using Artemis.Core.Models.Profile.LayerShapes; @@ -22,6 +23,7 @@ namespace Artemis.Core.Models.Profile /// public sealed class Layer : EffectProfileElement { + private DisplayConditionGroup _displayConditionGroup; private LayerGeneralProperties _general; private SKBitmap _layerBitmap; private BaseLayerBrush _layerBrush; @@ -115,6 +117,15 @@ namespace Artemis.Core.Models.Profile internal set => SetAndNotify(ref _layerBrush, value); } + /// + /// Gets or sets the root display condition group + /// + public DisplayConditionGroup DisplayConditionGroup + { + get => _displayConditionGroup; + set => SetAndNotify(ref _displayConditionGroup, value); + } + public override string ToString() { return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}"; diff --git a/src/Artemis.Core/Services/DataModelService.cs b/src/Artemis.Core/Services/DataModelService.cs index 1754d42c9..75cc7b729 100644 --- a/src/Artemis.Core/Services/DataModelService.cs +++ b/src/Artemis.Core/Services/DataModelService.cs @@ -21,13 +21,13 @@ namespace Artemis.Core.Services { private readonly List _dataModelExpansions; private readonly IPluginService _pluginService; - private readonly List _registeredConditionOperators; + private readonly List _registeredConditionOperators; internal DataModelService(IPluginService pluginService) { _pluginService = pluginService; _dataModelExpansions = new List(); - _registeredConditionOperators = new List(); + _registeredConditionOperators = new List(); _pluginService.PluginEnabled += PluginServiceOnPluginEnabled; _pluginService.PluginDisabled += PluginServiceOnPluginDisabled; @@ -81,40 +81,50 @@ namespace Artemis.Core.Services return null; } + public bool GetPluginExtendsDataModel(Plugin plugin) + { + if (plugin is Module module) + return module.InternalExpandsMainDataModel; + if (plugin is BaseDataModelExpansion) + return true; - public void RegisterConditionOperator(PluginInfo pluginInfo, LayerConditionOperator layerConditionOperator) + return false; + } + + + public void RegisterConditionOperator(PluginInfo pluginInfo, DisplayConditionOperator displayConditionOperator) { if (pluginInfo == null) throw new ArgumentNullException(nameof(pluginInfo)); - if (layerConditionOperator == null) - throw new ArgumentNullException(nameof(layerConditionOperator)); + if (displayConditionOperator == null) + throw new ArgumentNullException(nameof(displayConditionOperator)); lock (_registeredConditionOperators) { - if (_registeredConditionOperators.Contains(layerConditionOperator)) + if (_registeredConditionOperators.Contains(displayConditionOperator)) return; - layerConditionOperator.Register(pluginInfo, this); - _registeredConditionOperators.Add(layerConditionOperator); + displayConditionOperator.Register(pluginInfo, this); + _registeredConditionOperators.Add(displayConditionOperator); } } - public void RemoveConditionOperator(LayerConditionOperator layerConditionOperator) + public void RemoveConditionOperator(DisplayConditionOperator displayConditionOperator) { - if (layerConditionOperator == null) - throw new ArgumentNullException(nameof(layerConditionOperator)); + if (displayConditionOperator == null) + throw new ArgumentNullException(nameof(displayConditionOperator)); lock (_registeredConditionOperators) { - if (!_registeredConditionOperators.Contains(layerConditionOperator)) + if (!_registeredConditionOperators.Contains(displayConditionOperator)) return; - layerConditionOperator.Unsubscribe(); - _registeredConditionOperators.Remove(layerConditionOperator); + displayConditionOperator.Unsubscribe(); + _registeredConditionOperators.Remove(displayConditionOperator); } } - public List GetCompatibleConditionOperators(Type type) + public List GetCompatibleConditionOperators(Type type) { lock (_registeredConditionOperators) { diff --git a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs index 6f2fe6ba0..57a7f27d1 100644 --- a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs +++ b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs @@ -29,17 +29,24 @@ namespace Artemis.Core.Services.Interfaces /// Should be a module with a data model or a data model expansion DataModel GetPluginDataModel(Plugin plugin); + /// + /// Determines whether the given plugin expands the main data model + /// + /// + /// + bool GetPluginExtendsDataModel(Plugin plugin); + /// /// Registers a new condition operator for use in layer conditions /// /// The PluginInfo of the plugin this condition operator belongs to - /// The condition operator to register - void RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] LayerConditionOperator layerConditionOperator); + /// The condition operator to register + void RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] DisplayConditionOperator displayConditionOperator); /// /// Removes a condition operator so it is no longer available for use in layer conditions /// - /// The layer condition operator to remove - void RemoveConditionOperator([NotNull] LayerConditionOperator layerConditionOperator); + /// The layer condition operator to remove + void RemoveConditionOperator([NotNull] DisplayConditionOperator displayConditionOperator); } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs new file mode 100644 index 000000000..4af4f9b0d --- /dev/null +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Reflection; +using Artemis.Core.Plugins.Abstract.DataModels; +using Artemis.UI.Shared.Services; + +namespace Artemis.UI.Shared.DataModelVisualization.Shared +{ + public class DataModelListPropertiesViewModel : DataModelPropertiesViewModel + { + private object _displayValue; + private int _index; + private Type _listType; + + public DataModelListPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo) + { + } + + public int Index + { + get => _index; + set => SetAndNotify(ref _index, value); + } + + public Type ListType + { + get => _listType; + set => SetAndNotify(ref _listType, value); + } + + public object DisplayValue + { + get => _displayValue; + set => SetAndNotify(ref _displayValue, value); + } + + public override void Update(IDataModelVisualizationService dataModelVisualizationService) + { + // Display value gets updated by parent, don't do anything if it is null + if (DisplayValue == null) + return; + + ListType = DisplayValue.GetType(); + PopulateProperties(dataModelVisualizationService); + foreach (var dataModelVisualizationViewModel in Children) + dataModelVisualizationViewModel.Update(dataModelVisualizationService); + } + + public override object GetCurrentValue() + { + return DisplayValue; + } + + private void PopulateProperties(IDataModelVisualizationService dataModelVisualizationService) + { + if (Children.Any()) + return; + + foreach (var propertyInfo in ListType.GetProperties()) + { + var child = CreateChild(dataModelVisualizationService, propertyInfo); + if (child != null) + Children.Add(child); + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs new file mode 100644 index 000000000..434082dae --- /dev/null +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using System.Reflection; +using Artemis.Core.Plugins.Abstract.DataModels; +using Artemis.UI.Shared.Services; + +namespace Artemis.UI.Shared.DataModelVisualization.Shared +{ + public class DataModelListPropertyViewModel : DataModelPropertyViewModel + { + private int _index; + private Type _listType; + + public DataModelListPropertyViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo) + { + } + + public int Index + { + get => _index; + set => SetAndNotify(ref _index, value); + } + + public Type ListType + { + get => _listType; + set => SetAndNotify(ref _listType, value); + } + + public override object GetCurrentValue() + { + return DisplayValue; + } + + public override void Update(IDataModelVisualizationService dataModelVisualizationService) + { + // Display value gets updated by parent, don't do anything if it is null + if (DisplayValue == null) + return; + + if (DisplayViewModel == null && dataModelVisualizationService.RegisteredDataModelDisplays.Any(d => d.SupportedType == DisplayValue.GetType())) + dataModelVisualizationService.GetDataModelDisplayViewModel(DisplayValue.GetType()); + + ListType = DisplayValue.GetType(); + UpdateDisplayParameters(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs index e61194c73..3cd9a68f6 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs @@ -1,7 +1,8 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections; using System.Reflection; +using Artemis.Core.Extensions; +using Artemis.Core.Plugins.Abstract.DataModels; using Artemis.Core.Plugins.Abstract.DataModels.Attributes; using Artemis.UI.Shared.Services; using Stylet; @@ -10,25 +11,12 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared { public class DataModelListViewModel : DataModelVisualizationViewModel { - private readonly IDataModelVisualizationService _dataModelVisualizationService; - private BindableCollection _children; private string _count; private IList _list; - internal DataModelListViewModel(PropertyInfo propertyInfo, DataModelPropertyAttribute propertyDescription, DataModelVisualizationViewModel parent, - IDataModelVisualizationService dataModelVisualizationService) + internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo) { - _dataModelVisualizationService = dataModelVisualizationService; - PropertyInfo = propertyInfo; - Parent = parent; - PropertyDescription = propertyDescription; - Children = new BindableCollection(); - } - - public BindableCollection Children - { - get => _children; - set => SetAndNotify(ref _children, value); + ListChildren = new BindableCollection(); } public IList List @@ -37,43 +25,67 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared set => SetAndNotify(ref _list, value); } + public BindableCollection ListChildren { get; set; } + public string Count { get => _count; set => SetAndNotify(ref _count, value); } - public override void Update() + public override void Update(IDataModelVisualizationService dataModelVisualizationService) { - if (PropertyInfo != null && Parent?.Model != null && PropertyInfo.GetValue(Parent.Model) is IList listValue) - { - Model = new List(listValue.Cast()); - List = (IList) Model; - } + List = GetCurrentValue() as IList; + if (List == null) + return; var index = 0; foreach (var item in List) { DataModelVisualizationViewModel child; - if (Children.Count <= index) + if (ListChildren.Count <= index) { - child = CreateChild(_dataModelVisualizationService, item); - Children.Add(child); + child = CreateListChild(dataModelVisualizationService, item.GetType()); + ListChildren.Add(child); } else + child = ListChildren[index]; + + if (child is DataModelListPropertiesViewModel dataModelListClassViewModel) { - child = Children[index]; - child.Model = item; + dataModelListClassViewModel.DisplayValue = item; + dataModelListClassViewModel.Index = index; + } + else if (child is DataModelListPropertyViewModel dataModelListPropertyViewModel) + { + dataModelListPropertyViewModel.DisplayValue = item; + dataModelListPropertyViewModel.Index = index; } - child.Update(); + child.Update(dataModelVisualizationService); index++; } - while (Children.Count > List.Count) - Children.RemoveAt(Children.Count - 1); + while (ListChildren.Count > List.Count) + ListChildren.RemoveAt(ListChildren.Count - 1); - Count = $"{Children.Count} {(Children.Count == 1 ? "item" : "items")}"; + Count = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}"; + } + + protected DataModelVisualizationViewModel CreateListChild(IDataModelVisualizationService dataModelVisualizationService, Type listType) + { + // If a display VM was found, prefer to use that in any case + var typeViewModel = dataModelVisualizationService.GetDataModelDisplayViewModel(listType); + if (typeViewModel != null) + return new DataModelListPropertyViewModel(DataModel, this, PropertyInfo) {DisplayViewModel = typeViewModel}; + // For primitives, create a property view model, it may be null that is fine + if (listType.IsPrimitive || listType == typeof(string)) + return new DataModelListPropertyViewModel(DataModel, this, PropertyInfo); + // For other value types create a child view model + if (listType.IsClass || listType.IsStruct()) + return new DataModelListPropertiesViewModel(DataModel, this, PropertyInfo); + + return null; } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs new file mode 100644 index 000000000..b606ce2bc --- /dev/null +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs @@ -0,0 +1,42 @@ +using System.Linq; +using System.Reflection; +using Artemis.Core.Plugins.Abstract.DataModels; +using Artemis.UI.Shared.Services; + +namespace Artemis.UI.Shared.DataModelVisualization.Shared +{ + public class DataModelPropertiesViewModel : DataModelVisualizationViewModel + { + internal DataModelPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo) + { + } + + public override void Update(IDataModelVisualizationService dataModelVisualizationService) + { + PopulateProperties(dataModelVisualizationService); + foreach (var dataModelVisualizationViewModel in Children) + dataModelVisualizationViewModel.Update(dataModelVisualizationService); + } + + public override object GetCurrentValue() + { + return Parent.IsRootViewModel ? DataModel : base.GetCurrentValue(); + } + + + + private void PopulateProperties(IDataModelVisualizationService dataModelVisualizationService) + { + if (Children.Any()) + return; + + var modelType = Parent.IsRootViewModel ? DataModel.GetType() : PropertyInfo.PropertyType; + foreach (var propertyInfo in modelType.GetProperties()) + { + var child = CreateChild(dataModelVisualizationService, propertyInfo); + if (child != null) + Children.Add(child); + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs index cac4d9424..5ec5c3683 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs @@ -1,20 +1,26 @@ -using System.Reflection; -using Artemis.Core.Plugins.Abstract.DataModels.Attributes; +using System.Linq; +using System.Reflection; +using Artemis.Core.Plugins.Abstract.DataModels; +using Artemis.UI.Shared.Services; namespace Artemis.UI.Shared.DataModelVisualization.Shared { public class DataModelPropertyViewModel : DataModelVisualizationViewModel { + private object _displayValue; private DataModelDisplayViewModel _displayViewModel; private bool _showNull; private bool _showToString; private bool _showViewModel; - internal DataModelPropertyViewModel(PropertyInfo propertyInfo, DataModelPropertyAttribute propertyDescription, DataModelVisualizationViewModel parent) + internal DataModelPropertyViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo) { - PropertyInfo = propertyInfo; - Parent = parent; - PropertyDescription = propertyDescription; + } + + public object DisplayValue + { + get => _displayValue; + set => SetAndNotify(ref _displayValue, value); } public DataModelDisplayViewModel DisplayViewModel @@ -41,19 +47,22 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared set => SetAndNotify(ref _showViewModel, value); } - public override void Update() + public override void Update(IDataModelVisualizationService dataModelVisualizationService) { - if (PropertyInfo != null && Parent?.Model != null) - { - Model = PropertyInfo.GetValue(Parent.Model); - DisplayViewModel?.UpdateValue(Model); - } + if (DisplayViewModel == null && dataModelVisualizationService.RegisteredDataModelDisplays.Any(d => d.SupportedType == PropertyInfo.PropertyType)) + dataModelVisualizationService.GetDataModelDisplayViewModel(PropertyInfo.PropertyType); - ShowToString = Model != null && DisplayViewModel == null; - ShowNull = Model == null; - ShowViewModel = Model != null && DisplayViewModel != null; + DisplayValue = GetCurrentValue(); + UpdateDisplayParameters(); + } - UpdateListStatus(); + protected void UpdateDisplayParameters() + { + ShowToString = DisplayValue != null && DisplayViewModel == null; + ShowNull = DisplayValue == null; + ShowViewModel = DisplayValue != null && DisplayViewModel != null; + + DisplayViewModel?.UpdateValue(DisplayValue); } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelViewModel.cs deleted file mode 100644 index e93913225..000000000 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelViewModel.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Reflection; -using Artemis.Core.Extensions; -using Artemis.Core.Plugins.Abstract.DataModels.Attributes; -using Artemis.UI.Shared.Services; -using Stylet; - -namespace Artemis.UI.Shared.DataModelVisualization.Shared -{ - public class DataModelViewModel : DataModelVisualizationViewModel - { - private readonly IDataModelVisualizationService _dataModelVisualizationService; - private BindableCollection _children; - - internal DataModelViewModel() - { - Children = new BindableCollection(); - } - - internal DataModelViewModel(PropertyInfo propertyInfo, object model, DataModelPropertyAttribute propertyDescription, DataModelVisualizationViewModel parent, - IDataModelVisualizationService dataModelVisualizationService) - { - _dataModelVisualizationService = dataModelVisualizationService; - PropertyInfo = propertyInfo; - Model = model; - PropertyDescription = propertyDescription; - Parent = parent; - Children = new BindableCollection(); - - PopulateProperties(); - } - - public BindableCollection Children - { - get => _children; - set => SetAndNotify(ref _children, value); - } - - public void PopulateProperties() - { - Children.Clear(); - foreach (var propertyInfo in Model.GetType().GetProperties()) - { - var child = CreateChild(_dataModelVisualizationService, propertyInfo); - if (child != null) - Children.Add(child); - } - } - - public override void Update() - { - if (PropertyInfo != null && PropertyInfo.PropertyType.IsStruct()) - Model = PropertyInfo.GetValue(Parent.Model); - - foreach (var dataModelVisualizationViewModel in Children) - dataModelVisualizationViewModel.Update(); - - UpdateListStatus(); - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs index 1bb7f96b8..4c14e8c0f 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs @@ -1,8 +1,11 @@ using System; using System.Collections; +using System.Linq; using System.Reflection; using Artemis.Core.Extensions; +using Artemis.Core.Plugins.Abstract.DataModels; using Artemis.Core.Plugins.Abstract.DataModels.Attributes; +using Artemis.UI.Shared.Exceptions; using Artemis.UI.Shared.Services; using Humanizer; using Stylet; @@ -11,22 +14,31 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared { public abstract class DataModelVisualizationViewModel : PropertyChangedBase { - private bool _isListProperty; - private string _listDescription; - private object _model; + private BindableCollection _children; + private DataModel _dataModel; private DataModelVisualizationViewModel _parent; private DataModelPropertyAttribute _propertyDescription; private PropertyInfo _propertyInfo; - private Type _propertyType; - internal DataModelVisualizationViewModel() + internal DataModelVisualizationViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) { + DataModel = dataModel; + PropertyInfo = propertyInfo; + Parent = parent; + Children = new BindableCollection(); + + if (dataModel == null && parent == null && propertyInfo == null) + IsRootViewModel = true; + else + GetDescription(); } - public DataModelPropertyAttribute PropertyDescription + public bool IsRootViewModel { get; } + + public DataModel DataModel { - get => _propertyDescription; - protected set => SetAndNotify(ref _propertyDescription, value); + get => _dataModel; + set => SetAndNotify(ref _dataModel, value); } public PropertyInfo PropertyInfo @@ -35,10 +47,10 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared protected set => SetAndNotify(ref _propertyInfo, value); } - public Type PropertyType + public DataModelPropertyAttribute PropertyDescription { - get => _propertyType; - set => SetAndNotify(ref _propertyType, value); + get => _propertyDescription; + protected set => SetAndNotify(ref _propertyDescription, value); } public DataModelVisualizationViewModel Parent @@ -47,25 +59,53 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared protected set => SetAndNotify(ref _parent, value); } - public object Model + public BindableCollection Children { - get => _model; - set => SetAndNotify(ref _model, value); + get => _children; + set => SetAndNotify(ref _children, value); } - public bool IsListProperty + public abstract void Update(IDataModelVisualizationService dataModelVisualizationService); + + public virtual object GetCurrentValue() { - get => _isListProperty; - set => SetAndNotify(ref _isListProperty, value); + return Parent == null ? null : PropertyInfo.GetValue(Parent.GetCurrentValue()); } - public string ListDescription + public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath) { - get => _listDescription; - set => SetAndNotify(ref _listDescription, value); + var path = propertyPath.Split("."); + var currentPart = path.First(); + + if (IsRootViewModel) + { + var child = Children.FirstOrDefault(c => c.DataModel.PluginInfo.Guid == dataModelGuid); + return child?.GetChildByPath(dataModelGuid, propertyPath); + } + else + { + var child = Children.FirstOrDefault(c => c.DataModel.PluginInfo.Guid == dataModelGuid && c.PropertyInfo?.Name == currentPart); + if (child == null) + return null; + + if (path.Length > 1) + return child.GetChildByPath(dataModelGuid, string.Join(".", path.Skip(1))); + return child; + } + } - public abstract void Update(); + public string GetCurrentPath() + { + if (Parent == null) + return PropertyInfo?.Name; + + if (PropertyInfo == null) + return Parent.GetCurrentPath(); + + var parentPath = Parent.GetCurrentPath(); + return parentPath != null ? $"{parentPath}.{PropertyInfo.Name}" : PropertyInfo.Name; + } protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo) { @@ -73,64 +113,33 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared if (Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute))) return null; - var dataModelPropertyAttribute = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(DataModelPropertyAttribute)); - // If no DataModelProperty attribute was provided, pull one out of our ass - if (dataModelPropertyAttribute == null) - dataModelPropertyAttribute = new DataModelPropertyAttribute {Name = propertyInfo.Name.Humanize()}; - // If a display VM was found, prefer to use that in any case var typeViewModel = dataModelVisualizationService.GetDataModelDisplayViewModel(propertyInfo.PropertyType); if (typeViewModel != null) - return new DataModelPropertyViewModel(propertyInfo, dataModelPropertyAttribute, this) {DisplayViewModel = typeViewModel}; + return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {DisplayViewModel = typeViewModel}; // For primitives, create a property view model, it may be null that is fine if (propertyInfo.PropertyType.IsPrimitive || propertyInfo.PropertyType == typeof(string)) - return new DataModelPropertyViewModel(propertyInfo, dataModelPropertyAttribute, this); + return new DataModelPropertyViewModel(DataModel, this, propertyInfo); if (typeof(IList).IsAssignableFrom(propertyInfo.PropertyType)) - return new DataModelListViewModel(propertyInfo, dataModelPropertyAttribute, this, dataModelVisualizationService); - // For other value types create a child view model if the value type is not null + return new DataModelListViewModel(DataModel, this, propertyInfo); + // For other value types create a child view model if (propertyInfo.PropertyType.IsClass || propertyInfo.PropertyType.IsStruct()) - { - var value = propertyInfo.GetValue(Model); - if (value == null) - return null; - - return new DataModelViewModel(propertyInfo, value, dataModelPropertyAttribute, this, dataModelVisualizationService); - } + return new DataModelPropertiesViewModel(DataModel, this, propertyInfo); return null; } - protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, object value) + private void GetDescription() { - var dataModelPropertyAttribute = new DataModelPropertyAttribute {Name = "Unknown property"}; - - // If a display VM was found, prefer to use that in any case - var typeViewModel = dataModelVisualizationService.GetDataModelDisplayViewModel(value.GetType()); - if (typeViewModel != null) - return new DataModelPropertyViewModel(null, dataModelPropertyAttribute, this) {Model = value, DisplayViewModel = typeViewModel}; - // For primitives, create a property view model, it may be null that is fine - if (value.GetType().IsPrimitive || value is string) - return new DataModelPropertyViewModel(null, dataModelPropertyAttribute, this) {Model = value}; - // For other value types create a child view model if the value type is not null - if (value.GetType().IsClass || value.GetType().IsStruct()) - return new DataModelViewModel(null, value, dataModelPropertyAttribute, this, dataModelVisualizationService); - - return null; - } - - protected void UpdateListStatus() - { - if (Parent is DataModelListViewModel listViewModel) - { - IsListProperty = true; - ListDescription = $"List item [{listViewModel.List.IndexOf(Model)}]"; - PropertyType = Model.GetType(); - } + // If this is the first child of a root view model, use the data model description + if (Parent.IsRootViewModel) + PropertyDescription = DataModel?.DataModelDescription; + // Rely on property info for the description + else if (PropertyInfo != null) + PropertyDescription = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(PropertyInfo, typeof(DataModelPropertyAttribute)) ?? + new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize()}; else - { - IsListProperty = false; - PropertyType = PropertyInfo?.PropertyType; - } + throw new ArtemisSharedUIException("Failed to get property description because plugin info is null but the parent has a datamodel"); } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Exceptions/ArtemisUIException.cs b/src/Artemis.UI.Shared/Exceptions/ArtemisUIException.cs new file mode 100644 index 000000000..76b7ec01a --- /dev/null +++ b/src/Artemis.UI.Shared/Exceptions/ArtemisUIException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Artemis.UI.Shared.Exceptions +{ + public class ArtemisSharedUIException : Exception + { + internal ArtemisSharedUIException() + { + } + + internal ArtemisSharedUIException(string message) : base(message) + { + } + + internal ArtemisSharedUIException(string message, Exception inner) : base(message, inner) + { + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs b/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs index 9907b02e6..af71fa7df 100644 --- a/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs +++ b/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs @@ -18,6 +18,8 @@ namespace Artemis.UI.Shared.Services private readonly IKernel _kernel; private readonly List _registeredDataModelDisplays; private readonly List _registeredDataModelEditors; + private DataModelPropertiesViewModel _cachedMainDataModel; + private Dictionary _cachedDataModels; public DataModelVisualizationService(IDataModelService dataModelService, IKernel kernel) { @@ -25,28 +27,66 @@ namespace Artemis.UI.Shared.Services _kernel = kernel; _registeredDataModelEditors = new List(); _registeredDataModelDisplays = new List(); + _cachedDataModels = new Dictionary(); } - public DataModelViewModel GetMainDataModelVisualization() + public IReadOnlyCollection RegisteredDataModelEditors => _registeredDataModelEditors.AsReadOnly(); + public IReadOnlyCollection RegisteredDataModelDisplays => _registeredDataModelDisplays.AsReadOnly(); + + public DataModelPropertiesViewModel GetMainDataModelVisualization(bool useCache) { - var viewModel = new DataModelViewModel(); + // Return from cache if found + if (useCache && _cachedMainDataModel != null) + return _cachedMainDataModel; + + var viewModel = new DataModelPropertiesViewModel(null, null, null); foreach (var dataModelExpansion in _dataModelService.DataModelExpansions) - viewModel.Children.Add(new DataModelViewModel(null, dataModelExpansion, dataModelExpansion.DataModelDescription, viewModel, this)); + viewModel.Children.Add(new DataModelPropertiesViewModel(dataModelExpansion, viewModel, null)); + + // Update to populate children + viewModel.Update(this); + + // Add to cache + _cachedMainDataModel = viewModel; return viewModel; } - public DataModelViewModel GetPluginDataModelVisualization(Plugin plugin) + public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool useCache) { + // Return from cache if found + var isCached = _cachedDataModels.TryGetValue(plugin, out var cachedMainDataModel); + if (useCache && isCached) + return cachedMainDataModel; + var dataModel = _dataModelService.GetPluginDataModel(plugin); if (dataModel == null) return null; - var viewModel = new DataModelViewModel(); - viewModel.Children.Add(new DataModelViewModel(null, dataModel, dataModel.DataModelDescription, viewModel, this)); + var viewModel = new DataModelPropertiesViewModel(null, null, null); + viewModel.Children.Add(new DataModelPropertiesViewModel(dataModel, viewModel, null)); + + // Update to populate children + viewModel.Update(this); + + // Add to cache + if (!isCached) + _cachedDataModels.Add(plugin, viewModel); + return viewModel; } + public bool GetPluginExtendsDataModel(Plugin plugin) + { + return _dataModelService.GetPluginExtendsDataModel(plugin); + } + + public void BustCache() + { + _cachedMainDataModel = null; + _cachedDataModels.Clear(); + } + public DataModelVisualizationRegistration RegisterDataModelInput(PluginInfo pluginInfo) where T : DataModelInputViewModel { var viewModelType = typeof(T); @@ -133,8 +173,17 @@ namespace Artemis.UI.Shared.Services public interface IDataModelVisualizationService : IArtemisSharedUIService { - DataModelViewModel GetMainDataModelVisualization(); - DataModelViewModel GetPluginDataModelVisualization(Plugin plugin); + DataModelPropertiesViewModel GetMainDataModelVisualization(bool useCached); + DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool useCached); + + /// + /// Determines whether the given plugin expands the main data model + /// + /// + /// + bool GetPluginExtendsDataModel(Plugin plugin); + + void BustCache(); DataModelVisualizationRegistration RegisterDataModelInput(PluginInfo pluginInfo) where T : DataModelInputViewModel; DataModelVisualizationRegistration RegisterDataModelDisplay(PluginInfo pluginInfo) where T : DataModelDisplayViewModel; @@ -142,5 +191,7 @@ namespace Artemis.UI.Shared.Services void RemoveDataModelDisplay(DataModelVisualizationRegistration registration); DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType); + IReadOnlyCollection RegisteredDataModelEditors { get; } + IReadOnlyCollection RegisteredDataModelDisplays { get; } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs index bc40f25fe..5633ecdb6 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs @@ -27,6 +27,7 @@ namespace Artemis.UI.Shared.Services.Interfaces void RedoUpdateProfile(ProfileModule module); void StopRegularRender(); void ResumeRegularRender(); + Module GetCurrentModule(); /// /// Occurs when a new profile is selected diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs index f48cd18ef..18347f427 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs @@ -189,6 +189,11 @@ namespace Artemis.UI.Shared.Services } } + public Module GetCurrentModule() + { + return (Module) SelectedProfile?.PluginInfo.Instance; + } + public event EventHandler ProfileSelected; public event EventHandler SelectedProfileUpdated; public event EventHandler ProfileElementSelected; diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs index 0473f8167..06062b3bb 100644 --- a/src/Artemis.UI/Bootstrapper.cs +++ b/src/Artemis.UI/Bootstrapper.cs @@ -34,7 +34,7 @@ namespace Artemis.UI protected override void Launch() { - var test = new LayerCondition(); + var test = new DisplayCondition(); StartupArguments = Args.ToList(); diff --git a/src/Artemis.UI/Exceptions/ArtemisCoreException.cs b/src/Artemis.UI/Exceptions/ArtemisUIException.cs similarity index 88% rename from src/Artemis.UI/Exceptions/ArtemisCoreException.cs rename to src/Artemis.UI/Exceptions/ArtemisUIException.cs index 2d7c6c97b..7d5638eda 100644 --- a/src/Artemis.UI/Exceptions/ArtemisCoreException.cs +++ b/src/Artemis.UI/Exceptions/ArtemisUIException.cs @@ -2,7 +2,6 @@ namespace Artemis.UI.Exceptions { - // ReSharper disable once InconsistentNaming public class ArtemisUIException : Exception { public ArtemisUIException() diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index 8eb699bbc..46772a6c6 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -1,9 +1,12 @@ using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.Conditions; using Artemis.Core.Models.Profile.LayerProperties.Attributes; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.Abstract; using Artemis.UI.Screens.Module; using Artemis.UI.Screens.Module.ProfileEditor; +using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions; +using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.LayerEffects; @@ -71,6 +74,12 @@ namespace Artemis.UI.Ninject.Factories SelectionToolViewModel SelectionToolViewModel(ProfileViewModel profileViewModel); SelectionRemoveToolViewModel SelectionRemoveToolViewModel(ProfileViewModel profileViewModel); } + public interface IDisplayConditionsVmFactory : IVmFactory + { + DisplayConditionGroupViewModel DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent); + DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate, DisplayConditionViewModel parent); + DisplayConditionPredicateViewModel DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent); + } public interface ILayerPropertyVmFactory : IVmFactory { diff --git a/src/Artemis.UI/ResourceDictionaries/DisplayConditions.xaml b/src/Artemis.UI/ResourceDictionaries/DisplayConditions.xaml index 74600c8d0..543667e40 100644 --- a/src/Artemis.UI/ResourceDictionaries/DisplayConditions.xaml +++ b/src/Artemis.UI/ResourceDictionaries/DisplayConditions.xaml @@ -22,7 +22,7 @@ - + + + + + + + diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs index 11e29b839..1331406f1 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs @@ -1,24 +1,80 @@ -using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract; +using System; +using System.Linq; +using Artemis.Core.Models.Profile.Conditions; +using Artemis.UI.Ninject.Factories; +using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract; +using Humanizer; namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions { public class DisplayConditionGroupViewModel : DisplayConditionViewModel { + private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; private bool _isRootGroup; + public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(displayConditionGroup, parent) + { + _displayConditionsVmFactory = displayConditionsVmFactory; + } + + public DisplayConditionGroup DisplayConditionGroup => (DisplayConditionGroup) Model; + public bool IsRootGroup { get => _isRootGroup; set => SetAndNotify(ref _isRootGroup, value); } - public DisplayConditionGroupViewModel() + public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize(); + + public void SelectBooleanOperator(string type) { + var enumValue = Enum.Parse(type); + DisplayConditionGroup.BooleanOperator = enumValue; + NotifyOfPropertyChange(nameof(SelectedBooleanOperator)); + } + + public void AddCondition() + { + DisplayConditionGroup.AddChild(new DisplayConditionPredicate()); + Update(); + } + + public void AddGroup() + { + DisplayConditionGroup.AddChild(new DisplayConditionGroup()); + Update(); } public override void Update() { - + NotifyOfPropertyChange(nameof(SelectedBooleanOperator)); + + // Remove VMs of effects no longer applied on the layer + var toRemove = Children.Where(c => !DisplayConditionGroup.Children.Contains(c.Model)).ToList(); + Children.RemoveRange(toRemove); + + foreach (var childModel in Model.Children) + { + if (Children.Any(c => c.Model == childModel)) + continue; + + switch (childModel) + { + case DisplayConditionGroup displayConditionGroup: + Children.Add(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this)); + break; + case DisplayConditionListPredicate displayConditionListPredicate: + Children.Add(_displayConditionsVmFactory.DisplayConditionListPredicateViewModel(displayConditionListPredicate, this)); + break; + case DisplayConditionPredicate displayConditionPredicate: + Children.Add(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate, this)); + break; + } + } + + foreach (var childViewModel in Children) + childViewModel.Update(); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml new file mode 100644 index 000000000..67adf960d --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionListPredicateView.xaml @@ -0,0 +1,13 @@ + + + + + diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs new file mode 100644 index 000000000..5b3d981ba --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs @@ -0,0 +1,18 @@ +using Artemis.Core.Models.Profile.Conditions; +using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract; + +namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions +{ + public class DisplayConditionListPredicateViewModel : DisplayConditionViewModel + { + public DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate, DisplayConditionViewModel parent) : base(displayConditionListPredicate, parent) + { + } + + public DisplayConditionListPredicate DisplayConditionListPredicate => (DisplayConditionListPredicate) Model; + + public override void Update() + { + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml index a5b8ed03a..8df3ecc18 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml @@ -1,16 +1,28 @@ - + - + + + + + + + + + @@ -25,60 +37,45 @@ HorizontalAlignment="Left" Foreground="#E74C4C" Width="25" - Height="25"> + Height="25" + Command="{s:Action Delete}"> + + BorderBrush="{DynamicResource PrimaryHueMidBrush}" Content="PredicateRightSide" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs index 4aefbb41c..63a1cba5c 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs @@ -1,16 +1,86 @@ -using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract; +using Artemis.Core.Models.Profile.Conditions; +using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract; +using Artemis.UI.Shared.DataModelVisualization.Shared; +using Artemis.UI.Shared.Services; +using Artemis.UI.Shared.Services.Interfaces; +using Artemis.UI.Utilities; namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions { public class DisplayConditionPredicateViewModel : DisplayConditionViewModel { - public DisplayConditionPredicateViewModel() + private readonly IDataModelVisualizationService _dataModelVisualizationService; + private readonly IProfileEditorService _profileEditorService; + private DataModelPropertiesViewModel _dataModel; + private DataModelVisualizationViewModel _selectedLeftSideProperty; + private bool _leftSidePropertySelectionOpen; + + public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, + IProfileEditorService profileEditorService, IDataModelVisualizationService dataModelVisualizationService) : base(displayConditionPredicate, parent) { - + _profileEditorService = profileEditorService; + _dataModelVisualizationService = dataModelVisualizationService; + + SelectPropertyCommand = new DelegateCommand(ExecuteSelectProperty); + + GetDataModel(); + } + + public DelegateCommand SelectPropertyCommand { get; } + + public DataModelPropertiesViewModel DataModel + { + get => _dataModel; + set => SetAndNotify(ref _dataModel, value); + } + + public DataModelVisualizationViewModel SelectedLeftSideProperty + { + get => _selectedLeftSideProperty; + set => SetAndNotify(ref _selectedLeftSideProperty, value); + } + + public bool LeftSidePropertySelectionOpen + { + get => _leftSidePropertySelectionOpen; + set => SetAndNotify(ref _leftSidePropertySelectionOpen, value); + } + + + public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model; + + public void ToggleLeftSidePropertySelectionOpen() + { + LeftSidePropertySelectionOpen = !LeftSidePropertySelectionOpen; + } + + public void GetDataModel() + { + var mainDataModel = _dataModelVisualizationService.GetMainDataModelVisualization(true); + if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule())) + mainDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true)); + + DataModel = mainDataModel; + + Update(); } public override void Update() { + if (DisplayConditionPredicate.PropertyPath != null) + SelectedLeftSideProperty = DataModel.GetChildByPath(DisplayConditionPredicate.DataModelGuid, DisplayConditionPredicate.PropertyPath); + else + SelectedLeftSideProperty = null; + } + + private void ExecuteSelectProperty(object context) + { + if (!(context is DataModelVisualizationViewModel vm)) + return; + + DisplayConditionPredicate.PropertyPath = vm.GetCurrentPath(); + DisplayConditionPredicate.DataModelGuid = vm.DataModel.PluginInfo.Guid; + Update(); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs index 7a53fd84d..e71fea93b 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs @@ -1,27 +1,49 @@ -using Stylet; +using System.Linq; +using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.Conditions; +using Artemis.UI.Ninject.Factories; +using Artemis.UI.Shared.DataModelVisualization.Shared; +using Artemis.UI.Shared.Events; +using Artemis.UI.Shared.Services; +using Artemis.UI.Shared.Services.Interfaces; namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions { public class DisplayConditionsViewModel : ProfileEditorPanelViewModel { + private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; + private readonly IDataModelVisualizationService _dataModelVisualizationService; private DisplayConditionGroupViewModel _rootGroup; + public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelVisualizationService dataModelVisualizationService, + IDisplayConditionsVmFactory displayConditionsVmFactory) + { + _dataModelVisualizationService = dataModelVisualizationService; + _displayConditionsVmFactory = displayConditionsVmFactory; + profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected; + } + public DisplayConditionGroupViewModel RootGroup { get => _rootGroup; set => SetAndNotify(ref _rootGroup, value); } - public DisplayConditionsViewModel() + private void ProfileEditorServiceOnProfileElementSelected(object sender, ProfileElementEventArgs e) { - RootGroup = new DisplayConditionGroupViewModel {IsRootGroup = true}; - var subGroup = new DisplayConditionGroupViewModel(); + _dataModelVisualizationService.BustCache(); - RootGroup.Children.Add(new DisplayConditionPredicateViewModel()); - RootGroup.Children.Add(new DisplayConditionPredicateViewModel()); - RootGroup.Children.Add(subGroup); - subGroup.Children.Add(new DisplayConditionPredicateViewModel()); - subGroup.Children.Add(new DisplayConditionPredicateViewModel()); + if (e.ProfileElement is Layer layer) + { + // Ensure the layer has a root display condition group + if (layer.DisplayConditionGroup == null) + layer.DisplayConditionGroup = new DisplayConditionGroup(); + + RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(layer.DisplayConditionGroup, null); + RootGroup.IsRootGroup = true; + } + else + RootGroup = null; } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml index f26191f39..0dd684948 100644 --- a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml +++ b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml @@ -5,15 +5,11 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs" xmlns:s="https://github.com/canton7/Stylet" - xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:wpf="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:plugins="clr-namespace:Artemis.Core.Plugins.Abstract;assembly=Artemis.Core" xmlns:dataModel="clr-namespace:Artemis.UI.Shared.DataModelVisualization.Shared;assembly=Artemis.UI.Shared" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:DataModelDebugViewModel}"> - - - @@ -29,18 +25,18 @@ - - + + Filter module - - - - + + @@ -60,24 +56,10 @@ - - - - - - - - - - [] - - - - + + - + @@ -104,20 +86,13 @@ - [] + [] - - - + + @@ -127,13 +102,51 @@ HorizontalAlignment="Right" Foreground="{DynamicResource MaterialDesignCheckBoxDisabled}" Visibility="{Binding ShowNull, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" /> - - + Visibility="{Binding ShowViewModel, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" /> + + + + + + + + + + + [] + + + List item [] + + + + + + + + + + + List item [] + + diff --git a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs index 0a3918226..63f01122c 100644 --- a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs @@ -17,7 +17,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs private readonly Timer _updateTimer; private bool _isModuleFilterEnabled; private Core.Plugins.Abstract.Module _selectedModule; - private DataModelViewModel _mainDataModel; + private DataModelPropertiesViewModel _mainDataModel; private string _propertySearch; private List _modules; @@ -26,12 +26,12 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs _dataModelVisualizationService = dataModelVisualizationService; _pluginService = pluginService; _updateTimer = new Timer(500); - _updateTimer.Elapsed += (sender, args) => MainDataModel.Update(); + _updateTimer.Elapsed += (sender, args) => MainDataModel.Update(_dataModelVisualizationService); DisplayName = "Data model"; } - public DataModelViewModel MainDataModel + public DataModelPropertiesViewModel MainDataModel { get => _mainDataModel; set => SetAndNotify(ref _mainDataModel, value); @@ -93,8 +93,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs private void GetDataModel() { MainDataModel = SelectedModule != null - ? _dataModelVisualizationService.GetPluginDataModelVisualization(SelectedModule) - : _dataModelVisualizationService.GetMainDataModelVisualization(); + ? _dataModelVisualizationService.GetPluginDataModelVisualization(SelectedModule, false) + : _dataModelVisualizationService.GetMainDataModelVisualization(false); } private void PluginServiceOnPluginToggled(object? sender, PluginEventArgs e) diff --git a/src/Artemis.UI/Utilities/BindingProxy.cs b/src/Artemis.UI/Utilities/BindingProxy.cs new file mode 100644 index 000000000..b3bfde72d --- /dev/null +++ b/src/Artemis.UI/Utilities/BindingProxy.cs @@ -0,0 +1,26 @@ +using System.Windows; + +namespace Artemis.UI.Utilities +{ + public class BindingProxy : Freezable + { + // Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DataProperty = + DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); + + public object Data + { + get => GetValue(DataProperty); + set => SetValue(DataProperty, value); + } + + #region Overrides of Freezable + + protected override Freezable CreateInstanceCore() + { + return new BindingProxy(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Utilities/DelegateCommand.cs b/src/Artemis.UI/Utilities/DelegateCommand.cs new file mode 100644 index 000000000..0ce3e84b5 --- /dev/null +++ b/src/Artemis.UI/Utilities/DelegateCommand.cs @@ -0,0 +1,41 @@ +using System; +using System.Windows.Input; + +namespace Artemis.UI.Utilities +{ + public class DelegateCommand : ICommand + { + private readonly Predicate _canExecute; + private readonly Action _execute; + + public DelegateCommand(Action execute) : this(execute, null) + { + } + + public DelegateCommand(Action execute, Predicate canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + if (_canExecute == null) + return true; + + return _canExecute(parameter); + } + + public void Execute(object parameter) + { + _execute(parameter); + } + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } +} \ No newline at end of file