diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs
index deeff25bc..8440e8016 100644
--- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs
+++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs
@@ -385,31 +385,5 @@ public void Save()
Entity.Type = pathType.FullName;
}
- #region Equality members
-
- ///
- /// >
- protected bool Equals(DataModelPath other)
- {
- return ReferenceEquals(Target, other.Target) && Path == other.Path;
- }
-
- ///
- public override bool Equals(object? obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((DataModelPath) obj);
- }
-
- ///
- public override int GetHashCode()
- {
- return HashCode.Combine(Target, Path);
- }
-
- #endregion
-
#endregion
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
index 55e237b5d..ac8e3827b 100644
--- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
+++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using RGB.NET.Layout;
@@ -15,7 +16,7 @@ internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
DeviceLayout = deviceLayout;
RgbLayout = led;
LayoutCustomLedData = (LayoutCustomLedData?) led.CustomData ?? new LayoutCustomLedData();
-
+
// Default to the first logical layout for images
LayoutCustomLedDataLogicalLayout? defaultLogicalLayout = LayoutCustomLedData.LogicalLayouts?.FirstOrDefault();
if (defaultLogicalLayout != null)
@@ -46,7 +47,14 @@ internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
/// Gets the custom layout data embedded in the RGB.NET layout
///
public LayoutCustomLedData LayoutCustomLedData { get; }
-
+
+ ///
+ /// Gets the logical layout names available for this LED
+ ///
+ public IEnumerable GetLogicalLayoutNames()
+ {
+ return LayoutCustomLedData.LogicalLayouts?.Where(l => l.Name != null).Select(l => l.Name!) ?? [];
+ }
internal void ApplyCustomLedData(ArtemisDevice artemisDevice)
{
@@ -61,11 +69,11 @@ internal void ApplyCustomLedData(ArtemisDevice artemisDevice)
ApplyLogicalLayout(logicalLayout);
}
-
+
private void ApplyLogicalLayout(LayoutCustomLedDataLogicalLayout logicalLayout)
{
string? layoutDirectory = Path.GetDirectoryName(DeviceLayout.FilePath);
-
+
LogicalName = logicalLayout.Name;
if (layoutDirectory != null && logicalLayout.Image != null)
Image = new Uri(Path.Combine(layoutDirectory, logicalLayout.Image!), UriKind.Absolute);
diff --git a/src/Artemis.Core/Plugins/Profiling/Profiler.cs b/src/Artemis.Core/Plugins/Profiling/Profiler.cs
index e920ed3da..d8d184ef1 100644
--- a/src/Artemis.Core/Plugins/Profiling/Profiler.cs
+++ b/src/Artemis.Core/Plugins/Profiling/Profiler.cs
@@ -23,8 +23,7 @@ internal Profiler(Plugin plugin, string name)
/// Gets the name of this profiler
///
public string Name { get; }
-
-
+
///
/// Gets a dictionary containing measurements by their identifiers
///
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index 1714435f1..9702f8043 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -21,6 +21,7 @@
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs
index 51aeecec0..71d7891a8 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs
@@ -57,7 +57,7 @@ public PlaybackViewModel(IProfileEditorService profileEditorService, ISettingsSe
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
// Update timer
- Timer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000));
+ Timer updateTimer = new(TimeSpan.FromMilliseconds(16));
updateTimer.Elapsed += (_, _) => Update();
updateTimer.DisposeWith(d);
_profileEditorService.Playing.Subscribe(_ => _lastUpdate = DateTime.Now).DisposeWith(d);
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs
index 7435ac361..6e1da26b5 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs
@@ -51,7 +51,7 @@ public DataBindingViewModel(IProfileEditorService profileEditorService, INodeVmF
.DisposeWith(d);
_profileEditorService.Playing.CombineLatest(_profileEditorService.SuspendedEditing).Subscribe(tuple => _playing = tuple.First || tuple.Second).DisposeWith(d);
- Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000));
+ Timer updateTimer = new(TimeSpan.FromMilliseconds(25));
Timer saveTimer = new(TimeSpan.FromMinutes(2));
updateTimer.Elapsed += (_, _) => Update();
diff --git a/src/Artemis.UI/Screens/StartupWizard/StartupWizardView.axaml.cs b/src/Artemis.UI/Screens/StartupWizard/StartupWizardView.axaml.cs
index 094e16d39..71c72bb2e 100644
--- a/src/Artemis.UI/Screens/StartupWizard/StartupWizardView.axaml.cs
+++ b/src/Artemis.UI/Screens/StartupWizard/StartupWizardView.axaml.cs
@@ -29,10 +29,12 @@ private void ApplyCurrentStep(int step)
else if (step == 2)
Frame.NavigateToType(typeof(DevicesStep), null, new FrameNavigationOptions());
else if (step == 3)
- Frame.NavigateToType(typeof(LayoutStep), null, new FrameNavigationOptions());
+ Frame.NavigateToType(typeof(LayoutsStep), null, new FrameNavigationOptions());
else if (step == 4)
- Frame.NavigateToType(typeof(SettingsStep), null, new FrameNavigationOptions());
+ Frame.NavigateToType(typeof(SurfaceStep), null, new FrameNavigationOptions());
else if (step == 5)
+ Frame.NavigateToType(typeof(SettingsStep), null, new FrameNavigationOptions());
+ else if (step == 6)
Frame.NavigateToType(typeof(FinishStep), null, new FrameNavigationOptions());
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs
index 8ea9f3cbd..588338b35 100644
--- a/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs
+++ b/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs
@@ -9,6 +9,7 @@
using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Plugins;
+using Artemis.UI.Screens.Workshop.LayoutFinder;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services;
@@ -35,7 +36,8 @@ public StartupWizardViewModel(IContainer container,
IPluginManagementService pluginManagementService,
IWindowService windowService,
IDeviceService deviceService,
- ISettingsVmFactory settingsVmFactory)
+ ISettingsVmFactory settingsVmFactory,
+ LayoutFinderViewModel layoutFinderViewModel)
{
_settingsService = settingsService;
_windowService = windowService;
@@ -54,6 +56,7 @@ public StartupWizardViewModel(IContainer container,
.Where(p => p.Info.IsCompatible && p.Features.Any(f => f.AlwaysEnabled && f.FeatureType.IsAssignableTo(typeof(DeviceProvider))))
.OrderBy(p => p.Info.Name)
.Select(p => settingsVmFactory.PluginViewModel(p, ReactiveCommand.Create(() => new Unit()))));
+ LayoutFinderViewModel = layoutFinderViewModel;
CurrentStep = 1;
SetupButtons();
@@ -82,7 +85,8 @@ public StartupWizardViewModel(IContainer container,
public string Version { get; }
public ObservableCollection DeviceProviders { get; }
-
+ public LayoutFinderViewModel LayoutFinderViewModel { get; }
+
public bool IsAutoRunSupported => _autoRunProvider != null;
public PluginSetting UIAutoRun => _settingsService.GetSetting("UI.AutoRun", false);
@@ -98,7 +102,7 @@ private void ExecuteGoBack()
CurrentStep--;
// Skip the settings step if none of it's contents are supported
- if (CurrentStep == 4 && !IsAutoRunSupported)
+ if (CurrentStep == 5 && !IsAutoRunSupported)
CurrentStep--;
SetupButtons();
@@ -106,11 +110,11 @@ private void ExecuteGoBack()
private void ExecuteContinue()
{
- if (CurrentStep < 5)
+ if (CurrentStep < 6)
CurrentStep++;
// Skip the settings step if none of it's contents are supported
- if (CurrentStep == 4 && !IsAutoRunSupported)
+ if (CurrentStep == 5 && !IsAutoRunSupported)
CurrentStep++;
SetupButtons();
@@ -118,9 +122,9 @@ private void ExecuteContinue()
private void SetupButtons()
{
- ShowContinue = CurrentStep != 3 && CurrentStep < 5;
+ ShowContinue = CurrentStep != 4 && CurrentStep < 6;
ShowGoBack = CurrentStep > 1;
- ShowFinish = CurrentStep == 5;
+ ShowFinish = CurrentStep == 6;
}
private void ExecuteSkipOrFinishWizard()
diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutsStep.axaml b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutsStep.axaml
new file mode 100644
index 000000000..40810d841
--- /dev/null
+++ b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutsStep.axaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+ Device layouts provide Artemis with an image of your devices and exact LED positions.
+ While not strictly necessary, this helps to create effects that are perfectly aligned with your hardware.
+
+
+ Below you can automatically search the Artemis Workshop for device layouts of your devices.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutsStep.axaml.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutsStep.axaml.cs
new file mode 100644
index 000000000..b07123e59
--- /dev/null
+++ b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutsStep.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.StartupWizard.Steps;
+
+public partial class LayoutsStep : UserControl
+{
+ public LayoutsStep()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStep.axaml b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStep.axaml
similarity index 97%
rename from src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStep.axaml
rename to src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStep.axaml
index f8905dd96..bae89bde5 100644
--- a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStep.axaml
+++ b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStep.axaml
@@ -5,7 +5,7 @@
xmlns:startupWizard="clr-namespace:Artemis.UI.Screens.StartupWizard"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.StartupWizard.Steps.LayoutStep"
+ x:Class="Artemis.UI.Screens.StartupWizard.Steps.SurfaceStep"
x:DataType="startupWizard:StartupWizardViewModel">
diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStep.axaml.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStep.axaml.cs
similarity index 67%
rename from src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStep.axaml.cs
rename to src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStep.axaml.cs
index cf318b584..a099d29a8 100644
--- a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStep.axaml.cs
+++ b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStep.axaml.cs
@@ -3,9 +3,9 @@
namespace Artemis.UI.Screens.StartupWizard.Steps;
-public partial class LayoutStep : UserControl
+public partial class SurfaceStep : UserControl
{
- public LayoutStep()
+ public SurfaceStep()
{
InitializeComponent();
}
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs
index d37d5a09a..aeab47450 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs
@@ -66,7 +66,7 @@ public NodeScriptWindowViewModel(NodeScript nodeScript,
{
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
- Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000));
+ Timer updateTimer = new(TimeSpan.FromMilliseconds(25));
Timer saveTimer = new(TimeSpan.FromMinutes(2));
updateTimer.Elapsed += (_, _) => Update();
diff --git a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml
index 15127badd..eafe6dd46 100644
--- a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml
@@ -42,24 +42,24 @@
+
- Sign out
+ Click="Manage_OnClick">
+ Manage account
-
- Manage account
+ Click="Signout_OnClick">
+ Sign out
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml
index 597282d98..5a235b3bf 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml
@@ -15,8 +15,8 @@
-
-
+
+
Categories
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs
index f335c9128..a6b5309c7 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs
@@ -54,6 +54,7 @@ protected EntryListViewModel(IWorkshopClient workshopClient,
this.WhenActivated(d =>
{
InputViewModel.WhenAnyValue(vm => vm.Search).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset()).DisposeWith(d);
+ InputViewModel.WhenAnyValue(vm => vm.SortBy).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset()).DisposeWith(d);
CategoriesViewModel.WhenAnyValue(vm => vm.CategoryFilters).Skip(1).Subscribe(_ => Reset()).DisposeWith(d);
});
@@ -71,6 +72,7 @@ protected EntryListViewModel(IWorkshopClient workshopClient,
public CategoriesViewModel CategoriesViewModel { get; }
public EntryListInputViewModel InputViewModel { get; }
+ public bool ShowCategoryFilter { get; set; } = true;
public EntryType? EntryType { get; set; }
public ReadOnlyObservableCollection Entries { get; }
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogView.axaml
deleted file mode 100644
index 2f2962b95..000000000
--- a/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogView.axaml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- Select the devices on which you would like to apply the downloaded layout.
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogView.axaml.cs
deleted file mode 100644
index 76b6cb2ec..000000000
--- a/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogView.axaml.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Avalonia.ReactiveUI;
-
-namespace Artemis.UI.Screens.Workshop.Layout.Dialogs;
-
-public partial class DeviceSelectionDialogView : ReactiveUserControl
-{
- public DeviceSelectionDialogView()
- {
- InitializeComponent();
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogViewModel.cs
deleted file mode 100644
index 381d669b7..000000000
--- a/src/Artemis.UI/Screens/Workshop/Layout/Dialogs/DeviceSelectionDialogViewModel.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Reactive;
-using Artemis.Core;
-using Artemis.Core.Services;
-using Artemis.UI.DryIoc.Factories;
-using Artemis.UI.Screens.SurfaceEditor;
-using Artemis.UI.Shared;
-using Artemis.WebClient.Workshop.Models;
-using Artemis.WebClient.Workshop.Providers;
-using Artemis.WebClient.Workshop.Services;
-using ReactiveUI;
-
-namespace Artemis.UI.Screens.Workshop.Layout.Dialogs;
-
-public class DeviceSelectionDialogViewModel : ContentDialogViewModelBase
-{
- private readonly IDeviceService _deviceService;
- private readonly WorkshopLayoutProvider _layoutProvider;
-
- public DeviceSelectionDialogViewModel(List devices, InstalledEntry entry, ISurfaceVmFactory surfaceVmFactory, IDeviceService deviceService, WorkshopLayoutProvider layoutProvider)
- {
- _deviceService = deviceService;
- _layoutProvider = layoutProvider;
- Entry = entry;
- Devices = new ObservableCollection(devices.Select(surfaceVmFactory.ListDeviceViewModel));
- Apply = ReactiveCommand.Create(ExecuteApply);
- }
-
- public InstalledEntry Entry { get; }
- public ObservableCollection Devices { get; }
- public ReactiveCommand Apply { get; }
-
- private void ExecuteApply()
- {
- foreach (ListDeviceViewModel listDeviceViewModel in Devices.Where(d => d.IsSelected))
- {
- _layoutProvider.ConfigureDevice(listDeviceViewModel.Device, Entry);
- _deviceService.SaveDevice(listDeviceViewModel.Device);
- _deviceService.LoadDeviceLayout(listDeviceViewModel.Device);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
index 166652cad..ef8bb86ff 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
@@ -31,13 +31,14 @@ public LayoutInfoViewModel(ArtemisLayout layout,
IWindowService windowService,
IPluginManagementService pluginManagementService)
{
+ ArtemisDevice? device = deviceService.Devices.FirstOrDefault(d => d.Layout == layout);
+
_windowService = windowService;
- _vendor = layout.RgbLayout.Vendor;
- _model = layout.RgbLayout.Model;
+ _vendor = device?.RgbDevice.DeviceInfo.Manufacturer ?? layout.RgbLayout.Vendor;
+ _model = device?.RgbDevice.DeviceInfo.Model ?? layout.RgbLayout.Model;
- DeviceProvider? deviceProvider = deviceService.Devices.FirstOrDefault(d => d.Layout == layout)?.DeviceProvider;
- if (deviceProvider != null)
- _deviceProviderId = deviceProvider.Plugin.Guid;
+ if (device != null)
+ _deviceProviderId = device.DeviceProvider.Plugin.Guid;
_deviceProviders = this.WhenAnyValue(vm => vm.DeviceProviderId)
.Select(id => pluginManagementService.GetAllPlugins().FirstOrDefault(p => p.Guid == id)?.Features.Select(f => f.Name))
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultView.axaml
new file mode 100644
index 000000000..4aa66067a
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultView.axaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ Detected devices
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultView.axaml.cs
new file mode 100644
index 000000000..bc7603a0d
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.Workshop.Layout;
+
+public partial class LayoutListDefaultView : ReactiveUserControl
+{
+ public LayoutListDefaultView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultViewModel.cs
new file mode 100644
index 000000000..b9521529c
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListDefaultViewModel.cs
@@ -0,0 +1,20 @@
+using Artemis.UI.Screens.Workshop.Entries.List;
+using Artemis.UI.Screens.Workshop.LayoutFinder;
+using Artemis.UI.Shared.Routing;
+using Artemis.WebClient.Workshop;
+
+namespace Artemis.UI.Screens.Workshop.Layout;
+
+public class LayoutListDefaultViewModel : RoutableScreen
+{
+ public LayoutListDefaultViewModel(LayoutFinderViewModel layoutFinderViewModel, EntryListViewModel entryListViewModel)
+ {
+ LayoutFinderViewModel = layoutFinderViewModel;
+ EntryListViewModel = entryListViewModel;
+ EntryListViewModel.EntryType = EntryType.Layout;
+ EntryListViewModel.ShowCategoryFilter = false;
+ }
+
+ public LayoutFinderViewModel LayoutFinderViewModel { get; }
+ public EntryListViewModel EntryListViewModel { get; }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs
index a6165a585..014bde191 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs
@@ -7,11 +7,10 @@ namespace Artemis.UI.Screens.Workshop.Layout;
public class LayoutListViewModel : RoutableHostScreen
{
private readonly EntryListViewModel _entryListViewModel;
- public override RoutableScreen DefaultScreen => _entryListViewModel;
+ public override RoutableScreen DefaultScreen { get; }
- public LayoutListViewModel(EntryListViewModel entryListViewModel)
+ public LayoutListViewModel(LayoutListDefaultViewModel defaultViewModel)
{
- _entryListViewModel = entryListViewModel;
- _entryListViewModel.EntryType = EntryType.Layout;
+ DefaultScreen = defaultViewModel;
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceView.axaml b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceView.axaml
new file mode 100644
index 000000000..c24c410ec
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceView.axaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceView.axaml.cs b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceView.axaml.cs
new file mode 100644
index 000000000..b974af5a8
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Workshop.LayoutFinder;
+
+public partial class LayoutFinderDeviceView : UserControl
+{
+ public LayoutFinderDeviceView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceViewModel.cs b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceViewModel.cs
new file mode 100644
index 000000000..c15e72493
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderDeviceViewModel.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.Core.Services;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Utilities;
+using Artemis.WebClient.Workshop;
+using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
+using Artemis.WebClient.Workshop.Models;
+using Artemis.WebClient.Workshop.Providers;
+using Artemis.WebClient.Workshop.Services;
+using Material.Icons;
+using Material.Icons.Avalonia;
+using PropertyChanged.SourceGenerator;
+using StrawberryShake;
+
+namespace Artemis.UI.Screens.Workshop.LayoutFinder;
+
+public partial class LayoutFinderDeviceViewModel : ViewModelBase
+{
+ private readonly IWorkshopClient _client;
+ private readonly IDeviceService _deviceService;
+ private readonly IWorkshopService _workshopService;
+ private readonly WorkshopLayoutProvider _layoutProvider;
+ private readonly EntryInstallationHandlerFactory _factory;
+
+ [Notify] private bool _searching;
+ [Notify] private bool _hasLayout;
+
+ [Notify] private IEntrySummary? _entry;
+ [Notify] private IRelease? _release;
+ [Notify] private string? _logicalLayout;
+ [Notify] private string? _physicalLayout;
+
+ public LayoutFinderDeviceViewModel(ArtemisDevice device,
+ IWorkshopClient client,
+ IDeviceService deviceService,
+ IWorkshopService workshopService,
+ WorkshopLayoutProvider layoutProvider,
+ EntryInstallationHandlerFactory factory)
+ {
+ _client = client;
+ _deviceService = deviceService;
+ _workshopService = workshopService;
+ _layoutProvider = layoutProvider;
+ _factory = factory;
+
+ Device = device;
+ DeviceIcon = DetermineDeviceIcon();
+ HasLayout = Device.Layout != null && !Device.Layout.IsDefaultLayout;
+ }
+
+ public ArtemisDevice Device { get; }
+ public MaterialIconKind DeviceIcon { get; }
+
+ public async Task Search()
+ {
+ if (HasLayout)
+ return;
+
+ try
+ {
+ Searching = true;
+ Task delayTask = Task.Delay(400);
+
+ if (Device.DeviceType == RGB.NET.Core.RGBDeviceType.Keyboard)
+ await SearchKeyboardLayout();
+ else
+ await SearchLayout();
+
+ if (Entry != null && Release != null)
+ await InstallAndApplyEntry(Entry, Release);
+
+ await delayTask;
+ }
+ finally
+ {
+ Searching = false;
+ HasLayout = Device.Layout != null && !Device.Layout.IsDefaultLayout;
+ }
+ }
+
+ private async Task SearchKeyboardLayout()
+ {
+ IOperationResult result = await _client.SearchKeyboardLayout.ExecuteAsync(
+ Device.DeviceProvider.Plugin.Guid,
+ Device.RgbDevice.DeviceInfo.Model,
+ Device.RgbDevice.DeviceInfo.Manufacturer,
+ Device.LogicalLayout,
+ Enum.Parse(Device.PhysicalLayout.ToString(), true));
+
+ Entry = result.Data?.SearchKeyboardLayout?.Entry;
+ Release = result.Data?.SearchKeyboardLayout?.Entry.LatestRelease;
+ LogicalLayout = result.Data?.SearchKeyboardLayout?.LogicalLayout;
+ PhysicalLayout = result.Data?.SearchKeyboardLayout?.PhysicalLayout.ToString();
+ }
+
+ private async Task SearchLayout()
+ {
+ IOperationResult result = await _client.SearchLayout.ExecuteAsync(
+ Enum.Parse(Device.DeviceType.ToString(), true),
+ Device.DeviceProvider.Plugin.Guid,
+ Device.RgbDevice.DeviceInfo.Model,
+ Device.RgbDevice.DeviceInfo.Manufacturer);
+
+ Entry = result.Data?.SearchLayout?.Entry;
+ Release = result.Data?.SearchLayout?.Entry.LatestRelease;
+ LogicalLayout = null;
+ PhysicalLayout = null;
+ }
+
+ private async Task InstallAndApplyEntry(IEntrySummary entry, IRelease release)
+ {
+ // Try a local install first
+ InstalledEntry? installedEntry = _workshopService.GetInstalledEntry(entry.Id);
+ if (installedEntry == null)
+ {
+ IEntryInstallationHandler installationHandler = _factory.CreateHandler(EntryType.Layout);
+ EntryInstallResult result = await installationHandler.InstallAsync(entry, release, new Progress(), CancellationToken.None);
+ installedEntry = result.Entry;
+ }
+
+ if (installedEntry != null)
+ {
+ _layoutProvider.ConfigureDevice(Device, installedEntry);
+ _deviceService.SaveDevice(Device);
+ _deviceService.LoadDeviceLayout(Device);
+ }
+ }
+
+ private MaterialIconKind DetermineDeviceIcon()
+ {
+ return Device.DeviceType switch
+ {
+ RGB.NET.Core.RGBDeviceType.None => MaterialIconKind.QuestionMarkCircle,
+ RGB.NET.Core.RGBDeviceType.Keyboard => MaterialIconKind.Keyboard,
+ RGB.NET.Core.RGBDeviceType.Mouse => MaterialIconKind.Mouse,
+ RGB.NET.Core.RGBDeviceType.Headset => MaterialIconKind.Headset,
+ RGB.NET.Core.RGBDeviceType.Mousepad => MaterialIconKind.TextureBox,
+ RGB.NET.Core.RGBDeviceType.LedStripe => MaterialIconKind.LightStrip,
+ RGB.NET.Core.RGBDeviceType.LedMatrix => MaterialIconKind.DrawingBox,
+ RGB.NET.Core.RGBDeviceType.Mainboard => MaterialIconKind.Chip,
+ RGB.NET.Core.RGBDeviceType.GraphicsCard => MaterialIconKind.GraphicsProcessingUnit,
+ RGB.NET.Core.RGBDeviceType.DRAM => MaterialIconKind.Memory,
+ RGB.NET.Core.RGBDeviceType.HeadsetStand => MaterialIconKind.HeadsetDock,
+ RGB.NET.Core.RGBDeviceType.Keypad => MaterialIconKind.Keypad,
+ RGB.NET.Core.RGBDeviceType.Fan => MaterialIconKind.Fan,
+ RGB.NET.Core.RGBDeviceType.Speaker => MaterialIconKind.Speaker,
+ RGB.NET.Core.RGBDeviceType.Cooler => MaterialIconKind.FreezingPoint,
+ RGB.NET.Core.RGBDeviceType.Monitor => MaterialIconKind.DesktopWindows,
+ RGB.NET.Core.RGBDeviceType.LedController => MaterialIconKind.LedStripVariant,
+ RGB.NET.Core.RGBDeviceType.GameController => MaterialIconKind.MicrosoftXboxController,
+ RGB.NET.Core.RGBDeviceType.Unknown => MaterialIconKind.QuestionMarkCircle,
+ RGB.NET.Core.RGBDeviceType.All => MaterialIconKind.QuestionMarkCircle,
+ _ => MaterialIconKind.QuestionMarkCircle
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderView.axaml b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderView.axaml
new file mode 100644
index 000000000..499e28bb1
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderView.axaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderView.axaml.cs b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderView.axaml.cs
new file mode 100644
index 000000000..fa16aa052
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.Workshop.LayoutFinder;
+
+public partial class LayoutFinderView : ReactiveUserControl
+{
+ public LayoutFinderView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderViewModel.cs b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderViewModel.cs
new file mode 100644
index 000000000..24be58538
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/LayoutFinder/LayoutFinderViewModel.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.Core.Services;
+using Artemis.UI.Shared;
+using DynamicData;
+using PropertyChanged.SourceGenerator;
+using ReactiveUI;
+using RGB.NET.Core;
+using Serilog;
+
+namespace Artemis.UI.Screens.Workshop.LayoutFinder;
+
+public partial class LayoutFinderViewModel : ActivatableViewModelBase
+{
+ private readonly ILogger _logger;
+ private readonly SourceList _devices;
+ [Notify] private ReadOnlyObservableCollection _deviceViewModels;
+
+ public LayoutFinderViewModel(ILogger logger, IDeviceService deviceService, Func getDeviceViewModel)
+ {
+ _logger = logger;
+ SearchAll = ReactiveCommand.CreateFromTask(ExecuteSearchAll);
+ this.WhenActivated((CompositeDisposable _) =>
+ {
+ IEnumerable deviceGroups = deviceService.EnabledDevices.Select(getDeviceViewModel);
+ DeviceViewModels = new ReadOnlyObservableCollection(new ObservableCollection(deviceGroups));
+ });
+ }
+
+ public ReactiveCommand SearchAll { get; }
+
+
+ private async Task ExecuteSearchAll()
+ {
+ foreach (LayoutFinderDeviceViewModel deviceViewModel in DeviceViewModels)
+ {
+ try
+ {
+ await deviceViewModel.Search();
+ }
+ catch (Exception e)
+ {
+ _logger.Error(e, "Failed to search for layout on device {Device}", deviceViewModel.Device);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepView.axaml b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepView.axaml
index 5b621ec63..5627e318d 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepView.axaml
@@ -24,6 +24,11 @@
The information below is used by Artemis to automatically find your layout.
Some layouts can be shared across different devices and here you have a chance to set that up.
+
+ Ensure you enter the model and vendor
+ exactly
+ as detected on the device by Artemis.
+
+
+