diff --git a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/BooleanActivationRequirement.cs b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/BooleanActivationRequirement.cs index 2966fc4d0..7a9dddc12 100644 --- a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/BooleanActivationRequirement.cs +++ b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/BooleanActivationRequirement.cs @@ -15,5 +15,10 @@ { return ActivationMet; } + + public string GetUserFriendlyDescription() + { + return "No description available"; + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/IModuleActivationRequirement.cs b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/IModuleActivationRequirement.cs index 20a791477..11528beb9 100644 --- a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/IModuleActivationRequirement.cs +++ b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/IModuleActivationRequirement.cs @@ -10,5 +10,11 @@ /// /// bool Evaluate(); + + /// + /// Returns a user-friendly description of the activation requirement, should include parameters if applicable + /// + /// A user-friendly description of the activation requirement + string GetUserFriendlyDescription(); } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs index e18212b6d..1c226f1cc 100644 --- a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs +++ b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs @@ -43,5 +43,14 @@ namespace Artemis.Core.Plugins.Modules.ActivationRequirements ? processes.Any(p => string.Equals(Path.GetDirectoryName(p.GetProcessFilename()), Location, StringComparison.CurrentCultureIgnoreCase)) : processes.Any(); } + + public string GetUserFriendlyDescription() + { + var description = $"Requirement met when \"{ProcessName}.exe\" is running"; + if (Location != null) + description += $" from \"{Location}\""; + + return description; + } } } \ 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 8a6135300..2433fc64a 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -5,7 +5,9 @@ using Artemis.Core.Models.Profile.LayerProperties.Attributes; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins; using Artemis.Core.Plugins.Modules; +using Artemis.Core.Plugins.Modules.ActivationRequirements; using Artemis.UI.Screens.Module; +using Artemis.UI.Screens.Module.Tabs; using Artemis.UI.Screens.ProfileEditor; using Artemis.UI.Screens.ProfileEditor.DisplayConditions; using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; @@ -30,7 +32,10 @@ namespace Artemis.UI.Ninject.Factories public interface IModuleVmFactory : IVmFactory { - ModuleRootViewModel Create(Module module); + ModuleRootViewModel CreateModuleRootViewModel(Module module); + ProfileEditorViewModel CreateProfileEditorViewModel(ProfileModule module); + ActivationRequirementsViewModel CreateActivationRequirementsViewModel(Module module); + ActivationRequirementViewModel CreateActivationRequirementViewModel(IModuleActivationRequirement activationRequirement); } public interface ISettingsVmFactory : IVmFactory @@ -43,12 +48,7 @@ namespace Artemis.UI.Ninject.Factories { DeviceDebugViewModel Create(ArtemisDevice device); } - - public interface IProfileEditorVmFactory : IVmFactory - { - ProfileEditorViewModel Create(ProfileModule module); - } - + public interface IFolderVmFactory : IVmFactory { FolderViewModel Create(ProfileElement folder); diff --git a/src/Artemis.UI/Screens/Module/ModuleRootViewModel.cs b/src/Artemis.UI/Screens/Module/ModuleRootViewModel.cs index 6125fdac1..286cf7059 100644 --- a/src/Artemis.UI/Screens/Module/ModuleRootViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ModuleRootViewModel.cs @@ -1,9 +1,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Artemis.Core.Plugins.Modules; -using Artemis.Core.Services; -using Artemis.Core.Services.Interfaces; using Artemis.UI.Ninject.Factories; using Ninject; using Ninject.Parameters; @@ -13,17 +10,15 @@ namespace Artemis.UI.Screens.Module { public class ModuleRootViewModel : Conductor.Collection.OneActive { - private readonly IModuleService _moduleService; - private readonly IProfileEditorVmFactory _profileEditorVmFactory; private readonly IKernel _kernel; + private readonly IModuleVmFactory _moduleVmFactory; - public ModuleRootViewModel(Core.Plugins.Modules.Module module, IModuleService moduleService, IProfileEditorVmFactory profileEditorVmFactory, IKernel kernel) + public ModuleRootViewModel(Core.Plugins.Modules.Module module, IModuleVmFactory moduleVmFactory, IKernel kernel) { DisplayName = module?.DisplayName; Module = module; - _moduleService = moduleService; - _profileEditorVmFactory = profileEditorVmFactory; + _moduleVmFactory = moduleVmFactory; _kernel = kernel; } @@ -31,31 +26,18 @@ namespace Artemis.UI.Screens.Module protected override void OnActivate() { - Task.Run(async () => - { - await _moduleService.SetActiveModuleOverride(Module); - await AddTabsAsync(); - }); + AddTabs(); base.OnActivate(); } - protected override void OnDeactivate() - { - Task.Run(async () => - { - await _moduleService.SetActiveModuleOverride(null); - }); - base.OnDeactivate(); - } - - private async Task AddTabsAsync() + private void AddTabs() { // Create the profile editor and module VMs if (Module is ProfileModule profileModule) - { - var profileEditor = _profileEditorVmFactory.Create(profileModule); - Items.Add(profileEditor); - } + Items.Add(_moduleVmFactory.CreateProfileEditorViewModel(profileModule)); + + if (Module.ActivationRequirements.Any()) + Items.Add(_moduleVmFactory.CreateActivationRequirementsViewModel(Module)); if (Module.ModuleTabs != null) { diff --git a/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementView.xaml b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementView.xaml new file mode 100644 index 000000000..111376651 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementView.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementViewModel.cs b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementViewModel.cs new file mode 100644 index 000000000..1751c9888 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementViewModel.cs @@ -0,0 +1,64 @@ +using System.Timers; +using Artemis.Core.Plugins.Modules.ActivationRequirements; +using Humanizer; +using Stylet; + +namespace Artemis.UI.Screens.Module.Tabs +{ + public class ActivationRequirementViewModel : Screen + { + private readonly IModuleActivationRequirement _activationRequirement; + private readonly Timer _updateTimer; + private string _requirementDescription; + private bool _requirementMet; + + public ActivationRequirementViewModel(IModuleActivationRequirement activationRequirement) + { + _activationRequirement = activationRequirement; + _updateTimer = new Timer(500); + + RequirementName = activationRequirement.GetType().Name.Humanize(); + RequirementDescription = activationRequirement.GetUserFriendlyDescription(); + + _updateTimer.Elapsed += UpdateTimerOnElapsed; + } + + public string RequirementName { get; } + + public string RequirementDescription + { + get => _requirementDescription; + set => SetAndNotify(ref _requirementDescription, value); + } + + public bool RequirementMet + { + get => _requirementMet; + set => SetAndNotify(ref _requirementMet, value); + } + + protected override void OnActivate() + { + Update(); + _updateTimer.Start(); + base.OnActivate(); + } + + protected override void OnDeactivate() + { + _updateTimer.Stop(); + base.OnDeactivate(); + } + + private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e) + { + Update(); + } + + private void Update() + { + RequirementDescription = _activationRequirement.GetUserFriendlyDescription(); + RequirementMet = _activationRequirement.Evaluate(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementsView.xaml b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementsView.xaml new file mode 100644 index 000000000..6957a31a9 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementsView.xaml @@ -0,0 +1,53 @@ + + + + + Activation requirements + + This module has built-in activation requirements and won't activate until . + These requirements allow the module creator to decide when the module is activated and you cannot override them. + + + + Note: While you have the profile editor open the module is always activated and any other modules are deactivated. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementsViewModel.cs b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementsViewModel.cs new file mode 100644 index 000000000..d869f9794 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/Tabs/ActivationRequirementsViewModel.cs @@ -0,0 +1,36 @@ +using System.Linq; +using Artemis.Core.Plugins.Modules; +using Artemis.UI.Ninject.Factories; +using Stylet; + +namespace Artemis.UI.Screens.Module.Tabs +{ + public class ActivationRequirementsViewModel : Conductor.Collection.AllActive + { + private readonly IModuleVmFactory _moduleVmFactory; + + public ActivationRequirementsViewModel(Core.Plugins.Modules.Module module, IModuleVmFactory moduleVmFactory) + { + _moduleVmFactory = moduleVmFactory; + + DisplayName = "Activation requirements"; + Module = module; + + ActivationType = Module.ActivationRequirementMode == ActivationRequirementType.All + ? "all requirements are met" + : "any requirement is met"; + } + + public Core.Plugins.Modules.Module Module { get; } + + public string ActivationType { get; set; } + + protected override void OnActivate() + { + if (!Items.Any()) + Items.AddRange(Module.ActivationRequirements.Select(_moduleVmFactory.CreateActivationRequirementViewModel)); + + + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs index 8ea8c0210..4bf212d0f 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs @@ -7,6 +7,7 @@ using Artemis.Core.Models.Profile; using Artemis.Core.Plugins.Modules; using Artemis.Core.Plugins.Settings; using Artemis.Core.Services; +using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Storage.Interfaces; using Artemis.UI.Screens.ProfileEditor.Dialogs; using Artemis.UI.Screens.ProfileEditor.DisplayConditions; @@ -24,6 +25,7 @@ namespace Artemis.UI.Screens.ProfileEditor private readonly IProfileEditorService _profileEditorService; private readonly IProfileService _profileService; private readonly ISettingsService _settingsService; + private readonly IModuleService _moduleService; private readonly ISnackbarMessageQueue _snackbarMessageQueue; private BindableCollection _profiles; private PluginSetting _sidePanelsWidth; @@ -42,11 +44,13 @@ namespace Artemis.UI.Screens.ProfileEditor IProfileService profileService, IDialogService dialogService, ISettingsService settingsService, + IModuleService moduleService, ISnackbarMessageQueue snackbarMessageQueue) { _profileEditorService = profileEditorService; _profileService = profileService; _settingsService = settingsService; + _moduleService = moduleService; _snackbarMessageQueue = snackbarMessageQueue; DisplayName = "Profile editor"; @@ -233,19 +237,21 @@ namespace Artemis.UI.Screens.ProfileEditor LoadWorkspaceSettings(); Module.IsProfileUpdatingDisabled = true; Module.ActiveProfileChanged += ModuleOnActiveProfileChanged; - - Execute.PostToUIThread(LoadProfiles); + LoadProfiles(); + + Task.Run(async () => { await _moduleService.SetActiveModuleOverride(Module); }); base.OnActivate(); } - protected override void OnClose() + protected override void OnDeactivate() { SaveWorkspaceSettings(); Module.IsProfileUpdatingDisabled = false; Module.ActiveProfileChanged -= ModuleOnActiveProfileChanged; _profileEditorService.ChangeSelectedProfile(null); - base.OnClose(); + Task.Run(async () => { await _moduleService.SetActiveModuleOverride(null); }); + base.OnDeactivate(); } private void RemoveProfile(ProfileDescriptor profileDescriptor) @@ -309,19 +315,6 @@ namespace Artemis.UI.Screens.ProfileEditor // Get all profiles from the database Profiles.Clear(); Profiles.AddRange(_profileService.GetProfileDescriptors(Module).OrderBy(d => d.Name)); - - // Populate the selected profile - var lastActiveProfile = Profiles.FirstOrDefault(p => p.IsLastActiveProfile) ?? Profiles.FirstOrDefault(); - if (lastActiveProfile != null) - { - SelectedProfile = lastActiveProfile; - return; - } - - // Create a default profile if there is none - var defaultProfile = _profileService.CreateProfileDescriptor(Module, "Default"); - Profiles.Add(defaultProfile); - SelectedProfile = defaultProfile; } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs index 87d6e5779..7072d7bfe 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs @@ -211,7 +211,7 @@ namespace Artemis.UI.Screens.Sidebar private void ActivateModule(INavigationItem sidebarItem) { - SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.Create(SidebarModules[sidebarItem]) : null; + SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.CreateModuleRootViewModel(SidebarModules[sidebarItem]) : null; } #region Event handlers diff --git a/src/Plugins/Artemis.Plugins.Modules.Overlay/OverlayModule.cs b/src/Plugins/Artemis.Plugins.Modules.Overlay/OverlayModule.cs index d079c1626..843f4a3b5 100644 --- a/src/Plugins/Artemis.Plugins.Modules.Overlay/OverlayModule.cs +++ b/src/Plugins/Artemis.Plugins.Modules.Overlay/OverlayModule.cs @@ -1,4 +1,6 @@ -using Artemis.Core.Plugins.Modules; +using System; +using System.IO; +using Artemis.Core.Plugins.Modules; using Artemis.Core.Plugins.Modules.ActivationRequirements; namespace Artemis.Plugins.Modules.Overlay @@ -14,6 +16,11 @@ namespace Artemis.Plugins.Modules.Overlay DefaultPriorityCategory = ModulePriorityCategory.Overlay; ActivationRequirements.Add(new ProcessActivationRequirement("taskmgr")); + ActivationRequirements.Add(new ProcessActivationRequirement("calc")); + ActivationRequirements.Add(new ProcessActivationRequirement("mspaint") + { + Location = Path.Combine(Directory.GetParent(Environment.GetFolderPath(Environment.SpecialFolder.System)).FullName, "System32") + }); } // This is the end of your plugin life cycle.