mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-31 17:53:32 +00:00
Devices - Added custom layout loading
This commit is contained in:
parent
056b2bface
commit
b154badd9c
@ -249,6 +249,20 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the path of the custom layout to load when calling <see cref="IRgbService.ApplyBestDeviceLayout" />
|
||||||
|
/// for this device
|
||||||
|
/// </summary>
|
||||||
|
public string? CustomLayoutPath
|
||||||
|
{
|
||||||
|
get => DeviceEntity.CustomLayoutPath;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
DeviceEntity.CustomLayoutPath = value;
|
||||||
|
OnPropertyChanged(nameof(CustomLayoutPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the layout of the device expanded with Artemis-specific data
|
/// Gets the layout of the device expanded with Artemis-specific data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -304,10 +318,12 @@ namespace Artemis.Core
|
|||||||
/// Applies the provided layout to the device
|
/// Applies the provided layout to the device
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="layout">The layout to apply</param>
|
/// <param name="layout">The layout to apply</param>
|
||||||
internal void ApplyLayout(ArtemisLayout layout)
|
/// <param name="createMissingLeds">A boolean indicating whether to add missing LEDs defined in the layout but missing on the device</param>
|
||||||
|
/// <param name="removeExcessiveLeds">A boolean indicating whether to remove excess LEDs present in the device but missing in the layout</param>
|
||||||
|
internal void ApplyLayout(ArtemisLayout layout, bool createMissingLeds, bool removeExcessiveLeds)
|
||||||
{
|
{
|
||||||
if (layout.IsValid)
|
if (layout.IsValid)
|
||||||
layout.RgbLayout!.ApplyTo(RgbDevice);
|
layout.RgbLayout!.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
|
||||||
|
|
||||||
Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
|
Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
|
||||||
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
|
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
|
||||||
|
|||||||
@ -15,9 +15,11 @@ namespace Artemis.Core
|
|||||||
/// Creates a new instance of the <see cref="ArtemisLayout" /> class
|
/// Creates a new instance of the <see cref="ArtemisLayout" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filePath">The path of the layout XML file</param>
|
/// <param name="filePath">The path of the layout XML file</param>
|
||||||
public ArtemisLayout(string filePath)
|
/// <param name="source">The source from where this layout is being loaded</param>
|
||||||
|
public ArtemisLayout(string filePath, LayoutSource source)
|
||||||
{
|
{
|
||||||
FilePath = filePath;
|
FilePath = filePath;
|
||||||
|
Source = source;
|
||||||
Leds = new List<ArtemisLedLayout>();
|
Leds = new List<ArtemisLedLayout>();
|
||||||
|
|
||||||
LoadLayout();
|
LoadLayout();
|
||||||
@ -29,9 +31,9 @@ namespace Artemis.Core
|
|||||||
public string FilePath { get; }
|
public string FilePath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the RGB.NET device layout
|
/// Gets the source from where this layout was loaded
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DeviceLayout RgbLayout { get; private set; }
|
public LayoutSource Source { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the device this layout is applied to
|
/// Gets the device this layout is applied to
|
||||||
@ -48,9 +50,20 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Uri? Image { get; private set; }
|
public Uri? Image { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of LEDs this layout contains
|
||||||
|
/// </summary>
|
||||||
public List<ArtemisLedLayout> Leds { get; }
|
public List<ArtemisLedLayout> Leds { get; }
|
||||||
|
|
||||||
internal LayoutCustomDeviceData LayoutCustomDeviceData { get; set; }
|
/// <summary>
|
||||||
|
/// Gets the RGB.NET device layout
|
||||||
|
/// </summary>
|
||||||
|
public DeviceLayout RgbLayout { get; private set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the custom layout data embedded in the RGB.NET layout
|
||||||
|
/// </summary>
|
||||||
|
public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!;
|
||||||
|
|
||||||
public void ReloadFromDisk()
|
public void ReloadFromDisk()
|
||||||
{
|
{
|
||||||
@ -88,11 +101,43 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void ApplyCustomDeviceData()
|
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)
|
if (LayoutCustomDeviceData.DeviceImage != null)
|
||||||
Image = new Uri(layoutDirectory, new Uri(LayoutCustomDeviceData.DeviceImage, UriKind.Relative));
|
Image = new Uri(layoutDirectory, new Uri(LayoutCustomDeviceData.DeviceImage, UriKind.Relative));
|
||||||
else
|
else
|
||||||
Image = null;
|
Image = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a source from where a layout came
|
||||||
|
/// </summary>
|
||||||
|
public enum LayoutSource
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A layout loaded from config
|
||||||
|
/// </summary>
|
||||||
|
Configured,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A layout loaded from the user layout folder
|
||||||
|
/// </summary>
|
||||||
|
User,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A layout loaded from the plugin folder
|
||||||
|
/// </summary>
|
||||||
|
Plugin,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A default layout loaded as a fallback option
|
||||||
|
/// </summary>
|
||||||
|
Default
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -39,7 +39,10 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Uri? Image { get; private set; }
|
public Uri? Image { get; private set; }
|
||||||
|
|
||||||
internal LayoutCustomLedData LayoutCustomLedData { get; set; }
|
/// <summary>
|
||||||
|
/// Gets the custom layout data embedded in the RGB.NET layout
|
||||||
|
/// </summary>
|
||||||
|
public LayoutCustomLedData LayoutCustomLedData { get; }
|
||||||
|
|
||||||
internal void ApplyDevice(ArtemisDevice device)
|
internal void ApplyDevice(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -60,18 +60,35 @@ namespace Artemis.Core.DeviceProviders
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads a layout for the specified device and wraps it in an <see cref="ArtemisLayout" />
|
/// Loads a layout for the specified device and wraps it in an <see cref="ArtemisLayout" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rgbDevice">The device to load the layout for</param>
|
/// <param name="device">The device to load the layout for</param>
|
||||||
/// <returns>The resulting Artemis layout</returns>
|
/// <returns>The resulting Artemis layout</returns>
|
||||||
public virtual ArtemisLayout LoadLayout(ArtemisDevice rgbDevice)
|
public virtual ArtemisLayout LoadLayout(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
string layoutDir = Path.Combine(Plugin.Directory.FullName, "Layouts");
|
string layoutDir = Path.Combine(Plugin.Directory.FullName, "Layouts");
|
||||||
string filePath = Path.Combine(
|
string filePath = Path.Combine(
|
||||||
layoutDir,
|
layoutDir,
|
||||||
rgbDevice.RgbDevice.DeviceInfo.Manufacturer,
|
device.RgbDevice.DeviceInfo.Manufacturer,
|
||||||
rgbDevice.RgbDevice.DeviceInfo.DeviceType.ToString(),
|
device.RgbDevice.DeviceInfo.DeviceType.ToString(),
|
||||||
rgbDevice.GetLayoutFileName()
|
device.GetLayoutFileName()
|
||||||
);
|
);
|
||||||
return new ArtemisLayout(filePath);
|
return new ArtemisLayout(filePath, LayoutSource.Plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a layout from the user layout folder for the specified device and wraps it in an <see cref="ArtemisLayout" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to load the layout for</param>
|
||||||
|
/// <returns>The resulting Artemis layout</returns>
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -74,7 +74,9 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="device"></param>
|
/// <param name="device"></param>
|
||||||
/// <param name="layout"></param>
|
/// <param name="layout"></param>
|
||||||
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
|
/// <param name="createMissingLeds"></param>
|
||||||
|
/// <param name="removeExessiveLeds"></param>
|
||||||
|
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to retrieve the <see cref="ArtemisDevice" /> that corresponds the provided RGB.NET
|
/// Attempts to retrieve the <see cref="ArtemisDevice" /> that corresponds the provided RGB.NET
|
||||||
|
|||||||
@ -9,7 +9,6 @@ using Artemis.Core.DeviceProviders;
|
|||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using Humanizer;
|
|
||||||
using McMaster.NETCore.Plugins;
|
using McMaster.NETCore.Plugins;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Ninject.Extensions.ChildKernel;
|
using Ninject.Extensions.ChildKernel;
|
||||||
@ -147,7 +146,7 @@ namespace Artemis.Core.Services
|
|||||||
// TODO: move to a more appropriate service
|
// TODO: move to a more appropriate service
|
||||||
public DeviceProvider GetDeviceProviderByDevice(IRGBDevice rgbDevice)
|
public DeviceProvider GetDeviceProviderByDevice(IRGBDevice rgbDevice)
|
||||||
{
|
{
|
||||||
return GetFeaturesOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
|
return GetFeaturesOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices.Contains(rgbDevice));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plugin? GetCallingPlugin()
|
public Plugin? GetCallingPlugin()
|
||||||
@ -186,7 +185,6 @@ namespace Artemis.Core.Services
|
|||||||
// Load the plugin assemblies into the plugin context
|
// Load the plugin assemblies into the plugin context
|
||||||
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
|
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
|
||||||
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadPlugin(subDirectory);
|
LoadPlugin(subDirectory);
|
||||||
@ -195,7 +193,6 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
|
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
|
||||||
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
|
_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
|
// 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
|
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
|
||||||
foreach (Type featureType in featureTypes)
|
foreach (Type featureType in featureTypes)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope();
|
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);
|
_logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Activate plugins after they are all loaded
|
// Activate plugins after they are all loaded
|
||||||
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
|
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EnablePluginFeature(pluginFeature, false, !ignorePluginLock);
|
EnablePluginFeature(pluginFeature, false, !ignorePluginLock);
|
||||||
@ -373,7 +367,6 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
// ignored, logged in EnablePluginFeature
|
// ignored, logged in EnablePluginFeature
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (saveState)
|
if (saveState)
|
||||||
{
|
{
|
||||||
@ -474,13 +467,11 @@ namespace Artemis.Core.Services
|
|||||||
Directory.CreateDirectory(directoryInfo.FullName);
|
Directory.CreateDirectory(directoryInfo.FullName);
|
||||||
string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, "");
|
string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, "");
|
||||||
foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries)
|
foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries)
|
||||||
{
|
|
||||||
if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory))
|
if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory))
|
||||||
{
|
{
|
||||||
string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length));
|
string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length));
|
||||||
zipArchiveEntry.ExtractToFile(target);
|
zipArchiveEntry.ExtractToFile(target);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Load the newly extracted plugin and return the result
|
// Load the newly extracted plugin and return the result
|
||||||
return LoadPlugin(directoryInfo);
|
return LoadPlugin(directoryInfo);
|
||||||
@ -563,10 +554,8 @@ namespace Artemis.Core.Services
|
|||||||
private void SavePlugin(Plugin plugin)
|
private void SavePlugin(Plugin plugin)
|
||||||
{
|
{
|
||||||
foreach (PluginFeature pluginFeature in plugin.Features)
|
foreach (PluginFeature pluginFeature in plugin.Features)
|
||||||
{
|
|
||||||
if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
|
if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
|
||||||
plugin.Entity.Features.Add(pluginFeature.Entity);
|
plugin.Entity.Features.Add(pluginFeature.Entity);
|
||||||
}
|
|
||||||
|
|
||||||
_pluginRepository.SavePlugin(plugin.Entity);
|
_pluginRepository.SavePlugin(plugin.Entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -191,22 +191,49 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
public ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device)
|
public ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
// Look for a user layout
|
ArtemisLayout layout;
|
||||||
// ... here
|
|
||||||
|
|
||||||
// Try loading a device provider layout, if that comes back valid we use that
|
// Configured layout path takes precedence over all other options
|
||||||
ArtemisLayout layout = device.DeviceProvider.LoadLayout(device);
|
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
|
// Finally fall back to a default layout
|
||||||
// .. do it!
|
layout = LoadDefaultLayout(device);
|
||||||
|
ApplyDeviceLayout(device, layout, true, true);
|
||||||
ApplyDeviceLayout(device, layout);
|
|
||||||
return layout;
|
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
|
// Applying layouts can affect LEDs, update LED group
|
||||||
UpdateBitmapBrush();
|
UpdateBitmapBrush();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -18,8 +17,9 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void PrepareFirstLaunch()
|
public static void PrepareFirstLaunch()
|
||||||
{
|
{
|
||||||
CreateArtemisFolderIfMissing(Constants.DataFolder);
|
CreateAccessibleDirectory(Constants.DataFolder);
|
||||||
CreateArtemisFolderIfMissing(Constants.DataFolder + "plugins");
|
CreateAccessibleDirectory(Constants.DataFolder + "plugins");
|
||||||
|
CreateAccessibleDirectory(Constants.DataFolder + "user layouts");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -62,15 +62,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current application location
|
/// Creates all directories and subdirectories in the specified path unless they already exist with permissions
|
||||||
|
/// allowing access by everyone.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <param name="path">The directory to create.</param>
|
||||||
internal static string GetCurrentLocation()
|
public static void CreateAccessibleDirectory(string path)
|
||||||
{
|
|
||||||
return Process.GetCurrentProcess().MainModule!.FileName!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateArtemisFolderIfMissing(string path)
|
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(path))
|
if (!Directory.Exists(path))
|
||||||
{
|
{
|
||||||
@ -92,6 +88,15 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current application location
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static string GetCurrentLocation()
|
||||||
|
{
|
||||||
|
return Process.GetCurrentProcess().MainModule!.FileName!;
|
||||||
|
}
|
||||||
|
|
||||||
private static void OnRestartRequested(RestartEventArgs e)
|
private static void OnRestartRequested(RestartEventArgs e)
|
||||||
{
|
{
|
||||||
RestartRequested?.Invoke(null, e);
|
RestartRequested?.Invoke(null, e);
|
||||||
|
|||||||
@ -22,8 +22,10 @@ namespace Artemis.Storage.Entities.Surface
|
|||||||
|
|
||||||
public int PhysicalLayout { get; set; }
|
public int PhysicalLayout { get; set; }
|
||||||
public string LogicalLayout { get; set; }
|
public string LogicalLayout { get; set; }
|
||||||
|
public string CustomLayoutPath { get; set; }
|
||||||
|
|
||||||
public List<DeviceInputIdentifierEntity> InputIdentifiers { get; set; }
|
public List<DeviceInputIdentifierEntity> InputIdentifiers { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DeviceInputIdentifierEntity
|
public class DeviceInputIdentifierEntity
|
||||||
|
|||||||
@ -286,7 +286,7 @@ namespace Artemis.UI.Shared
|
|||||||
InvalidateMeasure();
|
InvalidateMeasure();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeviceUpdated(object sender, EventArgs e)
|
private void DeviceUpdated(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
SetupForDevice();
|
SetupForDevice();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ namespace Artemis.UI.Shared
|
|||||||
internal class DeviceVisualizerLed
|
internal class DeviceVisualizerLed
|
||||||
{
|
{
|
||||||
private SolidColorBrush? _renderColorBrush;
|
private SolidColorBrush? _renderColorBrush;
|
||||||
|
private Color _renderColor;
|
||||||
|
|
||||||
public DeviceVisualizerLed(ArtemisLed led)
|
public DeviceVisualizerLed(ArtemisLed led)
|
||||||
{
|
{
|
||||||
@ -40,13 +41,18 @@ namespace Artemis.UI.Shared
|
|||||||
if (DisplayGeometry == null)
|
if (DisplayGeometry == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_renderColorBrush ??= new SolidColorBrush();
|
||||||
|
|
||||||
RGB.NET.Core.Color originalColor = Led.GetOriginalColor();
|
RGB.NET.Core.Color originalColor = Led.GetOriginalColor();
|
||||||
byte r = originalColor.GetR();
|
byte r = originalColor.GetR();
|
||||||
byte g = originalColor.GetG();
|
byte g = originalColor.GetG();
|
||||||
byte b = originalColor.GetB();
|
byte b = originalColor.GetB();
|
||||||
|
|
||||||
_renderColorBrush ??= new SolidColorBrush();
|
_renderColor.A = isDimmed ? 100 : 255;
|
||||||
_renderColorBrush.Color = isDimmed ? Color.FromArgb(100, r, g, b) : Color.FromRgb(r, g, b);
|
_renderColor.R = r;
|
||||||
|
_renderColor.G = g;
|
||||||
|
_renderColor.B = b;
|
||||||
|
_renderColorBrush.Color = _renderColor;
|
||||||
drawingContext.DrawRectangle(_renderColorBrush, null, LedRect);
|
drawingContext.DrawRectangle(_renderColorBrush, null, LedRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,6 +65,9 @@
|
|||||||
<Reference Include="RGB.NET.Groups">
|
<Reference Include="RGB.NET.Groups">
|
||||||
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Groups.dll</HintPath>
|
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Groups.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="RGB.NET.Layout">
|
||||||
|
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Layout.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -179,7 +179,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RgbServiceOnDevicesModified(object? sender, DeviceEventArgs e)
|
private void RgbServiceOnDevicesModified(object sender, DeviceEventArgs e)
|
||||||
{
|
{
|
||||||
ApplyDevices();
|
ApplyDevices();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,9 +40,31 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<materialDesign:PopupBox PlacementMode="BottomAndAlignRightEdges" StaysOpen="False">
|
<materialDesign:PopupBox PlacementMode="BottomAndAlignRightEdges" StaysOpen="False">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<Button Content="Open plugin directory" Command="{s:Action OpenPluginDirectory}" />
|
<Button Command="{s:Action OpenPluginDirectory}">
|
||||||
<Button Content="Open image directory" Command="{s:Action OpenImageDirectory}" />
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Content="Reload layout" Command="{s:Action ReloadLayout}" />
|
<materialDesign:PackIcon Kind="Plugin" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">Open plugin directory</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{s:Action OpenImageDirectory}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="Image" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">Open layout image directory</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Separator />
|
||||||
|
<Button Command="{s:Action ReloadLayout}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="Reload" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">Reload layout</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{s:Action ExportLayout}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="Xml" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">Export layout</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</materialDesign:PopupBox>
|
</materialDesign:PopupBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@ -56,12 +78,12 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock TextWrapping="Wrap" Margin="0 0 00 10">
|
<TextBlock TextWrapping="Wrap" Margin="0 0 0 10">
|
||||||
In this window you can view detailed information of the device.
|
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.
|
Please note that having this window open can have a performance impact on your system.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" Grid.Row="1" Padding="15">
|
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" Grid.Row="1" Padding="15" Margin="0 10">
|
||||||
<shared:DeviceVisualizer Device="{Binding Device}"
|
<shared:DeviceVisualizer Device="{Binding Device}"
|
||||||
HighlightedLeds="{Binding SelectedLeds}"
|
HighlightedLeds="{Binding SelectedLeds}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@ -69,14 +91,14 @@
|
|||||||
ShowColors="True" />
|
ShowColors="True" />
|
||||||
</materialDesign:Card>
|
</materialDesign:Card>
|
||||||
|
|
||||||
<Expander Grid.Row="2" VerticalAlignment="Center" Header="Device properties" Margin="0 15">
|
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" Grid.Row="2" Padding="15" Margin="0 10">
|
||||||
<Grid>
|
<Expander VerticalAlignment="Center" Header="Device properties">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid>
|
||||||
<ColumnDefinition />
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
<ColumnDefinition />
|
||||||
<materialDesign:Card Grid.Column="0" materialDesign:ShadowAssist.ShadowDepth="Depth1" Margin="0,0,5,0">
|
</Grid.ColumnDefinitions>
|
||||||
<StackPanel Margin="15" HorizontalAlignment="Stretch">
|
<StackPanel Grid.Column="0" Margin="15" HorizontalAlignment="Stretch">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
@ -93,7 +115,7 @@
|
|||||||
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" />
|
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -111,7 +133,7 @@
|
|||||||
Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" />
|
Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -129,7 +151,7 @@
|
|||||||
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceType}" />
|
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceType}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -147,7 +169,7 @@
|
|||||||
Text="{Binding Device.PhysicalLayout}" />
|
Text="{Binding Device.PhysicalLayout}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -166,9 +188,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</materialDesign:Card>
|
<StackPanel Grid.Column="1" Margin="15" HorizontalAlignment="Stretch">
|
||||||
<materialDesign:Card Grid.Column="1" materialDesign:ShadowAssist.ShadowDepth="Depth1" Margin="5,0,0,0">
|
|
||||||
<StackPanel Margin="15" HorizontalAlignment="Stretch">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
@ -185,7 +205,7 @@
|
|||||||
Text="{Binding Device.RgbDevice.Size}" />
|
Text="{Binding Device.RgbDevice.Size}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
@ -202,7 +222,7 @@
|
|||||||
Text="{Binding Device.RgbDevice.Location}" />
|
Text="{Binding Device.RgbDevice.Location}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -220,7 +240,7 @@
|
|||||||
Text="{Binding Device.RgbDevice.Rotation.Degrees}" />
|
Text="{Binding Device.RgbDevice.Rotation.Degrees}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -238,7 +258,7 @@
|
|||||||
Text="{Binding Device.LogicalLayout}" />
|
Text="{Binding Device.LogicalLayout}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -257,11 +277,11 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</materialDesign:Card>
|
</Grid>
|
||||||
</Grid>
|
</Expander>
|
||||||
</Expander>
|
</materialDesign:Card>
|
||||||
|
|
||||||
<materialDesign:Card Grid.Row="3" materialDesign:ShadowAssist.ShadowDepth="Depth1" Padding="15" MaxHeight="413">
|
<materialDesign:Card Grid.Row="3" materialDesign:ShadowAssist.ShadowDepth="Depth1" Padding="15" MaxHeight="413" Margin="0 10">
|
||||||
<DataGrid ItemsSource="{Binding Device.Leds}"
|
<DataGrid ItemsSource="{Binding Device.Leds}"
|
||||||
d:DataContext="{d:DesignInstance Type=core:ArtemisLed}"
|
d:DataContext="{d:DesignInstance Type=core:ArtemisLed}"
|
||||||
CanUserSortColumns="True"
|
CanUserSortColumns="True"
|
||||||
|
|||||||
@ -2,18 +2,23 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Xml.Serialization;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Stylet; // using PropertyChanged;
|
using Ookii.Dialogs.Wpf;
|
||||||
|
using RGB.NET.Layout;
|
||||||
|
using Stylet;
|
||||||
|
|
||||||
|
// using PropertyChanged;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Settings.Debug
|
namespace Artemis.UI.Screens.Settings.Debug
|
||||||
{
|
{
|
||||||
public class DeviceDebugViewModel : Screen
|
public class DeviceDebugViewModel : Screen
|
||||||
{
|
{
|
||||||
private readonly IDeviceService _deviceService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
|
private readonly IRgbService _rgbService;
|
||||||
private ArtemisLed _selectedLed;
|
private ArtemisLed _selectedLed;
|
||||||
|
|
||||||
public DeviceDebugViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService)
|
public DeviceDebugViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService)
|
||||||
@ -81,13 +86,69 @@ namespace Artemis.UI.Screens.Settings.Debug
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadLayout()
|
public void ReloadLayout()
|
||||||
|
{
|
||||||
|
_rgbService.ApplyBestDeviceLayout(Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExportLayout()
|
||||||
{
|
{
|
||||||
if (Device.Layout == null)
|
if (Device.Layout == null)
|
||||||
_rgbService.ApplyBestDeviceLayout(Device);
|
return;
|
||||||
else
|
|
||||||
|
VistaFolderBrowserDialog dialog = new()
|
||||||
{
|
{
|
||||||
Device.Layout.ReloadFromDisk();
|
Description = "Select layout export target folder",
|
||||||
_rgbService.ApplyDeviceLayout(Device, Device.Layout);
|
UseDescriptionForTitle = true,
|
||||||
|
ShowNewFolderButton = true,
|
||||||
|
SelectedPath = Path.Combine(Constants.DataFolder, "user layouts")
|
||||||
|
};
|
||||||
|
|
||||||
|
bool? result = dialog.ShowDialog();
|
||||||
|
if (result != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string directory = Path.Combine(
|
||||||
|
dialog.SelectedPath,
|
||||||
|
Device.RgbDevice.DeviceInfo.Manufacturer,
|
||||||
|
Device.RgbDevice.DeviceInfo.DeviceType.ToString()
|
||||||
|
);
|
||||||
|
string filePath = Path.Combine(directory, Device.GetLayoutFileName());
|
||||||
|
Core.Utilities.CreateAccessibleDirectory(directory);
|
||||||
|
|
||||||
|
// XML
|
||||||
|
XmlSerializer serializer = new(typeof(DeviceLayout));
|
||||||
|
using StreamWriter writer = new(filePath);
|
||||||
|
serializer.Serialize(writer, Device.Layout!.RgbLayout);
|
||||||
|
|
||||||
|
// Device images
|
||||||
|
if (!Uri.IsWellFormedUriString(Device.Layout.FilePath, UriKind.Absolute))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Uri targetDirectory = new(directory + "/", UriKind.Absolute);
|
||||||
|
Uri sourceDirectory = new(Path.GetDirectoryName(Device.Layout.FilePath)! + "/", UriKind.Absolute);
|
||||||
|
Uri deviceImageTarget = new(targetDirectory, Device.Layout.LayoutCustomDeviceData.DeviceImage);
|
||||||
|
|
||||||
|
// Create folder (if needed) and copy image
|
||||||
|
Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(deviceImageTarget.LocalPath)!);
|
||||||
|
if (Device.Layout.Image != null && File.Exists(Device.Layout.Image.LocalPath) && !File.Exists(deviceImageTarget.LocalPath))
|
||||||
|
File.Copy(Device.Layout.Image.LocalPath, deviceImageTarget.LocalPath);
|
||||||
|
|
||||||
|
foreach (ArtemisLedLayout ledLayout in Device.Layout.Leds)
|
||||||
|
{
|
||||||
|
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Only the image of the current logical layout is available as an URI, iterate each layout and find the images manually
|
||||||
|
foreach (LayoutCustomLedDataLogicalLayout logicalLayout in ledLayout.LayoutCustomLedData.LogicalLayouts)
|
||||||
|
{
|
||||||
|
Uri image = new(sourceDirectory, logicalLayout.Image);
|
||||||
|
Uri imageTarget = new(targetDirectory, logicalLayout.Image);
|
||||||
|
|
||||||
|
// Create folder (if needed) and copy image
|
||||||
|
Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(imageTarget.LocalPath)!);
|
||||||
|
if (File.Exists(image.LocalPath) && !File.Exists(imageTarget.LocalPath))
|
||||||
|
File.Copy(image.LocalPath, imageTarget.LocalPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
<Button Command="{s:Action ViewProperties}">
|
<Button Command="{s:Action ViewProperties}">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<materialDesign:PackIcon Kind="Gear" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
<materialDesign:PackIcon Kind="Gear" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
<TextBlock VerticalAlignment="Center">View surface properties</TextBlock>
|
<TextBlock VerticalAlignment="Center">Device properties</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -57,6 +57,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
// Take it off the UI thread to avoid freezing on tab change
|
// Take it off the UI thread to avoid freezing on tab change
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
|
Items.Clear();
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
_instances = _pluginManagementService.GetAllPlugins()
|
_instances = _pluginManagementService.GetAllPlugins()
|
||||||
.Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
|
.Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
|
||||||
|
|||||||
@ -156,11 +156,38 @@
|
|||||||
Margin="0,0,5,0"
|
Margin="0,0,5,0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
|
Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Layout -->
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="0 25 0 0">
|
||||||
|
Custom layout
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||||
|
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
TextAlignment="Justify">
|
||||||
|
Select a custom layout below if you want to change the appearance and/or LEDs of this device.
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||||
|
Text="{Binding Device.CustomLayoutPath}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||||
|
IsReadOnly="True"
|
||||||
|
PreviewMouseLeftButtonUp="{s:Action BrowseCustomLayout}">
|
||||||
|
<materialDesign:HintAssist.Hint>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="-2 0 0 0">
|
||||||
|
<materialDesign:PackIcon Kind="Xml" Width="20" />
|
||||||
|
<TextBlock>
|
||||||
|
Layout path
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</materialDesign:HintAssist.Hint>
|
||||||
|
</TextBox>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<StackPanel Orientation="Horizontal"
|
<StackPanel Orientation="Horizontal"
|
||||||
HorizontalAlignment="Right">
|
HorizontalAlignment="Right">
|
||||||
|
|||||||
@ -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;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using MaterialDesignThemes.Wpf;
|
||||||
|
using Ookii.Dialogs.Wpf;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
@ -10,6 +15,8 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
|
|||||||
public class SurfaceDeviceConfigViewModel : DialogViewModelBase
|
public class SurfaceDeviceConfigViewModel : DialogViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
private readonly ICoreService _coreService;
|
||||||
|
private readonly IRgbService _rgbService;
|
||||||
|
private readonly IMessageService _messageService;
|
||||||
private readonly double _initialRedScale;
|
private readonly double _initialRedScale;
|
||||||
private readonly double _initialGreenScale;
|
private readonly double _initialGreenScale;
|
||||||
private readonly double _initialBlueScale;
|
private readonly double _initialBlueScale;
|
||||||
@ -23,41 +30,43 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
|
|||||||
private SKColor _currentColor;
|
private SKColor _currentColor;
|
||||||
private bool _displayOnDevices;
|
private bool _displayOnDevices;
|
||||||
|
|
||||||
public SurfaceDeviceConfigViewModel(ArtemisDevice device, ICoreService coreService, IModelValidator<SurfaceDeviceConfigViewModel> validator) : base(validator)
|
public SurfaceDeviceConfigViewModel(ArtemisDevice device,
|
||||||
|
ICoreService coreService,
|
||||||
|
IRgbService rgbService,
|
||||||
|
IMessageService messageService,
|
||||||
|
IModelValidator<SurfaceDeviceConfigViewModel> validator) : base(validator)
|
||||||
{
|
{
|
||||||
_coreService = coreService;
|
_coreService = coreService;
|
||||||
|
_rgbService = rgbService;
|
||||||
|
_messageService = messageService;
|
||||||
|
|
||||||
Device = device;
|
Device = device;
|
||||||
|
|
||||||
X = (int)Device.X;
|
X = (int) Device.X;
|
||||||
Y = (int)Device.Y;
|
Y = (int) Device.Y;
|
||||||
Scale = Device.Scale;
|
Scale = Device.Scale;
|
||||||
Rotation = (int)Device.Rotation;
|
Rotation = (int) Device.Rotation;
|
||||||
RedScale = Device.RedScale * 100d;
|
RedScale = Device.RedScale * 100d;
|
||||||
GreenScale = Device.GreenScale * 100d;
|
GreenScale = Device.GreenScale * 100d;
|
||||||
BlueScale = Device.BlueScale * 100d;
|
BlueScale = Device.BlueScale * 100d;
|
||||||
//we need to store the initial values to be able to restore them when the user clicks "Cancel"
|
//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;
|
_initialGreenScale = Device.GreenScale;
|
||||||
_initialBlueScale = Device.BlueScale;
|
_initialBlueScale = Device.BlueScale;
|
||||||
CurrentColor = SKColors.White;
|
CurrentColor = SKColors.White;
|
||||||
_coreService.FrameRendering += OnFrameRendering;
|
_coreService.FrameRendering += OnFrameRendering;
|
||||||
}
|
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtemisDevice Device { get; }
|
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
|
public int X
|
||||||
{
|
{
|
||||||
get => _x;
|
get => _x;
|
||||||
@ -130,7 +139,6 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
|
|||||||
Device.BlueScale = BlueScale / 100d;
|
Device.BlueScale = BlueScale / 100d;
|
||||||
|
|
||||||
_coreService.ModuleRenderingDisabled = false;
|
_coreService.ModuleRenderingDisabled = false;
|
||||||
_coreService.FrameRendering -= OnFrameRendering;
|
|
||||||
Session.Close(true);
|
Session.Close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +149,26 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
|
|||||||
Device.BlueScale = BlueScale / 100d;
|
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()
|
public override void Cancel()
|
||||||
{
|
{
|
||||||
Device.RedScale = _initialRedScale;
|
Device.RedScale = _initialRedScale;
|
||||||
@ -149,5 +177,25 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
|
|||||||
|
|
||||||
base.Cancel();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,6 +16,7 @@ using Artemis.UI.Screens.SurfaceEditor.Dialogs;
|
|||||||
using Artemis.UI.Screens.SurfaceEditor.Visualization;
|
using Artemis.UI.Screens.SurfaceEditor.Visualization;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using SkiaSharp;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
using MouseButton = System.Windows.Input.MouseButton;
|
using MouseButton = System.Windows.Input.MouseButton;
|
||||||
|
|
||||||
@ -27,13 +28,16 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
|||||||
private readonly IInputService _inputService;
|
private readonly IInputService _inputService;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IRgbService _rgbService;
|
||||||
|
private readonly ICoreService _coreService;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private Cursor _cursor;
|
private Cursor _cursor;
|
||||||
private PanZoomViewModel _panZoomViewModel;
|
private PanZoomViewModel _panZoomViewModel;
|
||||||
private RectangleGeometry _selectionRectangle;
|
private RectangleGeometry _selectionRectangle;
|
||||||
private PluginSetting<GridLength> _surfaceListWidth;
|
private PluginSetting<GridLength> _surfaceListWidth;
|
||||||
|
private List<ArtemisDevice> _shuffledDevices;
|
||||||
|
|
||||||
public SurfaceEditorViewModel(IRgbService rgbService,
|
public SurfaceEditorViewModel(IRgbService rgbService,
|
||||||
|
ICoreService coreService,
|
||||||
IDialogService dialogService,
|
IDialogService dialogService,
|
||||||
ISettingsService settingsService,
|
ISettingsService settingsService,
|
||||||
IDeviceService deviceService,
|
IDeviceService deviceService,
|
||||||
@ -48,6 +52,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
|||||||
ListDeviceViewModels = new BindableCollection<ListDeviceViewModel>();
|
ListDeviceViewModels = new BindableCollection<ListDeviceViewModel>();
|
||||||
|
|
||||||
_rgbService = rgbService;
|
_rgbService = rgbService;
|
||||||
|
_coreService = coreService;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_deviceService = deviceService;
|
_deviceService = deviceService;
|
||||||
@ -105,6 +110,22 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
|||||||
SurfaceListWidth.Save();
|
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
|
#region Overrides of Screen
|
||||||
|
|
||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
@ -112,6 +133,10 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
|||||||
LoadWorkspaceSettings();
|
LoadWorkspaceSettings();
|
||||||
SurfaceDeviceViewModels.AddRange(_rgbService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => new SurfaceDeviceViewModel(d, _rgbService)));
|
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)));
|
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();
|
base.OnInitialActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +145,9 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
|||||||
SaveWorkspaceSettings();
|
SaveWorkspaceSettings();
|
||||||
SurfaceDeviceViewModels.Clear();
|
SurfaceDeviceViewModels.Clear();
|
||||||
ListDeviceViewModels.Clear();
|
ListDeviceViewModels.Clear();
|
||||||
|
|
||||||
|
_coreService.FrameRendering -= CoreServiceOnFrameRendering;
|
||||||
|
|
||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,10 @@
|
|||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<shared:DeviceVisualizer Device="{Binding Device}" Width="30" Height="30" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
<shared:DeviceVisualizer Device="{Binding Device}" Width="30" Height="30" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||||
<TextBlock Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" Grid.Column="1" VerticalAlignment="Center" />
|
<StackPanel Grid.Column="1" VerticalAlignment="Center" >
|
||||||
|
<TextBlock Text="{Binding Device.RgbDevice.DeviceInfo.Model}" />
|
||||||
|
<TextBlock Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" Foreground="{DynamicResource MaterialDesignBodyLight}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
Loading…
x
Reference in New Issue
Block a user