diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs
index 61381faf4..8386f5fcf 100644
--- a/src/Artemis.Core/Constants.cs
+++ b/src/Artemis.Core/Constants.cs
@@ -62,6 +62,11 @@ public static class Constants
/// The full path to the Artemis user layouts folder
///
public static readonly string LayoutsFolder = Path.Combine(DataFolder, "User Layouts");
+
+ ///
+ /// The full path to the Artemis user layouts folder
+ ///
+ public static readonly string WorkshopFolder = Path.Combine(DataFolder, "workshop");
///
/// The current API version for plugins
@@ -155,4 +160,5 @@ public static class Constants
/// Gets the graphics context to be used for rendering by SkiaSharp.
///
public static IManagedGraphicsContext? ManagedGraphicsContext { get; internal set; }
+
}
\ No newline at end of file
diff --git a/src/Artemis.Core/DryIoc/ContainerExtensions.cs b/src/Artemis.Core/DryIoc/ContainerExtensions.cs
index b9b768c78..44d377e8a 100644
--- a/src/Artemis.Core/DryIoc/ContainerExtensions.cs
+++ b/src/Artemis.Core/DryIoc/ContainerExtensions.cs
@@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Reflection;
using Artemis.Core.DryIoc.Factories;
+using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.Storage;
using Artemis.Storage.Migrations.Interfaces;
@@ -35,7 +36,8 @@ public static class ContainerExtensions
// Bind migrations
container.RegisterMany(storageAssembly, type => type.IsAssignableTo(), Reuse.Singleton, nonPublicServiceTypes: true);
-
+
+ container.RegisterMany(coreAssembly, type => type.IsAssignableTo(), Reuse.Singleton);
container.Register(Reuse.Singleton);
container.Register(Made.Of(_ => ServiceInfo.Of(), f => f.CreatePluginSettings(Arg.Index(0)), r => r.Parent.ImplementationType));
container.Register(Reuse.Singleton);
diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
index d34a3cd7d..34a10d0fc 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
@@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
-using System.Diagnostics;
using System.Linq;
using Artemis.Core.DeviceProviders;
+using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Surface;
using RGB.NET.Core;
@@ -46,6 +46,7 @@ public class ArtemisDevice : CorePropertyChanged
InputIdentifiers = new List();
InputMappings = new Dictionary();
Categories = new HashSet();
+ LayoutSelection = new LayoutSelection {Type = DefaultLayoutProvider.LayoutType};
RgbDevice.ColorCorrections.Clear();
RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this));
@@ -74,6 +75,7 @@ public class ArtemisDevice : CorePropertyChanged
InputIdentifiers = new List();
InputMappings = new Dictionary();
Categories = new HashSet();
+ LayoutSelection = new LayoutSelection {Type = DefaultLayoutProvider.LayoutType};
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
@@ -153,6 +155,8 @@ public class ArtemisDevice : CorePropertyChanged
///
public HashSet Categories { get; }
+ public LayoutSelection LayoutSelection { get; }
+
///
/// Gets or sets the X-position of the device
///
@@ -293,19 +297,6 @@ public class ArtemisDevice : CorePropertyChanged
}
}
- ///
- /// Gets or sets a boolean indicating whether falling back to default layouts is enabled or not
- ///
- public bool DisableDefaultLayout
- {
- get => DeviceEntity.DisableDefaultLayout;
- set
- {
- DeviceEntity.DisableDefaultLayout = value;
- OnPropertyChanged(nameof(DisableDefaultLayout));
- }
- }
-
///
/// Gets or sets the logical layout of the device e.g. DE, UK or US.
/// Only applicable to keyboards
@@ -320,20 +311,6 @@ public class ArtemisDevice : CorePropertyChanged
}
}
- ///
- /// 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
///
@@ -381,40 +358,6 @@ public class ArtemisDevice : CorePropertyChanged
return artemisLed;
}
- ///
- /// Returns the most preferred device layout for this device.
- ///
- /// The most preferred device layout for this device.
- public ArtemisLayout? GetBestDeviceLayout()
- {
- ArtemisLayout? layout;
-
- // Configured layout path takes precedence over all other options
- if (CustomLayoutPath != null)
- {
- layout = new ArtemisLayout(CustomLayoutPath, LayoutSource.Configured);
- if (layout.IsValid)
- return layout;
- }
-
- // Look for a layout provided by the user
- layout = DeviceProvider.LoadUserLayout(this);
- if (layout.IsValid)
- return layout;
-
- if (DisableDefaultLayout)
- return null;
-
- // Look for a layout provided by the plugin
- layout = DeviceProvider.LoadLayout(this);
- if (layout.IsValid)
- return layout;
-
- // Finally fall back to a default layout
- layout = ArtemisLayout.GetDefaultLayout(this);
- return layout;
- }
-
///
/// Occurs when the underlying RGB.NET device was updated
///
@@ -481,7 +424,7 @@ public class ArtemisDevice : CorePropertyChanged
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} set to true because the device provider does not support it");
if (layout != null && layout.IsValid && removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported)
throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} set to true because the device provider does not support it");
-
+
// Always clear the current layout
ClearLayout();
@@ -534,6 +477,9 @@ public class ArtemisDevice : CorePropertyChanged
DeviceEntity.Categories.Clear();
foreach (DeviceCategory deviceCategory in Categories)
DeviceEntity.Categories.Add((int) deviceCategory);
+
+ DeviceEntity.LayoutType = LayoutSelection.Type;
+ DeviceEntity.LayoutParameter = LayoutSelection.Parameter;
}
internal void Load()
@@ -548,6 +494,9 @@ public class ArtemisDevice : CorePropertyChanged
if (!Categories.Any())
ApplyDefaultCategories();
+ LayoutSelection.Type = DeviceEntity.LayoutType;
+ LayoutSelection.Parameter = DeviceEntity.LayoutParameter;
+
LoadInputMappings();
}
@@ -573,7 +522,7 @@ public class ArtemisDevice : CorePropertyChanged
{
Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
-
+
if (loadInputMappings)
LoadInputMappings();
}
diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
index f44e3185d..5304f5e37 100644
--- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
+++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
@@ -12,17 +12,18 @@ namespace Artemis.Core;
///
public class ArtemisLayout
{
+ private static readonly string DefaultLayoutPath = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts", "Artemis");
+
///
/// Creates a new instance of the class
///
/// The path of the layout XML file
- /// The source from where this layout is being loaded
- public ArtemisLayout(string filePath, LayoutSource source)
+ public ArtemisLayout(string filePath)
{
FilePath = filePath;
- Source = source;
Leds = new List();
-
+ IsDefaultLayout = filePath.StartsWith(DefaultLayoutPath);
+
LoadLayout();
}
@@ -31,11 +32,6 @@ public class ArtemisLayout
///
public string FilePath { get; }
- ///
- /// Gets the source from where this layout was loaded
- ///
- public LayoutSource Source { get; }
-
///
/// Gets a boolean indicating whether a valid layout was loaded
///
@@ -61,6 +57,8 @@ public class ArtemisLayout
///
public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!;
+ public bool IsDefaultLayout { get; private set; }
+
///
/// Applies the layout to the provided device
///
@@ -121,29 +119,28 @@ public class ArtemisLayout
internal static ArtemisLayout? GetDefaultLayout(ArtemisDevice device)
{
- string layoutFolder = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts", "Artemis");
if (device.DeviceType == RGBDeviceType.Keyboard)
{
// XL layout is defined by its programmable macro keys
if (device.Leds.Any(l => l.RgbLed.Id >= LedId.Keyboard_Programmable1 && l.RgbLed.Id <= LedId.Keyboard_Programmable32))
{
if (device.PhysicalLayout == KeyboardLayoutType.ANSI)
- return new ArtemisLayout(Path.Combine(layoutFolder, "Keyboard", "Artemis XL keyboard-ANSI.xml"), LayoutSource.Default);
- return new ArtemisLayout(Path.Combine(layoutFolder, "Keyboard", "Artemis XL keyboard-ISO.xml"), LayoutSource.Default);
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Keyboard", "Artemis XL keyboard-ANSI.xml"));
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Keyboard", "Artemis XL keyboard-ISO.xml"));
}
// L layout is defined by its numpad
if (device.Leds.Any(l => l.RgbLed.Id >= LedId.Keyboard_NumLock && l.RgbLed.Id <= LedId.Keyboard_NumPeriodAndDelete))
{
if (device.PhysicalLayout == KeyboardLayoutType.ANSI)
- return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard", "Artemis L keyboard-ANSI.xml"), LayoutSource.Default);
- return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard", "Artemis L keyboard-ISO.xml"), LayoutSource.Default);
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Keyboard", "Artemis L keyboard-ANSI.xml"));
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Keyboard", "Artemis L keyboard-ISO.xml"));
}
// No numpad will result in TKL
if (device.PhysicalLayout == KeyboardLayoutType.ANSI)
- return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard", "Artemis TKL keyboard-ANSI.xml"), LayoutSource.Default);
- return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard", "Artemis TKL keyboard-ISO.xml"), LayoutSource.Default);
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Keyboard", "Artemis TKL keyboard-ANSI.xml"));
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Keyboard", "Artemis TKL keyboard-ISO.xml"));
}
// if (device.DeviceType == RGBDeviceType.Mouse)
@@ -151,21 +148,21 @@ public class ArtemisLayout
// if (device.Leds.Count == 1)
// {
// if (device.Leds.Any(l => l.RgbLed.Id == LedId.Logo))
- // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "1 LED mouse logo.xml"), LayoutSource.Default);
- // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "1 LED mouse.xml"), LayoutSource.Default);
+ // return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Mouse", "1 LED mouse logo.xml"), LayoutSource.Default);
+ // return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Mouse", "1 LED mouse.xml"), LayoutSource.Default);
// }
// if (device.Leds.Any(l => l.RgbLed.Id == LedId.Logo))
- // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "4 LED mouse logo.xml"), LayoutSource.Default);
- // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "4 LED mouse.xml"), LayoutSource.Default);
+ // return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Mouse", "4 LED mouse logo.xml"), LayoutSource.Default);
+ // return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Mouse", "4 LED mouse.xml"), LayoutSource.Default);
// }
if (device.DeviceType == RGBDeviceType.Headset)
{
if (device.Leds.Count == 1)
- return new ArtemisLayout(Path.Combine(layoutFolder + "Headset", "Artemis 1 LED headset.xml"), LayoutSource.Default);
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Headset", "Artemis 1 LED headset.xml"));
if (device.Leds.Count == 2)
- return new ArtemisLayout(Path.Combine(layoutFolder + "Headset", "Artemis 2 LED headset.xml"), LayoutSource.Default);
- return new ArtemisLayout(Path.Combine(layoutFolder + "Headset", "Artemis 4 LED headset.xml"), LayoutSource.Default);
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Headset", "Artemis 2 LED headset.xml"));
+ return new ArtemisLayout(Path.Combine(DefaultLayoutPath, "Headset", "Artemis 4 LED headset.xml"));
}
return null;
@@ -206,30 +203,10 @@ public class ArtemisLayout
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
+ ///
+ public override string ToString()
+ {
+ return FilePath;
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/LayoutSelection.cs b/src/Artemis.Core/Models/Surface/LayoutSelection.cs
new file mode 100644
index 000000000..f64c54eb1
--- /dev/null
+++ b/src/Artemis.Core/Models/Surface/LayoutSelection.cs
@@ -0,0 +1,28 @@
+namespace Artemis.Core;
+
+///
+/// Represents a reference to a layout for a device.
+///
+public class LayoutSelection : CorePropertyChanged
+{
+ private string? _type;
+ private string? _parameter;
+
+ ///
+ /// Gets or sets what kind of layout reference this is.
+ ///
+ public string? Type
+ {
+ get => _type;
+ set => SetAndNotify(ref _type, value);
+ }
+
+ ///
+ /// Gets or sets the parameter of the layout reference, such as a file path of workshop entry ID.
+ ///
+ public string? Parameter
+ {
+ get => _parameter;
+ set => SetAndNotify(ref _parameter, value);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
index 843710370..ee9f256a9 100644
--- a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
+++ b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
@@ -62,7 +62,7 @@ public abstract class DeviceProvider : PluginFeature
device.DeviceType.ToString(),
GetDeviceLayoutName(device)
);
- return new ArtemisLayout(filePath, LayoutSource.Plugin);
+ return new ArtemisLayout(filePath);
}
///
@@ -79,7 +79,7 @@ public abstract class DeviceProvider : PluginFeature
device.DeviceType.ToString(),
GetDeviceLayoutName(device)
);
- return new ArtemisLayout(filePath, LayoutSource.User);
+ return new ArtemisLayout(filePath);
}
///
diff --git a/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs b/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs
new file mode 100644
index 000000000..5b5e96e2c
--- /dev/null
+++ b/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs
@@ -0,0 +1,28 @@
+using RGB.NET.Layout;
+
+namespace Artemis.Core.Providers;
+
+public class CustomPathLayoutProvider : ILayoutProvider
+{
+ public static string LayoutType = "CustomPath";
+
+ ///
+ public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
+ {
+ if (device.LayoutSelection.Parameter == null)
+ return null;
+ return new ArtemisLayout(device.LayoutSelection.Parameter);
+ }
+
+ ///
+ public void ApplyLayout(ArtemisDevice device, ArtemisLayout layout)
+ {
+ device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
+ }
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return device.LayoutSelection.Type == LayoutType;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Providers/DefaultLayoutProvider.cs b/src/Artemis.Core/Providers/DefaultLayoutProvider.cs
new file mode 100644
index 000000000..2360ab11b
--- /dev/null
+++ b/src/Artemis.Core/Providers/DefaultLayoutProvider.cs
@@ -0,0 +1,31 @@
+namespace Artemis.Core.Providers;
+
+public class DefaultLayoutProvider : ILayoutProvider
+{
+ public static string LayoutType = "Default";
+
+ ///
+ public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
+ {
+ // Look for a layout provided by the plugin
+ ArtemisLayout layout = device.DeviceProvider.LoadLayout(device);
+
+ // Finally fall back to a default layout
+ return layout.IsValid ? layout : ArtemisLayout.GetDefaultLayout(device);
+ }
+
+ ///
+ public void ApplyLayout(ArtemisDevice device, ArtemisLayout layout)
+ {
+ if (layout.IsDefaultLayout)
+ device.ApplyLayout(layout, false, false);
+ else
+ device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
+ }
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return device.LayoutSelection.Type == LayoutType;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs b/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs
new file mode 100644
index 000000000..fe8d421c2
--- /dev/null
+++ b/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs
@@ -0,0 +1,17 @@
+namespace Artemis.Core.Providers;
+
+///
+/// Represents a class that can provide Artemis layouts for devices.
+///
+public interface ILayoutProvider
+{
+ ///
+ /// If available, loads an Artemis layout for the provided device.
+ ///
+ /// The device to load the layout for.
+ /// The resulting layout if one was available; otherwise .
+ ArtemisLayout? GetDeviceLayout(ArtemisDevice device);
+
+ void ApplyLayout(ArtemisDevice device, ArtemisLayout layout);
+ bool IsMatch(ArtemisDevice device);
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Providers/NoneLayoutProvider.cs b/src/Artemis.Core/Providers/NoneLayoutProvider.cs
new file mode 100644
index 000000000..c764b0ddb
--- /dev/null
+++ b/src/Artemis.Core/Providers/NoneLayoutProvider.cs
@@ -0,0 +1,24 @@
+namespace Artemis.Core.Providers;
+
+public class NoneLayoutProvider : ILayoutProvider
+{
+ public static string LayoutType = "None";
+
+ ///
+ public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
+ {
+ return null;
+ }
+
+ ///
+ public void ApplyLayout(ArtemisDevice device, ArtemisLayout layout)
+ {
+ device.ApplyLayout(null, false, false);
+ }
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return device.LayoutSelection.Type == LayoutType;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/DeviceService.cs b/src/Artemis.Core/Services/DeviceService.cs
index ae43ab5ef..ea3e09533 100644
--- a/src/Artemis.Core/Services/DeviceService.cs
+++ b/src/Artemis.Core/Services/DeviceService.cs
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.DeviceProviders;
+using Artemis.Core.Providers;
using Artemis.Core.Services.Models;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces;
@@ -18,15 +19,21 @@ internal class DeviceService : IDeviceService
private readonly IPluginManagementService _pluginManagementService;
private readonly IDeviceRepository _deviceRepository;
private readonly Lazy _renderService;
+ private readonly Func> _getLayoutProviders;
private readonly List _enabledDevices = new();
private readonly List _devices = new();
- public DeviceService(ILogger logger, IPluginManagementService pluginManagementService, IDeviceRepository deviceRepository, Lazy renderService)
+ public DeviceService(ILogger logger,
+ IPluginManagementService pluginManagementService,
+ IDeviceRepository deviceRepository,
+ Lazy renderService,
+ Func> getLayoutProviders)
{
_logger = logger;
_pluginManagementService = pluginManagementService;
_deviceRepository = deviceRepository;
_renderService = renderService;
+ _getLayoutProviders = getLayoutProviders;
EnabledDevices = new ReadOnlyCollection(_enabledDevices);
Devices = new ReadOnlyCollection(_devices);
@@ -157,12 +164,23 @@ internal class DeviceService : IDeviceService
}
///
- public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout)
+ public void LoadDeviceLayout(ArtemisDevice device)
{
- if (layout == null || layout.Source == LayoutSource.Default)
- device.ApplyLayout(layout, false, false);
+ ILayoutProvider? provider = _getLayoutProviders().FirstOrDefault(p => p.IsMatch(device));
+ if (provider == null)
+ _logger.Warning("Could not find a layout provider for type {LayoutType} of device {Device}", device.LayoutSelection.Type, device);
+
+ ArtemisLayout? layout = provider?.GetDeviceLayout(device);
+ if (layout != null && !layout.IsValid)
+ {
+ _logger.Warning("Got an invalid layout {Layout} from {LayoutProvider}", layout, provider!.GetType().FullName);
+ layout = null;
+ }
+
+ if (layout == null)
+ device.ApplyLayout(null, false, false);
else
- device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
+ provider!.ApplyLayout(device, layout);
UpdateLeds();
}
@@ -230,7 +248,7 @@ internal class DeviceService : IDeviceService
device = new ArtemisDevice(rgbDevice, deviceProvider);
}
- ApplyDeviceLayout(device, device.GetBestDeviceLayout());
+ LoadDeviceLayout(device);
return device;
}
diff --git a/src/Artemis.Core/Services/Interfaces/IDeviceService.cs b/src/Artemis.Core/Services/Interfaces/IDeviceService.cs
index b66f14022..51b19d937 100644
--- a/src/Artemis.Core/Services/Interfaces/IDeviceService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IDeviceService.cs
@@ -43,11 +43,10 @@ public interface IDeviceService : IArtemisService
void AutoArrangeDevices();
///
- /// Apples the provided to the provided
+ /// Apples the best available to the provided
///
///
- ///
- void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout);
+ void LoadDeviceLayout(ArtemisDevice device);
///
/// Enables the provided device
diff --git a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs
index 31dd8ac3d..b19e38c03 100644
--- a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs
+++ b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs
@@ -22,11 +22,11 @@ public class DeviceEntity
public float GreenScale { get; set; }
public float BlueScale { get; set; }
public bool IsEnabled { get; set; }
-
- public bool DisableDefaultLayout { get; set; }
+
public int PhysicalLayout { get; set; }
public string LogicalLayout { get; set; }
- public string CustomLayoutPath { get; set; }
+ public string LayoutType { get; set; }
+ public string LayoutParameter { get; set; }
public List InputIdentifiers { get; set; }
public List InputMappings { get; set; }
diff --git a/src/Artemis.Storage/Entities/Workshop/EntryEntity.cs b/src/Artemis.Storage/Entities/Workshop/EntryEntity.cs
index 86bff6dcd..e2acf7922 100644
--- a/src/Artemis.Storage/Entities/Workshop/EntryEntity.cs
+++ b/src/Artemis.Storage/Entities/Workshop/EntryEntity.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace Artemis.Storage.Entities.Workshop;
@@ -16,6 +17,6 @@ public class EntryEntity
public long ReleaseId { get; set; }
public string ReleaseVersion { get; set; }
public DateTimeOffset InstalledAt { get; set; }
-
- public string LocalReference { get; set; }
+
+ public Dictionary Metadata { get; set; }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index 71d09529d..e4a63012a 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -58,19 +58,4 @@
..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll
-
-
-
- EntryListInputView.axaml
- Code
-
-
- EntryListItemView.axaml
- Code
-
-
- EntrySpecificationsView.axaml
- Code
-
-
\ No newline at end of file
diff --git a/src/Artemis.UI/DryIoc/ContainerExtensions.cs b/src/Artemis.UI/DryIoc/ContainerExtensions.cs
index da3e9a188..2d7f58b22 100644
--- a/src/Artemis.UI/DryIoc/ContainerExtensions.cs
+++ b/src/Artemis.UI/DryIoc/ContainerExtensions.cs
@@ -1,6 +1,7 @@
using System.Reflection;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.DryIoc.InstanceProviders;
+using Artemis.UI.Screens.Device.Layout.LayoutProviders;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Services.Updating;
@@ -26,6 +27,7 @@ public static class ContainerExtensions
container.RegisterMany(thisAssembly, type => type.IsAssignableTo(), setup: Setup.With(preventDisposal: true));
container.RegisterMany(thisAssembly, type => type.IsAssignableTo() && type.IsInterface, setup: Setup.With(preventDisposal: true));
+ container.RegisterMany(thisAssembly, type => type.IsAssignableTo() && type.IsInterface, setup: Setup.With(preventDisposal: true));
container.RegisterMany(thisAssembly, type => type.IsAssignableTo() && type != typeof(PropertyVmFactory));
container.Register(Reuse.Singleton);
diff --git a/src/Artemis.UI/DryIoc/Factories/IVMFactory.cs b/src/Artemis.UI/DryIoc/Factories/IVMFactory.cs
index b66691036..e95a8639c 100644
--- a/src/Artemis.UI/DryIoc/Factories/IVMFactory.cs
+++ b/src/Artemis.UI/DryIoc/Factories/IVMFactory.cs
@@ -1,13 +1,14 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
using System.Reactive;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.ScriptingProviders;
-using Artemis.UI.Routing;
using Artemis.UI.Screens.Device;
+using Artemis.UI.Screens.Device.General;
+using Artemis.UI.Screens.Device.InputMappings;
+using Artemis.UI.Screens.Device.Layout;
+using Artemis.UI.Screens.Device.Leds;
using Artemis.UI.Screens.Plugins;
using Artemis.UI.Screens.Plugins.Features;
using Artemis.UI.Screens.Plugins.Prerequisites;
@@ -27,12 +28,8 @@ using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Screens.VisualScripting.Pins;
-using Artemis.UI.Shared;
-using Artemis.UI.Shared.Routing;
using Artemis.WebClient.Updating;
using DryIoc;
-using DynamicData;
-using Material.Icons;
using ReactiveUI;
namespace Artemis.UI.DryIoc.Factories;
@@ -51,48 +48,49 @@ public interface IDeviceVmFactory : IVmFactory
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds);
DeviceGeneralTabViewModel DeviceGeneralTabViewModel(ArtemisDevice device);
}
+
public class DeviceFactory : IDeviceVmFactory
{
private readonly IContainer _container;
-
+
public DeviceFactory(IContainer container)
{
_container = container;
}
-
+
public DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device)
{
- return _container.Resolve(new object[] { device });
+ return _container.Resolve(new object[] {device});
}
-
+
public DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel)
{
- return _container.Resolve(new object[] { device, devicesTabViewModel });
+ return _container.Resolve(new object[] {device, devicesTabViewModel});
}
-
+
public DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device)
{
- return _container.Resolve(new object[] { device });
+ return _container.Resolve(new object[] {device});
}
-
+
public DeviceLayoutTabViewModel DeviceLayoutTabViewModel(ArtemisDevice device)
{
- return _container.Resolve(new object[] { device });
+ return _container.Resolve(new object[] {device});
}
public DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds)
{
- return _container.Resolve(new object[] { device, selectedLeds });
+ return _container.Resolve(new object[] {device, selectedLeds});
}
-
+
public InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds)
{
- return _container.Resolve(new object[] { device, selectedLeds });
+ return _container.Resolve(new object[] {device, selectedLeds});
}
public DeviceGeneralTabViewModel DeviceGeneralTabViewModel(ArtemisDevice device)
{
- return _container.Resolve(new object[] { device });
+ return _container.Resolve(new object[] {device});
}
}
@@ -102,28 +100,29 @@ public interface ISettingsVmFactory : IVmFactory
PluginViewModel PluginViewModel(Plugin plugin, ReactiveCommand? reload);
PluginFeatureViewModel PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
}
+
public class SettingsVmFactory : ISettingsVmFactory
{
private readonly IContainer _container;
-
+
public SettingsVmFactory(IContainer container)
{
_container = container;
}
-
+
public PluginSettingsViewModel PluginSettingsViewModel(Plugin plugin)
{
- return _container.Resolve(new object[] { plugin });
+ return _container.Resolve(new object[] {plugin});
}
-
+
public PluginViewModel PluginViewModel(Plugin plugin, ReactiveCommand? reload)
{
- return _container.Resolve(new object?[] { plugin, reload });
+ return _container.Resolve(new object?[] {plugin, reload});
}
-
+
public PluginFeatureViewModel PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield)
{
- return _container.Resolve(new object[] { pluginFeatureInfo, showShield });
+ return _container.Resolve(new object[] {pluginFeatureInfo, showShield});
}
}
@@ -132,23 +131,24 @@ public interface ISidebarVmFactory : IVmFactory
SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory);
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration);
}
+
public class SidebarVmFactory : ISidebarVmFactory
{
private readonly IContainer _container;
-
+
public SidebarVmFactory(IContainer container)
{
_container = container;
}
-
+
public SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory)
{
- return _container.Resolve(new object[] { profileCategory });
+ return _container.Resolve(new object[] {profileCategory});
}
-
+
public SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration)
{
- return _container.Resolve(new object[] { profileConfiguration });
+ return _container.Resolve(new object[] {profileConfiguration});
}
}
@@ -157,6 +157,7 @@ public interface ISurfaceVmFactory : IVmFactory
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
}
+
public class SurfaceVmFactory : ISurfaceVmFactory
{
private readonly IContainer _container;
@@ -168,12 +169,12 @@ public class SurfaceVmFactory : ISurfaceVmFactory
public SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel)
{
- return _container.Resolve(new object[] { device, surfaceEditorViewModel });
+ return _container.Resolve(new object[] {device, surfaceEditorViewModel});
}
public ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel)
{
- return _container.Resolve(new object[] { device, surfaceEditorViewModel });
+ return _container.Resolve(new object[] {device, surfaceEditorViewModel});
}
}
@@ -181,6 +182,7 @@ public interface IPrerequisitesVmFactory : IVmFactory
{
PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall);
}
+
public class PrerequisitesVmFactory : IPrerequisitesVmFactory
{
private readonly IContainer _container;
@@ -192,7 +194,7 @@ public class PrerequisitesVmFactory : IPrerequisitesVmFactory
public PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall)
{
- return _container.Resolve(new object[] { pluginPrerequisite, uninstall });
+ return _container.Resolve(new object[] {pluginPrerequisite, uninstall});
}
}
@@ -204,6 +206,7 @@ public interface IProfileEditorVmFactory : IVmFactory
LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer);
LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer);
}
+
public class ProfileEditorVmFactory : IProfileEditorVmFactory
{
private readonly IContainer _container;
@@ -215,27 +218,27 @@ public class ProfileEditorVmFactory : IProfileEditorVmFactory
public FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder)
{
- return _container.Resolve(new object?[] { parent, folder });
+ return _container.Resolve(new object?[] {parent, folder});
}
public LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer)
{
- return _container.Resolve(new object[] { layer });
+ return _container.Resolve(new object[] {layer});
}
public LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer)
{
- return _container.Resolve(new object?[] { parent, layer });
+ return _container.Resolve(new object?[] {parent, layer});
}
public LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer)
{
- return _container.Resolve(new object[] { layer });
+ return _container.Resolve(new object[] {layer});
}
public ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen)
{
- return _container.Resolve(new object[] { hostScreen });
+ return _container.Resolve(new object[] {hostScreen});
}
}
@@ -251,6 +254,7 @@ public interface ILayerPropertyVmFactory : IVmFactory
TimelineViewModel TimelineViewModel(ObservableCollection propertyGroupViewModels);
TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
}
+
public class LayerPropertyVmFactory : ILayerPropertyVmFactory
{
private readonly IContainer _container;
@@ -262,37 +266,37 @@ public class LayerPropertyVmFactory : ILayerPropertyVmFactory
public PropertyViewModel PropertyViewModel(ILayerProperty layerProperty)
{
- return _container.Resolve(new object[] { layerProperty });
+ return _container.Resolve(new object[] {layerProperty});
}
public PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup)
{
- return _container.Resolve(new object[] { layerPropertyGroup });
+ return _container.Resolve(new object[] {layerPropertyGroup});
}
public PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush)
{
- return _container.Resolve(new object[] { layerPropertyGroup, layerBrush });
+ return _container.Resolve(new object[] {layerPropertyGroup, layerBrush});
}
public PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect)
{
- return _container.Resolve(new object[] { layerPropertyGroup, layerEffect });
+ return _container.Resolve(new object[] {layerPropertyGroup, layerEffect});
}
public TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel)
{
- return _container.Resolve(new object[] { propertyGroupViewModel });
+ return _container.Resolve(new object[] {propertyGroupViewModel});
}
public TimelineViewModel TimelineViewModel(ObservableCollection propertyGroupViewModels)
{
- return _container.Resolve(new object[] { propertyGroupViewModels });
+ return _container.Resolve(new object[] {propertyGroupViewModels});
}
public TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel)
{
- return _container.Resolve(new object[] { propertyGroupViewModel });
+ return _container.Resolve(new object[] {propertyGroupViewModel});
}
}
@@ -300,6 +304,7 @@ public interface IDataBindingVmFactory : IVmFactory
{
DataBindingViewModel DataBindingViewModel();
}
+
public class DataBindingVmFactory : IDataBindingVmFactory
{
private readonly IContainer _container;
@@ -333,6 +338,7 @@ public interface INodeVmFactory : IVmFactory
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
}
+
public class NodeVmFactory : INodeVmFactory
{
private readonly IContainer _container;
@@ -344,47 +350,47 @@ public class NodeVmFactory : INodeVmFactory
public NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview)
{
- return _container.Resolve(new object[] { nodeScript, isPreview });
+ return _container.Resolve(new object[] {nodeScript, isPreview});
}
public NodePickerViewModel NodePickerViewModel(NodeScript nodeScript)
{
- return _container.Resolve(new object[] { nodeScript });
+ return _container.Resolve(new object[] {nodeScript});
}
public NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node)
{
- return _container.Resolve(new object[] { nodeScriptViewModel, node });
+ return _container.Resolve(new object[] {nodeScriptViewModel, node});
}
public CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to)
{
- return _container.Resolve(new object[] { nodeScriptViewModel, from, to });
+ return _container.Resolve(new object[] {nodeScriptViewModel, from, to});
}
public DragCableViewModel DragCableViewModel(PinViewModel pinViewModel)
{
- return _container.Resolve(new object[] { pinViewModel });
+ return _container.Resolve(new object[] {pinViewModel});
}
public InputPinViewModel InputPinViewModel(IPin inputPin, NodeScriptViewModel nodeScriptViewModel)
{
- return _container.Resolve(new object[] { inputPin, nodeScriptViewModel });
+ return _container.Resolve(new object[] {inputPin, nodeScriptViewModel});
}
public OutputPinViewModel OutputPinViewModel(IPin outputPin, NodeScriptViewModel nodeScriptViewModel)
{
- return _container.Resolve(new object[] { outputPin, nodeScriptViewModel });
+ return _container.Resolve(new object[] {outputPin, nodeScriptViewModel});
}
public InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel)
{
- return _container.Resolve(new object[] { inputPinCollection, nodeScriptViewModel });
+ return _container.Resolve(new object[] {inputPinCollection, nodeScriptViewModel});
}
public OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel)
{
- return _container.Resolve(new object[] { outputPinCollection, nodeScriptViewModel });
+ return _container.Resolve(new object[] {outputPinCollection, nodeScriptViewModel});
}
}
@@ -395,6 +401,7 @@ public interface IConditionVmFactory : IVmFactory
StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
}
+
public class ConditionVmFactory : IConditionVmFactory
{
private readonly IContainer _container;
@@ -406,22 +413,22 @@ public class ConditionVmFactory : IConditionVmFactory
public AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition)
{
- return _container.Resolve(new object[] { alwaysOnCondition });
+ return _container.Resolve(new object[] {alwaysOnCondition});
}
public PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition)
{
- return _container.Resolve(new object[] { playOnceCondition });
+ return _container.Resolve(new object[] {playOnceCondition});
}
public StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition)
{
- return _container.Resolve(new object[] { staticCondition });
+ return _container.Resolve(new object[] {staticCondition});
}
public EventConditionViewModel EventConditionViewModel(EventCondition eventCondition)
{
- return _container.Resolve(new object[] { eventCondition });
+ return _container.Resolve(new object[] {eventCondition});
}
}
@@ -432,6 +439,7 @@ public interface ILayerHintVmFactory : IVmFactory
KeyboardSectionAdaptionHintViewModel KeyboardSectionAdaptionHintViewModel(Layer layer, KeyboardSectionAdaptionHint adaptionHint);
SingleLedAdaptionHintViewModel SingleLedAdaptionHintViewModel(Layer layer, SingleLedAdaptionHint adaptionHint);
}
+
public class LayerHintVmFactory : ILayerHintVmFactory
{
private readonly IContainer _container;
@@ -443,22 +451,22 @@ public class LayerHintVmFactory : ILayerHintVmFactory
public CategoryAdaptionHintViewModel CategoryAdaptionHintViewModel(Layer layer, CategoryAdaptionHint adaptionHint)
{
- return _container.Resolve(new object[] { layer, adaptionHint });
+ return _container.Resolve(new object[] {layer, adaptionHint});
}
public DeviceAdaptionHintViewModel DeviceAdaptionHintViewModel(Layer layer, DeviceAdaptionHint adaptionHint)
{
- return _container.Resolve(new object[] { layer, adaptionHint });
+ return _container.Resolve(new object[] {layer, adaptionHint});
}
public KeyboardSectionAdaptionHintViewModel KeyboardSectionAdaptionHintViewModel(Layer layer, KeyboardSectionAdaptionHint adaptionHint)
{
- return _container.Resolve(new object[] { layer, adaptionHint });
+ return _container.Resolve(new object[] {layer, adaptionHint});
}
-
+
public SingleLedAdaptionHintViewModel SingleLedAdaptionHintViewModel(Layer layer, SingleLedAdaptionHint adaptionHint)
{
- return _container.Resolve(new object[] { layer, adaptionHint });
+ return _container.Resolve(new object[] {layer, adaptionHint});
}
}
@@ -467,6 +475,7 @@ public interface IScriptVmFactory : IVmFactory
ScriptConfigurationViewModel ScriptConfigurationViewModel(ScriptConfiguration scriptConfiguration);
ScriptConfigurationViewModel ScriptConfigurationViewModel(Profile profile, ScriptConfiguration scriptConfiguration);
}
+
public class ScriptVmFactory : IScriptVmFactory
{
private readonly IContainer _container;
@@ -478,12 +487,12 @@ public class ScriptVmFactory : IScriptVmFactory
public ScriptConfigurationViewModel ScriptConfigurationViewModel(ScriptConfiguration scriptConfiguration)
{
- return _container.Resolve(new object[] { scriptConfiguration });
+ return _container.Resolve(new object[] {scriptConfiguration});
}
public ScriptConfigurationViewModel ScriptConfigurationViewModel(Profile profile, ScriptConfiguration scriptConfiguration)
{
- return _container.Resolve(new object[] { profile, scriptConfiguration });
+ return _container.Resolve(new object[] {profile, scriptConfiguration});
}
}
@@ -491,6 +500,7 @@ public interface IReleaseVmFactory : IVmFactory
{
ReleaseViewModel ReleaseListViewModel(IGetReleases_PublishedReleases_Nodes release);
}
+
public class ReleaseVmFactory : IReleaseVmFactory
{
private readonly IContainer _container;
@@ -499,9 +509,9 @@ public class ReleaseVmFactory : IReleaseVmFactory
{
_container = container;
}
-
+
public ReleaseViewModel ReleaseListViewModel(IGetReleases_PublishedReleases_Nodes release)
{
- return _container.Resolve(new object[] { release });
+ return _container.Resolve(new object[] {release});
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabView.axaml b/src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabView.axaml
similarity index 98%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabView.axaml
rename to src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabView.axaml
index 5512743c0..c6d42e2b5 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabView.axaml
@@ -5,9 +5,10 @@
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
+ xmlns:general="clr-namespace:Artemis.UI.Screens.Device.General"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650"
- x:Class="Artemis.UI.Screens.Device.DeviceGeneralTabView"
- x:DataType="device:DeviceGeneralTabViewModel">
+ x:Class="Artemis.UI.Screens.Device.General.DeviceGeneralTabView"
+ x:DataType="general:DeviceGeneralTabViewModel">
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabView.axaml.cs
similarity index 76%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabView.axaml.cs
rename to src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabView.axaml.cs
index 8c4dff8b0..6a0d32d75 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabView.axaml.cs
@@ -1,7 +1,6 @@
-using Avalonia.Controls;
using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.General;
public partial class DeviceGeneralTabView : ReactiveUserControl
{
public DeviceGeneralTabView()
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabViewModel.cs
similarity index 93%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabViewModel.cs
index 12275f57a..19bd02b61 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceGeneralTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/General/DeviceGeneralTabViewModel.cs
@@ -4,6 +4,7 @@ using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
+using Artemis.UI.Screens.Device.Layout;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using PropertyChanged.SourceGenerator;
@@ -11,7 +12,7 @@ using ReactiveUI;
using RGB.NET.Core;
using SkiaSharp;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.General;
public partial class DeviceGeneralTabViewModel : ActivatableViewModelBase
{
@@ -56,7 +57,7 @@ public partial class DeviceGeneralTabViewModel : ActivatableViewModelBase
_initialGreenScale = Device.GreenScale;
_initialBlueScale = Device.BlueScale;
- this.WhenAnyValue(x => x.RedScale, x => x.GreenScale, x => x.BlueScale).Subscribe(_ => ApplyScaling());
+ this.WhenAnyValue(x => x.RedScale, x => x.GreenScale, x => x.BlueScale).Subscribe(_ => ApplyScaling());
this.WhenActivated(d =>
{
@@ -127,12 +128,12 @@ public partial class DeviceGeneralTabViewModel : ActivatableViewModelBase
return;
if (!Device.DeviceProvider.CanDetectPhysicalLayout && !await DevicePhysicalLayoutDialogViewModel.SelectPhysicalLayout(_windowService, Device))
return;
- if (!Device.DeviceProvider.CanDetectLogicalLayout && !await DeviceLogicalLayoutDialogViewModel.SelectLogicalLayout(_windowService, Device))
+ if (!Device.DeviceProvider.CanDetectLogicalLayout && !await Layout.DeviceLogicalLayoutDialogViewModel.SelectLogicalLayout(_windowService, Device))
return;
await Task.Delay(400);
_deviceService.SaveDevice(Device);
- _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout());
+ _deviceService.LoadDeviceLayout(Device);
}
private void Apply()
diff --git a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml b/src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabView.axaml
similarity index 94%
rename from src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml
rename to src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabView.axaml
index e7c3bb42d..f76881aed 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabView.axaml
@@ -4,9 +4,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
+ xmlns:inputMappings="clr-namespace:Artemis.UI.Screens.Device.InputMappings"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Device.InputMappingsTabView"
- x:DataType="device:InputMappingsTabViewModel">
+ x:Class="Artemis.UI.Screens.Device.InputMappings.InputMappingsTabView"
+ x:DataType="inputMappings:InputMappingsTabViewModel">
diff --git a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabView.axaml.cs
similarity index 75%
rename from src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml.cs
rename to src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabView.axaml.cs
index c5a533ade..36dea6bb0 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabView.axaml.cs
@@ -1,7 +1,6 @@
-using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.InputMappings;
public partial class InputMappingsTabView : ReactiveUserControl
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabViewModel.cs
similarity index 98%
rename from src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabViewModel.cs
index 122029a67..9a116cd92 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/InputMappings/InputMappingsTabViewModel.cs
@@ -6,13 +6,12 @@ using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Shared;
-using HidSharp.Reports.Units;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
using RGB.NET.Core;
using Unit = System.Reactive.Unit;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.InputMappings;
public partial class InputMappingsTabViewModel : ActivatableViewModelBase
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabView.axaml
similarity index 64%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabView.axaml
index 5075c6e5d..b154411cf 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabView.axaml
@@ -5,9 +5,12 @@
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
+ xmlns:providers="clr-namespace:Artemis.Core.Providers;assembly=Artemis.Core"
+ xmlns:layout="clr-namespace:Artemis.UI.Screens.Device.Layout"
+ xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
- x:Class="Artemis.UI.Screens.Device.DeviceLayoutTabView"
- x:DataType="device:DeviceLayoutTabViewModel">
+ x:Class="Artemis.UI.Screens.Device.Layout.DeviceLayoutTabView"
+ x:DataType="layout:DeviceLayoutTabViewModel">
@@ -28,6 +31,7 @@
+
@@ -45,28 +49,43 @@
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabView.axaml.cs
similarity index 95%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabView.axaml.cs
index 18a162302..3663d773b 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabView.axaml.cs
@@ -2,7 +2,7 @@
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Layout;
public partial class DeviceLayoutTabView : ReactiveUserControl
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabViewModel.cs
similarity index 60%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabViewModel.cs
index c6e5dd6ba..4e3ef3455 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLayoutTabViewModel.cs
@@ -1,81 +1,52 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
+using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
-using System.Reactive.Disposables;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Artemis.Core;
-using Artemis.Core.Services;
+using Artemis.UI.Screens.Device.Layout.LayoutProviders;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
-using Avalonia.Controls;
-using ReactiveUI;
+using PropertyChanged.SourceGenerator;
using RGB.NET.Layout;
-using SkiaSharp;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Layout;
-public class DeviceLayoutTabViewModel : ActivatableViewModelBase
+public partial class DeviceLayoutTabViewModel : ActivatableViewModelBase
{
+ [Notify] private ILayoutProviderViewModel? _selectedLayoutProvider;
+
private readonly IWindowService _windowService;
private readonly INotificationService _notificationService;
- private readonly IDeviceService _deviceService;
- public DeviceLayoutTabViewModel(IWindowService windowService, INotificationService notificationService, IDeviceService deviceService, ArtemisDevice device)
+ public DeviceLayoutTabViewModel(IWindowService windowService, INotificationService notificationService, ArtemisDevice device, List layoutProviders)
{
_windowService = windowService;
_notificationService = notificationService;
- _deviceService = deviceService;
Device = device;
DisplayName = "Layout";
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
- this.WhenActivated(d =>
+ LayoutProviders = new ObservableCollection(layoutProviders);
+ foreach (ILayoutProviderViewModel layoutProviderViewModel in layoutProviders)
{
- Device.PropertyChanged += DeviceOnPropertyChanged;
- Disposable.Create(() => Device.PropertyChanged -= DeviceOnPropertyChanged).DisposeWith(d);
- });
+ layoutProviderViewModel.Device = Device;
+ if (layoutProviderViewModel.IsMatch(Device))
+ SelectedLayoutProvider = layoutProviderViewModel;
+ }
}
public ArtemisDevice Device { get; }
+ public ObservableCollection LayoutProviders { get; set; }
public string DefaultLayoutPath { get; }
public string? ImagePath => Device.Layout?.Image?.LocalPath;
- public string? CustomLayoutPath => Device.CustomLayoutPath;
-
- public bool HasCustomLayout => Device.CustomLayoutPath != null;
-
- public void ClearCustomLayout()
- {
- Device.CustomLayoutPath = null;
- _notificationService.CreateNotification()
- .WithMessage("Cleared imported layout.")
- .WithSeverity(NotificationSeverity.Informational);
- }
-
- public async Task BrowseCustomLayout()
- {
- string[]? files = await _windowService.CreateOpenFileDialog()
- .WithTitle("Select device layout file")
- .HavingFilter(f => f.WithName("Layout files").WithExtension("xml"))
- .ShowAsync();
-
- if (files?.Length > 0)
- {
- Device.CustomLayoutPath = files[0];
- _notificationService.CreateNotification()
- .WithTitle("Imported layout")
- .WithMessage($"File loaded from {files[0]}")
- .WithSeverity(NotificationSeverity.Informational);
- }
- }
-
public async Task ExportLayout()
{
string fileName = Device.DeviceProvider.GetDeviceLayoutName(Device);
@@ -145,19 +116,4 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
.WithSeverity(NotificationSeverity.Informational)
.Show();
}
-
- private void DeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
- {
- if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout))
- {
- Task.Run(() =>
- {
- _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout());
- _deviceService.SaveDevice(Device);
- });
-
- this.RaisePropertyChanged(nameof(CustomLayoutPath));
- this.RaisePropertyChanged(nameof(HasCustomLayout));
- }
- }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogView.axaml
similarity index 90%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogView.axaml
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogView.axaml
index 4a86455ad..0626dc357 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogView.axaml
@@ -4,9 +4,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:globalization="clr-namespace:System.Globalization;assembly=System.Runtime"
+ xmlns:layout="clr-namespace:Artemis.UI.Screens.Device.Layout"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Device.DeviceLogicalLayoutDialogView"
- x:DataType="device:DeviceLogicalLayoutDialogViewModel">
+ x:Class="Artemis.UI.Screens.Device.Layout.DeviceLogicalLayoutDialogView"
+ x:DataType="layout:DeviceLogicalLayoutDialogViewModel">
Artemis couldn't automatically determine the logical layout of your
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogView.axaml.cs
similarity index 92%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogView.axaml.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogView.axaml.cs
index 4eda36844..c91a64662 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogView.axaml.cs
@@ -1,12 +1,10 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Layout;
public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogViewModel.cs
similarity index 93%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogViewModel.cs
index f41937466..31ec7d3d8 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLogicalLayoutDialogViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DeviceLogicalLayoutDialogViewModel.cs
@@ -12,7 +12,7 @@ using PropertyChanged.SourceGenerator;
using ReactiveUI;
using ContentDialogButton = Artemis.UI.Shared.Services.Builders.ContentDialogButton;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Layout;
public partial class DeviceLogicalLayoutDialogViewModel : ContentDialogViewModelBase
{
@@ -24,7 +24,7 @@ public partial class DeviceLogicalLayoutDialogViewModel : ContentDialogViewModel
public DeviceLogicalLayoutDialogViewModel(ArtemisDevice device)
{
Device = device;
- ApplyLogicalLayout = ReactiveCommand.Create(ExecuteApplyLogicalLayout, this.WhenAnyValue(vm => vm.SelectedRegion).Select(r => r != null));
+ ApplyLogicalLayout = ReactiveCommand.Create(ExecuteApplyLogicalLayout, this.WhenAnyValue(vm => vm.SelectedRegion).Select(r => r != null));
Regions = new ObservableCollection(CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => c.LCID != LOCALE_INVARIANT &&
c.LCID != LOCALE_NEUTRAL &&
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogView.axaml
similarity index 95%
rename from src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogView.axaml
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogView.axaml
index 484a95b26..38298645d 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogView.axaml
@@ -3,9 +3,10 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
+ xmlns:layout="clr-namespace:Artemis.UI.Screens.Device.Layout"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Device.DevicePhysicalLayoutDialogView"
- x:DataType="device:DevicePhysicalLayoutDialogViewModel">
+ x:Class="Artemis.UI.Screens.Device.Layout.DevicePhysicalLayoutDialogView"
+ x:DataType="layout:DevicePhysicalLayoutDialogViewModel">
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogView.axaml.cs
similarity index 68%
rename from src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogView.axaml.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogView.axaml.cs
index 9c67bacfe..8466e2443 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogView.axaml.cs
@@ -1,7 +1,6 @@
-using Avalonia.Markup.Xaml;
-using Avalonia.ReactiveUI;
+using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Layout;
public partial class DevicePhysicalLayoutDialogView : ReactiveUserControl
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogViewModel.cs
similarity index 96%
rename from src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogViewModel.cs
index 52e13f6bc..b80167932 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DevicePhysicalLayoutDialogViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/DevicePhysicalLayoutDialogViewModel.cs
@@ -7,7 +7,7 @@ using Artemis.UI.Shared.Services;
using FluentAvalonia.UI.Controls;
using ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Layout;
public class DevicePhysicalLayoutDialogViewModel : ContentDialogViewModelBase
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutView.axaml
new file mode 100644
index 000000000..b03d7f765
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutView.axaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutView.axaml.cs
new file mode 100644
index 000000000..230302deb
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public partial class CustomLayoutView : UserControl
+{
+ public CustomLayoutView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs
new file mode 100644
index 000000000..4a22ff1f0
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs
@@ -0,0 +1,71 @@
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.Core.Providers;
+using Artemis.Core.Services;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Services.Builders;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
+{
+ private readonly CustomPathLayoutProvider _layoutProvider;
+ private readonly IDeviceService _deviceService;
+
+ public CustomLayoutViewModel(IWindowService windowService, INotificationService notificationService, CustomPathLayoutProvider layoutProvider, IDeviceService deviceService)
+ {
+ _layoutProvider = layoutProvider;
+ _deviceService = deviceService;
+ _windowService = windowService;
+ _notificationService = notificationService;
+ }
+
+ ///
+ public string Name => "Custom";
+
+ ///
+ public string Description => "Select a layout file from a folder on your computer";
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return _layoutProvider.IsMatch(device);
+ }
+
+ public ArtemisDevice Device { get; set; } = null!;
+
+ private readonly IWindowService _windowService;
+ private readonly INotificationService _notificationService;
+
+ public void ClearCustomLayout()
+ {
+ Device.LayoutSelection.Type = CustomPathLayoutProvider.LayoutType;
+ Device.LayoutSelection.Parameter = null;
+ _notificationService.CreateNotification()
+ .WithMessage("Cleared imported layout.")
+ .WithSeverity(NotificationSeverity.Informational);
+
+ _deviceService.SaveDevice(Device);
+ }
+
+ public async Task BrowseCustomLayout()
+ {
+ string[]? files = await _windowService.CreateOpenFileDialog()
+ .WithTitle("Select device layout file")
+ .HavingFilter(f => f.WithName("Layout files").WithExtension("xml"))
+ .ShowAsync();
+
+ if (files?.Length > 0)
+ {
+ Device.LayoutSelection.Type = CustomPathLayoutProvider.LayoutType;
+ Device.LayoutSelection.Parameter = files[0];
+ _notificationService.CreateNotification()
+ .WithTitle("Imported layout")
+ .WithMessage($"File loaded from {files[0]}")
+ .WithSeverity(NotificationSeverity.Informational);
+ }
+
+ _deviceService.SaveDevice(Device);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutView.axaml
new file mode 100644
index 000000000..7c248e017
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutView.axaml
@@ -0,0 +1,10 @@
+
+
+ Loading the default layout from the plugin or User Layouts folder.
+
+
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutView.axaml.cs
new file mode 100644
index 000000000..4dcf9a258
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public partial class DefaultLayoutView : UserControl
+{
+ public DefaultLayoutView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs
new file mode 100644
index 000000000..e49fbcf07
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs
@@ -0,0 +1,29 @@
+using Artemis.Core;
+using Artemis.Core.Providers;
+using Artemis.UI.Shared;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public class DefaultLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
+{
+ private readonly DefaultLayoutProvider _layoutProvider;
+
+ public DefaultLayoutViewModel(DefaultLayoutProvider layoutProvider)
+ {
+ _layoutProvider = layoutProvider;
+ }
+
+ public ArtemisDevice Device { get; set; } = null!;
+
+ ///
+ public string Name => "Default";
+
+ ///
+ public string Description => "Attempts to load a layout from the default paths";
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return _layoutProvider.IsMatch(device);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/ILayoutProviderViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/ILayoutProviderViewModel.cs
new file mode 100644
index 000000000..7de990659
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/ILayoutProviderViewModel.cs
@@ -0,0 +1,14 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public interface ILayoutProviderViewModel
+{
+ string Name { get; }
+
+ string Description { get; }
+
+ bool IsMatch(ArtemisDevice device);
+
+ ArtemisDevice Device { get; set; }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutView.axaml
new file mode 100644
index 000000000..95b49ed07
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutView.axaml
@@ -0,0 +1,10 @@
+
+
+ Not loading any layout, leaving LEDs to be positioned by the device provider.
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutView.axaml.cs
new file mode 100644
index 000000000..be8396558
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public partial class NoneLayoutView : UserControl
+{
+ public NoneLayoutView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs
new file mode 100644
index 000000000..9b0090b05
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs
@@ -0,0 +1,29 @@
+using Artemis.Core;
+using Artemis.Core.Providers;
+using Artemis.UI.Shared;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public class NoneLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
+{
+ private readonly NoneLayoutProvider _layoutProvider;
+
+ public NoneLayoutViewModel(NoneLayoutProvider layoutProvider)
+ {
+ _layoutProvider = layoutProvider;
+ }
+
+ public ArtemisDevice Device { get; set; } = null!;
+
+ ///
+ public string Name => "None";
+
+ ///
+ public string Description => "Do not load any layout";
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return _layoutProvider.IsMatch(device);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml
new file mode 100644
index 000000000..78a976e98
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml
@@ -0,0 +1,11 @@
+
+
+
+ Here you'll do workshop things
+
+
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs
new file mode 100644
index 000000000..98a51506d
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public partial class WorkshopLayoutView : UserControl
+{
+ public WorkshopLayoutView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs
new file mode 100644
index 000000000..07128d075
--- /dev/null
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs
@@ -0,0 +1,29 @@
+using Artemis.Core;
+using Artemis.UI.Shared;
+using Artemis.WebClient.Workshop.Providers;
+
+namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
+
+public class WorkshopLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
+{
+ private readonly WorkshopLayoutProvider _layoutProvider;
+
+ public WorkshopLayoutViewModel(WorkshopLayoutProvider layoutProvider)
+ {
+ _layoutProvider = layoutProvider;
+ }
+
+ public ArtemisDevice Device { get; set; } = null!;
+
+ ///
+ public string Name => "Workshop";
+
+ ///
+ public string Description => "Load a layout from the workshop";
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return _layoutProvider.IsMatch(device);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabLedViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabLedViewModel.cs
similarity index 96%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabLedViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabLedViewModel.cs
index 2b249fb4d..d8a352d4c 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabLedViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabLedViewModel.cs
@@ -2,7 +2,7 @@
using Artemis.Core;
using Artemis.UI.Shared;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Leds;
public class DeviceLedsTabLedViewModel : ViewModelBase
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabView.axaml
similarity index 93%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabView.axaml
rename to src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabView.axaml
index bdd07da58..9b0ccf258 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabView.axaml
@@ -5,9 +5,10 @@
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
+ xmlns:leds="clr-namespace:Artemis.UI.Screens.Device.Leds"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Device.DeviceLedsTabView"
- x:DataType="device:DeviceLedsTabViewModel">
+ x:Class="Artemis.UI.Screens.Device.Leds.DeviceLedsTabView"
+ x:DataType="leds:DeviceLedsTabViewModel">
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabView.axaml.cs
similarity index 74%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabView.axaml.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabView.axaml.cs
index 263abb0ce..c144c7558 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabView.axaml.cs
@@ -1,7 +1,6 @@
-using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Leds;
public partial class DeviceLedsTabView : ReactiveUserControl
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabViewModel.cs
similarity index 96%
rename from src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs
rename to src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabViewModel.cs
index 3eda03340..e05a22b08 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Leds/DeviceLedsTabViewModel.cs
@@ -8,7 +8,7 @@ using Artemis.UI.Shared;
using DynamicData.Binding;
using ReactiveUI;
-namespace Artemis.UI.Screens.Device;
+namespace Artemis.UI.Screens.Device.Leds;
public class DeviceLedsTabViewModel : ActivatableViewModelBase
{
diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs
index 4c2009334..81b66bec2 100644
--- a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs
@@ -46,7 +46,7 @@ public partial class InstalledTabItemViewModel : ViewModelBase
private async Task ExecuteViewLocal(CancellationToken cancellationToken)
{
- if (InstalledEntry.EntryType == EntryType.Profile && Guid.TryParse(InstalledEntry.LocalReference, out Guid profileId))
+ if (InstalledEntry.EntryType == EntryType.Profile && InstalledEntry.TryGetMetadata("ProfileId", out Guid profileId))
{
await _router.Navigate($"profile-editor/{profileId}");
}
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
index cdbdec029..a2396ae0e 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
@@ -63,7 +63,7 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
try
{
- ArtemisLayout layout = new(selected[0], LayoutSource.User);
+ ArtemisLayout layout = new(selected[0]);
if (!layout.IsValid)
{
await _windowService.ShowConfirmContentDialog("Failed to load layout", "The selected file does not appear to be a valid RGB.NET layout file.", "Close", null);
diff --git a/src/Artemis.UI/Services/DeviceLayoutService.cs b/src/Artemis.UI/Services/DeviceLayoutService.cs
index aa1994fbd..07f1c4d7a 100644
--- a/src/Artemis.UI/Services/DeviceLayoutService.cs
+++ b/src/Artemis.UI/Services/DeviceLayoutService.cs
@@ -5,11 +5,13 @@ using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.Device;
+using Artemis.UI.Screens.Device.Layout;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.MainWindow;
using Avalonia.Threading;
using RGB.NET.Core;
+using DeviceLogicalLayoutDialogViewModel = Artemis.UI.Screens.Device.Layout.DeviceLogicalLayoutDialogViewModel;
using KeyboardLayoutType = Artemis.Core.KeyboardLayoutType;
namespace Artemis.UI.Services;
@@ -61,7 +63,7 @@ public class DeviceLayoutService : IDeviceLayoutService
await Task.Delay(400);
_deviceService.SaveDevice(artemisDevice);
- _deviceService.ApplyDeviceLayout(artemisDevice, artemisDevice.GetBestDeviceLayout());
+ _deviceService.LoadDeviceLayout(artemisDevice);
}
private bool DeviceNeedsLayout(ArtemisDevice d)
diff --git a/src/Artemis.WebClient.Workshop/DryIoc/ContainerExtensions.cs b/src/Artemis.WebClient.Workshop/DryIoc/ContainerExtensions.cs
index ddadc110e..65449d45b 100644
--- a/src/Artemis.WebClient.Workshop/DryIoc/ContainerExtensions.cs
+++ b/src/Artemis.WebClient.Workshop/DryIoc/ContainerExtensions.cs
@@ -1,7 +1,9 @@
using System.Reflection;
+using Artemis.Core.Providers;
using Artemis.WebClient.Workshop.Extensions;
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
+using Artemis.WebClient.Workshop.Providers;
using Artemis.WebClient.Workshop.Repositories;
using Artemis.WebClient.Workshop.Services;
using Artemis.WebClient.Workshop.State;
@@ -46,6 +48,7 @@ public static class ContainerExtensions
container.Register(Reuse.Singleton);
container.Register(Reuse.Singleton);
container.Register(Reuse.Singleton);
+ container.Register(Reuse.Singleton);
container.Register(Reuse.Transient);
container.RegisterMany(workshopAssembly, type => type.IsAssignableTo(), Reuse.Transient);
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
index 575048c4f..13a2e2318 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
@@ -1,4 +1,9 @@
-using Artemis.UI.Shared.Extensions;
+using System.Data;
+using System.IO.Compression;
+using Artemis.Core;
+using Artemis.Core.Services;
+using Artemis.UI.Shared.Extensions;
+using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Services;
@@ -8,17 +13,17 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IWorkshopService _workshopService;
+ private readonly IDeviceService _deviceService;
- public LayoutEntryInstallationHandler(IHttpClientFactory httpClientFactory, IWorkshopService workshopService)
+ public LayoutEntryInstallationHandler(IHttpClientFactory httpClientFactory, IWorkshopService workshopService, IDeviceService deviceService)
{
_httpClientFactory = httpClientFactory;
_workshopService = workshopService;
+ _deviceService = deviceService;
}
public async Task InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress progress, CancellationToken cancellationToken)
{
- throw new NotImplementedException();
-
using MemoryStream stream = new();
// Download the provided release
@@ -31,15 +36,46 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
{
return EntryInstallResult.FromFailure(e.Message);
}
-
- // return EntryInstallResult.FromSuccess();
+
+ // Ensure there is an installed entry
+ InstalledEntry installedEntry = _workshopService.GetInstalledEntry(entry) ?? _workshopService.CreateInstalledEntry(entry);
+ DirectoryInfo entryDirectory = installedEntry.GetDirectory();
+
+ // If the folder already exists, remove it so that if the layout now contains less files, old things dont stick around
+ if (entryDirectory.Exists)
+ entryDirectory.Delete(true);
+ entryDirectory.Create();
+
+ // Extract the archive, we could go through the hoops of keeping track of progress but this should be so quick it doesn't matter
+ stream.Seek(0, SeekOrigin.Begin);
+ using ZipArchive archive = new(stream);
+ archive.ExtractToDirectory(entryDirectory.FullName);
+
+ ArtemisLayout layout = new(Path.Combine(entryDirectory.FullName, "layout.xml"));
+ if (layout.IsValid)
+ return EntryInstallResult.FromSuccess(layout);
+
+ // If the layout ended up being invalid yoink it out again, shoooo
+ entryDirectory.Delete(true);
+ _workshopService.RemoveInstalledEntry(installedEntry);
+ return EntryInstallResult.FromFailure("Layout failed to load because it is invalid");
}
public async Task UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken)
{
- throw new NotImplementedException();
-
- if (!Guid.TryParse(installedEntry.LocalReference, out Guid profileId))
- return EntryUninstallResult.FromFailure("Local reference does not contain a GUID");
+ // Remove the layout from any devices currently using it
+ foreach (ArtemisDevice device in _deviceService.Devices)
+ {
+ }
+
+ // Remove from filesystem
+ DirectoryInfo directory = installedEntry.GetDirectory(true);
+ if (directory.Exists)
+ directory.Delete();
+
+ // Remove entry
+ _workshopService.RemoveInstalledEntry(installedEntry);
+
+ return EntryUninstallResult.FromSuccess();
}
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
index 8b7f33e73..b322e40cd 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
@@ -36,13 +36,13 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
// Find existing installation to potentially replace the profile
InstalledEntry? installedEntry = _workshopService.GetInstalledEntry(entry);
- if (installedEntry != null && Guid.TryParse(installedEntry.LocalReference, out Guid profileId))
+ if (installedEntry != null && installedEntry.TryGetMetadata("ProfileId", out Guid profileId))
{
ProfileConfiguration? existing = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == profileId);
if (existing != null)
{
ProfileConfiguration overwritten = await _profileService.OverwriteProfile(stream, existing);
- installedEntry.LocalReference = overwritten.ProfileId.ToString();
+ installedEntry.SetMetadata("ProfileId", overwritten.ProfileId);
// Update the release and return the profile configuration
UpdateRelease(releaseId, installedEntry);
@@ -56,8 +56,8 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
// Add the profile as a fresh import
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "Workshop") ?? _profileService.CreateProfileCategory("Workshop", true);
ProfileConfiguration imported = await _profileService.ImportProfile(stream, category, true, true, null);
- installedEntry.LocalReference = imported.ProfileId.ToString();
-
+ installedEntry.SetMetadata("ProfileId", imported.ProfileId);
+
// Update the release and return the profile configuration
UpdateRelease(releaseId, installedEntry);
return EntryInstallResult.FromSuccess(imported);
@@ -65,7 +65,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
public async Task UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken)
{
- if (!Guid.TryParse(installedEntry.LocalReference, out Guid profileId))
+ if (!installedEntry.TryGetMetadata("ProfileId", out Guid profileId))
return EntryUninstallResult.FromFailure("Local reference does not contain a GUID");
return await Task.Run(() =>
diff --git a/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs b/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs
new file mode 100644
index 000000000..4b6901b3f
--- /dev/null
+++ b/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs
@@ -0,0 +1,48 @@
+using Artemis.Core;
+using Artemis.Core.Providers;
+using Artemis.WebClient.Workshop.Services;
+
+namespace Artemis.WebClient.Workshop.Providers;
+
+public class WorkshopLayoutProvider : ILayoutProvider
+{
+ public static string LayoutType = "Workshop";
+
+ private readonly IWorkshopService _workshopService;
+
+ public WorkshopLayoutProvider(IWorkshopService workshopService)
+ {
+ _workshopService = workshopService;
+ }
+
+ ///
+ public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
+ {
+ InstalledEntry? layoutEntry = _workshopService.GetInstalledEntries().FirstOrDefault(e => e.EntryType == EntryType.Layout && MatchesDevice(e, device));
+ if (layoutEntry == null)
+ return null;
+
+ string layoutPath = Path.Combine(Constants.WorkshopFolder, layoutEntry.EntryId.ToString(), "layout.xml");
+ if (!File.Exists(layoutPath))
+ return null;
+
+ return new ArtemisLayout(layoutPath);
+ }
+
+ ///
+ public void ApplyLayout(ArtemisDevice device, ArtemisLayout layout)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public bool IsMatch(ArtemisDevice device)
+ {
+ return device.LayoutSelection.Type == LayoutType;
+ }
+
+ private bool MatchesDevice(InstalledEntry entry, ArtemisDevice device)
+ {
+ return entry.TryGetMetadata("DeviceId", out HashSet? deviceIds) && deviceIds.Contains(device.Identifier);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs b/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs
index 8d5cebd18..e284595e8 100644
--- a/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs
+++ b/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs
@@ -1,13 +1,16 @@
-using Artemis.Storage.Entities.Workshop;
+using System.Diagnostics.CodeAnalysis;
+using Artemis.Core;
+using Artemis.Storage.Entities.Workshop;
namespace Artemis.WebClient.Workshop.Services;
public class InstalledEntry
{
+ private Dictionary _metadata = new();
+
internal InstalledEntry(EntryEntity entity)
{
Entity = entity;
-
Load();
}
@@ -32,8 +35,6 @@ public class InstalledEntry
public string ReleaseVersion { get; set; } = string.Empty;
public DateTimeOffset InstalledAt { get; set; }
- public string? LocalReference { get; set; }
-
internal EntryEntity Entity { get; }
internal void Load()
@@ -48,7 +49,7 @@ public class InstalledEntry
ReleaseVersion = Entity.ReleaseVersion;
InstalledAt = Entity.InstalledAt;
- LocalReference = Entity.LocalReference;
+ _metadata = new Dictionary(Entity.Metadata);
}
internal void Save()
@@ -63,6 +64,60 @@ public class InstalledEntry
Entity.ReleaseVersion = ReleaseVersion;
Entity.InstalledAt = InstalledAt;
- Entity.LocalReference = LocalReference;
+ Entity.Metadata = new Dictionary(_metadata);
+ }
+
+ ///
+ /// Gets the metadata value associated with the specified key.
+ ///
+ /// The key of the value to get.
+ /// When this method returns, contains the value associated with the specified key, if the key is found and of type ;
+ /// otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.
+ /// The type of the value.
+ /// if the metadata contains an element with the specified key; otherwise, .
+ public bool TryGetMetadata(string key, [NotNullWhen(true)] out T? value)
+ {
+ if (!_metadata.TryGetValue(key, out object? objectValue) || objectValue is not T result)
+ {
+ value = default;
+ return false;
+ }
+
+ value = result;
+ return true;
+ }
+
+ ///
+ /// Sets metadata with the provided key to the provided value.
+ ///
+ /// The key of the value to set
+ /// The value to set.
+ public void SetMetadata(string key, object value)
+ {
+ _metadata.Add(key, value);
+ }
+
+ ///
+ /// Removes metadata with the provided key.
+ ///
+ /// The key of the metadata to remove
+ /// if the element is successfully found and removed; otherwise, .
+ public bool RemoveMetadata(string key)
+ {
+ return _metadata.Remove(key);
+ }
+
+ ///
+ /// Returns the directory info of the entry, where any files would be stored if applicable.
+ ///
+ /// A value indicating whether or not to return the root directory of the entry, and not the version.
+ /// The directory info of the entry, where any files would be stored if applicable.
+ public DirectoryInfo GetDirectory(bool rootDirectory = false)
+ {
+ if (rootDirectory)
+ return new DirectoryInfo(Path.Combine(Constants.WorkshopFolder, EntryId.ToString()));
+
+ string safeVersion = Path.GetInvalidFileNameChars().Aggregate(ReleaseVersion, (current, c) => current.Replace(c, '-'));
+ return new DirectoryInfo(Path.Combine(Constants.WorkshopFolder, EntryId.ToString(), safeVersion));
}
}
\ No newline at end of file