From fa710777d7bf5c61cd8ca9fec0838f89167ada12 Mon Sep 17 00:00:00 2001 From: Robert Date: Sat, 20 May 2023 23:11:44 +0200 Subject: [PATCH 1/7] UI - Use Timer instead of DispatcherTimer where feasible --- .../Controls/DeviceVisualizer.cs | 2 +- .../Builders/OpenFileDialogBuilder.cs | 2 +- .../Panels/Playback/PlaybackViewModel.cs | 19 +++++++------- .../DataBinding/DataBindingViewModel.cs | 21 +++++++--------- .../ModuleActivationRequirementViewModel.cs | 17 +++++-------- .../NodeScriptWindowViewModel.cs | 25 ++++++++----------- .../DisplayValueNodeCustomViewModel.cs | 8 +++--- 7 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index 3d8a3192e..111675973 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs @@ -34,7 +34,7 @@ public class DeviceVisualizer : Control /// public DeviceVisualizer() { - _timer = new DispatcherTimer(DispatcherPriority.Render) {Interval = TimeSpan.FromMilliseconds(1000.0 / UpdateFrameRate)}; + _timer = new DispatcherTimer(DispatcherPriority.Background) {Interval = TimeSpan.FromMilliseconds(1000.0 / UpdateFrameRate)}; _deviceVisualizerLeds = new List(); PointerReleased += OnPointerReleased; diff --git a/src/Artemis.UI.Shared/Services/Builders/OpenFileDialogBuilder.cs b/src/Artemis.UI.Shared/Services/Builders/OpenFileDialogBuilder.cs index 616b7977a..c9ef09d65 100644 --- a/src/Artemis.UI.Shared/Services/Builders/OpenFileDialogBuilder.cs +++ b/src/Artemis.UI.Shared/Services/Builders/OpenFileDialogBuilder.cs @@ -78,6 +78,6 @@ public class OpenFileDialogBuilder public async Task ShowAsync() { IReadOnlyList files = await _parent.StorageProvider.OpenFilePickerAsync(_options); - return files.Select(f => f.Path.LocalPath).ToArray(); + return files.Count == 0 ? null : files.Select(f => f.Path.LocalPath).ToArray(); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs index dc4390255..5e91ae70f 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs @@ -2,11 +2,11 @@ using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Timers; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Shared; using Artemis.UI.Shared.Services.ProfileEditor; -using Avalonia.Threading; using ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.Playback; @@ -53,13 +53,17 @@ public class PlaybackViewModel : ActivatableViewModelBase _formattedCurrentTime = _profileEditorService.Time.Select(t => $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}").ToProperty(this, vm => vm.FormattedCurrentTime).DisposeWith(d); _playing = _profileEditorService.Playing.ToProperty(this, vm => vm.Playing).DisposeWith(d); _keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d); - + + // Update timer + Timer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000)); + updateTimer.Elapsed += (_, _) => Update(); + updateTimer.DisposeWith(d); + _profileEditorService.Playing.Subscribe(_ => _lastUpdate = DateTime.Now).DisposeWith(d); + _profileEditorService.Playing.Subscribe(p => updateTimer.Enabled = p).DisposeWith(d); _lastUpdate = DateTime.MinValue; - DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000), DispatcherPriority.Background, Update); - updateTimer.Start(); + Disposable.Create(() => { - updateTimer.Stop(); _settingsService.GetSetting("ProfileEditor.RepeatTimeline", true).Value = _repeating && _repeatTimeline; _settingsService.GetSetting("ProfileEditor.RepeatSegment", false).Value = _repeating && _repeatSegment; }).DisposeWith(d); @@ -206,13 +210,10 @@ public class PlaybackViewModel : ActivatableViewModelBase return TimeSpan.Zero; } - private void Update(object? sender, EventArgs e) + private void Update() { try { - if (!Playing) - return; - if (_lastUpdate == DateTime.MinValue) _lastUpdate = DateTime.Now; diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs index 672e50490..7435ac361 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs @@ -2,6 +2,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; +using System.Timers; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.DryIoc.Factories; @@ -50,20 +51,16 @@ public class DataBindingViewModel : ActivatableViewModelBase .DisposeWith(d); _profileEditorService.Playing.CombineLatest(_profileEditorService.SuspendedEditing).Subscribe(tuple => _playing = tuple.First || tuple.Second).DisposeWith(d); - DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Background, Update); - // TODO: Remove in favor of saving each time a node editor command is executed - DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save); + Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000)); + Timer saveTimer = new(TimeSpan.FromMinutes(2)); + updateTimer.Elapsed += (_, _) => Update(); + saveTimer.Elapsed += (_, _) => Save(); updateTimer.Start(); saveTimer.Start(); - Disposable.Create(() => - { - updateTimer.Stop(); - saveTimer.Stop(); - - _profileEditorService.SaveProfile(); - }).DisposeWith(d); + updateTimer.DisposeWith(d); + saveTimer.DisposeWith(d); }); } @@ -97,7 +94,7 @@ public class DataBindingViewModel : ActivatableViewModelBase } } - private void Update(object? sender, EventArgs e) + private void Update() { // If playing the data binding will already be updated, no need to do it here if (_playing || !_alwaysApplyDataBindings.Value) @@ -106,7 +103,7 @@ public class DataBindingViewModel : ActivatableViewModelBase LayerProperty?.UpdateDataBinding(); } - private void Save(object? sender, EventArgs e) + private void Save() { if (!_editorOpen) _profileEditorService.SaveProfile(); diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ModuleActivationRequirementViewModel.cs b/src/Artemis.UI/Screens/Sidebar/Dialogs/ModuleActivationRequirementViewModel.cs index 8a51d3810..c7844442c 100644 --- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ModuleActivationRequirementViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ModuleActivationRequirementViewModel.cs @@ -1,8 +1,8 @@ using System; using System.Reactive.Disposables; +using System.Timers; using Artemis.Core.Modules; using Artemis.UI.Shared; -using Avalonia.Threading; using Humanizer; using ReactiveUI; @@ -13,7 +13,6 @@ public class ModuleActivationRequirementViewModel : ActivatableViewModelBase private readonly IModuleActivationRequirement _activationRequirement; private string _requirementDescription; private bool _requirementMet; - private DispatcherTimer? _updateTimer; public ModuleActivationRequirementViewModel(IModuleActivationRequirement activationRequirement) { @@ -23,14 +22,10 @@ public class ModuleActivationRequirementViewModel : ActivatableViewModelBase this.WhenActivated(d => { - _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, Update); - _updateTimer.Start(); - - Disposable.Create(() => - { - _updateTimer?.Stop(); - _updateTimer = null; - }).DisposeWith(d); + Timer updateTimer = new(TimeSpan.FromMilliseconds(500)); + updateTimer.Elapsed += (_, _) => Update(); + updateTimer.Start(); + updateTimer.DisposeWith(d); }); } @@ -48,7 +43,7 @@ public class ModuleActivationRequirementViewModel : ActivatableViewModelBase set => RaiseAndSetIfChanged(ref _requirementMet, value); } - private void Update(object? sender, EventArgs e) + private void Update() { RequirementDescription = _activationRequirement.GetUserFriendlyDescription(); RequirementMet = _activationRequirement.Evaluate(); diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs index ba87decff..9383ccdec 100644 --- a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs +++ b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reactive; using System.Reactive.Disposables; using System.Threading.Tasks; +using System.Timers; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.DryIoc.Factories; @@ -12,9 +13,7 @@ using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.NodeEditor; using Artemis.UI.Shared.Services.NodeEditor.Commands; using Avalonia; -using Avalonia.Threading; using DynamicData; -using DynamicData.List; using ReactiveUI; namespace Artemis.UI.Screens.VisualScripting; @@ -66,19 +65,17 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase this.WhenActivated(d => { _keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d); - - DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Background, Update); - // TODO: Remove in favor of saving each time a node editor command is executed - DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Background, Save); + Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000)); + Timer saveTimer = new(TimeSpan.FromMinutes(2)); + + updateTimer.Elapsed += (_, _) => Update(); + saveTimer.Elapsed += (_, _) => Save(); updateTimer.Start(); saveTimer.Start(); - Disposable.Create(() => - { - updateTimer.Stop(); - saveTimer.Stop(); - }).DisposeWith(d); + updateTimer.DisposeWith(d); + saveTimer.DisposeWith(d); }); } @@ -93,7 +90,7 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase public ReactiveCommand Export { get; } public ReactiveCommand Import { get; } public bool KeyBindingsEnabled => _keyBindingsEnabled?.Value ?? false; - + public PluginSetting ShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false); public PluginSetting ShowFullPaths => _settingsService.GetSetting("ProfileEditor.ShowFullPaths", false); public PluginSetting AlwaysShowValues => _settingsService.GetSetting("ProfileEditor.AlwaysShowValues", true); @@ -176,13 +173,13 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase } } - private void Update(object? sender, EventArgs e) + private void Update() { if (!_pauseUpdate) NodeScript.Run(); } - private void Save(object? sender, EventArgs e) + private void Save() { if (NodeScript.Context is Profile profile) _profileService.SaveProfile(profile, true); diff --git a/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomViewModel.cs b/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomViewModel.cs index f9e462de4..590a397fb 100644 --- a/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomViewModel.cs +++ b/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomViewModel.cs @@ -1,7 +1,6 @@ using System.Reactive.Disposables; using Artemis.Core; using Artemis.UI.Shared.VisualScripting; -using Avalonia.Threading; using ReactiveUI; namespace Artemis.VisualScripting.Nodes.Static.Screens; @@ -18,9 +17,10 @@ public class DisplayValueNodeCustomViewModel : CustomNodeViewModel // Because the DisplayValueNode has no output it never evaluates, manually do so here this.WhenActivated(d => { - DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Background, Update); + System.Timers.Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000)); + updateTimer.Elapsed += (_, _) => Update(); updateTimer.Start(); - Disposable.Create(() => updateTimer.Stop()).DisposeWith(d); + updateTimer.DisposeWith(d); }); } @@ -30,7 +30,7 @@ public class DisplayValueNodeCustomViewModel : CustomNodeViewModel private set => this.RaiseAndSetIfChanged(ref _currentValue, value); } - private void Update(object? sender, EventArgs e) + private void Update() { try { From 51f08379ebcf84284975e396e0ebba3a26d8b00c Mon Sep 17 00:00:00 2001 From: Diogo Trindade Date: Sat, 20 May 2023 22:15:08 +0100 Subject: [PATCH 2/7] Fix possible NRE in node script import --- .../Screens/VisualScripting/NodeScriptWindowViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs index 9383ccdec..5ec88e4fc 100644 --- a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs +++ b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs @@ -154,7 +154,7 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase .HavingFilter(f => f.WithExtension("json").WithName("Artemis node script")) .ShowAsync(); - if (result == null) + if (result == null || result.Length == 0) return; try From e190059623fa7e087cd92c6f0c0bc8e8661c38c2 Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 21 May 2023 10:33:15 +0200 Subject: [PATCH 3/7] Device visualizer - Only rerender dirty devices --- .../Controls/DeviceVisualizer.cs | 66 +++++++++++-------- .../Controls/DeviceVisualizerLed.cs | 24 ++----- 2 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index 111675973..eca232ba4 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; @@ -13,6 +12,10 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; +using RGB.NET.Core; +using Color = RGB.NET.Core.Color; +using Point = Avalonia.Point; +using Size = Avalonia.Size; namespace Artemis.UI.Shared; @@ -21,20 +24,20 @@ namespace Artemis.UI.Shared; /// public class DeviceVisualizer : Control { - private const double UpdateFrameRate = 25.0; + private const double UPDATE_FRAME_RATE = 25.0; private readonly List _deviceVisualizerLeds; private readonly DispatcherTimer _timer; private Rect _deviceBounds; private RenderTargetBitmap? _deviceImage; - private List? _dimmedLeds; - private List? _highlightedLeds; private ArtemisDevice? _oldDevice; - + private bool _loading; + private Color[] _previousState = Array.Empty(); + /// public DeviceVisualizer() { - _timer = new DispatcherTimer(DispatcherPriority.Background) {Interval = TimeSpan.FromMilliseconds(1000.0 / UpdateFrameRate)}; + _timer = new DispatcherTimer(DispatcherPriority.Background) {Interval = TimeSpan.FromMilliseconds(1000.0 / UPDATE_FRAME_RATE)}; _deviceVisualizerLeds = new List(); PointerReleased += OnPointerReleased; @@ -77,7 +80,7 @@ public class DeviceVisualizer : Control // Apply device scale using DrawingContext.PushedState scalePush = drawingContext.PushTransform(Matrix.CreateScale(Device.Scale, Device.Scale)); foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) - deviceVisualizerLed.RenderGeometry(drawingContext, false); + deviceVisualizerLed.RenderGeometry(drawingContext); } } finally @@ -112,6 +115,31 @@ public class DeviceVisualizer : Control Clicked?.Invoke(this, e); } + private bool IsDirty() + { + if (Device == null) + return false; + + Color[] state = new Color[Device.RgbDevice.Count()]; + bool difference = _previousState.Length != state.Length; + + // Check all LEDs for differences and copy the colors to a new state + int index = 0; + foreach (Led led in Device.RgbDevice) + { + if (!difference && !led.Color.Equals(_previousState[index])) + difference = true; + + state[index] = led.Color; + index++; + } + + // Store the new state for next time + _previousState = state; + + return difference; + } + private void Update() { InvalidateVisual(); @@ -131,7 +159,7 @@ public class DeviceVisualizer : Control private void TimerOnTick(object? sender, EventArgs e) { - if (ShowColors && IsVisible && Opacity > 0) + if (IsDirty() && ShowColors && IsVisible && Opacity > 0) Update(); } @@ -200,23 +228,6 @@ public class DeviceVisualizer : Control set => SetValue(ShowColorsProperty, value); } - /// - /// Gets or sets a list of LEDs to highlight - /// - public static readonly StyledProperty?> HighlightedLedsProperty = - AvaloniaProperty.Register?>(nameof(HighlightedLeds)); - - private bool _loading; - - /// - /// Gets or sets a list of LEDs to highlight - /// - public ObservableCollection? HighlightedLeds - { - get => GetValue(HighlightedLedsProperty); - set => SetValue(HighlightedLedsProperty, value); - } - #endregion #region Lifetime management @@ -254,9 +265,6 @@ public class DeviceVisualizer : Control private void SetupForDevice() { - _highlightedLeds = new List(); - _dimmedLeds = new List(); - lock (_deviceVisualizerLeds) { _deviceVisualizerLeds.Clear(); @@ -305,7 +313,7 @@ public class DeviceVisualizer : Control using DrawingContext context = renderTargetBitmap.CreateDrawingContext(); using Bitmap bitmap = new(device.Layout.Image.LocalPath); using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize); - + context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size)); lock (_deviceVisualizerLeds) { diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs index 6c69cc674..576dd836d 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs @@ -20,13 +20,7 @@ internal class DeviceVisualizerLed public DeviceVisualizerLed(ArtemisLed led) { Led = led; - LedRect = new Rect( - Led.RgbLed.Location.X, - Led.RgbLed.Location.Y, - Led.RgbLed.Size.Width, - Led.RgbLed.Size.Height - ); - + _fillBrush = new SolidColorBrush(); _penBrush = new SolidColorBrush(); _pen = new Pen(_penBrush) {LineJoin = PenLineJoin.Round}; @@ -35,7 +29,6 @@ internal class DeviceVisualizerLed } public ArtemisLed Led { get; } - public Rect LedRect { get; set; } public Geometry? DisplayGeometry { get; private set; } public void DrawBitmap(DrawingContext drawingContext, double scale) @@ -58,7 +51,7 @@ internal class DeviceVisualizerLed } } - public void RenderGeometry(DrawingContext drawingContext, bool dimmed) + public void RenderGeometry(DrawingContext drawingContext) { if (DisplayGeometry == null) return; @@ -66,17 +59,8 @@ internal class DeviceVisualizerLed byte r = Led.RgbLed.Color.GetR(); byte g = Led.RgbLed.Color.GetG(); byte b = Led.RgbLed.Color.GetB(); - - if (dimmed) - { - _fillBrush.Color = new Color(50, r, g, b); - _penBrush.Color = new Color(100, r, g, b); - } - else - { - _fillBrush.Color = new Color(100, r, g, b); - _penBrush.Color = new Color(255, r, g, b); - } + _fillBrush.Color = new Color(100, r, g, b); + _penBrush.Color = new Color(255, r, g, b); // Render the LED geometry drawingContext.DrawGeometry(_fillBrush, _pen, DisplayGeometry); From a846bd592edc62c11d316498df39b93ce89c5fde Mon Sep 17 00:00:00 2001 From: Diogo Trindade Date: Sun, 21 May 2023 15:33:37 +0100 Subject: [PATCH 4/7] Core - Do not load abstract plugin features UI - Replace Equals check with EqualityComparer --- src/Artemis.Core/Services/PluginManagementService.cs | 2 +- .../Services/PropertyInput/PropertyInputViewModel.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index fb304baf5..964f5b0dd 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -396,7 +396,7 @@ internal class PluginManagementService : IPluginManagementService List featureTypes; try { - featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t)).ToList(); + featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t) && !t.IsAbstract).ToList(); } catch (ReflectionTypeLoadException e) { diff --git a/src/Artemis.UI.Shared/Services/PropertyInput/PropertyInputViewModel.cs b/src/Artemis.UI.Shared/Services/PropertyInput/PropertyInputViewModel.cs index 1382de03b..ede68d21f 100644 --- a/src/Artemis.UI.Shared/Services/PropertyInput/PropertyInputViewModel.cs +++ b/src/Artemis.UI.Shared/Services/PropertyInput/PropertyInputViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -193,7 +194,7 @@ public abstract class PropertyInputViewModel : PropertyInputViewModel _updating = true; // Avoid unnecessary UI updates and validator cycles - if (Equals(_inputValue, LayerProperty.CurrentValue)) + if (EqualityComparer.Default.Equals(_inputValue, LayerProperty.CurrentValue)) return; // Override the input value From 613686121542f8c44d6ac2b47587b55e32913887 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 22 May 2023 15:03:10 +0200 Subject: [PATCH 5/7] UI - Use lower quality rendering on the device visualizer --- src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml | 2 +- src/Artemis.UI/Screens/Device/DeviceSettingsView.axaml | 2 +- .../ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml | 2 +- src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml | 2 +- src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml b/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml index 37234283e..22b63510e 100644 --- a/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml +++ b/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml @@ -30,12 +30,12 @@ - diff --git a/src/Artemis.UI/Screens/Device/DeviceSettingsView.axaml b/src/Artemis.UI/Screens/Device/DeviceSettingsView.axaml index 064ea7154..e3157ba28 100644 --- a/src/Artemis.UI/Screens/Device/DeviceSettingsView.axaml +++ b/src/Artemis.UI/Screens/Device/DeviceSettingsView.axaml @@ -18,7 +18,7 @@ Margin="10" ShowColors="False" Device="{CompiledBinding Device}" - RenderOptions.BitmapInterpolationMode="HighQuality" /> + RenderOptions.BitmapInterpolationMode="MediumQuality" />