diff --git a/src/Artemis.Core/Extensions/ProcessExtensions.cs b/src/Artemis.Core/Extensions/ProcessExtensions.cs
new file mode 100644
index 000000000..3353ed60d
--- /dev/null
+++ b/src/Artemis.Core/Extensions/ProcessExtensions.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Artemis.Core.Extensions
+{
+ public static class ProcessExtensions
+ {
+ public static string GetProcessFilename(this Process p)
+ {
+ var capacity = 2000;
+ var builder = new StringBuilder(capacity);
+ var ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
+ if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity)) return string.Empty;
+
+ return builder.ToString();
+ }
+
+ [DllImport("kernel32.dll")]
+ private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
+
+ [DllImport("kernel32.dll")]
+ private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
+
+ [Flags]
+ private enum ProcessAccessFlags : uint
+ {
+ QueryLimitedInformation = 0x00001000
+ }
+ }
+}
diff --git a/src/Artemis.Core/Plugins/Abstract/Module.cs b/src/Artemis.Core/Plugins/Abstract/Module.cs
index 5592004e9..c0a26a264 100644
--- a/src/Artemis.Core/Plugins/Abstract/Module.cs
+++ b/src/Artemis.Core/Plugins/Abstract/Module.cs
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.Abstract.DataModels;
using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
using Artemis.Core.Plugins.Abstract.ViewModels;
+using Artemis.Core.Plugins.ModuleActivationRequirements;
using SkiaSharp;
namespace Artemis.Core.Plugins.Abstract
@@ -26,7 +28,7 @@ namespace Artemis.Core.Plugins.Abstract
/// Gets or sets whether this module must also expand the main data model
///
/// Note: If expanding the main data model is all you want your plugin to do, create a
- /// plugin instead.
+ /// plugin instead.
///
///
public bool ExpandsDataModel
@@ -66,9 +68,11 @@ namespace Artemis.Core.Plugins.Abstract
///
public abstract class Module : Plugin
{
- internal DataModel InternalDataModel { get; set; }
-
- internal bool InternalExpandsMainDataModel { get; set; }
+ protected Module()
+ {
+ ActivationRequirements = new List();
+ ActivationRequirementMode = ActivationRequirementType.Any;
+ }
///
/// The modules display name that's shown in the menu
@@ -81,6 +85,27 @@ namespace Artemis.Core.Plugins.Abstract
///
public string DisplayIcon { get; set; }
+ ///
+ /// Gets whether this module is activated. A module can only be active while its
+ /// are met
+ ///
+ public bool IsActivated { get; internal set; }
+
+ ///
+ /// A list of activation requirements
+ /// Note: if empty the module is always activated
+ ///
+ public List ActivationRequirements { get; }
+
+ ///
+ /// Gets or sets the activation requirement mode, defaults to
+ ///
+ public ActivationRequirementType ActivationRequirementMode { get; set; }
+
+ internal DataModel InternalDataModel { get; set; }
+
+ internal bool InternalExpandsMainDataModel { get; set; }
+
///
/// Called each frame when the module must update
///
@@ -101,5 +126,62 @@ namespace Artemis.Core.Plugins.Abstract
///
///
public abstract IEnumerable GetViewModels();
+
+ ///
+ /// Called when the are met
+ ///
+ public abstract void ModuleActivated();
+
+ ///
+ /// Called when the are no longer met
+ ///
+ public abstract void ModuleDeactivated();
+
+ ///
+ /// Evaluates the activation requirements following the and returns the result
+ ///
+ /// The evaluated result of the activation requirements
+ public bool EvaluateActivationRequirements()
+ {
+ if (!ActivationRequirements.Any())
+ return true;
+ if (ActivationRequirementMode == ActivationRequirementType.All)
+ return ActivationRequirements.All(r => r.Evaluate());
+ if (ActivationRequirementMode == ActivationRequirementType.Any)
+ return ActivationRequirements.Any(r => r.Evaluate());
+
+ return false;
+ }
+
+ internal virtual void Activate()
+ {
+ if (IsActivated)
+ return;
+
+ ModuleActivated();
+ IsActivated = true;
+ }
+
+ internal virtual void Deactivate()
+ {
+ if (!IsActivated)
+ return;
+
+ IsActivated = false;
+ ModuleDeactivated();
+ }
+ }
+
+ public enum ActivationRequirementType
+ {
+ ///
+ /// Any activation requirement must be met for the module to activate
+ ///
+ Any,
+
+ ///
+ /// All activation requirements must be met for the module to activate
+ ///
+ All
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
index 55965ca63..1fa60785b 100644
--- a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
+++ b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
@@ -143,7 +143,8 @@ namespace Artemis.Core.Plugins.Abstract
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}.");
-
+ if (!IsActivated)
+ throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
if (profile == ActiveProfile || AnimatingProfileChange)
return;
@@ -164,6 +165,9 @@ namespace Artemis.Core.Plugins.Abstract
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}.");
+ if (!IsActivated)
+ throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
+
lock (this)
{
if (profile == ActiveProfile)
@@ -188,6 +192,15 @@ namespace Artemis.Core.Plugins.Abstract
///
public bool AnimatingProfileChange { get; private set; }
+ internal override void Deactivate()
+ {
+ base.Deactivate();
+
+ var profile = ActiveProfile;
+ ActiveProfile = null;
+ profile?.Dispose();
+ }
+
#region Events
public event EventHandler ActiveProfileChanged;
diff --git a/src/Artemis.Core/Plugins/ModuleActivationRequirements/ProcessActivationRequirement.cs b/src/Artemis.Core/Plugins/ModuleActivationRequirements/ProcessActivationRequirement.cs
new file mode 100644
index 000000000..87cd32dbf
--- /dev/null
+++ b/src/Artemis.Core/Plugins/ModuleActivationRequirements/ProcessActivationRequirement.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Artemis.Core.Extensions;
+
+namespace Artemis.Core.Plugins.ModuleActivationRequirements
+{
+ public class ProcessActivationRequirement : IModuleActivationRequirement
+ {
+ public string ProcessName { get; set; }
+ public string Location { get; set; }
+
+ public ProcessActivationRequirement(string processName, string location = null)
+ {
+ ProcessName = processName;
+ Location = location;
+ }
+
+
+ public bool Evaluate()
+ {
+ if (ProcessName == null && Location == null)
+ return false;
+
+ var processes = ProcessName != null ? Process.GetProcessesByName(ProcessName).Where(p => !p.HasExited) : Process.GetProcesses().Where(p => !p.HasExited);
+ return Location != null
+ ? processes.Any(p => Path.GetDirectoryName(p.GetProcessFilename()) == Location)
+ : processes.Any();
+ }
+ }
+
+ public interface IModuleActivationRequirement
+ {
+ bool Evaluate();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index ec0654995..5193b45d1 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -39,6 +39,7 @@ namespace Artemis.Core.Services
private List _dataModelExpansions;
private List _modules;
private IntroAnimation _introAnimation;
+ private DateTime _lastModuleActivationUpdate;
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
internal CoreService(ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
@@ -95,8 +96,6 @@ namespace Artemis.Core.Services
_logger.Information("Initialized without an active surface entity");
PlayIntroAnimation();
- _profileService.ActivateLastActiveProfiles();
-
OnInitialized();
}
@@ -170,6 +169,11 @@ namespace Artemis.Core.Services
try
{
_frameStopWatch.Restart();
+
+ // Only run the module activation update every 2 seconds
+ if (DateTime.Now - _lastModuleActivationUpdate > TimeSpan.FromSeconds(2))
+ ModuleActivationUpdate();
+
lock (_dataModelExpansions)
{
// Update all active modules
@@ -177,13 +181,16 @@ namespace Artemis.Core.Services
dataModelExpansion.Update(args.DeltaTime);
}
+ List modules;
lock (_modules)
{
- // Update all active modules
- foreach (var module in _modules)
- module.Update(args.DeltaTime);
+ modules = _modules.Where(m => m.IsActivated).ToList();
}
+ // Update all active modules
+ foreach (var module in modules)
+ module.Update(args.DeltaTime);
+
// If there is no ready bitmap brush, skip the frame
if (_rgbService.BitmapBrush == null)
return;
@@ -194,20 +201,15 @@ namespace Artemis.Core.Services
return;
// Render all active modules
- using (var canvas = new SKCanvas(_rgbService.BitmapBrush.Bitmap))
+ using var canvas = new SKCanvas(_rgbService.BitmapBrush.Bitmap);
+ canvas.Clear(new SKColor(0, 0, 0));
+ if (!ModuleRenderingDisabled)
{
- canvas.Clear(new SKColor(0, 0, 0));
- if (!ModuleRenderingDisabled)
- {
- lock (_modules)
- {
- foreach (var module in _modules)
- module.Render(args.DeltaTime, _surfaceService.ActiveSurface, canvas, _rgbService.BitmapBrush.Bitmap.Info);
- }
- }
-
- OnFrameRendering(new FrameRenderingEventArgs(_modules, canvas, args.DeltaTime, _rgbService.Surface));
+ foreach (var module in modules)
+ module.Render(args.DeltaTime, _surfaceService.ActiveSurface, canvas, _rgbService.BitmapBrush.Bitmap.Info);
}
+
+ OnFrameRendering(new FrameRenderingEventArgs(modules, canvas, args.DeltaTime, _rgbService.Surface));
}
}
catch (Exception e)
@@ -221,6 +223,33 @@ namespace Artemis.Core.Services
}
}
+ private void ModuleActivationUpdate()
+ {
+ _lastModuleActivationUpdate = DateTime.Now;
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+ lock (_modules)
+ {
+ foreach (var module in _modules)
+ {
+ var shouldBeActivated = module.EvaluateActivationRequirements();
+ if (shouldBeActivated && !module.IsActivated)
+ {
+ module.Activate();
+ // If this is a profile module, activate the last active profile after module activation
+ if (module is ProfileModule profileModule)
+ _profileService.ActivateLastProfile(profileModule);
+ }
+ else if (!shouldBeActivated && module.IsActivated)
+ module.Deactivate();
+ }
+ }
+
+ stopwatch.Stop();
+ if (stopwatch.ElapsedMilliseconds > 100)
+ _logger.Warning("Activation requirements evaluation took too long: {moduleCount} module(s) in {elapsed}", _modules.Count, stopwatch.Elapsed);
+ }
+
private void SurfaceOnUpdated(UpdatedEventArgs args)
{
if (_rgbService.IsRenderPaused)
diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs
index 7ebbae54a..b4cb7235b 100644
--- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs
@@ -11,11 +11,6 @@ namespace Artemis.Core.Services.Storage.Interfaces
///
public interface IProfileService : IArtemisService
{
- ///
- /// Activates the last profile for each module
- ///
- void ActivateLastActiveProfiles();
-
///
/// Creates a new profile for the given module and returns a descriptor pointing to it
///
@@ -50,6 +45,12 @@ namespace Artemis.Core.Services.Storage.Interfaces
/// The descriptor pointing to the profile to delete
void DeleteProfile(ProfileDescriptor profileDescriptor);
+ ///
+ /// Activates the last profile of the given profile module
+ ///
+ ///
+ void ActivateLastProfile(ProfileModule profileModule);
+
///
/// Activates the profile described in the given with the currently active surface
///
diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs
index 0eee8c30d..e79e8f450 100644
--- a/src/Artemis.Core/Services/Storage/ProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/ProfileService.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.Events;
@@ -11,7 +10,6 @@ using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.LayerEffect.Abstract;
using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage.Interfaces;
-using Artemis.Core.Utilities;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
@@ -44,15 +42,6 @@ namespace Artemis.Core.Services.Storage
public JsonSerializerSettings MementoSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
public JsonSerializerSettings ExportSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented};
- public void ActivateLastActiveProfiles()
- {
- foreach (var profileModule in _pluginService.GetPluginsOfType())
- {
- var activeProfile = GetLastActiveProfile(profileModule);
- ActivateProfile(activeProfile);
- }
- }
-
public List GetProfileDescriptors(ProfileModule module)
{
var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
@@ -67,6 +56,12 @@ namespace Artemis.Core.Services.Storage
return new ProfileDescriptor(module, profileEntity);
}
+ public void ActivateLastProfile(ProfileModule profileModule)
+ {
+ var activeProfile = GetLastActiveProfile(profileModule);
+ ActivateProfile(activeProfile);
+ }
+
public Profile ActivateProfile(ProfileDescriptor profileDescriptor)
{
if (profileDescriptor.ProfileModule.ActiveProfile?.EntityId == profileDescriptor.Id)
@@ -109,16 +104,6 @@ namespace Artemis.Core.Services.Storage
SaveActiveProfile(module);
}
- private void SaveActiveProfile(ProfileModule module)
- {
- var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
- foreach (var profileEntity in profileEntities)
- {
- profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id;
- _profileRepository.Save(profileEntity);
- }
- }
-
public async Task ClearActiveProfileAnimated(ProfileModule module)
{
await module.ChangeActiveProfileAnimated(null, _surfaceService.ActiveSurface);
@@ -210,13 +195,6 @@ namespace Artemis.Core.Services.Storage
}
}
- public ProfileDescriptor GetLastActiveProfile(ProfileModule module)
- {
- var moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
- var profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault();
- return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity);
- }
-
public void InstantiateProfile(Profile profile)
{
profile.PopulateLeds(_surfaceService.ActiveSurface);
@@ -245,6 +223,23 @@ namespace Artemis.Core.Services.Storage
return new ProfileDescriptor(profileModule, profileEntity);
}
+ public ProfileDescriptor GetLastActiveProfile(ProfileModule module)
+ {
+ var moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
+ var profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault();
+ return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity);
+ }
+
+ private void SaveActiveProfile(ProfileModule module)
+ {
+ var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
+ foreach (var profileEntity in profileEntities)
+ {
+ profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id;
+ _profileRepository.Save(profileEntity);
+ }
+ }
+
///
/// Initializes the properties on the layers of the given profile
///
@@ -347,11 +342,6 @@ namespace Artemis.Core.Services.Storage
ActiveProfilesInstantiatePlugins();
if (e.PluginInfo.Instance is LayerEffectProvider)
ActiveProfilesInstantiatePlugins();
- else if (e.PluginInfo.Instance is ProfileModule profileModule)
- {
- var activeProfile = GetLastActiveProfile(profileModule);
- ActivateProfile(activeProfile);
- }
}
#endregion
diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs
index d44860799..879e6d9a4 100644
--- a/src/Artemis.Core/Utilities/IntroAnimation.cs
+++ b/src/Artemis.Core/Utilities/IntroAnimation.cs
@@ -2,18 +2,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile;
-using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Abstract.ViewModels;
-using Artemis.Core.Plugins.LayerBrush;
-using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage.Interfaces;
using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using Serilog;
using SkiaSharp;
@@ -33,6 +28,17 @@ namespace Artemis.Core.Utilities
CreateIntroProfile();
}
+ public Profile AnimationProfile { get; set; }
+
+ public void Render(double deltaTime, SKCanvas canvas, SKImageInfo bitmapInfo)
+ {
+ if (AnimationProfile == null)
+ return;
+
+ AnimationProfile.Update(deltaTime);
+ AnimationProfile.Render(deltaTime, canvas, bitmapInfo);
+ }
+
private void CreateIntroProfile()
{
try
@@ -43,10 +49,10 @@ namespace Artemis.Core.Utilities
// Inject every LED on the surface into each layer
foreach (var profileEntityLayer in profileEntity.Layers)
{
- profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity()
+ profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity
{
DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
- LedName = l.RgbLed.Id.ToString(),
+ LedName = l.RgbLed.Id.ToString()
}));
}
@@ -61,32 +67,33 @@ namespace Artemis.Core.Utilities
_logger.Warning(e, "Failed to load intro profile");
}
}
-
- public Profile AnimationProfile { get; set; }
-
- public void Render(double deltaTime, SKCanvas canvas, SKImageInfo bitmapInfo)
- {
- if (AnimationProfile == null)
- return;
-
- AnimationProfile.Update(deltaTime);
- AnimationProfile.Render(deltaTime, canvas, bitmapInfo);
- }
}
internal class DummyModule : ProfileModule
{
public override void EnablePlugin()
{
+ throw new NotImplementedException();
}
public override void DisablePlugin()
{
+ throw new NotImplementedException();
+ }
+
+ public override void ModuleActivated()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void ModuleDeactivated()
+ {
+ throw new NotImplementedException();
}
public override IEnumerable GetViewModels()
{
- return new List();
+ throw new NotImplementedException();
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index 7d5a3bd20..67ac10f68 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -121,6 +121,7 @@
+
@@ -296,6 +297,7 @@
+
diff --git a/src/Artemis.UI/Resources/Images/Sidebar/sidebar-header.png b/src/Artemis.UI/Resources/Images/Sidebar/sidebar-header.png
new file mode 100644
index 000000000..d377df9d7
Binary files /dev/null and b/src/Artemis.UI/Resources/Images/Sidebar/sidebar-header.png differ
diff --git a/src/Artemis.UI/Screens/RootView.xaml b/src/Artemis.UI/Screens/RootView.xaml
index 263ec1c2c..2d0bb3fcf 100644
--- a/src/Artemis.UI/Screens/RootView.xaml
+++ b/src/Artemis.UI/Screens/RootView.xaml
@@ -51,13 +51,13 @@
-
+
diff --git a/src/Artemis.UI/Screens/RootViewModel.cs b/src/Artemis.UI/Screens/RootViewModel.cs
index cdc5c2fe0..cb4a09650 100644
--- a/src/Artemis.UI/Screens/RootViewModel.cs
+++ b/src/Artemis.UI/Screens/RootViewModel.cs
@@ -66,13 +66,7 @@ namespace Artemis.UI.Screens
get => _mainMessageQueue;
set => SetAndNotify(ref _mainMessageQueue, value);
}
-
- public bool IsSidebarVisible
- {
- get => _isSidebarVisible;
- set => SetAndNotify(ref _isSidebarVisible, value);
- }
-
+
public bool ActiveItemReady
{
get => _activeItemReady;
@@ -140,7 +134,7 @@ namespace Artemis.UI.Screens
{
if (e.PropertyName == nameof(SidebarViewModel.SelectedItem))
{
- IsSidebarVisible = false;
+ SidebarViewModel.IsSidebarOpen = false;
ActiveItemReady = false;
// Allow the menu to close, it's slower but feels more responsive, funny how that works right
@@ -254,7 +248,7 @@ namespace Artemis.UI.Screens
protected override void OnClose()
{
SidebarViewModel.Dispose();
-
+
// Lets force the GC to run after closing the window so it is obvious to users watching task manager
// that closing the UI will decrease the memory footprint of the application.
diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarView.xaml b/src/Artemis.UI/Screens/Sidebar/SidebarView.xaml
index c0e13a66d..ae8d4acfb 100644
--- a/src/Artemis.UI/Screens/Sidebar/SidebarView.xaml
+++ b/src/Artemis.UI/Screens/Sidebar/SidebarView.xaml
@@ -10,22 +10,19 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance sidebar:SidebarViewModel}">
-
-
-
-
-
- Active module
-
-
- Profile 1
- Profile 2
- Profile 3
- Profile 4
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
\ 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 c45634af6..2b708ee6f 100644
--- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs
+++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using System.Timers;
using Artemis.Core.Events;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Events;
@@ -27,6 +28,9 @@ namespace Artemis.UI.Screens.Sidebar
private BindableCollection _sidebarItems;
private Dictionary _sidebarModules;
private IScreen _selectedItem;
+ private bool _isSidebarOpen;
+ private readonly Timer _activeModulesUpdateTimer;
+ private string _activeModules;
public SidebarViewModel(IKernel kernel, IEventAggregator eventAggregator, IModuleVmFactory moduleVmFactory, IPluginService pluginService)
{
@@ -37,6 +41,10 @@ namespace Artemis.UI.Screens.Sidebar
SidebarModules = new Dictionary();
SidebarItems = new BindableCollection();
+ _activeModulesUpdateTimer = new Timer(1000);
+ _activeModulesUpdateTimer.Start();
+ _activeModulesUpdateTimer.Elapsed += ActiveModulesUpdateTimerOnElapsed;
+
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled += PluginServiceOnPluginDisabled;
@@ -44,6 +52,15 @@ namespace Artemis.UI.Screens.Sidebar
eventAggregator.Subscribe(this);
}
+ private void ActiveModulesUpdateTimerOnElapsed(object sender, EventArgs e)
+ {
+ if (!IsSidebarOpen)
+ return;
+
+ var activeModules = SidebarModules.Count(m => m.Value.IsActivated);
+ ActiveModules = activeModules == 1 ? "1 active module" : $"{activeModules} active modules";
+ }
+
public BindableCollection SidebarItems
{
get => _sidebarItems;
@@ -56,19 +73,35 @@ namespace Artemis.UI.Screens.Sidebar
set => SetAndNotify(ref _sidebarModules, value);
}
+ public string ActiveModules
+ {
+ get => _activeModules;
+ set => SetAndNotify(ref _activeModules, value);
+ }
+
public IScreen SelectedItem
{
get => _selectedItem;
set => SetAndNotify(ref _selectedItem, value);
}
+ public bool IsSidebarOpen
+ {
+ get => _isSidebarOpen;
+ set
+ {
+ SetAndNotify(ref _isSidebarOpen, value);
+ if (value)
+ ActiveModulesUpdateTimerOnElapsed(this, EventArgs.Empty);
+ }
+ }
+
public void SetupSidebar()
{
- SidebarItems.Clear();
+ SidebarItems.Clear();
SidebarModules.Clear();
// Add all default sidebar items
- SidebarItems.Add(new DividerNavigationItem());
SidebarItems.Add(new FirstLevelNavigationItem {Icon = PackIconKind.Home, Label = "Home"});
SidebarItems.Add(new FirstLevelNavigationItem {Icon = PackIconKind.Newspaper, Label = "News"});
SidebarItems.Add(new FirstLevelNavigationItem {Icon = PackIconKind.TestTube, Label = "Workshop"});
@@ -83,7 +116,7 @@ namespace Artemis.UI.Screens.Sidebar
AddModule(module);
// Select the top item, which will be one of the defaults
- Task.Run(() => SelectSidebarItem(SidebarItems[1]));
+ Task.Run(() => SelectSidebarItem(SidebarItems[0]));
}
// ReSharper disable once UnusedMember.Global - Called by view
@@ -202,6 +235,9 @@ namespace Artemis.UI.Screens.Sidebar
_pluginService.PluginEnabled -= PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled -= PluginServiceOnPluginDisabled;
+
+ _activeModulesUpdateTimer.Stop();
+ _activeModulesUpdateTimer.Elapsed -= ActiveModulesUpdateTimerOnElapsed;
}
}
}
\ No newline at end of file
diff --git a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs
index 17e07abd4..e5e7e1da2 100644
--- a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs
+++ b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using Artemis.Core.Extensions;
using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
using Artemis.Plugins.Modules.General.Utilities;
@@ -16,7 +17,7 @@ namespace Artemis.Plugins.Modules.General.DataModel.Windows
ProcessName = process.ProcessName;
// Accessing MainModule requires admin privileges, this way does not
- ProgramLocation = WindowUtilities.GetProcessFilename(process);
+ ProgramLocation = process.GetProcessFilename();
}
public string WindowTitle { get; set; }
diff --git a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs
index cb5af53a0..9eda911f1 100644
--- a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs
+++ b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs
@@ -12,9 +12,28 @@ namespace Artemis.Plugins.Modules.General
{
public class GeneralModule : ProfileModule
{
- public override IEnumerable GetViewModels()
+ public override void EnablePlugin()
+ {
+ DisplayName = "General";
+ DisplayIcon = "AllInclusive";
+ ExpandsDataModel = true;
+
+ DataModel.TestTimeList.Add(new TimeDataModel { CurrentTime = DateTime.Now.AddDays(1), CurrentTimeUTC = DateTime.UtcNow.AddDays(1) });
+ DataModel.TestTimeList.Add(new TimeDataModel { CurrentTime = DateTime.Now.AddDays(2), CurrentTimeUTC = DateTime.UtcNow.AddDays(2) });
+ DataModel.TestTimeList.Add(new TimeDataModel { CurrentTime = DateTime.Now.AddDays(3), CurrentTimeUTC = DateTime.UtcNow.AddDays(3) });
+ DataModel.TestTimeList.Add(new TimeDataModel { CurrentTime = DateTime.Now.AddDays(4), CurrentTimeUTC = DateTime.UtcNow.AddDays(4) });
+ }
+
+ public override void DisablePlugin()
+ {
+ }
+
+ public override void ModuleActivated()
+ {
+ }
+
+ public override void ModuleDeactivated()
{
- return new List {new GeneralViewModel(this)};
}
public override void Update(double deltaTime)
@@ -26,20 +45,9 @@ namespace Artemis.Plugins.Modules.General
base.Update(deltaTime);
}
- public override void EnablePlugin()
- {
- DisplayName = "General";
- DisplayIcon = "AllInclusive";
- ExpandsDataModel = true;
-
- DataModel.TestTimeList.Add(new TimeDataModel {CurrentTime = DateTime.Now.AddDays(1), CurrentTimeUTC = DateTime.UtcNow.AddDays(1)});
- DataModel.TestTimeList.Add(new TimeDataModel {CurrentTime = DateTime.Now.AddDays(2), CurrentTimeUTC = DateTime.UtcNow.AddDays(2)});
- DataModel.TestTimeList.Add(new TimeDataModel {CurrentTime = DateTime.Now.AddDays(3), CurrentTimeUTC = DateTime.UtcNow.AddDays(3)});
- DataModel.TestTimeList.Add(new TimeDataModel {CurrentTime = DateTime.Now.AddDays(4), CurrentTimeUTC = DateTime.UtcNow.AddDays(4)});
- }
-
- public override void DisablePlugin()
+ public override IEnumerable GetViewModels()
{
+ return new List { new GeneralViewModel(this) };
}
#region Open windows
diff --git a/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs b/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs
index 37267cc4f..e8b32b08b 100644
--- a/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs
+++ b/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs
@@ -1,7 +1,5 @@
using System;
-using System.Diagnostics;
using System.Runtime.InteropServices;
-using System.Text;
namespace Artemis.Plugins.Modules.General.Utilities
{
@@ -14,33 +12,11 @@ namespace Artemis.Plugins.Modules.General.Utilities
return (int) processId;
}
- public static string GetProcessFilename(Process p)
- {
- var capacity = 2000;
- var builder = new StringBuilder(capacity);
- var ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
- if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity)) return string.Empty;
-
- return builder.ToString();
- }
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
-
- [DllImport("kernel32.dll")]
- private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
-
- [DllImport("kernel32.dll")]
- private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
-
-
- [Flags]
- private enum ProcessAccessFlags : uint
- {
- QueryLimitedInformation = 0x00001000
- }
}
}
\ No newline at end of file