diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs index e6301860a..c238ae350 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using Artemis.Core.Plugins.Models; using Artemis.Core.Services.Interfaces; @@ -8,6 +9,24 @@ namespace Artemis.Core.Models.Profile.Conditions { public abstract class DisplayConditionOperator { + /// + /// A read-only collection containing all primitive number types + /// + protected static IReadOnlyCollection NumberTypes = new List + { + typeof(sbyte), + typeof(byte), + typeof(short), + typeof(ushort), + typeof(int), + typeof(uint), + typeof(long), + typeof(ulong), + typeof(float), + typeof(double), + typeof(decimal) + }; + private IDataModelService _dataModelService; private bool _registered; @@ -25,12 +44,12 @@ namespace Artemis.Core.Models.Profile.Conditions /// /// Gets or sets the description of this logical operator /// - public string Description { get; set; } + public abstract string Description { get; } /// /// Gets or sets the icon of this logical operator /// - public string Icon { get; set; } + public abstract string Icon { get; } /// /// Creates a binary expression comparing two types @@ -69,5 +88,12 @@ namespace Artemis.Core.Models.Profile.Conditions // Profile editor service will call Unsubscribe _dataModelService.RemoveConditionOperator(this); } + + public bool SupportsType(Type type) + { + if (type == null) + return true; + return CompatibleTypes.Any(t => t.IsAssignableFrom(type)); + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs new file mode 100644 index 000000000..9f2b3cee9 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Artemis.Core.Models.Profile.Conditions.Operators +{ + public class EqualsConditionOperator : DisplayConditionOperator + { + public override IReadOnlyCollection CompatibleTypes => new List {typeof(object)}; + + public override string Description => "Equals"; + public override string Icon => "Equal"; + + public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType) + { + var leftSideParameter = Expression.Parameter(leftSideType, "a"); + var rightSideParameter = Expression.Parameter(rightSideType, "b"); + return Expression.Equal(leftSideParameter, rightSideParameter); + } + } +} \ 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 2d78469b0..058305ab7 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs @@ -6,20 +6,10 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators { public class GreaterThanConditionOperator : DisplayConditionOperator { - public override IReadOnlyCollection CompatibleTypes => new List - { - typeof(sbyte), - typeof(byte), - typeof(short), - typeof(ushort), - typeof(int), - typeof(uint), - typeof(long), - typeof(ulong), - typeof(float), - typeof(double), - typeof(decimal) - }; + public override IReadOnlyCollection CompatibleTypes => NumberTypes; + + public override string Description => "Is greater than"; + public override string Icon => "GreaterThan"; public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType) { diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs new file mode 100644 index 000000000..a40aefcbc --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Artemis.Core.Models.Profile.Conditions.Operators +{ + public class GreaterThanOrEqualConditionOperator : DisplayConditionOperator + { + public override IReadOnlyCollection CompatibleTypes => NumberTypes; + + public override string Description => "Is greater than or equal to"; + public override string Icon => "GreaterThanOrEqual"; + + public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType) + { + var leftSideParameter = Expression.Parameter(leftSideType, "a"); + var rightSideParameter = Expression.Parameter(rightSideType, "b"); + return Expression.GreaterThanOrEqual(leftSideParameter, rightSideParameter); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs new file mode 100644 index 000000000..1ce2f1e3c --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Artemis.Core.Models.Profile.Conditions.Operators +{ + public class LessThanConditionOperator : DisplayConditionOperator + { + public override IReadOnlyCollection CompatibleTypes => NumberTypes; + + public override string Description => "Is less than"; + public override string Icon => "LessThan"; + + public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType) + { + var leftSideParameter = Expression.Parameter(leftSideType, "a"); + var rightSideParameter = Expression.Parameter(rightSideType, "b"); + return Expression.LessThan(leftSideParameter, rightSideParameter); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs new file mode 100644 index 000000000..cd2d75ccb --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Artemis.Core.Models.Profile.Conditions.Operators +{ + public class LessThanOrEqualConditionOperator : DisplayConditionOperator + { + public override IReadOnlyCollection CompatibleTypes => NumberTypes; + + public override string Description => "Is less than or equal to"; + public override string Icon => "LessThanOrEqual"; + + public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType) + { + var leftSideParameter = Expression.Parameter(leftSideType, "a"); + var rightSideParameter = Expression.Parameter(rightSideType, "b"); + return Expression.LessThanOrEqual(leftSideParameter, rightSideParameter); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs new file mode 100644 index 000000000..00d83e516 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Artemis.Core.Models.Profile.Conditions.Operators +{ + public class NotEqualConditionOperator : DisplayConditionOperator + { + public override IReadOnlyCollection CompatibleTypes => new List { typeof(object) }; + + public override string Description => "Does not equal"; + public override string Icon => "NotEqualVariant"; + + public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType) + { + var leftSideParameter = Expression.Parameter(leftSideType, "a"); + var rightSideParameter = Expression.Parameter(rightSideType, "b"); + return Expression.NotEqual(leftSideParameter, rightSideParameter); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/DataModelService.cs b/src/Artemis.Core/Services/DataModelService.cs index 75cc7b729..dce63da5b 100644 --- a/src/Artemis.Core/Services/DataModelService.cs +++ b/src/Artemis.Core/Services/DataModelService.cs @@ -40,7 +40,18 @@ namespace Artemis.Core.Services AddDataModelExpansionDataModel(dataModelExpansion); } - public ReadOnlyCollection DataModelExpansions + public IReadOnlyCollection RegisteredConditionOperators + { + get + { + lock (_registeredConditionOperators) + { + return _registeredConditionOperators.AsReadOnly(); + } + } + } + + public IReadOnlyCollection DataModelExpansions { get { @@ -128,13 +139,20 @@ namespace Artemis.Core.Services { lock (_registeredConditionOperators) { - return _registeredConditionOperators.Where(c => c.CompatibleTypes.Contains(type)).ToList(); + if (type == null) + return new List(_registeredConditionOperators); + return _registeredConditionOperators.Where(c => c.CompatibleTypes.Any(t => t.IsAssignableFrom(type))).ToList(); } } private void RegisterBuiltInConditionOperators() { + RegisterConditionOperator(Constants.CorePluginInfo, new EqualsConditionOperator()); + RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator()); + RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator()); RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator()); + RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator()); + RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanOrEqualConditionOperator()); } private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e) diff --git a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs index 57a7f27d1..24b6d5ab4 100644 --- a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs +++ b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs @@ -1,4 +1,5 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.Generic; using Artemis.Core.Annotations; using Artemis.Core.Models.Profile.Conditions; using Artemis.Core.Plugins.Abstract; @@ -9,7 +10,8 @@ namespace Artemis.Core.Services.Interfaces { public interface IDataModelService : IArtemisService { - ReadOnlyCollection DataModelExpansions { get; } + IReadOnlyCollection RegisteredConditionOperators { get; } + IReadOnlyCollection DataModelExpansions { get; } /// /// Add an expansion to the datamodel to be available for use after the next update @@ -48,5 +50,7 @@ namespace Artemis.Core.Services.Interfaces /// /// The layer condition operator to remove void RemoveConditionOperator([NotNull] DisplayConditionOperator displayConditionOperator); + + List GetCompatibleConditionOperators(Type type); } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs index 7bc781608..aaf767c55 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs @@ -5,13 +5,26 @@ namespace Artemis.UI.Shared.DataModelVisualization { public abstract class DataModelInputViewModel : DataModelInputViewModel { - protected DataModelInputViewModel(DataModelPropertyAttribute description) + private T _inputValue; + + protected DataModelInputViewModel(DataModelPropertyAttribute description, T initialValue) { Description = description; + InputValue = initialValue; + } + + public T InputValue + { + get => _inputValue; + set => SetAndNotify(ref _inputValue, value); } public DataModelPropertyAttribute Description { get; } internal override object InternalGuard { get; } = null; + + protected void Submit() + { + } } /// diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs index 4c14e8c0f..77b136198 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Reflection; using Artemis.Core.Extensions; @@ -19,6 +20,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared private DataModelVisualizationViewModel _parent; private DataModelPropertyAttribute _propertyDescription; private PropertyInfo _propertyInfo; + private bool _isMatchingFilteredTypes; internal DataModelVisualizationViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) { @@ -26,6 +28,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared PropertyInfo = propertyInfo; Parent = parent; Children = new BindableCollection(); + IsMatchingFilteredTypes = true; if (dataModel == null && parent == null && propertyInfo == null) IsRootViewModel = true; @@ -65,6 +68,12 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared set => SetAndNotify(ref _children, value); } + public bool IsMatchingFilteredTypes + { + get => _isMatchingFilteredTypes; + set => SetAndNotify(ref _isMatchingFilteredTypes, value); + } + public abstract void Update(IDataModelVisualizationService dataModelVisualizationService); public virtual object GetCurrentValue() @@ -72,6 +81,34 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared return Parent == null ? null : PropertyInfo.GetValue(Parent.GetCurrentValue()); } + public void ApplyTypeFilter(params Type[] filteredTypes) + { + // If the VM has children, its own type is not relevant + if (Children.Any()) + { + foreach (var child in Children) + child.ApplyTypeFilter(filteredTypes); + + IsMatchingFilteredTypes = true; + return; + } + + // If null is passed, clear the type filter + if (filteredTypes == null || filteredTypes.Length == 0) + { + IsMatchingFilteredTypes = true; + return; + } + // If this VM has no property info, assume it does not match + if (PropertyInfo == null) + { + IsMatchingFilteredTypes = false; + return; + } + + IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsAssignableFrom(PropertyInfo.PropertyType)); + } + public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath) { var path = propertyPath.Split("."); diff --git a/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs b/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs index af71fa7df..b259f66d3 100644 --- a/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs +++ b/src/Artemis.UI.Shared/Services/DataModelVisualizationService.cs @@ -18,8 +18,6 @@ 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) { @@ -27,38 +25,24 @@ namespace Artemis.UI.Shared.Services _kernel = kernel; _registeredDataModelEditors = new List(); _registeredDataModelDisplays = new List(); - _cachedDataModels = new Dictionary(); } public IReadOnlyCollection RegisteredDataModelEditors => _registeredDataModelEditors.AsReadOnly(); public IReadOnlyCollection RegisteredDataModelDisplays => _registeredDataModelDisplays.AsReadOnly(); - public DataModelPropertiesViewModel GetMainDataModelVisualization(bool useCache) + public DataModelPropertiesViewModel GetMainDataModelVisualization() { - // 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 DataModelPropertiesViewModel(dataModelExpansion, viewModel, null)); // Update to populate children viewModel.Update(this); - - // Add to cache - _cachedMainDataModel = viewModel; - return viewModel; } - public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool useCache) + public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin) { - // 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; @@ -68,11 +52,6 @@ namespace Artemis.UI.Shared.Services // Update to populate children viewModel.Update(this); - - // Add to cache - if (!isCached) - _cachedDataModels.Add(plugin, viewModel); - return viewModel; } @@ -81,12 +60,6 @@ namespace Artemis.UI.Shared.Services return _dataModelService.GetPluginExtendsDataModel(plugin); } - public void BustCache() - { - _cachedMainDataModel = null; - _cachedDataModels.Clear(); - } - public DataModelVisualizationRegistration RegisterDataModelInput(PluginInfo pluginInfo) where T : DataModelInputViewModel { var viewModelType = typeof(T); @@ -173,8 +146,8 @@ namespace Artemis.UI.Shared.Services public interface IDataModelVisualizationService : IArtemisSharedUIService { - DataModelPropertiesViewModel GetMainDataModelVisualization(bool useCached); - DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool useCached); + DataModelPropertiesViewModel GetMainDataModelVisualization(); + DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin); /// /// Determines whether the given plugin expands the main data model @@ -183,8 +156,6 @@ namespace Artemis.UI.Shared.Services /// bool GetPluginExtendsDataModel(Plugin plugin); - void BustCache(); - DataModelVisualizationRegistration RegisterDataModelInput(PluginInfo pluginInfo) where T : DataModelInputViewModel; DataModelVisualizationRegistration RegisterDataModelDisplay(PluginInfo pluginInfo) where T : DataModelDisplayViewModel; void RemoveDataModelInput(DataModelVisualizationRegistration registration); diff --git a/src/Artemis.UI.Shared/Services/Dialog/DialogService.cs b/src/Artemis.UI.Shared/Services/Dialog/DialogService.cs index 62a761d62..bb5e9e677 100644 --- a/src/Artemis.UI.Shared/Services/Dialog/DialogService.cs +++ b/src/Artemis.UI.Shared/Services/Dialog/DialogService.cs @@ -22,6 +22,8 @@ namespace Artemis.UI.Shared.Services.Dialog _viewManager = viewManager; } + public bool IsExceptionDialogOpen { get; private set; } + public async Task ShowConfirmDialog(string header, string text, string confirmText = "Confirm", string cancelText = "Cancel") { var arguments = new IParameter[] @@ -90,31 +92,28 @@ namespace Artemis.UI.Shared.Services.Dialog public async Task ShowExceptionDialog(string message, Exception exception) { + if (IsExceptionDialogOpen) + return; + + IsExceptionDialogOpen = true; var arguments = new IParameter[] { new ConstructorArgument("message", message), new ConstructorArgument("exception", exception) }; - - try + + await Execute.OnUIThreadAsync(async () => { - await Execute.OnUIThreadAsync(async () => + try { - try - { - DialogHost.CloseDialogCommand.Execute(new object(), null); - await ShowDialog(arguments); - } - catch (Exception) - { - // ignored - } - }); - } - catch (Exception) - { - // ignored - } + DialogHost.CloseDialogCommand.Execute(new object(), null); + await ShowDialog(arguments); + } + finally + { + IsExceptionDialogOpen = false; + } + }); } private async Task ShowDialog(string identifier, DialogViewModelBase viewModel) diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IDialogService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IDialogService.cs index e67668db4..fc810d8ac 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IDialogService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IDialogService.cs @@ -102,5 +102,7 @@ namespace Artemis.UI.Shared.Services.Interfaces /// The exception to display. The exception message and stacktrace will be shown. /// A task resolving when the dialog is closed Task ShowExceptionDialog(string message, Exception exception); + + bool IsExceptionDialogOpen { get; } } } \ No newline at end of file diff --git a/src/Artemis.UI/DataModelVisualization/SKColorDataModelDisplayView.xaml b/src/Artemis.UI/DataModelVisualization/Display/SKColorDataModelDisplayView.xaml similarity index 88% rename from src/Artemis.UI/DataModelVisualization/SKColorDataModelDisplayView.xaml rename to src/Artemis.UI/DataModelVisualization/Display/SKColorDataModelDisplayView.xaml index 2e55a49de..40e594f93 100644 --- a/src/Artemis.UI/DataModelVisualization/SKColorDataModelDisplayView.xaml +++ b/src/Artemis.UI/DataModelVisualization/Display/SKColorDataModelDisplayView.xaml @@ -1,13 +1,14 @@ - + d:DataContext="{d:DesignInstance {x:Type display:SKColorDataModelDisplayViewModel}}"> diff --git a/src/Artemis.UI/DataModelVisualization/SKColorDataModelDisplayViewModel.cs b/src/Artemis.UI/DataModelVisualization/Display/SKColorDataModelDisplayViewModel.cs similarity index 76% rename from src/Artemis.UI/DataModelVisualization/SKColorDataModelDisplayViewModel.cs rename to src/Artemis.UI/DataModelVisualization/Display/SKColorDataModelDisplayViewModel.cs index cd1eff507..e57c1a9a2 100644 --- a/src/Artemis.UI/DataModelVisualization/SKColorDataModelDisplayViewModel.cs +++ b/src/Artemis.UI/DataModelVisualization/Display/SKColorDataModelDisplayViewModel.cs @@ -1,7 +1,7 @@ using Artemis.UI.Shared.DataModelVisualization; using SkiaSharp; -namespace Artemis.UI.DataModelVisualization +namespace Artemis.UI.DataModelVisualization.Display { public class SKColorDataModelDisplayViewModel : DataModelDisplayViewModel { diff --git a/src/Artemis.UI/DataModelVisualization/Input/StringDataModelInputView.xaml b/src/Artemis.UI/DataModelVisualization/Input/StringDataModelInputView.xaml new file mode 100644 index 000000000..d517649a5 --- /dev/null +++ b/src/Artemis.UI/DataModelVisualization/Input/StringDataModelInputView.xaml @@ -0,0 +1,10 @@ + + + diff --git a/src/Artemis.UI/DataModelVisualization/Input/StringDataModelInputViewModel.cs b/src/Artemis.UI/DataModelVisualization/Input/StringDataModelInputViewModel.cs new file mode 100644 index 000000000..f88355001 --- /dev/null +++ b/src/Artemis.UI/DataModelVisualization/Input/StringDataModelInputViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Artemis.Core.Plugins.Abstract.DataModels.Attributes; +using Artemis.UI.Shared.DataModelVisualization; + +namespace Artemis.UI.DataModelVisualization.Input +{ + public class StringDataModelInputViewModel : DataModelInputViewModel + { + public StringDataModelInputViewModel(DataModelPropertyAttribute description, string initialValue) : base(description, initialValue) + { + } + } +} \ 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 1a4c0aa16..cf0b1a06a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml @@ -48,9 +48,10 @@ Background="#ab47bc" BorderBrush="#ab47bc" Style="{StaticResource DisplayConditionButton}" + ToolTip="{Binding DisplayConditionPredicate.LeftPropertyPath}" Click="PropertyButton_OnClick"> - + + BorderBrush="#7B7B7B" + Content="{Binding DisplayConditionPredicate.Operator.Description}" + Click="PropertyButton_OnClick"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + @@ -127,10 +109,11 @@ Background="{DynamicResource PrimaryHueMidBrush}" BorderBrush="{DynamicResource PrimaryHueMidBrush}" Style="{StaticResource DisplayConditionButton}" + ToolTip="{Binding DisplayConditionPredicate.RightPropertyPath}" Click="PropertyButton_OnClick" Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"> - + _operators; + private DataModelPropertiesViewModel _rightSideDataModel; + private DataModelInputViewModel _rightSideInputViewModel; + private int _rightSideTransitionIndex; private DataModelVisualizationViewModel _selectedLeftSideProperty; private DataModelVisualizationViewModel _selectedRightSideProperty; - private int _rightSideTransitionIndex; - private DataModelInputViewModel _rightSideInputViewModel; + + private List _supportedInputTypes; public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, - IProfileEditorService profileEditorService, IDataModelVisualizationService dataModelVisualizationService) : base(displayConditionPredicate, parent) + IProfileEditorService profileEditorService, IDataModelVisualizationService dataModelVisualizationService, IDataModelService dataModelService) + : base(displayConditionPredicate, parent) { _profileEditorService = profileEditorService; _dataModelVisualizationService = dataModelVisualizationService; + _dataModelService = dataModelService; SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty); SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty); + SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); - GetDataModel(); + Initialize(); } public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model; public DelegateCommand SelectLeftPropertyCommand { get; } public DelegateCommand SelectRightPropertyCommand { get; } + public DelegateCommand SelectOperatorCommand { get; } public bool ShowRightSidePropertySelection => DisplayConditionPredicate.PredicateType == PredicateType.Dynamic; - public DataModelPropertiesViewModel DataModel + public bool IsInitialized { get; private set; } + + public DataModelPropertiesViewModel LeftSideDataModel { - get => _dataModel; - set => SetAndNotify(ref _dataModel, value); + get => _leftSideDataModel; + set => SetAndNotify(ref _leftSideDataModel, value); + } + + public DataModelPropertiesViewModel RightSideDataModel + { + get => _rightSideDataModel; + set => SetAndNotify(ref _rightSideDataModel, value); } public DataModelVisualizationViewModel SelectedLeftSideProperty @@ -65,26 +87,70 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions set => SetAndNotify(ref _rightSideInputViewModel, value); } - public void GetDataModel() + public List Operators { - var mainDataModel = _dataModelVisualizationService.GetMainDataModelVisualization(true); - if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule())) - mainDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true)); + get => _operators; + set => SetAndNotify(ref _operators, value); + } - DataModel = mainDataModel; + public void Initialize() + { + Task.Run(() => + { + // Get the data models + LeftSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization(); + RightSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization(); + if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule())) + { + LeftSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule())); + RightSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule())); + } - Update(); + // Determine which types are currently supported + _supportedInputTypes = _dataModelVisualizationService.RegisteredDataModelEditors.Select(e => e.SupportedType).ToList(); + + IsInitialized = true; + Update(); + }); } public override void Update() { - SelectedLeftSideProperty = DisplayConditionPredicate.LeftPropertyPath != null - ? DataModel.GetChildByPath(DisplayConditionPredicate.LeftDataModelGuid, DisplayConditionPredicate.LeftPropertyPath) - : null; + if (!IsInitialized) + return; - SelectedRightSideProperty = DisplayConditionPredicate.RightPropertyPath != null - ? DataModel.GetChildByPath(DisplayConditionPredicate.RightDataModelGuid, DisplayConditionPredicate.RightPropertyPath) + // If static, only allow selecting properties also supported by input + if (DisplayConditionPredicate.PredicateType == PredicateType.Static) + LeftSideDataModel.ApplyTypeFilter(_supportedInputTypes.ToArray()); + + // Determine the left side property first + SelectedLeftSideProperty = DisplayConditionPredicate.LeftPropertyPath != null + ? LeftSideDataModel.GetChildByPath(DisplayConditionPredicate.LeftDataModelGuid, DisplayConditionPredicate.LeftPropertyPath) : null; + var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType; + + // Right side may only select properties matching the left side + if (SelectedLeftSideProperty != null) + RightSideDataModel.ApplyTypeFilter(leftSideType); + else + RightSideDataModel.ApplyTypeFilter(); + + // Determine the right side property first + if (DisplayConditionPredicate.RightPropertyPath != null) + { + // Ensure the right side property still matches the left side type, else set it to null + var selectedProperty = RightSideDataModel.GetChildByPath(DisplayConditionPredicate.RightDataModelGuid, DisplayConditionPredicate.RightPropertyPath); + SelectedRightSideProperty = selectedProperty.IsMatchingFilteredTypes ? selectedProperty : null; + } + else + SelectedRightSideProperty = null; + + // Get the supported operators + Operators = _dataModelService.GetCompatibleConditionOperators(leftSideType); + if (DisplayConditionPredicate.Operator == null || !DisplayConditionPredicate.Operator.SupportsType(leftSideType)) + DisplayConditionPredicate.Operator = Operators.FirstOrDefault(o => o.SupportsType(leftSideType)); + + NotifyOfPropertyChange(nameof(DisplayConditionPredicate)); } public void ActivateRightSideInputViewModel() @@ -111,5 +177,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions DisplayConditionPredicate.RightDataModelGuid = vm.DataModel.PluginInfo.Guid; Update(); } + + private void ExecuteSelectOperatorCommand(object context) + { + if (!(context is DisplayConditionOperator displayConditionOperator)) + return; + + DisplayConditionPredicate.Operator = displayConditionOperator; + 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 e71fea93b..dfd94f012 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs @@ -1,10 +1,7 @@ -using System.Linq; -using Artemis.Core.Models.Profile; +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 @@ -12,13 +9,10 @@ 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) + public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) { - _dataModelVisualizationService = dataModelVisualizationService; _displayConditionsVmFactory = displayConditionsVmFactory; profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected; } @@ -31,8 +25,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions private void ProfileEditorServiceOnProfileElementSelected(object sender, ProfileElementEventArgs e) { - _dataModelVisualizationService.BustCache(); - if (e.ProfileElement is Layer layer) { // Ensure the layer has a root display condition group @@ -41,6 +33,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(layer.DisplayConditionGroup, null); RootGroup.IsRootGroup = true; + RootGroup.Update(); } else RootGroup = null; diff --git a/src/Artemis.UI/Screens/RootViewModel.cs b/src/Artemis.UI/Screens/RootViewModel.cs index c65277578..09f5dee88 100644 --- a/src/Artemis.UI/Screens/RootViewModel.cs +++ b/src/Artemis.UI/Screens/RootViewModel.cs @@ -10,6 +10,7 @@ using Artemis.Core.Services.Interfaces; using Artemis.UI.Events; using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.Sidebar; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using Artemis.UI.Utilities; using MaterialDesignExtensions.Controls; @@ -23,6 +24,7 @@ namespace Artemis.UI.Screens private readonly PluginSetting _colorScheme; private readonly ICoreService _coreService; private readonly IDebugService _debugService; + private readonly IRegistrationService _builtInRegistrationService; private readonly IEventAggregator _eventAggregator; private readonly ThemeWatcher _themeWatcher; private readonly Timer _titleUpdateTimer; @@ -33,13 +35,14 @@ namespace Artemis.UI.Screens private string _windowTitle; public RootViewModel(IEventAggregator eventAggregator, SidebarViewModel sidebarViewModel, ISettingsService settingsService, ICoreService coreService, - IDebugService debugService, ISnackbarMessageQueue snackbarMessageQueue) + IDebugService debugService, IRegistrationService builtInRegistrationService, ISnackbarMessageQueue snackbarMessageQueue) { SidebarViewModel = sidebarViewModel; MainMessageQueue = snackbarMessageQueue; _eventAggregator = eventAggregator; _coreService = coreService; _debugService = debugService; + _builtInRegistrationService = builtInRegistrationService; _titleUpdateTimer = new Timer(500); _titleUpdateTimer.Elapsed += (sender, args) => UpdateWindowTitle(); @@ -56,7 +59,7 @@ namespace Artemis.UI.Screens } public SidebarViewModel SidebarViewModel { get; } - public ISnackbarMessageQueue MainMessageQueue { get; } + public ISnackbarMessageQueue MainMessageQueue { get; } public bool IsSidebarVisible { @@ -184,6 +187,10 @@ namespace Artemis.UI.Screens { UpdateWindowTitle(); _titleUpdateTimer.Start(); + + _builtInRegistrationService.RegisterBuiltInDataModelDisplays(); + _builtInRegistrationService.RegisterBuiltInDataModelInputs(); + _builtInRegistrationService.RegisterBuiltInPropertyEditors(); } protected override void OnDeactivate() diff --git a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs index 63f01122c..66a7d317a 100644 --- a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugViewModel.cs @@ -93,8 +93,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs private void GetDataModel() { MainDataModel = SelectedModule != null - ? _dataModelVisualizationService.GetPluginDataModelVisualization(SelectedModule, false) - : _dataModelVisualizationService.GetMainDataModelVisualization(false); + ? _dataModelVisualizationService.GetPluginDataModelVisualization(SelectedModule) + : _dataModelVisualizationService.GetMainDataModelVisualization(); } private void PluginServiceOnPluginToggled(object? sender, PluginEventArgs e) diff --git a/src/Artemis.UI/Services/DebugService.cs b/src/Artemis.UI/Services/DebugService.cs index 54f02c062..04b228f1d 100644 --- a/src/Artemis.UI/Services/DebugService.cs +++ b/src/Artemis.UI/Services/DebugService.cs @@ -1,7 +1,7 @@ using System; using System.Windows; using Artemis.Core; -using Artemis.UI.DataModelVisualization; +using Artemis.UI.DataModelVisualization.Display; using Artemis.UI.Screens.Settings.Debug; using Artemis.UI.Services.Interfaces; using Artemis.UI.Shared.Services; @@ -15,21 +15,12 @@ namespace Artemis.UI.Services { private readonly IKernel _kernel; private readonly IWindowManager _windowManager; - private readonly IDataModelVisualizationService _dataModelVisualizationService; private DebugViewModel _debugViewModel; - public DebugService(IKernel kernel, IWindowManager windowManager, IDataModelVisualizationService dataModelVisualizationService) + public DebugService(IKernel kernel, IWindowManager windowManager) { _kernel = kernel; _windowManager = windowManager; - _dataModelVisualizationService = dataModelVisualizationService; - - RegisterBuiltInDataModelDisplays(); - } - - private void RegisterBuiltInDataModelDisplays() - { - _dataModelVisualizationService.RegisterDataModelDisplay(Constants.CorePluginInfo); } public void ShowDebugger() diff --git a/src/Artemis.UI/Services/LayerEditorService.cs b/src/Artemis.UI/Services/LayerEditorService.cs index f17726205..1445c8020 100644 --- a/src/Artemis.UI/Services/LayerEditorService.cs +++ b/src/Artemis.UI/Services/LayerEditorService.cs @@ -1,10 +1,8 @@ using System; using System.Windows; using System.Windows.Media; -using Artemis.Core; using Artemis.Core.Models.Profile; using Artemis.Core.Services; -using Artemis.UI.PropertyInput; using Artemis.UI.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces; using SkiaSharp; @@ -14,14 +12,11 @@ namespace Artemis.UI.Services { public class LayerEditorService : ILayerEditorService { - private readonly IProfileEditorService _profileEditorService; private readonly ISettingsService _settingsService; public LayerEditorService(ISettingsService settingsService, IProfileEditorService profileEditorService) { _settingsService = settingsService; - _profileEditorService = profileEditorService; - RegisterBuiltInPropertyEditors(); } /// @@ -138,16 +133,5 @@ namespace Artemis.UI.Services // The difference between the two is the offset return topLeft - tempTopLeft; } - - private void RegisterBuiltInPropertyEditors() - { - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs new file mode 100644 index 000000000..15cf49c12 --- /dev/null +++ b/src/Artemis.UI/Services/RegistrationService.cs @@ -0,0 +1,68 @@ +using Artemis.Core; +using Artemis.UI.DataModelVisualization.Display; +using Artemis.UI.DataModelVisualization.Input; +using Artemis.UI.PropertyInput; +using Artemis.UI.Services.Interfaces; +using Artemis.UI.Shared.Services; +using Artemis.UI.Shared.Services.Interfaces; + +namespace Artemis.UI.Services +{ + public class RegistrationService : IRegistrationService + { + private readonly IDataModelVisualizationService _dataModelVisualizationService; + private readonly IProfileEditorService _profileEditorService; + private bool _registeredBuiltInDataModelDisplays; + private bool _registeredBuiltInDataModelInputs; + private bool _registeredBuiltInPropertyEditors; + + public RegistrationService(IDataModelVisualizationService dataModelVisualizationService, IProfileEditorService profileEditorService) + { + _dataModelVisualizationService = dataModelVisualizationService; + _profileEditorService = profileEditorService; + } + + public void RegisterBuiltInDataModelDisplays() + { + if (_registeredBuiltInDataModelDisplays) + return; + + _dataModelVisualizationService.RegisterDataModelDisplay(Constants.CorePluginInfo); + + _registeredBuiltInDataModelDisplays = true; + } + + public void RegisterBuiltInDataModelInputs() + { + if (_registeredBuiltInDataModelInputs) + return; + + _dataModelVisualizationService.RegisterDataModelInput(Constants.CorePluginInfo); + + _registeredBuiltInDataModelInputs = true; + } + + public void RegisterBuiltInPropertyEditors() + { + if (_registeredBuiltInPropertyEditors) + return; + + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + + _registeredBuiltInPropertyEditors = true; + } + } + + public interface IRegistrationService : IArtemisUIService + { + void RegisterBuiltInDataModelDisplays(); + void RegisterBuiltInDataModelInputs(); + void RegisterBuiltInPropertyEditors(); + } +} \ No newline at end of file