diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
index d28885e65..0baf7d834 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
@@ -329,6 +329,13 @@ namespace Artemis.Core
/// A boolean indicating whether to remove excess LEDs present in the device but missing in the layout
internal void ApplyLayout(ArtemisLayout layout, bool createMissingLeds, bool removeExcessiveLeds)
{
+ if (createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
+ throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " +
+ "set to true because the device provider does not support it");
+ if (removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported)
+ throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} " +
+ "set to true because the device provider does not support it");
+
if (layout.IsValid)
layout.RgbLayout!.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
diff --git a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
index 949065503..f41e9c145 100644
--- a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
+++ b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
@@ -51,6 +51,18 @@ namespace Artemis.Core.DeviceProviders
///
public bool CanDetectLogicalLayout { get; protected set; }
+ ///
+ /// Gets or sets a boolean indicating whether adding missing LEDs defined in a layout but missing on the device is supported
+ /// Note: Defaults to .
+ ///
+ public bool CreateMissingLedsSupported { get; protected set; } = true;
+
+ ///
+ /// Gets or sets a boolean indicating whether removing excess LEDs present in the device but missing in the layout is supported
+ /// Note: Defaults to .
+ ///
+ public bool RemoveExcessiveLedsSupported { get; protected set; } = true;
+
///
/// Loads a layout for the specified device and wraps it in an
///
diff --git a/src/Artemis.Core/Services/Interfaces/IRgbService.cs b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
index 00c5f85d0..cfcb5ffb0 100644
--- a/src/Artemis.Core/Services/Interfaces/IRgbService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
@@ -90,9 +90,7 @@ namespace Artemis.Core.Services
///
///
///
- ///
- ///
- void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds);
+ void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
///
/// Attempts to retrieve the that corresponds the provided RGB.NET
diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs
index 4f812c886..5e4a5066a 100644
--- a/src/Artemis.Core/Services/RgbService.cs
+++ b/src/Artemis.Core/Services/RgbService.cs
@@ -312,7 +312,7 @@ namespace Artemis.Core.Services
layout = new ArtemisLayout(device.CustomLayoutPath, LayoutSource.Configured);
if (layout.IsValid)
{
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
}
@@ -321,7 +321,7 @@ namespace Artemis.Core.Services
layout = device.DeviceProvider.LoadUserLayout(device);
if (layout.IsValid)
{
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
@@ -329,13 +329,13 @@ namespace Artemis.Core.Services
layout = device.DeviceProvider.LoadLayout(device);
if (layout.IsValid)
{
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
// Finally fall back to a default layout
layout = LoadDefaultLayout(device);
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
@@ -344,9 +344,9 @@ namespace Artemis.Core.Services
return new("NYI", LayoutSource.Default);
}
- public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds)
+ public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout)
{
- device.ApplyLayout(layout, createMissingLeds, removeExessiveLeds);
+ device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
UpdateLedGroup();
}
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
index bc4b3648f..6dce06e42 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
@@ -10,7 +10,6 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core;
-using SkiaSharp;
using Stylet;
namespace Artemis.UI.Shared
@@ -88,6 +87,11 @@ namespace Artemis.UI.Shared
set => SetValue(HighlightedLedsProperty, value);
}
+ ///
+ /// Occurs when a LED of the device has been clicked
+ ///
+ public event EventHandler? LedClicked;
+
///
protected override void OnRender(DrawingContext drawingContext)
{
@@ -140,6 +144,27 @@ namespace Artemis.UI.Shared
return ResizeKeepAspect(deviceSize, availableSize.Width, availableSize.Height);
}
+ ///
+ /// Invokes the event
+ ///
+ ///
+ protected virtual void OnLedClicked(LedClickedEventArgs e)
+ {
+ LedClicked?.Invoke(this, e);
+ }
+
+ ///
+ /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
+ ///
+ ///
+ /// to release both managed and unmanaged resources;
+ /// to release only unmanaged resources.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing) _timer.Stop();
+ }
+
private static Size ResizeKeepAspect(Size src, double maxWidth, double maxHeight)
{
@@ -189,8 +214,8 @@ namespace Artemis.UI.Shared
return;
Point position = e.GetPosition(this);
- double x = (position.X / RenderSize.Width);
- double y = (position.Y / RenderSize.Height);
+ double x = position.X / RenderSize.Width;
+ double y = position.Y / RenderSize.Height;
Point scaledPosition = new(x * Device.Rectangle.Width, y * Device.Rectangle.Height);
DeviceVisualizerLed? deviceVisualizerLed = _deviceVisualizerLeds.FirstOrDefault(l => l.DisplayGeometry != null && l.LedRect.Contains(scaledPosition));
@@ -317,35 +342,6 @@ namespace Artemis.UI.Shared
drawingContext.Close();
}
- #region Events
-
- public event EventHandler? LedClicked;
-
- ///
- /// Invokes the event
- ///
- ///
- protected virtual void OnLedClicked(LedClickedEventArgs e)
- {
- LedClicked?.Invoke(this, e);
- }
-
- #endregion
-
- #region IDisposable
-
- ///
- /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
- ///
- ///
- /// to release both managed and unmanaged resources;
- /// to release only unmanaged resources.
- ///
- protected virtual void Dispose(bool disposing)
- {
- if (disposing) _timer.Stop();
- }
-
///
public void Dispose()
@@ -353,7 +349,5 @@ namespace Artemis.UI.Shared
Dispose(true);
GC.SuppressFinalize(this);
}
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreeGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreeGroupViewModel.cs
index 76c54481b..30d8fd822 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreeGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreeGroupViewModel.cs
@@ -32,7 +32,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
private readonly IDialogService _dialogService;
private readonly IProfileEditorService _profileEditorService;
private readonly IWindowManager _windowManager;
- private LayerBrushSettingsWindowViewModel? _layerBrushSettingsWindowVm;
+ private LayerBrushSettingsWindowViewModel _layerBrushSettingsWindowVm;
private LayerEffectSettingsWindowViewModel _layerEffectSettingsWindowVm;
public TreeGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel, IProfileEditorService profileEditorService, IDialogService dialogService, IWindowManager windowManager)
diff --git a/src/Artemis.UI/Screens/Settings/Device/DeviceDialogView.xaml b/src/Artemis.UI/Screens/Settings/Device/DeviceDialogView.xaml
index 8973e4109..8134150e7 100644
--- a/src/Artemis.UI/Screens/Settings/Device/DeviceDialogView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Device/DeviceDialogView.xaml
@@ -105,8 +105,8 @@
+
.Collection.OneActive
{
+ private readonly ICoreService _coreService;
private readonly IDeviceService _deviceService;
private readonly IDialogService _dialogService;
private readonly IRgbService _rgbService;
- private ArtemisLed _selectedLed;
private SnackbarMessageQueue _deviceMessageQueue;
+ private BindableCollection _selectedLeds;
- public DeviceDialogViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService, IDeviceDebugVmFactory factory)
+ public DeviceDialogViewModel(ArtemisDevice device,
+ ICoreService coreService,
+ IDeviceService deviceService,
+ IRgbService rgbService,
+ IDialogService dialogService,
+ IDeviceDebugVmFactory factory)
{
+ _coreService = coreService;
_deviceService = deviceService;
_rgbService = rgbService;
_dialogService = dialogService;
@@ -39,19 +47,8 @@ namespace Artemis.UI.Screens.Settings.Device
Items.Add(factory.DeviceLedsTabViewModel(device));
ActiveItem = Items.First();
DisplayName = $"{device.RgbDevice.DeviceInfo.Model} | Artemis";
- }
- protected override void OnInitialActivate()
- {
- DeviceMessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(5));
- Device.DeviceUpdated += DeviceOnDeviceUpdated;
- base.OnInitialActivate();
- }
-
- protected override void OnClose()
- {
- Device.DeviceUpdated -= DeviceOnDeviceUpdated;
- base.OnClose();
+ SelectedLeds = new BindableCollection();
}
public ArtemisDevice Device { get; }
@@ -63,29 +60,61 @@ namespace Artemis.UI.Screens.Settings.Device
set => SetAndNotify(ref _deviceMessageQueue, value);
}
- public ArtemisLed SelectedLed
- {
- get => _selectedLed;
- set
- {
- if (!SetAndNotify(ref _selectedLed, value)) return;
- NotifyOfPropertyChange(nameof(SelectedLeds));
- }
- }
-
public bool CanExportLayout => Device.Layout?.IsValid ?? false;
- public List SelectedLeds => SelectedLed != null ? new List {SelectedLed} : null;
+ public BindableCollection SelectedLeds
+ {
+ get => _selectedLeds;
+ set => SetAndNotify(ref _selectedLeds, value);
+ }
public bool CanOpenImageDirectory => Device.Layout?.Image != null;
+ public void OnLedClicked(object sender, LedClickedEventArgs e)
+ {
+ if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
+ SelectedLeds.Clear();
+ SelectedLeds.Add(e.Led);
+ }
+
+ protected override void OnInitialActivate()
+ {
+ _coreService.FrameRendering += CoreServiceOnFrameRendering;
+ DeviceMessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(5));
+ Device.DeviceUpdated += DeviceOnDeviceUpdated;
+ base.OnInitialActivate();
+ }
+
+ protected override void OnClose()
+ {
+ _coreService.FrameRendering -= CoreServiceOnFrameRendering;
+ Device.DeviceUpdated -= DeviceOnDeviceUpdated;
+ base.OnClose();
+ }
+
+ private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
+ {
+ if (SelectedLeds == null)
+ return;
+
+ using SKPaint highlightPaint = new() {Color = SKColors.White};
+ using SKPaint dimPaint = new() {Color = new SKColor(0, 0, 0, 192)};
+ foreach (ArtemisLed artemisLed in Device.Leds)
+ e.Canvas.DrawRect(artemisLed.AbsoluteRectangle, SelectedLeds.Contains(artemisLed) ? highlightPaint : dimPaint);
+ }
+
+ private void DeviceOnDeviceUpdated(object sender, EventArgs e)
+ {
+ NotifyOfPropertyChange(nameof(CanExportLayout));
+ }
+
// ReSharper disable UnusedMember.Global
#region Command handlers
public void ClearSelection()
{
- SelectedLed = null;
+ SelectedLeds.Clear();
}
public void IdentifyDevice()
@@ -190,19 +219,5 @@ namespace Artemis.UI.Screens.Settings.Device
#endregion
// ReSharper restore UnusedMember.Global
-
- #region Event handlers
-
- private void DeviceOnDeviceUpdated(object sender, EventArgs e)
- {
- NotifyOfPropertyChange(nameof(CanExportLayout));
- }
-
- public void OnLedClicked(object sender, LedClickedEventArgs e)
- {
- SelectedLed = e.Led;
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabView.xaml b/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabView.xaml
index 08b374383..6bfec663f 100644
--- a/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabView.xaml
@@ -4,34 +4,39 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
- xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
- xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DeviceLedsTabView"
+ xmlns:converters="clr-namespace:Artemis.UI.Converters"
+ x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DeviceLedsTabView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type tabs:DeviceLedsTabViewModel}}">
-
+
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabViewModel.cs
index d57f8cb1a..5ec8986f7 100644
--- a/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Device/Tabs/DeviceLedsTabViewModel.cs
@@ -1,17 +1,92 @@
-using Artemis.Core;
+using System.Collections.Specialized;
+using System.Linq;
+using Artemis.Core;
using Stylet;
namespace Artemis.UI.Screens.Settings.Device.Tabs
{
public class DeviceLedsTabViewModel : Screen
{
-
public DeviceLedsTabViewModel(ArtemisDevice device)
{
Device = device;
DisplayName = "LEDS";
+ LedViewModels = new BindableCollection();
}
public ArtemisDevice Device { get; }
+ public BindableCollection LedViewModels { get; }
+
+ private void SelectedLedsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ UpdateSelectedLeds();
+ }
+
+ private void UpdateSelectedLeds()
+ {
+ foreach (DeviceLedsTabLedViewModel deviceLedsTabLedViewModel in LedViewModels)
+ deviceLedsTabLedViewModel.Update();
+ }
+
+ #region Overrides of Screen
+
+ ///
+ protected override void OnInitialActivate()
+ {
+ BindableCollection selectedLeds = ((DeviceDialogViewModel) Parent).SelectedLeds;
+ LedViewModels.Clear();
+ LedViewModels.AddRange(Device.Leds.Select(l => new DeviceLedsTabLedViewModel(l, selectedLeds)));
+ selectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged;
+
+ base.OnInitialActivate();
+ }
+
+ ///
+ protected override void OnClose()
+ {
+ ((DeviceDialogViewModel) Parent).SelectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged;
+ base.OnClose();
+ }
+
+ #endregion
+ }
+
+ public class DeviceLedsTabLedViewModel : PropertyChangedBase
+ {
+ private readonly BindableCollection _selectedLeds;
+ private bool _isSelected;
+
+ public DeviceLedsTabLedViewModel(ArtemisLed artemisLed, BindableCollection selectedLeds)
+ {
+ _selectedLeds = selectedLeds;
+ ArtemisLed = artemisLed;
+
+ Update();
+ }
+
+ public ArtemisLed ArtemisLed { get; }
+
+ public bool IsSelected
+ {
+ get => _isSelected;
+ set
+ {
+ if (!SetAndNotify(ref _isSelected, value)) return;
+ Apply();
+ }
+ }
+
+ public void Update()
+ {
+ IsSelected = _selectedLeds.Contains(ArtemisLed);
+ }
+
+ public void Apply()
+ {
+ if (IsSelected && !_selectedLeds.Contains(ArtemisLed))
+ _selectedLeds.Add(ArtemisLed);
+ else if (!IsSelected && _selectedLeds.Contains(ArtemisLed))
+ _selectedLeds.Remove(ArtemisLed);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs
index a6a80b341..a4e8587dc 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs
@@ -16,7 +16,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
private readonly IPluginManagementService _pluginManagementService;
private bool _enabling;
private readonly IMessageService _messageService;
- private bool _canToggleEnabled;
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
bool showShield,