diff --git a/src/Artemis.Core/Services/DeviceService.cs b/src/Artemis.Core/Services/DeviceService.cs index 54dfd34a2..aaa27c59b 100644 --- a/src/Artemis.Core/Services/DeviceService.cs +++ b/src/Artemis.Core/Services/DeviceService.cs @@ -157,10 +157,11 @@ internal class DeviceService : IDeviceService } } + /// /// - public void AutoArrangeDevices() + public void AutoArrangeDevices(bool leftHanded) { - SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement(); + SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement(leftHanded); surfaceArrangement.Arrange(_devices); foreach (ArtemisDevice artemisDevice in _devices) artemisDevice.ApplyDefaultCategories(); diff --git a/src/Artemis.Core/Services/Interfaces/IDeviceService.cs b/src/Artemis.Core/Services/Interfaces/IDeviceService.cs index 6594535fd..eb611d613 100644 --- a/src/Artemis.Core/Services/Interfaces/IDeviceService.cs +++ b/src/Artemis.Core/Services/Interfaces/IDeviceService.cs @@ -46,7 +46,8 @@ public interface IDeviceService : IArtemisService /// /// Applies auto-arranging logic to the surface /// - void AutoArrangeDevices(); + /// + void AutoArrangeDevices(bool leftHanded); /// /// Apples the best available to the provided diff --git a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs index 9f4e78fe1..24cf3ba1d 100644 --- a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs +++ b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs @@ -48,22 +48,42 @@ internal class SurfaceArrangement } } - internal static SurfaceArrangement GetDefaultArrangement() + internal static SurfaceArrangement GetDefaultArrangement(bool leftHanded) { SurfaceArrangement arrangement = new(); - SurfaceArrangementType keypad = arrangement.AddType(RGBDeviceType.Keypad, 1); - keypad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20)); + SurfaceArrangementType keyboard, keypad, mousepad, mouse; + if (leftHanded) + { + mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1); + mousepad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10)); - SurfaceArrangementType keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1); - keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20)); + mouse = arrangement.AddType(RGBDeviceType.Mouse, 2); + mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0)); + mouse.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 10)); - SurfaceArrangementType mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1); - mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10)); + keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1); + keyboard.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10)); + keyboard.AddConfiguration(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100)); - SurfaceArrangementType mouse = arrangement.AddType(RGBDeviceType.Mouse, 2); - mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0)); - mouse.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100)); + keypad = arrangement.AddType(RGBDeviceType.Keypad, 1); + keypad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20)); + } + else + { + keypad = arrangement.AddType(RGBDeviceType.Keypad, 1); + keypad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20)); + + keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1); + keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20)); + + mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1); + mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10)); + + mouse = arrangement.AddType(RGBDeviceType.Mouse, 2); + mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0)); + mouse.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100)); + } SurfaceArrangementType headset = arrangement.AddType(RGBDeviceType.Headset, 1); headset.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Bottom, 100)); diff --git a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs index 7f59731f9..284c797a5 100644 --- a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs +++ b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs @@ -32,7 +32,7 @@ internal class SurfaceArrangementConfiguration public int MarginBottom { get; } public SurfaceArrangement SurfaceArrangement { get; set; } - public bool Apply(List devices) + public bool Apply(List devicesToArrange, List devices) { if (Anchor != null && !Anchor.HasDevices(devices)) return false; @@ -42,10 +42,10 @@ internal class SurfaceArrangementConfiguration new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition); // Stack multiple devices of the same type vertically if they are wider than they are tall - bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height); + bool stackVertically = devicesToArrange.Average(d => d.RgbDevice.Size.Width) >= devicesToArrange.Average(d => d.RgbDevice.Size.Height); ArtemisDevice? previous = null; - foreach (ArtemisDevice artemisDevice in devices) + foreach (ArtemisDevice artemisDevice in devicesToArrange) { if (previous != null) { diff --git a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs index 48d0d015f..d855b720b 100644 --- a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs +++ b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs @@ -28,18 +28,18 @@ internal class SurfaceArrangementType public void Arrange(List devices) { - devices = devices.Where(d => d.DeviceType == DeviceType).ToList(); - if (!devices.Any()) + List devicesToArrange = devices.Where(d => d.DeviceType == DeviceType).ToList(); + if (!devicesToArrange.Any()) return; AppliedConfiguration = null; foreach (SurfaceArrangementConfiguration configuration in Configurations) { - bool applied = configuration.Apply(devices); + bool applied = configuration.Apply(devicesToArrange, devices); if (applied) { AppliedConfiguration = configuration; - foreach (ArtemisDevice artemisDevice in devices) + foreach (ArtemisDevice artemisDevice in devicesToArrange) artemisDevice.ZIndex = ZIndex; return; } @@ -52,7 +52,7 @@ internal class SurfaceArrangementType VerticalArrangementPosition.Equal, 10 ) {SurfaceArrangement = SurfaceArrangement}; - fallback.Apply(devices); + fallback.Apply(devicesToArrange, devices); AppliedConfiguration = fallback; } diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs index a9f17c2ee..ae9518f83 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs @@ -185,12 +185,15 @@ internal class ProfileEditorService : IProfileEditorService { // Activate the profile if one was provided if (profileConfiguration != null) + { + _profileService.FocusProfile = profileConfiguration; _profileService.ActivateProfile(profileConfiguration); + } + // If there is no profile configuration or module, deliberately set the override to null _moduleService.SetActivationOverride(profileConfiguration?.Module); }); - - _profileService.FocusProfile = profileConfiguration; + _profileConfigurationSubject.OnNext(profileConfiguration); ChangeTime(TimeSpan.Zero); diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs index 38f8c6856..16bf19759 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs @@ -79,11 +79,6 @@ public partial class ProfileTreeViewModel : TreeItemViewModel public override bool SupportsChildren => true; - public void UpdateCanPaste() - { - throw new NotImplementedException(); - } - protected override Task ExecuteDuplicate() { throw new NotSupportedException(); diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs index ee5e5c111..5596c3e9f 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs @@ -132,6 +132,6 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase public bool Matches(string s) { - return s == $"profile/{ProfileConfiguration.ProfileId}/editor"; + return s.StartsWith($"profile/{ProfileConfiguration.ProfileId}"); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs index 0aa1d901e..2d288b321 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs @@ -27,20 +27,27 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase private readonly IWorkshopService _workshopService; private readonly IWindowService _windowService; private readonly IPluginManagementService _pluginManagementService; + private readonly IProfileService _profileService; private readonly ISettingsVmFactory _settingsVmFactory; private readonly Progress _progress = new(); [Notify] private bool _isInstalled; [Notify] private bool _shouldInstall; [Notify] private float _installProgress; - - public DefaultEntryItemViewModel(ILogger logger, IEntrySummary entry, IWorkshopService workshopService, IWindowService windowService, IPluginManagementService pluginManagementService, + + public DefaultEntryItemViewModel(ILogger logger, + IEntrySummary entry, + IWorkshopService workshopService, + IWindowService windowService, + IPluginManagementService pluginManagementService, + IProfileService profileService, ISettingsVmFactory settingsVmFactory) { _logger = logger; _workshopService = workshopService; _windowService = windowService; _pluginManagementService = pluginManagementService; + _profileService = profileService; _settingsVmFactory = settingsVmFactory; Entry = entry; @@ -62,19 +69,18 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase if (!result.IsSuccess) { - await _windowService.CreateContentDialog().WithTitle("Failed to install entry") + await _windowService.CreateContentDialog() + .WithTitle("Failed to install entry") .WithContent($"Failed to install entry '{Entry.Name}' ({Entry.Id}): {result.Message}") .WithCloseButtonText("Skip and continue") .ShowAsync(); } // If the entry is a plugin, enable the plugin and all features else if (result.Entry?.EntryType == EntryType.Plugin) - { await EnablePluginAndFeatures(result.Entry); - } else if (result.Entry?.EntryType == EntryType.Profile) - { - - } + // If the entry is a profile, move it to the General profile category + else if (result.Entry?.EntryType == EntryType.Profile) + MoveProfileToGeneral(result.Entry); return result.IsSuccess; } @@ -107,14 +113,31 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase _logger.Warning(e, "Failed to enable plugin feature '{FeatureName}', skipping", pluginFeatureInfo.Name); } } - + // If the plugin has a mandatory settings window, open it and wait if (plugin.ConfigurationDialog != null && plugin.ConfigurationDialog.IsMandatory) { if (plugin.Resolve(plugin.ConfigurationDialog.Type) is not PluginConfigurationViewModel viewModel) throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}"); - + await _windowService.ShowDialogAsync(new PluginSettingsWindowViewModel(viewModel)); } } + + private void MoveProfileToGeneral(InstalledEntry entry) + { + if (!entry.TryGetMetadata("ProfileId", out Guid profileId)) + return; + + ProfileConfiguration? profile = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == profileId); + if (profile == null) + return; + + ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "General") ?? _profileService.CreateProfileCategory("General", true); + if (category.ProfileConfigurations.Contains(profile)) + return; + + category.AddProfileConfiguration(profile, null); + _profileService.SaveProfileCategory(category); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepView.axaml b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepView.axaml index a66959529..c7aabb5c2 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepView.axaml +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepView.axaml @@ -24,15 +24,14 @@ HorizontalAlignment="Right" Margin="0 0 10 0" Width="280" - Height="280" - IsEnabled="False"> + Height="280"> - Left-handed preset (NYI) + Left-handed preset - A preset with the mouse on the left side of the keyboard + A preset with the mouse on the left side of the keyboard (are you the 10%?) diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs index 04989be43..ccdcdac00 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs @@ -27,9 +27,7 @@ public class SurfaceStepViewModel : WizardStepViewModel private void ExecuteSelectLayout(string layout) { - // TODO: Implement the layout - _deviceService.AutoArrangeDevices(); - + _deviceService.AutoArrangeDevices(layout == "left"); Wizard.ChangeScreen(); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs index 248ca19de..f596e0f23 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs @@ -13,6 +13,7 @@ using Artemis.UI.Shared; using Artemis.UI.Shared.Routing; using Artemis.UI.Shared.Services; using Avalonia; +using FluentAvalonia.UI.Controls; using PropertyChanged.SourceGenerator; using ReactiveUI; using SkiaSharp; @@ -180,11 +181,18 @@ public partial class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewMod private async Task ExecuteAutoArrange() { - bool confirmed = await _windowService.ShowConfirmContentDialog("Auto-arrange layout", "Are you sure you want to auto-arrange your layout? Your current settings will be overwritten."); - if (!confirmed) - return; - - _deviceService.AutoArrangeDevices(); + ContentDialogResult contentDialogResult = await _windowService.CreateContentDialog() + .WithTitle("Auto-arrange layout") + .WithContent("Which preset would you like to apply? Your current settings will be overwritten.") + .HavingPrimaryButton(b => b.WithText("Left-handed preset")) + .HavingSecondaryButton(b => b.WithText("Right-handed preset")) + .WithCloseButtonText("Cancel") + .ShowAsync(); + + if (contentDialogResult == ContentDialogResult.Primary) + _deviceService.AutoArrangeDevices(true); + else if (contentDialogResult == ContentDialogResult.Secondary) + _deviceService.AutoArrangeDevices(false); } private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)