diff --git a/src/Artemis.Core/Models/Profile/ProfileCategory.cs b/src/Artemis.Core/Models/Profile/ProfileCategory.cs index a15a63e91..f2e9cac5a 100644 --- a/src/Artemis.Core/Models/Profile/ProfileCategory.cs +++ b/src/Artemis.Core/Models/Profile/ProfileCategory.cs @@ -203,45 +203,4 @@ namespace Artemis.Core /// General } - - /// - /// Represents a type of behaviour when this profile is activated - /// - public enum ActivationBehaviour - { - /// - /// Do nothing to other profiles - /// - None, - - /// - /// Disable all other profiles - /// - DisableOthers, - - /// - /// Disable all other profiles below this one - /// - DisableOthersBelow, - - /// - /// Disable all other profiles above this one - /// - DisableOthersAbove, - - /// - /// Disable all other profiles in the same category - /// - DisableOthersInCategory, - - /// - /// Disable all other profiles below this one in the same category - /// - DisableOthersBelowInCategory, - - /// - /// Disable all other profiles above this one in the same category - /// - DisableOthersAboveInCategory - } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs index f70ef6e9c..22b15e662 100644 --- a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs +++ b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs @@ -92,6 +92,21 @@ namespace Artemis.Core /// public ProfileConfigurationIcon Icon { get; } + /// + /// Gets or sets the used to determine hotkey behaviour + /// + public ProfileConfigurationHotkeyMode HotkeyMode { get; set; } + + /// + /// Gets or sets the hotkey used to enable or toggle the profile + /// + public ProfileConfigurationHotkey? EnableHotkey { get; set; } + + /// + /// Gets or sets the hotkey used to disable the profile + /// + public ProfileConfigurationHotkey? DisableHotkey { get; set; } + /// /// Gets the profile of this profile configuration /// @@ -151,6 +166,10 @@ namespace Artemis.Core ActivationConditionMet = ActivationCondition == null || ActivationCondition.Evaluate(); } + /// + /// Determines whether the profile of this configuration should be active + /// + /// Whether or not to take activation conditions into consideration public bool ShouldBeActive(bool includeActivationCondition) { if (_disposed) @@ -201,13 +220,15 @@ namespace Artemis.Core Name = Entity.Name; IsSuspended = Entity.IsSuspended; ActivationBehaviour = (ActivationBehaviour) Entity.ActivationBehaviour; + HotkeyMode = (ProfileConfigurationHotkeyMode) Entity.HotkeyMode; Order = Entity.Order; Icon.Load(); - ActivationCondition = Entity.ActivationCondition != null - ? new DataModelConditionGroup(null, Entity.ActivationCondition) - : null; + ActivationCondition = Entity.ActivationCondition != null ? new DataModelConditionGroup(null, Entity.ActivationCondition) : null; + + EnableHotkey = Entity.EnableHotkey != null ? new ProfileConfigurationHotkey(Entity.EnableHotkey) : null; + DisableHotkey = Entity.DisableHotkey != null ? new ProfileConfigurationHotkey(Entity.DisableHotkey) : null; } /// @@ -219,18 +240,19 @@ namespace Artemis.Core Entity.Name = Name; Entity.IsSuspended = IsSuspended; Entity.ActivationBehaviour = (int) ActivationBehaviour; + Entity.HotkeyMode = (int) HotkeyMode; Entity.ProfileCategoryId = Category.Entity.Id; Entity.Order = Order; Icon.Save(); - if (ActivationCondition != null) - { - ActivationCondition.Save(); - Entity.ActivationCondition = ActivationCondition.Entity; - } - else - Entity.ActivationCondition = null; + ActivationCondition?.Save(); + Entity.ActivationCondition = ActivationCondition?.Entity; + + EnableHotkey?.Save(); + Entity.EnableHotkey = EnableHotkey?.Entity; + DisableHotkey?.Save(); + Entity.DisableHotkey = DisableHotkey?.Entity; if (!IsMissingModule) Entity.ModuleId = Module?.Id; @@ -238,4 +260,66 @@ namespace Artemis.Core #endregion } + + /// + /// Represents a type of behaviour when this profile is activated + /// + public enum ActivationBehaviour + { + /// + /// Do nothing to other profiles + /// + None, + + /// + /// Disable all other profiles + /// + DisableOthers, + + /// + /// Disable all other profiles below this one + /// + DisableOthersBelow, + + /// + /// Disable all other profiles above this one + /// + DisableOthersAbove, + + /// + /// Disable all other profiles in the same category + /// + DisableOthersInCategory, + + /// + /// Disable all other profiles below this one in the same category + /// + DisableOthersBelowInCategory, + + /// + /// Disable all other profiles above this one in the same category + /// + DisableOthersAboveInCategory + } + + /// + /// Represents a hotkey mode for a profile configuration + /// + public enum ProfileConfigurationHotkeyMode + { + /// + /// Use no hotkeys + /// + None, + + /// + /// Toggle the profile with one hotkey + /// + Toggle, + + /// + /// Enable and disable the profile with two separate hotkeys + /// + EnableDisable + } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfigurationHotkey.cs b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfigurationHotkey.cs new file mode 100644 index 000000000..0a735f875 --- /dev/null +++ b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfigurationHotkey.cs @@ -0,0 +1,65 @@ +using Artemis.Core.Services; +using Artemis.Storage.Entities.Profile; + +namespace Artemis.Core +{ + /// + /// Represents a key or combination of keys that changes the suspension status of a + /// + public class ProfileConfigurationHotkey : CorePropertyChanged, IStorageModel + { + + /// + /// Creates a new instance of + /// + public ProfileConfigurationHotkey() + { + Entity = new ProfileConfigurationHotkeyEntity(); + } + + internal ProfileConfigurationHotkey(ProfileConfigurationHotkeyEntity entity) + { + Entity = entity; + Load(); + } + + internal ProfileConfigurationHotkeyEntity Entity { get; } + + /// + /// Gets or sets the of the hotkey + /// + public KeyboardKey? Key { get; set; } + + /// + /// Gets or sets the s of the hotkey + /// + public KeyboardModifierKey? Modifiers { get; set; } + + /// + /// Determines whether the provided match the hotkey + /// + /// if the event args match the hotkey; otherwise + public bool MatchesEventArgs(ArtemisKeyboardKeyEventArgs eventArgs) + { + return eventArgs.Key == Key && eventArgs.Modifiers == Modifiers; + } + + #region Implementation of IStorageModel + + /// + public void Load() + { + Key = (KeyboardKey?) Entity.Key; + Modifiers = (KeyboardModifierKey?) Entity.Modifiers; + } + + /// + public void Save() + { + Entity.Key = (int?) Key; + Entity.Modifiers = (int?) Modifiers; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 027ac09af..00a74d2a5 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -537,15 +537,13 @@ namespace Artemis.Core.Services string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, ""); foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries) { - if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory)) + if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory) && !zipArchiveEntry.FullName.EndsWith("/")) { string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length)); // Create folders - if (zipArchiveEntry.FullName.EndsWith("/")) - Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(target)!); + Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(target)!); // Extract files - else - zipArchiveEntry.ExtractToFile(target); + zipArchiveEntry.ExtractToFile(target); } } diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs index 84fa8ebae..cdf7a8d52 100644 --- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs +++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs @@ -10,14 +10,14 @@ namespace Artemis.Core.Services public interface IProfileService : IArtemisService { /// - /// Gets the JSON serializer settings used to create profile mementos + /// Gets the JSON serializer settings used to create profile mementos /// public static JsonSerializerSettings MementoSettings { get; } = new() {TypeNameHandling = TypeNameHandling.All}; /// - /// 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}; + public static JsonSerializerSettings ExportSettings { get; } = new() {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented}; /// /// Gets a read only collection containing all the profile categories @@ -29,6 +29,11 @@ namespace Artemis.Core.Services /// ReadOnlyCollection ProfileConfigurations { get; } + /// + /// 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 /// @@ -128,10 +133,14 @@ namespace Artemis.Core.Services /// 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 + /// + /// Whether or not to mark the profile as a fresh import, causing it to be adapted until + /// any changes are made to it + /// /// 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"); + ProfileConfiguration ImportProfile(ProfileCategory category, ProfileConfigurationExportModel exportModel, bool makeUnique = true, bool markAsFreshImport = true, + string? nameAffix = "imported"); /// /// Adapts a given profile to the currently active devices diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index b3fb0c267..453e2c6e5 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -15,16 +15,17 @@ namespace Artemis.Core.Services internal class ProfileService : IProfileService { private readonly ILogger _logger; + + private readonly List _pendingKeyboardEvents = new(); private readonly IPluginManagementService _pluginManagementService; private readonly List _profileCategories; private readonly IProfileCategoryRepository _profileCategoryRepository; private readonly IProfileRepository _profileRepository; - private readonly IRgbService _rgbService; - - private readonly List _updateExceptions = new(); - private DateTime _lastUpdateExceptionLog; private readonly List _renderExceptions = new(); + private readonly IRgbService _rgbService; + private readonly List _updateExceptions = new(); private DateTime _lastRenderExceptionLog; + private DateTime _lastUpdateExceptionLog; public ProfileService(ILogger logger, IRgbService rgbService, @@ -33,6 +34,7 @@ namespace Artemis.Core.Services IDataBindingService dataBindingService, IProfileCategoryRepository profileCategoryRepository, IPluginManagementService pluginManagementService, + IInputService inputService, IProfileRepository profileRepository) { _logger = logger; @@ -46,10 +48,23 @@ namespace Artemis.Core.Services _pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled; _pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled; + inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp; + if (!_profileCategories.Any()) CreateDefaultProfileCategories(); } + private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e) + { + if (!HotkeysEnabled) + return; + + lock (_profileCategories) + { + _pendingKeyboardEvents.Add(e); + } + } + /// /// Populates all missing LEDs on all currently active profiles /// @@ -88,84 +103,25 @@ namespace Artemis.Core.Services UpdateModules(); } - public bool RenderForEditor { get; set; } - - public void UpdateProfiles(double deltaTime) + private void ProcessPendingKeyEvents(ProfileConfiguration profileConfiguration) { - lock (_profileCategories) + if (profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.None) + return; + + foreach (ArtemisKeyboardKeyEventArgs e in _pendingKeyboardEvents) { - // Iterate the children in reverse because the first category must be rendered last to end up on top - for (int i = _profileCategories.Count - 1; i > -1; i--) + if (profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.Toggle) { - ProfileCategory profileCategory = _profileCategories[i]; - for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--) - { - ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; - // Profiles being edited are updated at their own leisure - if (profileConfiguration.IsBeingEdited) - continue; - - bool shouldBeActive = profileConfiguration.ShouldBeActive(false); - if (shouldBeActive) - { - profileConfiguration.Update(); - shouldBeActive = profileConfiguration.ActivationConditionMet; - } - - try - { - // Make sure the profile is active or inactive according to the parameters above - if (shouldBeActive && profileConfiguration.Profile == null) - ActivateProfile(profileConfiguration); - else if (!shouldBeActive && profileConfiguration.Profile != null) - DeactivateProfile(profileConfiguration); - - profileConfiguration.Profile?.Update(deltaTime); - } - catch (Exception e) - { - _updateExceptions.Add(e); - } - } + if (profileConfiguration.EnableHotkey != null && profileConfiguration.EnableHotkey.MatchesEventArgs(e)) + profileConfiguration.IsSuspended = !profileConfiguration.IsSuspended; } - - LogProfileUpdateExceptions(); - } - } - - public void RenderProfiles(SKCanvas canvas) - { - lock (_profileCategories) - { - // Iterate the children in reverse because the first category must be rendered last to end up on top - for (int i = _profileCategories.Count - 1; i > -1; i--) + else { - ProfileCategory profileCategory = _profileCategories[i]; - for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--) - { - try - { - ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; - if (RenderForEditor) - { - if (profileConfiguration.IsBeingEdited) - profileConfiguration.Profile?.Render(canvas, SKPointI.Empty); - } - else - { - // Ensure all criteria are met before rendering - if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && profileConfiguration.ActivationConditionMet) - profileConfiguration.Profile?.Render(canvas, SKPointI.Empty); - } - } - catch (Exception e) - { - _renderExceptions.Add(e); - } - } + if (profileConfiguration.IsSuspended && profileConfiguration.EnableHotkey != null && profileConfiguration.EnableHotkey.MatchesEventArgs(e)) + profileConfiguration.IsSuspended = false; + if (!profileConfiguration.IsSuspended && profileConfiguration.DisableHotkey != null && profileConfiguration.DisableHotkey.MatchesEventArgs(e)) + profileConfiguration.IsSuspended = true; } - - LogProfileRenderExceptions(); } } @@ -211,6 +167,93 @@ namespace Artemis.Core.Services _renderExceptions.Clear(); } + public bool HotkeysEnabled { get; set; } + public bool RenderForEditor { get; set; } + + public void UpdateProfiles(double deltaTime) + { + lock (_profileCategories) + { + // Iterate the children in reverse because the first category must be rendered last to end up on top + for (int i = _profileCategories.Count - 1; i > -1; i--) + { + ProfileCategory profileCategory = _profileCategories[i]; + for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--) + { + ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; + + // Process hotkeys that where pressed since this profile last updated + ProcessPendingKeyEvents(profileConfiguration); + + // Profiles being edited are updated at their own leisure + if (profileConfiguration.IsBeingEdited) + continue; + + bool shouldBeActive = profileConfiguration.ShouldBeActive(false); + if (shouldBeActive) + { + profileConfiguration.Update(); + shouldBeActive = profileConfiguration.ActivationConditionMet; + } + + try + { + // Make sure the profile is active or inactive according to the parameters above + if (shouldBeActive && profileConfiguration.Profile == null) + ActivateProfile(profileConfiguration); + else if (!shouldBeActive && profileConfiguration.Profile != null) + DeactivateProfile(profileConfiguration); + + profileConfiguration.Profile?.Update(deltaTime); + } + catch (Exception e) + { + _updateExceptions.Add(e); + } + } + } + + LogProfileUpdateExceptions(); + _pendingKeyboardEvents.Clear(); + } + } + + public void RenderProfiles(SKCanvas canvas) + { + lock (_profileCategories) + { + // Iterate the children in reverse because the first category must be rendered last to end up on top + for (int i = _profileCategories.Count - 1; i > -1; i--) + { + ProfileCategory profileCategory = _profileCategories[i]; + for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--) + { + try + { + ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; + if (RenderForEditor) + { + if (profileConfiguration.IsBeingEdited) + profileConfiguration.Profile?.Render(canvas, SKPointI.Empty); + } + else + { + // Ensure all criteria are met before rendering + if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && profileConfiguration.ActivationConditionMet) + profileConfiguration.Profile?.Render(canvas, SKPointI.Empty); + } + } + catch (Exception e) + { + _renderExceptions.Add(e); + } + } + } + + LogProfileRenderExceptions(); + } + } + public ReadOnlyCollection ProfileCategories { get @@ -485,7 +528,8 @@ namespace Artemis.Core.Services if (markAsFreshImport) profileEntity.IsFreshImport = true; - _profileRepository.Add(profileEntity); + if (!makeUnique && _profileRepository.Get(profileEntity.Id) == null) + _profileRepository.Add(profileEntity); ProfileConfiguration profileConfiguration; if (exportModel.ProfileConfigurationEntity != null) @@ -500,9 +544,7 @@ namespace Artemis.Core.Services profileConfiguration.Name = $"{profileConfiguration.Name} - {nameAffix}"; } else - { profileConfiguration = new ProfileConfiguration(category, profileEntity.Name, "Import"); - } if (exportModel.ProfileImage != null) { diff --git a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs index 32c92e15f..8b9f08dd8 100644 --- a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs @@ -15,6 +15,10 @@ namespace Artemis.Storage.Entities.Profile public int ActivationBehaviour { get; set; } public DataModelConditionGroupEntity ActivationCondition { get; set; } + public int HotkeyMode { get; set; } + public ProfileConfigurationHotkeyEntity EnableHotkey { get; set; } + public ProfileConfigurationHotkeyEntity DisableHotkey { get; set; } + public string ModuleId { get; set; } public Guid ProfileCategoryId { get; set; } diff --git a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationHotkeyEntity.cs b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationHotkeyEntity.cs new file mode 100644 index 000000000..f99cecfb2 --- /dev/null +++ b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationHotkeyEntity.cs @@ -0,0 +1,8 @@ +namespace Artemis.Storage.Entities.Profile +{ + public class ProfileConfigurationHotkeyEntity + { + public int? Key { get; set; } + public int? Modifiers { get; set; } + } +} \ 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 1bf08637d..e63671e50 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -106,6 +106,7 @@ namespace Artemis.UI.Ninject.Factories { SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory); SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration); + ProfileConfigurationHotkeyViewModel ProfileConfigurationHotkeyViewModel(ProfileConfiguration profileConfiguration, bool isDisableHotkey); ModuleActivationRequirementViewModel ModuleActivationRequirementViewModel(IModuleActivationRequirement activationRequirement); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/Predicate/DataModelConditionEventPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/Predicate/DataModelConditionEventPredicateViewModel.cs index 90fb5702c..baadf2a02 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/Predicate/DataModelConditionEventPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/Predicate/DataModelConditionEventPredicateViewModel.cs @@ -9,6 +9,7 @@ using Artemis.UI.Extensions; using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; +using Stylet; namespace Artemis.UI.Screens.ProfileEditor.Conditions { @@ -62,7 +63,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions protected override List GetExtraRightSideDataModelViewModels() { // Extra data models are expected to not have an empty root, so lets return the child - return GetEventDataModel().Children.Cast().ToList(); + BindableCollection children = GetEventDataModel().Children; + return children.Cast().ToList(); } private DataModelPropertiesViewModel GetEventDataModel() diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyView.xaml b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyView.xaml new file mode 100644 index 000000000..badf3bd73 --- /dev/null +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyView.xaml @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs new file mode 100644 index 000000000..000b32945 --- /dev/null +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs @@ -0,0 +1,84 @@ +using System; +using System.Linq; +using System.Windows.Input; +using Artemis.Core; +using Artemis.Core.Services; +using Stylet; + +namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit +{ + public class ProfileConfigurationHotkeyViewModel : Screen + { + private readonly bool _isDisableHotkey; + private readonly ProfileConfiguration _profileConfiguration; + private string _hint; + private string _hotkeyDisplay; + + public ProfileConfigurationHotkeyViewModel(ProfileConfiguration profileConfiguration, bool isDisableHotkey) + { + _profileConfiguration = profileConfiguration; + _isDisableHotkey = isDisableHotkey; + + UpdateHotkeyDisplay(); + } + + public ProfileConfigurationHotkey Hotkey => _isDisableHotkey ? _profileConfiguration.DisableHotkey : _profileConfiguration.EnableHotkey; + + public string Hint + { + get => _hint; + set => SetAndNotify(ref _hint, value); + } + + public string HotkeyDisplay + { + get => _hotkeyDisplay; + set + { + if (!SetAndNotify(ref _hotkeyDisplay, value)) return; + if (value == null && Hotkey != null) + { + Hotkey.Key = null; + Hotkey.Modifiers = null; + } + } + } + + public void UpdateHotkeyDisplay() + { + string display = null; + if (Hotkey?.Modifiers != null) + display = string.Join("+", Enum.GetValues().Skip(1).Where(m => Hotkey.Modifiers.Value.HasFlag(m))); + if (Hotkey?.Key != null) + display = string.IsNullOrEmpty(display) ? Hotkey.Key.ToString() : $"{display}+{Hotkey.Key}"; + + HotkeyDisplay = display; + if (_profileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.EnableDisable) + Hint = _isDisableHotkey ? "Disable hotkey" : "Enable hotkey"; + else + Hint = "Toggle hotkey"; + } + + public void TextBoxKeyUp(object sender, KeyEventArgs e) + { + if (e.Key >= Key.LeftShift && e.Key <= Key.RightAlt) + return; + + if (_isDisableHotkey) + { + _profileConfiguration.DisableHotkey ??= new ProfileConfigurationHotkey(); + _profileConfiguration.DisableHotkey.Key = (KeyboardKey?) e.Key; + _profileConfiguration.DisableHotkey.Modifiers = (KeyboardModifierKey?) Keyboard.Modifiers; + } + else + { + _profileConfiguration.EnableHotkey ??= new ProfileConfigurationHotkey(); + _profileConfiguration.EnableHotkey.Key = (KeyboardKey?) e.Key; + _profileConfiguration.EnableHotkey.Modifiers = (KeyboardModifierKey?) Keyboard.Modifiers; + } + + e.Handled = true; + UpdateHotkeyDisplay(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditView.xaml b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditView.xaml index 9e279ca51..6e3c4939e 100644 --- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditView.xaml +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditView.xaml @@ -221,16 +221,29 @@ - Keybinds + Hotkeys - You may set up keybinds to activate/deactivate the profile + You may set up hotkeys to activate/deactivate the profile - - - - Keybinds are not yet implemented - - + + + + diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditViewModel.cs b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditViewModel.cs index a54e44440..bbfb771cd 100644 --- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileEditViewModel.cs @@ -48,17 +48,23 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit _modules = ProfileConfiguration.Module != null ? new List {ProfileConfiguration.Module} : new List(); IconTypes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(ProfileConfigurationIconType))); + HotkeyModes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(ProfileConfigurationHotkeyMode))); Icons = new BindableCollection(); Modules = new BindableCollection( pluginManagementService.GetFeaturesOfType().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m)) ); Initializing = true; + ActivationConditionViewModel = dataModelConditionsVmFactory.DataModelConditionGroupViewModel(_dataModelConditionGroup, ConditionGroupType.General, _modules); ActivationConditionViewModel.ConductWith(this); ActivationConditionViewModel.IsRootGroup = true; ModuleActivationRequirementsViewModel = new ModuleActivationRequirementsViewModel(sidebarVmFactory); ModuleActivationRequirementsViewModel.ConductWith(this); ModuleActivationRequirementsViewModel.SetModule(ProfileConfiguration.Module); + EnableHotkeyViewModel = sidebarVmFactory.ProfileConfigurationHotkeyViewModel(ProfileConfiguration, false); + EnableHotkeyViewModel.ConductWith(this); + DisableHotkeyViewModel = sidebarVmFactory.ProfileConfigurationHotkeyViewModel(ProfileConfiguration, true); + DisableHotkeyViewModel.ConductWith(this); _profileName = ProfileConfiguration.Name; _selectedModule = Modules.FirstOrDefault(m => m.Module == ProfileConfiguration.Module); @@ -79,9 +85,15 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit }); } - public ProfileConfiguration ProfileConfiguration { get; } + public DataModelConditionGroupViewModel ActivationConditionViewModel { get; } + public ModuleActivationRequirementsViewModel ModuleActivationRequirementsViewModel { get; } + public ProfileConfigurationHotkeyViewModel EnableHotkeyViewModel { get; } + public ProfileConfigurationHotkeyViewModel DisableHotkeyViewModel { get; } + public bool IsNew { get; } + public ProfileConfiguration ProfileConfiguration { get; } public BindableCollection IconTypes { get; } + public BindableCollection HotkeyModes { get; } public BindableCollection Icons { get; } public BindableCollection Modules { get; } public bool HasUsableModules => Modules.Any(); @@ -108,6 +120,24 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit } } + public ProfileConfigurationHotkeyMode SelectedHotkeyMode + { + get => ProfileConfiguration.HotkeyMode; + set + { + ProfileConfiguration.HotkeyMode = value; + NotifyOfPropertyChange(nameof(SelectedHotkeyMode)); + NotifyOfPropertyChange(nameof(ShowEnableHotkey)); + NotifyOfPropertyChange(nameof(ShowDisableHotkey)); + + EnableHotkeyViewModel.UpdateHotkeyDisplay(); + DisableHotkeyViewModel.UpdateHotkeyDisplay(); + } + } + + public bool ShowEnableHotkey => ProfileConfiguration.HotkeyMode != ProfileConfigurationHotkeyMode.None; + public bool ShowDisableHotkey => ProfileConfiguration.HotkeyMode == ProfileConfigurationHotkeyMode.EnableDisable; + public Stream SelectedImage { get => _selectedImage; @@ -135,9 +165,6 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit } } - public DataModelConditionGroupViewModel ActivationConditionViewModel { get; } - public ModuleActivationRequirementsViewModel ModuleActivationRequirementsViewModel { get; } - public void Delete() { Session.Close(nameof(Delete)); @@ -202,6 +229,22 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit _changedImage = true; SelectedImage = File.OpenRead(dialog.FileName); } + + #region Overrides of Screen + + protected override void OnInitialActivate() + { + _profileService.HotkeysEnabled = false; + base.OnInitialActivate(); + } + + protected override void OnClose() + { + _profileService.HotkeysEnabled = true; + base.OnClose(); + } + + #endregion } public class ProfileEditViewModelValidator : AbstractValidator diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs index a4f85cf19..0a8384003 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs @@ -200,13 +200,16 @@ namespace Artemis.UI.Screens.Sidebar private void ProfileCategoryOnProfileConfigurationAdded(object sender, ProfileConfigurationEventArgs e) { - if (!_addingProfile && ShowItems) + Execute.PostToUIThread(() => { - Items.Add(_vmFactory.SidebarProfileConfigurationViewModel(e.ProfileConfiguration)); - ((BindableCollection) Items).Sort(p => p.ProfileConfiguration.Order); - } + if (!_addingProfile && ShowItems) + { + Items.Add(_vmFactory.SidebarProfileConfigurationViewModel(e.ProfileConfiguration)); + ((BindableCollection)Items).Sort(p => p.ProfileConfiguration.Order); + } - SelectedProfileConfiguration = Items.FirstOrDefault(i => i.ProfileConfiguration.IsBeingEdited); + SelectedProfileConfiguration = Items.FirstOrDefault(i => i.ProfileConfiguration.IsBeingEdited); + }); } #region Overrides of Screen diff --git a/src/Artemis.sln.DotSettings b/src/Artemis.sln.DotSettings index bffc7dc38..c899abc86 100644 --- a/src/Artemis.sln.DotSettings +++ b/src/Artemis.sln.DotSettings @@ -241,6 +241,7 @@ True True True + True True True True