1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Artemis/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs
2020-08-21 22:32:46 +02:00

361 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Input;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.Settings;
using Artemis.Core.Services;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Events;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Utilities;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionListPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>
{
private readonly IDataModelService _dataModelService;
private readonly IDataModelVisualizationService _dataModelVisualizationService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer;
private bool _isInitialized;
private DataModelVisualizationViewModel _leftSideDataModel;
private BindableCollection<DisplayConditionOperator> _operators;
private DataModelVisualizationViewModel _rightSideDataModel;
private DataModelInputViewModel _rightSideInputViewModel;
private int _rightSideTransitionIndex;
private object _rightStaticValue;
private DataModelVisualizationViewModel _selectedLeftSideProperty;
private DisplayConditionOperator _selectedOperator;
private DataModelVisualizationViewModel _selectedRightSideProperty;
private List<Type> _supportedInputTypes;
public DisplayConditionListPredicateViewModel(
DisplayConditionListPredicate displayConditionListPredicate,
DisplayConditionViewModel parent,
IProfileEditorService profileEditorService,
IDataModelVisualizationService dataModelVisualizationService,
IDataModelService dataModelService,
ISettingsService settingsService,
IEventAggregator eventAggregator) : base(displayConditionListPredicate, parent)
{
_profileEditorService = profileEditorService;
_dataModelVisualizationService = dataModelVisualizationService;
_dataModelService = dataModelService;
_eventAggregator = eventAggregator;
_updateTimer = new Timer(500);
_supportedInputTypes = new List<Type>();
SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty);
SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty);
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<DisplayConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
// Initialize async, no need to wait for it
Task.Run(Initialize);
}
public DisplayConditionListPredicate DisplayConditionListPredicate => (DisplayConditionListPredicate) Model;
public bool ShowRightSidePropertySelection => DisplayConditionListPredicate.PredicateType == PredicateType.Dynamic;
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
public PluginSetting<bool> ShowDataModelValues { get; }
public bool IsInitialized
{
get => _isInitialized;
private set => SetAndNotify(ref _isInitialized, value);
}
public bool LeftSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel LeftSideDataModel
{
get => _leftSideDataModel;
set => SetAndNotify(ref _leftSideDataModel, value);
}
public DataModelVisualizationViewModel RightSideDataModel
{
get => _rightSideDataModel;
set => SetAndNotify(ref _rightSideDataModel, value);
}
public bool RightSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel SelectedLeftSideProperty
{
get => _selectedLeftSideProperty;
set
{
if (!SetAndNotify(ref _selectedLeftSideProperty, value)) return;
NotifyOfPropertyChange(nameof(CanActivateRightSideInputViewModel));
}
}
public DataModelVisualizationViewModel SelectedRightSideProperty
{
get => _selectedRightSideProperty;
set => SetAndNotify(ref _selectedRightSideProperty, value);
}
public object RightStaticValue
{
get => _rightStaticValue;
set => SetAndNotify(ref _rightStaticValue, value);
}
public int RightSideTransitionIndex
{
get => _rightSideTransitionIndex;
set => SetAndNotify(ref _rightSideTransitionIndex, value);
}
public DataModelInputViewModel RightSideInputViewModel
{
get => _rightSideInputViewModel;
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public BindableCollection<DisplayConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
}
public DisplayConditionOperator SelectedOperator
{
get => _selectedOperator;
set => SetAndNotify(ref _selectedOperator, value);
}
public DelegateCommand SelectLeftPropertyCommand { get; }
public DelegateCommand SelectRightPropertyCommand { get; }
public DelegateCommand SelectOperatorCommand { get; }
public void Handle(MainWindowKeyEvent message)
{
if (RightSideInputViewModel == null)
return;
if (!message.KeyDown && message.EventArgs.Key == Key.Escape)
RightSideInputViewModel.Cancel();
if (!message.KeyDown && message.EventArgs.Key == Key.Enter)
RightSideInputViewModel.Submit();
}
public void Handle(MainWindowMouseEvent message)
{
if (RightSideInputViewModel == null)
return;
if (message.Sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(RightSideInputViewModel.View))
RightSideInputViewModel.Submit();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
// Get the data models
LeftSideDataModel = GetListDataModel();
RightSideDataModel = GetListDataModel();
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
// Determine which types are currently supported
var editors = _dataModelVisualizationService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
public override void Update()
{
// Not yet initialized if these are null
if (LeftSideDataModel == null || RightSideDataModel == null)
return;
var listDataModelGuid = DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid;
// If static, only allow selecting properties also supported by input
if (DisplayConditionListPredicate.PredicateType == PredicateType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
// Determine the left side property first
SelectedLeftSideProperty = LeftSideDataModel.GetChildByPath(listDataModelGuid, DisplayConditionListPredicate.LeftPropertyPath);
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators.Clear();
Operators.AddRange(_dataModelService.GetCompatibleConditionOperators(leftSideType));
if (DisplayConditionListPredicate.Operator == null)
DisplayConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DisplayConditionListPredicate.Operator;
// Determine the right side
if (DisplayConditionListPredicate.PredicateType == PredicateType.Dynamic)
{
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DisplayConditionListPredicate.RightPropertyPath);
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
else
RightStaticValue = DisplayConditionListPredicate.RightStaticValue;
}
public void ApplyLeftSide()
{
DisplayConditionListPredicate.UpdateLeftSide(SelectedLeftSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
SelectedOperator = DisplayConditionListPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DisplayConditionListPredicate.UpdateRightSideDynamic(SelectedRightSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value, bool isSubmitted)
{
if (isSubmitted)
{
DisplayConditionListPredicate.UpdateRightSideStatic(value);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
RightSideTransitionIndex = 0;
RightSideInputViewModel = null;
RightStaticValue = value;
_eventAggregator.Unsubscribe(this);
}
public void ApplyOperator()
{
DisplayConditionListPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ActivateRightSideInputViewModel()
{
if (SelectedLeftSideProperty?.PropertyInfo == null)
return;
RightSideTransitionIndex = 1;
RightSideInputViewModel = _dataModelVisualizationService.GetDataModelInputViewModel(
SelectedLeftSideProperty.PropertyInfo.PropertyType,
SelectedLeftSideProperty.PropertyDescription,
DisplayConditionListPredicate.RightStaticValue,
ApplyRightSideStatic
);
_eventAggregator.Subscribe(this);
}
protected override void Dispose(bool disposing)
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (LeftSideDataModelOpen)
LeftSideDataModel.Update(_dataModelVisualizationService);
else if (RightSideDataModelOpen)
RightSideDataModel.Update(_dataModelVisualizationService);
}
private void RightDataModelUpdateRequested(object sender, EventArgs e)
{
var listDataModelGuid = DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid;
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// If the right side property is missing it may be available now that the data model has been updated
if (SelectedRightSideProperty == null && DisplayConditionListPredicate.RightPropertyPath != null)
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DisplayConditionListPredicate.RightPropertyPath);
// With the data model updated, also reapply the filter
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
{
if (DisplayConditionListPredicate.PredicateType == PredicateType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
}
private DataModelVisualizationViewModel GetListDataModel()
{
if (DisplayConditionListPredicate.ListDataModel == null || DisplayConditionListPredicate.ListPropertyPath == null)
throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list");
var dataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
dataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath(
DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid,
DisplayConditionListPredicate.ListPropertyPath
);
return listDataModel.GetListTypeViewModel(_dataModelVisualizationService);
}
private void ExecuteSelectLeftProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedLeftSideProperty = dataModelVisualizationViewModel;
ApplyLeftSide();
}
private void ExecuteSelectRightProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedRightSideProperty = dataModelVisualizationViewModel;
ApplyRightSideDynamic();
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is DisplayConditionOperator displayConditionOperator))
return;
SelectedOperator = displayConditionOperator;
ApplyOperator();
}
}
}