1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Devices - Implemented switching between layout providers

This commit is contained in:
RobertBeekman 2024-01-13 11:59:12 +01:00
parent e8590abd61
commit dad6a56238
25 changed files with 178 additions and 80 deletions

View File

@ -1,11 +1,18 @@
using RGB.NET.Layout; using Artemis.Core.Services;
using RGB.NET.Layout;
namespace Artemis.Core.Providers; namespace Artemis.Core.Providers;
public class CustomPathLayoutProvider : ILayoutProvider public class CustomPathLayoutProvider : ILayoutProvider
{ {
public static string LayoutType = "CustomPath"; public static string LayoutType = "CustomPath";
private readonly IDeviceService _deviceService;
public CustomPathLayoutProvider(IDeviceService deviceService)
{
_deviceService = deviceService;
}
/// <inheritdoc /> /// <inheritdoc />
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device) public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
{ {
@ -19,10 +26,23 @@ public class CustomPathLayoutProvider : ILayoutProvider
{ {
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported); device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
} }
/// <inheritdoc /> /// <inheritdoc />
public bool IsMatch(ArtemisDevice device) public bool IsMatch(ArtemisDevice device)
{ {
return device.LayoutSelection.Type == LayoutType; return device.LayoutSelection.Type == LayoutType;
} }
/// <summary>
/// Configures the provided device to use this layout provider.
/// </summary>
/// <param name="device">The device to apply the provider to.</param>
/// <param name="path">The path to the custom layout.</param>
public void ConfigureDevice(ArtemisDevice device, string? path)
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = path;
_deviceService.SaveDevice(device);
_deviceService.LoadDeviceLayout(device);
}
} }

View File

@ -1,8 +1,16 @@
namespace Artemis.Core.Providers; using Artemis.Core.Services;
namespace Artemis.Core.Providers;
public class DefaultLayoutProvider : ILayoutProvider public class DefaultLayoutProvider : ILayoutProvider
{ {
public static string LayoutType = "Default"; public static string LayoutType = "Default";
private readonly IDeviceService _deviceService;
public DefaultLayoutProvider(IDeviceService deviceService)
{
_deviceService = deviceService;
}
/// <inheritdoc /> /// <inheritdoc />
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device) public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
@ -28,4 +36,16 @@ public class DefaultLayoutProvider : ILayoutProvider
{ {
return device.LayoutSelection.Type == LayoutType; return device.LayoutSelection.Type == LayoutType;
} }
/// <summary>
/// Configures the provided device to use this layout provider.
/// </summary>
/// <param name="device">The device to apply the provider to.</param>
public void ConfigureDevice(ArtemisDevice device)
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = null;
_deviceService.SaveDevice(device);
_deviceService.LoadDeviceLayout(device);
}
} }

View File

@ -1,9 +1,17 @@
namespace Artemis.Core.Providers; using Artemis.Core.Services;
namespace Artemis.Core.Providers;
public class NoneLayoutProvider : ILayoutProvider public class NoneLayoutProvider : ILayoutProvider
{ {
private readonly IDeviceService _deviceService;
public static string LayoutType = "None"; public static string LayoutType = "None";
public NoneLayoutProvider(IDeviceService deviceService)
{
_deviceService = deviceService;
}
/// <inheritdoc /> /// <inheritdoc />
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device) public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
{ {
@ -21,4 +29,16 @@ public class NoneLayoutProvider : ILayoutProvider
{ {
return device.LayoutSelection.Type == LayoutType; return device.LayoutSelection.Type == LayoutType;
} }
/// <summary>
/// Configures the provided device to use this layout provider.
/// </summary>
/// <param name="device">The device to apply the provider to.</param>
public void ConfigureDevice(ArtemisDevice device)
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = null;
_deviceService.SaveDevice(device);
_deviceService.LoadDeviceLayout(device);
}
} }

View File

@ -8,6 +8,7 @@ using Artemis.Core.Providers;
using Artemis.Core.Services.Models; using Artemis.Core.Services.Models;
using Artemis.Storage.Entities.Surface; using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using DryIoc;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog; using Serilog;
@ -19,7 +20,7 @@ internal class DeviceService : IDeviceService
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly IDeviceRepository _deviceRepository; private readonly IDeviceRepository _deviceRepository;
private readonly Lazy<IRenderService> _renderService; private readonly Lazy<IRenderService> _renderService;
private readonly Func<IEnumerable<ILayoutProvider>> _getLayoutProviders; private readonly LazyEnumerable<ILayoutProvider> _layoutProviders;
private readonly List<ArtemisDevice> _enabledDevices = new(); private readonly List<ArtemisDevice> _enabledDevices = new();
private readonly List<ArtemisDevice> _devices = new(); private readonly List<ArtemisDevice> _devices = new();
@ -27,13 +28,13 @@ internal class DeviceService : IDeviceService
IPluginManagementService pluginManagementService, IPluginManagementService pluginManagementService,
IDeviceRepository deviceRepository, IDeviceRepository deviceRepository,
Lazy<IRenderService> renderService, Lazy<IRenderService> renderService,
Func<IEnumerable<ILayoutProvider>> getLayoutProviders) LazyEnumerable<ILayoutProvider> layoutProviders)
{ {
_logger = logger; _logger = logger;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_deviceRepository = deviceRepository; _deviceRepository = deviceRepository;
_renderService = renderService; _renderService = renderService;
_getLayoutProviders = getLayoutProviders; _layoutProviders = layoutProviders;
EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices); EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices);
Devices = new ReadOnlyCollection<ArtemisDevice>(_devices); Devices = new ReadOnlyCollection<ArtemisDevice>(_devices);
@ -166,7 +167,7 @@ internal class DeviceService : IDeviceService
/// <inheritdoc /> /// <inheritdoc />
public void LoadDeviceLayout(ArtemisDevice device) public void LoadDeviceLayout(ArtemisDevice device)
{ {
ILayoutProvider? provider = _getLayoutProviders().FirstOrDefault(p => p.IsMatch(device)); ILayoutProvider? provider = _layoutProviders.FirstOrDefault(p => p.IsMatch(device));
if (provider == null) if (provider == null)
_logger.Warning("Could not find a layout provider for type {LayoutType} of device {Device}", device.LayoutSelection.Type, device); _logger.Warning("Could not find a layout provider for type {LayoutType} of device {Device}", device.LayoutSelection.Type, device);
@ -176,11 +177,18 @@ internal class DeviceService : IDeviceService
_logger.Warning("Got an invalid layout {Layout} from {LayoutProvider}", layout, provider!.GetType().FullName); _logger.Warning("Got an invalid layout {Layout} from {LayoutProvider}", layout, provider!.GetType().FullName);
layout = null; layout = null;
} }
if (layout == null) try
device.ApplyLayout(null, false, false); {
else if (layout == null)
provider!.ApplyLayout(device, layout); device.ApplyLayout(null, false, false);
else
provider?.ApplyLayout(device, layout);
}
catch (Exception e)
{
_logger.Error(e, "Failed to apply device layout");
}
UpdateLeds(); UpdateLeds();
} }

View File

@ -4,8 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" 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:layout="clr-namespace:Artemis.UI.Screens.Device.Layout"
xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders" xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
@ -83,8 +81,8 @@
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>
</Grid> </Grid>
<ContentControl Content="{CompiledBinding SelectedLayoutProvider}" ClipToBounds="False"></ContentControl> <ContentControl Content="{CompiledBinding SelectedLayoutProvider}" ClipToBounds="False" />
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto"> <Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">

View File

@ -11,6 +11,7 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
using PropertyChanged.SourceGenerator; using PropertyChanged.SourceGenerator;
using ReactiveUI;
using RGB.NET.Layout; using RGB.NET.Layout;
namespace Artemis.UI.Screens.Device.Layout; namespace Artemis.UI.Screens.Device.Layout;
@ -35,9 +36,16 @@ public partial class DeviceLayoutTabViewModel : ActivatableViewModelBase
foreach (ILayoutProviderViewModel layoutProviderViewModel in layoutProviders) foreach (ILayoutProviderViewModel layoutProviderViewModel in layoutProviders)
{ {
layoutProviderViewModel.Device = Device; layoutProviderViewModel.Device = Device;
if (layoutProviderViewModel.IsMatch(Device)) if (layoutProviderViewModel.Provider.IsMatch(Device))
SelectedLayoutProvider = layoutProviderViewModel; SelectedLayoutProvider = layoutProviderViewModel;
} }
// When changing device provider to one that isn't currently on the device, apply it to the device immediately
this.WhenAnyValue(vm => vm.SelectedLayoutProvider).Subscribe(l =>
{
if (l != null && !l.Provider.IsMatch(Device))
l.Apply();
});
} }
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
@ -77,13 +85,13 @@ public partial class DeviceLayoutTabViewModel : ActivatableViewModelBase
} }
else else
{ {
List<LedLayout> ledLayouts = Device.Leds.Select(x => new LedLayout() List<LedLayout> ledLayouts = Device.Leds.Select(x => new LedLayout
{ {
Id = x.RgbLed.Id.ToString(), Id = x.RgbLed.Id.ToString(),
DescriptiveX = x.Rectangle.Left.ToString(), DescriptiveX = x.Rectangle.Left.ToString(),
DescriptiveY = x.Rectangle.Top.ToString(), DescriptiveY = x.Rectangle.Top.ToString(),
DescriptiveWidth = $"{x.Rectangle.Width}mm", DescriptiveWidth = $"{x.Rectangle.Width}mm",
DescriptiveHeight = $"{x.Rectangle.Height}mm", DescriptiveHeight = $"{x.Rectangle.Height}mm"
}).ToList(); }).ToList();
DeviceLayout emptyLayout = new() DeviceLayout emptyLayout = new()
@ -94,7 +102,7 @@ public partial class DeviceLayoutTabViewModel : ActivatableViewModelBase
Model = Device.RgbDevice.DeviceInfo.Model, Model = Device.RgbDevice.DeviceInfo.Model,
Width = Device.Rectangle.Width, Width = Device.Rectangle.Width,
Height = Device.Rectangle.Height, Height = Device.Rectangle.Height,
InternalLeds = ledLayouts, InternalLeds = ledLayouts
}; };
XmlSerializer serializer = new(typeof(DeviceLayout)); XmlSerializer serializer = new(typeof(DeviceLayout));

View File

@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 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:globalization="clr-namespace:System.Globalization;assembly=System.Runtime"
xmlns:layout="clr-namespace:Artemis.UI.Screens.Device.Layout" xmlns:layout="clr-namespace:Artemis.UI.Screens.Device.Layout"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
@ -28,8 +27,9 @@
<AutoCompleteBox.ItemTemplate> <AutoCompleteBox.ItemTemplate>
<DataTemplate DataType="{x:Type globalization:RegionInfo}"> <DataTemplate DataType="{x:Type globalization:RegionInfo}">
<TextBlock> <TextBlock>
<Run Text="{CompiledBinding EnglishName}"></Run> <Run Text="{CompiledBinding EnglishName}" />
<Run Text="(" /><Run FontWeight="SemiBold" Text="{CompiledBinding TwoLetterISORegionName}"></Run><Run Text=")" /> <Run Text="(" /><Run FontWeight="SemiBold" Text="{CompiledBinding TwoLetterISORegionName}" />
<Run Text=")" />
</TextBlock> </TextBlock>
</DataTemplate> </DataTemplate>
</AutoCompleteBox.ItemTemplate> </AutoCompleteBox.ItemTemplate>

View File

@ -11,7 +11,7 @@ public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceL
public DeviceLogicalLayoutDialogView() public DeviceLogicalLayoutDialogView()
{ {
InitializeComponent(); InitializeComponent();
RegionsAutoCompleteBox.ItemFilter += SearchRegions; RegionsAutoCompleteBox.ItemFilter += SearchRegions;
Dispatcher.UIThread.InvokeAsync(DelayedAutoFocus); Dispatcher.UIThread.InvokeAsync(DelayedAutoFocus);
} }
@ -34,5 +34,4 @@ public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceL
regionInfo.NativeName.Contains(search, StringComparison.OrdinalIgnoreCase) || regionInfo.NativeName.Contains(search, StringComparison.OrdinalIgnoreCase) ||
regionInfo.TwoLetterISORegionName.Contains(search, StringComparison.OrdinalIgnoreCase); regionInfo.TwoLetterISORegionName.Contains(search, StringComparison.OrdinalIgnoreCase);
} }
} }

View File

@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 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" xmlns:layout="clr-namespace:Artemis.UI.Screens.Device.Layout"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.Layout.DevicePhysicalLayoutDialogView" x:Class="Artemis.UI.Screens.Device.Layout.DevicePhysicalLayoutDialogView"

View File

@ -8,5 +8,4 @@ public partial class DevicePhysicalLayoutDialogView : ReactiveUserControl<Device
{ {
InitializeComponent(); InitializeComponent();
} }
} }

View File

@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders" xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.CustomLayoutView" x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.CustomLayoutView"
@ -12,7 +11,7 @@
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto"> <Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Row="1" Grid.Column="0"> <StackPanel Grid.Row="1" Grid.Column="0">
<TextBlock Text="Custom layout path" /> <TextBlock Text="Current layout" />
<TextBlock Classes="subtitle" FontSize="12" Text="{CompiledBinding Device.LayoutSelection.Parameter, TargetNullValue=None}" TextWrapping="Wrap" /> <TextBlock Classes="subtitle" FontSize="12" Text="{CompiledBinding Device.LayoutSelection.Parameter, TargetNullValue=None}" TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal"> <StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">

View File

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders; namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;

View File

@ -1,7 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Providers; using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
@ -11,12 +10,10 @@ namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
{ {
private readonly CustomPathLayoutProvider _layoutProvider; private readonly CustomPathLayoutProvider _layoutProvider;
private readonly IDeviceService _deviceService;
public CustomLayoutViewModel(IWindowService windowService, INotificationService notificationService, CustomPathLayoutProvider layoutProvider, IDeviceService deviceService) public CustomLayoutViewModel(IWindowService windowService, INotificationService notificationService, CustomPathLayoutProvider layoutProvider)
{ {
_layoutProvider = layoutProvider; _layoutProvider = layoutProvider;
_deviceService = deviceService;
_windowService = windowService; _windowService = windowService;
_notificationService = notificationService; _notificationService = notificationService;
} }
@ -28,25 +25,21 @@ public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public string Description => "Select a layout file from a folder on your computer"; public string Description => "Select a layout file from a folder on your computer";
/// <inheritdoc /> /// <inheritdoc />
public bool IsMatch(ArtemisDevice device) public ILayoutProvider Provider => _layoutProvider;
{
return _layoutProvider.IsMatch(device);
}
public ArtemisDevice Device { get; set; } = null!; public ArtemisDevice Device { get; set; } = null!;
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
private readonly INotificationService _notificationService; private readonly INotificationService _notificationService;
public void ClearCustomLayout() public void ClearCustomLayout()
{ {
Device.LayoutSelection.Type = CustomPathLayoutProvider.LayoutType; _layoutProvider.ConfigureDevice(Device, null);
Device.LayoutSelection.Parameter = null;
_notificationService.CreateNotification() _notificationService.CreateNotification()
.WithMessage("Cleared imported layout.") .WithMessage("Cleared imported layout.")
.WithSeverity(NotificationSeverity.Informational); .WithSeverity(NotificationSeverity.Informational);
_deviceService.SaveDevice(Device);
} }
public async Task BrowseCustomLayout() public async Task BrowseCustomLayout()
@ -58,14 +51,18 @@ public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
if (files?.Length > 0) if (files?.Length > 0)
{ {
Device.LayoutSelection.Type = CustomPathLayoutProvider.LayoutType; _layoutProvider.ConfigureDevice(Device, files[0]);
Device.LayoutSelection.Parameter = files[0];
_notificationService.CreateNotification() _notificationService.CreateNotification()
.WithTitle("Imported layout") .WithTitle("Imported layout")
.WithMessage($"File loaded from {files[0]}") .WithMessage($"File loaded from {files[0]}")
.WithSeverity(NotificationSeverity.Informational); .WithSeverity(NotificationSeverity.Informational);
} }
}
_deviceService.SaveDevice(Device);
/// <inheritdoc />
public void Apply()
{
_layoutProvider.ConfigureDevice(Device, null);
} }
} }

View File

@ -4,7 +4,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.DefaultLayoutView"> x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.DefaultLayoutView">
<TextBlock Classes="subtitle" FontSize="12"> <StackPanel ClipToBounds="False">
Loading the default layout from the plugin or User Layouts folder. <Border Classes="card-separator" />
</TextBlock> <StackPanel>
</UserControl> <TextBlock Text="Current layout" />
<TextBlock Classes="subtitle" FontSize="12" Text="Loading the default layout from the plugin or User Layouts folder." TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</UserControl>

View File

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders; namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;

View File

@ -13,8 +13,11 @@ public class DefaultLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
_layoutProvider = layoutProvider; _layoutProvider = layoutProvider;
} }
/// <inheritdoc />
public ILayoutProvider Provider => _layoutProvider;
public ArtemisDevice Device { get; set; } = null!; public ArtemisDevice Device { get; set; } = null!;
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Default"; public string Name => "Default";
@ -22,8 +25,8 @@ public class DefaultLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public string Description => "Attempts to load a layout from the default paths"; public string Description => "Attempts to load a layout from the default paths";
/// <inheritdoc /> /// <inheritdoc />
public bool IsMatch(ArtemisDevice device) public void Apply()
{ {
return _layoutProvider.IsMatch(device); _layoutProvider.ConfigureDevice(Device);
} }
} }

View File

@ -1,4 +1,5 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Providers;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders; namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
@ -8,7 +9,9 @@ public interface ILayoutProviderViewModel
string Description { get; } string Description { get; }
bool IsMatch(ArtemisDevice device); ILayoutProvider Provider { get; }
ArtemisDevice Device { get; set; } ArtemisDevice Device { get; set; }
void Apply();
} }

View File

@ -4,7 +4,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.NoneLayoutView"> x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.NoneLayoutView">
<TextBlock Classes="subtitle" FontSize="12"> <StackPanel ClipToBounds="False">
Not loading any layout, leaving LEDs to be positioned by the device provider. <Border Classes="card-separator" />
</TextBlock> <StackPanel>
<TextBlock Text="Current layout" />
<TextBlock Classes="subtitle" FontSize="12" Text="Not loading any layout, leaving LEDs to be positioned by the device provider." TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</UserControl> </UserControl>

View File

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders; namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;

View File

@ -13,8 +13,11 @@ public class NoneLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
_layoutProvider = layoutProvider; _layoutProvider = layoutProvider;
} }
/// <inheritdoc />
public ILayoutProvider Provider => _layoutProvider;
public ArtemisDevice Device { get; set; } = null!; public ArtemisDevice Device { get; set; } = null!;
/// <inheritdoc /> /// <inheritdoc />
public string Name => "None"; public string Name => "None";
@ -22,8 +25,8 @@ public class NoneLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public string Description => "Do not load any layout"; public string Description => "Do not load any layout";
/// <inheritdoc /> /// <inheritdoc />
public bool IsMatch(ArtemisDevice device) public void Apply()
{ {
return _layoutProvider.IsMatch(device); _layoutProvider.ConfigureDevice(Device);
} }
} }

View File

@ -4,8 +4,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.WorkshopLayoutView"> x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.WorkshopLayoutView">
<StackPanel ClipToBounds="False">
<TextBlock Classes="subtitle" FontSize="12"> <Border Classes="card-separator" />
Here you'll do workshop things <StackPanel>
</TextBlock> <TextBlock Text="Current layout" />
</UserControl> <TextBlock Classes="subtitle" FontSize="12" Text="Loading the layout from a workshop entry" TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</UserControl>

View File

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders; namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;

View File

@ -1,4 +1,5 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Providers;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.WebClient.Workshop.Providers; using Artemis.WebClient.Workshop.Providers;
@ -13,6 +14,9 @@ public class WorkshopLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
_layoutProvider = layoutProvider; _layoutProvider = layoutProvider;
} }
/// <inheritdoc />
public ILayoutProvider Provider => _layoutProvider;
public ArtemisDevice Device { get; set; } = null!; public ArtemisDevice Device { get; set; } = null!;
/// <inheritdoc /> /// <inheritdoc />
@ -22,8 +26,8 @@ public class WorkshopLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public string Description => "Load a layout from the workshop"; public string Description => "Load a layout from the workshop";
/// <inheritdoc /> /// <inheritdoc />
public bool IsMatch(ArtemisDevice device) public void Apply()
{ {
return _layoutProvider.IsMatch(device); _layoutProvider.ConfigureDevice(Device);
} }
} }

View File

@ -1,5 +1,6 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Providers; using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.WebClient.Workshop.Services; using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Providers; namespace Artemis.WebClient.Workshop.Providers;
@ -7,11 +8,13 @@ namespace Artemis.WebClient.Workshop.Providers;
public class WorkshopLayoutProvider : ILayoutProvider public class WorkshopLayoutProvider : ILayoutProvider
{ {
public static string LayoutType = "Workshop"; public static string LayoutType = "Workshop";
private readonly IDeviceService _deviceService;
private readonly IWorkshopService _workshopService; private readonly IWorkshopService _workshopService;
public WorkshopLayoutProvider(IWorkshopService workshopService) public WorkshopLayoutProvider(IDeviceService deviceService, IWorkshopService workshopService)
{ {
_deviceService = deviceService;
_workshopService = workshopService; _workshopService = workshopService;
} }
@ -45,4 +48,16 @@ public class WorkshopLayoutProvider : ILayoutProvider
{ {
return entry.TryGetMetadata("DeviceId", out HashSet<string>? deviceIds) && deviceIds.Contains(device.Identifier); return entry.TryGetMetadata("DeviceId", out HashSet<string>? deviceIds) && deviceIds.Contains(device.Identifier);
} }
/// <summary>
/// Configures the provided device to use this layout provider.
/// </summary>
/// <param name="device">The device to apply the provider to.</param>
public void ConfigureDevice(ArtemisDevice device)
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = null;
_deviceService.SaveDevice(device);
_deviceService.LoadDeviceLayout(device);
}
} }

View File

@ -49,7 +49,7 @@ public class InstalledEntry
ReleaseVersion = Entity.ReleaseVersion; ReleaseVersion = Entity.ReleaseVersion;
InstalledAt = Entity.InstalledAt; InstalledAt = Entity.InstalledAt;
_metadata = new Dictionary<string, object>(Entity.Metadata); _metadata = Entity.Metadata != null ? new Dictionary<string, object>(Entity.Metadata) : new Dictionary<string, object>();
} }
internal void Save() internal void Save()