From b154badd9cf01607a5144073a658660fa88e334f Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 19 Feb 2021 16:42:09 +0100 Subject: [PATCH] Devices - Added custom layout loading --- .../Models/Surface/ArtemisDevice.cs | 20 ++++- .../Models/Surface/Layout/ArtemisLayout.cs | 55 ++++++++++-- .../Models/Surface/Layout/ArtemisLedLayout.cs | 5 +- .../Plugins/DeviceProviders/DeviceProvider.cs | 29 +++++-- .../Services/Interfaces/IRgbService.cs | 4 +- .../Services/PluginManagementService.cs | 13 +-- src/Artemis.Core/Services/RgbService.cs | 47 +++++++--- src/Artemis.Core/Utilities/Utilities.cs | 27 +++--- .../Entities/Surface/DeviceEntity.cs | 4 +- .../Controls/DeviceVisualizer.cs | 2 +- .../Controls/DeviceVisualizerLed.cs | 10 ++- src/Artemis.UI/Artemis.UI.csproj | 3 + .../Visualization/ProfileViewModel.cs | 2 +- .../Settings/Debug/DeviceDebugView.xaml | 76 ++++++++++------ .../Settings/Debug/DeviceDebugViewModel.cs | 73 ++++++++++++++-- .../Tabs/Devices/DeviceSettingsView.xaml | 2 +- .../Plugins/PluginSettingsTabViewModel.cs | 1 + .../Dialogs/SurfaceDeviceConfigView.xaml | 31 ++++++- .../Dialogs/SurfaceDeviceConfigViewModel.cs | 86 +++++++++++++++---- .../SurfaceEditor/SurfaceEditorViewModel.cs | 28 ++++++ .../Visualization/ListDeviceView.xaml | 6 +- 21 files changed, 414 insertions(+), 110 deletions(-) diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs index 9f39a9864..90aee62a2 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs @@ -249,6 +249,20 @@ namespace Artemis.Core } } + /// + /// Gets or sets the path of the custom layout to load when calling + /// for this device + /// + public string? CustomLayoutPath + { + get => DeviceEntity.CustomLayoutPath; + set + { + DeviceEntity.CustomLayoutPath = value; + OnPropertyChanged(nameof(CustomLayoutPath)); + } + } + /// /// Gets the layout of the device expanded with Artemis-specific data /// @@ -304,10 +318,12 @@ namespace Artemis.Core /// Applies the provided layout to the device /// /// The layout to apply - internal void ApplyLayout(ArtemisLayout layout) + /// A boolean indicating whether to add missing LEDs defined in the layout but missing on the device + /// 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 (layout.IsValid) - layout.RgbLayout!.ApplyTo(RgbDevice); + layout.RgbLayout!.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds); Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l)); diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs index 672594b9a..cc837ca3b 100644 --- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs +++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs @@ -15,9 +15,11 @@ namespace Artemis.Core /// Creates a new instance of the class /// /// The path of the layout XML file - public ArtemisLayout(string filePath) + /// The source from where this layout is being loaded + public ArtemisLayout(string filePath, LayoutSource source) { FilePath = filePath; + Source = source; Leds = new List(); LoadLayout(); @@ -29,9 +31,9 @@ namespace Artemis.Core public string FilePath { get; } /// - /// Gets the RGB.NET device layout + /// Gets the source from where this layout was loaded /// - public DeviceLayout RgbLayout { get; private set; } + public LayoutSource Source { get; } /// /// Gets the device this layout is applied to @@ -48,9 +50,20 @@ namespace Artemis.Core /// public Uri? Image { get; private set; } + /// + /// Gets a list of LEDs this layout contains + /// public List Leds { get; } - internal LayoutCustomDeviceData LayoutCustomDeviceData { get; set; } + /// + /// Gets the RGB.NET device layout + /// + public DeviceLayout RgbLayout { get; private set; } = null!; + + /// + /// Gets the custom layout data embedded in the RGB.NET layout + /// + public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!; public void ReloadFromDisk() { @@ -88,11 +101,43 @@ namespace Artemis.Core private void ApplyCustomDeviceData() { - Uri layoutDirectory = new(Path.GetDirectoryName(FilePath)! + "\\", UriKind.Absolute); + if (!IsValid) + { + Image = null; + return; + } + + Uri layoutDirectory = new(Path.GetDirectoryName(FilePath)! + "/", UriKind.Absolute); if (LayoutCustomDeviceData.DeviceImage != null) Image = new Uri(layoutDirectory, new Uri(LayoutCustomDeviceData.DeviceImage, UriKind.Relative)); else Image = null; } } + + /// + /// Represents a source from where a layout came + /// + public enum LayoutSource + { + /// + /// A layout loaded from config + /// + Configured, + + /// + /// A layout loaded from the user layout folder + /// + User, + + /// + /// A layout loaded from the plugin folder + /// + Plugin, + + /// + /// A default layout loaded as a fallback option + /// + Default + } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs index 28bee2d41..e0ee9fa07 100644 --- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs +++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs @@ -39,7 +39,10 @@ namespace Artemis.Core /// public Uri? Image { get; private set; } - internal LayoutCustomLedData LayoutCustomLedData { get; set; } + /// + /// Gets the custom layout data embedded in the RGB.NET layout + /// + public LayoutCustomLedData LayoutCustomLedData { get; } internal void ApplyDevice(ArtemisDevice device) { diff --git a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs index 838a5a87a..dd3d7ad5b 100644 --- a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs +++ b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs @@ -60,18 +60,35 @@ namespace Artemis.Core.DeviceProviders /// /// Loads a layout for the specified device and wraps it in an /// - /// The device to load the layout for + /// The device to load the layout for /// The resulting Artemis layout - public virtual ArtemisLayout LoadLayout(ArtemisDevice rgbDevice) + public virtual ArtemisLayout LoadLayout(ArtemisDevice device) { string layoutDir = Path.Combine(Plugin.Directory.FullName, "Layouts"); string filePath = Path.Combine( layoutDir, - rgbDevice.RgbDevice.DeviceInfo.Manufacturer, - rgbDevice.RgbDevice.DeviceInfo.DeviceType.ToString(), - rgbDevice.GetLayoutFileName() + device.RgbDevice.DeviceInfo.Manufacturer, + device.RgbDevice.DeviceInfo.DeviceType.ToString(), + device.GetLayoutFileName() ); - return new ArtemisLayout(filePath); + return new ArtemisLayout(filePath, LayoutSource.Plugin); + } + + /// + /// Loads a layout from the user layout folder for the specified device and wraps it in an + /// + /// The device to load the layout for + /// The resulting Artemis layout + public virtual ArtemisLayout LoadUserLayout(ArtemisDevice device) + { + string layoutDir = Path.Combine(Constants.DataFolder, "user layouts"); + string filePath = Path.Combine( + layoutDir, + device.RgbDevice.DeviceInfo.Manufacturer, + device.RgbDevice.DeviceInfo.DeviceType.ToString(), + device.GetLayoutFileName() + ); + return new ArtemisLayout(filePath, LayoutSource.User); } /// diff --git a/src/Artemis.Core/Services/Interfaces/IRgbService.cs b/src/Artemis.Core/Services/Interfaces/IRgbService.cs index 3009ecccc..838f15ff8 100644 --- a/src/Artemis.Core/Services/Interfaces/IRgbService.cs +++ b/src/Artemis.Core/Services/Interfaces/IRgbService.cs @@ -74,7 +74,9 @@ namespace Artemis.Core.Services /// /// /// - void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout); + /// + /// + void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds); /// /// Attempts to retrieve the that corresponds the provided RGB.NET diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 30e8d79f0..31dc0070c 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -9,7 +9,6 @@ using Artemis.Core.DeviceProviders; using Artemis.Core.Ninject; using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Repositories.Interfaces; -using Humanizer; using McMaster.NETCore.Plugins; using Ninject; using Ninject.Extensions.ChildKernel; @@ -147,7 +146,7 @@ namespace Artemis.Core.Services // TODO: move to a more appropriate service public DeviceProvider GetDeviceProviderByDevice(IRGBDevice rgbDevice) { - return GetFeaturesOfType().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice)); + return GetFeaturesOfType().First(d => d.RgbDeviceProvider.Devices.Contains(rgbDevice)); } public Plugin? GetCallingPlugin() @@ -186,7 +185,6 @@ namespace Artemis.Core.Services // Load the plugin assemblies into the plugin context DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins")); foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories()) - { try { LoadPlugin(subDirectory); @@ -195,7 +193,6 @@ namespace Artemis.Core.Services { _logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception"); } - } // ReSharper disable InconsistentlySynchronizedField - It's read-only, idc _logger.Debug("Loaded {count} plugin(s)", _plugins.Count); @@ -338,7 +335,6 @@ namespace Artemis.Core.Services // Create instances of each feature and add them to the plugin // Construction should be simple and not contain any logic so failure at this point means the entire plugin fails foreach (Type featureType in featureTypes) - { try { plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope(); @@ -360,11 +356,9 @@ namespace Artemis.Core.Services { _logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin); } - } // Activate plugins after they are all loaded foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled)) - { try { EnablePluginFeature(pluginFeature, false, !ignorePluginLock); @@ -373,7 +367,6 @@ namespace Artemis.Core.Services { // ignored, logged in EnablePluginFeature } - } if (saveState) { @@ -474,13 +467,11 @@ namespace Artemis.Core.Services Directory.CreateDirectory(directoryInfo.FullName); string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, ""); foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries) - { if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory)) { string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length)); zipArchiveEntry.ExtractToFile(target); } - } // Load the newly extracted plugin and return the result return LoadPlugin(directoryInfo); @@ -563,10 +554,8 @@ namespace Artemis.Core.Services private void SavePlugin(Plugin plugin) { foreach (PluginFeature pluginFeature in plugin.Features) - { if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName)) plugin.Entity.Features.Add(pluginFeature.Entity); - } _pluginRepository.SavePlugin(plugin.Entity); } diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs index a494e34eb..3cec517d6 100644 --- a/src/Artemis.Core/Services/RgbService.cs +++ b/src/Artemis.Core/Services/RgbService.cs @@ -191,26 +191,53 @@ namespace Artemis.Core.Services public ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device) { - // Look for a user layout - // ... here + ArtemisLayout layout; - // Try loading a device provider layout, if that comes back valid we use that - ArtemisLayout layout = device.DeviceProvider.LoadLayout(device); + // Configured layout path takes precedence over all other options + if (device.CustomLayoutPath != null) + { + layout = new ArtemisLayout(device.CustomLayoutPath, LayoutSource.Configured); + if (layout.IsValid) + { + ApplyDeviceLayout(device, layout, true, true); + return layout; + } + } + + // Look for a layout provided by the user + layout = device.DeviceProvider.LoadUserLayout(device); + if (layout.IsValid) + { + ApplyDeviceLayout(device, layout, true, true); + return layout; + } + + // Look for a layout provided by the plugin + layout = device.DeviceProvider.LoadLayout(device); + if (layout.IsValid) + { + ApplyDeviceLayout(device, layout, true, true); + return layout; + } // Finally fall back to a default layout - // .. do it! - - ApplyDeviceLayout(device, layout); + layout = LoadDefaultLayout(device); + ApplyDeviceLayout(device, layout, true, true); return layout; } - public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout) + private ArtemisLayout LoadDefaultLayout(ArtemisDevice device) { - device.ApplyLayout(layout); + return new ArtemisLayout("NYI", LayoutSource.Default); + } + + public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds) + { + device.ApplyLayout(layout, createMissingLeds, removeExessiveLeds); // Applying layouts can affect LEDs, update LED group UpdateBitmapBrush(); } - + public ArtemisDevice? GetDevice(IRGBDevice rgbDevice) { return _devices.FirstOrDefault(d => d.RgbDevice == rgbDevice); diff --git a/src/Artemis.Core/Utilities/Utilities.cs b/src/Artemis.Core/Utilities/Utilities.cs index 9d7e7de8e..8577185bc 100644 --- a/src/Artemis.Core/Utilities/Utilities.cs +++ b/src/Artemis.Core/Utilities/Utilities.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -18,8 +17,9 @@ namespace Artemis.Core /// public static void PrepareFirstLaunch() { - CreateArtemisFolderIfMissing(Constants.DataFolder); - CreateArtemisFolderIfMissing(Constants.DataFolder + "plugins"); + CreateAccessibleDirectory(Constants.DataFolder); + CreateAccessibleDirectory(Constants.DataFolder + "plugins"); + CreateAccessibleDirectory(Constants.DataFolder + "user layouts"); } /// @@ -62,15 +62,11 @@ namespace Artemis.Core } /// - /// Gets the current application location + /// Creates all directories and subdirectories in the specified path unless they already exist with permissions + /// allowing access by everyone. /// - /// - internal static string GetCurrentLocation() - { - return Process.GetCurrentProcess().MainModule!.FileName!; - } - - private static void CreateArtemisFolderIfMissing(string path) + /// The directory to create. + public static void CreateAccessibleDirectory(string path) { if (!Directory.Exists(path)) { @@ -92,6 +88,15 @@ namespace Artemis.Core } } + /// + /// Gets the current application location + /// + /// + internal static string GetCurrentLocation() + { + return Process.GetCurrentProcess().MainModule!.FileName!; + } + private static void OnRestartRequested(RestartEventArgs e) { RestartRequested?.Invoke(null, e); diff --git a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs index 142786138..670cec969 100644 --- a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs +++ b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs @@ -22,8 +22,10 @@ namespace Artemis.Storage.Entities.Surface public int PhysicalLayout { get; set; } public string LogicalLayout { get; set; } - + public string CustomLayoutPath { get; set; } + public List InputIdentifiers { get; set; } + } public class DeviceInputIdentifierEntity diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index a7ce6b3d1..397dd2dae 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs @@ -286,7 +286,7 @@ namespace Artemis.UI.Shared InvalidateMeasure(); } - private void DeviceUpdated(object sender, EventArgs e) + private void DeviceUpdated(object? sender, EventArgs e) { SetupForDevice(); } diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs index 7c6af66b1..c3ba1fd6a 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs @@ -12,6 +12,7 @@ namespace Artemis.UI.Shared internal class DeviceVisualizerLed { private SolidColorBrush? _renderColorBrush; + private Color _renderColor; public DeviceVisualizerLed(ArtemisLed led) { @@ -40,13 +41,18 @@ namespace Artemis.UI.Shared if (DisplayGeometry == null) return; + _renderColorBrush ??= new SolidColorBrush(); + RGB.NET.Core.Color originalColor = Led.GetOriginalColor(); byte r = originalColor.GetR(); byte g = originalColor.GetG(); byte b = originalColor.GetB(); - _renderColorBrush ??= new SolidColorBrush(); - _renderColorBrush.Color = isDimmed ? Color.FromArgb(100, r, g, b) : Color.FromRgb(r, g, b); + _renderColor.A = isDimmed ? 100 : 255; + _renderColor.R = r; + _renderColor.G = g; + _renderColor.B = b; + _renderColorBrush.Color = _renderColor; drawingContext.DrawRectangle(_renderColorBrush, null, LedRect); } diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 55ecf0e3a..a78e9658a 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -65,6 +65,9 @@ ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Groups.dll + + ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Layout.dll + diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index 9fae16063..071884dcd 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -179,7 +179,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization base.OnClose(); } - private void RgbServiceOnDevicesModified(object? sender, DeviceEventArgs e) + private void RgbServiceOnDevicesModified(object sender, DeviceEventArgs e) { ApplyDevices(); } diff --git a/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml b/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml index ba1a04987..80dc57da4 100644 --- a/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml +++ b/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml @@ -40,9 +40,31 @@ - + + + + @@ -56,12 +78,12 @@ - + In this window you can view detailed information of the device. Please note that having this window open can have a performance impact on your system. - + - - - - - - - - + + + + + + + + @@ -93,7 +115,7 @@ Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" /> - + @@ -111,7 +133,7 @@ Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" /> - + @@ -129,7 +151,7 @@ Text="{Binding Device.RgbDevice.DeviceInfo.DeviceType}" /> - + @@ -147,7 +169,7 @@ Text="{Binding Device.PhysicalLayout}" /> - + @@ -166,9 +188,7 @@ - - - + @@ -185,7 +205,7 @@ Text="{Binding Device.RgbDevice.Size}" /> - + @@ -202,7 +222,7 @@ Text="{Binding Device.RgbDevice.Location}" /> - + @@ -220,7 +240,7 @@ Text="{Binding Device.RgbDevice.Rotation.Degrees}" /> - + @@ -238,7 +258,7 @@ Text="{Binding Device.LogicalLayout}" /> - + @@ -257,11 +277,11 @@ - - - + + + - + - View surface properties + Device properties diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs index 57f54847c..5d17a2c72 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs @@ -57,6 +57,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins // Take it off the UI thread to avoid freezing on tab change Task.Run(async () => { + Items.Clear(); await Task.Delay(200); _instances = _pluginManagementService.GetAllPlugins() .Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p)) diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml index 52b34bab4..561b259f7 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml @@ -155,12 +155,39 @@ + Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}" + VerticalAlignment="Center" /> + + + Custom layout + + + Select a custom layout below if you want to change the appearance and/or LEDs of this device. + + + + + + + + Layout path + + + + + diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs index 8d96c2147..ad06f0ca0 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs @@ -1,7 +1,12 @@ -using System.Threading.Tasks; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Input; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Shared.Services; +using MaterialDesignThemes.Wpf; +using Ookii.Dialogs.Wpf; using SkiaSharp; using Stylet; @@ -10,6 +15,8 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs public class SurfaceDeviceConfigViewModel : DialogViewModelBase { private readonly ICoreService _coreService; + private readonly IRgbService _rgbService; + private readonly IMessageService _messageService; private readonly double _initialRedScale; private readonly double _initialGreenScale; private readonly double _initialBlueScale; @@ -23,41 +30,43 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs private SKColor _currentColor; private bool _displayOnDevices; - public SurfaceDeviceConfigViewModel(ArtemisDevice device, ICoreService coreService, IModelValidator validator) : base(validator) + public SurfaceDeviceConfigViewModel(ArtemisDevice device, + ICoreService coreService, + IRgbService rgbService, + IMessageService messageService, + IModelValidator validator) : base(validator) { _coreService = coreService; + _rgbService = rgbService; + _messageService = messageService; Device = device; - X = (int)Device.X; - Y = (int)Device.Y; + X = (int) Device.X; + Y = (int) Device.Y; Scale = Device.Scale; - Rotation = (int)Device.Rotation; + Rotation = (int) Device.Rotation; RedScale = Device.RedScale * 100d; GreenScale = Device.GreenScale * 100d; BlueScale = Device.BlueScale * 100d; //we need to store the initial values to be able to restore them when the user clicks "Cancel" - _initialRedScale = Device.RedScale; + _initialRedScale = Device.RedScale; _initialGreenScale = Device.GreenScale; _initialBlueScale = Device.BlueScale; CurrentColor = SKColors.White; _coreService.FrameRendering += OnFrameRendering; - } - - private void OnFrameRendering(object sender, FrameRenderingEventArgs e) - { - if (!_displayOnDevices) - return; - - using SKPaint overlayPaint = new() - { - Color = CurrentColor - }; - e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint); + Device.PropertyChanged += DeviceOnPropertyChanged; } public ArtemisDevice Device { get; } + public override void OnDialogClosed(object sender, DialogClosingEventArgs e) + { + _coreService.FrameRendering -= OnFrameRendering; + Device.PropertyChanged -= DeviceOnPropertyChanged; + base.OnDialogClosed(sender, e); + } + public int X { get => _x; @@ -130,7 +139,6 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs Device.BlueScale = BlueScale / 100d; _coreService.ModuleRenderingDisabled = false; - _coreService.FrameRendering -= OnFrameRendering; Session.Close(true); } @@ -141,6 +149,26 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs Device.BlueScale = BlueScale / 100d; } + public void BrowseCustomLayout(object sender, MouseEventArgs e) + { + if (e.OriginalSource is Button) + { + Device.CustomLayoutPath = null; + _messageService.ShowMessage("Cleared imported layout"); + return; + } + + VistaOpenFileDialog dialog = new(); + dialog.Filter = "Layout files (*.xml)|*.xml"; + dialog.Title = "Select device layout file"; + bool? result = dialog.ShowDialog(); + if (result == true) + { + Device.CustomLayoutPath = dialog.FileName; + _messageService.ShowMessage($"Imported layout from {dialog.FileName}"); + } + } + public override void Cancel() { Device.RedScale = _initialRedScale; @@ -149,5 +177,25 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs base.Cancel(); } + + private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Device.CustomLayoutPath)) + { + _rgbService.ApplyBestDeviceLayout(Device); + } + } + + private void OnFrameRendering(object sender, FrameRenderingEventArgs e) + { + if (!_displayOnDevices) + return; + + using SKPaint overlayPaint = new() + { + Color = CurrentColor + }; + e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs index d736c5f0c..714002c0d 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs @@ -16,6 +16,7 @@ using Artemis.UI.Screens.SurfaceEditor.Dialogs; using Artemis.UI.Screens.SurfaceEditor.Visualization; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; +using SkiaSharp; using Stylet; using MouseButton = System.Windows.Input.MouseButton; @@ -27,13 +28,16 @@ namespace Artemis.UI.Screens.SurfaceEditor private readonly IInputService _inputService; private readonly IDialogService _dialogService; private readonly IRgbService _rgbService; + private readonly ICoreService _coreService; private readonly ISettingsService _settingsService; private Cursor _cursor; private PanZoomViewModel _panZoomViewModel; private RectangleGeometry _selectionRectangle; private PluginSetting _surfaceListWidth; + private List _shuffledDevices; public SurfaceEditorViewModel(IRgbService rgbService, + ICoreService coreService, IDialogService dialogService, ISettingsService settingsService, IDeviceService deviceService, @@ -48,6 +52,7 @@ namespace Artemis.UI.Screens.SurfaceEditor ListDeviceViewModels = new BindableCollection(); _rgbService = rgbService; + _coreService = coreService; _dialogService = dialogService; _settingsService = settingsService; _deviceService = deviceService; @@ -105,6 +110,22 @@ namespace Artemis.UI.Screens.SurfaceEditor SurfaceListWidth.Save(); } + + private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e) + { + float amount = 360f / _shuffledDevices.Count; + for (int i = 0; i < _shuffledDevices.Count; i++) + { + ArtemisDevice rgbServiceDevice = _shuffledDevices[i]; + foreach (ArtemisLed artemisLed in rgbServiceDevice.Leds) + { + SKColor color = SKColor.FromHsv(amount * i, 100, 100); + e.Canvas.DrawRect(artemisLed.AbsoluteRectangle, new SKPaint(){Color = color}); + } + } + + } + #region Overrides of Screen protected override void OnInitialActivate() @@ -112,6 +133,10 @@ namespace Artemis.UI.Screens.SurfaceEditor LoadWorkspaceSettings(); SurfaceDeviceViewModels.AddRange(_rgbService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => new SurfaceDeviceViewModel(d, _rgbService))); ListDeviceViewModels.AddRange(_rgbService.EnabledDevices.OrderBy(d => d.ZIndex * -1).Select(d => new ListDeviceViewModel(d))); + _shuffledDevices = _rgbService.EnabledDevices.OrderBy(d => Guid.NewGuid()).ToList(); + + _coreService.FrameRendering += CoreServiceOnFrameRendering; + base.OnInitialActivate(); } @@ -120,6 +145,9 @@ namespace Artemis.UI.Screens.SurfaceEditor SaveWorkspaceSettings(); SurfaceDeviceViewModels.Clear(); ListDeviceViewModels.Clear(); + + _coreService.FrameRendering -= CoreServiceOnFrameRendering; + base.OnClose(); } diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Visualization/ListDeviceView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/Visualization/ListDeviceView.xaml index c2fa66ce8..97a252739 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Visualization/ListDeviceView.xaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/Visualization/ListDeviceView.xaml @@ -14,6 +14,10 @@ - + + + + + \ No newline at end of file