1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Plugins - Added button to delete plugin settings

UI - Try to release SDKs on Windows logout/shutdown
Plugins UI - Increased width and fixed long feature names not wrapping
This commit is contained in:
Robert 2021-03-30 19:15:16 +02:00
parent 7a5f07c686
commit 1eebb1f247
15 changed files with 174 additions and 116 deletions

View File

@ -53,7 +53,7 @@ steps:
command: 'publish' command: 'publish'
publishWebProjects: false publishWebProjects: false
projects: '$(artemisSolution)' projects: '$(artemisSolution)'
arguments: '--runtime win-x64 --self-contained false --output $(Build.ArtifactStagingDirectory)/build /nowarn:cs1591' arguments: '--runtime win-x64 --self-contained false --configuration Release --output $(Build.ArtifactStagingDirectory)/build /nowarn:cs1591'
zipAfterPublish: false zipAfterPublish: false
modifyOutputPath: false modifyOutputPath: false
@ -73,12 +73,13 @@ steps:
fileType: 'json' fileType: 'json'
targetFiles: '**/buildinfo.json' targetFiles: '**/buildinfo.json'
# Copy Artemis binaries to where plugin projects expect them
- task: CopyFiles@2 - task: CopyFiles@2
displayName: 'Plugins - Prepare Artemis binaries' displayName: 'Plugins - Prepare Artemis binaries'
inputs: inputs:
SourceFolder: '$(Build.ArtifactStagingDirectory)/build' SourceFolder: '$(Build.ArtifactStagingDirectory)/build'
Contents: '**' Contents: '**'
TargetFolder: 'Artemis/src/Artemis.UI/bin/x64/Debug/net5.0-windows' TargetFolder: 'Artemis/src/Artemis.UI/bin/net5.0-windows'
- task: PowerShell@2 - task: PowerShell@2
displayName: 'Plugins - Insert build number into plugin.json' displayName: 'Plugins - Insert build number into plugin.json'
@ -99,7 +100,7 @@ steps:
inputs: inputs:
command: 'publish' command: 'publish'
publishWebProjects: false publishWebProjects: false
arguments: '--runtime win-x64 --self-contained false --output $(Build.ArtifactStagingDirectory)/build/Plugins' arguments: '--runtime win-x64 --configuration Release --self-contained false --output $(Build.ArtifactStagingDirectory)/build/Plugins'
projects: '$(pluginProjects)' projects: '$(pluginProjects)'
zipAfterPublish: true zipAfterPublish: true

View File

@ -1,4 +1,7 @@
using System.IO; using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Storage; using Artemis.Storage;
using Artemis.Storage.Migrations.Interfaces; using Artemis.Storage.Migrations.Interfaces;

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using Ninject.Activation; using Ninject.Activation;
using Serilog; using Serilog;
using Serilog.Core; using Serilog.Core;

View File

@ -88,6 +88,11 @@ namespace Artemis.Core
/// </summary> /// </summary>
internal PluginEntity Entity { get; set; } internal PluginEntity Entity { get; set; }
/// <summary>
/// Populated when plugin settings are first loaded
/// </summary>
internal PluginSettings? Settings { get; set; }
/// <summary> /// <summary>
/// Resolves the relative path provided in the <paramref name="path" /> parameter to an absolute path /// Resolves the relative path provided in the <paramref name="path" /> parameter to an absolute path
/// </summary> /// </summary>
@ -101,7 +106,6 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Looks up the instance of the feature of type <typeparamref name="T" /> /// Looks up the instance of the feature of type <typeparamref name="T" />
/// <para>Note: This method only returns instances of enabled features</para>
/// </summary> /// </summary>
/// <typeparam name="T">The type of feature to find</typeparam> /// <typeparam name="T">The type of feature to find</typeparam>
/// <returns>If found, the instance of the feature</returns> /// <returns>If found, the instance of the feature</returns>
@ -116,6 +120,83 @@ namespace Artemis.Core
return Info.ToString(); return Info.ToString();
} }
/// <summary>
/// Occurs when the plugin is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the plugin is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Occurs when an feature is loaded and added to the plugin
/// </summary>
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureAdded;
/// <summary>
/// Occurs when an feature is disabled and removed from the plugin
/// </summary>
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureRemoved;
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
foreach (PluginFeatureInfo feature in Features)
feature.Instance?.Dispose();
SetEnabled(false);
Kernel?.Dispose();
PluginLoader?.Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();
_features.Clear();
}
}
/// <summary>
/// Invokes the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
Enabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the Disabled event
/// </summary>
protected virtual void OnDisabled()
{
Disabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the FeatureAdded event
/// </summary>
protected virtual void OnFeatureAdded(PluginFeatureInfoEventArgs e)
{
FeatureAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the FeatureRemoved event
/// </summary>
protected virtual void OnFeatureRemoved(PluginFeatureInfoEventArgs e)
{
FeatureRemoved?.Invoke(this, e);
}
internal void ApplyToEntity() internal void ApplyToEntity()
{ {
Entity.Id = Guid; Entity.Id = Guid;
@ -169,96 +250,11 @@ namespace Artemis.Core
return Entity.Features.Any(f => f.IsEnabled) || Features.Any(f => f.AlwaysEnabled); return Entity.Features.Any(f => f.IsEnabled) || Features.Any(f => f.AlwaysEnabled);
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
foreach (PluginFeatureInfo feature in Features)
feature.Instance?.Dispose();
SetEnabled(false);
Kernel?.Dispose();
PluginLoader?.Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();
_features.Clear();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
#endregion
#region Events
/// <summary>
/// Occurs when the plugin is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the plugin is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Occurs when an feature is loaded and added to the plugin
/// </summary>
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureAdded;
/// <summary>
/// Occurs when an feature is disabled and removed from the plugin
/// </summary>
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureRemoved;
/// <summary>
/// Invokes the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
Enabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the Disabled event
/// </summary>
protected virtual void OnDisabled()
{
Disabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the FeatureAdded event
/// </summary>
protected virtual void OnFeatureAdded(PluginFeatureInfoEventArgs e)
{
FeatureAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the FeatureRemoved event
/// </summary>
protected virtual void OnFeatureRemoved(PluginFeatureInfoEventArgs e)
{
FeatureRemoved?.Invoke(this, e);
}
#endregion
} }
} }

View File

@ -17,6 +17,8 @@ namespace Artemis.Core
internal PluginSettings(Plugin plugin, IPluginRepository pluginRepository) internal PluginSettings(Plugin plugin, IPluginRepository pluginRepository)
{ {
Plugin = plugin; Plugin = plugin;
Plugin.Settings = this;
_pluginRepository = pluginRepository; _pluginRepository = pluginRepository;
_settingEntities = new Dictionary<string, object>(); _settingEntities = new Dictionary<string, object>();
} }
@ -65,5 +67,10 @@ namespace Artemis.Core
return pluginSetting; return pluginSetting;
} }
} }
internal void ClearSettings()
{
_settingEntities.Clear();
}
} }
} }

View File

@ -74,7 +74,14 @@ namespace Artemis.Core.Services
/// Unloads and permanently removes the provided plugin /// Unloads and permanently removes the provided plugin
/// </summary> /// </summary>
/// <param name="plugin">The plugin to remove</param> /// <param name="plugin">The plugin to remove</param>
void RemovePlugin(Plugin plugin); /// <param name="removeSettings"></param>
void RemovePlugin(Plugin plugin, bool removeSettings);
/// <summary>
/// Removes the settings of a disabled plugin
/// </summary>
/// <param name="plugin">The plugin whose settings to remove</param>
void RemovePluginSettings(Plugin plugin);
/// <summary> /// <summary>
/// Enables the provided plugin feature /// Enables the provided plugin feature
@ -134,8 +141,6 @@ namespace Artemis.Core.Services
/// <param name="pluginAction">The action to take</param> /// <param name="pluginAction">The action to take</param>
void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction); void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction);
#region Events
/// <summary> /// <summary>
/// Occurs when built-in plugins are being loaded /// Occurs when built-in plugins are being loaded
/// </summary> /// </summary>
@ -190,7 +195,5 @@ namespace Artemis.Core.Services
/// Occurs when a plugin feature has been disabled /// Occurs when a plugin feature has been disabled
/// </summary> /// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled; public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
#endregion
} }
} }

View File

@ -479,7 +479,7 @@ namespace Artemis.Core.Services
if (existing != null) if (existing != null)
try try
{ {
RemovePlugin(existing); RemovePlugin(existing, false);
} }
catch (Exception e) catch (Exception e)
{ {
@ -519,7 +519,7 @@ namespace Artemis.Core.Services
return LoadPlugin(directoryInfo); return LoadPlugin(directoryInfo);
} }
public void RemovePlugin(Plugin plugin) public void RemovePlugin(Plugin plugin, bool removeSettings)
{ {
DirectoryInfo directory = plugin.Directory; DirectoryInfo directory = plugin.Directory;
lock (_plugins) lock (_plugins)
@ -529,6 +529,16 @@ namespace Artemis.Core.Services
} }
directory.Delete(true); directory.Delete(true);
if (removeSettings)
RemovePluginSettings(plugin);
}
public void RemovePluginSettings(Plugin plugin)
{
if (plugin.IsEnabled)
throw new ArtemisCoreException("Cannot remove the settings of an enabled plugin");
_pluginRepository.RemoveSettings(plugin.Guid);
plugin.Settings?.ClearSettings();
} }
#endregion #endregion

View File

@ -14,7 +14,8 @@ namespace Artemis.Storage.Repositories.Interfaces
PluginSettingEntity GetSettingByGuid(Guid pluginGuid); PluginSettingEntity GetSettingByGuid(Guid pluginGuid);
PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid); PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid);
void SaveSetting(PluginSettingEntity pluginSettingEntity); void SaveSetting(PluginSettingEntity pluginSettingEntity);
void RemoveSettings(Guid pluginGuid);
void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity); void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
List<PluginQueuedActionEntity> GetQueuedActions(); List<PluginQueuedActionEntity> GetQueuedActions();
List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid); List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid);

View File

@ -54,6 +54,12 @@ namespace Artemis.Storage.Repositories
_repository.Upsert(pluginSettingEntity); _repository.Upsert(pluginSettingEntity);
} }
/// <inheritdoc />
public void RemoveSettings(Guid pluginGuid)
{
_repository.DeleteMany<PluginSettingEntity>(s => s.PluginGuid == pluginGuid);
}
public List<PluginQueuedActionEntity> GetQueuedActions() public List<PluginQueuedActionEntity> GetQueuedActions()
{ {
return _repository.Query<PluginQueuedActionEntity>().ToList(); return _repository.Query<PluginQueuedActionEntity>().ToList();

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Utilities; using Artemis.UI.Utilities;
using Ninject;
using Stylet; using Stylet;
namespace Artemis.UI namespace Artemis.UI
@ -19,13 +20,16 @@ namespace Artemis.UI
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released // ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
private Mutex _artemisMutex; private Mutex _artemisMutex;
public ApplicationStateManager(string[] startupArguments) public ApplicationStateManager(IKernel kernel, string[] startupArguments)
{ {
StartupArguments = startupArguments; StartupArguments = startupArguments;
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested; Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested; Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
// On Windows shutdown dispose the kernel just so device providers get a chance to clean up
Application.Current.SessionEnding += (_, _) => kernel.Dispose();
} }
public string[] StartupArguments { get; } public string[] StartupArguments { get; }

View File

@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Markup; using System.Windows.Markup;
using System.Windows.Threading; using System.Windows.Threading;
using Artemis.Core;
using Artemis.Core.Ninject; using Artemis.Core.Ninject;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject; using Artemis.UI.Ninject;
@ -14,11 +13,9 @@ using Artemis.UI.Screens;
using Artemis.UI.Services; using Artemis.UI.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.SkiaSharp;
using Artemis.UI.Stylet; using Artemis.UI.Stylet;
using Ninject; using Ninject;
using Serilog; using Serilog;
using SkiaSharp;
using Stylet; using Stylet;
namespace Artemis.UI namespace Artemis.UI
@ -39,7 +36,7 @@ namespace Artemis.UI
protected override void Launch() protected override void Launch()
{ {
_applicationStateManager = new ApplicationStateManager(Args); _applicationStateManager = new ApplicationStateManager(Kernel, Args);
Core.Utilities.PrepareFirstLaunch(); Core.Utilities.PrepareFirstLaunch();
ILogger logger = Kernel.Get<ILogger>(); ILogger logger = Kernel.Get<ILogger>();
@ -94,10 +91,7 @@ namespace Artemis.UI
registrationService.RegisterInputProvider(); registrationService.RegisterInputProvider();
registrationService.RegisterControllers(); registrationService.RegisterControllers();
Execute.OnUIThreadSync(() => Execute.OnUIThreadSync(() => { registrationService.ApplyPreferredGraphicsContext(); });
{
registrationService.ApplyPreferredGraphicsContext();
});
// Initialize background services // Initialize background services
Kernel.Get<IDeviceLayoutService>(); Kernel.Get<IDeviceLayoutService>();

View File

@ -17,8 +17,8 @@
<Grid Margin="-3 -8"> <Grid Margin="-3 -8">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="30" /> <ColumnDefinition Width="30" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Icon column --> <!-- Icon column -->
@ -42,15 +42,20 @@
</Button> </Button>
<!-- Display name column --> <!-- Display name column -->
<TextBlock Grid.Column="1" Text="{Binding FeatureInfo.Name}" Style="{StaticResource MaterialDesignTextBlock}" VerticalAlignment="Center" ToolTip="{Binding FeatureInfo.Description}" /> <TextBlock Grid.Column="1"
Text="{Binding FeatureInfo.Name}"
Style="{StaticResource MaterialDesignTextBlock}"
VerticalAlignment="Center"
TextWrapping="Wrap"
ToolTip="{Binding FeatureInfo.Description}" />
<!-- Enable toggle column --> <!-- Enable toggle column -->
<StackPanel Grid.Column="2" <StackPanel Grid.Column="2"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="8" Margin="8"
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}" Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
Orientation="Horizontal" Orientation="Horizontal"
VerticalAlignment="Top"
ToolTip="This feature cannot be disabled without disabling the whole plugin" ToolTip="This feature cannot be disabled without disabling the whole plugin"
ToolTipService.IsEnabled="{Binding FeatureInfo.AlwaysEnabled}"> ToolTipService.IsEnabled="{Binding FeatureInfo.AlwaysEnabled}">
<materialDesign:PackIcon Kind="ShieldHalfFull" <materialDesign:PackIcon Kind="ShieldHalfFull"

View File

@ -14,7 +14,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="0 15" Width="810"> <Grid Margin="0 15" Width="910">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />

View File

@ -12,7 +12,7 @@
d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}" d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<materialDesign:Card Width="800"> <materialDesign:Card Width="900">
<Grid Margin="8"> <Grid Margin="8">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -47,7 +47,7 @@
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}" behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Name}" behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Name}"
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}" behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"/> behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}" />
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
Grid.Row="1" Grid.Row="1"
@ -67,12 +67,20 @@
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Style="{StaticResource MaterialDesignOutlinedButton}" Style="{StaticResource MaterialDesignRaisedButton}"
ToolTip="Open the plugins settings window" ToolTip="Open the plugins settings window"
Margin="4" Margin="4"
Command="{s:Action OpenSettings}"> Command="{s:Action OpenSettings}">
SETTINGS SETTINGS
</Button> </Button>
<Button
VerticalAlignment="Bottom"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="Clear plugin settings"
Margin="4"
Command="{s:Action RemoveSettings}">
<materialDesign:PackIcon Kind="DatabaseRemove" />
</Button>
<Button <Button
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Style="{StaticResource MaterialDesignOutlinedButton}" Style="{StaticResource MaterialDesignOutlinedButton}"

View File

@ -85,6 +85,25 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
} }
} }
public async Task RemoveSettings()
{
bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
if (!confirmed)
return;
bool wasEnabled = IsEnabled;
if (IsEnabled)
await UpdateEnabled(false);
_pluginManagementService.RemovePluginSettings(Plugin);
if (wasEnabled)
await UpdateEnabled(true);
_messageService.ShowMessage("Cleared plugin settings.");
}
public async Task Remove() public async Task Remove()
{ {
bool confirmed = await _dialogService.ShowConfirmDialog("Delete plugin", "Are you sure you want to delete this plugin?"); bool confirmed = await _dialogService.ShowConfirmDialog("Delete plugin", "Are you sure you want to delete this plugin?");
@ -93,7 +112,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
try try
{ {
_pluginManagementService.RemovePlugin(Plugin); _pluginManagementService.RemovePlugin(Plugin, false);
((PluginSettingsTabViewModel) Parent).GetPluginInstances(); ((PluginSettingsTabViewModel) Parent).GetPluginInstances();
} }
catch (Exception e) catch (Exception e)
@ -101,6 +120,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
_dialogService.ShowExceptionDialog("Failed to remove plugin", e); _dialogService.ShowExceptionDialog("Failed to remove plugin", e);
throw; throw;
} }
_messageService.ShowMessage("Removed plugin.");
} }
public void ShowLogsFolder() public void ShowLogsFolder()