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

Profile editor - Continuously update datamodel in cond. target select

Profile editor - Fixed undo/redo sometimes not registering Ctrl+Z/Ctrl+Y
Profile editor - Added feedback messaging to undo/redo
This commit is contained in:
Robert 2020-08-07 21:29:34 +02:00
parent c5dbe73000
commit 1f70c65651
10 changed files with 117 additions and 49 deletions

View File

@ -24,8 +24,8 @@ namespace Artemis.UI.Shared.Services.Interfaces
void ChangeSelectedProfileElement(RenderProfileElement profileElement);
void UpdateSelectedProfileElement();
void UpdateProfilePreview();
void UndoUpdateProfile(ProfileModule module);
void RedoUpdateProfile(ProfileModule module);
bool UndoUpdateProfile(ProfileModule module);
bool RedoUpdateProfile(ProfileModule module);
Module GetCurrentModule();
/// <summary>

View File

@ -132,11 +132,11 @@ namespace Artemis.UI.Shared.Services
OnProfilePreviewUpdated();
}
public void UndoUpdateProfile(ProfileModule module)
public bool UndoUpdateProfile(ProfileModule module)
{
var undid = _profileService.UndoUpdateProfile(SelectedProfile, module);
if (!undid)
return;
return false;
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
@ -149,13 +149,14 @@ namespace Artemis.UI.Shared.Services
}
UpdateProfilePreview();
return true;
}
public void RedoUpdateProfile(ProfileModule module)
public bool RedoUpdateProfile(ProfileModule module)
{
var redid = _profileService.RedoUpdateProfile(SelectedProfile, module);
if (!redid)
return;
return false;
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
@ -168,6 +169,7 @@ namespace Artemis.UI.Shared.Services
}
UpdateProfilePreview();
return true;
}
public PropertyInputRegistration RegisterPropertyInput<T>(PluginInfo pluginInfo) where T : PropertyInputViewModel

View File

@ -1,10 +1,10 @@
using Artemis.Core.Models.Profile.Conditions;
using System;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract
{
public abstract class DisplayConditionViewModel : PropertyChangedBase
public abstract class DisplayConditionViewModel : PropertyChangedBase, IDisposable
{
protected DisplayConditionViewModel(DisplayConditionPart model, DisplayConditionViewModel parent)
{
@ -17,6 +17,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract
public DisplayConditionViewModel Parent { get; set; }
public BindableCollection<DisplayConditionViewModel> Children { get; }
public void Dispose()
{
foreach (var child in Children)
child.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
public abstract void Update();
public virtual void Delete()
@ -24,5 +33,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract
Model.Parent.RemoveChild(Model);
Parent.Update();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
}
}

View File

@ -87,7 +87,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
var toRemove = Children.Where(c => !DisplayConditionGroup.Children.Contains(c.Model)).ToList();
// Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove)
{
Children.Remove(displayConditionViewModel);
displayConditionViewModel.Dispose();
}
foreach (var childModel in Model.Children)
{

View File

@ -93,7 +93,7 @@
ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}">
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}" IsOpen="{Binding LeftSideDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />

View File

@ -2,6 +2,7 @@
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;
@ -27,7 +28,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private readonly IProfileEditorService _profileEditorService;
private bool _isInitialized;
private DataModelPropertiesViewModel _leftSideDataModel;
private List<DisplayConditionOperator> _operators;
private BindableCollection<DisplayConditionOperator> _operators;
private DataModelPropertiesViewModel _rightSideDataModel;
private DataModelInputViewModel _rightSideInputViewModel;
private int _rightSideTransitionIndex;
@ -37,6 +38,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private DataModelVisualizationViewModel _selectedRightSideProperty;
private List<Type> _supportedInputTypes;
private readonly Timer _updateTimer;
public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, IProfileEditorService profileEditorService,
IDataModelVisualizationService dataModelVisualizationService, IDataModelService dataModelService, ISettingsService settingsService, IEventAggregator eventAggregator)
@ -46,10 +48,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
_dataModelVisualizationService = dataModelVisualizationService;
_dataModelService = dataModelService;
_eventAggregator = eventAggregator;
_updateTimer = new Timer(500);
SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty);
SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty);
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<DisplayConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
@ -68,6 +72,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private set => SetAndNotify(ref _isInitialized, value);
}
public bool LeftSideDataModelOpen { get; set; }
public DataModelPropertiesViewModel LeftSideDataModel
{
get => _leftSideDataModel;
@ -80,6 +86,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _rightSideDataModel, value);
}
public bool RightSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel SelectedLeftSideProperty
{
get => _selectedLeftSideProperty;
@ -114,7 +122,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public List<DisplayConditionOperator> Operators
public BindableCollection<DisplayConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
@ -172,17 +180,20 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
public override void Update()
{
if (LeftSideDataModel == null || (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic && RightSideDataModel == null))
if (LeftSideDataModel == null || DisplayConditionPredicate.PredicateType == PredicateType.Dynamic && RightSideDataModel == null)
return;
// If static, only allow selecting properties also supported by input
@ -194,7 +205,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators = _dataModelService.GetCompatibleConditionOperators(leftSideType);
Operators.Clear();
Operators.AddRange(_dataModelService.GetCompatibleConditionOperators(leftSideType));
if (DisplayConditionPredicate.Operator == null)
DisplayConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DisplayConditionPredicate.Operator;
@ -265,6 +277,20 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
_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 leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;

View File

@ -1,27 +1,24 @@
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
{
public class DisplayConditionsViewModel : ProfileEditorPanelViewModel
{
private readonly IProfileEditorService _profileEditorService;
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private DisplayConditionGroupViewModel _rootGroup;
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement _renderProfileElement;
private DisplayConditionGroupViewModel _rootGroup;
private int _transitionerIndex;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
{
_profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory;
_displayConditionsVmFactory = displayConditionsVmFactory;
}
public int TransitionerIndex
@ -57,31 +54,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
public bool ConditionBehaviourEnabled => RenderProfileElement != null;
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
RenderProfileElement = e.RenderProfileElement;
NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
if (e.RenderProfileElement == null)
{
RootGroup = null;
return;
}
// Ensure the layer has a root display condition group
if (e.RenderProfileElement.DisplayConditionGroup == null)
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null);
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
RootGroup.IsRootGroup = true;
RootGroup.Update();
// Only show the intro to conditions once, and only if the layer has no conditions
if (TransitionerIndex != 1)
TransitionerIndex = RootGroup.Children.Any() ? 1 : 0;
}
protected override void OnActivate()
{
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
@ -90,6 +62,36 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
protected override void OnDeactivate()
{
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
RootGroup?.Dispose();
RootGroup = null;
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
RenderProfileElement = e.RenderProfileElement;
NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
if (e.RenderProfileElement == null)
{
RootGroup?.Dispose();
RootGroup = null;
return;
}
// Ensure the layer has a root display condition group
if (e.RenderProfileElement.DisplayConditionGroup == null)
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null);
RootGroup?.Dispose();
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
RootGroup.IsRootGroup = true;
RootGroup.Update();
// Only show the intro to conditions once, and only if the layer has no conditions
if (TransitionerIndex != 1)
TransitionerIndex = RootGroup.Children.Any() ? 1 : 0;
}
}
}

View File

@ -6,7 +6,9 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:profileEditor="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor"
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
mc:Ignorable="d"
behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance Type=profileEditor:ProfileEditorViewModel, IsDesignTimeCreatable=False}">
<UserControl.Resources>

View File

@ -14,6 +14,7 @@ using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties;
using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.Module.ProfileEditor.Visualization;
using Artemis.UI.Shared.Services.Interfaces;
using MaterialDesignThemes.Wpf;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor
@ -23,6 +24,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
private readonly IProfileEditorService _profileEditorService;
private readonly IProfileService _profileService;
private readonly ISettingsService _settingsService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private BindableCollection<Profile> _profiles;
private PluginSetting<GridLength> _sidePanelsWidth;
private PluginSetting<GridLength> _displayConditionsHeight;
@ -38,11 +40,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
IProfileEditorService profileEditorService,
IProfileService profileService,
IDialogService dialogService,
ISettingsService settingsService)
ISettingsService settingsService,
ISnackbarMessageQueue snackbarMessageQueue)
{
_profileEditorService = profileEditorService;
_profileService = profileService;
_settingsService = settingsService;
_snackbarMessageQueue = snackbarMessageQueue;
DisplayName = "Profile editor";
Module = module;
@ -172,11 +176,17 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
var beforeGroups = LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels();
var expandedPaths = beforeGroups.Where(g => g.IsExpanded).Select(g => g.LayerPropertyGroup.Path).ToList();
_profileEditorService.UndoUpdateProfile(Module);
if (!_profileEditorService.UndoUpdateProfile(Module))
{
_snackbarMessageQueue.Enqueue("Nothing to undo");
return;
}
// Restore the expanded status
foreach (var allLayerPropertyGroupViewModel in LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels())
allLayerPropertyGroupViewModel.IsExpanded = expandedPaths.Contains(allLayerPropertyGroupViewModel.LayerPropertyGroup.Path);
_snackbarMessageQueue.Enqueue("Undid profile update", "REDO", Redo);
}
public void Redo()
@ -185,11 +195,17 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
var beforeGroups = LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels();
var expandedPaths = beforeGroups.Where(g => g.IsExpanded).Select(g => g.LayerPropertyGroup.Path).ToList();
_profileEditorService.RedoUpdateProfile(Module);
if (!_profileEditorService.RedoUpdateProfile(Module))
{
_snackbarMessageQueue.Enqueue("Nothing to redo");
return;
}
// Restore the expanded status
foreach (var allLayerPropertyGroupViewModel in LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels())
allLayerPropertyGroupViewModel.IsExpanded = expandedPaths.Contains(allLayerPropertyGroupViewModel.LayerPropertyGroup.Path);
_snackbarMessageQueue.Enqueue("Redid profile update", "UNDO", Undo);
}
protected override void OnInitialActivate()

View File

@ -245,4 +245,5 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=leds/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=snackbar/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=snackbar/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=transitioner/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>