diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index b6cf25e6d..ed6e16421 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -43,9 +43,9 @@ - - - + + + 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/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj index c297f372f..0c4439627 100644 --- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj +++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index 3d8a3192e..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.Render) {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); diff --git a/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml b/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml index 8322b5738..0c571239e 100644 --- a/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml +++ b/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml @@ -32,8 +32,7 @@ SimpleNumberFormat="{CompiledBinding $parent[sharedControls:DraggableNumberBox].SimpleNumberFormat}" attachedProperties:NumberBoxAssist.PrefixText="{CompiledBinding $parent[sharedControls:DraggableNumberBox].Prefix}" attachedProperties:NumberBoxAssist.SuffixText="{CompiledBinding $parent[sharedControls:DraggableNumberBox].Suffix}" - HorizontalAlignment="{CompiledBinding $parent[sharedControls:DraggableNumberBox].HorizontalAlignment}" - ValueChanged="NumberBox_OnValueChanged"/> + HorizontalAlignment="{CompiledBinding $parent[sharedControls:DraggableNumberBox].HorizontalAlignment}"/> diff --git a/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml.cs b/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml.cs index 663a76a13..1b3124421 100644 --- a/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml.cs +++ b/src/Artemis.UI.Shared/Controls/DraggableNumberBox.axaml.cs @@ -4,7 +4,7 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; +using Avalonia.LogicalTree; using Avalonia.VisualTree; using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; @@ -68,13 +68,12 @@ public partial class DraggableNumberBox : UserControl public DraggableNumberBox() { InitializeComponent(); - InnerNumberBox.Value = Value; - + PointerPressed += OnPointerPressed; PointerMoved += OnPointerMoved; PointerReleased += OnPointerReleased; PropertyChanged += OnPropertyChanged; - + AddHandler(KeyUpEvent, HandleKeyUp, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true); } @@ -159,7 +158,22 @@ public partial class DraggableNumberBox : UserControl /// Occurs when the user finishes dragging over the control. /// public event TypedEventHandler? DragFinished; - + + /// + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + InnerNumberBox.Value = Value; + InnerNumberBox.ValueChanged += InnerNumberBoxOnValueChanged; + base.OnAttachedToLogicalTree(e); + } + + /// + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + InnerNumberBox.ValueChanged -= InnerNumberBoxOnValueChanged; + base.OnDetachedFromLogicalTree(e); + } + private void SetNumberBoxValue(double value) { if (!(Math.Abs(InnerNumberBox.Value - Value) > 0.00001)) @@ -243,14 +257,14 @@ public partial class DraggableNumberBox : UserControl e.Handled = true; } - + private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == ValueProperty) SetNumberBoxValue(Value); } - private void NumberBox_OnValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) + private void InnerNumberBoxOnValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) { if (_updating) return; 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.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 diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 054ca5857..ce2eaaf18 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -32,8 +32,8 @@ - - + + 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" />