From d4bd44b50491f5ba3cc3ac06e72c624c0d47a925 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 12 Oct 2022 21:20:39 +0200 Subject: [PATCH] Sidebar - Fixed removed categories not dissapearing Sidebar - Fixed exception when creating a category with an already in use name --- .../Profiles/ProfileCategoryEventArgs.cs | 19 +++ .../Models/Profile/ProfileCategory.cs | 4 +- .../Storage/Interfaces/IProfileService.cs | 96 ++++++++------- .../Services/Storage/ProfileService.cs | 45 +++++-- .../Ninject/Factories/IVMFactory.cs | 4 +- .../SidebarCategoryEditViewModel.cs | 10 +- .../Sidebar/SidebarCategoryViewModel.cs | 21 ++-- .../SidebarProfileConfigurationViewModel.cs | 13 +- .../Screens/Sidebar/SidebarView.axaml | 12 +- .../Screens/Sidebar/SidebarViewModel.cs | 113 ++++++++++-------- 10 files changed, 193 insertions(+), 144 deletions(-) create mode 100644 src/Artemis.Core/Events/Profiles/ProfileCategoryEventArgs.cs diff --git a/src/Artemis.Core/Events/Profiles/ProfileCategoryEventArgs.cs b/src/Artemis.Core/Events/Profiles/ProfileCategoryEventArgs.cs new file mode 100644 index 000000000..bc7e6652f --- /dev/null +++ b/src/Artemis.Core/Events/Profiles/ProfileCategoryEventArgs.cs @@ -0,0 +1,19 @@ +using System; + +namespace Artemis.Core; + +/// +/// Provides data for profile configuration events. +/// +public class ProfileCategoryEventArgs : EventArgs +{ + internal ProfileCategoryEventArgs(ProfileCategory profileCategory) + { + ProfileCategory = profileCategory; + } + + /// + /// Gets the profile category this event is related to + /// + public ProfileCategory ProfileCategory { get; } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/ProfileCategory.cs b/src/Artemis.Core/Models/Profile/ProfileCategory.cs index 3c3358fd7..603bfdf08 100644 --- a/src/Artemis.Core/Models/Profile/ProfileCategory.cs +++ b/src/Artemis.Core/Models/Profile/ProfileCategory.cs @@ -20,9 +20,11 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel /// Creates a new instance of the class /// /// The name of the category - internal ProfileCategory(string name) + /// The order of the category + internal ProfileCategory(string name, int order) { _name = name; + _order = order; Entity = new ProfileCategoryEntity(); ProfileConfigurations = new ReadOnlyCollection(_profileConfigurations); } diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs index 7c5e786d5..98e0bc322 100644 --- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs +++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs @@ -6,135 +6,135 @@ using SkiaSharp; namespace Artemis.Core.Services; /// -/// Provides access to profile storage and is responsible for activating default profiles +/// Provides access to profile storage and is responsible for activating default profiles. /// public interface IProfileService : IArtemisService { /// - /// Gets the JSON serializer settings used to import/export profiles + /// Gets the JSON serializer settings used to import/export profiles. /// public static JsonSerializerSettings ExportSettings { get; } = new() {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented}; /// - /// Gets a read only collection containing all the profile categories + /// Gets a read only collection containing all the profile categories. /// ReadOnlyCollection ProfileCategories { get; } /// - /// Gets a read only collection containing all the profile configurations + /// Gets a read only collection containing all the profile configurations. /// ReadOnlyCollection ProfileConfigurations { get; } /// - /// Gets or sets a boolean indicating whether hotkeys are enabled + /// Gets or sets a boolean indicating whether hotkeys are enabled. /// bool HotkeysEnabled { get; set; } /// - /// Gets or sets a boolean indicating whether rendering should only be done for profiles being edited + /// Gets or sets a boolean indicating whether rendering should only be done for profiles being edited. /// bool RenderForEditor { get; set; } /// - /// Gets or sets the profile element to focus on while rendering for the editor + /// Gets or sets the profile element to focus on while rendering for the editor. /// ProfileElement? EditorFocus { get; set; } /// - /// Activates the profile of the given with the currently active surface + /// Activates the profile of the given with the currently active surface. /// - /// The profile configuration of the profile to activate + /// The profile configuration of the profile to activate. Profile ActivateProfile(ProfileConfiguration profileConfiguration); /// - /// Deactivates the profile of the given with the currently active surface + /// Deactivates the profile of the given with the currently active surface. /// - /// The profile configuration of the profile to activate + /// The profile configuration of the profile to activate. void DeactivateProfile(ProfileConfiguration profileConfiguration); /// - /// Permanently deletes the profile of the given + /// Permanently deletes the profile of the given . /// - /// The profile configuration of the profile to delete + /// The profile configuration of the profile to delete. void DeleteProfile(ProfileConfiguration profileConfiguration); /// /// Saves the provided and it's s but not the - /// s themselves + /// s themselves. /// - /// The profile category to update + /// The profile category to update. void SaveProfileCategory(ProfileCategory profileCategory); /// - /// Creates a new profile category and saves it to persistent storage + /// Creates a new profile category and saves it to persistent storage. /// - /// The name of the new profile category, must be unique - /// The newly created profile category + /// The name of the new profile category, must be unique. + /// The newly created profile category. ProfileCategory CreateProfileCategory(string name); /// - /// Permanently deletes the provided profile category + /// Permanently deletes the provided profile category. /// void DeleteProfileCategory(ProfileCategory profileCategory); /// - /// Creates a new profile configuration and adds it to the provided + /// Creates a new profile configuration and adds it to the provided . /// - /// The profile category to add the profile to - /// The name of the new profile configuration - /// The icon of the new profile configuration - /// The newly created profile configuration + /// The profile category to add the profile to. + /// The name of the new profile configuration. + /// The icon of the new profile configuration. + /// The newly created profile configuration. ProfileConfiguration CreateProfileConfiguration(ProfileCategory category, string name, string icon); /// - /// Removes the provided profile configuration from the + /// Removes the provided profile configuration from the . /// /// void RemoveProfileConfiguration(ProfileConfiguration profileConfiguration); /// - /// Loads the icon of this profile configuration if needed and puts it into ProfileConfiguration.Icon.FileIcon + /// Loads the icon of this profile configuration if needed and puts it into ProfileConfiguration.Icon.FileIcon. /// void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration); /// - /// Saves the current icon of this profile + /// Saves the current icon of this profile. /// void SaveProfileConfigurationIcon(ProfileConfiguration profileConfiguration); /// - /// Writes the profile to persistent storage + /// Writes the profile to persistent storage. /// /// /// void SaveProfile(Profile profile, bool includeChildren); /// - /// Exports the profile described in the given into an export model + /// Exports the profile described in the given into an export model. /// - /// The profile configuration of the profile to export - /// The resulting export model + /// The profile configuration of the profile to export. + /// The resulting export model. ProfileConfigurationExportModel ExportProfile(ProfileConfiguration profileConfiguration); /// - /// Imports the provided base64 encoded GZIPed JSON as a profile configuration + /// Imports the provided base64 encoded GZIPed JSON as a profile configuration. /// - /// The in which to import the profile - /// The model containing the profile to import - /// Whether or not to give the profile a new GUID, making it unique + /// The in which to import the profile. + /// The model containing the profile to import. + /// Whether or not to give the profile a new GUID, making it unique. /// /// Whether or not to mark the profile as a fresh import, causing it to be adapted until - /// any changes are made to it + /// any changes are made to it. /// - /// Text to add after the name of the profile (separated by a dash) - /// The resulting profile configuration + /// Text to add after the name of the profile (separated by a dash). + /// The resulting profile configuration. ProfileConfiguration ImportProfile(ProfileCategory category, ProfileConfigurationExportModel exportModel, bool makeUnique = true, bool markAsFreshImport = true, string? nameAffix = "imported"); /// - /// Adapts a given profile to the currently active devices + /// Adapts a given profile to the currently active devices. /// - /// The profile to adapt + /// The profile to adapt. void AdaptProfile(Profile profile); /// @@ -143,18 +143,28 @@ public interface IProfileService : IArtemisService void UpdateProfiles(double deltaTime); /// - /// Renders all currently active profiles + /// Renders all currently active profiles. /// /// void RenderProfiles(SKCanvas canvas); /// - /// Occurs whenever a profile has been activated + /// Occurs whenever a profile has been activated. /// public event EventHandler? ProfileActivated; /// - /// Occurs whenever a profile has been deactivated + /// Occurs whenever a profile has been deactivated. /// public event EventHandler? ProfileDeactivated; + + /// + /// Occurs whenever a profile category is added. + /// + public event EventHandler? ProfileCategoryAdded; + + /// + /// Occurs whenever a profile category is removed. + /// + public event EventHandler? ProfileCategoryRemoved; } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 16a2e0341..d5adc7d87 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -53,16 +53,6 @@ internal class ProfileService : IProfileService UpdateModules(); } - protected virtual void OnProfileActivated(ProfileConfigurationEventArgs e) - { - ProfileActivated?.Invoke(this, e); - } - - protected virtual void OnProfileDeactivated(ProfileConfigurationEventArgs e) - { - ProfileDeactivated?.Invoke(this, e); - } - private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e) { if (!HotkeysEnabled) @@ -386,13 +376,16 @@ internal class ProfileService : IProfileService public ProfileCategory CreateProfileCategory(string name) { + ProfileCategory profileCategory; lock (_profileRepository) { - ProfileCategory profileCategory = new(name); + profileCategory = new ProfileCategory(name, _profileCategories.Count + 1); _profileCategories.Add(profileCategory); SaveProfileCategory(profileCategory); - return profileCategory; } + + OnProfileCategoryAdded(new ProfileCategoryEventArgs(profileCategory)); + return profileCategory; } public void DeleteProfileCategory(ProfileCategory profileCategory) @@ -406,6 +399,8 @@ internal class ProfileService : IProfileService _profileCategories.Remove(profileCategory); _profileCategoryRepository.Remove(profileCategory.Entity); } + + OnProfileCategoryRemoved(new ProfileCategoryEventArgs(profileCategory)); } public ProfileConfiguration CreateProfileConfiguration(ProfileCategory category, string name, string icon) @@ -543,6 +538,32 @@ internal class ProfileService : IProfileService _profileRepository.Save(profile.ProfileEntity); } + #region Events + public event EventHandler? ProfileActivated; public event EventHandler? ProfileDeactivated; + public event EventHandler? ProfileCategoryAdded; + public event EventHandler? ProfileCategoryRemoved; + + protected virtual void OnProfileActivated(ProfileConfigurationEventArgs e) + { + ProfileActivated?.Invoke(this, e); + } + + protected virtual void OnProfileDeactivated(ProfileConfigurationEventArgs e) + { + ProfileDeactivated?.Invoke(this, e); + } + + protected virtual void OnProfileCategoryAdded(ProfileCategoryEventArgs e) + { + ProfileCategoryAdded?.Invoke(this, e); + } + + protected virtual void OnProfileCategoryRemoved(ProfileCategoryEventArgs e) + { + ProfileCategoryRemoved?.Invoke(this, e); + } + + #endregion } \ No newline at end of file diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index da4b16196..06d0b3dfa 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -50,8 +50,8 @@ public interface ISettingsVmFactory : IVmFactory public interface ISidebarVmFactory : IVmFactory { SidebarViewModel? SidebarViewModel(IScreen hostScreen); - SidebarCategoryViewModel SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory); - SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration); + SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory); + SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration); } public interface ISurfaceVmFactory : IVmFactory diff --git a/src/Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditViewModel.cs b/src/Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditViewModel.cs index f08a25c5e..66d186faf 100644 --- a/src/Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditViewModel.cs @@ -1,4 +1,5 @@ -using System.Reactive; +using System.Linq; +using System.Reactive; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Shared; @@ -23,7 +24,8 @@ public class SidebarCategoryEditViewModel : ContentDialogViewModelBase _categoryName = _category.Name; Confirm = ReactiveCommand.Create(ExecuteConfirm, ValidationContext.Valid); - this.ValidationRule(vm => vm.CategoryName, categoryName => !string.IsNullOrWhiteSpace(categoryName), "You must specify a valid name"); + this.ValidationRule(vm => vm.CategoryName, categoryName => !string.IsNullOrWhiteSpace(categoryName?.Trim()), "You must specify a valid name"); + this.ValidationRule(vm => vm.CategoryName, categoryName => profileService.ProfileCategories.All(c => c.Name != categoryName?.Trim()), "You must specify a unique name"); } public string? CategoryName @@ -38,12 +40,12 @@ public class SidebarCategoryEditViewModel : ContentDialogViewModelBase { if (_category != null) { - _category.Name = CategoryName!; + _category.Name = CategoryName!.Trim(); _profileService.SaveProfileCategory(_category); } else { - _profileService.CreateProfileCategory(CategoryName!); + _profileService.CreateProfileCategory(CategoryName!.Trim()); } ContentDialog?.Hide(ContentDialogResult.Primary); diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs index 101ed0908..8da48b281 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs @@ -24,17 +24,18 @@ namespace Artemis.UI.Screens.Sidebar; public class SidebarCategoryViewModel : ActivatableViewModelBase { private readonly IProfileService _profileService; - private readonly SidebarViewModel _sidebarViewModel; private readonly ISidebarVmFactory _vmFactory; private readonly IWindowService _windowService; private ObservableAsPropertyHelper? _isCollapsed; private ObservableAsPropertyHelper? _isSuspended; private SidebarProfileConfigurationViewModel? _selectedProfileConfiguration; - public SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory, IProfileService profileService, IWindowService windowService, - IProfileEditorService profileEditorService, ISidebarVmFactory vmFactory) + public SidebarCategoryViewModel(ProfileCategory profileCategory, + IProfileService profileService, + IWindowService windowService, + IProfileEditorService profileEditorService, + ISidebarVmFactory vmFactory) { - _sidebarViewModel = sidebarViewModel; _profileService = profileService; _windowService = windowService; _vmFactory = vmFactory; @@ -47,7 +48,7 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase profileConfigurations.Connect() .Filter(profileConfigurationsFilter) .Sort(SortExpressionComparer.Ascending(c => c.Order)) - .Transform(c => _vmFactory.SidebarProfileConfigurationViewModel(_sidebarViewModel, c)) + .Transform(c => _vmFactory.SidebarProfileConfigurationViewModel(c)) .Bind(out ReadOnlyObservableCollection profileConfigurationViewModels) .Subscribe(); ProfileConfigurations = profileConfigurationViewModels; @@ -93,7 +94,7 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase _windowService.ShowExceptionDialog(s.ProfileConfiguration.BrokenState, s.ProfileConfiguration.BrokenStateException); else _windowService.ShowExceptionDialog(e.Message, e); - + profileEditorService.ChangeCurrentProfileConfiguration(null); SelectedProfileConfiguration = null; } @@ -148,8 +149,6 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase .WithCloseButtonText("Cancel") .WithDefaultButton(ContentDialogButton.Primary) .ShowAsync(); - - _sidebarViewModel.UpdateProfileCategories(); } private async Task ExecuteDeleteCategory() @@ -166,7 +165,7 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase ); if (result != null) { - SidebarProfileConfigurationViewModel viewModel = _vmFactory.SidebarProfileConfigurationViewModel(_sidebarViewModel, result); + SidebarProfileConfigurationViewModel viewModel = _vmFactory.SidebarProfileConfigurationViewModel(result); SelectedProfileConfiguration = viewModel; } } @@ -230,8 +229,6 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase ProfileCategory.Order--; _profileService.SaveProfileCategory(categories[index - 1]); _profileService.SaveProfileCategory(ProfileCategory); - - _sidebarViewModel.UpdateProfileCategories(); } private void ExecuteMoveDown() @@ -245,7 +242,5 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase ProfileCategory.Order++; _profileService.SaveProfileCategory(categories[index + 1]); _profileService.SaveProfileCategory(ProfileCategory); - - _sidebarViewModel.UpdateProfileCategories(); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs index 57c743f1d..8f21f7eca 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs @@ -19,17 +19,11 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase { private readonly IProfileEditorService _profileEditorService; private readonly IProfileService _profileService; - private readonly SidebarViewModel _sidebarViewModel; private readonly IWindowService _windowService; private ObservableAsPropertyHelper? _isDisabled; - public SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, - ProfileConfiguration profileConfiguration, - IProfileService profileService, - IProfileEditorService profileEditorService, - IWindowService windowService) + public SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration, IProfileService profileService, IProfileEditorService profileEditorService, IWindowService windowService) { - _sidebarViewModel = sidebarViewModel; _profileService = profileService; _profileEditorService = profileEditorService; _windowService = windowService; @@ -63,13 +57,10 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase private async Task ExecuteEditProfile() { - ProfileConfiguration? edited = await _windowService.ShowDialogAsync( + await _windowService.ShowDialogAsync( ("profileCategory", ProfileConfiguration.Category), ("profileConfiguration", ProfileConfiguration) ); - - if (edited != null) - _sidebarViewModel.UpdateProfileCategories(); } private void ExecuteToggleSuspended() diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarView.axaml b/src/Artemis.UI/Screens/Sidebar/SidebarView.axaml index 5c617be53..d20036165 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarView.axaml +++ b/src/Artemis.UI/Screens/Sidebar/SidebarView.axaml @@ -4,8 +4,10 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:sidebar="clr-namespace:Artemis.UI.Screens.Sidebar" mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="450" - x:Class="Artemis.UI.Screens.Sidebar.SidebarView"> + x:Class="Artemis.UI.Screens.Sidebar.SidebarView" + x:DataType="sidebar:SidebarViewModel"> @@ -24,18 +26,18 @@ + Items="{CompiledBinding SidebarScreens}" + SelectedItem="{CompiledBinding SelectedSidebarScreen}" /> - +