1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Profile editor - Fix race condition causing the editor to fail to activate on suspended profiles

Surface editor - Implemented left-handed preset
This commit is contained in:
Robert 2025-12-11 22:55:31 +01:00
parent 108cbaae3d
commit 06c5294e88
12 changed files with 99 additions and 51 deletions

View File

@ -157,10 +157,11 @@ internal class DeviceService : IDeviceService
}
}
/// <param name="leftHanded"></param>
/// <inheritdoc />
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();

View File

@ -46,7 +46,8 @@ public interface IDeviceService : IArtemisService
/// <summary>
/// Applies auto-arranging logic to the surface
/// </summary>
void AutoArrangeDevices();
/// <param name="leftHanded"></param>
void AutoArrangeDevices(bool leftHanded);
/// <summary>
/// Apples the best available to the provided <see cref="ArtemisDevice" />

View File

@ -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);
SurfaceArrangementType keyboard, keypad, mousepad, mouse;
if (leftHanded)
{
mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(null, 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(null, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 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));
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));
SurfaceArrangementType keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20));
SurfaceArrangementType mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
SurfaceArrangementType mouse = arrangement.AddType(RGBDeviceType.Mouse, 2);
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));

View File

@ -32,7 +32,7 @@ internal class SurfaceArrangementConfiguration
public int MarginBottom { get; }
public SurfaceArrangement SurfaceArrangement { get; set; }
public bool Apply(List<ArtemisDevice> devices)
public bool Apply(List<ArtemisDevice> devicesToArrange, List<ArtemisDevice> 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)
{

View File

@ -28,18 +28,18 @@ internal class SurfaceArrangementType
public void Arrange(List<ArtemisDevice> devices)
{
devices = devices.Where(d => d.DeviceType == DeviceType).ToList();
if (!devices.Any())
List<ArtemisDevice> 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;
}

View File

@ -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);

View File

@ -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();

View File

@ -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}");
}
}

View File

@ -27,6 +27,7 @@ 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<StreamProgress> _progress = new();
@ -34,13 +35,19 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
[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;
}
@ -117,4 +123,21 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
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);
}
}

View File

@ -24,15 +24,14 @@
HorizontalAlignment="Right"
Margin="0 0 10 0"
Width="280"
Height="280"
IsEnabled="False">
Height="280">
<StackPanel>
<avalonia:MaterialIcon Kind="HandBackLeft" Width="150" Height="150" HorizontalAlignment="Center" />
<TextBlock TextAlignment="Center" Classes="h4" Margin="0 10 0 0">
Left-handed preset (NYI)
Left-handed preset
</TextBlock>
<TextBlock TextAlignment="Center" Classes="subtitle" TextWrapping="Wrap">
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%?)
</TextBlock>
</StackPanel>
</Button>

View File

@ -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<SettingsStepViewModel>();
}
}

View File

@ -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;
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();
_deviceService.AutoArrangeDevices();
if (contentDialogResult == ContentDialogResult.Primary)
_deviceService.AutoArrangeDevices(true);
else if (contentDialogResult == ContentDialogResult.Secondary)
_deviceService.AutoArrangeDevices(false);
}
private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)