From 523483581df4196077d203fb89e3c0d0ea758426 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 18 Jan 2025 18:47:16 -0800 Subject: [PATCH 01/47] Adding DI and skeleton of EditableList --- userinterface/userinterface.csproj | 1 + .../ModelTests/SystemDevicesTests.cs | 60 +++++++++++++++ .../userspace-backend-tests.csproj | 1 + userspace-backend/BackEnd.cs | 12 +-- userspace-backend/Model/DevicesModel.cs | 77 +++++++++++++++++++ .../Model/EditableSettings/EditableList.cs | 56 ++++++++++++++ userspace-backend/Model/ProfilesModel.cs | 18 +++++ userspace-backend/userspace-backend.csproj | 1 + 8 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 userspace-backend-tests/ModelTests/SystemDevicesTests.cs create mode 100644 userspace-backend/Model/EditableSettings/EditableList.cs diff --git a/userinterface/userinterface.csproj b/userinterface/userinterface.csproj index 27802fcd..dc4b325a 100644 --- a/userinterface/userinterface.csproj +++ b/userinterface/userinterface.csproj @@ -26,6 +26,7 @@ + diff --git a/userspace-backend-tests/ModelTests/SystemDevicesTests.cs b/userspace-backend-tests/ModelTests/SystemDevicesTests.cs new file mode 100644 index 00000000..49574acf --- /dev/null +++ b/userspace-backend-tests/ModelTests/SystemDevicesTests.cs @@ -0,0 +1,60 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using userspace_backend.Model; + +namespace userspace_backend_tests.ModelTests +{ + [TestClass] + public class SystemDevicesTests + { + private class TestDevicesRetriever : ISystemDevicesRetriever + { + public List Devices { get; set; } + + public IList GetSystemDevices() => Devices; + } + + private class TestSystemDevice : ISystemDevice + { + public string Name { get; set; } + + public string HWID { get; set; } + } + + [TestMethod] + public void SystemDevicesProvider_RetrievesDevices() + { + List testSystemDevices = new List() + { + new TestSystemDevice() + { + Name = "test", + HWID = "garble", + } + }; + + TestDevicesRetriever testDevicesRetriever = new TestDevicesRetriever() + { + Devices = testSystemDevices, + }; + var services = new ServiceCollection(); + services.Add(ServiceDescriptor.Describe( + serviceType: typeof(ISystemDevicesRetriever), + implementationFactory: _ => testDevicesRetriever, + lifetime: ServiceLifetime.Singleton)); + services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + + SystemDevicesProvider testObject = serviceProvider.GetRequiredService(); + + Assert.IsNotNull(testObject); + Assert.AreEqual(testSystemDevices.Count, testObject.SystemDevices.Count); + } + } +} diff --git a/userspace-backend-tests/userspace-backend-tests.csproj b/userspace-backend-tests/userspace-backend-tests.csproj index 6019e606..779b1296 100644 --- a/userspace-backend-tests/userspace-backend-tests.csproj +++ b/userspace-backend-tests/userspace-backend-tests.csproj @@ -11,6 +11,7 @@ + diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 96e03886..2ac4b9c4 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -1,16 +1,18 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using DATA = userspace_backend.Data; -using userspace_backend.IO; using userspace_backend.Model; -using userspace_backend.Data.Profiles; namespace userspace_backend { + public interface IBackEnd + { + void Load(); + + void Apply(); + } + public class BackEnd { diff --git a/userspace-backend/Model/DevicesModel.cs b/userspace-backend/Model/DevicesModel.cs index 3558e4b0..a1bf4bc5 100644 --- a/userspace-backend/Model/DevicesModel.cs +++ b/userspace-backend/Model/DevicesModel.cs @@ -146,4 +146,81 @@ public bool Validate(string modelValue) } } + public interface ISystemDevicesProvider + { + ReadOnlyObservableCollection SystemDevices { get; } + + void RefreshSystemDevices(); + } + + public class SystemDevicesProvider : ISystemDevicesProvider + { + public SystemDevicesProvider(ISystemDevicesRetriever devicesRetriever) + { + DevicesRetriever = devicesRetriever; + SystemDevicesInternal = new ObservableCollection(); + SystemDevices = new ReadOnlyObservableCollection(SystemDevicesInternal); + RefreshSystemDevices(); + } + + public ReadOnlyObservableCollection SystemDevices { get; } + + protected ObservableCollection SystemDevicesInternal { get; } + + protected ISystemDevicesRetriever DevicesRetriever { get; } + + public void RefreshSystemDevices() + { + // TODO: Replace with "addrange" equivalent from ObservableCollection child class + SystemDevicesInternal.Clear(); + IList retrievedDevices = DevicesRetriever.GetSystemDevices(); + + foreach (ISystemDevice retrievedDevice in retrievedDevices) + { + SystemDevicesInternal.Add(retrievedDevice); + } + } + } + + public interface ISystemDevicesRetriever + { + IList GetSystemDevices(); + } + + public sealed class SystemDevicesRetriever : ISystemDevicesRetriever + { + public IList GetSystemDevices() + { + IList rawDevices = MultiHandleDevice.GetList(); + return rawDevices.Select(d => new SystemDevice(d) as ISystemDevice).ToList(); + } + } + + /// + /// Interface to represent devices as they come from windows. + /// The actual classes from windows are non-trivial to construct and test. + /// + public interface ISystemDevice + { + public string Name { get; } + + public string HWID { get; } + } + + /// + /// Data class to wrap + /// + public class SystemDevice : ISystemDevice + { + public SystemDevice(MultiHandleDevice multiHandleDevice) + { + RawDevice = multiHandleDevice; + } + + public string Name { get => RawDevice.name; } + + public string HWID { get => RawDevice.id; } + + private MultiHandleDevice RawDevice { get; } + } } diff --git a/userspace-backend/Model/EditableSettings/EditableList.cs b/userspace-backend/Model/EditableSettings/EditableList.cs new file mode 100644 index 00000000..03c78d14 --- /dev/null +++ b/userspace-backend/Model/EditableSettings/EditableList.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace userspace_backend.Model.EditableSettings +{ + public abstract class EditableList + { + public bool TryAddNewDefault() + { + for (int i = 0; i < 10; i++) + { + string defaultProfileName = GenerateDefaultName(i); + + if (!ContainsElementWithName(defaultProfileName)) + { + T newDefaultElement = GenerateDefaultElement(defaultProfileName); + AddElement(newDefaultElement); + + return true; + } + } + + return false; + } + + public bool TryAdd(T element) + { + string name = GetNameFromElement(element); + + if (!ContainsElementWithName(name)) + { + AddElement(element); + return true; + } + + return true; + } + + protected abstract string GetNameFromElement(T element); + + public abstract bool TryGetElement(string name, out T element); + + protected abstract string DefaultNameTemplate { get; } + + protected string GenerateDefaultName(int index) => $"{DefaultNameTemplate}{index}"; + + protected bool ContainsElementWithName(string name) => TryGetElement(name, out T _); + + protected abstract bool AddElement(T element); + + protected abstract T GenerateDefaultElement(string name); + } +} diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 74dde87e..bf82c0c2 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using userspace_backend.Model.EditableSettings; using DATA = userspace_backend.Data; @@ -74,6 +75,23 @@ public bool TryAddProfile(DATA.Profile profileToAdd) return true; } + protected bool TryCreateNewDefaultProfile([MaybeNullWhen(false)] out DATA.Profile newDefaultProfile) + { + for (int i = 0; i < 10; i++) + { + string newProfileName = $"Profile{i}"; + + if (!TryGetProfile(newProfileName, out _)) + { + newDefaultProfile = GenerateNewDefaultProfile(newProfileName); + return false; + } + } + + newDefaultProfile = null; + return false; + } + public bool RemoveProfile(ProfileModel profile) { return Profiles.Remove(profile); diff --git a/userspace-backend/userspace-backend.csproj b/userspace-backend/userspace-backend.csproj index bd556195..84011a76 100644 --- a/userspace-backend/userspace-backend.csproj +++ b/userspace-backend/userspace-backend.csproj @@ -9,6 +9,7 @@ + From 3dd3cfc8b4fb6ebbe90f108d33af0cb8a79c93bb Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 18 Jan 2025 20:13:12 -0800 Subject: [PATCH 02/47] Further DI work + cleaning up IEditableSettings interface --- .../ModelTests/SystemDevicesTests.cs | 20 +++- userspace-backend/BackEnd.cs | 7 +- userspace-backend/BackEndComposer.cs | 16 +++ userspace-backend/Model/DevicesModel.cs | 96 +----------------- .../Model/EditableSettings/EditableSetting.cs | 11 +-- .../EditableSettings/IEditableSetting.cs | 10 +- userspace-backend/Model/SystemDevices.cs | 99 +++++++++++++++++++ 7 files changed, 152 insertions(+), 107 deletions(-) create mode 100644 userspace-backend/BackEndComposer.cs create mode 100644 userspace-backend/Model/SystemDevices.cs diff --git a/userspace-backend-tests/ModelTests/SystemDevicesTests.cs b/userspace-backend-tests/ModelTests/SystemDevicesTests.cs index 49574acf..6120402a 100644 --- a/userspace-backend-tests/ModelTests/SystemDevicesTests.cs +++ b/userspace-backend-tests/ModelTests/SystemDevicesTests.cs @@ -28,7 +28,7 @@ private class TestSystemDevice : ISystemDevice } [TestMethod] - public void SystemDevicesProvider_RetrievesDevices() + public void SystemDevicesProvider_ProvidesDevices() { List testSystemDevices = new List() { @@ -55,6 +55,24 @@ public void SystemDevicesProvider_RetrievesDevices() Assert.IsNotNull(testObject); Assert.AreEqual(testSystemDevices.Count, testObject.SystemDevices.Count); + CollectionAssert.AreEquivalent(testSystemDevices, testObject.SystemDevices); + } + + // This test may need to be excluded if built from deviceless server. + [TestMethod] + public void SystemDevicesRetriever_RetrievesDevices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + + SystemDevicesRetriever testObject = serviceProvider.GetRequiredService(); + Assert.IsNotNull(testObject); + + // These devices will be different per user, but should not be null as long as the user has something giving mouse input. + IList retrievedDevices = testObject.GetSystemDevices(); + Assert.IsNotNull(retrievedDevices); + Assert.IsTrue(retrievedDevices.Count > 0); } } } diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 2ac4b9c4..b8a51d2d 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -3,6 +3,7 @@ using System.Linq; using DATA = userspace_backend.Data; using userspace_backend.Model; +using Microsoft.Extensions.DependencyInjection; namespace userspace_backend { @@ -18,8 +19,12 @@ public class BackEnd public BackEnd(IBackEndLoader backEndLoader) { + // TODO: fully construct BackEnd via DI + ServiceCollection services = new ServiceCollection(); + IServiceProvider serviceProvider = BackEndComposer.Compose(services); + BackEndLoader = backEndLoader; - Devices = new DevicesModel(); + Devices = new DevicesModel(serviceProvider.GetRequiredService()); Profiles = new ProfilesModel([]); } diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs new file mode 100644 index 00000000..feddb85a --- /dev/null +++ b/userspace-backend/BackEndComposer.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using userspace_backend.Model; + +namespace userspace_backend +{ + public static class BackEndComposer + { + public static IServiceProvider Compose(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + return services.BuildServiceProvider(); + } + } +} diff --git a/userspace-backend/Model/DevicesModel.cs b/userspace-backend/Model/DevicesModel.cs index a1bf4bc5..66b28dd2 100644 --- a/userspace-backend/Model/DevicesModel.cs +++ b/userspace-backend/Model/DevicesModel.cs @@ -10,14 +10,13 @@ namespace userspace_backend.Model { public class DevicesModel { - public DevicesModel() + public DevicesModel(ISystemDevicesProvider systemDevicesProvider) { Devices = new ObservableCollection(); DeviceGroups = new DeviceGroups([]); DeviceModelNameValidator = new DeviceModelNameValidator(this); DeviceModelHWIDValidator = new DeviceModelHWIDValidator(this); - SystemDevices = new ObservableCollection(); - RefreshSystemDevices(); + SystemDevices = systemDevicesProvider; } public DeviceGroups DeviceGroups { get; set; } @@ -26,7 +25,8 @@ public DevicesModel() public ObservableCollection Devices { get; set; } - public ObservableCollection SystemDevices { get; protected set; } + + public ISystemDevicesProvider SystemDevices { get; protected set; } protected DeviceModelNameValidator DeviceModelNameValidator { get; } @@ -104,16 +104,6 @@ public bool RemoveDevice(DeviceModel device) { return Devices.Remove(device); } - - protected void RefreshSystemDevices() - { - SystemDevices.Clear(); - var systemDevicesList = MultiHandleDevice.GetList(); - foreach (var systemDevice in systemDevicesList) - { - SystemDevices.Add(systemDevice); - } - } } public class DeviceModelNameValidator : IModelValueValidator @@ -145,82 +135,4 @@ public bool Validate(string modelValue) return !Devices.DoesDeviceHardwareIDAlreadyExist(modelValue); } } - - public interface ISystemDevicesProvider - { - ReadOnlyObservableCollection SystemDevices { get; } - - void RefreshSystemDevices(); - } - - public class SystemDevicesProvider : ISystemDevicesProvider - { - public SystemDevicesProvider(ISystemDevicesRetriever devicesRetriever) - { - DevicesRetriever = devicesRetriever; - SystemDevicesInternal = new ObservableCollection(); - SystemDevices = new ReadOnlyObservableCollection(SystemDevicesInternal); - RefreshSystemDevices(); - } - - public ReadOnlyObservableCollection SystemDevices { get; } - - protected ObservableCollection SystemDevicesInternal { get; } - - protected ISystemDevicesRetriever DevicesRetriever { get; } - - public void RefreshSystemDevices() - { - // TODO: Replace with "addrange" equivalent from ObservableCollection child class - SystemDevicesInternal.Clear(); - IList retrievedDevices = DevicesRetriever.GetSystemDevices(); - - foreach (ISystemDevice retrievedDevice in retrievedDevices) - { - SystemDevicesInternal.Add(retrievedDevice); - } - } - } - - public interface ISystemDevicesRetriever - { - IList GetSystemDevices(); - } - - public sealed class SystemDevicesRetriever : ISystemDevicesRetriever - { - public IList GetSystemDevices() - { - IList rawDevices = MultiHandleDevice.GetList(); - return rawDevices.Select(d => new SystemDevice(d) as ISystemDevice).ToList(); - } - } - - /// - /// Interface to represent devices as they come from windows. - /// The actual classes from windows are non-trivial to construct and test. - /// - public interface ISystemDevice - { - public string Name { get; } - - public string HWID { get; } - } - - /// - /// Data class to wrap - /// - public class SystemDevice : ISystemDevice - { - public SystemDevice(MultiHandleDevice multiHandleDevice) - { - RawDevice = multiHandleDevice; - } - - public string Name { get => RawDevice.name; } - - public string HWID { get => RawDevice.id; } - - private MultiHandleDevice RawDevice { get; } - } } diff --git a/userspace-backend/Model/EditableSettings/EditableSetting.cs b/userspace-backend/Model/EditableSettings/EditableSetting.cs index f4d5aa68..fd34fe85 100644 --- a/userspace-backend/Model/EditableSettings/EditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/EditableSetting.cs @@ -3,7 +3,7 @@ namespace userspace_backend.Model.EditableSettings { - public partial class EditableSetting : ObservableObject, IEditableSetting where T : IComparable + public partial class EditableSetting : ObservableObject, IEditableSettingSpecific where T : IComparable { /// /// This value can be bound in UI for direct editing @@ -11,12 +11,6 @@ public partial class EditableSetting : ObservableObject, IEditableSetting whe [ObservableProperty] public string interfaceValue; - /// - /// This value can be bound in UI for readonly display of validated input - /// - [ObservableProperty] - public string currentValidatedValueString; - /// /// This value can be bound in UI for logic based on validated input /// @@ -48,8 +42,6 @@ public EditableSetting( public T LastWrittenValue { get; protected set; } - public string EditedValueForDiplay { get => ModelValue?.ToString() ?? string.Empty; } - /// /// Interface can set this for cases when new value arrives all at once (such as menu selection) /// instead of cases where new value arrives in parts (typing) @@ -106,7 +98,6 @@ protected void UpdatedModeValue(T value) { ModelValue = value; CurrentValidatedValue = ModelValue; - CurrentValidatedValueString = ModelValue?.ToString(); } partial void OnInterfaceValueChanged(string value) diff --git a/userspace-backend/Model/EditableSettings/IEditableSetting.cs b/userspace-backend/Model/EditableSettings/IEditableSetting.cs index 0131fd3c..6e84729b 100644 --- a/userspace-backend/Model/EditableSettings/IEditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/IEditableSetting.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; namespace userspace_backend.Model.EditableSettings { @@ -6,12 +7,15 @@ public interface IEditableSetting : INotifyPropertyChanged { string DisplayName { get; } - string EditedValueForDiplay { get; } - string InterfaceValue { get; set; } bool HasChanged(); bool TryUpdateFromInterface(); } + + public interface IEditableSettingSpecific : IEditableSetting where T : IComparable + { + public T CurrentValidatedValue { get; } + } } diff --git a/userspace-backend/Model/SystemDevices.cs b/userspace-backend/Model/SystemDevices.cs new file mode 100644 index 00000000..28fc8fd3 --- /dev/null +++ b/userspace-backend/Model/SystemDevices.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace userspace_backend.Model +{ + /// + /// Holds system devices in observable collection and refreshes list when desired. + /// + public interface ISystemDevicesProvider + { + ReadOnlyObservableCollection SystemDevices { get; } + + void RefreshSystemDevices(); + } + + /// + /// Application implementation of + /// + public class SystemDevicesProvider : ISystemDevicesProvider + { + public SystemDevicesProvider(ISystemDevicesRetriever devicesRetriever) + { + DevicesRetriever = devicesRetriever; + SystemDevicesInternal = new ObservableCollection(); + SystemDevices = new ReadOnlyObservableCollection(SystemDevicesInternal); + RefreshSystemDevices(); + } + + public ReadOnlyObservableCollection SystemDevices { get; } + + protected ObservableCollection SystemDevicesInternal { get; } + + protected ISystemDevicesRetriever DevicesRetriever { get; } + + public void RefreshSystemDevices() + { + // TODO: Replace with "addrange" equivalent from ObservableCollection child class + SystemDevicesInternal.Clear(); + IList retrievedDevices = DevicesRetriever.GetSystemDevices(); + + foreach (ISystemDevice retrievedDevice in retrievedDevices) + { + SystemDevicesInternal.Add(retrievedDevice); + } + } + } + + /// + /// Retrieves list of devices from operating system + /// + public interface ISystemDevicesRetriever + { + IList GetSystemDevices(); + } + + /// + /// Application implementation of + /// + public sealed class SystemDevicesRetriever : ISystemDevicesRetriever + { + public IList GetSystemDevices() + { + IList rawDevices = MultiHandleDevice.GetList(); + return rawDevices.Select(d => new SystemDevice(d) as ISystemDevice).ToList(); + } + } + + /// + /// Interface to represent devices as they come from windows. + /// The actual class from windows is non-trivial to construct and test. + /// + public interface ISystemDevice + { + public string Name { get; } + + public string HWID { get; } + } + + /// + /// Data class to wrap + /// + public class SystemDevice : ISystemDevice + { + public SystemDevice(MultiHandleDevice multiHandleDevice) + { + RawDevice = multiHandleDevice; + } + + public string Name { get => RawDevice.name; } + + public string HWID { get => RawDevice.id; } + + private MultiHandleDevice RawDevice { get; } + } +} From 27ef9f77bbc80992b6368bce5ba0a83498e6a99f Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 19 Jan 2025 14:02:07 -0800 Subject: [PATCH 03/47] Condense currentvalidatedvalue and modelvalue --- .../AccelerationProfileSettingsViewModel.cs | 2 +- userinterface/ViewModels/MappingViewModel.cs | 2 +- userinterface/ViewModels/ProfileViewModel.cs | 2 +- .../Views/AccelerationFormulaSettingsView.axaml | 2 +- .../Views/AccelerationProfileSettingsView.axaml | 2 +- userinterface/Views/DeviceGroupSelectorView.axaml | 2 +- userinterface/Views/MappingView.axaml | 8 ++++---- .../Model/AccelDefinitions/AccelerationModel.cs | 2 +- .../Model/AccelDefinitions/FormulaAccelModel.cs | 2 +- userspace-backend/Model/DeviceModel.cs | 15 ++++++++------- .../Model/EditableSettings/EditableSetting.cs | 5 +---- .../EditableSettingsCollection.cs | 2 +- .../Model/EditableSettings/IEditableSetting.cs | 2 +- userspace-backend/Model/ProfileModel.cs | 6 +++--- 14 files changed, 26 insertions(+), 28 deletions(-) diff --git a/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs b/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs index 1a84203c..5e5b970d 100644 --- a/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs @@ -44,7 +44,7 @@ public AccelerationProfileSettingsViewModel(BE.AccelerationModel accelerationBE) private void OnDefinitionTypeChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(AccelerationBE.DefinitionType.CurrentValidatedValue)) + if (e.PropertyName == nameof(AccelerationBE.DefinitionType.ModelValue)) { AreAccelSettingsVisible = AccelerationBE.DefinitionType.ModelValue != BEData.AccelerationDefinitionType.None; } diff --git a/userinterface/ViewModels/MappingViewModel.cs b/userinterface/ViewModels/MappingViewModel.cs index 5bd6b636..16154d9f 100644 --- a/userinterface/ViewModels/MappingViewModel.cs +++ b/userinterface/ViewModels/MappingViewModel.cs @@ -24,7 +24,7 @@ public void HandleAddMappingSelection(SelectionChangedEventArgs e) if (e.AddedItems.Count > 0 && e.AddedItems[0] is BE.DeviceGroupModel deviceGroup) { - MappingBE.TryAddMapping(deviceGroup.CurrentValidatedValue, BE.ProfilesModel.DefaultProfile.CurrentNameForDisplay); + MappingBE.TryAddMapping(deviceGroup.ModelValue, BE.ProfilesModel.DefaultProfile.CurrentNameForDisplay); } } diff --git a/userinterface/ViewModels/ProfileViewModel.cs b/userinterface/ViewModels/ProfileViewModel.cs index b56e7007..c5141189 100644 --- a/userinterface/ViewModels/ProfileViewModel.cs +++ b/userinterface/ViewModels/ProfileViewModel.cs @@ -20,7 +20,7 @@ public ProfileViewModel(BE.ProfileModel profileBE) protected BE.ProfileModel ProfileModelBE { get; } - public string CurrentName => ProfileModelBE.Name.CurrentValidatedValue; + public string CurrentName => ProfileModelBE.Name.ModelValue; public ProfileSettingsViewModel Settings { get; set; } diff --git a/userinterface/Views/AccelerationFormulaSettingsView.axaml b/userinterface/Views/AccelerationFormulaSettingsView.axaml index 15f12057..3d7196a7 100644 --- a/userinterface/Views/AccelerationFormulaSettingsView.axaml +++ b/userinterface/Views/AccelerationFormulaSettingsView.axaml @@ -17,7 +17,7 @@ /> + SelectedIndex="{Binding FormulaAccelBE.FormulaType.ModelValue}"> diff --git a/userinterface/Views/AccelerationProfileSettingsView.axaml b/userinterface/Views/AccelerationProfileSettingsView.axaml index faee44a9..13858f5e 100644 --- a/userinterface/Views/AccelerationProfileSettingsView.axaml +++ b/userinterface/Views/AccelerationProfileSettingsView.axaml @@ -20,7 +20,7 @@ /> + SelectedIndex="{Binding AccelerationBE.DefinitionType.ModelValue}"> diff --git a/userinterface/Views/DeviceGroupSelectorView.axaml b/userinterface/Views/DeviceGroupSelectorView.axaml index 8d44e243..1d3e585d 100644 --- a/userinterface/Views/DeviceGroupSelectorView.axaml +++ b/userinterface/Views/DeviceGroupSelectorView.axaml @@ -26,7 +26,7 @@ + Text="{Binding ModelValue}"/> diff --git a/userinterface/Views/MappingView.axaml b/userinterface/Views/MappingView.axaml index 1bf1da29..59222523 100644 --- a/userinterface/Views/MappingView.axaml +++ b/userinterface/Views/MappingView.axaml @@ -12,7 +12,7 @@ + Text="{Binding MappingBE.Name.ModelValue}"/> [ObservableProperty] - public T currentValidatedValue; + public T modelValue; public EditableSetting( string displayName, @@ -38,8 +38,6 @@ public EditableSetting( /// public string DisplayName { get; } - public virtual T ModelValue { get; protected set; } - public T LastWrittenValue { get; protected set; } /// @@ -97,7 +95,6 @@ protected void UpdateModelValueFromLastKnown() protected void UpdatedModeValue(T value) { ModelValue = value; - CurrentValidatedValue = ModelValue; } partial void OnInterfaceValueChanged(string value) diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs index 226ee7b2..9e945246 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs @@ -63,7 +63,7 @@ public void GatherEditableSettingsCollections() protected void EditableSettingChangedEventHandler(object? sender, PropertyChangedEventArgs e) { - if (string.Equals(e.PropertyName, nameof(EditableSetting.CurrentValidatedValue))) + if (string.Equals(e.PropertyName, nameof(IEditableSettingSpecific.ModelValue))) { OnAnySettingChanged(); } diff --git a/userspace-backend/Model/EditableSettings/IEditableSetting.cs b/userspace-backend/Model/EditableSettings/IEditableSetting.cs index 6e84729b..47373ce7 100644 --- a/userspace-backend/Model/EditableSettings/IEditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/IEditableSetting.cs @@ -16,6 +16,6 @@ public interface IEditableSetting : INotifyPropertyChanged public interface IEditableSettingSpecific : IEditableSetting where T : IComparable { - public T CurrentValidatedValue { get; } + public T ModelValue { get; } } } diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index 64ed4547..a4981e13 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -21,7 +21,7 @@ public ProfileModel(DATA.Profile dataObject, IModelValueValidator nameVa RecalculateDriverDataAndCurvePreview(); } - public string CurrentNameForDisplay => Name.CurrentValidatedValue; + public string CurrentNameForDisplay => Name.ModelValue; public EditableSetting Name { get; set; } @@ -53,7 +53,7 @@ public override DATA.Profile MapToData() protected void AnyNonPreviewPropertyChangedEventHandler(object? send, PropertyChangedEventArgs e) { - if (string.Equals(e.PropertyName, nameof(EditableSetting.CurrentValidatedValue))) + if (string.Equals(e.PropertyName, nameof(IEditableSettingSpecific.ModelValue))) { RecalculateDriverData(); } @@ -61,7 +61,7 @@ protected void AnyNonPreviewPropertyChangedEventHandler(object? send, PropertyCh protected void AnyCurvePreviewPropertyChangedEventHandler(object? send, PropertyChangedEventArgs e) { - if (string.Equals(e.PropertyName, nameof(EditableSetting.CurrentValidatedValue))) + if (string.Equals(e.PropertyName, nameof(IEditableSettingSpecific.ModelValue))) { RecalculateDriverDataAndCurvePreview(); } From c719e8fb17df8df3415ed05f0bc093ad3d11d0ea Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 19 Jan 2025 14:16:55 -0800 Subject: [PATCH 04/47] Change EditableSettings references to IEditableSettingsSpecific --- .../AccelerationProfileSettingsViewModel.cs | 3 +- .../AccelDefinitions/AccelerationModel.cs | 2 +- .../ClassicAccelerationDefinitionModel.cs | 8 +++--- .../JumpAccelerationDefinitionModel.cs | 6 ++-- .../LinearAccelerationDefinitionModel.cs | 6 ++-- .../NaturalAccelerationDefinitionModel.cs | 6 ++-- .../PowerAccelerationDefinitionModel.cs | 8 +++--- .../SynchronousAccelerationDefinitionModel.cs | 8 +++--- .../AccelDefinitions/FormulaAccelModel.cs | 2 +- .../LookupTableDefinitionModel.cs | 4 +-- userspace-backend/Model/MappingConstructor.cs | 28 ------------------- userspace-backend/Model/MappingModel.cs | 2 +- .../ProfileComponents/AnisotropyModel.cs | 12 ++++---- .../ProfileComponents/CoalescionModel.cs | 4 +-- .../Model/ProfileComponents/HiddenModel.cs | 12 ++++---- userspace-backend/Model/ProfileModel.cs | 6 ++-- 16 files changed, 45 insertions(+), 72 deletions(-) delete mode 100644 userspace-backend/Model/MappingConstructor.cs diff --git a/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs b/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs index 5e5b970d..33dbd180 100644 --- a/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs @@ -26,7 +26,8 @@ public AccelerationProfileSettingsViewModel(BE.AccelerationModel accelerationBE) AccelerationLUTSettings = new AccelerationLUTSettingsViewModel(accelerationBE.LookupTableAccel); AnisotropySettings = new AnisotropyProfileSettingsViewModel(accelerationBE.Anisotropy); CoalescionSettings = new CoalescionProfileSettingsViewModel(accelerationBE.Coalescion); - AccelerationBE.DefinitionType.AutoUpdateFromInterface = true; + // TODO: editable settings composition + //AccelerationBE.DefinitionType.AutoUpdateFromInterface = true; AccelerationBE.DefinitionType.PropertyChanged += OnDefinitionTypeChanged; } diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index 9f065fdf..ea897ab0 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -15,7 +15,7 @@ public AccelerationModel(Acceleration dataObject) : base(dataObject) DefinitionType.PropertyChanged += DefinitionTypeChangedEventHandler; } - public EditableSetting DefinitionType { get; set; } + public IEditableSettingSpecific DefinitionType { get; set; } protected Dictionary DefinitionModels { get; set; } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index 5f68f711..5ef7648d 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -15,13 +15,13 @@ public ClassicAccelerationDefinitionModel(Acceleration dataObject) : base(dataOb { } - public EditableSetting Acceleration { get; set; } + public IEditableSettingSpecific Acceleration { get; set; } - public EditableSetting Exponent { get; set; } + public IEditableSettingSpecific Exponent { get; set; } - public EditableSetting Offset { get; set; } + public IEditableSettingSpecific Offset { get; set; } - public EditableSetting Cap { get; set; } + public IEditableSettingSpecific Cap { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs index a80e67b0..04520ba8 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs @@ -15,11 +15,11 @@ public JumpAccelerationDefinitionModel(Acceleration dataObject) : base(dataObjec { } - public EditableSetting Smooth { get; set; } + public IEditableSettingSpecific Smooth { get; set; } - public EditableSetting Input { get; set; } + public IEditableSettingSpecific Input { get; set; } - public EditableSetting Output { get; set; } + public IEditableSettingSpecific Output { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs index bc778e61..09b75f48 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs @@ -15,11 +15,11 @@ public LinearAccelerationDefinitionModel(Acceleration dataObject) : base(dataObj { } - public EditableSetting Acceleration { get; set; } + public IEditableSettingSpecific Acceleration { get; set; } - public EditableSetting Offset { get; set; } + public IEditableSettingSpecific Offset { get; set; } - public EditableSetting Cap { get; set; } + public IEditableSettingSpecific Cap { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs index f12dfc0c..2fa78ec3 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs @@ -14,11 +14,11 @@ public class NaturalAccelerationDefinitionModel : AccelDefinitionModel DecayRate { get; set; } + public IEditableSettingSpecific DecayRate { get; set; } - public EditableSetting InputOffset { get; set; } + public IEditableSettingSpecific InputOffset { get; set; } - public EditableSetting Limit { get; set; } + public IEditableSettingSpecific Limit { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs index 87634939..a6861edc 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs @@ -15,13 +15,13 @@ public PowerAccelerationDefinitionModel(Acceleration dataObject) : base(dataObje { } - public EditableSetting Scale { get; set; } + public IEditableSettingSpecific Scale { get; set; } - public EditableSetting Exponent { get; set; } + public IEditableSettingSpecific Exponent { get; set; } - public EditableSetting OutputOffset { get; set; } + public IEditableSettingSpecific OutputOffset { get; set; } - public EditableSetting Cap { get; set; } + public IEditableSettingSpecific Cap { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs index 7078b3e8..90cc9488 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs @@ -12,13 +12,13 @@ public SynchronousAccelerationDefinitionModel(Acceleration dataObject) : base(da { } - public EditableSetting Gamma { get; set; } + public IEditableSettingSpecific Gamma { get; set; } - public EditableSetting Motivity { get; set; } + public IEditableSettingSpecific Motivity { get; set; } - public EditableSetting SyncSpeed { get; set; } + public IEditableSettingSpecific SyncSpeed { get; set; } - public EditableSetting Smoothness { get; set; } + public IEditableSettingSpecific Smoothness { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs index 33494e69..0126b4c5 100644 --- a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs +++ b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs @@ -20,7 +20,7 @@ public FormulaAccelModel(Acceleration dataObject) : base(dataObject) } - public EditableSetting FormulaType { get; set; } + public IEditableSettingSpecific FormulaType { get; set; } public int FormulaTypeIndex { get => (int)FormulaType.ModelValue; } diff --git a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs index 1a4057fa..16df77f6 100644 --- a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs @@ -16,9 +16,9 @@ public LookupTableDefinitionModel(Acceleration dataObject) : base(dataObject) { } - public EditableSetting ApplyAs { get; set; } + public IEditableSettingSpecific ApplyAs { get; set; } - public EditableSetting Data { get; set; } + public IEditableSettingSpecific Data { get; set; } public override AccelArgs MapToDriver() { diff --git a/userspace-backend/Model/MappingConstructor.cs b/userspace-backend/Model/MappingConstructor.cs deleted file mode 100644 index c91a102a..00000000 --- a/userspace-backend/Model/MappingConstructor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using DATA = userspace_backend.Data; -using userspace_backend.Model.AccelDefinitions; -using userspace_backend.Model.EditableSettings; -using userspace_backend.Model.ProfileComponents; -using userspace_backend.Data; -using CommunityToolkit.Mvvm.Collections; -using System.Collections.ObjectModel; - -namespace userspace_backend.Model -{ - public class MappingConstructor - { - public MappingConstructor(ProfilesModel profiles, DeviceGroups deviceGroups) - { - Profiles = profiles; - DeviceGroups = deviceGroups; - } - - public ProfilesModel Profiles { get; } - - public DeviceGroups DeviceGroups { get; } - } -} diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index 4f9e16fd..9c713274 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -35,7 +35,7 @@ public MappingModel( public bool SetActive { get; set; } - public EditableSetting Name { get; set; } + public IEditableSettingSpecific Name { get; set; } public ObservableCollection IndividualMappings { get; protected set; } diff --git a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs index f7f219c6..6ce06512 100644 --- a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs +++ b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs @@ -14,17 +14,17 @@ public AnisotropyModel(Anisotropy dataObject) : base(dataObject) { } - public EditableSetting DomainX { get; set; } + public IEditableSettingSpecific DomainX { get; set; } - public EditableSetting DomainY { get; set; } + public IEditableSettingSpecific DomainY { get; set; } - public EditableSetting RangeX { get; set; } + public IEditableSettingSpecific RangeX { get; set; } - public EditableSetting RangeY { get; set; } + public IEditableSettingSpecific RangeY { get; set; } - public EditableSetting LPNorm { get; set; } + public IEditableSettingSpecific LPNorm { get; set; } - public EditableSetting CombineXYComponents { get; set; } + public IEditableSettingSpecific CombineXYComponents { get; set; } public override Anisotropy MapToData() { diff --git a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs index 45fd136f..20df72d6 100644 --- a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs +++ b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs @@ -14,9 +14,9 @@ public CoalescionModel(Coalescion dataObject) : base(dataObject) { } - public EditableSetting InputSmoothingHalfLife { get; set; } + public IEditableSettingSpecific InputSmoothingHalfLife { get; set; } - public EditableSetting ScaleSmoothingHalfLife { get; set; } + public IEditableSettingSpecific ScaleSmoothingHalfLife { get; set; } public override Coalescion MapToData() { diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index 264156ed..ac30a099 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -14,17 +14,17 @@ public HiddenModel(Hidden dataObject) : base(dataObject) { } - public EditableSetting RotationDegrees { get; set; } + public IEditableSettingSpecific RotationDegrees { get; set; } - public EditableSetting AngleSnappingDegrees { get; set; } + public IEditableSettingSpecific AngleSnappingDegrees { get; set; } - public EditableSetting LeftRightRatio { get; set; } + public IEditableSettingSpecific LeftRightRatio { get; set; } - public EditableSetting UpDownRatio { get; set; } + public IEditableSettingSpecific UpDownRatio { get; set; } - public EditableSetting SpeedCap { get; set; } + public IEditableSettingSpecific SpeedCap { get; set; } - public EditableSetting OutputSmoothingHalfLife { get; set; } + public IEditableSettingSpecific OutputSmoothingHalfLife { get; set; } public override Hidden MapToData() { diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index a4981e13..3c96f5a7 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -23,11 +23,11 @@ public ProfileModel(DATA.Profile dataObject, IModelValueValidator nameVa public string CurrentNameForDisplay => Name.ModelValue; - public EditableSetting Name { get; set; } + public IEditableSettingSpecific Name { get; set; } - public EditableSetting OutputDPI { get; set; } + public IEditableSettingSpecific OutputDPI { get; set; } - public EditableSetting YXRatio { get; set; } + public IEditableSettingSpecific YXRatio { get; set; } public AccelerationModel Acceleration { get; set; } From 183d79f8206464dd913b8b529f16487574524b10 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 25 Jan 2025 22:31:17 -0800 Subject: [PATCH 05/47] new format solidified with test --- .../ModelTests/EditableSettingsTests.cs | 67 +++++++++++ .../Model/EditableSettings/EditableSetting.cs | 104 ++++++++++++++++++ .../EditableSettingsCollection.cs | 96 ++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 userspace-backend-tests/ModelTests/EditableSettingsTests.cs diff --git a/userspace-backend-tests/ModelTests/EditableSettingsTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsTests.cs new file mode 100644 index 00000000..8fc65e1e --- /dev/null +++ b/userspace-backend-tests/ModelTests/EditableSettingsTests.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using userspace_backend.Model.EditableSettings; + +namespace userspace_backend_tests.ModelTests +{ + [TestClass] + public class EditableSettingsTests + { + protected class TestDataType + { + public int Property { get; set; } + } + + protected interface IEditableSettingsTestCollection : IEditableSettingsCollectionV2 + { + public IEditableSettingSpecific PropertySetting { get; } + } + + protected class EditableSettingsTestCollection : EditableSettingsCollectionV2, IEditableSettingsTestCollection + { + public const string ProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(PropertySetting)}"; + + public EditableSettingsTestCollection( + [FromKeyedServices(ProperySettingName)]IEditableSettingSpecific propertySetting) + : base([propertySetting], []) + { + PropertySetting = propertySetting; + } + + public IEditableSettingSpecific PropertySetting { get; protected set; } + + public override TestDataType MapToData() + { + return new TestDataType() + { + Property = PropertySetting.ModelValue, + }; + } + } + + [TestMethod] + public void EditableSettings_Construction() + { + string testSettingName = "Test Setting"; + int testSettingInitialValue = 0; + + var services = new ServiceCollection(); + services.AddTransient(); + services.AddKeyedTransient>( + EditableSettingsTestCollection.ProperySettingName, (_, _) => + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false)); + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IEditableSettingsTestCollection testObject = serviceProvider.GetRequiredService(); + Assert.IsNotNull(testObject); + Assert.AreEqual(testSettingInitialValue, testObject.PropertySetting.ModelValue); + Assert.AreEqual(0, testObject.PropertySetting.ModelValue); + } + } +} diff --git a/userspace-backend/Model/EditableSettings/EditableSetting.cs b/userspace-backend/Model/EditableSettings/EditableSetting.cs index 43a20790..e23fad3f 100644 --- a/userspace-backend/Model/EditableSettings/EditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/EditableSetting.cs @@ -105,4 +105,108 @@ partial void OnInterfaceValueChanged(string value) } } } + + public partial class EditableSettingV2 : ObservableObject, IEditableSettingSpecific where T : IComparable + { + /// + /// This value can be bound in UI for direct editing + /// + [ObservableProperty] + public string interfaceValue; + + /// + /// This value can be bound in UI for logic based on validated input + /// + [ObservableProperty] + public T modelValue; + + public EditableSettingV2( + string displayName, + T initialValue, + IUserInputParser parser, + IModelValueValidator validator, + bool autoUpdateFromInterface = false) + { + DisplayName = displayName; + LastWrittenValue = initialValue; + Parser = parser; + Validator = validator; + UpdateModelValueFromLastKnown(); + UpdateInterfaceValue(); + AutoUpdateFromInterface = autoUpdateFromInterface; + } + + /// + /// Display name for this setting in UI + /// + public string DisplayName { get; } + + public T LastWrittenValue { get; protected set; } + + /// + /// Interface can set this for cases when new value arrives all at once (such as menu selection) + /// instead of cases where new value arrives in parts (typing) + /// + public bool AutoUpdateFromInterface { get; set; } + + private IUserInputParser Parser { get; } + + //TODO: change settings collections init so that this can be made private for non-static validators + public IModelValueValidator Validator { get; set; } + + public bool HasChanged() => ModelValue.CompareTo(LastWrittenValue) == 0; + + public bool TryUpdateFromInterface() + { + if (string.IsNullOrEmpty(InterfaceValue)) + { + UpdateInterfaceValue(); + return false; + } + + if (!Parser.TryParse(InterfaceValue.Trim(), out T parsedValue)) + { + UpdateInterfaceValue(); + return false; + } + + if (parsedValue.CompareTo(ModelValue) == 0) + { + return true; + } + + if (!Validator.Validate(parsedValue)) + { + UpdateInterfaceValue(); + return false; + } + + UpdatedModeValue(parsedValue); + return true; + } + + protected void UpdateInterfaceValue() + { + InterfaceValue = ModelValue?.ToString(); + } + + protected void UpdateModelValueFromLastKnown() + { + UpdatedModeValue(LastWrittenValue); + } + + protected void UpdatedModeValue(T value) + { + ModelValue = value; + } + + partial void OnInterfaceValueChanged(string value) + { + if (AutoUpdateFromInterface) + { + TryUpdateFromInterface(); + } + } + } + } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs index 9e945246..e66d48eb 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs @@ -87,4 +87,100 @@ protected void OnAnySettingChanged() public abstract T MapToData(); } + + /// + /// Collection of editable settings and other collections. + /// Internal node of settings tree. + /// + /// + /// The settings model for this backend is a composed object containing other composed objects and settings. + /// In this way, the objects form a tree. The root node is the base model class, the internal nodes are objects containing other objects + /// and settings, and the settings themselves are the leaf nodes. + /// This interface is then an internal node of the tree. It can contain other settings collections (internal nodes) and also contain + /// settings themselves. + /// + public interface IEditableSettingsCollectionV2 + { + public bool HasChanged { get; } + + public EventHandler AnySettingChanged { get; set; } + } + + /// + /// Base class for settings collections. + /// + /// + /// Each unique set of collection logic should be generalized into a class that is either this class or a child of this class, + /// but not the actual class in the model. + /// This class, and any child class that is a parent to actual settings collections in the model, requires unit tests. + /// The actual settings collections in the model do not need to each be tested beyond composition. + /// + /// + public abstract class EditableSettingsCollectionV2 : ObservableObject, IEditableSettingsCollection + { + public EditableSettingsCollectionV2( + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + { + AllContainedEditableSettings = editableSettings; + AllContainedEditableSettingsCollections = editableSettingsCollections; + + foreach (var setting in AllContainedEditableSettings) + { + // TODO: revisit settings composition so that this null check is unnecessary + if (setting != null) + { + setting.PropertyChanged += EditableSettingChangedEventHandler; + } + } + + // TODO: separate "All" and "currently selected" settings collections + // so that incorrect assignment is not done here for collections that alter this through use + foreach (var settingsCollection in AllContainedEditableSettingsCollections) + { + settingsCollection.AnySettingChanged += EditableSettingsCollectionChangedEventHandler; + } + } + + public EventHandler AnySettingChanged { get; set; } + + public IEnumerable AllContainedEditableSettings { get; set; } + + public IEnumerable AllContainedEditableSettingsCollections { get; set; } + + public bool HasChanged { get; protected set; } + + public void EvaluateWhetherHasChanged() + { + if (AllContainedEditableSettings.Any(s => s.HasChanged()) || + AllContainedEditableSettingsCollections.Any(c => c.HasChanged)) + { + HasChanged = true; + } + else + { + HasChanged = false; + } + } + + protected void EditableSettingChangedEventHandler(object? sender, PropertyChangedEventArgs e) + { + if (string.Equals(e.PropertyName, nameof(IEditableSettingSpecific.ModelValue))) + { + OnAnySettingChanged(); + } + } + + protected void EditableSettingsCollectionChangedEventHandler(object? sender, EventArgs e) + { + OnAnySettingChanged(); + } + + protected void OnAnySettingChanged() + { + AnySettingChanged?.Invoke(this, new EventArgs()); + } + + public abstract T MapToData(); + } } From dda421df74437ece1683baf60965b98966cc1b4c Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 26 Jan 2025 00:38:10 -0800 Subject: [PATCH 06/47] EditableSettingsTests --- .../EditableSettingsCollectionTests.cs | 67 ++++++++++ .../ModelTests/EditableSettingsTests.cs | 120 +++++++++++++----- .../Model/EditableSettings/EditableSetting.cs | 2 +- 3 files changed, 157 insertions(+), 32 deletions(-) create mode 100644 userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs diff --git a/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs new file mode 100644 index 00000000..f0796b47 --- /dev/null +++ b/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using userspace_backend.Model.EditableSettings; + +namespace userspace_backend_tests.ModelTests +{ + [TestClass] + public class EditableSettingsCollectionTests + { + protected class TestDataType + { + public int Property { get; set; } + } + + protected interface IEditableSettingsTestCollection : IEditableSettingsCollectionV2 + { + public IEditableSettingSpecific PropertySetting { get; } + } + + protected class EditableSettingsTestCollection : EditableSettingsCollectionV2, IEditableSettingsTestCollection + { + public const string ProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(PropertySetting)}"; + + public EditableSettingsTestCollection( + [FromKeyedServices(ProperySettingName)]IEditableSettingSpecific propertySetting) + : base([propertySetting], []) + { + PropertySetting = propertySetting; + } + + public IEditableSettingSpecific PropertySetting { get; protected set; } + + public override TestDataType MapToData() + { + return new TestDataType() + { + Property = PropertySetting.ModelValue, + }; + } + } + + [TestMethod] + public void EditableSettingsCollection_Construction() + { + string testSettingName = "Test Setting"; + int testSettingInitialValue = 0; + + var services = new ServiceCollection(); + services.AddTransient(); + services.AddKeyedTransient>( + EditableSettingsTestCollection.ProperySettingName, (_, _) => + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false)); + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IEditableSettingsTestCollection testObject = serviceProvider.GetRequiredService(); + Assert.IsNotNull(testObject); + Assert.AreEqual(testSettingInitialValue, testObject.PropertySetting.ModelValue); + Assert.AreEqual(testSettingName, testObject.PropertySetting.DisplayName); + } + } +} diff --git a/userspace-backend-tests/ModelTests/EditableSettingsTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsTests.cs index 8fc65e1e..dd02dd63 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsTests.cs @@ -1,6 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; @@ -13,55 +11,115 @@ namespace userspace_backend_tests.ModelTests [TestClass] public class EditableSettingsTests { - protected class TestDataType + [TestMethod] + public void EditableSetting_Construction() { - public int Property { get; set; } - } + string testSettingName = "Test Setting"; + int testSettingInitialValue = 0; - protected interface IEditableSettingsTestCollection : IEditableSettingsCollectionV2 - { - public IEditableSettingSpecific PropertySetting { get; } + EditableSettingV2 testObject = + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false); + + Assert.IsNotNull(testObject); + Assert.AreEqual(testSettingInitialValue, testObject.ModelValue); + Assert.AreEqual(testSettingName, testObject.DisplayName); } - protected class EditableSettingsTestCollection : EditableSettingsCollectionV2, IEditableSettingsTestCollection + [TestMethod] + public void EditableSetting_SetFromInterface() { - public const string ProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(PropertySetting)}"; + int propertyChangedHookCalls = 0; - public EditableSettingsTestCollection( - [FromKeyedServices(ProperySettingName)]IEditableSettingSpecific propertySetting) - : base([propertySetting], []) + void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { - PropertySetting = propertySetting; + if (string.Equals(e.PropertyName, nameof(EditableSettingV2.ModelValue))) + { + propertyChangedHookCalls++; + } } - public IEditableSettingSpecific PropertySetting { get; protected set; } + string testSettingName = "Test Setting"; + int testSettingInitialValue = 0; + int testSettingSecondValue = 500; + + EditableSettingV2 testObject = + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false); + + testObject.PropertyChanged += TestObject_PropertyChanged; + testObject.InterfaceValue = testSettingSecondValue.ToString(); + bool updateResult = testObject.TryUpdateFromInterface(); + + Assert.IsTrue(updateResult); + Assert.AreEqual(testSettingSecondValue, testObject.ModelValue); + Assert.AreEqual(1, propertyChangedHookCalls); + } + + [TestMethod] + public void EditableSetting_SetFromInterface_Automatic() + { + int propertyChangedHookCalls = 0; - public override TestDataType MapToData() + void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { - return new TestDataType() + if (string.Equals(e.PropertyName, nameof(EditableSettingV2.ModelValue))) { - Property = PropertySetting.ModelValue, - }; + propertyChangedHookCalls++; + } } + + string testSettingName = "Test Setting"; + int testSettingInitialValue = 0; + int testSettingSecondValue = 500; + + EditableSettingV2 testObject = + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, true); + + testObject.PropertyChanged += TestObject_PropertyChanged; + testObject.InterfaceValue = testSettingSecondValue.ToString(); + + Assert.AreEqual(testSettingSecondValue, testObject.ModelValue); + Assert.AreEqual(1, propertyChangedHookCalls); } [TestMethod] - public void EditableSettings_Construction() + public void EditableSetting_SetFromInterface_BadValue() { + int propertyChangedHookCalls = 0; + + void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (string.Equals(e.PropertyName, nameof(EditableSettingV2.ModelValue))) + { + propertyChangedHookCalls++; + } + } + string testSettingName = "Test Setting"; int testSettingInitialValue = 0; - var services = new ServiceCollection(); - services.AddTransient(); - services.AddKeyedTransient>( - EditableSettingsTestCollection.ProperySettingName, (_, _) => - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false)); - ServiceProvider serviceProvider = services.BuildServiceProvider(); + // Test case: bad value, cannot parse + EditableSettingV2 testObject = + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false); - IEditableSettingsTestCollection testObject = serviceProvider.GetRequiredService(); - Assert.IsNotNull(testObject); - Assert.AreEqual(testSettingInitialValue, testObject.PropertySetting.ModelValue); - Assert.AreEqual(0, testObject.PropertySetting.ModelValue); + testObject.PropertyChanged += TestObject_PropertyChanged; + testObject.InterfaceValue = "ASDFJLKL"; + bool updateResult = testObject.TryUpdateFromInterface(); + + Assert.IsFalse(updateResult); + Assert.AreEqual(testSettingInitialValue, testObject.ModelValue); + Assert.AreEqual(0, propertyChangedHookCalls); + + // Test case: validator determines input is invalid + testObject = + new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, new AllChangeInvalidValueValidator(), false); + + testObject.PropertyChanged += TestObject_PropertyChanged; + testObject.InterfaceValue = 500.ToString(); + updateResult = testObject.TryUpdateFromInterface(); + + Assert.IsFalse(updateResult); + Assert.AreEqual(testSettingInitialValue, testObject.ModelValue); + Assert.AreEqual(0, propertyChangedHookCalls); } } } diff --git a/userspace-backend/Model/EditableSettings/EditableSetting.cs b/userspace-backend/Model/EditableSettings/EditableSetting.cs index e23fad3f..4634c4a4 100644 --- a/userspace-backend/Model/EditableSettings/EditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/EditableSetting.cs @@ -38,6 +38,7 @@ public EditableSetting( /// public string DisplayName { get; } + // TODO: test or remove public T LastWrittenValue { get; protected set; } /// @@ -208,5 +209,4 @@ partial void OnInterfaceValueChanged(string value) } } } - } From bd3dede07e74978928d95a0e43dae80440205043 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 8 Feb 2025 13:33:01 -0800 Subject: [PATCH 07/47] Editable Settings Collections tests + organization --- .../EditableSettingsCollectionTests.cs | 213 ++++++++++++++++-- .../ModelTests/EditableSettingsTests.cs | 44 ++-- .../EditableSettingsCollection.cs | 7 +- 3 files changed, 237 insertions(+), 27 deletions(-) diff --git a/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs index f0796b47..2fc9fd05 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs @@ -1,11 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Model.EditableSettings; namespace userspace_backend_tests.ModelTests @@ -13,55 +9,246 @@ namespace userspace_backend_tests.ModelTests [TestClass] public class EditableSettingsCollectionTests { + #region TestClasses + protected class TestDataType { public int Property { get; set; } + + public TestSubDataType? SubData { get; set; } + + public override bool Equals(object? obj) + { + return obj is TestDataType type && + Property == type.Property && + EqualityComparer.Default.Equals(SubData, type.SubData); + } + + public override int GetHashCode() + { + return HashCode.Combine(Property, SubData); + } } - protected interface IEditableSettingsTestCollection : IEditableSettingsCollectionV2 + protected class TestSubDataType + { + public int SubProperty { get; set; } + + public override bool Equals(object? obj) + { + return obj is TestSubDataType type && + SubProperty == type.SubProperty; + } + + public override int GetHashCode() + { + return HashCode.Combine(SubProperty); + } + } + + protected interface IEditableSettingsTestCollection : IEditableSettingsCollectionSpecific { public IEditableSettingSpecific PropertySetting { get; } + + public IEditableSettingsTestSubCollection SubCollection { get; } + } + + protected interface IEditableSettingsTestSubCollection : IEditableSettingsCollectionSpecific + { + public IEditableSettingSpecific SubPropertySetting { get; } } protected class EditableSettingsTestCollection : EditableSettingsCollectionV2, IEditableSettingsTestCollection { public const string ProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(PropertySetting)}"; + public const string SubCollectionName = $"{nameof(EditableSettingsTestCollection)}.{nameof(SubCollection)}"; public EditableSettingsTestCollection( - [FromKeyedServices(ProperySettingName)]IEditableSettingSpecific propertySetting) - : base([propertySetting], []) + [FromKeyedServices(ProperySettingName)]IEditableSettingSpecific propertySetting, + IEditableSettingsTestSubCollection subCollection) + : base([propertySetting], [subCollection]) { PropertySetting = propertySetting; + SubCollection = subCollection; } public IEditableSettingSpecific PropertySetting { get; protected set; } + public IEditableSettingsTestSubCollection SubCollection { get; } + public override TestDataType MapToData() { return new TestDataType() { Property = PropertySetting.ModelValue, + SubData = SubCollection.MapToData(), }; } } - [TestMethod] - public void EditableSettingsCollection_Construction() + protected class EditableSettingsTestSubCollection : EditableSettingsCollectionV2, IEditableSettingsTestSubCollection { - string testSettingName = "Test Setting"; - int testSettingInitialValue = 0; + public const string SubPropertySettingName = $"{nameof(EditableSettingsTestSubCollection)}.{nameof(SubPropertySetting)}"; - var services = new ServiceCollection(); + public EditableSettingsTestSubCollection( + [FromKeyedServices(SubPropertySettingName)]IEditableSettingSpecific subPropertySetting) + : base([subPropertySetting], []) + { + SubPropertySetting = subPropertySetting; + } + + public IEditableSettingSpecific SubPropertySetting { get; protected set; } + + public override TestSubDataType MapToData() + { + return new TestSubDataType() + { + SubProperty = SubPropertySetting.ModelValue, + }; + } + } + + #endregion TestClasses + + #region InitDI + + protected static IEditableSettingsTestCollection InitTestObject( + string testSettingName, + string testSubSettingName, + int testSettingInitialValue, + int testSubSettingInitialValue) + { + ServiceCollection services = new ServiceCollection(); services.AddTransient(); services.AddKeyedTransient>( EditableSettingsTestCollection.ProperySettingName, (_, _) => - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false)); + new EditableSettingV2( + testSettingName, + testSettingInitialValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: false)); + services.AddTransient(); + services.AddKeyedTransient>( + EditableSettingsTestSubCollection.SubPropertySettingName, (_, _) => + new EditableSettingV2( + testSubSettingName, + testSubSettingInitialValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: false)); + ServiceProvider serviceProvider = services.BuildServiceProvider(); IEditableSettingsTestCollection testObject = serviceProvider.GetRequiredService(); + return testObject; + } + + #endregion InitDI + + #region Tests + + [TestMethod] + public void EditableSettingsCollection_Construction() + { + string testSettingName = "Test Setting"; + string testSubSettingName = "Test Sub Setting"; + int testSettingInitialValue = 0; + int testSubSettingInitialValue = 1; + + IEditableSettingsTestCollection testObject = + InitTestObject(testSettingName, testSubSettingName, testSettingInitialValue, testSubSettingInitialValue); + Assert.IsNotNull(testObject); + Assert.IsNotNull(testObject.PropertySetting); Assert.AreEqual(testSettingInitialValue, testObject.PropertySetting.ModelValue); Assert.AreEqual(testSettingName, testObject.PropertySetting.DisplayName); + + Assert.IsNotNull(testObject.SubCollection); + Assert.IsNotNull(testObject.SubCollection.SubPropertySetting); + Assert.AreEqual(testSubSettingInitialValue, testObject.SubCollection.SubPropertySetting.ModelValue); + Assert.AreEqual(testSubSettingName, testObject.SubCollection.SubPropertySetting.DisplayName); + } + + [TestMethod] + public void EditableSettingsCollection_PropertyChange() + { + string testSettingName = "Test Setting"; + string testSubSettingName = "Test Sub Setting"; + int testSettingInitialValue = 0; + int testSubSettingInitialValue = 1; + + IEditableSettingsTestCollection testObject = + InitTestObject(testSettingName, testSubSettingName, testSettingInitialValue, testSubSettingInitialValue); + + // Test Case: property is changed + int propertyChangedHandlerCalls = 0; + void TestSettingChangedHandler(object? sender, EventArgs e) + { + propertyChangedHandlerCalls++; + } + + testObject.AnySettingChanged += TestSettingChangedHandler; + testObject.PropertySetting.InterfaceValue = "2"; + testObject.PropertySetting.TryUpdateFromInterface(); + + Assert.AreEqual(2, testObject.PropertySetting.ModelValue); + Assert.AreEqual(1, propertyChangedHandlerCalls); + + // Test Case: property of sub collection is changed + propertyChangedHandlerCalls = 0; + testObject.SubCollection.SubPropertySetting.InterfaceValue = "3"; + testObject.SubCollection.SubPropertySetting.TryUpdateFromInterface(); + + Assert.AreEqual(3, testObject.SubCollection.SubPropertySetting.ModelValue); + Assert.AreEqual(1, propertyChangedHandlerCalls); } + + [TestMethod] + public void EditableSettingsCollection_MapToData() + { + string testSettingName = "Test Setting"; + string testSubSettingName = "Test Sub Setting"; + int testSettingInitialValue = 0; + int testSubSettingInitialValue = 1; + + IEditableSettingsTestCollection testObject = + InitTestObject(testSettingName, testSubSettingName, testSettingInitialValue, testSubSettingInitialValue); + + // Test Case: initial values + TestDataType expectedData = new TestDataType() + { + Property = testSettingInitialValue, + SubData = new TestSubDataType() + { + SubProperty = testSubSettingInitialValue, + } + }; + + TestDataType actualData = testObject.MapToData(); + Assert.IsNotNull(actualData); + Assert.AreEqual(expectedData, actualData); + + // Test case: properties change + testObject.PropertySetting.InterfaceValue = "2"; + testObject.PropertySetting.TryUpdateFromInterface(); + testObject.SubCollection.SubPropertySetting.InterfaceValue = "3"; + testObject.SubCollection.SubPropertySetting.TryUpdateFromInterface(); + + expectedData = new TestDataType() + { + Property = 2, + SubData = new TestSubDataType() + { + SubProperty = 3, + } + }; + + actualData = testObject.MapToData(); + Assert.IsNotNull(actualData); + Assert.AreEqual(expectedData, actualData); + } + + #endregion Tests } } diff --git a/userspace-backend-tests/ModelTests/EditableSettingsTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsTests.cs index dd02dd63..517e06ca 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsTests.cs @@ -1,9 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Model.EditableSettings; namespace userspace_backend_tests.ModelTests @@ -11,14 +7,32 @@ namespace userspace_backend_tests.ModelTests [TestClass] public class EditableSettingsTests { + #region Init + + public static EditableSettingV2 InitTestObject( + string testSettingName, + int testSettingInitialValue, + bool autoUpddateFromInterface = false) + { + return new EditableSettingV2( + testSettingName, + testSettingInitialValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: autoUpddateFromInterface); + } + + #endregion Init + + #region Tests + [TestMethod] public void EditableSetting_Construction() { string testSettingName = "Test Setting"; int testSettingInitialValue = 0; - EditableSettingV2 testObject = - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false); + EditableSettingV2 testObject = InitTestObject(testSettingName, testSettingInitialValue); Assert.IsNotNull(testObject); Assert.AreEqual(testSettingInitialValue, testObject.ModelValue); @@ -42,8 +56,7 @@ void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyCh int testSettingInitialValue = 0; int testSettingSecondValue = 500; - EditableSettingV2 testObject = - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false); + EditableSettingV2 testObject = InitTestObject(testSettingName, testSettingInitialValue); testObject.PropertyChanged += TestObject_PropertyChanged; testObject.InterfaceValue = testSettingSecondValue.ToString(); @@ -71,8 +84,7 @@ void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyCh int testSettingInitialValue = 0; int testSettingSecondValue = 500; - EditableSettingV2 testObject = - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, true); + EditableSettingV2 testObject = InitTestObject(testSettingName, testSettingInitialValue, autoUpddateFromInterface: true); testObject.PropertyChanged += TestObject_PropertyChanged; testObject.InterfaceValue = testSettingSecondValue.ToString(); @@ -98,8 +110,7 @@ void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyCh int testSettingInitialValue = 0; // Test case: bad value, cannot parse - EditableSettingV2 testObject = - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, ModelValueValidators.DefaultIntValidator, false); + EditableSettingV2 testObject = InitTestObject(testSettingName, testSettingInitialValue); testObject.PropertyChanged += TestObject_PropertyChanged; testObject.InterfaceValue = "ASDFJLKL"; @@ -111,7 +122,12 @@ void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyCh // Test case: validator determines input is invalid testObject = - new EditableSettingV2(testSettingName, testSettingInitialValue, UserInputParsers.IntParser, new AllChangeInvalidValueValidator(), false); + new EditableSettingV2( + testSettingName, + testSettingInitialValue, + UserInputParsers.IntParser, + new AllChangeInvalidValueValidator(), + autoUpdateFromInterface: false); testObject.PropertyChanged += TestObject_PropertyChanged; testObject.InterfaceValue = 500.ToString(); @@ -121,5 +137,7 @@ void TestObject_PropertyChanged(object? sender, System.ComponentModel.PropertyCh Assert.AreEqual(testSettingInitialValue, testObject.ModelValue); Assert.AreEqual(0, propertyChangedHookCalls); } + + #endregion Tests } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs index e66d48eb..55c44415 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs @@ -106,6 +106,11 @@ public interface IEditableSettingsCollectionV2 public EventHandler AnySettingChanged { get; set; } } + public interface IEditableSettingsCollectionSpecific : IEditableSettingsCollectionV2 + { + T MapToData(); + } + /// /// Base class for settings collections. /// @@ -116,7 +121,7 @@ public interface IEditableSettingsCollectionV2 /// The actual settings collections in the model do not need to each be tested beyond composition. /// /// - public abstract class EditableSettingsCollectionV2 : ObservableObject, IEditableSettingsCollection + public abstract class EditableSettingsCollectionV2 : ObservableObject, IEditableSettingsCollectionSpecific { public EditableSettingsCollectionV2( IEnumerable editableSettings, From 993c909bb173353041ffae8b6f40b622d13442be Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 9 Feb 2025 23:46:17 -0800 Subject: [PATCH 08/47] Start work on editable selector --- .../EditableSettingsSelectorTests.cs | 111 ++++++++++++++++++ .../EditableSettingsSelector.cs | 43 +++++++ 2 files changed, 154 insertions(+) create mode 100644 userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs create mode 100644 userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs diff --git a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs new file mode 100644 index 00000000..e96467fe --- /dev/null +++ b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs @@ -0,0 +1,111 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using userspace_backend.Model.EditableSettings; + +namespace userspace_backend_tests.ModelTests +{ + [TestClass] + public class EditableSettingsSelectorTests + { + + #region TestClasses + + public abstract class TestDataAbstract + { + public enum TestDataType + { + A, + B, + } + + public TestDataType Type { get; set; } + } + + public class TestDataA + { + public int PropertyA; + } + + public class TestDataB + { + public int PropertyB; + } + + public interface IEditableSettingsTestA : IEditableSettingsCollectionSpecific + { + public IEditableSettingSpecific PropertyA { get; } + } + + public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific + { + public IEditableSettingSpecific PropertyB { get; } + } + + public interface IEditableSettingsTestSelector : IEditableSettingsSelector< + TestDataAbstract.TestDataType, + IEditableSettingsCollectionSpecific, + TestDataAbstract> + { + } + + public class EditableSettingsTestA : EditableSettingsCollectionV2, IEditableSettingsTestA + { + public EditableSettingsTestA(IEditableSettingSpecific propertyA) + : base([propertyA], []) + { + PropertyA = propertyA; + } + + public IEditableSettingSpecific PropertyA { get; } + + public override TestDataA MapToData() + { + return new TestDataA() + { + PropertyA = PropertyA.ModelValue, + }; + } + } + + public class EditableSettingsTestB : EditableSettingsCollectionV2, IEditableSettingsTestB + { + public EditableSettingsTestB(IEditableSettingSpecific propertyB) + : base([propertyB], []) + { + PropertyB = propertyB; + } + + public IEditableSettingSpecific PropertyB { get; } + + public override TestDataB MapToData() + { + return new TestDataB() + { + PropertyB = PropertyB.ModelValue, + }; + } + } + + public class EditableSettingsTestSelector : EditableSettingsSelector< + TestDataAbstract.TestDataType, + IEditableSettingsCollectionSpecific, + TestDataAbstract> + , IEditableSettingsTestSelector + { + public EditableSettingsTestSelector( + IEditableSettingSpecific selection, + IServiceProvider serviceProvider) + : base(selection, serviceProvider, [], []) + { + } + + public override TestDataAbstract MapToData() + { + return GetSelectable(Selection.ModelValue).MapToData(); + } + } + + #endregion TestClasses + } +} diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs new file mode 100644 index 00000000..8ec441cd --- /dev/null +++ b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; + +namespace userspace_backend.Model.EditableSettings +{ + public interface IEditableSettingsSelector : IEditableSettingsCollectionSpecific where T : Enum where U : IEditableSettingsCollectionV2 + { + public IEditableSettingSpecific Selection { get; } + + public U GetSelectable(T choice); + } + + public abstract class EditableSettingsSelector + : EditableSettingsCollectionV2, + IEditableSettingsSelector where T : Enum where U : IEditableSettingsCollectionV2 + { + protected EditableSettingsSelector( + IEditableSettingSpecific selection, + IServiceProvider serviceProvider, + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + : base(editableSettings, editableSettingsCollections) + { + SelectionLookup = new Dictionary(); + Selection = selection; + } + + public IEditableSettingSpecific Selection { get; } + + protected IDictionary SelectionLookup { get; } + + public U GetSelectable(T choice) => SelectionLookup[choice]; + + protected void InitSelectionLookup(IServiceProvider serviceProvider) + { + foreach (T value in Enum.GetValues(typeof(T))) + { + SelectionLookup.Add(value, serviceProvider.GetRequiredService()); + } + } + } +} From 4768f69da2b1101f18aa1e697b6387dfefb58267 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 15 Feb 2025 22:29:57 -0800 Subject: [PATCH 09/47] editable selector and tests --- .../EditableSettingsSelectorTests.cs | 237 ++++++++++++++++-- .../EditableSettingsSelector.cs | 38 ++- 2 files changed, 248 insertions(+), 27 deletions(-) diff --git a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs index e96467fe..8171a3b6 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using userspace_backend.Model.EditableSettings; @@ -19,39 +20,100 @@ public enum TestDataType B, } - public TestDataType Type { get; set; } + public abstract TestDataType Type { get; } + + public static DefaultModelValueValidator DefaultTestDataTypeValidator = + new DefaultModelValueValidator(); + + public override bool Equals(object? obj) + { + return obj is TestDataAbstract @abstract && + Type == @abstract.Type; + } + + public override int GetHashCode() + { + return HashCode.Combine(Type); + } + } + + public class TestDataTypeParser : IUserInputParser + { + public static TestDataTypeParser Singleton = new TestDataTypeParser(); + + public bool TryParse(string input, out TestDataAbstract.TestDataType parsedValue) + { + if (Enum.TryParse(input, ignoreCase: true, out parsedValue)) + { + return true; + } + + parsedValue = default; + return false; + } } - public class TestDataA + public class TestDataA : TestDataAbstract { public int PropertyA; + + public override TestDataType Type => TestDataType.A; + + public override bool Equals(object? obj) + { + return obj is TestDataA a && + base.Equals(obj) && + Type == a.Type && + PropertyA == a.PropertyA; + } + + public override int GetHashCode() + { + return HashCode.Combine(base.GetHashCode(), Type, PropertyA); + } } - public class TestDataB + public class TestDataB : TestDataAbstract { public int PropertyB; + + public override TestDataType Type => TestDataType.B; + + public override bool Equals(object? obj) + { + return obj is TestDataB b && + base.Equals(obj) && + Type == b.Type && + PropertyB == b.PropertyB; + } + + public override int GetHashCode() + { + return HashCode.Combine(base.GetHashCode(), Type, PropertyB); + } } - public interface IEditableSettingsTestA : IEditableSettingsCollectionSpecific + public interface IEditableSettingsTestA : IEditableSettingsCollectionSpecific { public IEditableSettingSpecific PropertyA { get; } } - public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific + public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific { public IEditableSettingSpecific PropertyB { get; } } public interface IEditableSettingsTestSelector : IEditableSettingsSelector< TestDataAbstract.TestDataType, - IEditableSettingsCollectionSpecific, TestDataAbstract> { } - public class EditableSettingsTestA : EditableSettingsCollectionV2, IEditableSettingsTestA + public class EditableSettingsTestA : EditableSettingsCollectionV2, IEditableSettingsTestA { - public EditableSettingsTestA(IEditableSettingSpecific propertyA) + public const string PropertyAName = $"{nameof(EditableSettingsTestA)}.{nameof(PropertyA)}"; + + public EditableSettingsTestA([FromKeyedServices(PropertyAName)]IEditableSettingSpecific propertyA) : base([propertyA], []) { PropertyA = propertyA; @@ -59,7 +121,7 @@ public EditableSettingsTestA(IEditableSettingSpecific propertyA) public IEditableSettingSpecific PropertyA { get; } - public override TestDataA MapToData() + public override TestDataAbstract MapToData() { return new TestDataA() { @@ -68,9 +130,11 @@ public override TestDataA MapToData() } } - public class EditableSettingsTestB : EditableSettingsCollectionV2, IEditableSettingsTestB + public class EditableSettingsTestB : EditableSettingsCollectionV2, IEditableSettingsTestB { - public EditableSettingsTestB(IEditableSettingSpecific propertyB) + public const string PropertyBName = $"{nameof(EditableSettingsTestB)}.{nameof(PropertyB)}"; + + public EditableSettingsTestB([FromKeyedServices(PropertyBName)]IEditableSettingSpecific propertyB) : base([propertyB], []) { PropertyB = propertyB; @@ -89,23 +153,162 @@ public override TestDataB MapToData() public class EditableSettingsTestSelector : EditableSettingsSelector< TestDataAbstract.TestDataType, - IEditableSettingsCollectionSpecific, TestDataAbstract> , IEditableSettingsTestSelector { + public const string SelectionName = $"{nameof(EditableSettingsTestSelector)}.{nameof(Selection)}"; + public EditableSettingsTestSelector( - IEditableSettingSpecific selection, + [FromKeyedServices(SelectionName)]IEditableSettingSpecific selection, IServiceProvider serviceProvider) : base(selection, serviceProvider, [], []) { } + } - public override TestDataAbstract MapToData() + #endregion TestClasses + + protected static IEditableSettingsTestSelector InitTestObject( + string selectionName, + string aName, + string bName, + TestDataAbstract.TestDataType selectionInitialValue, + int aInitialValue, + int bInitialValue) + { + ServiceCollection services = new ServiceCollection(); + services.AddTransient(); + services.AddKeyedTransient>( + EditableSettingsTestSelector.SelectionName, (_, _) => + new EditableSettingV2( + selectionName, + selectionInitialValue, + TestDataTypeParser.Singleton, + TestDataAbstract.DefaultTestDataTypeValidator, + autoUpdateFromInterface: false)); + services.AddTransient(); + services.AddKeyedTransient, EditableSettingsTestA>( + EditableSettingsSelectorHelper.GetSelectionKey(TestDataAbstract.TestDataType.A)); + services.AddKeyedTransient>( + EditableSettingsTestA.PropertyAName, (_, _) => + new EditableSettingV2( + aName, + aInitialValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: false)); + services.AddTransient(); + services.AddKeyedTransient, EditableSettingsTestB>( + EditableSettingsSelectorHelper.GetSelectionKey(TestDataAbstract.TestDataType.B)); + services.AddKeyedTransient>( + EditableSettingsTestB.PropertyBName, (_, _) => + new EditableSettingV2( + bName, + bInitialValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: false)); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + IEditableSettingsTestSelector testObject = serviceProvider.GetRequiredService(); + return testObject; + } + + #region Tests + + [TestMethod] + public void EditableSettingsSelector_Construction() + { + string selectionName = "Selection"; + string aName = "Property A"; + string bName = "Property B"; + TestDataAbstract.TestDataType selectionInitialValue = TestDataAbstract.TestDataType.A; + int aInitialValue = 1; + int bInitialValue = 2; + + IEditableSettingsTestSelector testObject = + InitTestObject(selectionName, aName, bName, selectionInitialValue, aInitialValue, bInitialValue); + Assert.IsNotNull(testObject); + Assert.IsNotNull(testObject.Selection); + Assert.AreEqual(selectionInitialValue, testObject.Selection.ModelValue); + + IEditableSettingsCollectionSpecific testObjectAbstractA = + testObject.GetSelectable(TestDataAbstract.TestDataType.A); + Assert.IsNotNull(testObjectAbstractA); + IEditableSettingsTestA testObjectA = testObjectAbstractA as IEditableSettingsTestA; + Assert.IsNotNull(testObjectA); + Assert.AreEqual(aName, testObjectA.PropertyA.DisplayName); + Assert.AreEqual(aInitialValue, testObjectA.PropertyA.ModelValue); + + IEditableSettingsCollectionSpecific testObjectAbstractB = + testObject.GetSelectable(TestDataAbstract.TestDataType.B); + Assert.IsNotNull(testObjectAbstractB); + IEditableSettingsTestB testObjectB = testObjectAbstractB as IEditableSettingsTestB; + Assert.IsNotNull(testObjectB); + Assert.AreEqual(bName, testObjectB.PropertyB.DisplayName); + Assert.AreEqual(bInitialValue, testObjectB.PropertyB.ModelValue); + } + + [TestMethod] + public void EditableSettingsSelector_SelectionChange() + { + string selectionName = "Selection"; + string aName = "Property A"; + string bName = "Property B"; + TestDataAbstract.TestDataType selectionInitialValue = TestDataAbstract.TestDataType.A; + int aInitialValue = 1; + int bInitialValue = 2; + + IEditableSettingsTestSelector testObject = + InitTestObject(selectionName, aName, bName, selectionInitialValue, aInitialValue, bInitialValue); + + int propertyChangedHandlerCalls = 0; + void TestSettingChangedHandler(object? sender, EventArgs e) { - return GetSelectable(Selection.ModelValue).MapToData(); + propertyChangedHandlerCalls++; } + + testObject.AnySettingChanged += TestSettingChangedHandler; + testObject.Selection.InterfaceValue = "b"; + testObject.Selection.TryUpdateFromInterface(); + + Assert.AreEqual(TestDataAbstract.TestDataType.B, testObject.Selection.ModelValue); + Assert.AreEqual(1, propertyChangedHandlerCalls); } + - #endregion TestClasses + [TestMethod] + public void EditableSettingsSelector_MapToData() + { + string selectionName = "Selection"; + string aName = "Property A"; + string bName = "Property B"; + TestDataAbstract.TestDataType selectionInitialValue = TestDataAbstract.TestDataType.A; + int aInitialValue = 1; + int bInitialValue = 2; + + IEditableSettingsTestSelector testObject = + InitTestObject(selectionName, aName, bName, selectionInitialValue, aInitialValue, bInitialValue); + + // Test case: initial values + TestDataAbstract expectedData = new TestDataA() + { + PropertyA = aInitialValue, + }; + TestDataAbstract actualData = testObject.MapToData(); + Assert.AreEqual(expectedData, actualData); + + // Test case: selection changes + testObject.Selection.InterfaceValue = "b"; + testObject.Selection.TryUpdateFromInterface(); + expectedData = new TestDataB() + { + PropertyB = bInitialValue, + }; + actualData = testObject.MapToData(); + Assert.AreEqual(expectedData, actualData); + } + + #endregion Tests } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs index 8ec441cd..be244a30 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs @@ -1,43 +1,61 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; +using System.Linq; namespace userspace_backend.Model.EditableSettings { - public interface IEditableSettingsSelector : IEditableSettingsCollectionSpecific where T : Enum where U : IEditableSettingsCollectionV2 + public interface IEditableSettingsSelector : IEditableSettingsCollectionSpecific where T : Enum { public IEditableSettingSpecific Selection { get; } - public U GetSelectable(T choice); + public IEditableSettingsCollectionSpecific GetSelectable(T choice); } - public abstract class EditableSettingsSelector - : EditableSettingsCollectionV2, - IEditableSettingsSelector where T : Enum where U : IEditableSettingsCollectionV2 + public static class EditableSettingsSelectorHelper + { + public static string GetSelectionKey(T value) where T : Enum + { + return $"{typeof(T)}.{value.ToString()}"; + } + + } + + public abstract class EditableSettingsSelector + : EditableSettingsCollectionV2, + IEditableSettingsSelector where T : Enum { protected EditableSettingsSelector( IEditableSettingSpecific selection, IServiceProvider serviceProvider, IEnumerable editableSettings, IEnumerable editableSettingsCollections) - : base(editableSettings, editableSettingsCollections) + : base(editableSettings.Union([selection]), editableSettingsCollections) { - SelectionLookup = new Dictionary(); + SelectionLookup = new Dictionary>(); + InitSelectionLookup(serviceProvider); Selection = selection; } public IEditableSettingSpecific Selection { get; } - protected IDictionary SelectionLookup { get; } + protected IDictionary> SelectionLookup { get; } - public U GetSelectable(T choice) => SelectionLookup[choice]; + public IEditableSettingsCollectionSpecific GetSelectable(T choice) => SelectionLookup[choice]; protected void InitSelectionLookup(IServiceProvider serviceProvider) { foreach (T value in Enum.GetValues(typeof(T))) { - SelectionLookup.Add(value, serviceProvider.GetRequiredService()); + string key = EditableSettingsSelectorHelper.GetSelectionKey(value); + SelectionLookup.Add(value, serviceProvider.GetRequiredKeyedService>(key)); } } + + public override U MapToData() + { + return GetSelectable(Selection.ModelValue).MapToData(); + } + } } From 6427ad28b6297c30836d168652ad34965b1e7f11 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Tue, 18 Feb 2025 21:28:17 -0800 Subject: [PATCH 10/47] editable list test --- .../ModelTests/EditableSettingsListTests.cs | 244 ++++++++++++++++++ .../EditableSettingsSelectorTests.cs | 6 +- .../Model/EditableSettings/EditableList.cs | 56 ---- .../EditableSettings/EditableSettingsList.cs | 118 +++++++++ 4 files changed, 366 insertions(+), 58 deletions(-) create mode 100644 userspace-backend-tests/ModelTests/EditableSettingsListTests.cs delete mode 100644 userspace-backend/Model/EditableSettings/EditableList.cs create mode 100644 userspace-backend/Model/EditableSettings/EditableSettingsList.cs diff --git a/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs new file mode 100644 index 00000000..fb86a08d --- /dev/null +++ b/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs @@ -0,0 +1,244 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using userspace_backend.Model.EditableSettings; + +namespace userspace_backend_tests.ModelTests +{ + [TestClass] + public class EditableSettingsListTests + { + #region TestClasses + + public class TestData + { + public string Name { get; set; } + + public int Property { get; set; } + } + + public class TestDataList + { + public TestData[] Data { get; set; } + + public int OtherProperty { get; set; } + } + + public interface IEditableSettingsTestCollection : IEditableSettingsCollectionSpecific + { + public IEditableSettingSpecific NameSetting { get; } + + public IEditableSettingSpecific PropertySetting { get; } + } + + public interface IEditableSettingsTestList : IEditableSettingsList + { + public IEditableSettingSpecific OtherPropertySetting { get; } + } + + public class EditableSettingsTestCollection : EditableSettingsCollectionV2, IEditableSettingsTestCollection + { + public const string NameSettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(NameSetting)}"; + public const string ProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(PropertySetting)}"; + + public EditableSettingsTestCollection( + [FromKeyedServices(NameSettingName)]IEditableSettingSpecific nameSetting, + [FromKeyedServices(ProperySettingName)]IEditableSettingSpecific propertySetting) + : base([propertySetting], []) + + { + NameSetting = nameSetting; + PropertySetting = propertySetting; + } + + public IEditableSettingSpecific NameSetting { get; } + + public IEditableSettingSpecific PropertySetting { get; } + + public override TestData MapToData() + { + return new TestData() + { + Property = PropertySetting.ModelValue, + }; + } + } + + public class EditableSettingsTestList : EditableSettingsList, IEditableSettingsTestList + { + public const string OtherProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(OtherPropertySetting)}"; + public const string TestNameTemplate = "TestData"; + + public EditableSettingsTestList([FromKeyedServices(OtherProperySettingName)]IEditableSettingSpecific otherPropertySetting, + IServiceProvider serviceProvider) + : base(serviceProvider, [otherPropertySetting], []) + { + OtherPropertySetting = otherPropertySetting; + } + + public IEditableSettingSpecific OtherPropertySetting { get; } + + protected override string DefaultNameTemplate => TestNameTemplate; + + public override TestDataList MapToData() + { + return new TestDataList() + { + Data = ElementsInternal.Select(e => e.MapToData()).ToArray(), + OtherProperty = OtherPropertySetting.ModelValue, + }; + } + + protected override string GetNameFromElement(IEditableSettingsTestCollection element) => element.NameSetting.ModelValue; + + protected override void SetElementName(IEditableSettingsTestCollection element, string name) + { + element.NameSetting.InterfaceValue = name; + element.NameSetting.TryUpdateFromInterface(); + } + } + + #endregion TestClasses + + #region InitDI + + public (IEditableSettingsTestList, IServiceProvider) InitTestObject( + string otherPropertyName, + int otherPropertyValue, + string propertyName, + int propertyValue, + string nameName, + string nameValue) + { + ServiceCollection services = new ServiceCollection(); + services.AddTransient(); + services.AddKeyedTransient>( + EditableSettingsTestList.OtherProperySettingName, (_, _) => + new EditableSettingV2( + otherPropertyName, + otherPropertyValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: false)); + services.AddTransient(); + services.AddKeyedTransient>( + EditableSettingsTestCollection.ProperySettingName, (_, _) => + new EditableSettingV2( + propertyName, + propertyValue, + UserInputParsers.IntParser, + ModelValueValidators.DefaultIntValidator, + autoUpdateFromInterface: false)); + services.AddKeyedTransient>( + EditableSettingsTestCollection.NameSettingName, (_, _) => + new EditableSettingV2( + nameName, + nameValue, + UserInputParsers.StringParser, + ModelValueValidators.DefaultStringValidator, + autoUpdateFromInterface: false)); + + IServiceProvider serviceProvider = services.BuildServiceProvider(); + IEditableSettingsTestList testObject = serviceProvider.GetRequiredService(); + return (testObject, serviceProvider); + } + + #endregion InitDI + + #region Tests + + [TestMethod] + public void EditableSettingsList_Construction() + { + string otherPropertyName = "Other Property"; + int otherPropertyInitialValue = 1; + string propertyName = "Property"; + int propertyInitialValue = 2; + string nameName = "Name"; + string nameInitialValue = "My test data list"; + (IEditableSettingsTestList testObject, _) = InitTestObject( + otherPropertyName, + otherPropertyInitialValue, + propertyName, + propertyInitialValue, + nameName, + nameInitialValue); + + Assert.IsNotNull(testObject); + Assert.IsNotNull(testObject.OtherPropertySetting); + Assert.IsNotNull(testObject.Elements); + Assert.AreEqual(0, testObject.Elements.Count); + Assert.AreEqual(otherPropertyName, testObject.OtherPropertySetting.DisplayName); + Assert.AreEqual(otherPropertyInitialValue, testObject.OtherPropertySetting.ModelValue); + } + + [TestMethod] + public void EditableSettingsList_AddRemoveElements() + { + string otherPropertyName = "Other Property"; + int otherPropertyInitialValue = 1; + string propertyName = "Property"; + int propertyInitialValue = 2; + string nameName = "Name"; + string nameInitialValue = "My test data list"; + (IEditableSettingsTestList testObject, IServiceProvider serviceProvider) = InitTestObject( + otherPropertyName, + otherPropertyInitialValue, + propertyName, + propertyInitialValue, + nameName, + nameInitialValue); + + Assert.IsNotNull(testObject); + Assert.IsNotNull(testObject.Elements); + Assert.AreEqual(0, testObject.Elements.Count); + + // Test case: add one default element + testObject.TryAddNewDefault(); + Assert.AreEqual(1, testObject.Elements.Count); + IEditableSettingsTestCollection firstElement = testObject.Elements[0]; + Assert.IsNotNull(firstElement); + Assert.AreEqual($"{EditableSettingsTestList.TestNameTemplate}1", firstElement.NameSetting.ModelValue); + Assert.AreEqual(propertyInitialValue, firstElement.PropertySetting.ModelValue); + + // Test case: add second default element + testObject.TryAddNewDefault(); + Assert.AreEqual(2, testObject.Elements.Count); + IEditableSettingsTestCollection secondElement = testObject.Elements[1]; + Assert.IsNotNull(secondElement ); + Assert.AreEqual($"{EditableSettingsTestList.TestNameTemplate}2", secondElement.NameSetting.ModelValue); + Assert.AreEqual(propertyInitialValue, secondElement.PropertySetting.ModelValue); + + // Test case: add custom-created element + IEditableSettingsTestCollection elementToAdd = serviceProvider.GetRequiredService(); + bool result = testObject.TryAdd(elementToAdd); + Assert.IsTrue(result); + Assert.AreEqual(3, testObject.Elements.Count); + IEditableSettingsTestCollection thirdElement = testObject.Elements[2]; + Assert.AreEqual(elementToAdd, thirdElement); + Assert.AreEqual(nameInitialValue, elementToAdd.NameSetting.ModelValue); + + // Test case: add element with same name (should fail) + IEditableSettingsTestCollection duplicateElementToAdd = serviceProvider.GetRequiredService(); + result = testObject.TryAdd(duplicateElementToAdd); + Assert.IsFalse(result); + Assert.AreEqual(3, testObject.Elements.Count); + + // Test case: remove element + result = testObject.TryRemoveElement(elementToAdd); + Assert.IsTrue(result); + Assert.AreEqual(2, testObject.Elements.Count); + + // Test case: remove element that was never added (should fail) + result = testObject.TryRemoveElement(duplicateElementToAdd); + Assert.IsFalse(result); + Assert.AreEqual(2, testObject.Elements.Count); + } + + #endregion Tests + } +} diff --git a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs index 8171a3b6..acff3801 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; using userspace_backend.Model.EditableSettings; namespace userspace_backend_tests.ModelTests @@ -9,7 +8,6 @@ namespace userspace_backend_tests.ModelTests [TestClass] public class EditableSettingsSelectorTests { - #region TestClasses public abstract class TestDataAbstract @@ -168,6 +166,8 @@ public EditableSettingsTestSelector( #endregion TestClasses + #region InitDI + protected static IEditableSettingsTestSelector InitTestObject( string selectionName, string aName, @@ -214,6 +214,8 @@ protected static IEditableSettingsTestSelector InitTestObject( return testObject; } + #endregion InitDI + #region Tests [TestMethod] diff --git a/userspace-backend/Model/EditableSettings/EditableList.cs b/userspace-backend/Model/EditableSettings/EditableList.cs deleted file mode 100644 index 03c78d14..00000000 --- a/userspace-backend/Model/EditableSettings/EditableList.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace userspace_backend.Model.EditableSettings -{ - public abstract class EditableList - { - public bool TryAddNewDefault() - { - for (int i = 0; i < 10; i++) - { - string defaultProfileName = GenerateDefaultName(i); - - if (!ContainsElementWithName(defaultProfileName)) - { - T newDefaultElement = GenerateDefaultElement(defaultProfileName); - AddElement(newDefaultElement); - - return true; - } - } - - return false; - } - - public bool TryAdd(T element) - { - string name = GetNameFromElement(element); - - if (!ContainsElementWithName(name)) - { - AddElement(element); - return true; - } - - return true; - } - - protected abstract string GetNameFromElement(T element); - - public abstract bool TryGetElement(string name, out T element); - - protected abstract string DefaultNameTemplate { get; } - - protected string GenerateDefaultName(int index) => $"{DefaultNameTemplate}{index}"; - - protected bool ContainsElementWithName(string name) => TryGetElement(name, out T _); - - protected abstract bool AddElement(T element); - - protected abstract T GenerateDefaultElement(string name); - } -} diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs new file mode 100644 index 00000000..68bf1979 --- /dev/null +++ b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs @@ -0,0 +1,118 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace userspace_backend.Model.EditableSettings +{ + public interface IEditableSettingsList : IEditableSettingsCollectionSpecific where T : class, IEditableSettingsCollectionV2 + { + public ReadOnlyObservableCollection Elements { get; } + + public bool TryAddNewDefault(); + + public bool TryAdd(T element); + + public bool TryGetElement(string name, out T? element); + + public bool TryRemoveElement(T element); + } + + public abstract class EditableSettingsList : EditableSettingsCollectionV2, IEditableSettingsList where T : class, IEditableSettingsCollectionV2 + { + public EditableSettingsList( + IServiceProvider serviceProvider, + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + : base(editableSettings, editableSettingsCollections) + { + ServiceProvider = serviceProvider; + ElementsInternal = new ObservableCollection(); + Elements = new ReadOnlyObservableCollection(ElementsInternal); + } + + public ReadOnlyObservableCollection Elements { get; } + + protected ObservableCollection ElementsInternal { get; } + + protected IServiceProvider ServiceProvider { get; } + + protected abstract string DefaultNameTemplate { get; } + + public bool TryAdd(T element) + { + string name = GetNameFromElement(element); + + if (!ContainsElementWithName(name)) + { + AddElement(element); + return true; + } + + return false; + } + + public bool TryAddNewDefault() + { + for (int i = 1; i < 10; i++) + { + string defaultProfileName = GenerateDefaultName(i); + + if (!ContainsElementWithName(defaultProfileName)) + { + T newDefaultElement = GenerateDefaultElement(defaultProfileName); + AddElement(newDefaultElement); + + return true; + } + } + + return false; + } + + public bool TryGetElement(string name, out T? element) + { + element = null; + + foreach (T elementInList in ElementsInternal) + { + if (string.Equals(name, GetNameFromElement(elementInList), StringComparison.InvariantCultureIgnoreCase)) + { + element = elementInList; + return true; + } + } + + return false; + } + + public bool TryRemoveElement(T element) + { + return ElementsInternal.Remove(element); + } + + protected bool ContainsElementWithName(string name) => TryGetElement(name, out T? _); + + protected string GenerateDefaultName(int index) => $"{DefaultNameTemplate}{index}"; + + protected void AddElement(T element) + { + ElementsInternal.Add(element); + } + + protected T GenerateDefaultElement(string name) + { + T newDefaultElement = ServiceProvider.GetRequiredService(); + SetElementName(newDefaultElement, name); + + return newDefaultElement; + } + + protected abstract string GetNameFromElement(T element); + + protected abstract void SetElementName(T element, string name); + } +} From 1b02553949d5be8b83af598db61d06ef5936f0cd Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Mon, 10 Mar 2025 19:43:34 -0700 Subject: [PATCH 11/47] add AutoUpdateFromInterface to editable setting interface --- .../ViewModels/AccelerationProfileSettingsViewModel.cs | 2 +- userinterface/Views/AccelerationProfileSettingsView.axaml | 4 ++-- userspace-backend/Model/EditableSettings/IEditableSetting.cs | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs b/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs index 33dbd180..3667c87d 100644 --- a/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/AccelerationProfileSettingsViewModel.cs @@ -27,7 +27,7 @@ public AccelerationProfileSettingsViewModel(BE.AccelerationModel accelerationBE) AnisotropySettings = new AnisotropyProfileSettingsViewModel(accelerationBE.Anisotropy); CoalescionSettings = new CoalescionProfileSettingsViewModel(accelerationBE.Coalescion); // TODO: editable settings composition - //AccelerationBE.DefinitionType.AutoUpdateFromInterface = true; + AccelerationBE.DefinitionType.AutoUpdateFromInterface = true; AccelerationBE.DefinitionType.PropertyChanged += OnDefinitionTypeChanged; } diff --git a/userinterface/Views/AccelerationProfileSettingsView.axaml b/userinterface/Views/AccelerationProfileSettingsView.axaml index 30ab3c47..851f84a2 100644 --- a/userinterface/Views/AccelerationProfileSettingsView.axaml +++ b/userinterface/Views/AccelerationProfileSettingsView.axaml @@ -29,8 +29,8 @@ - + SelectedIndex="{Binding AccelerationBE.DefinitionType.ModelValue}"> + diff --git a/userspace-backend/Model/EditableSettings/IEditableSetting.cs b/userspace-backend/Model/EditableSettings/IEditableSetting.cs index 47373ce7..ad2f80bd 100644 --- a/userspace-backend/Model/EditableSettings/IEditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/IEditableSetting.cs @@ -11,6 +11,8 @@ public interface IEditableSetting : INotifyPropertyChanged bool HasChanged(); + bool AutoUpdateFromInterface { get; set; } + bool TryUpdateFromInterface(); } From 9a0df61e03790836929079cdeb5c59dcf5041387 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Wed, 2 Apr 2025 14:50:31 -0700 Subject: [PATCH 12/47] Updating DI defns --- .../Model/AccelDefinitions/AccelDefinitionModel.cs | 4 +--- .../Formula/ClassicAccelerationDefinitionModel.cs | 6 ++---- .../Model/EditableSettings/EditableSettingsList.cs | 3 --- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs index 6c437f27..13851fb0 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs @@ -8,10 +8,8 @@ namespace userspace_backend.Model.AccelDefinitions { - public interface IAccelDefinitionModel : IEditableSettingsCollection + public interface IAccelDefinitionModel : IEditableSettingsCollectionSpecific { - Acceleration MapToData(); - AccelArgs MapToDriver(); } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index 5ef7648d..ec9c34e6 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -1,14 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { + public class ClassicAccelerationDefinitionModel : AccelDefinitionModel { public ClassicAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs index 68bf1979..5457403f 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs @@ -2,9 +2,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace userspace_backend.Model.EditableSettings { From 453af711b802041938534bec90396f75f6a57f6b Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Fri, 4 Jul 2025 11:07:20 -0700 Subject: [PATCH 13/47] tests pass after merge --- userinterface/Views/Device/DeviceGroupSelectorView.axaml | 2 +- userinterface/Views/Mapping/MappingView.axaml | 8 ++++---- .../Model/AccelDefinitions/AccelerationModel.cs | 2 +- .../Formula/ClassicAccelerationDefinitionModel.cs | 4 ++-- .../Formula/JumpAccelerationDefinitionModel.cs | 4 ++-- .../Formula/LinearAccelerationDefinitionModel.cs | 4 ++-- .../Formula/NaturalAccelerationDefinitionModel.cs | 4 ++-- .../Formula/PowerAccelerationDefinitionModel.cs | 4 ++-- .../Formula/SynchronousAccelerationDefinitionModel.cs | 4 ++-- .../Model/AccelDefinitions/FormulaAccelModel.cs | 2 +- .../Model/AccelDefinitions/LookupTableDefinitionModel.cs | 4 ++-- .../Model/AccelDefinitions/NoAccelDefinitionModel.cs | 4 ++-- userspace-backend/Model/DeviceGroups.cs | 2 +- userspace-backend/Model/DeviceModel.cs | 2 +- .../Model/EditableSettings/EditableSettingsCollection.cs | 6 +++--- userspace-backend/Model/MappingModel.cs | 2 +- userspace-backend/Model/MappingsModel.cs | 2 +- .../Model/ProfileComponents/AnisotropyModel.cs | 4 ++-- .../Model/ProfileComponents/CoalescionModel.cs | 4 ++-- userspace-backend/Model/ProfileComponents/HiddenModel.cs | 4 ++-- userspace-backend/Model/ProfileModel.cs | 2 +- userspace-backend/Model/ProfilesModel.cs | 2 +- 22 files changed, 38 insertions(+), 38 deletions(-) diff --git a/userinterface/Views/Device/DeviceGroupSelectorView.axaml b/userinterface/Views/Device/DeviceGroupSelectorView.axaml index d9afab00..832dd896 100644 --- a/userinterface/Views/Device/DeviceGroupSelectorView.axaml +++ b/userinterface/Views/Device/DeviceGroupSelectorView.axaml @@ -113,7 +113,7 @@ Margin="12,0,0,0"> - + diff --git a/userinterface/Views/Mapping/MappingView.axaml b/userinterface/Views/Mapping/MappingView.axaml index 072155b0..2796c96a 100644 --- a/userinterface/Views/Mapping/MappingView.axaml +++ b/userinterface/Views/Mapping/MappingView.axaml @@ -132,7 +132,7 @@ @@ -155,7 +155,7 @@ BorderThickness="0"> - + @@ -173,7 +173,7 @@ + Text="{Binding DeviceGroup.InterfaceValue}"/> - + diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index ea897ab0..480def82 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -73,7 +73,7 @@ protected override IEnumerable EnumerateEditableSettings() return [DefinitionType]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return [DefinitionModels[DefinitionType.ModelValue]]; } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index ec9c34e6..34f29a85 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -51,9 +51,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ Acceleration, Exponent, Offset, Cap ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override ClassicAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs index 04520ba8..031ecc75 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs @@ -46,9 +46,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ Smooth, Input, Output ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override JumpAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs index 09b75f48..b2666224 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs @@ -49,9 +49,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ Acceleration, Offset, Cap ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override LinearAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs index 2fa78ec3..8dd34d06 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs @@ -46,9 +46,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ DecayRate, InputOffset, Limit ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override NaturalAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs index a6861edc..7498a5b1 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs @@ -46,9 +46,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ Scale, Exponent, OutputOffset, Cap ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override PowerAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs index 90cc9488..5c439a7d 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs @@ -48,9 +48,9 @@ protected override IEnumerable EnumerateEditableSettings() return [Gamma, Motivity, SyncSpeed, Smoothness]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override SynchronousAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs index 0126b4c5..c1e16cbb 100644 --- a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs +++ b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs @@ -55,7 +55,7 @@ protected override IEnumerable EnumerateEditableSettings() return [ FormulaType ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return [ FormulaModels[FormulaType.ModelValue] ]; } diff --git a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs index 16df77f6..f814d313 100644 --- a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs @@ -48,9 +48,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ApplyAs, Data]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override LookupTableAccel GenerateDefaultDataObject() diff --git a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs index f1f21c9f..956601f9 100644 --- a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs @@ -36,9 +36,9 @@ protected override IEnumerable EnumerateEditableSettings() return Enumerable.Empty(); } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override NoAcceleration GenerateDefaultDataObject() diff --git a/userspace-backend/Model/DeviceGroups.cs b/userspace-backend/Model/DeviceGroups.cs index cf5d02cc..3ec2c86c 100644 --- a/userspace-backend/Model/DeviceGroups.cs +++ b/userspace-backend/Model/DeviceGroups.cs @@ -95,7 +95,7 @@ protected override IEnumerable EnumerateEditableSettings() return DeviceGroupModels; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return []; } diff --git a/userspace-backend/Model/DeviceModel.cs b/userspace-backend/Model/DeviceModel.cs index b5eef665..d422a32a 100644 --- a/userspace-backend/Model/DeviceModel.cs +++ b/userspace-backend/Model/DeviceModel.cs @@ -42,7 +42,7 @@ protected override IEnumerable EnumerateEditableSettings() return [Name, HardwareID, DPI, PollRate, Ignore, DeviceGroup]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return []; } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs index 55c44415..a4e076df 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs @@ -6,7 +6,7 @@ namespace userspace_backend.Model.EditableSettings { - public abstract class EditableSettingsCollection : ObservableObject, IEditableSettingsCollection + public abstract class EditableSettingsCollection : ObservableObject, IEditableSettingsCollectionV2 { public EditableSettingsCollection(T dataObject) { @@ -19,7 +19,7 @@ public EditableSettingsCollection(T dataObject) public IEnumerable AllContainedEditableSettings { get; set; } - public IEnumerable AllContainedEditableSettingsCollections { get; set; } + public IEnumerable AllContainedEditableSettingsCollections { get; set; } public bool HasChanged { get; protected set; } @@ -83,7 +83,7 @@ protected void OnAnySettingChanged() protected abstract IEnumerable EnumerateEditableSettings(); - protected abstract IEnumerable EnumerateEditableSettingsCollections(); + protected abstract IEnumerable EnumerateEditableSettingsCollections(); public abstract T MapToData(); } diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index 9c713274..736f3348 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -68,7 +68,7 @@ protected override IEnumerable EnumerateEditableSettings() return []; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return []; } diff --git a/userspace-backend/Model/MappingsModel.cs b/userspace-backend/Model/MappingsModel.cs index 9c56c74b..2c277e61 100644 --- a/userspace-backend/Model/MappingsModel.cs +++ b/userspace-backend/Model/MappingsModel.cs @@ -102,7 +102,7 @@ protected override IEnumerable EnumerateEditableSettings() return []; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return Mappings; } diff --git a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs index 6ce06512..c6eb6dee 100644 --- a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs +++ b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs @@ -41,9 +41,9 @@ protected override IEnumerable EnumerateEditableSettings() return [DomainX, DomainY, RangeX, RangeY, LPNorm]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override void InitEditableSettingsAndCollections(Anisotropy dataObject) diff --git a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs index 20df72d6..27f8cfa8 100644 --- a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs +++ b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs @@ -32,9 +32,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ InputSmoothingHalfLife, ScaleSmoothingHalfLife ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return []; } protected override void InitEditableSettingsAndCollections(Coalescion dataObject) diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index ac30a099..24ce60e5 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -44,9 +44,9 @@ protected override IEnumerable EnumerateEditableSettings() return [ RotationDegrees, AngleSnappingDegrees, LeftRightRatio, UpDownRatio, SpeedCap ]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { - return Enumerable.Empty(); + return Enumerable.Empty(); } protected override void InitEditableSettingsAndCollections(Hidden dataObject) diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index 3c96f5a7..ce4b0796 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -89,7 +89,7 @@ protected override IEnumerable EnumerateEditableSettings() return [Name, OutputDPI, YXRatio]; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return [Acceleration, Hidden]; } diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index bf82c0c2..423352e9 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -32,7 +32,7 @@ protected override IEnumerable EnumerateEditableSettings() return []; } - protected override IEnumerable EnumerateEditableSettingsCollections() + protected override IEnumerable EnumerateEditableSettingsCollections() { return Profiles; } From 34bb91197657dd81b81543d249ca0586958bac0c Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Fri, 4 Jul 2025 13:24:03 -0700 Subject: [PATCH 14/47] Profile and hidden models converted to DI --- .../ModelTests/EditableSettingsListTests.cs | 50 +-------- userspace-backend/BackEndComposer.cs | 49 +++++++++ .../AccelDefinitions/AccelerationModel.cs | 6 +- .../EditableSettings/EditableSettingsList.cs | 12 +- .../Model/ProfileComponents/HiddenModel.cs | 69 ++++-------- userspace-backend/Model/ProfileModel.cs | 40 ++++--- userspace-backend/Model/ProfilesModel.cs | 103 ++---------------- 7 files changed, 126 insertions(+), 203 deletions(-) diff --git a/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs index fb86a08d..94b23ee8 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs @@ -21,13 +21,6 @@ public class TestData public int Property { get; set; } } - public class TestDataList - { - public TestData[] Data { get; set; } - - public int OtherProperty { get; set; } - } - public interface IEditableSettingsTestCollection : IEditableSettingsCollectionSpecific { public IEditableSettingSpecific NameSetting { get; } @@ -35,9 +28,8 @@ public interface IEditableSettingsTestCollection : IEditableSettingsCollectionSp public IEditableSettingSpecific PropertySetting { get; } } - public interface IEditableSettingsTestList : IEditableSettingsList + public interface IEditableSettingsTestList : IEditableSettingsList { - public IEditableSettingSpecific OtherPropertySetting { get; } } public class EditableSettingsTestCollection : EditableSettingsCollectionV2, IEditableSettingsTestCollection @@ -68,29 +60,20 @@ public override TestData MapToData() } } - public class EditableSettingsTestList : EditableSettingsList, IEditableSettingsTestList + public class EditableSettingsTestList : EditableSettingsList, IEditableSettingsTestList { - public const string OtherProperySettingName = $"{nameof(EditableSettingsTestCollection)}.{nameof(OtherPropertySetting)}"; public const string TestNameTemplate = "TestData"; - public EditableSettingsTestList([FromKeyedServices(OtherProperySettingName)]IEditableSettingSpecific otherPropertySetting, - IServiceProvider serviceProvider) - : base(serviceProvider, [otherPropertySetting], []) + public EditableSettingsTestList(IServiceProvider serviceProvider) + : base(serviceProvider, [], []) { - OtherPropertySetting = otherPropertySetting; } - public IEditableSettingSpecific OtherPropertySetting { get; } - protected override string DefaultNameTemplate => TestNameTemplate; - public override TestDataList MapToData() + public override IEnumerable MapToData() { - return new TestDataList() - { - Data = ElementsInternal.Select(e => e.MapToData()).ToArray(), - OtherProperty = OtherPropertySetting.ModelValue, - }; + return ElementsInternal.Select(e => e.MapToData()); } protected override string GetNameFromElement(IEditableSettingsTestCollection element) => element.NameSetting.ModelValue; @@ -107,8 +90,6 @@ protected override void SetElementName(IEditableSettingsTestCollection element, #region InitDI public (IEditableSettingsTestList, IServiceProvider) InitTestObject( - string otherPropertyName, - int otherPropertyValue, string propertyName, int propertyValue, string nameName, @@ -116,14 +97,6 @@ protected override void SetElementName(IEditableSettingsTestCollection element, { ServiceCollection services = new ServiceCollection(); services.AddTransient(); - services.AddKeyedTransient>( - EditableSettingsTestList.OtherProperySettingName, (_, _) => - new EditableSettingV2( - otherPropertyName, - otherPropertyValue, - UserInputParsers.IntParser, - ModelValueValidators.DefaultIntValidator, - autoUpdateFromInterface: false)); services.AddTransient(); services.AddKeyedTransient>( EditableSettingsTestCollection.ProperySettingName, (_, _) => @@ -154,40 +127,29 @@ protected override void SetElementName(IEditableSettingsTestCollection element, [TestMethod] public void EditableSettingsList_Construction() { - string otherPropertyName = "Other Property"; - int otherPropertyInitialValue = 1; string propertyName = "Property"; int propertyInitialValue = 2; string nameName = "Name"; string nameInitialValue = "My test data list"; (IEditableSettingsTestList testObject, _) = InitTestObject( - otherPropertyName, - otherPropertyInitialValue, propertyName, propertyInitialValue, nameName, nameInitialValue); Assert.IsNotNull(testObject); - Assert.IsNotNull(testObject.OtherPropertySetting); Assert.IsNotNull(testObject.Elements); Assert.AreEqual(0, testObject.Elements.Count); - Assert.AreEqual(otherPropertyName, testObject.OtherPropertySetting.DisplayName); - Assert.AreEqual(otherPropertyInitialValue, testObject.OtherPropertySetting.ModelValue); } [TestMethod] public void EditableSettingsList_AddRemoveElements() { - string otherPropertyName = "Other Property"; - int otherPropertyInitialValue = 1; string propertyName = "Property"; int propertyInitialValue = 2; string nameName = "Name"; string nameInitialValue = "My test data list"; (IEditableSettingsTestList testObject, IServiceProvider serviceProvider) = InitTestObject( - otherPropertyName, - otherPropertyInitialValue, propertyName, propertyInitialValue, nameName, diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index feddb85a..0f7bed45 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.DependencyInjection; using System; using userspace_backend.Model; +using userspace_backend.Model.EditableSettings; +using userspace_backend.Model.ProfileComponents; namespace userspace_backend { @@ -10,6 +12,53 @@ public static IServiceProvider Compose(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); + + // Hidden + services.AddTransient(); + services.AddKeyedTransient>( + HiddenModel.RotationDegreesDIKey, (_, _) => + new EditableSettingV2( + displayName: "Rotation", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + HiddenModel.AngleSnappingDegreesDIKey, (_, _) => + new EditableSettingV2( + displayName: "Angle Snapping", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + HiddenModel.LeftRightRatioDIKey, (_, _) => + new EditableSettingV2( + displayName: "L/R Ratio", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + HiddenModel.UpDownRatioDIKey, (_, _) => + new EditableSettingV2( + displayName: "U/D Ratio", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + HiddenModel.SpeedCapDIKey, (_, _) => + new EditableSettingV2( + displayName: "Speed Cap", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + HiddenModel.OutputSmoothingHalfLifeDIKey, (_, _) => + new EditableSettingV2( + displayName: "Output Smoothing Half-Life", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + return services.BuildServiceProvider(); } } diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index 480def82..6c93f94e 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -8,7 +8,11 @@ namespace userspace_backend.Model.AccelDefinitions { - public class AccelerationModel : EditableSettingsCollection + public interface IAccelerationModel : IEditableSettingsCollectionSpecific + { + } + + public class AccelerationModel : IAccelerationModel { public AccelerationModel(Acceleration dataObject) : base(dataObject) { diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs index 5457403f..cb6d3ee8 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs @@ -2,10 +2,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; namespace userspace_backend.Model.EditableSettings { - public interface IEditableSettingsList : IEditableSettingsCollectionSpecific where T : class, IEditableSettingsCollectionV2 + public interface IEditableSettingsList + : IEditableSettingsCollectionSpecific> where T : class, IEditableSettingsCollectionSpecific { public ReadOnlyObservableCollection Elements { get; } @@ -18,7 +20,8 @@ public interface IEditableSettingsList : IEditableSettingsCollectionSpecif public bool TryRemoveElement(T element); } - public abstract class EditableSettingsList : EditableSettingsCollectionV2, IEditableSettingsList where T : class, IEditableSettingsCollectionV2 + public abstract class EditableSettingsList + : EditableSettingsCollectionV2>, IEditableSettingsList where T : class, IEditableSettingsCollectionSpecific { public EditableSettingsList( IServiceProvider serviceProvider, @@ -39,6 +42,11 @@ public EditableSettingsList( protected abstract string DefaultNameTemplate { get; } + public override IEnumerable MapToData() + { + return ElementsInternal.Select(e => e.MapToData()); + } + public bool TryAdd(T element) { string name = GetNameFromElement(element); diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index 24ce60e5..caedfdcc 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,9 +9,27 @@ namespace userspace_backend.Model.ProfileComponents { - public class HiddenModel : EditableSettingsCollection + public interface IHiddenModel : IEditableSettingsCollectionSpecific { - public HiddenModel(Hidden dataObject) : base(dataObject) + } + + public class HiddenModel : EditableSettingsCollectionV2, IHiddenModel + { + public const string RotationDegreesDIKey = $"{nameof(HiddenModel)}.{nameof(RotationDegrees)}"; + public const string AngleSnappingDegreesDIKey = $"{nameof(HiddenModel)}.{nameof(AngleSnappingDegrees)}"; + public const string LeftRightRatioDIKey = $"{nameof(HiddenModel)}.{nameof(LeftRightRatio)}"; + public const string UpDownRatioDIKey = $"{nameof(HiddenModel)}.{nameof(UpDownRatio)}"; + public const string SpeedCapDIKey = $"{nameof(HiddenModel)}.{nameof(SpeedCap)}"; + public const string OutputSmoothingHalfLifeDIKey = $"{nameof(HiddenModel)}.{nameof(OutputSmoothingHalfLife)}"; + + public HiddenModel( + [FromKeyedServices(RotationDegreesDIKey)]IEditableSettingSpecific rotationDegrees, + [FromKeyedServices(AngleSnappingDegreesDIKey)]IEditableSettingSpecific angleSnappingDegrees, + [FromKeyedServices(LeftRightRatioDIKey)]IEditableSettingSpecific leftRightRatio, + [FromKeyedServices(UpDownRatioDIKey)]IEditableSettingSpecific upDownRatio, + [FromKeyedServices(SpeedCapDIKey)]IEditableSettingSpecific speedCap, + [FromKeyedServices(OutputSmoothingHalfLifeDIKey)]IEditableSettingSpecific outputSmoothingHalfLife + ) : base([rotationDegrees, angleSnappingDegrees, leftRightRatio, upDownRatio, speedCap, outputSmoothingHalfLife], []) { } @@ -38,49 +57,5 @@ public override Hidden MapToData() OutputSmoothingHalfLife = OutputSmoothingHalfLife.ModelValue, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ RotationDegrees, AngleSnappingDegrees, LeftRightRatio, UpDownRatio, SpeedCap ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return Enumerable.Empty(); - } - - protected override void InitEditableSettingsAndCollections(Hidden dataObject) - { - RotationDegrees = new EditableSetting( - displayName: "Rotation", - initialValue: dataObject?.RotationDegrees ?? 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - AngleSnappingDegrees = new EditableSetting( - displayName: "Angle Snapping", - initialValue: dataObject?.AngleSnappingDegrees ?? 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - LeftRightRatio = new EditableSetting( - displayName: "L/R Ratio", - initialValue: dataObject?.LeftRightRatio ?? 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - UpDownRatio = new EditableSetting( - displayName: "U/D Ratio", - initialValue: dataObject?.UpDownRatio ?? 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - SpeedCap = new EditableSetting( - displayName: "Speed Cap", - initialValue: dataObject?.SpeedCap ?? 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - OutputSmoothingHalfLife = new EditableSetting( - displayName: "Output Smoothing Half-Life", - initialValue: dataObject?.OutputSmoothingHalfLife ?? 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index ce4b0796..eb21fea8 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -12,11 +12,30 @@ namespace userspace_backend.Model { - public class ProfileModel : EditableSettingsCollection + public class ProfileModel : EditableSettingsCollectionV2 { - public ProfileModel(DATA.Profile dataObject, IModelValueValidator nameValidator) : base(dataObject) + public ProfileModel( + IEditableSettingSpecific name, + IEditableSettingSpecific outputDPI, + IEditableSettingSpecific yxRatio, + IAccelerationModel acceleration, + IHiddenModel hidden + ) : base([name, outputDPI, yxRatio], [acceleration, hidden]) { - NameValidator = nameValidator; + Name = name; + OutputDPI = outputDPI; + YXRatio = yxRatio; + Acceleration = acceleration; + Hidden = hidden; + // Name and Output DPI do not need to generate a new curve preview + Name.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; + OutputDPI.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; + + // The rest of settings should generate a new curve preview + YXRatio.PropertyChanged += AnyCurvePreviewPropertyChangedEventHandler; + Acceleration.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; + Hidden.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; + CurvePreview = new CurvePreview(); RecalculateDriverDataAndCurvePreview(); } @@ -29,9 +48,9 @@ public ProfileModel(DATA.Profile dataObject, IModelValueValidator nameVa public IEditableSettingSpecific YXRatio { get; set; } - public AccelerationModel Acceleration { get; set; } + public IAccelerationModel Acceleration { get; set; } - public HiddenModel Hidden { get; set; } + public IHiddenModel Hidden { get; set; } public Profile CurrentValidatedDriverProfile { get; protected set; } @@ -84,16 +103,7 @@ protected void RecalculateDriverDataAndCurvePreview() CurvePreview.GeneratePoints(CurrentValidatedDriverProfile); } - protected override IEnumerable EnumerateEditableSettings() - { - return [Name, OutputDPI, YXRatio]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return [Acceleration, Hidden]; - } - + // TODO: DI - Add init to composition protected override void InitEditableSettingsAndCollections(DATA.Profile dataObject) { Name = new EditableSetting( diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 423352e9..f509154a 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -8,113 +8,28 @@ namespace userspace_backend.Model { - public class ProfilesModel : EditableSettingsCollection> + public class ProfilesModel : EditableSettingsList { + // TODO: DI - hand default profile to profiles model public static readonly ProfileModel DefaultProfile = new ProfileModel( GenerateNewDefaultProfile("Default"), ModelValueValidators.AllChangesInvalidStringValidator); - public ProfilesModel(IEnumerable dataObject) : base(dataObject) + public ProfilesModel(IServiceProvider serviceProvider) + : base(serviceProvider, [], []) { - NameValidator = new ProfileNameValidator(this); } - public ObservableCollection Profiles { get; protected set; } - protected ProfileNameValidator NameValidator { get; } + protected override string DefaultNameTemplate => "Profile"; - public override IEnumerable MapToData() + protected override string GetNameFromElement(ProfileModel element) { - return Profiles.Select(p => p.MapToData()); + return element.Name.ModelValue; } - protected override IEnumerable EnumerateEditableSettings() + protected override void SetElementName(ProfileModel element, string name) { - return []; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return Profiles; - } - - protected override void InitEditableSettingsAndCollections(IEnumerable dataObject) - { - Profiles = new ObservableCollection() { DefaultProfile, }; - } - - public bool TryGetProfile(string name, out ProfileModel? profileModel) - { - profileModel = Profiles.FirstOrDefault( - p => string.Equals(p.Name.ModelValue, name, StringComparison.InvariantCultureIgnoreCase)); - - return profileModel != null; - } - - public bool TryAddNewDefaultProfile(string name) - { - if (TryGetProfile(name, out _)) - { - return false; - } - - DATA.Profile profile = GenerateNewDefaultProfile(name); - ProfileModel profileModel = new ProfileModel(profile, NameValidator); - Profiles.Add(profileModel); - return true; - } - - public bool TryAddProfile(DATA.Profile profileToAdd) - { - if (TryGetProfile(profileToAdd.Name, out _)) - { - return false; - } - - ProfileModel profileModel = new ProfileModel(profileToAdd, NameValidator); - Profiles.Add(profileModel); - return true; - } - - protected bool TryCreateNewDefaultProfile([MaybeNullWhen(false)] out DATA.Profile newDefaultProfile) - { - for (int i = 0; i < 10; i++) - { - string newProfileName = $"Profile{i}"; - - if (!TryGetProfile(newProfileName, out _)) - { - newDefaultProfile = GenerateNewDefaultProfile(newProfileName); - return false; - } - } - - newDefaultProfile = null; - return false; - } - - public bool RemoveProfile(ProfileModel profile) - { - return Profiles.Remove(profile); - } - - protected static DATA.Profile GenerateNewDefaultProfile(string name) - { - return new DATA.Profile() - { - Name = name, - OutputDPI = 1000, - YXRatio = 1, - }; - } - } - - public class ProfileNameValidator(ProfilesModel profilesModel) : IModelValueValidator - { - ProfilesModel ProfilesModel { get; } = profilesModel; - - public bool Validate(string value) - { - return !ProfilesModel.TryGetProfile(value, out _); + element.Name.InterfaceValue = name; } } } From 5c8ea316d15ea05a2c14d32f94582dfe1af693da Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Fri, 4 Jul 2025 14:07:50 -0700 Subject: [PATCH 15/47] Coalescion and anisotropy to DI plus some accel --- userspace-backend/BackEndComposer.cs | 61 +++++++++++++++ .../AccelDefinitions/AccelerationModel.cs | 19 +++-- .../AccelerationSelectorModel.cs | 12 +++ .../EditableSettingsSelector.cs | 1 - .../ProfileComponents/AnisotropyModel.cs | 78 +++++++------------ .../ProfileComponents/CoalescionModel.cs | 46 ++++------- .../Model/ProfileComponents/HiddenModel.cs | 5 -- userspace-backend/Model/ProfileModel.cs | 2 + 8 files changed, 129 insertions(+), 95 deletions(-) create mode 100644 userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 0f7bed45..9ec1dfa9 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -58,6 +58,67 @@ public static IServiceProvider Compose(IServiceCollection services) parser: UserInputParsers.DoubleParser, validator: ModelValueValidators.DefaultDoubleValidator)); + // Coalescion + services.AddTransient(); + services.AddKeyedTransient>( + CoalescionModel.InputSmoothingHalfLifeDIKey, (_, _) => + new EditableSettingV2( + displayName: "Input Smoothing Half-Life", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + CoalescionModel.ScaleSmoothingHalfLifeDIKey, (_, _) => + new EditableSettingV2( + displayName: "Scale Smoothing Half-Life", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + // Anisotropy + services.AddTransient(); + services.AddKeyedTransient>( + AnisotropyModel.DomainXDIKey, (_, _) => + new EditableSettingV2( + displayName: "Domain X", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + AnisotropyModel.DomainYDIKey, (_, _) => + new EditableSettingV2( + displayName: "Domain Y", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + AnisotropyModel.RangeXDIKey, (_, _) => + new EditableSettingV2( + displayName: "Range X", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + AnisotropyModel.RangeYDIKey, (_, _) => + new EditableSettingV2( + displayName: "Range Y", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + AnisotropyModel.LPNormDIKey, (_, _) => + new EditableSettingV2( + displayName: "LP Norm", + initialValue: 2, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + AnisotropyModel.CombineXYComponentsDIKey, (_, _) => + new EditableSettingV2( + displayName: "Combine X and Y Components", + initialValue: false, + parser: UserInputParsers.BoolParser, + validator: ModelValueValidators.DefaultBoolValidator)); return services.BuildServiceProvider(); } diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index 6c93f94e..26dc5060 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -12,20 +12,25 @@ public interface IAccelerationModel : IEditableSettingsCollectionSpecific, IAccelerationModel { - public AccelerationModel(Acceleration dataObject) : base(dataObject) + public AccelerationModel( + IEditableSettingSpecific definitionType, + IServiceProvider serviceProvider, + IAnisotropyModel anisotropy, + ICoalescionModel coalescion) + : base(definitionType, serviceProvider, [], [anisotropy, coalescion]) { - DefinitionType.PropertyChanged += DefinitionTypeChangedEventHandler; + Anisotropy = anisotropy; + Coalescion = coalescion; + Selection.PropertyChanged += DefinitionTypeChangedEventHandler; } - public IEditableSettingSpecific DefinitionType { get; set; } - protected Dictionary DefinitionModels { get; set; } - public AnisotropyModel Anisotropy { get; set; } + public IAnisotropyModel Anisotropy { get; set; } - public CoalescionModel Coalescion { get; set; } + public ICoalescionModel Coalescion { get; set; } public FormulaAccelModel FormulaAccel { diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs new file mode 100644 index 00000000..04178a7a --- /dev/null +++ b/userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace userspace_backend.Model.AccelDefinitions +{ + public class AccelerationSelectorModel + { + } +} diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs index be244a30..dda5e566 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs @@ -56,6 +56,5 @@ public override U MapToData() { return GetSelectable(Selection.ModelValue).MapToData(); } - } } diff --git a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs index c6eb6dee..1ec03bb3 100644 --- a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs +++ b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs @@ -1,17 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.ProfileComponents { - public class AnisotropyModel : EditableSettingsCollection + public interface IAnisotropyModel : IEditableSettingsCollectionSpecific { - public AnisotropyModel(Anisotropy dataObject) : base(dataObject) + } + + public class AnisotropyModel : EditableSettingsCollectionV2, IAnisotropyModel + { + public const string DomainXDIKey = $"{nameof(AnisotropyModel)}.{nameof(DomainX)}"; + public const string DomainYDIKey = $"{nameof(AnisotropyModel)}.{nameof(DomainY)}"; + public const string RangeXDIKey = $"{nameof(AnisotropyModel)}.{nameof(RangeX)}"; + public const string RangeYDIKey = $"{nameof(AnisotropyModel)}.{nameof(RangeYDIKey)}"; + public const string LPNormDIKey = $"{nameof(AnisotropyModel)}.{nameof(LPNorm)}"; + public const string CombineXYComponentsDIKey = $"{nameof(AnisotropyModel)}.{nameof(CombineXYComponents)}"; + + public AnisotropyModel( + [FromKeyedServices(DomainXDIKey)]IEditableSettingSpecific domainX, + [FromKeyedServices(DomainYDIKey)]IEditableSettingSpecific domainY, + [FromKeyedServices(RangeXDIKey)]IEditableSettingSpecific rangeX, + [FromKeyedServices(RangeYDIKey)]IEditableSettingSpecific rangeY, + [FromKeyedServices(LPNormDIKey)]IEditableSettingSpecific lpNorm, + [FromKeyedServices(CombineXYComponentsDIKey)]IEditableSettingSpecific combineXYComponents + ) : base([domainX, domainY, rangeX, rangeY, lpNorm, combineXYComponents], []) { + DomainX = domainX; + DomainY = domainY; + RangeX = rangeX; + RangeY = rangeY; + LPNorm = lpNorm; + CombineXYComponents = combineXYComponents; } public IEditableSettingSpecific DomainX { get; set; } @@ -35,49 +55,5 @@ public override Anisotropy MapToData() LPNorm = LPNorm.ModelValue, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [DomainX, DomainY, RangeX, RangeY, LPNorm]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override void InitEditableSettingsAndCollections(Anisotropy dataObject) - { - DomainX = new EditableSetting( - displayName: "Domain X", - initialValue: dataObject?.Domain?.X ?? 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - DomainY = new EditableSetting( - displayName: "Domain Y", - initialValue: dataObject?.Domain?.Y ?? 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - RangeX = new EditableSetting( - displayName: "Range X", - initialValue: dataObject?.Range?.X ?? 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - RangeY = new EditableSetting( - displayName: "Range Y", - initialValue: dataObject?.Range?.Y ?? 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - LPNorm = new EditableSetting( - displayName: "LP Norm", - initialValue: dataObject?.LPNorm ?? 2, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - CombineXYComponents = new EditableSetting( - displayName: "Combine X and Y Components", - initialValue: dataObject?.CombineXYComponents ?? false, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator); - } } } diff --git a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs index 27f8cfa8..079e2781 100644 --- a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs +++ b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs @@ -1,17 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.ProfileComponents { - public class CoalescionModel : EditableSettingsCollection + public interface ICoalescionModel : IEditableSettingsCollectionSpecific { - public CoalescionModel(Coalescion dataObject) : base(dataObject) + } + + public class CoalescionModel : EditableSettingsCollectionV2, ICoalescionModel + { + public const string InputSmoothingHalfLifeDIKey = $"{nameof(CoalescionModel)}.{nameof(InputSmoothingHalfLife)}"; + public const string ScaleSmoothingHalfLifeDIKey = $"{nameof(CoalescionModel)}.{nameof(ScaleSmoothingHalfLife)}"; + + public CoalescionModel( + [FromKeyedServices(InputSmoothingHalfLifeDIKey)]IEditableSettingSpecific inputSmoothingHalfLife, + [FromKeyedServices(ScaleSmoothingHalfLifeDIKey)]IEditableSettingSpecific scaleSmoothHalfLife) + : base([inputSmoothingHalfLife, scaleSmoothHalfLife], []) { + InputSmoothingHalfLife = inputSmoothingHalfLife; + ScaleSmoothingHalfLife = scaleSmoothHalfLife; } public IEditableSettingSpecific InputSmoothingHalfLife { get; set; } @@ -26,29 +34,5 @@ public override Coalescion MapToData() ScaleSmoothingHalfLife = ScaleSmoothingHalfLife.ModelValue, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ InputSmoothingHalfLife, ScaleSmoothingHalfLife ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override void InitEditableSettingsAndCollections(Coalescion dataObject) - { - InputSmoothingHalfLife = new EditableSetting( - displayName: "Input Smoothing Half-Life", - initialValue: dataObject?.InputSmoothingHalfLife ?? 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - ScaleSmoothingHalfLife = new EditableSetting( - displayName: "Scale Smoothing Half-Life", - initialValue: dataObject?.ScaleSmoothingHalfLife ?? 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index caedfdcc..645756ab 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -1,9 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Data.Profiles; using userspace_backend.Model.EditableSettings; diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index eb21fea8..ca484241 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -27,6 +27,7 @@ IHiddenModel hidden YXRatio = yxRatio; Acceleration = acceleration; Hidden = hidden; + // Name and Output DPI do not need to generate a new curve preview Name.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; OutputDPI.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; @@ -36,6 +37,7 @@ IHiddenModel hidden Acceleration.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; Hidden.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; + // TODO: DI - Curve preview to DI CurvePreview = new CurvePreview(); RecalculateDriverDataAndCurvePreview(); } From a72e0ef0860a5afcf355ed2e0eefaae399ff1653 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 5 Jul 2025 12:51:32 -0700 Subject: [PATCH 16/47] Most of profile and accel changed to use DI --- .../ViewModels/Mapping/MappingViewModel.cs | 4 +- .../EditableSettingsSelectorTests.cs | 8 +- userspace-backend/BackEnd.cs | 6 +- userspace-backend/BackEndComposer.cs | 291 +++++++++++++++++- .../Accel/Formula/SynchronousAccel.cs | 4 +- .../Data/Profiles/Acceleration.cs | 4 +- .../AccelDefinitions/AccelDefinitionModel.cs | 21 +- .../AccelDefinitions/AccelerationModel.cs | 103 ++----- .../AccelerationSelectorModel.cs | 12 - .../ClassicAccelerationDefinitionModel.cs | 76 ++--- .../JumpAccelerationDefinitionModel.cs | 69 ++--- .../LinearAccelerationDefinitionModel.cs | 69 ++--- .../NaturalAccelerationDefinitionModel.cs | 70 ++--- .../PowerAccelerationDefinitionModel.cs | 79 ++--- .../SynchronousAccelerationDefinitionModel.cs | 83 ++--- .../AccelDefinitions/FormulaAccelModel.cs | 108 +------ .../LookupTableDefinitionModel.cs | 58 ++-- .../NoAccelDefinitionModel.cs | 35 +-- .../EditableSettingsSelector.cs | 6 +- userspace-backend/Model/MappingModel.cs | 2 +- .../ProfileComponents/AnisotropyModel.cs | 11 + .../ProfileComponents/CoalescionModel.cs | 3 + .../Model/ProfileComponents/HiddenModel.cs | 11 + userspace-backend/Model/ProfileModel.cs | 48 +-- userspace-backend/Model/ProfilesModel.cs | 4 +- 25 files changed, 554 insertions(+), 631 deletions(-) delete mode 100644 userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs diff --git a/userinterface/ViewModels/Mapping/MappingViewModel.cs b/userinterface/ViewModels/Mapping/MappingViewModel.cs index 69a01001..2516b7ff 100644 --- a/userinterface/ViewModels/Mapping/MappingViewModel.cs +++ b/userinterface/ViewModels/Mapping/MappingViewModel.cs @@ -23,7 +23,9 @@ public void HandleAddMappingSelection(SelectionChangedEventArgs e) { if (e.AddedItems.Count > 0 && e.AddedItems[0] is BE.DeviceGroupModel deviceGroup) { - MappingBE.TryAddMapping(deviceGroup.ModelValue, BE.ProfilesModel.DefaultProfile.CurrentNameForDisplay); + // TODO: re-add default profile + // MappingBE.TryAddMapping(deviceGroup.ModelValue, BE.ProfilesModel.DefaultProfile.CurrentNameForDisplay); + MappingBE.TryAddMapping(deviceGroup.ModelValue, "Default"); } } diff --git a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs index acff3801..55e1a49d 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs @@ -119,7 +119,7 @@ public EditableSettingsTestA([FromKeyedServices(PropertyAName)]IEditableSettingS public IEditableSettingSpecific PropertyA { get; } - public override TestDataAbstract MapToData() + public override TestDataA MapToData() { return new TestDataA() { @@ -157,9 +157,9 @@ public class EditableSettingsTestSelector : EditableSettingsSelector< public const string SelectionName = $"{nameof(EditableSettingsTestSelector)}.{nameof(Selection)}"; public EditableSettingsTestSelector( - [FromKeyedServices(SelectionName)]IEditableSettingSpecific selection, - IServiceProvider serviceProvider) - : base(selection, serviceProvider, [], []) + IServiceProvider serviceProvider, + [FromKeyedServices(SelectionName)]IEditableSettingSpecific selection) + : base(serviceProvider, selection, [], []) { } } diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index b8a51d2d..ba1e1bc4 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -25,7 +25,7 @@ public BackEnd(IBackEndLoader backEndLoader) BackEndLoader = backEndLoader; Devices = new DevicesModel(serviceProvider.GetRequiredService()); - Profiles = new ProfilesModel([]); + Profiles = new ProfilesModel(serviceProvider); } public DevicesModel Devices { get; set; } @@ -60,7 +60,7 @@ protected void LoadProfilesFromData(IEnumerable profileData) { foreach (var profile in profileData) { - Profiles.TryAddProfile(profile); + Profiles.TryAdd(profile); } } @@ -83,7 +83,7 @@ protected void WriteSettingsToDisk() BackEndLoader.WriteSettingsToDisk( Devices.DevicesEnumerable, Mappings, - Profiles.Profiles); + Profiles.Elements); } protected void WriteToDriver() diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 9ec1dfa9..964fb729 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -1,8 +1,13 @@ using Microsoft.Extensions.DependencyInjection; using System; using userspace_backend.Model; +using userspace_backend.Model.AccelDefinitions; +using userspace_backend.Model.AccelDefinitions.Formula; using userspace_backend.Model.EditableSettings; using userspace_backend.Model.ProfileComponents; +using static userspace_backend.Data.Profiles.Accel.FormulaAccel; +using static userspace_backend.Data.Profiles.Accel.LookupTableAccel; +using static userspace_backend.Data.Profiles.Acceleration; namespace userspace_backend { @@ -13,7 +18,8 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); - // Hidden + #region Hidden + services.AddTransient(); services.AddKeyedTransient>( HiddenModel.RotationDegreesDIKey, (_, _) => @@ -58,7 +64,10 @@ public static IServiceProvider Compose(IServiceCollection services) parser: UserInputParsers.DoubleParser, validator: ModelValueValidators.DefaultDoubleValidator)); - // Coalescion + #endregion Hidden + + #region Coalescion + services.AddTransient(); services.AddKeyedTransient>( CoalescionModel.InputSmoothingHalfLifeDIKey, (_, _) => @@ -75,7 +84,10 @@ public static IServiceProvider Compose(IServiceCollection services) parser: UserInputParsers.DoubleParser, validator: ModelValueValidators.DefaultDoubleValidator)); - // Anisotropy + #endregion Coalescion + + #region Anisotropy + services.AddTransient(); services.AddKeyedTransient>( AnisotropyModel.DomainXDIKey, (_, _) => @@ -120,6 +132,279 @@ public static IServiceProvider Compose(IServiceCollection services) parser: UserInputParsers.BoolParser, validator: ModelValueValidators.DefaultBoolValidator)); + #endregion Anisotropy + + #region Acceleration + + services.AddTransient(); + services.AddKeyedTransient>( + AccelerationModel.SelectionDIKey, (_, _) => + new EditableSettingV2( + displayName: "Definition Type", + initialValue: AccelerationDefinitionType.None, + parser: UserInputParsers.AccelerationDefinitionTypeParser, + validator: ModelValueValidators.DefaultAccelerationTypeValidator)); + + #endregion Acceleration + + #region FormulaAccel + + services.AddTransient(); + services.AddKeyedTransient>( + FormulaAccelModel.SelectionDIKey, (_, _) => + new EditableSettingV2( + displayName: "Formula Type", + initialValue: AccelerationFormulaType.Synchronous, + parser: UserInputParsers.AccelerationFormulaTypeParser, + validator: ModelValueValidators.DefaultAccelerationFormulaTypeValidator, + autoUpdateFromInterface: true)); + services.AddKeyedTransient>( + FormulaAccelModel.GainDIKey, (_, _) => + new EditableSettingV2( + displayName: "Apply to Gain", + initialValue: false, + parser: UserInputParsers.BoolParser, + validator: ModelValueValidators.DefaultBoolValidator)); + + #endregion FormulaAccel + + #region LookupTable + + services.AddTransient(); + services.AddKeyedTransient>( + LookupTableDefinitionModel.ApplyAsDIKey, (_, _) => + new EditableSettingV2( + displayName: "Apply as", + initialValue: LookupTableType.Velocity, + parser: UserInputParsers.LookupTableTypeParser, + validator: ModelValueValidators.DefaultLookupTableTypeValidator)); + services.AddKeyedTransient>( + LookupTableDefinitionModel.DataDIKey, (_, _) => + new EditableSettingV2( + displayName: "Data", + initialValue: new LookupTableData(), + parser: UserInputParsers.LookupTableDataParser, + validator: ModelValueValidators.DefaultLookupTableDataValidator)); + + #endregion LookupTable + + #region NoAccel + + services.AddTransient(); + + #endregion NoAccel + + #region SynchronousAccel + + services.AddTransient(); + services.AddKeyedTransient>( + SynchronousAccelerationDefinitionModel.SyncSpeedDIKey, (_, _) => + new EditableSettingV2( + displayName: "Sync Speed", + 15, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + SynchronousAccelerationDefinitionModel.MotivityDIKey, (_, _) => + new EditableSettingV2( + displayName: "Motivity", + 1.4, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + SynchronousAccelerationDefinitionModel.GammaDIKey, (_, _) => + new EditableSettingV2( + displayName: "Gamma", + 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + SynchronousAccelerationDefinitionModel.SmoothnessDIKey, (_, _) => + new EditableSettingV2( + displayName: "Smoothness", + 0.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion SynchronousAccel + + #region LinearAccel + + services.AddTransient(); + services.AddKeyedTransient>( + LinearAccelerationDefinitionModel.AccelerationDIKey, (_, _) => + new EditableSettingV2( + displayName: "Acceleration", + 0.01, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + LinearAccelerationDefinitionModel.OffsetDIKey, (_, _) => + new EditableSettingV2( + displayName: "Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + LinearAccelerationDefinitionModel.CapDIKey, (_, _) => + new EditableSettingV2( + displayName: "Cap", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion LinearAccel + + #region ClassicAccel + + services.AddTransient(); + services.AddKeyedTransient>( + ClassicAccelerationDefinitionModel.AccelerationDIKey, (_, _) => + new EditableSettingV2( + displayName: "Acceleration", + 0.01, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + ClassicAccelerationDefinitionModel.ExponentDIKey, (_, _) => + new EditableSettingV2( + displayName: "Exponent", + 2, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + ClassicAccelerationDefinitionModel.OffsetDIKey, (_, _) => + new EditableSettingV2( + displayName: "Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + ClassicAccelerationDefinitionModel.CapDIKey, (_, _) => + new EditableSettingV2( + displayName: "Cap", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion ClassicAccel + + #region PowerAccel + + services.AddTransient(); + services.AddKeyedTransient>( + PowerAccelerationDefinitionModel.ScaleDIKey, (_, _) => + new EditableSettingV2( + displayName: "Scale", + 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + PowerAccelerationDefinitionModel.ExponentDIKey, (_, _) => + new EditableSettingV2( + displayName: "Exponent", + 0.05, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + PowerAccelerationDefinitionModel.OutputOffsetDIKey, (_, _) => + new EditableSettingV2( + displayName: "Output Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + PowerAccelerationDefinitionModel.CapDIKey, (_, _) => + new EditableSettingV2( + displayName: "Cap", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion PowerAccel + + #region JumpAccel + + services.AddTransient(); + services.AddKeyedTransient>( + JumpAccelerationDefinitionModel.SmoothDIKey, (_, _) => + new EditableSettingV2( + displayName: "Smooth", + 0.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + JumpAccelerationDefinitionModel.InputDIKey, (_, _) => + new EditableSettingV2( + displayName: "Input", + 15, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + JumpAccelerationDefinitionModel.OutputDIKey, (_, _) => + new EditableSettingV2( + displayName: "Output", + 1.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion JumpAccel + + #region NaturalAccel + + services.AddTransient(); + services.AddKeyedTransient>( + NaturalAccelerationDefinitionModel.DecayRateDIKey, (_, _) => + new EditableSettingV2( + displayName: "Decay Rate", + 0.1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + NaturalAccelerationDefinitionModel.InputOffsetDIKey, (_, _) => + new EditableSettingV2( + displayName: "Input Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + NaturalAccelerationDefinitionModel.LimitDIKey, (_, _) => + new EditableSettingV2( + displayName: "Limit", + 1.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion NaturalAccel + + #region Profile + + services.AddTransient(); + services.AddKeyedTransient>( + ProfileModel.NameDIKey, (_, _) => + new EditableSettingV2( + displayName: "Name", + "Empty", + parser: UserInputParsers.StringParser, + // TODO: DI - change to max name length validator + validator: ModelValueValidators.DefaultStringValidator)); + services.AddKeyedTransient>( + ProfileModel.OutputDPIDIKey, (_, _) => + new EditableSettingV2( + displayName: "Output DPI", + 1000, + parser: UserInputParsers.IntParser, + validator: ModelValueValidators.DefaultIntValidator)); + services.AddKeyedTransient>( + ProfileModel.YXRatioDIKey, (_, _) => + new EditableSettingV2( + displayName: "Y/X Ratio", + 1.0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + + #endregion Profile + return services.BuildServiceProvider(); } } diff --git a/userspace-backend/Data/Profiles/Accel/Formula/SynchronousAccel.cs b/userspace-backend/Data/Profiles/Accel/Formula/SynchronousAccel.cs index 8f239836..822a79fe 100644 --- a/userspace-backend/Data/Profiles/Accel/Formula/SynchronousAccel.cs +++ b/userspace-backend/Data/Profiles/Accel/Formula/SynchronousAccel.cs @@ -10,10 +10,10 @@ public class SynchronousAccel : FormulaAccel { public override AccelerationFormulaType FormulaType => AccelerationFormulaType.Synchronous; - public double Motivity { get; set; } - public double SyncSpeed { get; set; } + public double Motivity { get; set; } + public double Gamma { get; set; } public double Smoothness { get; set; } diff --git a/userspace-backend/Data/Profiles/Acceleration.cs b/userspace-backend/Data/Profiles/Acceleration.cs index c8b0992e..a4379505 100644 --- a/userspace-backend/Data/Profiles/Acceleration.cs +++ b/userspace-backend/Data/Profiles/Acceleration.cs @@ -23,8 +23,8 @@ public enum AccelerationDefinitionType public virtual AccelerationDefinitionType Type { get; init; } - public Anisotropy Anisotropy { get; init; } + public Anisotropy Anisotropy { get; set; } - public Coalescion Coalescion { get; init; } + public Coalescion Coalescion { get; set; } } } diff --git a/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs index 13851fb0..1b8f0054 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs @@ -8,27 +8,12 @@ namespace userspace_backend.Model.AccelDefinitions { - public interface IAccelDefinitionModel : IEditableSettingsCollectionSpecific + public interface IAccelDefinitionModel : IEditableSettingsCollectionV2 { AccelArgs MapToDriver(); } - public abstract class AccelDefinitionModel : EditableSettingsCollection, IAccelDefinitionModel where T : Acceleration + public interface IAccelDefinitionModelSpecific : IAccelDefinitionModel, IEditableSettingsCollectionSpecific where T : Acceleration { - protected AccelDefinitionModel(Acceleration dataObject) : base(dataObject) - { - } - - protected override void InitEditableSettingsAndCollections(Acceleration dataObject) - { - T dataAccel = dataObject as T ?? GenerateDefaultDataObject(); - InitSpecificSettingsAndCollections(dataAccel); - } - - protected abstract void InitSpecificSettingsAndCollections(T dataObject); - - protected abstract T GenerateDefaultDataObject(); - - public abstract AccelArgs MapToDriver(); - } + } } diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index 26dc5060..55aeccc9 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -1,6 +1,6 @@ -using System; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; -using System.ComponentModel; using userspace_backend.Data.Profiles; using userspace_backend.Model.EditableSettings; using userspace_backend.Model.ProfileComponents; @@ -10,113 +10,54 @@ namespace userspace_backend.Model.AccelDefinitions { public interface IAccelerationModel : IEditableSettingsCollectionSpecific { + IAnisotropyModel Anisotropy { get; } + + ICoalescionModel Coalescion { get; } + + AccelArgs MapToDriver(); } public class AccelerationModel : EditableSettingsSelector, IAccelerationModel { + public const string SelectionDIKey = $"{nameof(AccelerationModel)}.{nameof(Selection)}"; + public AccelerationModel( - IEditableSettingSpecific definitionType, IServiceProvider serviceProvider, + [FromKeyedServices(SelectionDIKey)]IEditableSettingSpecific definitionType, IAnisotropyModel anisotropy, ICoalescionModel coalescion) - : base(definitionType, serviceProvider, [], [anisotropy, coalescion]) + : base(serviceProvider, definitionType, [], [anisotropy, coalescion]) { Anisotropy = anisotropy; Coalescion = coalescion; - Selection.PropertyChanged += DefinitionTypeChangedEventHandler; } - protected Dictionary DefinitionModels { get; set; } - public IAnisotropyModel Anisotropy { get; set; } public ICoalescionModel Coalescion { get; set; } public FormulaAccelModel FormulaAccel { - get - { - if (DefinitionModels.TryGetValue(AccelerationDefinitionType.Formula, out IAccelDefinitionModel value)) - { - return value as FormulaAccelModel; - } - - return null; - } + get => SelectionLookup.TryGetValue(AccelerationDefinitionType.Formula, out IEditableSettingsCollectionSpecific value) + ? value as FormulaAccelModel + : null; } public LookupTableDefinitionModel LookupTableAccel { - get - { - if (DefinitionModels.TryGetValue(AccelerationDefinitionType.LookupTable, out IAccelDefinitionModel value)) - { - return value as LookupTableDefinitionModel; - } - - return null; - } + get => SelectionLookup.TryGetValue(AccelerationDefinitionType.LookupTable, out IEditableSettingsCollectionSpecific value) + ? value as LookupTableDefinitionModel + : null; } public override Acceleration MapToData() { - return DefinitionModels[DefinitionType.ModelValue].MapToData(); + Acceleration acceleration = base.MapToData(); + acceleration.Anisotropy = Anisotropy.MapToData(); + acceleration.Coalescion = Coalescion.MapToData(); + return acceleration; } - public AccelArgs MapToDriver() - { - return DefinitionModels[DefinitionType.ModelValue].MapToDriver(); - } - - protected void DefinitionTypeChangedEventHandler(object? sender, PropertyChangedEventArgs e) - { - // When the definition type changes, contained editable settings collections need to correspond to new type - if (string.Equals(e.PropertyName, nameof(DefinitionType.ModelValue))) - { - GatherEditableSettingsCollections(); - } - } - - protected override IEnumerable EnumerateEditableSettings() - { - return [DefinitionType]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return [DefinitionModels[DefinitionType.ModelValue]]; - } - - protected override void InitEditableSettingsAndCollections(Acceleration dataObject) - { - DefinitionType = new EditableSetting( - displayName: "Definition Type", - initialValue: dataObject?.Type ?? AccelerationDefinitionType.None, - parser: UserInputParsers.AccelerationDefinitionTypeParser, - validator: ModelValueValidators.DefaultAccelerationTypeValidator); - - DefinitionModels = new Dictionary(); - foreach (AccelerationDefinitionType defnType in Enum.GetValues(typeof(AccelerationDefinitionType))) - { - DefinitionModels.Add(defnType, CreateAccelerationDefinitionModelOfType(defnType, dataObject)); - } - - Anisotropy = new AnisotropyModel(dataObject?.Anisotropy); - Coalescion = new CoalescionModel(dataObject?.Coalescion); - } - - protected IAccelDefinitionModel CreateAccelerationDefinitionModelOfType(AccelerationDefinitionType definitionType, Acceleration dataObject) - { - switch (definitionType) - { - case AccelerationDefinitionType.Formula: - return new FormulaAccelModel(dataObject); - case AccelerationDefinitionType.LookupTable: - return new LookupTableDefinitionModel(dataObject); - case AccelerationDefinitionType.None: - default: - return new NoAccelDefinitionModel(dataObject); - } - } + public AccelArgs MapToDriver() => ((IAccelDefinitionModel)Selected)?.MapToDriver() ?? new AccelArgs(); } } diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs deleted file mode 100644 index 04178a7a..00000000 --- a/userspace-backend/Model/AccelDefinitions/AccelerationSelectorModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace userspace_backend.Model.AccelDefinitions -{ - public class AccelerationSelectorModel - { - } -} diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index 34f29a85..80fa55b7 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -1,16 +1,32 @@ -using System.Collections.Generic; -using System.Linq; -using userspace_backend.Data.Profiles; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { - public class ClassicAccelerationDefinitionModel : AccelDefinitionModel + public interface IClassicAccelerationDefinitionModel : IAccelDefinitionModelSpecific { - public ClassicAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class ClassicAccelerationDefinitionModel : EditableSettingsCollectionV2, IClassicAccelerationDefinitionModel + { + public const string AccelerationDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Acceleration)}"; + public const string ExponentDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Exponent)}"; + public const string OffsetDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Offset)}"; + public const string CapDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(CapDIKey)}"; + + public ClassicAccelerationDefinitionModel( + [FromKeyedServices(AccelerationDIKey)]IEditableSettingSpecific acceleration, + [FromKeyedServices(ExponentDIKey)]IEditableSettingSpecific exponent, + [FromKeyedServices(OffsetDIKey)]IEditableSettingSpecific offset, + [FromKeyedServices(CapDIKey)]IEditableSettingSpecific cap) + : base([acceleration, exponent, offset, cap], []) { + Acceleration = acceleration; + Exponent = exponent; + Offset = offset; + Cap = cap; } public IEditableSettingSpecific Acceleration { get; set; } @@ -21,7 +37,7 @@ public ClassicAccelerationDefinitionModel(Acceleration dataObject) : base(dataOb public IEditableSettingSpecific Cap { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs { @@ -34,7 +50,7 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override ClassicAccel MapToData() { return new ClassicAccel() { @@ -43,52 +59,6 @@ public override Acceleration MapToData() Offset = Offset.ModelValue, Cap = Cap.ModelValue, }; - - } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ Acceleration, Exponent, Offset, Cap ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override ClassicAccel GenerateDefaultDataObject() - { - return new ClassicAccel() - { - Acceleration = 0.001, - Exponent = 2, - Offset = 0, - Cap = 0, - }; - } - - protected override void InitSpecificSettingsAndCollections(ClassicAccel dataObject) - { - Acceleration = new EditableSetting( - displayName: "Acceleration", - initialValue: dataObject.Acceleration, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Exponent = new EditableSetting( - displayName: "Exponent", - initialValue: dataObject.Exponent, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Offset = new EditableSetting( - displayName: "Offset", - initialValue: dataObject.Offset, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Cap = new EditableSetting( - displayName: "Cap", - initialValue: dataObject.Cap, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs index 031ecc75..ab40d9a4 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs @@ -1,18 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using userspace_backend.Data.Profiles; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { - public class JumpAccelerationDefinitionModel : AccelDefinitionModel + public interface IJumpAccelerationDefinitionModel : IAccelDefinitionModelSpecific { - public JumpAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class JumpAccelerationDefinitionModel : EditableSettingsCollectionV2, IJumpAccelerationDefinitionModel + { + public const string SmoothDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Smooth)}"; + public const string InputDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Input)}"; + public const string OutputDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Output)}"; + + public JumpAccelerationDefinitionModel( + [FromKeyedServices(SmoothDIKey)]IEditableSettingSpecific smooth, + [FromKeyedServices(InputDIKey)]IEditableSettingSpecific input, + [FromKeyedServices(OutputDIKey)]IEditableSettingSpecific output) + : base([smooth, input, output], []) { + Smooth = smooth; + Input = input; + Output = output; } public IEditableSettingSpecific Smooth { get; set; } @@ -21,7 +31,7 @@ public JumpAccelerationDefinitionModel(Acceleration dataObject) : base(dataObjec public IEditableSettingSpecific Output { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs { @@ -31,7 +41,7 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override JumpAccel MapToData() { return new JumpAccel() { @@ -40,44 +50,5 @@ public override Acceleration MapToData() Output = Output.ModelValue }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ Smooth, Input, Output ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override JumpAccel GenerateDefaultDataObject() - { - return new JumpAccel() - { - Smooth = 0.5, - Input = 15, - Output = 1.5, - }; - } - - protected override void InitSpecificSettingsAndCollections(JumpAccel dataObject) - { - Smooth = new EditableSetting( - displayName: "Smooth", - initialValue: dataObject.Smooth, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Input = new EditableSetting( - displayName: "Input", - initialValue: dataObject.Input, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Output = new EditableSetting( - displayName: "Output", - initialValue: dataObject.Output, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs index b2666224..9f76378a 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs @@ -1,18 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using userspace_backend.Data.Profiles; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { - public class LinearAccelerationDefinitionModel : AccelDefinitionModel + public interface ILinearAccelerationDefinitionModel : IAccelDefinitionModelSpecific { - public LinearAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class LinearAccelerationDefinitionModel : EditableSettingsCollectionV2, ILinearAccelerationDefinitionModel + { + public const string AccelerationDIKey = $"{nameof(LinearAccelerationDefinitionModel)}.{nameof(Acceleration)}"; + public const string OffsetDIKey = $"{nameof(LinearAccelerationDefinitionModel)}.{nameof(Offset)}"; + public const string CapDIKey = $"{nameof(LinearAccelerationDefinitionModel)}.{nameof(CapDIKey)}"; + + public LinearAccelerationDefinitionModel( + [FromKeyedServices(AccelerationDIKey)]IEditableSettingSpecific acceleration, + [FromKeyedServices(OffsetDIKey)]IEditableSettingSpecific offset, + [FromKeyedServices(CapDIKey)]IEditableSettingSpecific cap) + : base([acceleration, offset, cap], []) { + Acceleration = acceleration; + Offset = offset; + Cap = cap; } public IEditableSettingSpecific Acceleration { get; set; } @@ -21,7 +31,7 @@ public LinearAccelerationDefinitionModel(Acceleration dataObject) : base(dataObj public IEditableSettingSpecific Cap { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs { @@ -34,7 +44,7 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override LinearAccel MapToData() { return new LinearAccel() { @@ -43,44 +53,5 @@ public override Acceleration MapToData() Cap = Cap.ModelValue, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ Acceleration, Offset, Cap ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override LinearAccel GenerateDefaultDataObject() - { - return new LinearAccel() - { - Acceleration = 0.001, - Offset = 0, - Cap = 0, - }; - } - - protected override void InitSpecificSettingsAndCollections(LinearAccel dataObject) - { - Acceleration = new EditableSetting( - displayName: "Acceleration", - initialValue: dataObject.Acceleration, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Offset = new EditableSetting( - displayName: "Offset", - initialValue: dataObject.Offset, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Cap = new EditableSetting( - displayName: "Cap", - initialValue: dataObject.Cap, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs index 8dd34d06..8645c426 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs @@ -1,26 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using userspace_backend.Data.Profiles; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { - public class NaturalAccelerationDefinitionModel : AccelDefinitionModel + public interface INaturalAccelerationDefinitionModel : IAccelDefinitionModelSpecific { - public NaturalAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class NaturalAccelerationDefinitionModel : EditableSettingsCollectionV2, INaturalAccelerationDefinitionModel + { + public const string DecayRateDIKey = $"{nameof(NaturalAccelerationDefinitionModel)}.{nameof(DecayRate)}"; + public const string InputOffsetDIKey = $"{nameof(NaturalAccelerationDefinitionModel)}.{nameof(InputOffset)}"; + public const string LimitDIKey = $"{nameof(NaturalAccelerationDefinitionModel)}.{nameof(Limit)}"; + + public NaturalAccelerationDefinitionModel( + [FromKeyedServices(DecayRateDIKey)]IEditableSettingSpecific decayRate, + [FromKeyedServices(InputOffsetDIKey)]IEditableSettingSpecific inputOffset, + [FromKeyedServices(LimitDIKey)]IEditableSettingSpecific limit) + : base([decayRate, inputOffset, limit], []) { + DecayRate = decayRate; + InputOffset = inputOffset; + Limit = limit; } + public IEditableSettingSpecific DecayRate { get; set; } public IEditableSettingSpecific InputOffset { get; set; } public IEditableSettingSpecific Limit { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs { @@ -31,7 +42,7 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override NaturalAccel MapToData() { return new NaturalAccel() { @@ -40,44 +51,5 @@ public override Acceleration MapToData() Limit = Limit.ModelValue, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ DecayRate, InputOffset, Limit ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override NaturalAccel GenerateDefaultDataObject() - { - return new NaturalAccel() - { - DecayRate = 0.1, - InputOffset = 0, - Limit = 1.5, - }; - } - - protected override void InitSpecificSettingsAndCollections(NaturalAccel dataObject) - { - DecayRate = new EditableSetting( - displayName: "Decay Rate", - initialValue: dataObject.DecayRate, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - InputOffset = new EditableSetting( - displayName: "Input Offset", - initialValue: dataObject.InputOffset, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Limit = new EditableSetting( - displayName: "Limit", - initialValue: dataObject.Limit, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs index 7498a5b1..133a7d2a 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs @@ -1,18 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { - public class PowerAccelerationDefinitionModel : AccelDefinitionModel + public interface IPowerAccelerationDefinitionModel : IAccelDefinitionModelSpecific { - public PowerAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class PowerAccelerationDefinitionModel : EditableSettingsCollectionV2, IPowerAccelerationDefinitionModel + { + public const string ScaleDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Scale)}"; + public const string ExponentDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Exponent)}"; + public const string OutputOffsetDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(OutputOffset)}"; + public const string CapDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(CapDIKey)}"; + + public PowerAccelerationDefinitionModel( + [FromKeyedServices(ScaleDIKey)]IEditableSettingSpecific scale, + [FromKeyedServices(ExponentDIKey)]IEditableSettingSpecific exponent, + [FromKeyedServices(OutputOffsetDIKey)]IEditableSettingSpecific outputOffset, + [FromKeyedServices(CapDIKey)]IEditableSettingSpecific cap) + : base([scale, exponent, outputOffset, cap], []) { + Scale = scale; + Exponent = exponent; + OutputOffset = outputOffset; + Cap = cap; } public IEditableSettingSpecific Scale { get; set; } @@ -23,7 +37,7 @@ public PowerAccelerationDefinitionModel(Acceleration dataObject) : base(dataObje public IEditableSettingSpecific Cap { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs { @@ -36,54 +50,15 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() - { - throw new NotImplementedException(); - } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ Scale, Exponent, OutputOffset, Cap ]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override PowerAccel GenerateDefaultDataObject() + public override PowerAccel MapToData() { return new PowerAccel() { - Scale = 1, - Exponent = 0.05, - Cap = 0, - OutputOffset = 0, + Scale = Scale.ModelValue, + Exponent = Exponent.ModelValue, + OutputOffset = OutputOffset.ModelValue, + Cap = Cap.ModelValue, }; } - - protected override void InitSpecificSettingsAndCollections(PowerAccel dataObject) - { - Scale = new EditableSetting( - displayName: "Scale", - initialValue: dataObject.Scale, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Exponent = new EditableSetting( - displayName: "Exponent", - initialValue: dataObject.Exponent, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - OutputOffset = new EditableSetting( - displayName: "Output Offset", - initialValue: dataObject.OutputOffset, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Cap = new EditableSetting( - displayName: "Cap", - initialValue: dataObject.Cap, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs index 5c439a7d..a57ab714 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs @@ -1,26 +1,42 @@ -using System.Collections.Generic; -using System.Linq; -using userspace_backend.Data.Profiles; +using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions.Formula { - public class SynchronousAccelerationDefinitionModel : AccelDefinitionModel + public interface ISynchronousAccelerationDefinitionModel : IEditableSettingsCollectionSpecific { - public SynchronousAccelerationDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class SynchronousAccelerationDefinitionModel : EditableSettingsCollectionV2, ISynchronousAccelerationDefinitionModel + { + public const string SyncSpeedDIKey = $"{nameof(SynchronousAccelerationDefinitionModel)}.{nameof(SyncSpeed)}"; + public const string MotivityDIKey = $"{nameof(SynchronousAccelerationDefinitionModel)}.{nameof(Motivity)}"; + public const string GammaDIKey = $"{nameof(SynchronousAccelerationDefinitionModel)}.{nameof(Gamma)}"; + public const string SmoothnessDIKey = $"{nameof(SynchronousAccelerationDefinitionModel)}.{nameof(Smoothness)}"; + + public SynchronousAccelerationDefinitionModel( + [FromKeyedServices(SyncSpeedDIKey)]IEditableSettingSpecific syncSpeed, + [FromKeyedServices(MotivityDIKey)]IEditableSettingSpecific motivity, + [FromKeyedServices(GammaDIKey)]IEditableSettingSpecific gamma, + [FromKeyedServices(SmoothnessDIKey)]IEditableSettingSpecific smoothness) + : base([syncSpeed, motivity, gamma, smoothness], []) { + SyncSpeed = syncSpeed; + Motivity = motivity; + Gamma = gamma; + Smoothness = smoothness; } - public IEditableSettingSpecific Gamma { get; set; } + public IEditableSettingSpecific SyncSpeed { get; set; } public IEditableSettingSpecific Motivity { get; set; } - public IEditableSettingSpecific SyncSpeed { get; set; } + public IEditableSettingSpecific Gamma { get; set; } public IEditableSettingSpecific Smoothness { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs { @@ -32,60 +48,15 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override SynchronousAccel MapToData() { return new SynchronousAccel() { - Gamma = Gamma.ModelValue, - Motivity = Motivity.ModelValue, SyncSpeed = SyncSpeed.ModelValue, + Motivity = Motivity.ModelValue, + Gamma = Gamma.ModelValue, Smoothness = Smoothness.ModelValue, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [Gamma, Motivity, SyncSpeed, Smoothness]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override SynchronousAccel GenerateDefaultDataObject() - { - return new SynchronousAccel() - { - Gamma = 1, - Motivity = 1.4, - SyncSpeed = 12, - Smoothness = 0.5, - }; - } - - protected override void InitSpecificSettingsAndCollections(SynchronousAccel dataObject) - { - Gamma = new EditableSetting( - displayName: "Gamma", - dataObject.Gamma, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Motivity = new EditableSetting( - displayName: "Motivity", - dataObject.Motivity, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - SyncSpeed = new EditableSetting( - displayName: "Sync Speed", - dataObject.SyncSpeed, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Smoothness = new EditableSetting( - displayName: "Smoothness", - dataObject.Smoothness, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - } } } diff --git a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs index c1e16cbb..c33cade3 100644 --- a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs +++ b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs @@ -1,110 +1,36 @@ -using System; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.AccelDefinitions.Formula; using userspace_backend.Model.EditableSettings; +using static userspace_backend.Data.Profiles.Accel.FormulaAccel; namespace userspace_backend.Model.AccelDefinitions { - public class FormulaAccelModel : AccelDefinitionModel + public interface IFormulaAccelModel: IAccelDefinitionModelSpecific { - public FormulaAccelModel(Acceleration dataObject) : base(dataObject) - { - FormulaType.PropertyChanged += FormulaTypeChangedEventHandler; - } - - - public IEditableSettingSpecific FormulaType { get; set; } - - public int FormulaTypeIndex { get => (int)FormulaType.ModelValue; } - - protected Dictionary FormulaModels { get; set; } - - public override AccelArgs MapToDriver() - { - return FormulaModels[FormulaType.ModelValue].MapToDriver(); - } - - public override Acceleration MapToData() - { - return FormulaModels[FormulaType.ModelValue].MapToData(); - } - - public IAccelDefinitionModel GetAccelerationModelOfType(FormulaAccel.AccelerationFormulaType formulaType) - { - return FormulaModels[formulaType]; - } - - protected void FormulaTypeChangedEventHandler(object? sender, PropertyChangedEventArgs e) - { - // When the formula type changes, contained editable settings collections need to correspond to new type - if (string.Equals(e.PropertyName, nameof(FormulaType.ModelValue))) - { - GatherEditableSettingsCollections(); - } - } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ FormulaType ]; - } + } - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return [ FormulaModels[FormulaType.ModelValue] ]; - } + public class FormulaAccelModel : EditableSettingsSelector, IFormulaAccelModel + { + public const string SelectionDIKey = $"{nameof(FormulaAccelModel)}.{nameof(Selection)}"; + public const string GainDIKey = $"{nameof(FormulaAccelModel)}.{nameof(Gain)}"; - protected override FormulaAccel GenerateDefaultDataObject() + public FormulaAccelModel( + IServiceProvider serviceProvider, + [FromKeyedServices(SelectionDIKey)]IEditableSettingSpecific formulaType, + [FromKeyedServices(GainDIKey)]IEditableSettingSpecific gain) + : base(serviceProvider, formulaType, [gain], []) { - return new LinearAccel() - { - Acceleration = 0.001, - Cap = 0, - Offset = 0, - }; + Gain = gain; } - protected override void InitSpecificSettingsAndCollections(FormulaAccel dataObject) - { - FormulaType = new EditableSetting( - displayName: "Formula Type", - initialValue: dataObject.FormulaType, - parser: UserInputParsers.AccelerationFormulaTypeParser, - validator: ModelValueValidators.DefaultAccelerationFormulaTypeValidator, - autoUpdateFromInterface: true); - - FormulaModels = new Dictionary(); + public IEditableSettingSpecific Gain { get; set; } - foreach (FormulaAccel.AccelerationFormulaType formulaType in Enum.GetValues(typeof(FormulaAccel.AccelerationFormulaType))) - { - FormulaModels.Add(formulaType, CreateAccelerationDefinitionModelOfType(formulaType, dataObject)); - } - } - - protected IAccelDefinitionModel CreateAccelerationDefinitionModelOfType(FormulaAccel.AccelerationFormulaType formulaType, Acceleration dataObject) - { - switch (formulaType) - { - case FormulaAccel.AccelerationFormulaType.Synchronous: - return new SynchronousAccelerationDefinitionModel(dataObject); - case FormulaAccel.AccelerationFormulaType.Jump: - return new JumpAccelerationDefinitionModel(dataObject); - case FormulaAccel.AccelerationFormulaType.Power: - return new PowerAccelerationDefinitionModel(dataObject); - case FormulaAccel.AccelerationFormulaType.Natural: - return new NaturalAccelerationDefinitionModel(dataObject); - case FormulaAccel.AccelerationFormulaType.Classic: - return new ClassicAccelerationDefinitionModel(dataObject); - case FormulaAccel.AccelerationFormulaType.Linear: - default: - return new LinearAccelerationDefinitionModel(dataObject); - } - } + public AccelArgs MapToDriver() => ((IAccelDefinitionModel)Selected)?.MapToDriver() ?? new AccelArgs(); } } diff --git a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs index f814d313..659b8a2a 100644 --- a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs @@ -1,8 +1,7 @@ -using System; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel; using userspace_backend.Model.EditableSettings; @@ -10,17 +9,29 @@ namespace userspace_backend.Model.AccelDefinitions { - public class LookupTableDefinitionModel : AccelDefinitionModel + public interface ILookupTableDefinitionModel : IAccelDefinitionModelSpecific { - public LookupTableDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class LookupTableDefinitionModel : EditableSettingsCollectionV2, ILookupTableDefinitionModel + { + public const string ApplyAsDIKey = $"{nameof(LookupTableDefinitionModel)}.{nameof(ApplyAs)}"; + public const string DataDIKey = $"{nameof(LookupTableDefinitionModel)}.{nameof(Data)}"; + + public LookupTableDefinitionModel( + [FromKeyedServices(ApplyAsDIKey)]IEditableSettingSpecific applyAs, + [FromKeyedServices(DataDIKey)]IEditableSettingSpecific data) + : base([applyAs, data], []) { + ApplyAs = applyAs; + Data = data; } public IEditableSettingSpecific ApplyAs { get; set; } public IEditableSettingSpecific Data { get; set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { // data in driver profile must be predefined length for marshalling purposes var accelArgsData = new float[AccelArgs.MaxLutPoints*2]; @@ -34,7 +45,7 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override LookupTableAccel MapToData() { return new LookupTableAccel() { @@ -42,39 +53,6 @@ public override Acceleration MapToData() Data = this.Data.ModelValue.Data, }; } - - protected override IEnumerable EnumerateEditableSettings() - { - return [ApplyAs, Data]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override LookupTableAccel GenerateDefaultDataObject() - { - return new LookupTableAccel() - { - ApplyAs = LookupTableType.Velocity, - Data = [], - }; - } - - protected override void InitSpecificSettingsAndCollections(LookupTableAccel dataObject) - { - ApplyAs = new EditableSetting( - displayName: "Apply as", - initialValue: dataObject.ApplyAs, - parser: UserInputParsers.LookupTableTypeParser, - validator: ModelValueValidators.DefaultLookupTableTypeValidator); - Data = new EditableSetting( - displayName: "Data", - initialValue: new LookupTableData(dataObject.Data), - parser: UserInputParsers.LookupTableDataParser, - validator: ModelValueValidators.DefaultLookupTableDataValidator); - } } public class LookupTableData : IComparable diff --git a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs index 956601f9..b467c629 100644 --- a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs @@ -1,24 +1,27 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions { - public class NoAccelDefinitionModel : AccelDefinitionModel + public interface INoAccelDefinitionModel : IAccelDefinitionModelSpecific { - public NoAccelDefinitionModel(Acceleration dataObject) : base(dataObject) + } + + public class NoAccelDefinitionModel : EditableSettingsCollectionV2, INoAccelDefinitionModel + { + public NoAccelDefinitionModel() + : base([], []) { NoAcceleration = new NoAcceleration(); } public NoAcceleration NoAcceleration { get; protected set; } - public override AccelArgs MapToDriver() + public AccelArgs MapToDriver() { return new AccelArgs() { @@ -26,29 +29,9 @@ public override AccelArgs MapToDriver() }; } - public override Acceleration MapToData() + public override NoAcceleration MapToData() { return NoAcceleration; } - - protected override IEnumerable EnumerateEditableSettings() - { - return Enumerable.Empty(); - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override NoAcceleration GenerateDefaultDataObject() - { - return new NoAcceleration(); - } - - protected override void InitSpecificSettingsAndCollections(NoAcceleration dataObject) - { - // Nothing to do here since no acceleration has no settings - } } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs index dda5e566..7566fbc9 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs @@ -26,8 +26,8 @@ public abstract class EditableSettingsSelector IEditableSettingsSelector where T : Enum { protected EditableSettingsSelector( - IEditableSettingSpecific selection, IServiceProvider serviceProvider, + IEditableSettingSpecific selection, IEnumerable editableSettings, IEnumerable editableSettingsCollections) : base(editableSettings.Union([selection]), editableSettingsCollections) @@ -43,6 +43,8 @@ protected EditableSettingsSelector( public IEditableSettingsCollectionSpecific GetSelectable(T choice) => SelectionLookup[choice]; + public IEditableSettingsCollectionSpecific Selected => GetSelectable(Selection.ModelValue); + protected void InitSelectionLookup(IServiceProvider serviceProvider) { foreach (T value in Enum.GetValues(typeof(T))) @@ -54,7 +56,7 @@ protected void InitSelectionLookup(IServiceProvider serviceProvider) public override U MapToData() { - return GetSelectable(Selection.ModelValue).MapToData(); + return Selected.MapToData(); } } } diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index 736f3348..11828d9f 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -101,7 +101,7 @@ public bool TryAddMapping(string deviceGroupName, string profileName) return false; } - if (!Profiles.TryGetProfile(profileName, out ProfileModel? profile) + if (!Profiles.TryGetElement(profileName, out ProfileModel? profile) || profile == null) { return false; diff --git a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs index 1ec03bb3..04c86913 100644 --- a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs +++ b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs @@ -6,6 +6,17 @@ namespace userspace_backend.Model.ProfileComponents { public interface IAnisotropyModel : IEditableSettingsCollectionSpecific { + IEditableSettingSpecific DomainX { get; } + + IEditableSettingSpecific DomainY { get; } + + IEditableSettingSpecific RangeX { get; } + + IEditableSettingSpecific RangeY { get; } + + IEditableSettingSpecific LPNorm { get; } + + IEditableSettingSpecific CombineXYComponents { get; } } public class AnisotropyModel : EditableSettingsCollectionV2, IAnisotropyModel diff --git a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs index 079e2781..f643b681 100644 --- a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs +++ b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs @@ -6,6 +6,9 @@ namespace userspace_backend.Model.ProfileComponents { public interface ICoalescionModel : IEditableSettingsCollectionSpecific { + IEditableSettingSpecific InputSmoothingHalfLife { get; } + + IEditableSettingSpecific ScaleSmoothingHalfLife { get; } } public class CoalescionModel : EditableSettingsCollectionV2, ICoalescionModel diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index 645756ab..0eb267c1 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -6,6 +6,17 @@ namespace userspace_backend.Model.ProfileComponents { public interface IHiddenModel : IEditableSettingsCollectionSpecific { + IEditableSettingSpecific RotationDegrees { get; } + + IEditableSettingSpecific AngleSnappingDegrees { get; } + + IEditableSettingSpecific LeftRightRatio { get; } + + IEditableSettingSpecific UpDownRatio { get; } + + IEditableSettingSpecific SpeedCap { get; } + + IEditableSettingSpecific OutputSmoothingHalfLife { get; } } public class HiddenModel : EditableSettingsCollectionV2, IHiddenModel diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index ca484241..3d0561d8 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -9,15 +9,24 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using userspace_backend.Display; +using Microsoft.Extensions.DependencyInjection; namespace userspace_backend.Model { - public class ProfileModel : EditableSettingsCollectionV2 + public interface IProfileModel : IEditableSettingsCollectionSpecific { + } + + public class ProfileModel : EditableSettingsCollectionV2, IProfileModel + { + public const string NameDIKey = $"{nameof(ProfileModel)}.{nameof(Name)}"; + public const string OutputDPIDIKey = $"{nameof(ProfileModel)}.{nameof(OutputDPI)}"; + public const string YXRatioDIKey = $"{nameof(ProfileModel)}.{nameof(YXRatio)}"; + public ProfileModel( - IEditableSettingSpecific name, - IEditableSettingSpecific outputDPI, - IEditableSettingSpecific yxRatio, + [FromKeyedServices(NameDIKey)]IEditableSettingSpecific name, + [FromKeyedServices(OutputDPIDIKey)]IEditableSettingSpecific outputDPI, + [FromKeyedServices(YXRatioDIKey)]IEditableSettingSpecific yxRatio, IAccelerationModel acceleration, IHiddenModel hidden ) : base([name, outputDPI, yxRatio], [acceleration, hidden]) @@ -104,36 +113,5 @@ protected void RecalculateDriverDataAndCurvePreview() RecalculateDriverData(); CurvePreview.GeneratePoints(CurrentValidatedDriverProfile); } - - // TODO: DI - Add init to composition - protected override void InitEditableSettingsAndCollections(DATA.Profile dataObject) - { - Name = new EditableSetting( - displayName: "Name", - initialValue: dataObject.Name, - parser: UserInputParsers.StringParser, - validator: NameValidator); - OutputDPI = new EditableSetting( - displayName: "Output DPI", - initialValue: dataObject.OutputDPI, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator); - YXRatio = new EditableSetting( - displayName: "Y/X Ratio", - initialValue: dataObject.YXRatio, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator); - Acceleration = new AccelerationModel(dataObject.Acceleration); - Hidden = new HiddenModel(dataObject.Hidden); - - // Name and Output DPI do not need to generate a new curve preview - Name.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; - OutputDPI.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; - - // The rest of settings should generate a new curve preview - YXRatio.PropertyChanged += AnyCurvePreviewPropertyChangedEventHandler; - Acceleration.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; - Hidden.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; - } } } diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index f509154a..7676dadc 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -11,8 +11,8 @@ namespace userspace_backend.Model public class ProfilesModel : EditableSettingsList { // TODO: DI - hand default profile to profiles model - public static readonly ProfileModel DefaultProfile = new ProfileModel( - GenerateNewDefaultProfile("Default"), ModelValueValidators.AllChangesInvalidStringValidator); + // public static readonly ProfileModel DefaultProfile = new ProfileModel( + // GenerateNewDefaultProfile("Default"), ModelValueValidators.AllChangesInvalidStringValidator); public ProfilesModel(IServiceProvider serviceProvider) : base(serviceProvider, [], []) From 63f648290916e74ab16a388616939c3566961544 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 6 Jul 2025 00:46:59 -0700 Subject: [PATCH 17/47] Partial work on setting editable settings from data --- userspace-backend/BackEndLoader.cs | 6 ++-- .../AccelDefinitions/AccelerationModel.cs | 10 +++++++ .../ClassicAccelerationDefinitionModel.cs | 8 +++++ .../JumpAccelerationDefinitionModel.cs | 7 +++++ .../Model/EditableSettings/EditableSetting.cs | 30 ++++++++++++++++--- .../EditableSettingsCollection.cs | 16 ++++++++++ .../EditableSettings/EditableSettingsList.cs | 6 ++-- .../EditableSettings/IEditableSetting.cs | 2 ++ userspace-backend/Model/ProfileModel.cs | 13 ++++++++ userspace-backend/Model/ProfilesModel.cs | 11 +++++-- 10 files changed, 96 insertions(+), 13 deletions(-) diff --git a/userspace-backend/BackEndLoader.cs b/userspace-backend/BackEndLoader.cs index ae09dfef..33d5ba2b 100644 --- a/userspace-backend/BackEndLoader.cs +++ b/userspace-backend/BackEndLoader.cs @@ -21,7 +21,7 @@ public interface IBackEndLoader public void WriteSettingsToDisk( IEnumerable devices, MappingsModel mappings, - IEnumerable profiles); + IEnumerable profiles); } public class BackEndLoader : IBackEndLoader @@ -60,7 +60,7 @@ public DATA.MappingSet LoadMappings() public void WriteSettingsToDisk( IEnumerable devices, MappingsModel mappings, - IEnumerable profiles) + IEnumerable profiles) { WriteDevices(devices); WriteMappings(mappings); @@ -83,7 +83,7 @@ protected void WriteMappings(MappingsModel mappings) File.WriteAllText(mappingsFilePath, mappingsFileText); } - protected void WriteProfiles(IEnumerable profiles) + protected void WriteProfiles(IEnumerable profiles) { string profilesDirectory = GetProfilesDirectory(SettingsDirectory); Directory.CreateDirectory(profilesDirectory); diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index 55aeccc9..784cf5da 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -59,5 +59,15 @@ public override Acceleration MapToData() } public AccelArgs MapToDriver() => ((IAccelDefinitionModel)Selected)?.MapToDriver() ?? new AccelArgs(); + + protected override void TryMapEditableSettingsFromData(Acceleration data) + { + } + + protected override void TryMapEditableSettingsCollectionsFromData(Acceleration data) + { + Anisotropy.TryMapFromData(data.Anisotropy); + Coalescion.TryMapFromData(data.Coalescion); + } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index 80fa55b7..1e09d3f8 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -60,5 +60,13 @@ public override ClassicAccel MapToData() Cap = Cap.ModelValue, }; } + + protected override void TryMapEditableSettingsFromData(ClassicAccel data) + { + Acceleration.InterfaceValue = data.Acceleration.ToString(); + Exponent.InterfaceValue = data.Exponent.ToString(); + Offset.InterfaceValue = data.Offset.ToString(); + Cap.InterfaceValue = data.Cap.ToString(); + } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs index ab40d9a4..4690b4c0 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs @@ -50,5 +50,12 @@ public override JumpAccel MapToData() Output = Output.ModelValue }; } + + protected override void TryMapEditableSettingsFromData(JumpAccel data) + { + Smooth.InterfaceValue = data.Smooth.ToString(); + Input.InterfaceValue = data.Input.ToString(); + Output.InterfaceValue = data.Output.ToString(); + } } } diff --git a/userspace-backend/Model/EditableSettings/EditableSetting.cs b/userspace-backend/Model/EditableSettings/EditableSetting.cs index 4634c4a4..fa8af3a1 100644 --- a/userspace-backend/Model/EditableSettings/EditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/EditableSetting.cs @@ -105,6 +105,11 @@ partial void OnInterfaceValueChanged(string value) TryUpdateFromInterface(); } } + + public bool TrySetFromData(T data) + { + throw new NotImplementedException(); + } } public partial class EditableSettingV2 : ObservableObject, IEditableSettingSpecific where T : IComparable @@ -155,6 +160,8 @@ public EditableSettingV2( //TODO: change settings collections init so that this can be made private for non-static validators public IModelValueValidator Validator { get; set; } + private bool AllowAutoUpdateFromInterface { get; set; } = true; + public bool HasChanged() => ModelValue.CompareTo(LastWrittenValue) == 0; public bool TryUpdateFromInterface() @@ -182,31 +189,46 @@ public bool TryUpdateFromInterface() return false; } - UpdatedModeValue(parsedValue); + UpdateModeValue(parsedValue); return true; } protected void UpdateInterfaceValue() { + bool previous = AllowAutoUpdateFromInterface; + AllowAutoUpdateFromInterface = false; InterfaceValue = ModelValue?.ToString(); + AllowAutoUpdateFromInterface = true; } protected void UpdateModelValueFromLastKnown() { - UpdatedModeValue(LastWrittenValue); + UpdateModeValue(LastWrittenValue); } - protected void UpdatedModeValue(T value) + protected void UpdateModeValue(T value) { ModelValue = value; } partial void OnInterfaceValueChanged(string value) { - if (AutoUpdateFromInterface) + // TODO: double-check race conditions + if (AutoUpdateFromInterface && AllowAutoUpdateFromInterface) { TryUpdateFromInterface(); } } + + // TODO: unit test + public bool TrySetFromData(T data) + { + bool previous = AllowAutoUpdateFromInterface; + AllowAutoUpdateFromInterface = false; + InterfaceValue = data.ToString(); + bool result = TryUpdateFromInterface(); + AllowAutoUpdateFromInterface = previous; + return result; + } } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs index a4e076df..d437f2cd 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs @@ -109,6 +109,8 @@ public interface IEditableSettingsCollectionV2 public interface IEditableSettingsCollectionSpecific : IEditableSettingsCollectionV2 { T MapToData(); + + bool TryMapFromData(T data); } /// @@ -155,6 +157,16 @@ public EditableSettingsCollectionV2( public bool HasChanged { get; protected set; } + public bool TryMapFromData(T data) + { + bool result = true; + + result &= TryMapEditableSettingsFromData(data); + result &= TryMapEditableSettingsCollectionsFromData(data); + + return result; + } + public void EvaluateWhetherHasChanged() { if (AllContainedEditableSettings.Any(s => s.HasChanged()) || @@ -187,5 +199,9 @@ protected void OnAnySettingChanged() } public abstract T MapToData(); + + protected abstract bool TryMapEditableSettingsFromData(T data); + + protected abstract bool TryMapEditableSettingsCollectionsFromData(T data); } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs index cb6d3ee8..f5ba2528 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs @@ -7,7 +7,7 @@ namespace userspace_backend.Model.EditableSettings { public interface IEditableSettingsList - : IEditableSettingsCollectionSpecific> where T : class, IEditableSettingsCollectionSpecific + : IEditableSettingsCollectionSpecific> where T : IEditableSettingsCollectionSpecific { public ReadOnlyObservableCollection Elements { get; } @@ -21,7 +21,7 @@ public interface IEditableSettingsList } public abstract class EditableSettingsList - : EditableSettingsCollectionV2>, IEditableSettingsList where T : class, IEditableSettingsCollectionSpecific + : EditableSettingsCollectionV2>, IEditableSettingsList where T : IEditableSettingsCollectionSpecific { public EditableSettingsList( IServiceProvider serviceProvider, @@ -80,7 +80,7 @@ public bool TryAddNewDefault() public bool TryGetElement(string name, out T? element) { - element = null; + element = default; foreach (T elementInList in ElementsInternal) { diff --git a/userspace-backend/Model/EditableSettings/IEditableSetting.cs b/userspace-backend/Model/EditableSettings/IEditableSetting.cs index ad2f80bd..85fd8b3d 100644 --- a/userspace-backend/Model/EditableSettings/IEditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/IEditableSetting.cs @@ -19,5 +19,7 @@ public interface IEditableSetting : INotifyPropertyChanged public interface IEditableSettingSpecific : IEditableSetting where T : IComparable { public T ModelValue { get; } + + public bool TrySetFromData(T data); } } diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index 3d0561d8..a6f0bec5 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -15,6 +15,12 @@ namespace userspace_backend.Model { public interface IProfileModel : IEditableSettingsCollectionSpecific { + IEditableSettingSpecific Name { get; } + + IEditableSettingSpecific OutputDPI { get; } + + IEditableSettingSpecific YXRatio { get; } + } public class ProfileModel : EditableSettingsCollectionV2, IProfileModel @@ -113,5 +119,12 @@ protected void RecalculateDriverDataAndCurvePreview() RecalculateDriverData(); CurvePreview.GeneratePoints(CurrentValidatedDriverProfile); } + + protected override void TryMapEditableSettingsFromData(DATA.Profile data) + { + Name.InterfaceValue = data.Name; + OutputDPI.InterfaceValue = data.OutputDPI.ToString(); + YXRatio.InterfaceValue = data.YXRatio.ToString(); + } } } diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 7676dadc..1805edd3 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -8,7 +8,7 @@ namespace userspace_backend.Model { - public class ProfilesModel : EditableSettingsList + public class ProfilesModel : EditableSettingsList { // TODO: DI - hand default profile to profiles model // public static readonly ProfileModel DefaultProfile = new ProfileModel( @@ -22,12 +22,17 @@ public ProfilesModel(IServiceProvider serviceProvider) protected override string DefaultNameTemplate => "Profile"; - protected override string GetNameFromElement(ProfileModel element) + protected override string GetNameFromElement(IProfileModel element) { return element.Name.ModelValue; } - protected override void SetElementName(ProfileModel element, string name) + protected override void TryMapEditableSettingsFromData(IEnumerable data) + { + // No editable settings in this class + } + + protected override void SetElementName(IProfileModel element, string name) { element.Name.InterfaceValue = name; } From af9f723a6dd32d7c6f244a8d0fb7e463e09e5aeb Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sat, 12 Jul 2025 19:35:12 -0700 Subject: [PATCH 18/47] Tests build --- .../AccelerationFormulaSettingsViewModel.cs | 20 +++--- .../AccelerationLUTSettingsViewModel.cs | 4 +- .../AccelerationProfileSettingsViewModel.cs | 18 +++--- .../AnisotropyProfileSettingsViewModel.cs | 4 +- .../CoalescionProfileSettingsViewModel.cs | 4 +- .../Profile/HiddenProfileSettingsViewModel.cs | 4 +- .../Profile/ProfileListViewModel.cs | 15 ++--- .../Profile/ProfileSettingsViewModel.cs | 4 +- .../ViewModels/Profile/ProfileViewModel.cs | 4 +- .../Profile/ProfilesPageViewModel.cs | 4 +- userinterface/Views/Mapping/MappingView.axaml | 2 +- .../AccelerationFormulaSettingsView.axaml.cs | 6 +- .../AccelerationProfileSettingsView.axaml.cs | 2 +- .../EditableSettingsCollectionTests.cs | 20 ++++++ .../ModelTests/EditableSettingsListTests.cs | 21 +++++++ .../EditableSettingsSelectorTests.cs | 42 +++++++++++-- userspace-backend/BackEnd.cs | 5 +- userspace-backend/Bootstrapper.cs | 5 +- .../AccelDefinitions/AccelerationModel.cs | 12 ++-- .../ClassicAccelerationDefinitionModel.cs | 15 +++-- .../JumpAccelerationDefinitionModel.cs | 13 ++-- .../LinearAccelerationDefinitionModel.cs | 12 ++++ .../NaturalAccelerationDefinitionModel.cs | 12 ++++ .../PowerAccelerationDefinitionModel.cs | 13 ++++ .../SynchronousAccelerationDefinitionModel.cs | 20 ++++++ .../AccelDefinitions/FormulaAccelModel.cs | 12 +++- .../LookupTableDefinitionModel.cs | 15 +++++ .../NoAccelDefinitionModel.cs | 10 +++ .../Model/EditableSettings/EditableSetting.cs | 63 ++++++++++++------- .../EditableSettings/EditableSettingsList.cs | 21 +++++++ .../EditableSettings/IEditableSetting.cs | 9 ++- userspace-backend/Model/MappingModel.cs | 4 +- .../ProfileComponents/AnisotropyModel.cs | 16 +++++ .../ProfileComponents/CoalescionModel.cs | 11 ++++ .../Model/ProfileComponents/HiddenModel.cs | 15 +++++ userspace-backend/Model/ProfileModel.cs | 26 +++++--- userspace-backend/Model/ProfilesModel.cs | 9 ++- 37 files changed, 378 insertions(+), 114 deletions(-) diff --git a/userinterface/ViewModels/Profile/AccelerationFormulaSettingsViewModel.cs b/userinterface/ViewModels/Profile/AccelerationFormulaSettingsViewModel.cs index 304ad2c9..34465bdb 100644 --- a/userinterface/ViewModels/Profile/AccelerationFormulaSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/AccelerationFormulaSettingsViewModel.cs @@ -14,30 +14,30 @@ public class AccelerationFormulaSettingsViewModel : ViewModelBase .Cast() .Select(formulaType => formulaType.ToString())); - public AccelerationFormulaSettingsViewModel(BE.FormulaAccelModel formulaAccel) + public AccelerationFormulaSettingsViewModel(BE.IFormulaAccelModel formulaAccel) { FormulaAccelBE = formulaAccel; - SynchronousSettings = new SynchronousSettings((formulaAccel.GetAccelerationModelOfType(BEData.AccelerationFormulaType.Synchronous) - as BE.Formula.SynchronousAccelerationDefinitionModel)!); + SynchronousSettings = new SynchronousSettings((formulaAccel.GetSelectable(BEData.AccelerationFormulaType.Synchronous) + as BE.Formula.ISynchronousAccelerationDefinitionModel)!); - LinearSettings = new LinearSettings((formulaAccel.GetAccelerationModelOfType(BEData.AccelerationFormulaType.Linear) + LinearSettings = new LinearSettings((formulaAccel.GetSelectable(BEData.AccelerationFormulaType.Linear) as BE.Formula.LinearAccelerationDefinitionModel)!); - ClassicSettings = new ClassicSettings((formulaAccel.GetAccelerationModelOfType(BEData.AccelerationFormulaType.Classic) + ClassicSettings = new ClassicSettings((formulaAccel.GetSelectable(BEData.AccelerationFormulaType.Classic) as BE.Formula.ClassicAccelerationDefinitionModel)!); - PowerSettings = new PowerSettings((formulaAccel.GetAccelerationModelOfType(BEData.AccelerationFormulaType.Power) + PowerSettings = new PowerSettings((formulaAccel.GetSelectable(BEData.AccelerationFormulaType.Power) as BE.Formula.PowerAccelerationDefinitionModel)!); - NaturalSettings = new NaturalSettings((formulaAccel.GetAccelerationModelOfType(BEData.AccelerationFormulaType.Natural) + NaturalSettings = new NaturalSettings((formulaAccel.GetSelectable(BEData.AccelerationFormulaType.Natural) as BE.Formula.NaturalAccelerationDefinitionModel)!); - JumpSettings = new JumpSettings((formulaAccel.GetAccelerationModelOfType(BEData.AccelerationFormulaType.Jump) + JumpSettings = new JumpSettings((formulaAccel.GetSelectable(BEData.AccelerationFormulaType.Jump) as BE.Formula.JumpAccelerationDefinitionModel)!); } - public BE.FormulaAccelModel FormulaAccelBE { get; } + public BE.IFormulaAccelModel FormulaAccelBE { get; } public ObservableCollection FormulaTypesLocal => FormulaTypes; @@ -56,7 +56,7 @@ public AccelerationFormulaSettingsViewModel(BE.FormulaAccelModel formulaAccel) public class SynchronousSettings { - public SynchronousSettings(BE.Formula.SynchronousAccelerationDefinitionModel synchronousAccelModelBE) + public SynchronousSettings(BE.Formula.ISynchronousAccelerationDefinitionModel synchronousAccelModelBE) { SyncSpeed = new EditableFieldViewModel(synchronousAccelModelBE.SyncSpeed); Motivity = new EditableFieldViewModel(synchronousAccelModelBE.Motivity); diff --git a/userinterface/ViewModels/Profile/AccelerationLUTSettingsViewModel.cs b/userinterface/ViewModels/Profile/AccelerationLUTSettingsViewModel.cs index 7164b457..0b854acc 100644 --- a/userinterface/ViewModels/Profile/AccelerationLUTSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/AccelerationLUTSettingsViewModel.cs @@ -5,13 +5,13 @@ namespace userinterface.ViewModels.Profile { public partial class AccelerationLUTSettingsViewModel : ViewModelBase { - public AccelerationLUTSettingsViewModel(BE.LookupTableDefinitionModel lutAccelBE) + public AccelerationLUTSettingsViewModel(BE.ILookupTableDefinitionModel lutAccelBE) { LUTAccelBE = lutAccelBE; LUTPoints = new EditableFieldViewModel(lutAccelBE.Data); } - public BE.LookupTableDefinitionModel LUTAccelBE { get; } + public BE.ILookupTableDefinitionModel LUTAccelBE { get; } public EditableFieldViewModel LUTPoints { get; set; } } diff --git a/userinterface/ViewModels/Profile/AccelerationProfileSettingsViewModel.cs b/userinterface/ViewModels/Profile/AccelerationProfileSettingsViewModel.cs index 904ff68f..e3eb4469 100644 --- a/userinterface/ViewModels/Profile/AccelerationProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/AccelerationProfileSettingsViewModel.cs @@ -18,19 +18,21 @@ public partial class AccelerationProfileSettingsViewModel : ViewModelBase [ObservableProperty] public bool areAccelSettingsVisible; - public AccelerationProfileSettingsViewModel(BE.AccelerationModel accelerationBE) + public AccelerationProfileSettingsViewModel(BE.IAccelerationModel accelerationBE) { AccelerationBE = accelerationBE; - AccelerationFormulaSettings = new AccelerationFormulaSettingsViewModel(accelerationBE.FormulaAccel); - AccelerationLUTSettings = new AccelerationLUTSettingsViewModel(accelerationBE.LookupTableAccel); + AccelerationFormulaSettings = new AccelerationFormulaSettingsViewModel( + accelerationBE.GetSelectable(BEData.AccelerationDefinitionType.Formula) as BE.IFormulaAccelModel); + AccelerationLUTSettings = new AccelerationLUTSettingsViewModel( + accelerationBE.GetSelectable(BEData.AccelerationDefinitionType.LookupTable) as BE.ILookupTableDefinitionModel); AnisotropySettings = new AnisotropyProfileSettingsViewModel(accelerationBE.Anisotropy); CoalescionSettings = new CoalescionProfileSettingsViewModel(accelerationBE.Coalescion); // TODO: editable settings composition - AccelerationBE.DefinitionType.AutoUpdateFromInterface = true; - AccelerationBE.DefinitionType.PropertyChanged += OnDefinitionTypeChanged; + AccelerationBE.Selection.AutoUpdateFromInterface = true; + AccelerationBE.Selection.PropertyChanged += OnDefinitionTypeChanged; } - public BE.AccelerationModel AccelerationBE { get; } + public BE.IAccelerationModel AccelerationBE { get; } public ObservableCollection DefinitionTypesLocal => DefinitionTypes; @@ -44,9 +46,9 @@ public AccelerationProfileSettingsViewModel(BE.AccelerationModel accelerationBE) private void OnDefinitionTypeChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(AccelerationBE.DefinitionType.ModelValue)) + if (e.PropertyName == nameof(AccelerationBE.Selection.ModelValue)) { - AreAccelSettingsVisible = AccelerationBE.DefinitionType.ModelValue != BEData.AccelerationDefinitionType.None; + AreAccelSettingsVisible = AccelerationBE.Selection.ModelValue != BEData.AccelerationDefinitionType.None; } } } diff --git a/userinterface/ViewModels/Profile/AnisotropyProfileSettingsViewModel.cs b/userinterface/ViewModels/Profile/AnisotropyProfileSettingsViewModel.cs index d39935c5..cadb929a 100644 --- a/userinterface/ViewModels/Profile/AnisotropyProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/AnisotropyProfileSettingsViewModel.cs @@ -5,7 +5,7 @@ namespace userinterface.ViewModels.Profile { public partial class AnisotropyProfileSettingsViewModel : ViewModelBase { - public AnisotropyProfileSettingsViewModel(BE.AnisotropyModel anisotropyBE) + public AnisotropyProfileSettingsViewModel(BE.IAnisotropyModel anisotropyBE) { AnisotropyBE = anisotropyBE; DomainX = new EditableFieldViewModel(AnisotropyBE.DomainX); @@ -15,7 +15,7 @@ public AnisotropyProfileSettingsViewModel(BE.AnisotropyModel anisotropyBE) LPNorm = new NamedEditableFieldViewModel(AnisotropyBE.LPNorm); } - protected BE.AnisotropyModel AnisotropyBE { get; } + protected BE.IAnisotropyModel AnisotropyBE { get; } public EditableFieldViewModel DomainX { get; set; } diff --git a/userinterface/ViewModels/Profile/CoalescionProfileSettingsViewModel.cs b/userinterface/ViewModels/Profile/CoalescionProfileSettingsViewModel.cs index 6c71d099..2e27cf4a 100644 --- a/userinterface/ViewModels/Profile/CoalescionProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/CoalescionProfileSettingsViewModel.cs @@ -5,14 +5,14 @@ namespace userinterface.ViewModels.Profile { public partial class CoalescionProfileSettingsViewModel : ViewModelBase { - public CoalescionProfileSettingsViewModel(BE.CoalescionModel coalescionBE) + public CoalescionProfileSettingsViewModel(BE.ICoalescionModel coalescionBE) { CoalescionBE = coalescionBE; InputSmoothingHalfLife = new EditableFieldViewModel(coalescionBE.InputSmoothingHalfLife); ScaleSmoothingHalfLife = new EditableFieldViewModel(coalescionBE.ScaleSmoothingHalfLife); } - protected BE.CoalescionModel CoalescionBE { get; } + protected BE.ICoalescionModel CoalescionBE { get; } public EditableFieldViewModel InputSmoothingHalfLife { get; set; } diff --git a/userinterface/ViewModels/Profile/HiddenProfileSettingsViewModel.cs b/userinterface/ViewModels/Profile/HiddenProfileSettingsViewModel.cs index 81eb5daf..e4179df0 100644 --- a/userinterface/ViewModels/Profile/HiddenProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/HiddenProfileSettingsViewModel.cs @@ -5,7 +5,7 @@ namespace userinterface.ViewModels.Profile { public partial class HiddenProfileSettingsViewModel : ViewModelBase { - public HiddenProfileSettingsViewModel(BE.ProfileComponents.HiddenModel hiddenBE) + public HiddenProfileSettingsViewModel(BE.ProfileComponents.IHiddenModel hiddenBE) { HiddenBE = hiddenBE; RotationField = new EditableFieldViewModel(hiddenBE.RotationDegrees); @@ -16,7 +16,7 @@ public HiddenProfileSettingsViewModel(BE.ProfileComponents.HiddenModel hiddenBE) OutputSmoothingHalfLifeField = new EditableFieldViewModel(hiddenBE.OutputSmoothingHalfLife); } - protected BE.ProfileComponents.HiddenModel HiddenBE { get; } + protected BE.ProfileComponents.IHiddenModel HiddenBE { get; } public EditableFieldViewModel RotationField { get; set; } diff --git a/userinterface/ViewModels/Profile/ProfileListViewModel.cs b/userinterface/ViewModels/Profile/ProfileListViewModel.cs index 2fa99b0f..8af5eb8f 100644 --- a/userinterface/ViewModels/Profile/ProfileListViewModel.cs +++ b/userinterface/ViewModels/Profile/ProfileListViewModel.cs @@ -1,6 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using System; using System.Collections.ObjectModel; +using userspace_backend.Model; using BE = userspace_backend.Model; namespace userinterface.ViewModels.Profile @@ -20,7 +21,7 @@ public ProfileListViewModel(BE.ProfilesModel profiles, Action selectionChangeAct SelectionChangeAction = selectionChangeAction; } - public ObservableCollection Profiles => profilesModel.Profiles; + public ReadOnlyObservableCollection Profiles => profilesModel.Elements; public Action SelectionChangeAction { get; } partial void OnCurrentSelectedProfileChanged(BE.ProfileModel? value) @@ -30,22 +31,14 @@ partial void OnCurrentSelectedProfileChanged(BE.ProfileModel? value) public bool TryAddProfile() { - for (int i = 0; i < MaxProfileAttempts; i++) - { - string newProfileName = $"Profile{i}"; - if (profilesModel.TryAddNewDefaultProfile(newProfileName)) - { - return true; - } - } - return false; + return profilesModel.TryAddNewDefault(); } public void RemoveSelectedProfile() { if (CurrentSelectedProfile != null) { - _ = profilesModel.RemoveProfile(CurrentSelectedProfile); + _ = profilesModel.TryRemoveElement(CurrentSelectedProfile); } } } diff --git a/userinterface/ViewModels/Profile/ProfileSettingsViewModel.cs b/userinterface/ViewModels/Profile/ProfileSettingsViewModel.cs index db86a9c3..63f0e0aa 100644 --- a/userinterface/ViewModels/Profile/ProfileSettingsViewModel.cs +++ b/userinterface/ViewModels/Profile/ProfileSettingsViewModel.cs @@ -5,7 +5,7 @@ namespace userinterface.ViewModels.Profile { public partial class ProfileSettingsViewModel : ViewModelBase { - public ProfileSettingsViewModel(BE.ProfileModel profileBE) + public ProfileSettingsViewModel(BE.IProfileModel profileBE) { ProfileModelBE = profileBE; OutputDPIField = new EditableFieldViewModel(profileBE.OutputDPI); @@ -14,7 +14,7 @@ public ProfileSettingsViewModel(BE.ProfileModel profileBE) HiddenSettings = new HiddenProfileSettingsViewModel(profileBE.Hidden); } - protected BE.ProfileModel ProfileModelBE { get; } + protected BE.IProfileModel ProfileModelBE { get; } public EditableFieldViewModel OutputDPIField { get; set; } diff --git a/userinterface/ViewModels/Profile/ProfileViewModel.cs b/userinterface/ViewModels/Profile/ProfileViewModel.cs index 2d00d705..6fdfa996 100644 --- a/userinterface/ViewModels/Profile/ProfileViewModel.cs +++ b/userinterface/ViewModels/Profile/ProfileViewModel.cs @@ -4,14 +4,14 @@ namespace userinterface.ViewModels.Profile { public partial class ProfileViewModel : ViewModelBase { - public ProfileViewModel(BE.ProfileModel profileBE) + public ProfileViewModel(BE.IProfileModel profileBE) { ProfileModelBE = profileBE; Settings = new ProfileSettingsViewModel(profileBE); Chart = new ProfileChartViewModel(profileBE.CurvePreview); } - protected BE.ProfileModel ProfileModelBE { get; } + protected BE.IProfileModel ProfileModelBE { get; } public string CurrentName => ProfileModelBE.Name.ModelValue; diff --git a/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs b/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs index 47766f5d..5b05d957 100644 --- a/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs +++ b/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs @@ -14,7 +14,7 @@ public partial class ProfilesPageViewModel : ViewModelBase public ProfilesPageViewModel(BE.ProfilesModel profileModels) { - ProfileModels = profileModels.Profiles; + ProfileModels = profileModels.Elements; ProfileViewModels = new ObservableCollection(); UpdateProfileViewModels(); SelectedProfileView = ProfileViewModels.FirstOrDefault(); @@ -22,7 +22,7 @@ public ProfilesPageViewModel(BE.ProfilesModel profileModels) ActiveProfilesListView = new ActiveProfilesListViewModel(); } - protected IEnumerable ProfileModels { get; } + protected IEnumerable ProfileModels { get; } protected ObservableCollection ProfileViewModels { get; } diff --git a/userinterface/Views/Mapping/MappingView.axaml b/userinterface/Views/Mapping/MappingView.axaml index 2796c96a..55cd2221 100644 --- a/userinterface/Views/Mapping/MappingView.axaml +++ b/userinterface/Views/Mapping/MappingView.axaml @@ -183,7 +183,7 @@ diff --git a/userinterface/Views/Profile/AccelerationFormulaSettingsView.axaml.cs b/userinterface/Views/Profile/AccelerationFormulaSettingsView.axaml.cs index 74118125..26cd229c 100644 --- a/userinterface/Views/Profile/AccelerationFormulaSettingsView.axaml.cs +++ b/userinterface/Views/Profile/AccelerationFormulaSettingsView.axaml.cs @@ -48,7 +48,7 @@ private void SetupControls() } CreateFormulaFieldViewModel(); - var currentFormulaType = GetCurrentFormulaType(viewModel.FormulaAccelBE.FormulaType.InterfaceValue); + var currentFormulaType = GetCurrentFormulaType(viewModel.FormulaAccelBE.Selection.InterfaceValue); AddFormulaSpecificFields(currentFormulaType, viewModel); AddControlToMainPanel(); } @@ -107,9 +107,9 @@ private void OnFormulaTypeSelectionChanged(object? sender, SelectionChangedEvent return; } - viewModel.FormulaAccelBE.FormulaType.TryUpdateFromInterface(); + viewModel.FormulaAccelBE.Selection.TryUpdateFromInterface(); RemoveFormulaSpecificFields(); - var currentFormulaType = GetCurrentFormulaType(viewModel.FormulaAccelBE.FormulaType.InterfaceValue); + var currentFormulaType = GetCurrentFormulaType(viewModel.FormulaAccelBE.Selection.InterfaceValue); AddFormulaSpecificFields(currentFormulaType, viewModel); } diff --git a/userinterface/Views/Profile/AccelerationProfileSettingsView.axaml.cs b/userinterface/Views/Profile/AccelerationProfileSettingsView.axaml.cs index a27ab1fa..bc37826a 100644 --- a/userinterface/Views/Profile/AccelerationProfileSettingsView.axaml.cs +++ b/userinterface/Views/Profile/AccelerationProfileSettingsView.axaml.cs @@ -57,7 +57,7 @@ private void CreateAccelerationComboBox(AccelerationProfileSettingsViewModel vie VerticalAlignment = VerticalAlignment.Center, DataContext = viewModel, ItemsSource = viewModel.DefinitionTypesLocal, - SelectedItem = viewModel.AccelerationBE.DefinitionType.InterfaceValue + SelectedItem = viewModel.AccelerationBE.Selection.InterfaceValue }; _accelerationComboBox.SelectionChanged += OnAccelerationTypeSelectionChanged; diff --git a/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs index 2fc9fd05..e6391b9b 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsCollectionTests.cs @@ -84,6 +84,16 @@ public override TestDataType MapToData() SubData = SubCollection.MapToData(), }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(TestDataType data) + { + return SubCollection.TryMapFromData(data.SubData); + } + + protected override bool TryMapEditableSettingsFromData(TestDataType data) + { + return PropertySetting.TryUpdateModelDirectly(data.Property); + } } protected class EditableSettingsTestSubCollection : EditableSettingsCollectionV2, IEditableSettingsTestSubCollection @@ -106,6 +116,16 @@ public override TestSubDataType MapToData() SubProperty = SubPropertySetting.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(TestSubDataType data) + { + return true; + } + + protected override bool TryMapEditableSettingsFromData(TestSubDataType data) + { + return SubPropertySetting.TryUpdateModelDirectly(data.SubProperty); + } } #endregion TestClasses diff --git a/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs index 94b23ee8..2eaf163b 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsListTests.cs @@ -58,6 +58,17 @@ public override TestData MapToData() Property = PropertySetting.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(TestData data) + { + return PropertySetting.TryUpdateModelDirectly(data.Property) + & NameSetting.TryUpdateModelDirectly(data.Name); + } + + protected override bool TryMapEditableSettingsFromData(TestData data) + { + return true; + } } public class EditableSettingsTestList : EditableSettingsList, IEditableSettingsTestList @@ -76,6 +87,11 @@ public override IEnumerable MapToData() return ElementsInternal.Select(e => e.MapToData()); } + protected override string GetNameFromData(TestData data) + { + return data.Name; + } + protected override string GetNameFromElement(IEditableSettingsTestCollection element) => element.NameSetting.ModelValue; protected override void SetElementName(IEditableSettingsTestCollection element, string name) @@ -83,6 +99,11 @@ protected override void SetElementName(IEditableSettingsTestCollection element, element.NameSetting.InterfaceValue = name; element.NameSetting.TryUpdateFromInterface(); } + + protected override bool TryMapEditableSettingsFromData(IEnumerable data) + { + return true; + } } #endregion TestClasses diff --git a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs index 55e1a49d..7fdff225 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs @@ -91,12 +91,12 @@ public override int GetHashCode() } } - public interface IEditableSettingsTestA : IEditableSettingsCollectionSpecific + public interface IEditableSettingsTestA : IEditableSettingsCollectionSpecific { public IEditableSettingSpecific PropertyA { get; } } - public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific + public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific { public IEditableSettingSpecific PropertyB { get; } } @@ -107,7 +107,7 @@ public interface IEditableSettingsTestSelector : IEditableSettingsSelector< { } - public class EditableSettingsTestA : EditableSettingsCollectionV2, IEditableSettingsTestA + public class EditableSettingsTestA : EditableSettingsCollectionV2, IEditableSettingsTestA { public const string PropertyAName = $"{nameof(EditableSettingsTestA)}.{nameof(PropertyA)}"; @@ -126,9 +126,19 @@ public override TestDataA MapToData() PropertyA = PropertyA.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(TestDataA data) + { + return true; + } + + protected override bool TryMapEditableSettingsFromData(TestDataA data) + { + return PropertyA.TryUpdateModelDirectly(data.PropertyA); + } } - public class EditableSettingsTestB : EditableSettingsCollectionV2, IEditableSettingsTestB + public class EditableSettingsTestB : EditableSettingsCollectionV2, IEditableSettingsTestB { public const string PropertyBName = $"{nameof(EditableSettingsTestB)}.{nameof(PropertyB)}"; @@ -147,6 +157,16 @@ public override TestDataB MapToData() PropertyB = PropertyB.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(TestDataB data) + { + return true; + } + + protected override bool TryMapEditableSettingsFromData(TestDataB data) + { + return PropertyB.TryUpdateModelDirectly(data.PropertyB); + } } public class EditableSettingsTestSelector : EditableSettingsSelector< @@ -162,6 +182,16 @@ public EditableSettingsTestSelector( : base(serviceProvider, selection, [], []) { } + + protected override bool TryMapEditableSettingsCollectionsFromData(TestDataAbstract data) + { + return Selection.TryUpdateModelDirectly(data.Type); + } + + protected override bool TryMapEditableSettingsFromData(TestDataAbstract data) + { + return Selected.TryMapFromData(data); + } } #endregion TestClasses @@ -187,7 +217,7 @@ protected static IEditableSettingsTestSelector InitTestObject( TestDataAbstract.DefaultTestDataTypeValidator, autoUpdateFromInterface: false)); services.AddTransient(); - services.AddKeyedTransient, EditableSettingsTestA>( + services.AddKeyedTransient, EditableSettingsTestA>( EditableSettingsSelectorHelper.GetSelectionKey(TestDataAbstract.TestDataType.A)); services.AddKeyedTransient>( EditableSettingsTestA.PropertyAName, (_, _) => @@ -198,7 +228,7 @@ protected static IEditableSettingsTestSelector InitTestObject( ModelValueValidators.DefaultIntValidator, autoUpdateFromInterface: false)); services.AddTransient(); - services.AddKeyedTransient, EditableSettingsTestB>( + services.AddKeyedTransient, EditableSettingsTestB>( EditableSettingsSelectorHelper.GetSelectionKey(TestDataAbstract.TestDataType.B)); services.AddKeyedTransient>( EditableSettingsTestB.PropertyBName, (_, _) => diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index ba1e1bc4..25bad39e 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -58,10 +58,7 @@ protected void LoadDevicesFromData(IEnumerable devicesData) protected void LoadProfilesFromData(IEnumerable profileData) { - foreach (var profile in profileData) - { - Profiles.TryAdd(profile); - } + Profiles.TryMapFromData(profileData); } public void Apply() diff --git a/userspace-backend/Bootstrapper.cs b/userspace-backend/Bootstrapper.cs index 71408ad8..9dbe6306 100644 --- a/userspace-backend/Bootstrapper.cs +++ b/userspace-backend/Bootstrapper.cs @@ -35,10 +35,7 @@ public DATA.MappingSet LoadMappings() return ProfilesToLoad; } - public void WriteSettingsToDisk( - IEnumerable devices, - MappingsModel mappings, - IEnumerable profiles) + public void WriteSettingsToDisk(IEnumerable devices, MappingsModel mappings, IEnumerable profiles) { BackEndLoader.WriteSettingsToDisk(devices, mappings, profiles); } diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index 784cf5da..fea60917 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -8,7 +8,7 @@ namespace userspace_backend.Model.AccelDefinitions { - public interface IAccelerationModel : IEditableSettingsCollectionSpecific + public interface IAccelerationModel : IEditableSettingsSelector { IAnisotropyModel Anisotropy { get; } @@ -60,14 +60,16 @@ public override Acceleration MapToData() public AccelArgs MapToDriver() => ((IAccelDefinitionModel)Selected)?.MapToDriver() ?? new AccelArgs(); - protected override void TryMapEditableSettingsFromData(Acceleration data) + protected override bool TryMapEditableSettingsFromData(Acceleration data) { + return Selection.TryUpdateModelDirectly(data.Type); } - protected override void TryMapEditableSettingsCollectionsFromData(Acceleration data) + protected override bool TryMapEditableSettingsCollectionsFromData(Acceleration data) { - Anisotropy.TryMapFromData(data.Anisotropy); - Coalescion.TryMapFromData(data.Coalescion); + return Anisotropy.TryMapFromData(data.Anisotropy) + & Coalescion.TryMapFromData(data.Coalescion) + & Selected.TryMapFromData(data); } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index 1e09d3f8..73b8c770 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -61,12 +61,17 @@ public override ClassicAccel MapToData() }; } - protected override void TryMapEditableSettingsFromData(ClassicAccel data) + protected override bool TryMapEditableSettingsFromData(ClassicAccel data) { - Acceleration.InterfaceValue = data.Acceleration.ToString(); - Exponent.InterfaceValue = data.Exponent.ToString(); - Offset.InterfaceValue = data.Offset.ToString(); - Cap.InterfaceValue = data.Cap.ToString(); + return Acceleration.TryUpdateModelDirectly(data.Acceleration) + & Exponent.TryUpdateModelDirectly(data.Exponent) + & Offset.TryUpdateModelDirectly(data.Offset) + & Cap.TryUpdateModelDirectly(data.Cap); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(ClassicAccel data) + { + return true; } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs index 4690b4c0..4f29b2bf 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs @@ -51,11 +51,16 @@ public override JumpAccel MapToData() }; } - protected override void TryMapEditableSettingsFromData(JumpAccel data) + protected override bool TryMapEditableSettingsFromData(JumpAccel data) { - Smooth.InterfaceValue = data.Smooth.ToString(); - Input.InterfaceValue = data.Input.ToString(); - Output.InterfaceValue = data.Output.ToString(); + return Smooth.TryUpdateModelDirectly(data.Smooth) + & Input.TryUpdateModelDirectly(data.Input) + & Output.TryUpdateModelDirectly(data.Output); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(JumpAccel data) + { + return true; } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs index 9f76378a..e183a254 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs @@ -53,5 +53,17 @@ public override LinearAccel MapToData() Cap = Cap.ModelValue, }; } + + protected override bool TryMapEditableSettingsFromData(LinearAccel data) + { + return Acceleration.TryUpdateModelDirectly(data.Acceleration) + & Offset.TryUpdateModelDirectly(data.Offset) + & Cap.TryUpdateModelDirectly(data.Cap); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(LinearAccel data) + { + return true; + } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs index 8645c426..5ca95bad 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs @@ -51,5 +51,17 @@ public override NaturalAccel MapToData() Limit = Limit.ModelValue, }; } + + protected override bool TryMapEditableSettingsFromData(NaturalAccel data) + { + return DecayRate.TryUpdateModelDirectly(data.DecayRate) + & InputOffset.TryUpdateModelDirectly(data.InputOffset) + & Limit.TryUpdateModelDirectly(data.Limit); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(NaturalAccel data) + { + return true; + } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs index 133a7d2a..1d563c6c 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs @@ -60,5 +60,18 @@ public override PowerAccel MapToData() Cap = Cap.ModelValue, }; } + + protected override bool TryMapEditableSettingsFromData(PowerAccel data) + { + return Scale.TryUpdateModelDirectly(data.Scale) + & Exponent.TryUpdateModelDirectly(data.Exponent) + & OutputOffset.TryUpdateModelDirectly(data.OutputOffset) + & Cap.TryUpdateModelDirectly(data.Cap); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(PowerAccel data) + { + return true; + } } } diff --git a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs index a57ab714..376d602b 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs @@ -6,6 +6,13 @@ namespace userspace_backend.Model.AccelDefinitions.Formula { public interface ISynchronousAccelerationDefinitionModel : IEditableSettingsCollectionSpecific { + IEditableSettingSpecific SyncSpeed { get; } + + IEditableSettingSpecific Motivity { get; } + + IEditableSettingSpecific Gamma { get; } + + IEditableSettingSpecific Smoothness { get; } } public class SynchronousAccelerationDefinitionModel : EditableSettingsCollectionV2, ISynchronousAccelerationDefinitionModel @@ -58,5 +65,18 @@ public override SynchronousAccel MapToData() Smoothness = Smoothness.ModelValue, }; } + + protected override bool TryMapEditableSettingsFromData(SynchronousAccel data) + { + return SyncSpeed.TryUpdateModelDirectly(data.SyncSpeed) + & Motivity.TryUpdateModelDirectly(data.Motivity) + & Gamma.TryUpdateModelDirectly(data.Gamma) + & Smoothness.TryUpdateModelDirectly(data.Smoothness); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(SynchronousAccel data) + { + return true; + } } } diff --git a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs index c33cade3..0483e107 100644 --- a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs +++ b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs @@ -11,7 +11,7 @@ namespace userspace_backend.Model.AccelDefinitions { - public interface IFormulaAccelModel: IAccelDefinitionModelSpecific + public interface IFormulaAccelModel: IEditableSettingsSelector { } @@ -32,5 +32,15 @@ public FormulaAccelModel( public IEditableSettingSpecific Gain { get; set; } public AccelArgs MapToDriver() => ((IAccelDefinitionModel)Selected)?.MapToDriver() ?? new AccelArgs(); + + protected override bool TryMapEditableSettingsCollectionsFromData(FormulaAccel data) + { + return Selected.TryMapFromData(data); + } + + protected override bool TryMapEditableSettingsFromData(FormulaAccel data) + { + return Gain.TryUpdateModelDirectly(data.Gain); + } } } diff --git a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs index 659b8a2a..878d1afb 100644 --- a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs @@ -11,6 +11,10 @@ namespace userspace_backend.Model.AccelDefinitions { public interface ILookupTableDefinitionModel : IAccelDefinitionModelSpecific { + IEditableSettingSpecific ApplyAs { get; } + + IEditableSettingSpecific Data { get; } + } public class LookupTableDefinitionModel : EditableSettingsCollectionV2, ILookupTableDefinitionModel @@ -53,6 +57,17 @@ public override LookupTableAccel MapToData() Data = this.Data.ModelValue.Data, }; } + + protected override bool TryMapEditableSettingsFromData(LookupTableAccel data) + { + return ApplyAs.TryUpdateModelDirectly(data.ApplyAs) + & Data.TryUpdateModelDirectly(new LookupTableData(data.Data)); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(LookupTableAccel data) + { + return true; + } } public class LookupTableData : IComparable diff --git a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs index b467c629..a91d90e4 100644 --- a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs @@ -33,5 +33,15 @@ public override NoAcceleration MapToData() { return NoAcceleration; } + + protected override bool TryMapEditableSettingsFromData(NoAcceleration data) + { + return true; + } + + protected override bool TryMapEditableSettingsCollectionsFromData(NoAcceleration data) + { + return true; + } } } diff --git a/userspace-backend/Model/EditableSettings/EditableSetting.cs b/userspace-backend/Model/EditableSettings/EditableSetting.cs index fa8af3a1..35c296e4 100644 --- a/userspace-backend/Model/EditableSettings/EditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/EditableSetting.cs @@ -106,7 +106,7 @@ partial void OnInterfaceValueChanged(string value) } } - public bool TrySetFromData(T data) + public bool TryUpdateModelDirectly(T data) { throw new NotImplementedException(); } @@ -138,7 +138,7 @@ public EditableSettingV2( Parser = parser; Validator = validator; UpdateModelValueFromLastKnown(); - UpdateInterfaceValue(); + SetInterfaceToModel(); AutoUpdateFromInterface = autoUpdateFromInterface; } @@ -166,34 +166,34 @@ public EditableSettingV2( public bool TryUpdateFromInterface() { - if (string.IsNullOrEmpty(InterfaceValue)) - { - UpdateInterfaceValue(); - return false; - } + bool result = TryUpdateFromInterfaceImpl(out bool editedInterfaceNeedsReset); - if (!Parser.TryParse(InterfaceValue.Trim(), out T parsedValue)) + if (editedInterfaceNeedsReset) { - UpdateInterfaceValue(); - return false; + SetInterfaceToModel(); } - if (parsedValue.CompareTo(ModelValue) == 0) + return result; + } + + protected bool TryUpdateFromInterfaceImpl(out bool editedInterfaceNeedsReset) + { + editedInterfaceNeedsReset = true; + + if (string.IsNullOrEmpty(InterfaceValue)) { - return true; + return false; } - if (!Validator.Validate(parsedValue)) + if (!Parser.TryParse(InterfaceValue.Trim(), out T parsedValue)) { - UpdateInterfaceValue(); return false; } - UpdateModeValue(parsedValue); - return true; + return TryUpdateModelDirectlyImpl(parsedValue, out editedInterfaceNeedsReset); } - protected void UpdateInterfaceValue() + protected void SetInterfaceToModel() { bool previous = AllowAutoUpdateFromInterface; AllowAutoUpdateFromInterface = false; @@ -221,14 +221,29 @@ partial void OnInterfaceValueChanged(string value) } // TODO: unit test - public bool TrySetFromData(T data) + public bool TryUpdateModelDirectly(T data) { - bool previous = AllowAutoUpdateFromInterface; - AllowAutoUpdateFromInterface = false; - InterfaceValue = data.ToString(); - bool result = TryUpdateFromInterface(); - AllowAutoUpdateFromInterface = previous; - return result; + return TryUpdateModelDirectlyImpl(data, out _); + } + + private bool TryUpdateModelDirectlyImpl(T data, out bool editedInterfaceNeedsReset) + { + editedInterfaceNeedsReset = false; + + if (data.CompareTo(ModelValue) == 0) + { + return true; + } + + if (!Validator.Validate(data)) + { + editedInterfaceNeedsReset = true; + return false; + } + + UpdateModeValue(data); + return true; + } } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs index f5ba2528..e691bc20 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs @@ -119,5 +119,26 @@ protected T GenerateDefaultElement(string name) protected abstract string GetNameFromElement(T element); protected abstract void SetElementName(T element, string name); + + protected abstract string GetNameFromData(U data); + + protected override bool TryMapEditableSettingsCollectionsFromData(IEnumerable data) + { + bool result = true; + + foreach (U dataElement in data) + { + string elementName = GetNameFromData(dataElement); + + if (!TryGetElement(elementName, out T? element)) + { + element = GenerateDefaultElement(elementName); + } + + result &= element!.TryMapFromData(dataElement); + } + + return result; + } } } diff --git a/userspace-backend/Model/EditableSettings/IEditableSetting.cs b/userspace-backend/Model/EditableSettings/IEditableSetting.cs index 85fd8b3d..a57199fc 100644 --- a/userspace-backend/Model/EditableSettings/IEditableSetting.cs +++ b/userspace-backend/Model/EditableSettings/IEditableSetting.cs @@ -20,6 +20,13 @@ public interface IEditableSettingSpecific : IEditableSetting where T : ICompa { public T ModelValue { get; } - public bool TrySetFromData(T data); + /// + /// Attempts to update the model directly. Validates the input as if it had been parsed from interface. + /// This method should probably not be called from the interface. Instead, set InterfaceValue and + /// call TryUpdateFromInterface(). + /// + /// Value to which model should be tried to be set. + /// bool indicating success + public bool TryUpdateModelDirectly(T data); } } diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index 11828d9f..a869be3f 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -101,7 +101,7 @@ public bool TryAddMapping(string deviceGroupName, string profileName) return false; } - if (!Profiles.TryGetElement(profileName, out ProfileModel? profile) + if (!Profiles.TryGetElement(profileName, out IProfileModel? profile) || profile == null) { return false; @@ -110,7 +110,7 @@ public bool TryAddMapping(string deviceGroupName, string profileName) MappingGroup group = new MappingGroup() { DeviceGroup = deviceGroup, - Profile = profile, + Profile = profile as ProfileModel, Profiles = Profiles, }; diff --git a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs index 04c86913..4576f164 100644 --- a/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs +++ b/userspace-backend/Model/ProfileComponents/AnisotropyModel.cs @@ -66,5 +66,21 @@ public override Anisotropy MapToData() LPNorm = LPNorm.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(Anisotropy data) + { + // Nothing to do here + return true; + } + + protected override bool TryMapEditableSettingsFromData(Anisotropy data) + { + return DomainX.TryUpdateModelDirectly(data.Domain.X) + & DomainY.TryUpdateModelDirectly(data.Domain.Y) + & RangeX.TryUpdateModelDirectly(data.Range.X) + & RangeY.TryUpdateModelDirectly(data.Range.Y) + & LPNorm.TryUpdateModelDirectly(data.LPNorm) + & CombineXYComponents.TryUpdateModelDirectly(data.CombineXYComponents); + } } } diff --git a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs index f643b681..5e58a3ea 100644 --- a/userspace-backend/Model/ProfileComponents/CoalescionModel.cs +++ b/userspace-backend/Model/ProfileComponents/CoalescionModel.cs @@ -37,5 +37,16 @@ public override Coalescion MapToData() ScaleSmoothingHalfLife = ScaleSmoothingHalfLife.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(Coalescion data) + { + return true; + } + + protected override bool TryMapEditableSettingsFromData(Coalescion data) + { + return InputSmoothingHalfLife.TryUpdateModelDirectly(data.InputSmoothingHalfLife) + & ScaleSmoothingHalfLife.TryUpdateModelDirectly(data.ScaleSmoothingHalfLife); + } } } diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index 0eb267c1..619bcd9b 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -63,5 +63,20 @@ public override Hidden MapToData() OutputSmoothingHalfLife = OutputSmoothingHalfLife.ModelValue, }; } + + protected override bool TryMapEditableSettingsCollectionsFromData(Hidden data) + { + return true; + } + + protected override bool TryMapEditableSettingsFromData(Hidden data) + { + return RotationDegrees.TryUpdateModelDirectly(data.RotationDegrees) + & AngleSnappingDegrees.TryUpdateModelDirectly(data.AngleSnappingDegrees) + & LeftRightRatio.TryUpdateModelDirectly(data.LeftRightRatio) + & UpDownRatio.TryUpdateModelDirectly(data.UpDownRatio) + & SpeedCap.TryUpdateModelDirectly(data.SpeedCap) + & OutputSmoothingHalfLife.TryUpdateModelDirectly(data.OutputSmoothingHalfLife); + } } } diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index a6f0bec5..b0af1e3c 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -1,13 +1,10 @@ -using System.Collections.Generic; -using DATA = userspace_backend.Data; +using DATA = userspace_backend.Data; using userspace_backend.Model.AccelDefinitions; using userspace_backend.Model.EditableSettings; using userspace_backend.Model.ProfileComponents; using System; using System.ComponentModel; using userspace_backend.Common; -using System.Collections.ObjectModel; -using CommunityToolkit.Mvvm.ComponentModel; using userspace_backend.Display; using Microsoft.Extensions.DependencyInjection; @@ -21,6 +18,13 @@ public interface IProfileModel : IEditableSettingsCollectionSpecific YXRatio { get; } + IAccelerationModel Acceleration { get; } + + IHiddenModel Hidden { get; } + + ICurvePreview CurvePreview { get; } + + string CurrentNameForDisplay { get; } } public class ProfileModel : EditableSettingsCollectionV2, IProfileModel @@ -120,11 +124,17 @@ protected void RecalculateDriverDataAndCurvePreview() CurvePreview.GeneratePoints(CurrentValidatedDriverProfile); } - protected override void TryMapEditableSettingsFromData(DATA.Profile data) + protected override bool TryMapEditableSettingsFromData(DATA.Profile data) + { + return Name.TryUpdateModelDirectly(data.Name) + & OutputDPI.TryUpdateModelDirectly(data.OutputDPI) + & YXRatio.TryUpdateModelDirectly(data.YXRatio); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(DATA.Profile data) { - Name.InterfaceValue = data.Name; - OutputDPI.InterfaceValue = data.OutputDPI.ToString(); - YXRatio.InterfaceValue = data.YXRatio.ToString(); + return Acceleration.TryMapFromData(data.Acceleration) + & Hidden.TryMapFromData(data.Hidden); } } } diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 1805edd3..305b808c 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -27,14 +27,19 @@ protected override string GetNameFromElement(IProfileModel element) return element.Name.ModelValue; } - protected override void TryMapEditableSettingsFromData(IEnumerable data) + protected override bool TryMapEditableSettingsFromData(IEnumerable data) { - // No editable settings in this class + return true; } protected override void SetElementName(IProfileModel element, string name) { element.Name.InterfaceValue = name; } + + protected override string GetNameFromData(DATA.Profile data) + { + return data.Name; + } } } From 560f4ef3e0ec6788a669b3c5e70b8865eb887d64 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 13 Jul 2025 13:53:56 -0700 Subject: [PATCH 19/47] Add editableSelectable and tests pass --- .../EditableSettingsSelectorTests.cs | 11 +++--- .../EditableSettingsSelector.cs | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs index 7fdff225..eb3a091a 100644 --- a/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs +++ b/userspace-backend-tests/ModelTests/EditableSettingsSelectorTests.cs @@ -96,7 +96,8 @@ public interface IEditableSettingsTestA : IEditableSettingsCollectionSpecific PropertyA { get; } } - public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific + public interface IEditableSettingsTestB : IEditableSettingsCollectionSpecific, + IEditableSettingsCollectionSpecific { public IEditableSettingSpecific PropertyB { get; } } @@ -107,7 +108,7 @@ public interface IEditableSettingsTestSelector : IEditableSettingsSelector< { } - public class EditableSettingsTestA : EditableSettingsCollectionV2, IEditableSettingsTestA + public class EditableSettingsTestA : EditableSettingsSelectable, IEditableSettingsTestA { public const string PropertyAName = $"{nameof(EditableSettingsTestA)}.{nameof(PropertyA)}"; @@ -138,7 +139,7 @@ protected override bool TryMapEditableSettingsFromData(TestDataA data) } } - public class EditableSettingsTestB : EditableSettingsCollectionV2, IEditableSettingsTestB + public class EditableSettingsTestB : EditableSettingsSelectable, IEditableSettingsTestB { public const string PropertyBName = $"{nameof(EditableSettingsTestB)}.{nameof(PropertyB)}"; @@ -217,7 +218,7 @@ protected static IEditableSettingsTestSelector InitTestObject( TestDataAbstract.DefaultTestDataTypeValidator, autoUpdateFromInterface: false)); services.AddTransient(); - services.AddKeyedTransient, EditableSettingsTestA>( + services.AddKeyedTransient, EditableSettingsTestA>( EditableSettingsSelectorHelper.GetSelectionKey(TestDataAbstract.TestDataType.A)); services.AddKeyedTransient>( EditableSettingsTestA.PropertyAName, (_, _) => @@ -228,7 +229,7 @@ protected static IEditableSettingsTestSelector InitTestObject( ModelValueValidators.DefaultIntValidator, autoUpdateFromInterface: false)); services.AddTransient(); - services.AddKeyedTransient, EditableSettingsTestB>( + services.AddKeyedTransient, EditableSettingsTestB>( EditableSettingsSelectorHelper.GetSelectionKey(TestDataAbstract.TestDataType.B)); services.AddKeyedTransient>( EditableSettingsTestB.PropertyBName, (_, _) => diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs index 7566fbc9..47ca85ba 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs @@ -21,6 +21,40 @@ public static string GetSelectionKey(T value) where T : Enum } + public abstract class EditableSettingsSelectableIntermediate : + EditableSettingsCollectionV2 + { + protected EditableSettingsSelectableIntermediate( + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + : base(editableSettings, editableSettingsCollections) + { + } + } + + public abstract class EditableSettingsSelectable : + EditableSettingsSelectableIntermediate, + IEditableSettingsCollectionSpecific where T : class, U + { + protected EditableSettingsSelectable( + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + : base(editableSettings, editableSettingsCollections) + { + } + + public bool TryMapFromData(U data) + { + T dataCasted = data as T; + return dataCasted == null ? false : TryMapFromData(dataCasted); + } + + U IEditableSettingsCollectionSpecific.MapToData() + { + return MapToData(); + } + } + public abstract class EditableSettingsSelector : EditableSettingsCollectionV2, IEditableSettingsSelector where T : Enum From 162f0bd564160dc7ebe7a55ed0f878995fcd16c0 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 13 Jul 2025 14:11:46 -0700 Subject: [PATCH 20/47] Acceleration models now selectable --- .../AccelDefinitions/AccelDefinitionModel.cs | 7 +----- .../AccelDefinitions/AccelerationModel.cs | 1 - .../ClassicAccelerationDefinitionModel.cs | 5 +++- .../JumpAccelerationDefinitionModel.cs | 5 +++- .../LinearAccelerationDefinitionModel.cs | 5 +++- .../NaturalAccelerationDefinitionModel.cs | 5 +++- .../PowerAccelerationDefinitionModel.cs | 5 +++- .../SynchronousAccelerationDefinitionModel.cs | 5 +++- .../AccelDefinitions/FormulaAccelModel.cs | 4 ++- .../LookupTableDefinitionModel.cs | 3 +-- .../NoAccelDefinitionModel.cs | 7 ++---- .../EditableSettingsSelector.cs | 25 +++++++++++++++++++ 12 files changed, 56 insertions(+), 21 deletions(-) diff --git a/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs index 1b8f0054..17b194f1 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelDefinitionModel.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using userspace_backend.Data.Profiles; +using userspace_backend.Data.Profiles; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model.AccelDefinitions diff --git a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs index fea60917..33bb3827 100644 --- a/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs +++ b/userspace-backend/Model/AccelDefinitions/AccelerationModel.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using System; -using System.Collections.Generic; using userspace_backend.Data.Profiles; using userspace_backend.Model.EditableSettings; using userspace_backend.Model.ProfileComponents; diff --git a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs index 73b8c770..b1f0fb70 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/ClassicAccelerationDefinitionModel.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; @@ -9,7 +10,9 @@ public interface IClassicAccelerationDefinitionModel : IAccelDefinitionModelSpec { } - public class ClassicAccelerationDefinitionModel : EditableSettingsCollectionV2, IClassicAccelerationDefinitionModel + public class ClassicAccelerationDefinitionModel + : EditableSettingsSelectable, + IClassicAccelerationDefinitionModel { public const string AccelerationDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Acceleration)}"; public const string ExponentDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Exponent)}"; diff --git a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs index 4f29b2bf..a38c6131 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/JumpAccelerationDefinitionModel.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; @@ -8,7 +9,9 @@ public interface IJumpAccelerationDefinitionModel : IAccelDefinitionModelSpecifi { } - public class JumpAccelerationDefinitionModel : EditableSettingsCollectionV2, IJumpAccelerationDefinitionModel + public class JumpAccelerationDefinitionModel + : EditableSettingsSelectable, + IJumpAccelerationDefinitionModel { public const string SmoothDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Smooth)}"; public const string InputDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Input)}"; diff --git a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs index e183a254..086a8bba 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/LinearAccelerationDefinitionModel.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; @@ -8,7 +9,9 @@ public interface ILinearAccelerationDefinitionModel : IAccelDefinitionModelSpeci { } - public class LinearAccelerationDefinitionModel : EditableSettingsCollectionV2, ILinearAccelerationDefinitionModel + public class LinearAccelerationDefinitionModel + : EditableSettingsSelectable, + ILinearAccelerationDefinitionModel { public const string AccelerationDIKey = $"{nameof(LinearAccelerationDefinitionModel)}.{nameof(Acceleration)}"; public const string OffsetDIKey = $"{nameof(LinearAccelerationDefinitionModel)}.{nameof(Offset)}"; diff --git a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs index 5ca95bad..a4abde4c 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/NaturalAccelerationDefinitionModel.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; @@ -8,7 +9,9 @@ public interface INaturalAccelerationDefinitionModel : IAccelDefinitionModelSpec { } - public class NaturalAccelerationDefinitionModel : EditableSettingsCollectionV2, INaturalAccelerationDefinitionModel + public class NaturalAccelerationDefinitionModel + : EditableSettingsSelectable, + INaturalAccelerationDefinitionModel { public const string DecayRateDIKey = $"{nameof(NaturalAccelerationDefinitionModel)}.{nameof(DecayRate)}"; public const string InputOffsetDIKey = $"{nameof(NaturalAccelerationDefinitionModel)}.{nameof(InputOffset)}"; diff --git a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs index 1d563c6c..25c4df61 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/PowerAccelerationDefinitionModel.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using userspace_backend.Data.Profiles; +using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; @@ -9,7 +10,9 @@ public interface IPowerAccelerationDefinitionModel : IAccelDefinitionModelSpecif { } - public class PowerAccelerationDefinitionModel : EditableSettingsCollectionV2, IPowerAccelerationDefinitionModel + public class PowerAccelerationDefinitionModel + : EditableSettingsSelectable, + IPowerAccelerationDefinitionModel { public const string ScaleDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Scale)}"; public const string ExponentDIKey = $"{nameof(ClassicAccelerationDefinitionModel)}.{nameof(Exponent)}"; diff --git a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs index 376d602b..77a90d05 100644 --- a/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/Formula/SynchronousAccelerationDefinitionModel.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using userspace_backend.Data.Profiles.Accel; using userspace_backend.Data.Profiles.Accel.Formula; using userspace_backend.Model.EditableSettings; @@ -15,7 +16,9 @@ public interface ISynchronousAccelerationDefinitionModel : IEditableSettingsColl IEditableSettingSpecific Smoothness { get; } } - public class SynchronousAccelerationDefinitionModel : EditableSettingsCollectionV2, ISynchronousAccelerationDefinitionModel + public class SynchronousAccelerationDefinitionModel + : EditableSettingsSelectable, + ISynchronousAccelerationDefinitionModel { public const string SyncSpeedDIKey = $"{nameof(SynchronousAccelerationDefinitionModel)}.{nameof(SyncSpeed)}"; public const string MotivityDIKey = $"{nameof(SynchronousAccelerationDefinitionModel)}.{nameof(Motivity)}"; diff --git a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs index 0483e107..2647e121 100644 --- a/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs +++ b/userspace-backend/Model/AccelDefinitions/FormulaAccelModel.cs @@ -15,7 +15,9 @@ public interface IFormulaAccelModel: IEditableSettingsSelector, IFormulaAccelModel + public class FormulaAccelModel : + EditableSettingsSelectableSelector, + IFormulaAccelModel { public const string SelectionDIKey = $"{nameof(FormulaAccelModel)}.{nameof(Selection)}"; public const string GainDIKey = $"{nameof(FormulaAccelModel)}.{nameof(Gain)}"; diff --git a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs index 878d1afb..a414a2fe 100644 --- a/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/LookupTableDefinitionModel.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using System; -using System.Collections.Generic; using System.Linq; using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel; @@ -17,7 +16,7 @@ public interface ILookupTableDefinitionModel : IAccelDefinitionModelSpecific, ILookupTableDefinitionModel + public class LookupTableDefinitionModel : EditableSettingsSelectable, ILookupTableDefinitionModel { public const string ApplyAsDIKey = $"{nameof(LookupTableDefinitionModel)}.{nameof(ApplyAs)}"; public const string DataDIKey = $"{nameof(LookupTableDefinitionModel)}.{nameof(Data)}"; diff --git a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs index a91d90e4..5155103e 100644 --- a/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs +++ b/userspace-backend/Model/AccelDefinitions/NoAccelDefinitionModel.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using userspace_backend.Data.Profiles; +using userspace_backend.Data.Profiles; using userspace_backend.Data.Profiles.Accel; using userspace_backend.Model.EditableSettings; @@ -11,7 +8,7 @@ public interface INoAccelDefinitionModel : IAccelDefinitionModelSpecific, INoAccelDefinitionModel + public class NoAccelDefinitionModel : EditableSettingsSelectable, INoAccelDefinitionModel { public NoAccelDefinitionModel() : base([], []) diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs index 47ca85ba..2a33477d 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsSelector.cs @@ -93,4 +93,29 @@ public override U MapToData() return Selected.MapToData(); } } + + public abstract class EditableSettingsSelectableSelector + : EditableSettingsSelector , + IEditableSettingsCollectionSpecific where U : class, V where T : Enum + { + protected EditableSettingsSelectableSelector( + IServiceProvider serviceProvider, + IEditableSettingSpecific selection, + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + : base(serviceProvider, selection, editableSettings, editableSettingsCollections) + { + } + + public bool TryMapFromData(V data) + { + U dataCasted = data as U; + return dataCasted == null ? false : TryMapFromData(dataCasted); + } + + V IEditableSettingsCollectionSpecific.MapToData() + { + return MapToData(); + } + } } From a802aeda4b4de6c3edddd2f5a48ed45b4d473828 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 13 Jul 2025 14:26:50 -0700 Subject: [PATCH 21/47] Working DI through BackEnd and into UI --- userinterface/App.axaml.cs | 6 +++++- userinterface/ViewModels/MainWindowViewModel.cs | 4 ++-- .../ViewModels/Profile/ProfileListViewModel.cs | 4 ++-- .../ViewModels/Profile/ProfilesPageViewModel.cs | 2 +- userspace-backend/BackEnd.cs | 17 ++++++++++++----- userspace-backend/Model/MappingModel.cs | 10 +++++----- userspace-backend/Model/MappingsModel.cs | 4 ++-- userspace-backend/Model/ProfileModel.cs | 2 ++ userspace-backend/Model/ProfilesModel.cs | 6 +++++- 9 files changed, 36 insertions(+), 19 deletions(-) diff --git a/userinterface/App.axaml.cs b/userinterface/App.axaml.cs index 1db2efbc..5bd7498e 100644 --- a/userinterface/App.axaml.cs +++ b/userinterface/App.axaml.cs @@ -2,6 +2,8 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; +using Microsoft.Extensions.DependencyInjection; +using System; using userinterface.ViewModels; using userinterface.Views; using userspace_backend; @@ -18,7 +20,9 @@ public override void Initialize() public override void OnFrameworkInitializationCompleted() { - BackEnd backEnd = new BackEnd(BootstrapBackEnd()); + ServiceCollection services = new ServiceCollection(); + IServiceProvider serviceProvider = BackEndComposer.Compose(services); + IBackEnd backEnd = serviceProvider.GetRequiredService(); backEnd.Load(); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) diff --git a/userinterface/ViewModels/MainWindowViewModel.cs b/userinterface/ViewModels/MainWindowViewModel.cs index 9a1752b9..431d925b 100644 --- a/userinterface/ViewModels/MainWindowViewModel.cs +++ b/userinterface/ViewModels/MainWindowViewModel.cs @@ -16,7 +16,7 @@ public partial class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged private string _selectedPage = DefaultPage; - public MainWindowViewModel(BE.BackEnd backEnd) + public MainWindowViewModel(BE.IBackEnd backEnd) { BackEnd = backEnd; DevicesPage = new DevicesPageViewModel(backEnd.Devices); @@ -30,7 +30,7 @@ public MainWindowViewModel(BE.BackEnd backEnd) public MappingsPageViewModel MappingsPage { get; } - protected BE.BackEnd BackEnd { get; } + protected BE.IBackEnd BackEnd { get; } public string SelectedPage { diff --git a/userinterface/ViewModels/Profile/ProfileListViewModel.cs b/userinterface/ViewModels/Profile/ProfileListViewModel.cs index 8af5eb8f..4e27b92a 100644 --- a/userinterface/ViewModels/Profile/ProfileListViewModel.cs +++ b/userinterface/ViewModels/Profile/ProfileListViewModel.cs @@ -13,9 +13,9 @@ public partial class ProfileListViewModel : ViewModelBase [ObservableProperty] public BE.ProfileModel? currentSelectedProfile; - private BE.ProfilesModel profilesModel { get; } + private BE.IProfilesModel profilesModel { get; } - public ProfileListViewModel(BE.ProfilesModel profiles, Action selectionChangeAction) + public ProfileListViewModel(BE.IProfilesModel profiles, Action selectionChangeAction) { profilesModel = profiles; SelectionChangeAction = selectionChangeAction; diff --git a/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs b/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs index 5b05d957..8228e13e 100644 --- a/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs +++ b/userinterface/ViewModels/Profile/ProfilesPageViewModel.cs @@ -12,7 +12,7 @@ public partial class ProfilesPageViewModel : ViewModelBase [ObservableProperty] public ProfileViewModel? selectedProfileView; - public ProfilesPageViewModel(BE.ProfilesModel profileModels) + public ProfilesPageViewModel(BE.IProfilesModel profileModels) { ProfileModels = profileModels.Elements; ProfileViewModels = new ObservableCollection(); diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 25bad39e..9f73fddb 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -12,12 +12,19 @@ public interface IBackEnd void Load(); void Apply(); + + DevicesModel Devices { get; } + + MappingsModel Mappings { get; } + + IProfilesModel Profiles { get; } } public class BackEnd { - - public BackEnd(IBackEndLoader backEndLoader) + public BackEnd( + IBackEndLoader backEndLoader, + IProfilesModel profilesModel) { // TODO: fully construct BackEnd via DI ServiceCollection services = new ServiceCollection(); @@ -25,14 +32,14 @@ public BackEnd(IBackEndLoader backEndLoader) BackEndLoader = backEndLoader; Devices = new DevicesModel(serviceProvider.GetRequiredService()); - Profiles = new ProfilesModel(serviceProvider); + Profiles = profilesModel; } public DevicesModel Devices { get; set; } public MappingsModel Mappings { get; set; } - public ProfilesModel Profiles { get; set; } + public IProfilesModel Profiles { get; set; } protected IBackEndLoader BackEndLoader { get; set; } @@ -117,7 +124,7 @@ protected IEnumerable MapToDriverDevices(MappingModel mapping) protected IEnumerable MapToDriverProfiles(MappingModel mapping) { - IEnumerable ProfilesToMap = mapping.IndividualMappings.Select(m => m.Profile).Distinct(); + IEnumerable ProfilesToMap = mapping.IndividualMappings.Select(m => m.Profile).Distinct(); return ProfilesToMap.Select(p => p.CurrentValidatedDriverProfile); } diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index a869be3f..e6ada9a7 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -20,7 +20,7 @@ public MappingModel( Mapping dataObject, IModelValueValidator nameValidator, DeviceGroups deviceGroups, - ProfilesModel profiles) : base(dataObject) + IProfilesModel profiles) : base(dataObject) { NameValidator = nameValidator; SetActive = true; @@ -45,7 +45,7 @@ public MappingModel( protected DeviceGroups DeviceGroups { get; } - protected ProfilesModel Profiles { get; } + protected IProfilesModel Profiles { get; } public override Mapping MapToData() { @@ -110,7 +110,7 @@ public bool TryAddMapping(string deviceGroupName, string profileName) MappingGroup group = new MappingGroup() { DeviceGroup = deviceGroup, - Profile = profile as ProfileModel, + Profile = profile, Profiles = Profiles, }; @@ -141,9 +141,9 @@ public class MappingGroup { public DeviceGroupModel DeviceGroup { get; set; } - public ProfileModel Profile { get; set; } + public IProfileModel Profile { get; set; } // This is here for easy binding - public ProfilesModel Profiles { get; set; } + public IProfilesModel Profiles { get; set; } } } diff --git a/userspace-backend/Model/MappingsModel.cs b/userspace-backend/Model/MappingsModel.cs index 2c277e61..9883fb6c 100644 --- a/userspace-backend/Model/MappingsModel.cs +++ b/userspace-backend/Model/MappingsModel.cs @@ -10,7 +10,7 @@ namespace userspace_backend.Model { public class MappingsModel : EditableSettingsCollection { - public MappingsModel(DATA.MappingSet dataObject, DeviceGroups deviceGroups, ProfilesModel profiles) + public MappingsModel(DATA.MappingSet dataObject, DeviceGroups deviceGroups, IProfilesModel profiles) : base(dataObject) { DeviceGroups = deviceGroups; @@ -23,7 +23,7 @@ public MappingsModel(DATA.MappingSet dataObject, DeviceGroups deviceGroups, Prof protected DeviceGroups DeviceGroups { get; } - protected ProfilesModel Profiles { get; } + protected IProfilesModel Profiles { get; } protected MappingNameValidator NameValidator { get; } diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index b0af1e3c..cc489eb4 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -25,6 +25,8 @@ public interface IProfileModel : IEditableSettingsCollectionSpecific, IProfileModel diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 305b808c..65c76697 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -8,7 +8,11 @@ namespace userspace_backend.Model { - public class ProfilesModel : EditableSettingsList + public interface IProfilesModel : IEditableSettingsList + { + } + + public class ProfilesModel : EditableSettingsList, IProfilesModel { // TODO: DI - hand default profile to profiles model // public static readonly ProfileModel DefaultProfile = new ProfileModel( From 3c2f7f0f02ae9efecf8371dc21bb611e231727b1 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 13 Jul 2025 15:02:56 -0700 Subject: [PATCH 22/47] Starting DI through devices --- userspace-backend/BackEndComposer.cs | 480 +++++++++++--------- userspace-backend/Model/DeviceGroupModel.cs | 41 -- userspace-backend/Model/DeviceModel.cs | 113 +++-- 3 files changed, 320 insertions(+), 314 deletions(-) delete mode 100644 userspace-backend/Model/DeviceGroupModel.cs diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 964fb729..b842920b 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -23,46 +23,46 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( HiddenModel.RotationDegreesDIKey, (_, _) => - new EditableSettingV2( - displayName: "Rotation", + new EditableSettingV2( + displayName: "Rotation", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); + services.AddKeyedTransient>( + HiddenModel.AngleSnappingDegreesDIKey, (_, _) => + new EditableSettingV2( + displayName: "Angle Snapping", initialValue: 0, parser: UserInputParsers.DoubleParser, validator: ModelValueValidators.DefaultDoubleValidator)); - services.AddKeyedTransient>( - HiddenModel.AngleSnappingDegreesDIKey, (_, _) => - new EditableSettingV2( - displayName: "Angle Snapping", - initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( HiddenModel.LeftRightRatioDIKey, (_, _) => - new EditableSettingV2( - displayName: "L/R Ratio", - initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "L/R Ratio", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( HiddenModel.UpDownRatioDIKey, (_, _) => - new EditableSettingV2( - displayName: "U/D Ratio", - initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "U/D Ratio", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( HiddenModel.SpeedCapDIKey, (_, _) => - new EditableSettingV2( - displayName: "Speed Cap", - initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Speed Cap", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( HiddenModel.OutputSmoothingHalfLifeDIKey, (_, _) => - new EditableSettingV2( - displayName: "Output Smoothing Half-Life", - initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Output Smoothing Half-Life", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion Hidden @@ -71,18 +71,18 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( CoalescionModel.InputSmoothingHalfLifeDIKey, (_, _) => - new EditableSettingV2( - displayName: "Input Smoothing Half-Life", - initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Input Smoothing Half-Life", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( CoalescionModel.ScaleSmoothingHalfLifeDIKey, (_, _) => - new EditableSettingV2( - displayName: "Scale Smoothing Half-Life", - initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Scale Smoothing Half-Life", + initialValue: 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion Coalescion @@ -91,46 +91,46 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( AnisotropyModel.DomainXDIKey, (_, _) => - new EditableSettingV2( - displayName: "Domain X", - initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Domain X", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( AnisotropyModel.DomainYDIKey, (_, _) => - new EditableSettingV2( - displayName: "Domain Y", - initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Domain Y", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( AnisotropyModel.RangeXDIKey, (_, _) => - new EditableSettingV2( - displayName: "Range X", - initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Range X", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( AnisotropyModel.RangeYDIKey, (_, _) => - new EditableSettingV2( - displayName: "Range Y", - initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Range Y", + initialValue: 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( AnisotropyModel.LPNormDIKey, (_, _) => - new EditableSettingV2( - displayName: "LP Norm", - initialValue: 2, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "LP Norm", + initialValue: 2, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( AnisotropyModel.CombineXYComponentsDIKey, (_, _) => - new EditableSettingV2( - displayName: "Combine X and Y Components", - initialValue: false, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator)); + new EditableSettingV2( + displayName: "Combine X and Y Components", + initialValue: false, + parser: UserInputParsers.BoolParser, + validator: ModelValueValidators.DefaultBoolValidator)); #endregion Anisotropy @@ -139,11 +139,11 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( AccelerationModel.SelectionDIKey, (_, _) => - new EditableSettingV2( - displayName: "Definition Type", - initialValue: AccelerationDefinitionType.None, - parser: UserInputParsers.AccelerationDefinitionTypeParser, - validator: ModelValueValidators.DefaultAccelerationTypeValidator)); + new EditableSettingV2( + displayName: "Definition Type", + initialValue: AccelerationDefinitionType.None, + parser: UserInputParsers.AccelerationDefinitionTypeParser, + validator: ModelValueValidators.DefaultAccelerationTypeValidator)); #endregion Acceleration @@ -152,19 +152,19 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( FormulaAccelModel.SelectionDIKey, (_, _) => - new EditableSettingV2( - displayName: "Formula Type", - initialValue: AccelerationFormulaType.Synchronous, - parser: UserInputParsers.AccelerationFormulaTypeParser, - validator: ModelValueValidators.DefaultAccelerationFormulaTypeValidator, - autoUpdateFromInterface: true)); + new EditableSettingV2( + displayName: "Formula Type", + initialValue: AccelerationFormulaType.Synchronous, + parser: UserInputParsers.AccelerationFormulaTypeParser, + validator: ModelValueValidators.DefaultAccelerationFormulaTypeValidator, + autoUpdateFromInterface: true)); services.AddKeyedTransient>( FormulaAccelModel.GainDIKey, (_, _) => - new EditableSettingV2( - displayName: "Apply to Gain", - initialValue: false, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator)); + new EditableSettingV2( + displayName: "Apply to Gain", + initialValue: false, + parser: UserInputParsers.BoolParser, + validator: ModelValueValidators.DefaultBoolValidator)); #endregion FormulaAccel @@ -173,18 +173,18 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( LookupTableDefinitionModel.ApplyAsDIKey, (_, _) => - new EditableSettingV2( - displayName: "Apply as", - initialValue: LookupTableType.Velocity, - parser: UserInputParsers.LookupTableTypeParser, - validator: ModelValueValidators.DefaultLookupTableTypeValidator)); + new EditableSettingV2( + displayName: "Apply as", + initialValue: LookupTableType.Velocity, + parser: UserInputParsers.LookupTableTypeParser, + validator: ModelValueValidators.DefaultLookupTableTypeValidator)); services.AddKeyedTransient>( LookupTableDefinitionModel.DataDIKey, (_, _) => - new EditableSettingV2( - displayName: "Data", - initialValue: new LookupTableData(), - parser: UserInputParsers.LookupTableDataParser, - validator: ModelValueValidators.DefaultLookupTableDataValidator)); + new EditableSettingV2( + displayName: "Data", + initialValue: new LookupTableData(), + parser: UserInputParsers.LookupTableDataParser, + validator: ModelValueValidators.DefaultLookupTableDataValidator)); #endregion LookupTable @@ -199,32 +199,32 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( SynchronousAccelerationDefinitionModel.SyncSpeedDIKey, (_, _) => - new EditableSettingV2( - displayName: "Sync Speed", - 15, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Sync Speed", + 15, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( SynchronousAccelerationDefinitionModel.MotivityDIKey, (_, _) => - new EditableSettingV2( - displayName: "Motivity", - 1.4, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Motivity", + 1.4, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( SynchronousAccelerationDefinitionModel.GammaDIKey, (_, _) => - new EditableSettingV2( - displayName: "Gamma", - 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Gamma", + 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( SynchronousAccelerationDefinitionModel.SmoothnessDIKey, (_, _) => - new EditableSettingV2( - displayName: "Smoothness", - 0.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Smoothness", + 0.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion SynchronousAccel @@ -233,25 +233,25 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( LinearAccelerationDefinitionModel.AccelerationDIKey, (_, _) => - new EditableSettingV2( - displayName: "Acceleration", - 0.01, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Acceleration", + 0.01, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( LinearAccelerationDefinitionModel.OffsetDIKey, (_, _) => - new EditableSettingV2( - displayName: "Offset", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( LinearAccelerationDefinitionModel.CapDIKey, (_, _) => - new EditableSettingV2( - displayName: "Cap", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Cap", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion LinearAccel @@ -260,32 +260,32 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( ClassicAccelerationDefinitionModel.AccelerationDIKey, (_, _) => - new EditableSettingV2( - displayName: "Acceleration", - 0.01, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Acceleration", + 0.01, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( ClassicAccelerationDefinitionModel.ExponentDIKey, (_, _) => - new EditableSettingV2( - displayName: "Exponent", - 2, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Exponent", + 2, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( ClassicAccelerationDefinitionModel.OffsetDIKey, (_, _) => - new EditableSettingV2( - displayName: "Offset", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( ClassicAccelerationDefinitionModel.CapDIKey, (_, _) => - new EditableSettingV2( - displayName: "Cap", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Cap", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion ClassicAccel @@ -294,32 +294,32 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( PowerAccelerationDefinitionModel.ScaleDIKey, (_, _) => - new EditableSettingV2( - displayName: "Scale", - 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Scale", + 1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( PowerAccelerationDefinitionModel.ExponentDIKey, (_, _) => - new EditableSettingV2( - displayName: "Exponent", - 0.05, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Exponent", + 0.05, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( PowerAccelerationDefinitionModel.OutputOffsetDIKey, (_, _) => - new EditableSettingV2( - displayName: "Output Offset", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Output Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( PowerAccelerationDefinitionModel.CapDIKey, (_, _) => - new EditableSettingV2( - displayName: "Cap", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Cap", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion PowerAccel @@ -328,25 +328,25 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( JumpAccelerationDefinitionModel.SmoothDIKey, (_, _) => - new EditableSettingV2( - displayName: "Smooth", - 0.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Smooth", + 0.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( JumpAccelerationDefinitionModel.InputDIKey, (_, _) => - new EditableSettingV2( - displayName: "Input", - 15, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Input", + 15, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( JumpAccelerationDefinitionModel.OutputDIKey, (_, _) => - new EditableSettingV2( - displayName: "Output", - 1.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Output", + 1.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion JumpAccel @@ -355,25 +355,25 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( NaturalAccelerationDefinitionModel.DecayRateDIKey, (_, _) => - new EditableSettingV2( - displayName: "Decay Rate", - 0.1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Decay Rate", + 0.1, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( NaturalAccelerationDefinitionModel.InputOffsetDIKey, (_, _) => - new EditableSettingV2( - displayName: "Input Offset", - 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Input Offset", + 0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); services.AddKeyedTransient>( NaturalAccelerationDefinitionModel.LimitDIKey, (_, _) => - new EditableSettingV2( - displayName: "Limit", - 1.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Limit", + 1.5, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion NaturalAccel @@ -382,29 +382,77 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( ProfileModel.NameDIKey, (_, _) => - new EditableSettingV2( - displayName: "Name", - "Empty", - parser: UserInputParsers.StringParser, - // TODO: DI - change to max name length validator - validator: ModelValueValidators.DefaultStringValidator)); + new EditableSettingV2( + displayName: "Name", + "Empty", + parser: UserInputParsers.StringParser, + // TODO: DI - change to max name length validator + validator: ModelValueValidators.DefaultStringValidator)); services.AddKeyedTransient>( ProfileModel.OutputDPIDIKey, (_, _) => - new EditableSettingV2( - displayName: "Output DPI", - 1000, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator)); + new EditableSettingV2( + displayName: "Output DPI", + 1000, + parser: UserInputParsers.IntParser, + validator: ModelValueValidators.DefaultIntValidator)); services.AddKeyedTransient>( ProfileModel.YXRatioDIKey, (_, _) => - new EditableSettingV2( - displayName: "Y/X Ratio", - 1.0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + new EditableSettingV2( + displayName: "Y/X Ratio", + 1.0, + parser: UserInputParsers.DoubleParser, + validator: ModelValueValidators.DefaultDoubleValidator)); #endregion Profile + #region DeviceGroup + + services.AddTransient(); + services.AddKeyedTransient>( + DeviceModel.NameDIKey, (_, _) => + new EditableSettingV2( + displayName: "Name", + initialValue: "name", + parser: UserInputParsers.StringParser, + validator: ModelValueValidators.DefaultStringValidator)); + services.AddKeyedTransient>( + DeviceModel.HardwareIDDIKey, (_, _) => + new EditableSettingV2( + displayName: "Hardware ID", + initialValue: "hwid", + parser: UserInputParsers.StringParser, + validator: ModelValueValidators.DefaultStringValidator)); + services.AddKeyedTransient>( + DeviceModel.DPIDIKey, (_, _) => + new EditableSettingV2( + displayName: "DPI", + initialValue: 1000, + parser: UserInputParsers.IntParser, + validator: ModelValueValidators.DefaultIntValidator)); + services.AddKeyedTransient>( + DeviceModel.PollRateDIKey, (_, _) => + new EditableSettingV2( + displayName: "Polling Rate", + initialValue: 1000, + parser: UserInputParsers.IntParser, + validator: ModelValueValidators.DefaultIntValidator)); + services.AddKeyedTransient>( + DeviceModel.IgnoreDIKey, (_, _) => + new EditableSettingV2( + displayName: "Ignore", + initialValue: false, + parser: UserInputParsers.BoolParser, + validator: ModelValueValidators.DefaultBoolValidator)); + services.AddKeyedTransient>( + DeviceModel.DeviceGroupDIKey, (_, _) => + new EditableSettingV2( + displayName: "Device Group", + initialValue: "default", + parser: UserInputParsers.StringParser, + validator: ModelValueValidators.DefaultStringValidator)); + + #endregion DeviceGroup + return services.BuildServiceProvider(); } } diff --git a/userspace-backend/Model/DeviceGroupModel.cs b/userspace-backend/Model/DeviceGroupModel.cs deleted file mode 100644 index a1c4df15..00000000 --- a/userspace-backend/Model/DeviceGroupModel.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using userspace_backend.Data; -using userspace_backend.Model.EditableSettings; - -namespace userspace_backend.Model -{ - public class DeviceGroupModel : EditableSetting, IComparable - { - public DeviceGroupModel(string dataObject, IModelValueValidator validator) - : base("Device Group", dataObject, UserInputParsers.StringParser, validator) - { - } - - public int CompareTo(object? obj) - { - DeviceGroupModel other = obj as DeviceGroupModel; - - if (other == null) - { - return int.MaxValue; - } - - return other.ModelValue.CompareTo(ModelValue); - } - - public override bool Equals(object? obj) - { - return obj is DeviceGroupModel model && - string.Equals(model.ModelValue, this.ModelValue, StringComparison.InvariantCultureIgnoreCase); - } - - public override int GetHashCode() - { - return HashCode.Combine(ModelValue); - } - } -} diff --git a/userspace-backend/Model/DeviceModel.cs b/userspace-backend/Model/DeviceModel.cs index d422a32a..3f3338c0 100644 --- a/userspace-backend/Model/DeviceModel.cs +++ b/userspace-backend/Model/DeviceModel.cs @@ -1,24 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using userspace_backend.Data; +using userspace_backend.Data; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model { - public class DeviceModel : EditableSettingsCollection + public interface IDeviceModel : IEditableSettingsCollectionSpecific { + IEditableSettingSpecific Name { get; } + + IEditableSettingSpecific HardwareID { get; } + + IEditableSettingSpecific DPI { get; } + + IEditableSettingSpecific PollRate { get; } + + IEditableSettingSpecific Ignore { get; } + + IEditableSettingSpecific DeviceGroup { get; } + } + + public class DeviceModel : EditableSettingsCollectionV2, IDeviceModel + { + public const string NameDIKey = $"{nameof(DeviceModel)}.{nameof(Name)}"; + public const string HardwareIDDIKey = $"{nameof(DeviceModel)}.{nameof(HardwareID)}"; + public const string DPIDIKey = $"{nameof(DeviceModel)}.{nameof(DPI)}"; + public const string PollRateDIKey = $"{nameof(DeviceModel)}.{nameof(PollRate)}"; + public const string IgnoreDIKey = $"{nameof(DeviceModel)}.{nameof(Ignore)}"; + public const string DeviceGroupDIKey = $"{nameof(DeviceModel)}.{nameof(DeviceGroup)}"; + public DeviceModel( - Device device, - DeviceGroupModel deviceGroup, - DeviceModelNameValidator deviceModelNameValidator, - DeviceModelHWIDValidator deviceModelHWIDValidator) - : base(device) + IEditableSettingSpecific name, + IEditableSettingSpecific hardwareID, + IEditableSettingSpecific dpi, + IEditableSettingSpecific pollRate, + IEditableSettingSpecific ignore, + IEditableSettingSpecific deviceGroup) + : base([name, hardwareID, dpi, pollRate, ignore, deviceGroup], []) { + Name = name; + HardwareID = hardwareID; + DPI = dpi; + PollRate = pollRate; + Ignore = ignore; DeviceGroup = deviceGroup; - // TODO: validator composition - //Name.Validator = deviceModelNameValidator; - //HardwareID.Validator = deviceModelHWIDValidator; } public IEditableSettingSpecific Name { get; protected set; } @@ -31,62 +54,38 @@ public DeviceModel( public IEditableSettingSpecific Ignore { get; protected set; } - public DeviceGroupModel DeviceGroup { get; set; } + public IEditableSettingSpecific DeviceGroup { get; set; } protected DeviceModelNameValidator DeviceModelNameValidator { get; } protected DeviceModelHWIDValidator DeviceModelHWIDValidator { get; } - protected override IEnumerable EnumerateEditableSettings() - { - return [Name, HardwareID, DPI, PollRate, Ignore, DeviceGroup]; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() + public override Device MapToData() { - return []; + return new Device() + { + Name = Name.ModelValue, + HWID = HardwareID.ModelValue, + DPI = DPI.ModelValue, + PollingRate = PollRate.ModelValue, + Ignore = Ignore.ModelValue, + DeviceGroup = DeviceGroup.ModelValue, + }; } - protected override void InitEditableSettingsAndCollections(Device device) + protected override bool TryMapEditableSettingsFromData(Device data) { - Name = new EditableSetting( - displayName: "Name", - initialValue: device.Name, - parser: UserInputParsers.StringParser, - validator: ModelValueValidators.DefaultStringValidator); - HardwareID = new EditableSetting( - displayName: "Hardware ID", - initialValue: device.HWID, - parser: UserInputParsers.StringParser, - validator: ModelValueValidators.DefaultStringValidator); - DPI = new EditableSetting( - displayName: "DPI", - initialValue: device.DPI, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator); - PollRate = new EditableSetting( - displayName: "Polling Rate", - initialValue: device.PollingRate, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator); - Ignore = new EditableSetting( - displayName: "Ignore", - initialValue: device.Ignore, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator); + return Name.TryUpdateModelDirectly(data.Name) + & HardwareID.TryUpdateModelDirectly(data.HWID) + & DPI.TryUpdateModelDirectly(data.DPI) + & PollRate.TryUpdateModelDirectly(data.PollingRate) + & Ignore.TryUpdateModelDirectly(data.Ignore) + & DeviceGroup.TryUpdateModelDirectly(data.DeviceGroup); } - public override Device MapToData() + protected override bool TryMapEditableSettingsCollectionsFromData(Device data) { - return new Device() - { - Name = this.Name.ModelValue, - HWID = this.HardwareID.ModelValue, - DPI = this.DPI.ModelValue, - PollingRate = this.PollRate.ModelValue, - Ignore = this.Ignore.ModelValue, - DeviceGroup = DeviceGroup.ModelValue, - }; + return true; } } } From d5c15d10cbf632442397b16723bd04488efbb0d6 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 13 Jul 2025 17:49:10 -0700 Subject: [PATCH 23/47] Starting DI through models --- userspace-backend/BackEnd.cs | 4 +- userspace-backend/BackEndComposer.cs | 13 +++ userspace-backend/Model/DeviceGroups.cs | 5 + userspace-backend/Model/DevicesModel.cs | 121 ++++------------------- userspace-backend/Model/MappingModel.cs | 65 +++++------- userspace-backend/Model/ProfilesModel.cs | 2 +- 6 files changed, 61 insertions(+), 149 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 9f73fddb..f741641f 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -128,9 +128,9 @@ protected IEnumerable MapToDriverProfiles(MappingModel mapping) return ProfilesToMap.Select(p => p.CurrentValidatedDriverProfile); } - protected IEnumerable MapToDriverDevices(DeviceGroupModel dg, string profileName) + protected IEnumerable MapToDriverDevices(string dg, string profileName) { - IEnumerable deviceModels = Devices.Devices.Where(d => d.DeviceGroup.Equals(dg)); + IEnumerable deviceModels = Devices.Devices.Where(d => d.DeviceGroup.ModelValue.Equals(dg)); return deviceModels.Select(dm => MapToDriverDevice(dm, profileName)); } diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index b842920b..fc795392 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -453,6 +453,19 @@ public static IServiceProvider Compose(IServiceCollection services) #endregion DeviceGroup + #region Mapping + + services.AddTransient(); + services.AddKeyedTransient>( + MappingModel.NameDIKey, (_, _) => + new EditableSettingV2( + displayName: "Name", + initialValue: "name", + parser: UserInputParsers.StringParser, + validator: ModelValueValidators.DefaultStringValidator)); + + #endregion Mapping + return services.BuildServiceProvider(); } } diff --git a/userspace-backend/Model/DeviceGroups.cs b/userspace-backend/Model/DeviceGroups.cs index 3ec2c86c..dca2a3ed 100644 --- a/userspace-backend/Model/DeviceGroups.cs +++ b/userspace-backend/Model/DeviceGroups.cs @@ -7,6 +7,11 @@ namespace userspace_backend.Model { + public interface IDeviceGroups + { + + } + public class DeviceGroups : EditableSettingsCollection> { public static readonly DeviceGroupModel DefaultDeviceGroup = diff --git a/userspace-backend/Model/DevicesModel.cs b/userspace-backend/Model/DevicesModel.cs index 66b28dd2..f3db5cdd 100644 --- a/userspace-backend/Model/DevicesModel.cs +++ b/userspace-backend/Model/DevicesModel.cs @@ -8,131 +8,44 @@ namespace userspace_backend.Model { - public class DevicesModel + public interface IDevicesModel : IEditableSettingsList { - public DevicesModel(ISystemDevicesProvider systemDevicesProvider) + } + + public class DevicesModel : EditableSettingsList, IDevicesModel + { + public DevicesModel( + IServiceProvider serviceProvider, + ISystemDevicesProvider systemDevicesProvider) + : base(serviceProvider, [], []) { - Devices = new ObservableCollection(); - DeviceGroups = new DeviceGroups([]); - DeviceModelNameValidator = new DeviceModelNameValidator(this); - DeviceModelHWIDValidator = new DeviceModelHWIDValidator(this); SystemDevices = systemDevicesProvider; } public DeviceGroups DeviceGroups { get; set; } - public IEnumerable DevicesEnumerable { get => Devices; } - - public ObservableCollection Devices { get; set; } - - public ISystemDevicesProvider SystemDevices { get; protected set; } - protected DeviceModelNameValidator DeviceModelNameValidator { get; } - - protected DeviceModelHWIDValidator DeviceModelHWIDValidator { get; } + protected override string DefaultNameTemplate => "Device"; - public bool DoesDeviceAlreadyExist(string name, string hwid) + protected override string GetNameFromElement(IDeviceModel element) { - return Devices.Any(d => - string.Equals(d.Name.ModelValue, name, StringComparison.InvariantCultureIgnoreCase) - || string.Equals(d.HardwareID.ModelValue, hwid, StringComparison.InvariantCultureIgnoreCase)); + return element.Name.ModelValue; } - public bool DoesDeviceNameAlreadyExist(string name) + protected override void SetElementName(IDeviceModel element, string name) { - return Devices.Any(d => - string.Equals(d.Name.ModelValue, name, StringComparison.InvariantCultureIgnoreCase)); + element.Name.TryUpdateModelDirectly(name); } - public bool DoesDeviceHardwareIDAlreadyExist(string hwid) + protected override string GetNameFromData(Device data) { - return Devices.Any(d => - string.Equals(d.HardwareID.ModelValue, hwid, StringComparison.InvariantCultureIgnoreCase)); + return data.Name; } - protected bool TryGetDefaultDevice([MaybeNullWhen(false)] out Device device) + protected override bool TryMapEditableSettingsFromData(IEnumerable data) { - for (int i = 0; i < 10; i++) - { - string deviceNameToAdd = $"Device{i}"; - if (DoesDeviceNameAlreadyExist(deviceNameToAdd)) - { - continue; - } - - device = new() - { - Name = deviceNameToAdd, - HWID = "", - DPI = 1600, - PollingRate = 1000, - DeviceGroup = "Default", - }; - - return true; - } - - device = null; - return false; - } - - public bool TryAddDevice(Device? deviceData = null) - { - if (deviceData is null) - { - if (!TryGetDefaultDevice(out var defaultDevice)) - { - return false; - } - - deviceData = defaultDevice; - } - else if (DoesDeviceAlreadyExist(deviceData.Name, deviceData.HWID)) - { - return false; - } - - DeviceGroupModel deviceGroup = DeviceGroups.AddOrGetDeviceGroup(deviceData.DeviceGroup); - DeviceModel deviceModel = new DeviceModel(deviceData, deviceGroup, DeviceModelNameValidator, DeviceModelHWIDValidator); - Devices.Add(deviceModel); - return true; } - - public bool RemoveDevice(DeviceModel device) - { - return Devices.Remove(device); - } - } - - public class DeviceModelNameValidator : IModelValueValidator - { - public DeviceModelNameValidator(DevicesModel devices) - { - Devices = devices; - } - - public DevicesModel Devices { get; } - - public bool Validate(string modelValue) - { - return !Devices.DoesDeviceNameAlreadyExist(modelValue); - } - } - - public class DeviceModelHWIDValidator : IModelValueValidator - { - public DeviceModelHWIDValidator(DevicesModel devices) - { - Devices = devices; - } - - public DevicesModel Devices { get; } - - public bool Validate(string modelValue) - { - return !Devices.DoesDeviceHardwareIDAlreadyExist(modelValue); - } } } diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index e6ada9a7..68d3448b 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -1,49 +1,41 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using DATA = userspace_backend.Data; -using userspace_backend.Model.AccelDefinitions; using userspace_backend.Model.EditableSettings; -using userspace_backend.Model.ProfileComponents; using userspace_backend.Data; -using CommunityToolkit.Mvvm.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; namespace userspace_backend.Model { - public class MappingModel : EditableSettingsCollection + public interface IMappingModel : IEditableSettingsCollectionSpecific { + + } + + public class MappingModel: EditableSettingsCollectionV2, IMappingModel + { + public const string NameDIKey = $"{nameof(MappingModel)}.{nameof(Name)}"; + public MappingModel( - Mapping dataObject, + IEditableSettingSpecific name, IModelValueValidator nameValidator, - DeviceGroups deviceGroups, - IProfilesModel profiles) : base(dataObject) + IDeviceGroups deviceGroups, + IProfilesModel profiles) : base([], []) { + Name = name; NameValidator = nameValidator; SetActive = true; DeviceGroups = deviceGroups; Profiles = profiles; - InitIndividualMappings(dataObject); - DeviceGroupsStillUnmapped = new ObservableCollection(); - FindDeviceGroupsStillUnmapped(); - IndividualMappings.CollectionChanged += OnIndividualMappingsChanged; - DeviceGroups.DeviceGroupModels.CollectionChanged += OnIndividualMappingsChanged; } public bool SetActive { get; set; } public IEditableSettingSpecific Name { get; set; } - public ObservableCollection IndividualMappings { get; protected set; } - - public ObservableCollection DeviceGroupsStillUnmapped { get; protected set; } - protected IModelValueValidator NameValidator { get; } - protected DeviceGroups DeviceGroups { get; } + protected IDeviceGroups DeviceGroups { get; } protected IProfilesModel Profiles { get; } @@ -63,27 +55,6 @@ public override Mapping MapToData() return mapping; } - protected override IEnumerable EnumerateEditableSettings() - { - return []; - } - - protected override IEnumerable EnumerateEditableSettingsCollections() - { - return []; - } - - protected override void InitEditableSettingsAndCollections(Mapping dataObject) - { - Name = new EditableSetting( - displayName: "Name", - initialValue: dataObject.Name, - parser: UserInputParsers.StringParser, - validator: NameValidator); - - IndividualMappings = new ObservableCollection(); - } - protected void InitIndividualMappings(Mapping dataObject) { foreach (var kvp in dataObject.GroupsToProfiles) @@ -135,6 +106,16 @@ protected void OnIndividualMappingsChanged(object? sender, NotifyCollectionChang { FindDeviceGroupsStillUnmapped(); } + + protected override bool TryMapEditableSettingsFromData(Mapping data) + { + return Name.TryUpdateModelDirectly(data.Name); + } + + protected override bool TryMapEditableSettingsCollectionsFromData(Mapping data) + { + return true; + } } public class MappingGroup diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 65c76697..77af4fb5 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -38,7 +38,7 @@ protected override bool TryMapEditableSettingsFromData(IEnumerable protected override void SetElementName(IProfileModel element, string name) { - element.Name.InterfaceValue = name; + element.Name.TryUpdateModelDirectly(name); } protected override string GetNameFromData(DATA.Profile data) From 777d79da64717150e644495a83c6180fa606fd5d Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 20 Jul 2025 23:41:15 -0700 Subject: [PATCH 24/47] Add validators and parsers to DI --- userspace-backend/BackEndComposer.cs | 329 ++++++++++-------- .../EditableSettings/ModelValueValidator.cs | 26 +- ...UserInputParsers.cs => UserInputParser.cs} | 12 - 3 files changed, 182 insertions(+), 185 deletions(-) rename userspace-backend/Model/EditableSettings/{UserInputParsers.cs => UserInputParser.cs} (84%) diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index fc795392..938f22f3 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -18,51 +18,80 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); + #region Parsers + + services.AddSingleton, StringParser>(); + services.AddSingleton, IntParser>(); + services.AddSingleton, DoubleParser>(); + services.AddSingleton, BoolParser>(); + services.AddSingleton, AccelerationDefinitionTypeParser>(); + services.AddSingleton, AccelerationFormulaTypeParser>(); + services.AddSingleton, LookupTableTypeParser>(); + services.AddSingleton, LookupTableDataParser>(); + + #endregion Parsers + + #region Validators + + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + services.AddSingleton, DefaultModelValueValidator>(); + + services.AddKeyedSingleton, DefaultModelValueValidator>( + DefaultModelValueValidator.AllChangeInvalidDIKey); + + #endregion Validators + #region Hidden services.AddTransient(); services.AddKeyedTransient>( - HiddenModel.RotationDegreesDIKey, (_, _) => + HiddenModel.RotationDegreesDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Rotation", initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - HiddenModel.AngleSnappingDegreesDIKey, (_, _) => + HiddenModel.AngleSnappingDegreesDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Angle Snapping", initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - HiddenModel.LeftRightRatioDIKey, (_, _) => + HiddenModel.LeftRightRatioDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "L/R Ratio", initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - HiddenModel.UpDownRatioDIKey, (_, _) => + HiddenModel.UpDownRatioDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "U/D Ratio", initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - HiddenModel.SpeedCapDIKey, (_, _) => + HiddenModel.SpeedCapDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Speed Cap", initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - HiddenModel.OutputSmoothingHalfLifeDIKey, (_, _) => + HiddenModel.OutputSmoothingHalfLifeDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Output Smoothing Half-Life", initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion Hidden @@ -70,19 +99,19 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - CoalescionModel.InputSmoothingHalfLifeDIKey, (_, _) => + CoalescionModel.InputSmoothingHalfLifeDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Input Smoothing Half-Life", initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - CoalescionModel.ScaleSmoothingHalfLifeDIKey, (_, _) => + CoalescionModel.ScaleSmoothingHalfLifeDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Scale Smoothing Half-Life", initialValue: 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion Coalescion @@ -90,47 +119,47 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - AnisotropyModel.DomainXDIKey, (_, _) => + AnisotropyModel.DomainXDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Domain X", initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - AnisotropyModel.DomainYDIKey, (_, _) => + AnisotropyModel.DomainYDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Domain Y", initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - AnisotropyModel.RangeXDIKey, (_, _) => + AnisotropyModel.RangeXDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Range X", initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - AnisotropyModel.RangeYDIKey, (_, _) => + AnisotropyModel.RangeYDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Range Y", initialValue: 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - AnisotropyModel.LPNormDIKey, (_, _) => + AnisotropyModel.LPNormDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "LP Norm", initialValue: 2, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - AnisotropyModel.CombineXYComponentsDIKey, (_, _) => + AnisotropyModel.CombineXYComponentsDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Combine X and Y Components", initialValue: false, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion Anisotropy @@ -138,12 +167,12 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - AccelerationModel.SelectionDIKey, (_, _) => + AccelerationModel.SelectionDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Definition Type", initialValue: AccelerationDefinitionType.None, - parser: UserInputParsers.AccelerationDefinitionTypeParser, - validator: ModelValueValidators.DefaultAccelerationTypeValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion Acceleration @@ -151,20 +180,20 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - FormulaAccelModel.SelectionDIKey, (_, _) => + FormulaAccelModel.SelectionDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Formula Type", initialValue: AccelerationFormulaType.Synchronous, - parser: UserInputParsers.AccelerationFormulaTypeParser, - validator: ModelValueValidators.DefaultAccelerationFormulaTypeValidator, + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>(), autoUpdateFromInterface: true)); services.AddKeyedTransient>( - FormulaAccelModel.GainDIKey, (_, _) => + FormulaAccelModel.GainDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Apply to Gain", initialValue: false, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion FormulaAccel @@ -172,19 +201,19 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - LookupTableDefinitionModel.ApplyAsDIKey, (_, _) => + LookupTableDefinitionModel.ApplyAsDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Apply as", initialValue: LookupTableType.Velocity, - parser: UserInputParsers.LookupTableTypeParser, - validator: ModelValueValidators.DefaultLookupTableTypeValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - LookupTableDefinitionModel.DataDIKey, (_, _) => + LookupTableDefinitionModel.DataDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Data", initialValue: new LookupTableData(), - parser: UserInputParsers.LookupTableDataParser, - validator: ModelValueValidators.DefaultLookupTableDataValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion LookupTable @@ -198,33 +227,33 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - SynchronousAccelerationDefinitionModel.SyncSpeedDIKey, (_, _) => + SynchronousAccelerationDefinitionModel.SyncSpeedDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Sync Speed", 15, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - SynchronousAccelerationDefinitionModel.MotivityDIKey, (_, _) => + SynchronousAccelerationDefinitionModel.MotivityDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Motivity", 1.4, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - SynchronousAccelerationDefinitionModel.GammaDIKey, (_, _) => + SynchronousAccelerationDefinitionModel.GammaDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Gamma", 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - SynchronousAccelerationDefinitionModel.SmoothnessDIKey, (_, _) => + SynchronousAccelerationDefinitionModel.SmoothnessDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Smoothness", 0.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion SynchronousAccel @@ -232,26 +261,26 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - LinearAccelerationDefinitionModel.AccelerationDIKey, (_, _) => + LinearAccelerationDefinitionModel.AccelerationDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Acceleration", 0.01, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - LinearAccelerationDefinitionModel.OffsetDIKey, (_, _) => + LinearAccelerationDefinitionModel.OffsetDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Offset", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - LinearAccelerationDefinitionModel.CapDIKey, (_, _) => + LinearAccelerationDefinitionModel.CapDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Cap", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion LinearAccel @@ -259,33 +288,33 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - ClassicAccelerationDefinitionModel.AccelerationDIKey, (_, _) => + ClassicAccelerationDefinitionModel.AccelerationDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Acceleration", 0.01, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - ClassicAccelerationDefinitionModel.ExponentDIKey, (_, _) => + ClassicAccelerationDefinitionModel.ExponentDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Exponent", 2, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - ClassicAccelerationDefinitionModel.OffsetDIKey, (_, _) => + ClassicAccelerationDefinitionModel.OffsetDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Offset", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - ClassicAccelerationDefinitionModel.CapDIKey, (_, _) => + ClassicAccelerationDefinitionModel.CapDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Cap", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion ClassicAccel @@ -293,33 +322,33 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - PowerAccelerationDefinitionModel.ScaleDIKey, (_, _) => + PowerAccelerationDefinitionModel.ScaleDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Scale", 1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - PowerAccelerationDefinitionModel.ExponentDIKey, (_, _) => + PowerAccelerationDefinitionModel.ExponentDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Exponent", 0.05, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - PowerAccelerationDefinitionModel.OutputOffsetDIKey, (_, _) => + PowerAccelerationDefinitionModel.OutputOffsetDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Output Offset", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - PowerAccelerationDefinitionModel.CapDIKey, (_, _) => + PowerAccelerationDefinitionModel.CapDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Cap", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion PowerAccel @@ -327,26 +356,26 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - JumpAccelerationDefinitionModel.SmoothDIKey, (_, _) => + JumpAccelerationDefinitionModel.SmoothDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Smooth", 0.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - JumpAccelerationDefinitionModel.InputDIKey, (_, _) => + JumpAccelerationDefinitionModel.InputDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Input", 15, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - JumpAccelerationDefinitionModel.OutputDIKey, (_, _) => + JumpAccelerationDefinitionModel.OutputDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Output", 1.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion JumpAccel @@ -354,26 +383,26 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - NaturalAccelerationDefinitionModel.DecayRateDIKey, (_, _) => + NaturalAccelerationDefinitionModel.DecayRateDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Decay Rate", 0.1, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - NaturalAccelerationDefinitionModel.InputOffsetDIKey, (_, _) => + NaturalAccelerationDefinitionModel.InputOffsetDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Input Offset", 0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - NaturalAccelerationDefinitionModel.LimitDIKey, (_, _) => + NaturalAccelerationDefinitionModel.LimitDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Limit", 1.5, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion NaturalAccel @@ -381,27 +410,27 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - ProfileModel.NameDIKey, (_, _) => + ProfileModel.NameDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Name", "Empty", - parser: UserInputParsers.StringParser, + parser: services.GetRequiredService>(), // TODO: DI - change to max name length validator - validator: ModelValueValidators.DefaultStringValidator)); + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - ProfileModel.OutputDPIDIKey, (_, _) => + ProfileModel.OutputDPIDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Output DPI", 1000, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - ProfileModel.YXRatioDIKey, (_, _) => + ProfileModel.YXRatioDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Y/X Ratio", 1.0, - parser: UserInputParsers.DoubleParser, - validator: ModelValueValidators.DefaultDoubleValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion Profile @@ -409,47 +438,47 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - DeviceModel.NameDIKey, (_, _) => + DeviceModel.NameDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Name", initialValue: "name", - parser: UserInputParsers.StringParser, - validator: ModelValueValidators.DefaultStringValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - DeviceModel.HardwareIDDIKey, (_, _) => + DeviceModel.HardwareIDDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Hardware ID", initialValue: "hwid", - parser: UserInputParsers.StringParser, - validator: ModelValueValidators.DefaultStringValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - DeviceModel.DPIDIKey, (_, _) => + DeviceModel.DPIDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "DPI", initialValue: 1000, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - DeviceModel.PollRateDIKey, (_, _) => + DeviceModel.PollRateDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Polling Rate", initialValue: 1000, - parser: UserInputParsers.IntParser, - validator: ModelValueValidators.DefaultIntValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - DeviceModel.IgnoreDIKey, (_, _) => + DeviceModel.IgnoreDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Ignore", initialValue: false, - parser: UserInputParsers.BoolParser, - validator: ModelValueValidators.DefaultBoolValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); services.AddKeyedTransient>( - DeviceModel.DeviceGroupDIKey, (_, _) => + DeviceModel.DeviceGroupDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Device Group", initialValue: "default", - parser: UserInputParsers.StringParser, - validator: ModelValueValidators.DefaultStringValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredService>())); #endregion DeviceGroup @@ -457,12 +486,12 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddTransient(); services.AddKeyedTransient>( - MappingModel.NameDIKey, (_, _) => + MappingModel.NameDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( displayName: "Name", initialValue: "name", - parser: UserInputParsers.StringParser, - validator: ModelValueValidators.DefaultStringValidator)); + parser: services.GetRequiredService>(), + validator: services.GetRequiredKeyedService>(MappingModel.NameDIKey))); #endregion Mapping diff --git a/userspace-backend/Model/EditableSettings/ModelValueValidator.cs b/userspace-backend/Model/EditableSettings/ModelValueValidator.cs index 476df45e..22ffcb0e 100644 --- a/userspace-backend/Model/EditableSettings/ModelValueValidator.cs +++ b/userspace-backend/Model/EditableSettings/ModelValueValidator.cs @@ -1,34 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using userspace_backend.Data.Profiles.Accel; -using userspace_backend.Data.Profiles; -using userspace_backend.Model.AccelDefinitions; - -namespace userspace_backend.Model.EditableSettings +namespace userspace_backend.Model.EditableSettings { public interface IModelValueValidator { bool Validate(T value); } - public static class ModelValueValidators - { - public static DefaultModelValueValidator DefaultIntValidator = new DefaultModelValueValidator(); - public static DefaultModelValueValidator DefaultDoubleValidator = new DefaultModelValueValidator(); - public static DefaultModelValueValidator DefaultStringValidator = new DefaultModelValueValidator(); - public static AllChangeInvalidValueValidator AllChangesInvalidStringValidator = new AllChangeInvalidValueValidator(); - public static DefaultModelValueValidator DefaultBoolValidator = new DefaultModelValueValidator(); - public static DefaultModelValueValidator DefaultAccelerationTypeValidator = new DefaultModelValueValidator(); - public static DefaultModelValueValidator DefaultLookupTableTypeValidator = new DefaultModelValueValidator(); - public static DefaultModelValueValidator DefaultLookupTableDataValidator = new DefaultModelValueValidator(); - public static DefaultModelValueValidator DefaultAccelerationFormulaTypeValidator = new DefaultModelValueValidator(); - } - public class DefaultModelValueValidator : IModelValueValidator { + public const string AllChangeInvalidDIKey = nameof(AllChangeInvalidDIKey); + public bool Validate(T value) { return true; diff --git a/userspace-backend/Model/EditableSettings/UserInputParsers.cs b/userspace-backend/Model/EditableSettings/UserInputParser.cs similarity index 84% rename from userspace-backend/Model/EditableSettings/UserInputParsers.cs rename to userspace-backend/Model/EditableSettings/UserInputParser.cs index ca78e370..a3f6aa11 100644 --- a/userspace-backend/Model/EditableSettings/UserInputParsers.cs +++ b/userspace-backend/Model/EditableSettings/UserInputParser.cs @@ -9,18 +9,6 @@ namespace userspace_backend.Model.EditableSettings { - public static class UserInputParsers - { - public static StringParser StringParser = new StringParser(); - public static IntParser IntParser = new IntParser(); - public static DoubleParser DoubleParser = new DoubleParser(); - public static BoolParser BoolParser = new BoolParser(); - public static AccelerationDefinitionTypeParser AccelerationDefinitionTypeParser = new AccelerationDefinitionTypeParser(); - public static LookupTableTypeParser LookupTableTypeParser = new LookupTableTypeParser(); - public static LookupTableDataParser LookupTableDataParser = new LookupTableDataParser(); - public static AccelerationFormulaTypeParser AccelerationFormulaTypeParser = new AccelerationFormulaTypeParser(); - } - public interface IUserInputParser { bool TryParse(string input, out T parsedValue); From df411503ef906382921d6dbcb50158596d9ab9b6 Mon Sep 17 00:00:00 2001 From: Jacob Palecki Date: Sun, 20 Jul 2025 23:53:01 -0700 Subject: [PATCH 25/47] Add named editablesettings --- userspace-backend/Model/DeviceModel.cs | 11 ++--------- .../EditableSettingsCollection.cs | 19 +++++++++++++++++++ .../IEditableSettingsCollection.cs | 15 --------------- userspace-backend/Model/MappingModel.cs | 10 +++------- userspace-backend/Model/ProfileModel.cs | 9 +++------ 5 files changed, 27 insertions(+), 37 deletions(-) delete mode 100644 userspace-backend/Model/EditableSettings/IEditableSettingsCollection.cs diff --git a/userspace-backend/Model/DeviceModel.cs b/userspace-backend/Model/DeviceModel.cs index 3f3338c0..19fd6c74 100644 --- a/userspace-backend/Model/DeviceModel.cs +++ b/userspace-backend/Model/DeviceModel.cs @@ -18,7 +18,7 @@ public interface IDeviceModel : IEditableSettingsCollectionSpecific IEditableSettingSpecific DeviceGroup { get; } } - public class DeviceModel : EditableSettingsCollectionV2, IDeviceModel + public class DeviceModel : NamedEditableSettingsCollection, IDeviceModel { public const string NameDIKey = $"{nameof(DeviceModel)}.{nameof(Name)}"; public const string HardwareIDDIKey = $"{nameof(DeviceModel)}.{nameof(HardwareID)}"; @@ -34,9 +34,8 @@ public DeviceModel( IEditableSettingSpecific pollRate, IEditableSettingSpecific ignore, IEditableSettingSpecific deviceGroup) - : base([name, hardwareID, dpi, pollRate, ignore, deviceGroup], []) + : base(name, [hardwareID, dpi, pollRate, ignore, deviceGroup], []) { - Name = name; HardwareID = hardwareID; DPI = dpi; PollRate = pollRate; @@ -44,8 +43,6 @@ public DeviceModel( DeviceGroup = deviceGroup; } - public IEditableSettingSpecific Name { get; protected set; } - public IEditableSettingSpecific HardwareID { get; protected set; } public IEditableSettingSpecific DPI { get; protected set; } @@ -56,10 +53,6 @@ public DeviceModel( public IEditableSettingSpecific DeviceGroup { get; set; } - protected DeviceModelNameValidator DeviceModelNameValidator { get; } - - protected DeviceModelHWIDValidator DeviceModelHWIDValidator { get; } - public override Device MapToData() { return new Device() diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs index d437f2cd..64c15d1f 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsCollection.cs @@ -113,6 +113,11 @@ public interface IEditableSettingsCollectionSpecific : IEditableSettingsColle bool TryMapFromData(T data); } + public interface INamedEditableSettingsCollectionSpecific : IEditableSettingsCollectionSpecific + { + public IEditableSettingSpecific Name { get; } + } + /// /// Base class for settings collections. /// @@ -204,4 +209,18 @@ protected void OnAnySettingChanged() protected abstract bool TryMapEditableSettingsCollectionsFromData(T data); } + + public abstract class NamedEditableSettingsCollection : EditableSettingsCollectionV2, INamedEditableSettingsCollectionSpecific + { + public NamedEditableSettingsCollection( + IEditableSettingSpecific name, + IEnumerable editableSettings, + IEnumerable editableSettingsCollections) + : base(editableSettings.Union([name]), editableSettingsCollections) + { + Name = name; + } + + public IEditableSettingSpecific Name { get; } + } } diff --git a/userspace-backend/Model/EditableSettings/IEditableSettingsCollection.cs b/userspace-backend/Model/EditableSettings/IEditableSettingsCollection.cs deleted file mode 100644 index d2f91676..00000000 --- a/userspace-backend/Model/EditableSettings/IEditableSettingsCollection.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace userspace_backend.Model.EditableSettings -{ - public interface IEditableSettingsCollection - { - bool HasChanged { get; } - - EventHandler AnySettingChanged { get; set; } - } -} diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index 68d3448b..47a04ed1 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -2,17 +2,15 @@ using System.Linq; using userspace_backend.Model.EditableSettings; using userspace_backend.Data; -using System.Collections.ObjectModel; using System.Collections.Specialized; namespace userspace_backend.Model { public interface IMappingModel : IEditableSettingsCollectionSpecific { - } - public class MappingModel: EditableSettingsCollectionV2, IMappingModel + public class MappingModel: NamedEditableSettingsCollection, IMappingModel { public const string NameDIKey = $"{nameof(MappingModel)}.{nameof(Name)}"; @@ -20,9 +18,9 @@ public MappingModel( IEditableSettingSpecific name, IModelValueValidator nameValidator, IDeviceGroups deviceGroups, - IProfilesModel profiles) : base([], []) + IProfilesModel profiles) + : base(name, [], []) { - Name = name; NameValidator = nameValidator; SetActive = true; DeviceGroups = deviceGroups; @@ -31,8 +29,6 @@ public MappingModel( public bool SetActive { get; set; } - public IEditableSettingSpecific Name { get; set; } - protected IModelValueValidator NameValidator { get; } protected IDeviceGroups DeviceGroups { get; } diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index cc489eb4..2fbf2c61 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -29,7 +29,7 @@ public interface IProfileModel : IEditableSettingsCollectionSpecific, IProfileModel + public class ProfileModel : NamedEditableSettingsCollection, IProfileModel { public const string NameDIKey = $"{nameof(ProfileModel)}.{nameof(Name)}"; public const string OutputDPIDIKey = $"{nameof(ProfileModel)}.{nameof(OutputDPI)}"; @@ -41,16 +41,15 @@ public ProfileModel( [FromKeyedServices(YXRatioDIKey)]IEditableSettingSpecific yxRatio, IAccelerationModel acceleration, IHiddenModel hidden - ) : base([name, outputDPI, yxRatio], [acceleration, hidden]) + ) : base(name, [outputDPI, yxRatio], [acceleration, hidden]) { - Name = name; OutputDPI = outputDPI; YXRatio = yxRatio; Acceleration = acceleration; Hidden = hidden; // Name and Output DPI do not need to generate a new curve preview - Name.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; + Name!.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; OutputDPI.PropertyChanged += AnyNonPreviewPropertyChangedEventHandler; // The rest of settings should generate a new curve preview @@ -65,8 +64,6 @@ IHiddenModel hidden public string CurrentNameForDisplay => Name.ModelValue; - public IEditableSettingSpecific Name { get; set; } - public IEditableSettingSpecific OutputDPI { get; set; } public IEditableSettingSpecific YXRatio { get; set; } From 82a7cf117c4da5273e6c1c74fc8180abc4e7dbab Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:21:33 -0400 Subject: [PATCH 26/47] Register IO Layer Components in DI --- .gitignore | 3 ++- userspace-backend/BackEndComposer.cs | 9 +++++++++ userspace-backend/BackEndLoader.cs | 18 +++++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 2a7c213a..63f1c47a 100644 --- a/.gitignore +++ b/.gitignore @@ -351,4 +351,5 @@ ASALocalRun/ .localhistory/ # BeatPulse healthcheck temp database -healthchecksdb \ No newline at end of file +healthchecksdb +.vscode/settings.json diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 938f22f3..29baa7c4 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using System; +using userspace_backend.IO; using userspace_backend.Model; using userspace_backend.Model.AccelDefinitions; using userspace_backend.Model.AccelDefinitions.Formula; @@ -495,6 +496,14 @@ public static IServiceProvider Compose(IServiceCollection services) #endregion Mapping + #region IO Layer + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + #endregion IO Layer + return services.BuildServiceProvider(); } } diff --git a/userspace-backend/BackEndLoader.cs b/userspace-backend/BackEndLoader.cs index 33d5ba2b..3ad6be35 100644 --- a/userspace-backend/BackEndLoader.cs +++ b/userspace-backend/BackEndLoader.cs @@ -26,18 +26,22 @@ public void WriteSettingsToDisk( public class BackEndLoader : IBackEndLoader { - public static DevicesReaderWriter DevicesReaderWriter = new DevicesReaderWriter(); - - public static MappingsReaderWriter MappingsReaderWriter = new MappingsReaderWriter(); - - public static ProfileReaderWriter ProfileReaderWriter = new ProfileReaderWriter(); - - public BackEndLoader(string settingsDirectory) + public BackEndLoader( + string settingsDirectory, + DevicesReaderWriter devicesReaderWriter, + MappingsReaderWriter mappingsReaderWriter, + ProfileReaderWriter profileReaderWriter) { SettingsDirectory = settingsDirectory; + DevicesReaderWriter = devicesReaderWriter; + MappingsReaderWriter = mappingsReaderWriter; + ProfileReaderWriter = profileReaderWriter; } public string SettingsDirectory { get; private set; } + protected DevicesReaderWriter DevicesReaderWriter { get; } + protected MappingsReaderWriter MappingsReaderWriter { get; } + protected ProfileReaderWriter ProfileReaderWriter { get; } public IEnumerable LoadDevices() { From 26096060c172504f941f353b8c845afddc09f13c Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:44:18 -0400 Subject: [PATCH 27/47] Register CurvePreview in DI --- userspace-backend/BackEndComposer.cs | 7 +++++++ userspace-backend/Model/ProfileModel.cs | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 29baa7c4..668af518 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using System; +using userspace_backend.Display; using userspace_backend.IO; using userspace_backend.Model; using userspace_backend.Model.AccelDefinitions; @@ -435,6 +436,12 @@ public static IServiceProvider Compose(IServiceCollection services) #endregion Profile + #region Display + + services.AddTransient(); + + #endregion Display + #region DeviceGroup services.AddTransient(); diff --git a/userspace-backend/Model/ProfileModel.cs b/userspace-backend/Model/ProfileModel.cs index 2fbf2c61..312b8ea4 100644 --- a/userspace-backend/Model/ProfileModel.cs +++ b/userspace-backend/Model/ProfileModel.cs @@ -40,7 +40,8 @@ public ProfileModel( [FromKeyedServices(OutputDPIDIKey)]IEditableSettingSpecific outputDPI, [FromKeyedServices(YXRatioDIKey)]IEditableSettingSpecific yxRatio, IAccelerationModel acceleration, - IHiddenModel hidden + IHiddenModel hidden, + ICurvePreview curvePreview ) : base(name, [outputDPI, yxRatio], [acceleration, hidden]) { OutputDPI = outputDPI; @@ -57,8 +58,7 @@ IHiddenModel hidden Acceleration.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; Hidden.AnySettingChanged += AnyCurveSettingCollectionChangedEventHandler; - // TODO: DI - Curve preview to DI - CurvePreview = new CurvePreview(); + CurvePreview = curvePreview; RecalculateDriverDataAndCurvePreview(); } From 6f7e972f7774b5e6ae81daca21eeecda7ffc7e2b Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:56:19 -0400 Subject: [PATCH 28/47] Create Specialized Validators --- userspace-backend/BackEndComposer.cs | 6 +++-- .../MaxNameLengthValidator.cs | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 668af518..a5b0c4bb 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -47,6 +47,9 @@ public static IServiceProvider Compose(IServiceCollection services) services.AddKeyedSingleton, DefaultModelValueValidator>( DefaultModelValueValidator.AllChangeInvalidDIKey); + services.AddKeyedSingleton, MaxNameLengthValidator>( + ProfileModel.NameDIKey); + #endregion Validators #region Hidden @@ -417,8 +420,7 @@ public static IServiceProvider Compose(IServiceCollection services) displayName: "Name", "Empty", parser: services.GetRequiredService>(), - // TODO: DI - change to max name length validator - validator: services.GetRequiredService>())); + validator: services.GetRequiredKeyedService>(ProfileModel.NameDIKey))); services.AddKeyedTransient>( ProfileModel.OutputDPIDIKey, (IServiceProvider services, object? key) => new EditableSettingV2( diff --git a/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs b/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs new file mode 100644 index 00000000..332812b2 --- /dev/null +++ b/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs @@ -0,0 +1,24 @@ +namespace userspace_backend.Model.EditableSettings +{ + public class MaxNameLengthValidator : IModelValueValidator + { + public const int MaxNameLength = 100; + + public MaxNameLengthValidator() + { + MaxLength = MaxNameLength; + } + + public MaxNameLengthValidator(int maxLength) + { + MaxLength = maxLength; + } + + public int MaxLength { get; } + + public bool Validate(string value) + { + return !string.IsNullOrEmpty(value) && value.Length <= MaxLength; + } + } +} From e8a608018b65214a317591cf76741d7b2208d01b Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:56:30 -0400 Subject: [PATCH 29/47] Register DeviceGroups and Validators --- userspace-backend/BackEndComposer.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index a5b0c4bb..90116f5b 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -446,6 +446,27 @@ public static IServiceProvider Compose(IServiceCollection services) #region DeviceGroup + services.AddSingleton(sp => + { + // Initialize with empty collection - will be populated during BackEnd.Load() + return new DeviceGroups([]); + }); + + services.AddSingleton(sp => + { + var deviceGroups = sp.GetRequiredService(); + return new DeviceGroupValidator(deviceGroups); + }); + + services.AddSingleton(sp => + { + var systemDevicesProvider = sp.GetRequiredService(); + var deviceGroups = sp.GetRequiredService(); + var devicesModel = new DevicesModel(sp, systemDevicesProvider); + devicesModel.DeviceGroups = deviceGroups; + return devicesModel; + }); + services.AddTransient(); services.AddKeyedTransient>( DeviceModel.NameDIKey, (IServiceProvider services, object? key) => From a2aba28d0201f9d926e832d8fe1343fc6282232a Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:57:02 -0400 Subject: [PATCH 30/47] Register MappingsModel and Dependencies --- userspace-backend/BackEndComposer.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 90116f5b..f28d91a2 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using System; +using DATA = userspace_backend.Data; using userspace_backend.Display; using userspace_backend.IO; using userspace_backend.Model; @@ -515,6 +516,21 @@ public static IServiceProvider Compose(IServiceCollection services) #region Mapping + services.AddSingleton(sp => + { + // Initialize with empty MappingSet - will be populated during BackEnd.Load() + var deviceGroups = sp.GetRequiredService(); + var profiles = sp.GetRequiredService(); + var emptyMappingSet = new DATA.MappingSet { Mappings = [] }; + return new MappingsModel(emptyMappingSet, deviceGroups, profiles); + }); + + services.AddSingleton(sp => + { + var mappings = sp.GetRequiredService(); + return new MappingNameValidator(mappings); + }); + services.AddTransient(); services.AddKeyedTransient>( MappingModel.NameDIKey, (IServiceProvider services, object? key) => @@ -522,7 +538,7 @@ public static IServiceProvider Compose(IServiceCollection services) displayName: "Name", initialValue: "name", parser: services.GetRequiredService>(), - validator: services.GetRequiredKeyedService>(MappingModel.NameDIKey))); + validator: services.GetRequiredService())); #endregion Mapping From 7c8a4adb2c43c2ba039af2cfaec77084c85442c9 Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:57:38 -0400 Subject: [PATCH 31/47] Complete BackEnd DI Integration --- userspace-backend/BackEnd.cs | 29 ++++++++++++++++++---------- userspace-backend/BackEndComposer.cs | 8 ++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index f741641f..4e2e4c64 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -20,18 +20,17 @@ public interface IBackEnd IProfilesModel Profiles { get; } } - public class BackEnd + public class BackEnd : IBackEnd { public BackEnd( IBackEndLoader backEndLoader, - IProfilesModel profilesModel) + IProfilesModel profilesModel, + DevicesModel devicesModel, + MappingsModel mappingsModel) { - // TODO: fully construct BackEnd via DI - ServiceCollection services = new ServiceCollection(); - IServiceProvider serviceProvider = BackEndComposer.Compose(services); - BackEndLoader = backEndLoader; - Devices = new DevicesModel(serviceProvider.GetRequiredService()); + Devices = devicesModel; + Mappings = mappingsModel; Profiles = profilesModel; } @@ -45,14 +44,14 @@ public BackEnd( public void Load() { - IEnumerable devicesData = BackEndLoader.LoadDevices(); ; + IEnumerable devicesData = BackEndLoader.LoadDevices(); LoadDevicesFromData(devicesData); - IEnumerable profilesData = BackEndLoader.LoadProfiles(); ; + IEnumerable profilesData = BackEndLoader.LoadProfiles(); LoadProfilesFromData(profilesData); DATA.MappingSet mappingData = BackEndLoader.LoadMappings(); - Mappings = new MappingsModel(mappingData, Devices.DeviceGroups, Profiles); + LoadMappingsFromData(mappingData); } protected void LoadDevicesFromData(IEnumerable devicesData) @@ -68,6 +67,16 @@ protected void LoadProfilesFromData(IEnumerable profileData) Profiles.TryMapFromData(profileData); } + protected void LoadMappingsFromData(DATA.MappingSet mappingData) + { + // Clear existing mappings and reload from data + Mappings.Mappings.Clear(); + foreach (var mapping in mappingData.Mappings) + { + Mappings.TryAddMapping(mapping); + } + } + public void Apply() { try diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index f28d91a2..18ccdf02 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -550,6 +550,14 @@ public static IServiceProvider Compose(IServiceCollection services) #endregion IO Layer + #region BackEnd + + services.AddSingleton(); + + services.AddSingleton(); + + #endregion BackEnd + return services.BuildServiceProvider(); } } From d998c5b0bd7b7b47524f4bda6a7f105479632e24 Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:57:54 -0400 Subject: [PATCH 32/47] Handle Default Profile --- userspace-backend/Model/ProfilesModel.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 77af4fb5..acd4ef8b 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -14,9 +14,8 @@ public interface IProfilesModel : IEditableSettingsList, IProfilesModel { - // TODO: DI - hand default profile to profiles model - // public static readonly ProfileModel DefaultProfile = new ProfileModel( - // GenerateNewDefaultProfile("Default"), ModelValueValidators.AllChangesInvalidStringValidator); + // Default profile is handled via application bootstrap if needed + // Profiles are loaded from disk during BackEnd.Load() public ProfilesModel(IServiceProvider serviceProvider) : base(serviceProvider, [], []) From 754660731bb47ba222c914f7e27656351a04b320 Mon Sep 17 00:00:00 2001 From: Lex Date: Wed, 22 Oct 2025 12:58:19 -0400 Subject: [PATCH 33/47] Update Application Bootstrap --- userinterface/App.axaml.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/userinterface/App.axaml.cs b/userinterface/App.axaml.cs index 5bd7498e..95d389c9 100644 --- a/userinterface/App.axaml.cs +++ b/userinterface/App.axaml.cs @@ -7,6 +7,7 @@ using userinterface.ViewModels; using userinterface.Views; using userspace_backend; +using userspace_backend.IO; using DATA = userspace_backend.Data; namespace userinterface; @@ -20,8 +21,23 @@ public override void Initialize() public override void OnFrameworkInitializationCompleted() { + // Create the DI container ServiceCollection services = new ServiceCollection(); + + // Register BackEndLoader first with settings directory + string settingsDirectory = System.AppDomain.CurrentDomain.BaseDirectory; + services.AddSingleton(sp => + { + var devicesRW = sp.GetRequiredService(); + var mappingsRW = sp.GetRequiredService(); + var profileRW = sp.GetRequiredService(); + return new BackEndLoader(settingsDirectory, devicesRW, mappingsRW, profileRW); + }); + + // Compose all other services IServiceProvider serviceProvider = BackEndComposer.Compose(services); + + // Resolve and initialize BackEnd IBackEnd backEnd = serviceProvider.GetRequiredService(); backEnd.Load(); From 08905808f6b3c84d2eefe5f7572654505f73218f Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 13:33:54 -0400 Subject: [PATCH 34/47] Revert "Handle Default Profile" This reverts commit d998c5b0bd7b7b47524f4bda6a7f105479632e24. --- userspace-backend/Model/ProfilesModel.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index acd4ef8b..77af4fb5 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -14,8 +14,9 @@ public interface IProfilesModel : IEditableSettingsList, IProfilesModel { - // Default profile is handled via application bootstrap if needed - // Profiles are loaded from disk during BackEnd.Load() + // TODO: DI - hand default profile to profiles model + // public static readonly ProfileModel DefaultProfile = new ProfileModel( + // GenerateNewDefaultProfile("Default"), ModelValueValidators.AllChangesInvalidStringValidator); public ProfilesModel(IServiceProvider serviceProvider) : base(serviceProvider, [], []) From d12e4a420a4cc85b509a7887b5dc506502d56122 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 13:51:55 -0400 Subject: [PATCH 35/47] Device groups are strings now + load default profile --- .../Device/DeviceGroupSelectorViewModel.cs | 12 +++--- .../ViewModels/Device/DeviceGroupViewModel.cs | 4 +- .../Device/DeviceGroupsViewModel.cs | 6 +-- .../ViewModels/Mapping/MappingViewModel.cs | 6 +-- .../Views/Device/DeviceGroupView.axaml.cs | 9 +---- .../Views/Mapping/MappingView.axaml.cs | 2 +- userspace-backend/BackEnd.cs | 31 ++++++++++++-- userspace-backend/BackEndComposer.cs | 2 +- userspace-backend/Model/DeviceGroups.cs | 28 ++++++------- userspace-backend/Model/MappingModel.cs | 40 +++++++++++++++---- userspace-backend/Model/MappingsModel.cs | 15 ++++++- userspace-backend/Model/ProfilesModel.cs | 4 +- 12 files changed, 104 insertions(+), 55 deletions(-) diff --git a/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs b/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs index fd6cea58..b65d5541 100644 --- a/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs +++ b/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs @@ -5,7 +5,7 @@ namespace userinterface.ViewModels.Device { public partial class DeviceGroupSelectorViewModel : ViewModelBase { - protected DeviceGroupModel selectedEntry = null!; + protected string selectedEntry = null!; public DeviceGroupSelectorViewModel(DeviceModel device, DeviceGroups deviceGroupsBE) { @@ -17,17 +17,17 @@ public DeviceGroupSelectorViewModel(DeviceModel device, DeviceGroups deviceGroup protected DeviceModel Device { get; } protected DeviceGroups DeviceGroupsBE { get; } - public ObservableCollection DeviceGroupEntries => + public ObservableCollection DeviceGroupEntries => DeviceGroupsBE.DeviceGroupModels; - public DeviceGroupModel SelectedEntry + public string SelectedEntry { get => selectedEntry; set { if (DeviceGroupEntries.Contains(value)) { - Device.DeviceGroup = value; + Device.DeviceGroup.TryUpdateUserInput(value); selectedEntry = value; } } @@ -37,7 +37,7 @@ public DeviceGroupModel SelectedEntry public void RefreshSelectedDeviceGroup() { - if (!DeviceGroupEntries.Contains(Device.DeviceGroup)) + if (!DeviceGroupEntries.Contains(Device.DeviceGroup.ModelValue)) { IsValid = false; SelectedEntry = DeviceGroups.DefaultDeviceGroup; @@ -45,7 +45,7 @@ public void RefreshSelectedDeviceGroup() } IsValid = true; - selectedEntry = Device.DeviceGroup; + selectedEntry = Device.DeviceGroup.ModelValue; } } } diff --git a/userinterface/ViewModels/Device/DeviceGroupViewModel.cs b/userinterface/ViewModels/Device/DeviceGroupViewModel.cs index 0cc59cd8..69e26549 100644 --- a/userinterface/ViewModels/Device/DeviceGroupViewModel.cs +++ b/userinterface/ViewModels/Device/DeviceGroupViewModel.cs @@ -5,13 +5,13 @@ namespace userinterface.ViewModels.Device { public partial class DeviceGroupViewModel : ViewModelBase { - public DeviceGroupViewModel(BE.DeviceGroupModel deviceGroupBE, BE.DeviceGroups deviceGroupsBE) + public DeviceGroupViewModel(string deviceGroupBE, BE.DeviceGroups deviceGroupsBE) { DeviceGroupBE = deviceGroupBE; DeviceGroupsBE = deviceGroupsBE; } - public BE.DeviceGroupModel DeviceGroupBE { get; } + public string DeviceGroupBE { get; } protected BE.DeviceGroups DeviceGroupsBE { get; } diff --git a/userinterface/ViewModels/Device/DeviceGroupsViewModel.cs b/userinterface/ViewModels/Device/DeviceGroupsViewModel.cs index 97ad801e..7fc54716 100644 --- a/userinterface/ViewModels/Device/DeviceGroupsViewModel.cs +++ b/userinterface/ViewModels/Device/DeviceGroupsViewModel.cs @@ -16,17 +16,17 @@ public DeviceGroupsViewModel(BE.DeviceGroups deviceGroupsBE) protected BE.DeviceGroups DeviceGroupsBE { get; } - public ObservableCollection DeviceGroups => DeviceGroupsBE.DeviceGroupModels; + public ObservableCollection DeviceGroups => DeviceGroupsBE.DeviceGroupModels; public ObservableCollection DeviceGroupViews { get; } - private void DeviceGroupsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => + private void DeviceGroupsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => UpdateDeviceGroupViews(); public void UpdateDeviceGroupViews() { DeviceGroupViews.Clear(); - foreach (BE.DeviceGroupModel deviceGroup in DeviceGroupsBE.DeviceGroupModels) + foreach (string deviceGroup in DeviceGroupsBE.DeviceGroupModels) { DeviceGroupViews.Add(new DeviceGroupViewModel(deviceGroup, DeviceGroupsBE)); } diff --git a/userinterface/ViewModels/Mapping/MappingViewModel.cs b/userinterface/ViewModels/Mapping/MappingViewModel.cs index 2516b7ff..48d6c170 100644 --- a/userinterface/ViewModels/Mapping/MappingViewModel.cs +++ b/userinterface/ViewModels/Mapping/MappingViewModel.cs @@ -21,11 +21,11 @@ public MappingViewModel(BE.MappingModel mappingBE, BE.MappingsModel mappingsBE) public void HandleAddMappingSelection(SelectionChangedEventArgs e) { - if (e.AddedItems.Count > 0 && e.AddedItems[0] is BE.DeviceGroupModel deviceGroup) + if (e.AddedItems.Count > 0 && e.AddedItems[0] is string deviceGroup) { // TODO: re-add default profile - // MappingBE.TryAddMapping(deviceGroup.ModelValue, BE.ProfilesModel.DefaultProfile.CurrentNameForDisplay); - MappingBE.TryAddMapping(deviceGroup.ModelValue, "Default"); + // MappingBE.TryAddMapping(deviceGroup, BE.ProfilesModel.DefaultProfile.CurrentNameForDisplay); + MappingBE.TryAddMapping(deviceGroup, "Default"); } } diff --git a/userinterface/Views/Device/DeviceGroupView.axaml.cs b/userinterface/Views/Device/DeviceGroupView.axaml.cs index 4fc30295..a2f26927 100644 --- a/userinterface/Views/Device/DeviceGroupView.axaml.cs +++ b/userinterface/Views/Device/DeviceGroupView.axaml.cs @@ -31,12 +31,7 @@ public void TextBox_KeyDown(object sender, KeyEventArgs e) public void LostFocusHandler(object sender, RoutedEventArgs args) { - if (sender is TextBox senderTextBox) - { - if (senderTextBox.DataContext is BE.DeviceGroupModel deviceGroup) - { - deviceGroup.TryUpdateFromInterface(); - } - } + // Device groups are now simple strings, no longer editable settings + // The binding handles updates automatically } } \ No newline at end of file diff --git a/userinterface/Views/Mapping/MappingView.axaml.cs b/userinterface/Views/Mapping/MappingView.axaml.cs index 60f0b4ca..2bce604b 100644 --- a/userinterface/Views/Mapping/MappingView.axaml.cs +++ b/userinterface/Views/Mapping/MappingView.axaml.cs @@ -26,7 +26,7 @@ public void AddMappingSelectionChanged(object sender, SelectionChangedEventArgs if (e.AddedItems.Count > 0 && DataContext is MappingViewModel viewModel) { - DeviceGroupSelectorToAddMapping.ItemsSource = Enumerable.Empty(); + DeviceGroupSelectorToAddMapping.ItemsSource = Enumerable.Empty(); viewModel.HandleAddMappingSelection(e); DeviceGroupSelectorToAddMapping.ItemsSource = viewModel.MappingBE.DeviceGroupsStillUnmapped; } diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 4e2e4c64..8b3955af 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -26,12 +26,14 @@ public BackEnd( IBackEndLoader backEndLoader, IProfilesModel profilesModel, DevicesModel devicesModel, - MappingsModel mappingsModel) + MappingsModel mappingsModel, + IServiceProvider serviceProvider) { BackEndLoader = backEndLoader; Devices = devicesModel; Mappings = mappingsModel; Profiles = profilesModel; + ServiceProvider = serviceProvider; } public DevicesModel Devices { get; set; } @@ -42,6 +44,8 @@ public BackEnd( protected IBackEndLoader BackEndLoader { get; set; } + protected IServiceProvider ServiceProvider { get; set; } + public void Load() { IEnumerable devicesData = BackEndLoader.LoadDevices(); @@ -50,6 +54,9 @@ public void Load() IEnumerable profilesData = BackEndLoader.LoadProfiles(); LoadProfilesFromData(profilesData); + // Ensure at least one default profile exists + EnsureDefaultProfileExists(); + DATA.MappingSet mappingData = BackEndLoader.LoadMappings(); LoadMappingsFromData(mappingData); } @@ -58,7 +65,7 @@ protected void LoadDevicesFromData(IEnumerable devicesData) { foreach(var deviceData in devicesData) { - Devices.TryAddDevice(deviceData); + Devices.TryAddFromData(deviceData); } } @@ -77,6 +84,22 @@ protected void LoadMappingsFromData(DATA.MappingSet mappingData) } } + protected void EnsureDefaultProfileExists() + { + // If no "Default" profile exists, create one and add it to the beginning + if (!Profiles.TryGetElement("Default", out _)) + { + var defaultProfile = ServiceProvider.GetRequiredService(); + defaultProfile.Name.TryUpdateModelDirectly("Default"); + + // Access ProfilesModel directly to insert at the beginning + if (Profiles is ProfilesModel profilesModel) + { + profilesModel.ElementsInternal.Insert(0, defaultProfile); + } + } + } + public void Apply() { try @@ -94,7 +117,7 @@ public void Apply() protected void WriteSettingsToDisk() { BackEndLoader.WriteSettingsToDisk( - Devices.DevicesEnumerable, + Devices.Elements, Mappings, Profiles.Elements); } @@ -139,7 +162,7 @@ protected IEnumerable MapToDriverProfiles(MappingModel mapping) protected IEnumerable MapToDriverDevices(string dg, string profileName) { - IEnumerable deviceModels = Devices.Devices.Where(d => d.DeviceGroup.ModelValue.Equals(dg)); + IEnumerable deviceModels = Devices.Elements.Where(d => d.DeviceGroup.ModelValue.Equals(dg)); return deviceModels.Select(dm => MapToDriverDevice(dm, profileName)); } diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index 18ccdf02..ce6d2032 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -522,7 +522,7 @@ public static IServiceProvider Compose(IServiceCollection services) var deviceGroups = sp.GetRequiredService(); var profiles = sp.GetRequiredService(); var emptyMappingSet = new DATA.MappingSet { Mappings = [] }; - return new MappingsModel(emptyMappingSet, deviceGroups, profiles); + return new MappingsModel(emptyMappingSet, deviceGroups, profiles, sp); }); services.AddSingleton(sp => diff --git a/userspace-backend/Model/DeviceGroups.cs b/userspace-backend/Model/DeviceGroups.cs index dca2a3ed..82a29144 100644 --- a/userspace-backend/Model/DeviceGroups.cs +++ b/userspace-backend/Model/DeviceGroups.cs @@ -9,13 +9,12 @@ namespace userspace_backend.Model { public interface IDeviceGroups { - + bool TryGetDeviceGroup(string name, out string? deviceGroup); } public class DeviceGroups : EditableSettingsCollection> { - public static readonly DeviceGroupModel DefaultDeviceGroup = - new DeviceGroupModel("Default", ModelValueValidators.AllChangesInvalidStringValidator); + public const string DefaultDeviceGroup = "Default"; public DeviceGroups(IEnumerable devices) : base(devices) @@ -23,23 +22,23 @@ public DeviceGroups(IEnumerable devices) GroupNameChangeValidator = new DeviceGroupValidator(this); } - public ObservableCollection DeviceGroupModels { get; set; } + public ObservableCollection DeviceGroupModels { get; set; } protected DeviceGroupValidator GroupNameChangeValidator { get; set; } - public bool TryGetDeviceGroup(string name, out DeviceGroupModel? deviceGroup) + public bool TryGetDeviceGroup(string name, out string? deviceGroup) { deviceGroup = DeviceGroupModels.FirstOrDefault( - g => string.Equals(g.ModelValue, name, StringComparison.InvariantCultureIgnoreCase)); + g => string.Equals(g, name, StringComparison.InvariantCultureIgnoreCase)); return deviceGroup is not null; } - public DeviceGroupModel AddOrGetDeviceGroup(string deviceGroupName) + public string AddOrGetDeviceGroup(string deviceGroupName) { - if (!TryGetDeviceGroup(deviceGroupName, out DeviceGroupModel? deviceGroup)) + if (!TryGetDeviceGroup(deviceGroupName, out string? deviceGroup)) { - deviceGroup = new DeviceGroupModel(deviceGroupName, GroupNameChangeValidator); + deviceGroup = deviceGroupName; DeviceGroupModels.Add(deviceGroup); } @@ -80,24 +79,23 @@ public bool TryAddDeviceGroup(string? deviceGroupName = null) return false; } - DeviceGroupModel deviceGroup = new DeviceGroupModel(deviceGroupName, GroupNameChangeValidator); - DeviceGroupModels.Add(deviceGroup); + DeviceGroupModels.Add(deviceGroupName); return true; } - public bool RemoveDeviceGroup(DeviceGroupModel deviceGroup) + public bool RemoveDeviceGroup(string deviceGroup) { return DeviceGroupModels.Remove(deviceGroup); } public override IEnumerable MapToData() { - return DeviceGroupModels.Select(g => g.ModelValue); + return DeviceGroupModels; } protected override IEnumerable EnumerateEditableSettings() { - return DeviceGroupModels; + return []; } protected override IEnumerable EnumerateEditableSettingsCollections() @@ -109,7 +107,7 @@ protected override void InitEditableSettingsAndCollections(IEnumerable d { // This initialization does not set up all device group models. // That is done in backend construction in order to point the devices to their groups. - DeviceGroupModels = new ObservableCollection() { DefaultDeviceGroup }; + DeviceGroupModels = new ObservableCollection() { DefaultDeviceGroup }; } } diff --git a/userspace-backend/Model/MappingModel.cs b/userspace-backend/Model/MappingModel.cs index 47a04ed1..3ec2d29b 100644 --- a/userspace-backend/Model/MappingModel.cs +++ b/userspace-backend/Model/MappingModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.ObjectModel; using System.Linq; using userspace_backend.Model.EditableSettings; using userspace_backend.Data; @@ -18,17 +19,37 @@ public MappingModel( IEditableSettingSpecific name, IModelValueValidator nameValidator, IDeviceGroups deviceGroups, - IProfilesModel profiles) + IProfilesModel profiles, + Mapping dataObject) : base(name, [], []) { NameValidator = nameValidator; SetActive = true; DeviceGroups = deviceGroups; Profiles = profiles; + + // Initialize collections + IndividualMappings = new ObservableCollection(); + DeviceGroupsStillUnmapped = new ObservableCollection(); + + // Initialize mappings from data + InitIndividualMappings(dataObject); + FindDeviceGroupsStillUnmapped(); + + // Wire up events + IndividualMappings.CollectionChanged += OnIndividualMappingsChanged; + if (DeviceGroups is DeviceGroups dg) + { + dg.DeviceGroupModels.CollectionChanged += OnIndividualMappingsChanged; + } } public bool SetActive { get; set; } + public ObservableCollection IndividualMappings { get; protected set; } + + public ObservableCollection DeviceGroupsStillUnmapped { get; protected set; } + protected IModelValueValidator NameValidator { get; } protected IDeviceGroups DeviceGroups { get; } @@ -45,7 +66,7 @@ public override Mapping MapToData() foreach (var group in IndividualMappings) { - mapping.GroupsToProfiles.Add(group.DeviceGroup.ModelValue, group.Profile.Name.ModelValue); + mapping.GroupsToProfiles.Add(group.DeviceGroup, group.Profile.Name.ModelValue); } return mapping; @@ -61,9 +82,9 @@ protected void InitIndividualMappings(Mapping dataObject) public bool TryAddMapping(string deviceGroupName, string profileName) { - if (!DeviceGroups.TryGetDeviceGroup(deviceGroupName, out DeviceGroupModel? deviceGroup) + if (!DeviceGroups.TryGetDeviceGroup(deviceGroupName, out string? deviceGroup) || deviceGroup == null - || IndividualMappings.Any(m => m.DeviceGroup.Equals(deviceGroup))) + || IndividualMappings.Any(m => string.Equals(m.DeviceGroup, deviceGroup, StringComparison.InvariantCultureIgnoreCase))) { return false; } @@ -89,11 +110,14 @@ protected void FindDeviceGroupsStillUnmapped() { DeviceGroupsStillUnmapped.Clear(); - foreach (DeviceGroupModel group in DeviceGroups.DeviceGroupModels) + if (DeviceGroups is DeviceGroups dg) { - if (!IndividualMappings.Any(m => m.DeviceGroup.Equals(group))) + foreach (string group in dg.DeviceGroupModels) { - DeviceGroupsStillUnmapped.Add(group); + if (!IndividualMappings.Any(m => string.Equals(m.DeviceGroup, group, StringComparison.InvariantCultureIgnoreCase))) + { + DeviceGroupsStillUnmapped.Add(group); + } } } } @@ -116,7 +140,7 @@ protected override bool TryMapEditableSettingsCollectionsFromData(Mapping data) public class MappingGroup { - public DeviceGroupModel DeviceGroup { get; set; } + public string DeviceGroup { get; set; } public IProfileModel Profile { get; set; } diff --git a/userspace-backend/Model/MappingsModel.cs b/userspace-backend/Model/MappingsModel.cs index 9883fb6c..9ebee3a7 100644 --- a/userspace-backend/Model/MappingsModel.cs +++ b/userspace-backend/Model/MappingsModel.cs @@ -10,9 +10,10 @@ namespace userspace_backend.Model { public class MappingsModel : EditableSettingsCollection { - public MappingsModel(DATA.MappingSet dataObject, DeviceGroups deviceGroups, IProfilesModel profiles) + public MappingsModel(DATA.MappingSet dataObject, DeviceGroups deviceGroups, IProfilesModel profiles, IServiceProvider serviceProvider) : base(dataObject) { + ServiceProvider = serviceProvider; DeviceGroups = deviceGroups; Profiles = profiles; NameValidator = new MappingNameValidator(this); @@ -21,6 +22,8 @@ public MappingsModel(DATA.MappingSet dataObject, DeviceGroups deviceGroups, IPro public ObservableCollection Mappings { get; protected set; } + protected IServiceProvider ServiceProvider { get; } + protected DeviceGroups DeviceGroups { get; } protected IProfilesModel Profiles { get; } @@ -79,7 +82,15 @@ public bool TryAddMapping(DATA.Mapping? mappingToAdd = null) return false; } - MappingModel mapping = new MappingModel(mappingToAdd, NameValidator, DeviceGroups, Profiles); + // Create Name setting for the mapping + var nameSetting = new EditableSettingV2( + displayName: "Name", + initialValue: mappingToAdd.Name, + parser: UserInputParsers.StringParser, + validator: NameValidator); + + // Construct MappingModel with DI pattern + MappingModel mapping = new MappingModel(nameSetting, NameValidator, DeviceGroups, Profiles, mappingToAdd); Mappings.Add(mapping); return true; } diff --git a/userspace-backend/Model/ProfilesModel.cs b/userspace-backend/Model/ProfilesModel.cs index 77af4fb5..48692d22 100644 --- a/userspace-backend/Model/ProfilesModel.cs +++ b/userspace-backend/Model/ProfilesModel.cs @@ -14,9 +14,7 @@ public interface IProfilesModel : IEditableSettingsList, IProfilesModel { - // TODO: DI - hand default profile to profiles model - // public static readonly ProfileModel DefaultProfile = new ProfileModel( - // GenerateNewDefaultProfile("Default"), ModelValueValidators.AllChangesInvalidStringValidator); + // Default profile is created during BackEnd.Load() if it doesn't exist public ProfilesModel(IServiceProvider serviceProvider) : base(serviceProvider, [], []) From 37c741209bb913560b34e9fe2ceea78687d7ce65 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 14:51:18 -0400 Subject: [PATCH 36/47] Properly load devices from data --- userspace-backend/BackEnd.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 8b3955af..8dfd4ff6 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -63,10 +63,7 @@ public void Load() protected void LoadDevicesFromData(IEnumerable devicesData) { - foreach(var deviceData in devicesData) - { - Devices.TryAddFromData(deviceData); - } + Devices.TryMapFromData(devicesData); } protected void LoadProfilesFromData(IEnumerable profileData) From 9119560d09af4c1a5e385b00aaadcc093e4b3ca5 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 15:00:22 -0400 Subject: [PATCH 37/47] Fix device loading and profile insertion --- userspace-backend/BackEnd.cs | 7 +------ .../EditableSettings/EditableSettingsList.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 8dfd4ff6..cf63eb4a 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -88,12 +88,7 @@ protected void EnsureDefaultProfileExists() { var defaultProfile = ServiceProvider.GetRequiredService(); defaultProfile.Name.TryUpdateModelDirectly("Default"); - - // Access ProfilesModel directly to insert at the beginning - if (Profiles is ProfilesModel profilesModel) - { - profilesModel.ElementsInternal.Insert(0, defaultProfile); - } + Profiles.TryInsert(0, defaultProfile); } } diff --git a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs index e691bc20..7e85777e 100644 --- a/userspace-backend/Model/EditableSettings/EditableSettingsList.cs +++ b/userspace-backend/Model/EditableSettings/EditableSettingsList.cs @@ -15,6 +15,8 @@ public interface IEditableSettingsList public bool TryAdd(T element); + public bool TryInsert(int index, T element); + public bool TryGetElement(string name, out T? element); public bool TryRemoveElement(T element); @@ -60,6 +62,19 @@ public bool TryAdd(T element) return false; } + public bool TryInsert(int index, T element) + { + string name = GetNameFromElement(element); + + if (!ContainsElementWithName(name)) + { + ElementsInternal.Insert(index, element); + return true; + } + + return false; + } + public bool TryAddNewDefault() { for (int i = 1; i < 10; i++) From 6f468019f6c84adf2d70901edb260d283e8d1a34 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 15:07:18 -0400 Subject: [PATCH 38/47] Changed method signatures to use Interfaces --- userspace-backend/BackEnd.cs | 4 ++-- userspace-backend/BackEndLoader.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index cf63eb4a..7e1c26e3 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -154,11 +154,11 @@ protected IEnumerable MapToDriverProfiles(MappingModel mapping) protected IEnumerable MapToDriverDevices(string dg, string profileName) { - IEnumerable deviceModels = Devices.Elements.Where(d => d.DeviceGroup.ModelValue.Equals(dg)); + IEnumerable deviceModels = Devices.Elements.Where(d => d.DeviceGroup.ModelValue.Equals(dg)); return deviceModels.Select(dm => MapToDriverDevice(dm, profileName)); } - protected DeviceSettings MapToDriverDevice(DeviceModel deviceModel, string profileName) + protected DeviceSettings MapToDriverDevice(IDeviceModel deviceModel, string profileName) { return new DeviceSettings() { diff --git a/userspace-backend/BackEndLoader.cs b/userspace-backend/BackEndLoader.cs index 3ad6be35..401344ee 100644 --- a/userspace-backend/BackEndLoader.cs +++ b/userspace-backend/BackEndLoader.cs @@ -19,7 +19,7 @@ public interface IBackEndLoader public IEnumerable LoadProfiles(); public void WriteSettingsToDisk( - IEnumerable devices, + IEnumerable devices, MappingsModel mappings, IEnumerable profiles); } @@ -62,7 +62,7 @@ public DATA.MappingSet LoadMappings() } public void WriteSettingsToDisk( - IEnumerable devices, + IEnumerable devices, MappingsModel mappings, IEnumerable profiles) { @@ -71,7 +71,7 @@ public void WriteSettingsToDisk( WriteProfiles(profiles); } - protected void WriteDevices(IEnumerable devices) + protected void WriteDevices(IEnumerable devices) { IEnumerable devicesData = devices.Select(d => d.MapToData()); string devicesFileText = DevicesReaderWriter.Serialize(devicesData); From 8a9c89b698410f17faa427842049c07e26c31a95 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 15:13:24 -0400 Subject: [PATCH 39/47] Fix bootstrapper --- userinterface/App.axaml.cs | 6 +++++- userspace-backend/Bootstrapper.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/userinterface/App.axaml.cs b/userinterface/App.axaml.cs index 95d389c9..fc839a04 100644 --- a/userinterface/App.axaml.cs +++ b/userinterface/App.axaml.cs @@ -64,7 +64,11 @@ protected Bootstrapper BootstrapBackEnd() { return new Bootstrapper() { - BackEndLoader = new BackEndLoader(System.AppDomain.CurrentDomain.BaseDirectory), + BackEndLoader = new BackEndLoader( + System.AppDomain.CurrentDomain.BaseDirectory, + new DevicesReaderWriter(), + new MappingsReaderWriter(), + new ProfileReaderWriter()), DevicesToLoad = [ new DATA.Device() { Name = "Superlight 2", DPI = 32000, HWID = @"HID\VID_046D&PID_C54D&MI_00", PollingRate = 1000, DeviceGroup = "Logitech Mice" }, diff --git a/userspace-backend/Bootstrapper.cs b/userspace-backend/Bootstrapper.cs index 9dbe6306..0ed0ede9 100644 --- a/userspace-backend/Bootstrapper.cs +++ b/userspace-backend/Bootstrapper.cs @@ -35,7 +35,7 @@ public DATA.MappingSet LoadMappings() return ProfilesToLoad; } - public void WriteSettingsToDisk(IEnumerable devices, MappingsModel mappings, IEnumerable profiles) + public void WriteSettingsToDisk(IEnumerable devices, MappingsModel mappings, IEnumerable profiles) { BackEndLoader.WriteSettingsToDisk(devices, mappings, profiles); } From e6742d16261561977bd1ced699bd758d9066f6bf Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 15:18:05 -0400 Subject: [PATCH 40/47] parsers from static to DI --- userspace-backend/Model/DeviceGroups.cs | 2 +- userspace-backend/Model/MappingsModel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/userspace-backend/Model/DeviceGroups.cs b/userspace-backend/Model/DeviceGroups.cs index 82a29144..aa908b5c 100644 --- a/userspace-backend/Model/DeviceGroups.cs +++ b/userspace-backend/Model/DeviceGroups.cs @@ -12,7 +12,7 @@ public interface IDeviceGroups bool TryGetDeviceGroup(string name, out string? deviceGroup); } - public class DeviceGroups : EditableSettingsCollection> + public class DeviceGroups : EditableSettingsCollection>, IDeviceGroups { public const string DefaultDeviceGroup = "Default"; diff --git a/userspace-backend/Model/MappingsModel.cs b/userspace-backend/Model/MappingsModel.cs index 9ebee3a7..7130bf61 100644 --- a/userspace-backend/Model/MappingsModel.cs +++ b/userspace-backend/Model/MappingsModel.cs @@ -86,7 +86,7 @@ public bool TryAddMapping(DATA.Mapping? mappingToAdd = null) var nameSetting = new EditableSettingV2( displayName: "Name", initialValue: mappingToAdd.Name, - parser: UserInputParsers.StringParser, + parser: ServiceProvider.GetRequiredService>(), validator: NameValidator); // Construct MappingModel with DI pattern From 707037a68cfb2d01957141ac3f5891bba2bc52c4 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 15:21:55 -0400 Subject: [PATCH 41/47] add missing imports --- userspace-backend/Model/MappingsModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/userspace-backend/Model/MappingsModel.cs b/userspace-backend/Model/MappingsModel.cs index 7130bf61..e884ffb5 100644 --- a/userspace-backend/Model/MappingsModel.cs +++ b/userspace-backend/Model/MappingsModel.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; From b424448b9a48b9bb035422a60316c79af6a1a2fd Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 15:36:46 -0400 Subject: [PATCH 42/47] Fix UI compile time errors --- .../ViewModels/Device/DeviceGroupSelectorViewModel.cs | 6 +++--- userinterface/ViewModels/Device/DeviceViewModel.cs | 6 +++--- userinterface/ViewModels/Device/DevicesListViewModel.cs | 8 ++++---- userinterface/Views/Device/DeviceGroupSelectorView.axaml | 2 +- userinterface/Views/Device/DeviceGroupView.axaml | 2 +- userinterface/Views/Mapping/MappingView.axaml | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs b/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs index b65d5541..9b6db302 100644 --- a/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs +++ b/userinterface/ViewModels/Device/DeviceGroupSelectorViewModel.cs @@ -7,14 +7,14 @@ public partial class DeviceGroupSelectorViewModel : ViewModelBase { protected string selectedEntry = null!; - public DeviceGroupSelectorViewModel(DeviceModel device, DeviceGroups deviceGroupsBE) + public DeviceGroupSelectorViewModel(IDeviceModel device, DeviceGroups deviceGroupsBE) { Device = device; DeviceGroupsBE = deviceGroupsBE; RefreshSelectedDeviceGroup(); } - protected DeviceModel Device { get; } + protected IDeviceModel Device { get; } protected DeviceGroups DeviceGroupsBE { get; } public ObservableCollection DeviceGroupEntries => @@ -27,7 +27,7 @@ public string SelectedEntry { if (DeviceGroupEntries.Contains(value)) { - Device.DeviceGroup.TryUpdateUserInput(value); + Device.DeviceGroup.TryUpdateModelDirectly(value); selectedEntry = value; } } diff --git a/userinterface/ViewModels/Device/DeviceViewModel.cs b/userinterface/ViewModels/Device/DeviceViewModel.cs index 670dbd40..f58b5ef1 100644 --- a/userinterface/ViewModels/Device/DeviceViewModel.cs +++ b/userinterface/ViewModels/Device/DeviceViewModel.cs @@ -6,7 +6,7 @@ namespace userinterface.ViewModels.Device { public partial class DeviceViewModel : ViewModelBase { - public DeviceViewModel(BE.DeviceModel deviceBE, BE.DevicesModel devicesBE) + public DeviceViewModel(BE.IDeviceModel deviceBE, BE.DevicesModel devicesBE) { DeviceBE = deviceBE; DevicesBE = devicesBE; @@ -18,7 +18,7 @@ public DeviceViewModel(BE.DeviceModel deviceBE, BE.DevicesModel devicesBE) DeviceGroup = new DeviceGroupSelectorViewModel(DeviceBE, DevicesBE.DeviceGroups); } - protected BE.DeviceModel DeviceBE { get; } + protected BE.IDeviceModel DeviceBE { get; } protected BE.DevicesModel DevicesBE { get; } @@ -36,7 +36,7 @@ public DeviceViewModel(BE.DeviceModel deviceBE, BE.DevicesModel devicesBE) public void DeleteSelf() { - bool success = DevicesBE.RemoveDevice(DeviceBE); + bool success = DevicesBE.TryRemoveElement(DeviceBE); Debug.Assert(success); } } diff --git a/userinterface/ViewModels/Device/DevicesListViewModel.cs b/userinterface/ViewModels/Device/DevicesListViewModel.cs index 3a0d4239..2cb913a2 100644 --- a/userinterface/ViewModels/Device/DevicesListViewModel.cs +++ b/userinterface/ViewModels/Device/DevicesListViewModel.cs @@ -11,12 +11,12 @@ public DevicesListViewModel(BE.DevicesModel devicesBE) DevicesBE = devicesBE; DeviceViews = []; UpdateDeviceViews(); - DevicesBE.Devices.CollectionChanged += DevicesCollectionChanged; + ((INotifyCollectionChanged)DevicesBE.Elements).CollectionChanged += DevicesCollectionChanged; } protected BE.DevicesModel DevicesBE { get; } - public ObservableCollection Devices => DevicesBE.Devices; + public ReadOnlyObservableCollection Devices => DevicesBE.Elements; public ObservableCollection DeviceViews { get; } @@ -26,12 +26,12 @@ private void DevicesCollectionChanged(object? sender, NotifyCollectionChangedEve public void UpdateDeviceViews() { DeviceViews.Clear(); - foreach (BE.DeviceModel device in DevicesBE.Devices) + foreach (BE.IDeviceModel device in DevicesBE.Elements) { DeviceViews.Add(new DeviceViewModel(device, DevicesBE)); } } - public bool TryAddDevice() => DevicesBE.TryAddDevice(); + public bool TryAddDevice() => DevicesBE.TryAddNewDefault(); } } diff --git a/userinterface/Views/Device/DeviceGroupSelectorView.axaml b/userinterface/Views/Device/DeviceGroupSelectorView.axaml index 832dd896..afacaf20 100644 --- a/userinterface/Views/Device/DeviceGroupSelectorView.axaml +++ b/userinterface/Views/Device/DeviceGroupSelectorView.axaml @@ -113,7 +113,7 @@ Margin="12,0,0,0"> - + diff --git a/userinterface/Views/Device/DeviceGroupView.axaml b/userinterface/Views/Device/DeviceGroupView.axaml index 3ba04d58..bcb2ba21 100644 --- a/userinterface/Views/Device/DeviceGroupView.axaml +++ b/userinterface/Views/Device/DeviceGroupView.axaml @@ -61,7 +61,7 @@ + Text="{Binding DeviceGroupBE, Mode=TwoWay}" /> diff --git a/userinterface/Views/Mapping/MappingView.axaml b/userinterface/Views/Mapping/MappingView.axaml index 55cd2221..5eb4d757 100644 --- a/userinterface/Views/Mapping/MappingView.axaml +++ b/userinterface/Views/Mapping/MappingView.axaml @@ -155,7 +155,7 @@ BorderThickness="0"> - + @@ -173,7 +173,7 @@ + Text="{Binding DeviceGroup}"/> Date: Sun, 26 Oct 2025 16:06:24 -0400 Subject: [PATCH 43/47] Defaults for everything and runtime errors fixed --- userspace-backend/BackEnd.cs | 42 +++++++++++++++++-- userspace-backend/BackEndComposer.cs | 32 ++++++++++++++ userspace-backend/BackEndLoader.cs | 35 +++++++++++++++- .../Model/ProfileComponents/HiddenModel.cs | 6 +++ 4 files changed, 110 insertions(+), 5 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 7e1c26e3..ab8ddef5 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -54,11 +54,13 @@ public void Load() IEnumerable profilesData = BackEndLoader.LoadProfiles(); LoadProfilesFromData(profilesData); - // Ensure at least one default profile exists - EnsureDefaultProfileExists(); - DATA.MappingSet mappingData = BackEndLoader.LoadMappings(); LoadMappingsFromData(mappingData); + + // Ensure defaults exist for first-run experience + EnsureDefaultDeviceGroupExists(); + EnsureDefaultProfileExists(); + EnsureDefaultMappingExists(); } protected void LoadDevicesFromData(IEnumerable devicesData) @@ -81,6 +83,15 @@ protected void LoadMappingsFromData(DATA.MappingSet mappingData) } } + protected void EnsureDefaultDeviceGroupExists() + { + // If no device groups exist, create a "Default" group + if (Devices.DeviceGroups.DeviceGroupModels.Count == 0) + { + Devices.DeviceGroups.AddOrGetDeviceGroup(DeviceGroups.DefaultDeviceGroup); + } + } + protected void EnsureDefaultProfileExists() { // If no "Default" profile exists, create one and add it to the beginning @@ -92,6 +103,31 @@ protected void EnsureDefaultProfileExists() } } + protected void EnsureDefaultMappingExists() + { + // If no mappings exist, create a "Default" mapping + if (Mappings.Mappings.Count == 0) + { + var defaultMapping = new DATA.Mapping + { + Name = "Default", + GroupsToProfiles = new DATA.Mapping.GroupsToProfilesMapping + { + { DeviceGroups.DefaultDeviceGroup, "Default" } + } + }; + + if (Mappings.TryAddMapping(defaultMapping)) + { + // Set this as the active mapping + if (Mappings.TryGetMapping("Default", out MappingModel? mapping) && mapping != null) + { + mapping.SetActive = true; + } + } + } + } + public void Apply() { try diff --git a/userspace-backend/BackEndComposer.cs b/userspace-backend/BackEndComposer.cs index ce6d2032..2af9974b 100644 --- a/userspace-backend/BackEndComposer.cs +++ b/userspace-backend/BackEndComposer.cs @@ -11,6 +11,7 @@ using static userspace_backend.Data.Profiles.Accel.FormulaAccel; using static userspace_backend.Data.Profiles.Accel.LookupTableAccel; using static userspace_backend.Data.Profiles.Acceleration; +using static userspace_backend.Model.EditableSettings.EditableSettingsSelectorHelper; namespace userspace_backend { @@ -180,6 +181,17 @@ public static IServiceProvider Compose(IServiceCollection services) parser: services.GetRequiredService>(), validator: services.GetRequiredService>())); + // Register selector options for AccelerationDefinitionType + services.AddKeyedTransient>( + GetSelectionKey(AccelerationDefinitionType.None), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationDefinitionType.Formula), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationDefinitionType.LookupTable), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + #endregion Acceleration #region FormulaAccel @@ -201,6 +213,26 @@ public static IServiceProvider Compose(IServiceCollection services) parser: services.GetRequiredService>(), validator: services.GetRequiredService>())); + // Register selector options for AccelerationFormulaType + services.AddKeyedTransient>( + GetSelectionKey(AccelerationFormulaType.Synchronous), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationFormulaType.Linear), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationFormulaType.Classic), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationFormulaType.Power), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationFormulaType.Natural), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + services.AddKeyedTransient>( + GetSelectionKey(AccelerationFormulaType.Jump), + (sp, key) => (IEditableSettingsCollectionSpecific)sp.GetRequiredService()); + #endregion FormulaAccel #region LookupTable diff --git a/userspace-backend/BackEndLoader.cs b/userspace-backend/BackEndLoader.cs index 401344ee..3074eca1 100644 --- a/userspace-backend/BackEndLoader.cs +++ b/userspace-backend/BackEndLoader.cs @@ -46,6 +46,10 @@ public BackEndLoader( public IEnumerable LoadDevices() { string devicesFile = GetDevicesFile(SettingsDirectory); + if (!File.Exists(devicesFile)) + { + return []; + } string devicesText = File.ReadAllText(devicesFile); IEnumerable devicesData = DevicesReaderWriter.Read(devicesText); return devicesData; @@ -53,12 +57,39 @@ public BackEndLoader( public DATA.MappingSet LoadMappings() { - throw new NotImplementedException(); + string mappingsFile = GetMappingsFile(SettingsDirectory); + if (!File.Exists(mappingsFile)) + { + return new DATA.MappingSet { Mappings = [] }; + } + string mappingsText = File.ReadAllText(mappingsFile); + DATA.MappingSet mappingsData = MappingsReaderWriter.Read(mappingsText); + return mappingsData; } public IEnumerable LoadProfiles() { - throw new NotImplementedException(); + string profilesDirectory = GetProfilesDirectory(SettingsDirectory); + if (!Directory.Exists(profilesDirectory)) + { + return []; + } + + string[] profileFiles = Directory.GetFiles(profilesDirectory, "*.json"); + if (profileFiles.Length == 0) + { + return []; + } + + List profiles = []; + foreach (string profileFile in profileFiles) + { + string profileText = File.ReadAllText(profileFile); + DATA.Profile profileData = ProfileReaderWriter.Read(profileText); + profiles.Add(profileData); + } + + return profiles; } public void WriteSettingsToDisk( diff --git a/userspace-backend/Model/ProfileComponents/HiddenModel.cs b/userspace-backend/Model/ProfileComponents/HiddenModel.cs index 619bcd9b..411c5164 100644 --- a/userspace-backend/Model/ProfileComponents/HiddenModel.cs +++ b/userspace-backend/Model/ProfileComponents/HiddenModel.cs @@ -37,6 +37,12 @@ public HiddenModel( [FromKeyedServices(OutputSmoothingHalfLifeDIKey)]IEditableSettingSpecific outputSmoothingHalfLife ) : base([rotationDegrees, angleSnappingDegrees, leftRightRatio, upDownRatio, speedCap, outputSmoothingHalfLife], []) { + RotationDegrees = rotationDegrees; + AngleSnappingDegrees = angleSnappingDegrees; + LeftRightRatio = leftRightRatio; + UpDownRatio = upDownRatio; + SpeedCap = speedCap; + OutputSmoothingHalfLife = outputSmoothingHalfLife; } public IEditableSettingSpecific RotationDegrees { get; set; } From 9e66b07ed231fef94399410bbcc33f47c3ab3c92 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 16:24:10 -0400 Subject: [PATCH 44/47] Add device defult --- userspace-backend/BackEnd.cs | 21 +++++++++++++++++++++ userspace-backend/Model/DeviceModel.cs | 15 ++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index ab8ddef5..5669c1de 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -59,6 +59,7 @@ public void Load() // Ensure defaults exist for first-run experience EnsureDefaultDeviceGroupExists(); + EnsureDefaultDeviceExists(); EnsureDefaultProfileExists(); EnsureDefaultMappingExists(); } @@ -92,6 +93,26 @@ protected void EnsureDefaultDeviceGroupExists() } } + protected void EnsureDefaultDeviceExists() + { + // If no devices exist, create a hardcoded dummy device for bootstrapping + // TODO: Replace with actual device detection via wrapper abstraction (Windows/Linux) + if (Devices.Elements.Count == 0) + { + var defaultDevice = new DATA.Device + { + Name = "Default Device", + HWID = "DEFAULT_DEVICE_ID", + DPI = 1000, + PollingRate = 1000, + Ignore = false, + DeviceGroup = DeviceGroups.DefaultDeviceGroup + }; + + Devices.TryMapFromData([defaultDevice]); + } + } + protected void EnsureDefaultProfileExists() { // If no "Default" profile exists, create one and add it to the beginning diff --git a/userspace-backend/Model/DeviceModel.cs b/userspace-backend/Model/DeviceModel.cs index 19fd6c74..8b5bfab9 100644 --- a/userspace-backend/Model/DeviceModel.cs +++ b/userspace-backend/Model/DeviceModel.cs @@ -1,4 +1,5 @@ -using userspace_backend.Data; +using Microsoft.Extensions.DependencyInjection; +using userspace_backend.Data; using userspace_backend.Model.EditableSettings; namespace userspace_backend.Model @@ -28,12 +29,12 @@ public class DeviceModel : NamedEditableSettingsCollection, IDeviceModel public const string DeviceGroupDIKey = $"{nameof(DeviceModel)}.{nameof(DeviceGroup)}"; public DeviceModel( - IEditableSettingSpecific name, - IEditableSettingSpecific hardwareID, - IEditableSettingSpecific dpi, - IEditableSettingSpecific pollRate, - IEditableSettingSpecific ignore, - IEditableSettingSpecific deviceGroup) + [FromKeyedServices(NameDIKey)] IEditableSettingSpecific name, + [FromKeyedServices(HardwareIDDIKey)] IEditableSettingSpecific hardwareID, + [FromKeyedServices(DPIDIKey)] IEditableSettingSpecific dpi, + [FromKeyedServices(PollRateDIKey)] IEditableSettingSpecific pollRate, + [FromKeyedServices(IgnoreDIKey)] IEditableSettingSpecific ignore, + [FromKeyedServices(DeviceGroupDIKey)] IEditableSettingSpecific deviceGroup) : base(name, [hardwareID, dpi, pollRate, ignore, deviceGroup], []) { HardwareID = hardwareID; From cbe7bcd2f75fd74f685177a63fa4d789e73c5443 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 17:02:18 -0400 Subject: [PATCH 45/47] Load default device like the others --- userspace-backend/BackEnd.cs | 31 ++++++++++---------- userspace-backend/BackEndLoader.cs | 11 ++----- userspace-backend/IO/DevicesReaderWriter.cs | 2 +- userspace-backend/IO/MappingsReaderWriter.cs | 2 +- userspace-backend/IO/ProfileReaderWriter.cs | 2 +- 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 5669c1de..f0e16bb9 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -95,21 +95,16 @@ protected void EnsureDefaultDeviceGroupExists() protected void EnsureDefaultDeviceExists() { - // If no devices exist, create a hardcoded dummy device for bootstrapping - // TODO: Replace with actual device detection via wrapper abstraction (Windows/Linux) - if (Devices.Elements.Count == 0) + // If no "Default" device exists, create one + if (!Devices.TryGetElement("Default", out _)) { - var defaultDevice = new DATA.Device - { - Name = "Default Device", - HWID = "DEFAULT_DEVICE_ID", - DPI = 1000, - PollingRate = 1000, - Ignore = false, - DeviceGroup = DeviceGroups.DefaultDeviceGroup - }; + var defaultDevice = ServiceProvider.GetRequiredService(); + defaultDevice.Name.TryUpdateModelDirectly("Default"); + defaultDevice.HardwareID.TryUpdateModelDirectly("DEFAULT_DEVICE_ID"); + defaultDevice.DeviceGroup.TryUpdateModelDirectly(DeviceGroups.DefaultDeviceGroup); + // DPI, PollRate, and Ignore already have sensible defaults from DI (1000, 1000, false) - Devices.TryMapFromData([defaultDevice]); + Devices.TryInsert(0, defaultDevice); } } @@ -126,8 +121,8 @@ protected void EnsureDefaultProfileExists() protected void EnsureDefaultMappingExists() { - // If no mappings exist, create a "Default" mapping - if (Mappings.Mappings.Count == 0) + // If no "Default" mapping exists, create one + if (!Mappings.TryGetMapping("Default", out _)) { var defaultMapping = new DATA.Mapping { @@ -147,6 +142,12 @@ protected void EnsureDefaultMappingExists() } } } + + // Ensure at least one mapping has SetActive = true + if (Mappings.GetMappingToSetActive() == null && Mappings.Mappings.Count > 0) + { + Mappings.Mappings[0].SetActive = true; + } } public void Apply() diff --git a/userspace-backend/BackEndLoader.cs b/userspace-backend/BackEndLoader.cs index 3074eca1..a0cea532 100644 --- a/userspace-backend/BackEndLoader.cs +++ b/userspace-backend/BackEndLoader.cs @@ -51,7 +51,7 @@ public BackEndLoader( return []; } string devicesText = File.ReadAllText(devicesFile); - IEnumerable devicesData = DevicesReaderWriter.Read(devicesText); + IEnumerable devicesData = DevicesReaderWriter.Deserialize(devicesText); return devicesData; } @@ -63,7 +63,7 @@ public DATA.MappingSet LoadMappings() return new DATA.MappingSet { Mappings = [] }; } string mappingsText = File.ReadAllText(mappingsFile); - DATA.MappingSet mappingsData = MappingsReaderWriter.Read(mappingsText); + DATA.MappingSet mappingsData = MappingsReaderWriter.Deserialize(mappingsText); return mappingsData; } @@ -76,16 +76,11 @@ public DATA.MappingSet LoadMappings() } string[] profileFiles = Directory.GetFiles(profilesDirectory, "*.json"); - if (profileFiles.Length == 0) - { - return []; - } - List profiles = []; foreach (string profileFile in profileFiles) { string profileText = File.ReadAllText(profileFile); - DATA.Profile profileData = ProfileReaderWriter.Read(profileText); + DATA.Profile profileData = ProfileReaderWriter.Deserialize(profileText); profiles.Add(profileData); } diff --git a/userspace-backend/IO/DevicesReaderWriter.cs b/userspace-backend/IO/DevicesReaderWriter.cs index b1883988..964f7e8c 100644 --- a/userspace-backend/IO/DevicesReaderWriter.cs +++ b/userspace-backend/IO/DevicesReaderWriter.cs @@ -19,7 +19,7 @@ public override string Serialize(IEnumerable devices) public override IEnumerable Deserialize(string toRead) { - return JsonSerializer.Deserialize>(toRead); + return JsonSerializer.Deserialize>(toRead, JsonOptions); } } } diff --git a/userspace-backend/IO/MappingsReaderWriter.cs b/userspace-backend/IO/MappingsReaderWriter.cs index af6b605c..5c4d5018 100644 --- a/userspace-backend/IO/MappingsReaderWriter.cs +++ b/userspace-backend/IO/MappingsReaderWriter.cs @@ -25,7 +25,7 @@ public override string Serialize(MappingSet toWrite) public override MappingSet Deserialize(string toRead) { - return JsonSerializer.Deserialize(toRead); + return JsonSerializer.Deserialize(toRead, JsonOptions); } } } diff --git a/userspace-backend/IO/ProfileReaderWriter.cs b/userspace-backend/IO/ProfileReaderWriter.cs index a0c39c83..175c7067 100644 --- a/userspace-backend/IO/ProfileReaderWriter.cs +++ b/userspace-backend/IO/ProfileReaderWriter.cs @@ -20,7 +20,7 @@ public class ProfileReaderWriter : ReaderWriterBase public override DATA.Profile Deserialize(string toRead) { - return JsonSerializer.Deserialize(toRead); + return JsonSerializer.Deserialize(toRead, JsonOptions); } public override string Serialize(DATA.Profile toWrite) From 2abefaca1d7f1d669da34c68fdbd85e542b83d6e Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 17:12:26 -0400 Subject: [PATCH 46/47] defaults with defaults --- userspace-backend/BackEnd.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index f0e16bb9..40243ef8 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -95,8 +95,8 @@ protected void EnsureDefaultDeviceGroupExists() protected void EnsureDefaultDeviceExists() { - // If no "Default" device exists, create one - if (!Devices.TryGetElement("Default", out _)) + // If no devices exist, create a default device + if (Devices.Elements.Count == 0) { var defaultDevice = ServiceProvider.GetRequiredService(); defaultDevice.Name.TryUpdateModelDirectly("Default"); @@ -110,8 +110,8 @@ protected void EnsureDefaultDeviceExists() protected void EnsureDefaultProfileExists() { - // If no "Default" profile exists, create one and add it to the beginning - if (!Profiles.TryGetElement("Default", out _)) + // If no profiles exist, create a default profile + if (Profiles.Elements.Count == 0) { var defaultProfile = ServiceProvider.GetRequiredService(); defaultProfile.Name.TryUpdateModelDirectly("Default"); @@ -121,8 +121,8 @@ protected void EnsureDefaultProfileExists() protected void EnsureDefaultMappingExists() { - // If no "Default" mapping exists, create one - if (!Mappings.TryGetMapping("Default", out _)) + // If no mappings exist, create a default mapping + if (Mappings.Mappings.Count == 0) { var defaultMapping = new DATA.Mapping { From 8075fea7a9c1f37d5f2289d29838bf51201fd623 Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 26 Oct 2025 18:17:17 -0400 Subject: [PATCH 47/47] Fix MaxNameLength to match driver constant Update MaxNameLength from 100 to 256 to match driver's MAX_NAME_LEN constant defined in common/rawaccel-base.hpp --- userspace-backend/BackEnd.cs | 1 - .../Model/EditableSettings/MaxNameLengthValidator.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/userspace-backend/BackEnd.cs b/userspace-backend/BackEnd.cs index 40243ef8..c8fbc197 100644 --- a/userspace-backend/BackEnd.cs +++ b/userspace-backend/BackEnd.cs @@ -57,7 +57,6 @@ public void Load() DATA.MappingSet mappingData = BackEndLoader.LoadMappings(); LoadMappingsFromData(mappingData); - // Ensure defaults exist for first-run experience EnsureDefaultDeviceGroupExists(); EnsureDefaultDeviceExists(); EnsureDefaultProfileExists(); diff --git a/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs b/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs index 332812b2..31011c6f 100644 --- a/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs +++ b/userspace-backend/Model/EditableSettings/MaxNameLengthValidator.cs @@ -2,7 +2,7 @@ namespace userspace_backend.Model.EditableSettings { public class MaxNameLengthValidator : IModelValueValidator { - public const int MaxNameLength = 100; + public const int MaxNameLength = 256; public MaxNameLengthValidator() {