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

Editor - Fixed a bunch of memory leaks

This commit is contained in:
Robert 2022-08-18 20:13:15 +02:00
parent 84b394fc51
commit 9da71f7b97
6 changed files with 60 additions and 93 deletions

View File

@ -60,12 +60,7 @@ public interface IProfileEditorService : IArtemisSharedUIService
/// Gets an observable of the suspended keybindings state. /// Gets an observable of the suspended keybindings state.
/// </summary> /// </summary>
IObservable<ProfileEditorFocusMode> FocusMode { get; } IObservable<ProfileEditorFocusMode> FocusMode { get; }
/// <summary>
/// Gets an observable read only collection of all available editor tools.
/// </summary>
ReadOnlyObservableCollection<IToolViewModel> Tools { get; }
/// <summary> /// <summary>
/// Gets an observable read only collection of selected keyframes. /// Gets an observable read only collection of selected keyframes.
/// </summary> /// </summary>
@ -209,16 +204,4 @@ public interface IProfileEditorService : IArtemisSharedUIService
/// Pauses profile preview playback. /// Pauses profile preview playback.
/// </summary> /// </summary>
void Pause(); void Pause();
/// <summary>
/// Adds a profile editor tool by it's view model.
/// </summary>
/// <param name="toolViewModel">The view model of the tool to add.</param>
void AddTool(IToolViewModel toolViewModel);
/// <summary>
/// Removes a profile editor tool by it's view model.
/// </summary>
/// <param name="toolViewModel">The view model of the tool to remove.</param>
void RemoveTool(IToolViewModel toolViewModel);
} }

View File

@ -33,7 +33,6 @@ internal class ProfileEditorService : IProfileEditorService
private readonly BehaviorSubject<bool> _suspendedKeybindingsSubject = new(false); private readonly BehaviorSubject<bool> _suspendedKeybindingsSubject = new(false);
private readonly BehaviorSubject<ProfileEditorFocusMode> _focusModeSubject = new(ProfileEditorFocusMode.None); private readonly BehaviorSubject<ProfileEditorFocusMode> _focusModeSubject = new(ProfileEditorFocusMode.None);
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero); private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
private readonly SourceList<IToolViewModel> _tools;
private readonly SourceList<ILayerPropertyKeyframe> _selectedKeyframes; private readonly SourceList<ILayerPropertyKeyframe> _selectedKeyframes;
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
private ProfileEditorCommandScope? _profileEditorHistoryScope; private ProfileEditorCommandScope? _profileEditorHistoryScope;
@ -53,10 +52,7 @@ internal class ProfileEditorService : IProfileEditorService
_layerBrushService = layerBrushService; _layerBrushService = layerBrushService;
_windowService = windowService; _windowService = windowService;
_tools = new SourceList<IToolViewModel>();
_selectedKeyframes = new SourceList<ILayerPropertyKeyframe>(); _selectedKeyframes = new SourceList<ILayerPropertyKeyframe>();
_tools.Connect().AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Subscribe(OnToolSelected);
_tools.Connect().Bind(out ReadOnlyObservableCollection<IToolViewModel> tools).Subscribe();
_selectedKeyframes.Connect().Bind(out ReadOnlyObservableCollection<ILayerPropertyKeyframe> selectedKeyframes).Subscribe(); _selectedKeyframes.Connect().Bind(out ReadOnlyObservableCollection<ILayerPropertyKeyframe> selectedKeyframes).Subscribe();
ProfileConfiguration = _profileConfigurationSubject.AsObservable(); ProfileConfiguration = _profileConfigurationSubject.AsObservable();
@ -69,8 +65,6 @@ internal class ProfileEditorService : IProfileEditorService
SuspendedKeybindings = _suspendedKeybindingsSubject.AsObservable(); SuspendedKeybindings = _suspendedKeybindingsSubject.AsObservable();
PixelsPerSecond = _pixelsPerSecondSubject.AsObservable(); PixelsPerSecond = _pixelsPerSecondSubject.AsObservable();
FocusMode = _focusModeSubject.AsObservable(); FocusMode = _focusModeSubject.AsObservable();
Tools = tools;
SelectedKeyframes = selectedKeyframes; SelectedKeyframes = selectedKeyframes;
// Observe executing, undoing and redoing commands and run the auto-save after 1 second // Observe executing, undoing and redoing commands and run the auto-save after 1 second
@ -100,7 +94,6 @@ internal class ProfileEditorService : IProfileEditorService
public IObservable<bool> Playing { get; } public IObservable<bool> Playing { get; }
public IObservable<int> PixelsPerSecond { get; } public IObservable<int> PixelsPerSecond { get; }
public IObservable<ProfileEditorFocusMode> FocusMode { get; } public IObservable<ProfileEditorFocusMode> FocusMode { get; }
public ReadOnlyObservableCollection<IToolViewModel> Tools { get; }
public ReadOnlyObservableCollection<ILayerPropertyKeyframe> SelectedKeyframes { get; } public ReadOnlyObservableCollection<ILayerPropertyKeyframe> SelectedKeyframes { get; }
public void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration) public void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration)
@ -384,19 +377,7 @@ internal class ProfileEditorService : IProfileEditorService
if (_playingSubject.Value) if (_playingSubject.Value)
_playingSubject.OnNext(false); _playingSubject.OnNext(false);
} }
/// <inheritdoc />
public void AddTool(IToolViewModel toolViewModel)
{
_tools.Add(toolViewModel);
}
/// <inheritdoc />
public void RemoveTool(IToolViewModel toolViewModel)
{
_tools.Remove(toolViewModel);
}
#region Commands #region Commands
public void ExecuteCommand(IProfileEditorCommand command) public void ExecuteCommand(IProfileEditorCommand command)
@ -451,22 +432,7 @@ internal class ProfileEditorService : IProfileEditorService
} }
#endregion #endregion
private void OnToolSelected(IChangeSet<IToolViewModel> changeSet)
{
IToolViewModel? changed = changeSet.FirstOrDefault()?.Item.Current;
if (changed == null)
return;
// Disable all others if the changed one is selected and exclusive
if (changed.IsSelected && changed.IsExclusive)
_tools.Edit(list =>
{
foreach (IToolViewModel toolViewModel in list.Where(t => t.IsExclusive && t != changed))
toolViewModel.IsSelected = false;
});
}
private ProfileEditorHistory? GetHistory(ProfileConfiguration? profileConfiguration) private ProfileEditorHistory? GetHistory(ProfileConfiguration? profileConfiguration)
{ {
if (profileConfiguration == null) if (profileConfiguration == null)

View File

@ -62,7 +62,8 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
_isFocused = ProfileEditorService.FocusMode _isFocused = ProfileEditorService.FocusMode
.CombineLatest(ProfileEditorService.ProfileElement) .CombineLatest(ProfileEditorService.ProfileElement)
.Select(tuple => GetIsFocused(tuple.First, tuple.Second)) .Select(tuple => GetIsFocused(tuple.First, tuple.Second))
.ToProperty(this, vm => vm.IsFocused); .ToProperty(this, vm => vm.IsFocused)
.DisposeWith(d);
ProfileEditorService.Time.Subscribe(t => _time = t).DisposeWith(d); ProfileEditorService.Time.Subscribe(t => _time = t).DisposeWith(d);
ProfileEditorService.ProfileElement.Subscribe(element => _currentProfileElement = element).DisposeWith(d); ProfileEditorService.ProfileElement.Subscribe(element => _currentProfileElement = element).DisposeWith(d);

View File

@ -43,15 +43,6 @@ public class VisualEditorViewModel : ActivatableViewModelBase
_suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d); _suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d);
profileEditorService.ProfileConfiguration.Subscribe(CreateVisualizers).DisposeWith(d); profileEditorService.ProfileConfiguration.Subscribe(CreateVisualizers).DisposeWith(d);
profileEditorService.Tools
.ToObservableChangeSet()
.AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected))
.Filter(t => t.IsSelected)
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
.Subscribe()
.DisposeWith(d);
Tools = tools;
this.WhenAnyValue(vm => vm.ProfileConfiguration) this.WhenAnyValue(vm => vm.ProfileConfiguration)
.Select(p => p?.Profile) .Select(p => p?.Profile)
.Select(p => p != null .Select(p => p != null
@ -74,15 +65,15 @@ public class VisualEditorViewModel : ActivatableViewModelBase
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value; public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
public bool SuspendedEditing => _suspendedEditing?.Value ?? false; public bool SuspendedEditing => _suspendedEditing?.Value ?? false;
public ObservableCollection<ArtemisDevice> Devices { get; }
public ReadOnlyObservableCollection<IVisualizerViewModel> Visualizers { get; }
public ReadOnlyObservableCollection<IToolViewModel>? Tools public ReadOnlyObservableCollection<IToolViewModel>? Tools
{ {
get => _tools; get => _tools;
set => RaiseAndSetIfChanged(ref _tools, value); set => RaiseAndSetIfChanged(ref _tools, value);
} }
public ReadOnlyObservableCollection<IVisualizerViewModel> Visualizers { get; }
public ObservableCollection<ArtemisDevice> Devices { get; }
private void RemoveElement(EventPattern<ProfileElementEventArgs> eventPattern) private void RemoveElement(EventPattern<ProfileElementEventArgs> eventPattern)
{ {
List<IVisualizerViewModel> visualizers = Visualizers.Where(v => v.ProfileElement == eventPattern.EventArgs.ProfileElement).ToList(); List<IVisualizerViewModel> visualizers = Visualizers.Where(v => v.ProfileElement == eventPattern.EventArgs.ProfileElement).ToList();
@ -113,7 +104,17 @@ public class VisualEditorViewModel : ActivatableViewModelBase
visualizerViewModels.Add(_vmFactory.LayerShapeVisualizerViewModel(layer)); visualizerViewModels.Add(_vmFactory.LayerShapeVisualizerViewModel(layer));
visualizerViewModels.Add(_vmFactory.LayerVisualizerViewModel(layer)); visualizerViewModels.Add(_vmFactory.LayerVisualizerViewModel(layer));
} }
public void SetTools(SourceList<IToolViewModel> tools)
{
tools.Connect()
.AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected))
.Filter(t => t.IsSelected)
.Bind(out ReadOnlyObservableCollection<IToolViewModel> selectedTools)
.Subscribe();
Tools = selectedTools;
}
public void RequestAutoFit() public void RequestAutoFit()
{ {
AutoFitRequested?.Invoke(this, EventArgs.Empty); AutoFitRequested?.Invoke(this, EventArgs.Empty);

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
@ -21,10 +23,10 @@ public class ProfileEditorViewModel : MainScreenViewModel
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly SourceList<IToolViewModel> _tools;
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history; private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration; private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
private ObservableAsPropertyHelper<bool>? _suspendedEditing; private ObservableAsPropertyHelper<bool>? _suspendedEditing;
private ReadOnlyObservableCollection<IToolViewModel>? _tools;
private StatusBarViewModel _statusBarViewModel; private StatusBarViewModel _statusBarViewModel;
private DisplayConditionScriptViewModel _displayConditionScriptViewModel; private DisplayConditionScriptViewModel _displayConditionScriptViewModel;
private PropertiesViewModel _propertiesViewModel; private PropertiesViewModel _propertiesViewModel;
@ -40,26 +42,30 @@ public class ProfileEditorViewModel : MainScreenViewModel
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel, ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
PropertiesViewModel propertiesViewModel, PropertiesViewModel propertiesViewModel,
DisplayConditionScriptViewModel displayConditionScriptViewModel, DisplayConditionScriptViewModel displayConditionScriptViewModel,
StatusBarViewModel statusBarViewModel) StatusBarViewModel statusBarViewModel,
IEnumerable<IToolViewModel> toolViewModels)
: base(hostScreen, "profile-editor") : base(hostScreen, "profile-editor")
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_settingsService = settingsService; _settingsService = settingsService;
_tools = new SourceList<IToolViewModel>();
_tools.AddRange(toolViewModels);
_tools.Connect().AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Subscribe(OnToolSelected);
_tools.Connect()
.Filter(t => t.ShowInToolbar)
.Sort(SortExpressionComparer<IToolViewModel>.Ascending(vm => vm.Order))
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
.Subscribe();
Tools = tools;
visualEditorViewModel.SetTools(_tools);
this.WhenActivated(d => this.WhenActivated(d =>
{ {
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d); _profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d); _history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
_suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d); _suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d);
profileEditorService.Tools
.ToObservableChangeSet()
.Filter(t => t.ShowInToolbar)
.Sort(SortExpressionComparer<IToolViewModel>.Ascending(vm => vm.Order))
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
.Subscribe()
.DisposeWith(d);
Tools = tools;
// Slow and steady wins the race (and doesn't lock up the entire UI) // Slow and steady wins the race (and doesn't lock up the entire UI)
Dispatcher.UIThread.Post(() => StatusBarViewModel = statusBarViewModel, DispatcherPriority.Loaded); Dispatcher.UIThread.Post(() => StatusBarViewModel = statusBarViewModel, DispatcherPriority.Loaded);
Dispatcher.UIThread.Post(() => VisualEditorViewModel = visualEditorViewModel, DispatcherPriority.Loaded); Dispatcher.UIThread.Post(() => VisualEditorViewModel = visualEditorViewModel, DispatcherPriority.Loaded);
@ -103,12 +109,7 @@ public class ProfileEditorViewModel : MainScreenViewModel
set => RaiseAndSetIfChanged(ref _statusBarViewModel, value); set => RaiseAndSetIfChanged(ref _statusBarViewModel, value);
} }
public ReadOnlyObservableCollection<IToolViewModel>? Tools public ReadOnlyObservableCollection<IToolViewModel> Tools { get; }
{
get => _tools;
set => RaiseAndSetIfChanged(ref _tools, value);
}
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value; public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
public ProfileEditorHistory? History => _history?.Value; public ProfileEditorHistory? History => _history?.Value;
public bool SuspendedEditing => _suspendedEditing?.Value ?? false; public bool SuspendedEditing => _suspendedEditing?.Value ?? false;
@ -134,4 +135,21 @@ public class ProfileEditorViewModel : MainScreenViewModel
setting.Value = !setting.Value; setting.Value = !setting.Value;
setting.Save(); setting.Save();
} }
private void OnToolSelected(IChangeSet<IToolViewModel> changeSet)
{
IToolViewModel? changed = changeSet.FirstOrDefault()?.Item.Current;
if (changed == null)
return;
if (!changed.IsSelected || !changed.IsExclusive)
return;
// Disable all others if the changed one is selected and exclusive
_tools.Edit(list =>
{
foreach (IToolViewModel toolViewModel in list.Where(t => t.IsExclusive && t != changed))
toolViewModel.IsSelected = false;
});
}
} }

View File

@ -27,14 +27,14 @@ public class RegistrationService : IRegistrationService
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private bool _registeredBuiltInPropertyEditors; private bool _registeredBuiltInPropertyEditors;
public RegistrationService(IKernel kernel, public RegistrationService(IKernel kernel,
IInputService inputService, IInputService inputService,
IPropertyInputService propertyInputService, IPropertyInputService propertyInputService,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
INodeService nodeService, INodeService nodeService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
IDeviceLayoutService deviceLayoutService, // here to make sure it is instantiated IDeviceLayoutService deviceLayoutService // here to make sure it is instantiated
IEnumerable<IToolViewModel> toolViewModels) )
{ {
_kernel = kernel; _kernel = kernel;
_inputService = inputService; _inputService = inputService;
@ -42,8 +42,6 @@ public class RegistrationService : IRegistrationService
_nodeService = nodeService; _nodeService = nodeService;
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
foreach (IToolViewModel toolViewModel in toolViewModels)
profileEditorService.AddTool(toolViewModel);
CreateCursorResources(); CreateCursorResources();
} }
@ -90,7 +88,7 @@ public class RegistrationService : IRegistrationService
public void RegisterControllers() public void RegisterControllers()
{ {
} }
public void RegisterBuiltInNodeTypes() public void RegisterBuiltInNodeTypes()
{ {
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(bool), new SKColor(0xFFCD3232)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(bool), new SKColor(0xFFCD3232));