diff --git a/README.md b/README.md index 3888dc4d7..1bfda7a2c 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ [![Discord](https://img.shields.io/discord/392093058352676874?logo=discord&logoColor=white)](https://discord.gg/S3MVaC9) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VQBAEJYUFLU4J) -Artemis 2 adds highly configurable support for several games to a range of RGB keyboards, mice and headsets. -Artemis 1 is no longer supported and Artemis 2 is in active development. This entire readme and all websites/documents refer to Artemis 2. +Artemis adds highly configurable support for several games to a range of RGB keyboards, mice and headsets. ### Check out our [Wiki](https://wiki.artemis-rgb.com) and more specifically, the [getting started guide](https://wiki.artemis-rgb.com/en/guides/user). **Pre-release download**: https://github.com/SpoinkyNL/Artemis/releases (pre-release means your profiles may break at any given time!) @@ -31,23 +30,17 @@ dotnet build .\Artemis\src\Artemis.sln dotnet build .\Artemis.Plugins\src\Artemis.Plugins.sln ``` -For an up-to-date overview of what's currently being worked on, see the [Projects](https://github.com/SpoinkyNL/Artemis/projects) page +For an up-to-date overview of what's currently being worked on, see the [Projects](https://github.com/Artemis-RGB/Artemis/projects?type=classic) page ## Plugin development While Artemis 2 is still in development, the plugin API is pretty far along. -To get started, you can download the Visual Studio extension in the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=SpoinkyNL.ArtemisTemplates). - -This extension provides you with interactive template projects for each type of Artemis 2 plugin. -To use the templates you must have Artemis 2 extracted somewhere on your drive. The plugin wizard will ask you to locate the exectuable in order to setup all the required project references for you. +To get started, you can download the .NET templates, check out the [Artemis.Plugins.Templates](https://github.com/Artemis-RGB/Artemis.Plugins.Templates) repository. Due to the volatine nature of the project right now, there is no documentation yet. The templates provide some commentary to get you going and feel free to ask for more help in Discord. ### Third party plugins -A few people have already started working on plugins! If you want your plugins to be added to this list feel free to PR or ask on Discord. -- https://github.com/diogotr7/Artemis.Plugins -- https://github.com/Wibble199/Artemis.Plugins -- https://github.com/F-Lehmann/MyArtemisPlugins -- https://github.com/Cheerpipe/Artemis.Plugins.Public +A few people have already started working on plugins! We're keeping a list on our wiki +https://wiki.artemis-rgb.com/en/guides/user/plugins ## Work in progress screenshots **Note:** Video tutorials and written guides on many of the features displayed below are planned for when Artemis 2 nears feature-completion. @@ -55,7 +48,8 @@ A few people have already started working on plugins! If you want your plugins t ![Surface editor](https://wiki.artemis-rgb.com/screenshots/surface-editor.png) _The surface editor allows you to recreate your desktop in 2D space, this provides Artemis with spatial awareness and ensures effects scale properly over your different devices. Right clicking a device lets you change its properties such as rotation and scale._ -![Keyframes](http://artemis-rgb.com/github/sSEvdAXeTQ.gif) +[profile-editor.webm](https://user-images.githubusercontent.com/8858506/194389752-7cf1b69a-8ee5-478a-a404-0ef08532c28e.webm) + _With the keyframe engine you can animate almost any property of the layer. In the example above the position and scale of the shape have been animated using keyframes._ For more screenshots check out the wiki: https://wiki.artemis-rgb.com/en/screenshots @@ -63,5 +57,4 @@ For more screenshots check out the wiki: https://wiki.artemis-rgb.com/en/screens ### Special thanks Over the years several companies have supported Artemis by providing both hardware and software, thank you! [![Corsair](https://i.imgur.com/UKUdDOy.png)](https://www.corsair.com/) -[![JetBrains](https://i.imgur.com/JYfXjjB.png)](https://www.jetbrains.com/?from=ArtemisRGB) [![Wooting](https://i.imgur.com/Zh3bVza.png)](https://wooting.io/) diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 4a3d0f3b1..9a766c559 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -35,7 +35,7 @@ - + @@ -56,6 +56,7 @@ + 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.Windows/ApplicationStateManager.cs b/src/Artemis.UI.Windows/ApplicationStateManager.cs index c08f42316..1630c95ef 100644 --- a/src/Artemis.UI.Windows/ApplicationStateManager.cs +++ b/src/Artemis.UI.Windows/ApplicationStateManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -16,6 +16,8 @@ namespace Artemis.UI.Windows; public class ApplicationStateManager { + private const int SM_SHUTTINGDOWN = 0x2000; + public ApplicationStateManager(IKernel kernel, string[] startupArguments) { StartupArguments = startupArguments; @@ -100,7 +102,8 @@ public class ApplicationStateManager private void RunForcedShutdownIfEnabled() { - if (StartupArguments.Contains("--disable-forced-shutdown")) + // Don't run a forced shutdown if Windows itself is shutting down, the new PowerShell process will fail + if (GetSystemMetrics(SM_SHUTTINGDOWN) != 0 || StartupArguments.Contains("--disable-forced-shutdown")) return; ProcessStartInfo info = new() @@ -112,4 +115,7 @@ public class ApplicationStateManager }; Process.Start(info); } + + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern int GetSystemMetrics(int nIndex); } \ No newline at end of file diff --git a/src/Artemis.UI.Windows/Assets/autorun.xml b/src/Artemis.UI.Windows/Assets/autorun.xml index e31980c8b..bbe5261ad 100644 Binary files a/src/Artemis.UI.Windows/Assets/autorun.xml and b/src/Artemis.UI.Windows/Assets/autorun.xml differ diff --git a/src/Artemis.UI.Windows/Providers/AutoRunProvider.cs b/src/Artemis.UI.Windows/Providers/AutoRunProvider.cs index 9cc27d35f..40c2600a4 100644 --- a/src/Artemis.UI.Windows/Providers/AutoRunProvider.cs +++ b/src/Artemis.UI.Windows/Providers/AutoRunProvider.cs @@ -14,6 +14,8 @@ namespace Artemis.UI.Windows.Providers; public class AutoRunProvider : IAutoRunProvider { + private readonly string _autorunName = $"Artemis 2 autorun {Environment.UserName}"; + private readonly string _oldAutorunName = "Artemis 2 autorun"; private readonly IAssetLoader _assetLoader; public AutoRunProvider(IAssetLoader assetLoader) @@ -21,7 +23,7 @@ public class AutoRunProvider : IAutoRunProvider _assetLoader = assetLoader; } - private async Task IsAutoRunTaskCreated() + private async Task IsAutoRunTaskCreated(string autorunName) { Process schtasks = new() { @@ -30,7 +32,7 @@ public class AutoRunProvider : IAutoRunProvider WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = true, FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"), - Arguments = "/TN \"Artemis 2 autorun\"" + Arguments = $"/TN \"{autorunName}\"" } }; @@ -39,7 +41,7 @@ public class AutoRunProvider : IAutoRunProvider return schtasks.ExitCode == 0; } - private async Task CreateAutoRunTask(TimeSpan autoRunDelay) + private async Task CreateAutoRunTask(TimeSpan autoRunDelay, string autorunName) { await using Stream taskFile = _assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/autorun.xml")); @@ -53,6 +55,8 @@ public class AutoRunProvider : IAutoRunProvider task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "Delay") .SetValue(autoRunDelay); + task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "UserId") + .SetValue(WindowsIdentity.GetCurrent().Name); task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId") .SetValue(WindowsIdentity.GetCurrent().User!.Value); @@ -76,7 +80,7 @@ public class AutoRunProvider : IAutoRunProvider UseShellExecute = true, Verb = "runas", FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"), - Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\" /F" + Arguments = $"/Create /XML \"{xmlPath}\" /tn \"{autorunName}\" /F" } }; @@ -86,7 +90,7 @@ public class AutoRunProvider : IAutoRunProvider File.Delete(xmlPath); } - private async Task RemoveAutoRunTask() + private async Task RemoveAutoRunTask(string autorunName) { Process schtasks = new() { @@ -96,7 +100,7 @@ public class AutoRunProvider : IAutoRunProvider UseShellExecute = true, Verb = "runas", FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"), - Arguments = "/Delete /TN \"Artemis 2 autorun\" /f" + Arguments = $"/Delete /TN \"{autorunName}\" /f" } }; @@ -107,22 +111,31 @@ public class AutoRunProvider : IAutoRunProvider /// public async Task EnableAutoRun(bool recreate, int autoRunDelay) { - // if (Constants.BuildInfo.IsLocalBuild) - // return; + if (Constants.BuildInfo.IsLocalBuild) + return; + + await CleanupOldAutorun(); // Create or remove the task if necessary bool taskCreated = false; if (!recreate) - taskCreated = await IsAutoRunTaskCreated(); + taskCreated = await IsAutoRunTaskCreated(_autorunName); if (!taskCreated) - await CreateAutoRunTask(TimeSpan.FromSeconds(autoRunDelay)); + await CreateAutoRunTask(TimeSpan.FromSeconds(autoRunDelay), _autorunName); } /// public async Task DisableAutoRun() { - bool taskCreated = await IsAutoRunTaskCreated(); + bool taskCreated = await IsAutoRunTaskCreated(_autorunName); if (taskCreated) - await RemoveAutoRunTask(); + await RemoveAutoRunTask(_autorunName); + } + + private async Task CleanupOldAutorun() + { + bool taskCreated = await IsAutoRunTaskCreated(_oldAutorunName); + if (taskCreated) + await RemoveAutoRunTask(_oldAutorunName); } } \ 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}" /> - +