mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
4152c290d2
@ -17,6 +17,7 @@ namespace Artemis.Core
|
|||||||
private DataModel? _dynamicDataModel;
|
private DataModel? _dynamicDataModel;
|
||||||
private Type? _dynamicDataModelType;
|
private Type? _dynamicDataModelType;
|
||||||
private DataModelPropertyAttribute? _dynamicDataModelAttribute;
|
private DataModelPropertyAttribute? _dynamicDataModelAttribute;
|
||||||
|
private PropertyInfo? _property;
|
||||||
|
|
||||||
internal DataModelPathSegment(DataModelPath dataModelPath, string identifier, string path)
|
internal DataModelPathSegment(DataModelPath dataModelPath, string identifier, string path)
|
||||||
{
|
{
|
||||||
@ -99,7 +100,7 @@ namespace Artemis.Core
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// If this is not the first segment in a path, the property is located on the previous segment
|
// If this is not the first segment in a path, the property is located on the previous segment
|
||||||
return Previous?.GetPropertyType()?.GetProperty(Identifier);
|
return Previous?.GetPropertyType()?.GetProperties().FirstOrDefault(p => p.Name == Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -209,14 +210,18 @@ namespace Artemis.Core
|
|||||||
accessorExpression = expression;
|
accessorExpression = expression;
|
||||||
// A static segment just needs to access the property or filed
|
// A static segment just needs to access the property or filed
|
||||||
else if (Type == DataModelPathSegmentType.Static)
|
else if (Type == DataModelPathSegmentType.Static)
|
||||||
accessorExpression = Expression.PropertyOrField(expression, Identifier);
|
{
|
||||||
|
accessorExpression = _property != null
|
||||||
|
? Expression.Property(expression, _property)
|
||||||
|
: Expression.PropertyOrField(expression, Identifier);
|
||||||
|
}
|
||||||
// A dynamic segment calls the generic method DataModel.DynamicChild<T> and provides the identifier as an argument
|
// A dynamic segment calls the generic method DataModel.DynamicChild<T> and provides the identifier as an argument
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
accessorExpression = Expression.Call(
|
accessorExpression = Expression.Call(
|
||||||
expression,
|
expression,
|
||||||
nameof(DataModel.GetDynamicChildValue),
|
nameof(DataModel.GetDynamicChildValue),
|
||||||
_dynamicDataModelType != null ? new[] { _dynamicDataModelType } : null,
|
_dynamicDataModelType != null ? new[] {_dynamicDataModelType} : null,
|
||||||
Expression.Constant(Identifier)
|
Expression.Constant(Identifier)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -243,8 +248,11 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void DetermineStaticType(Type previousType)
|
private void DetermineStaticType(Type previousType)
|
||||||
{
|
{
|
||||||
PropertyInfo? property = previousType.GetProperty(Identifier, BindingFlags.Public | BindingFlags.Instance);
|
// Situations in which AmbiguousMatchException occurs ...
|
||||||
Type = property == null ? DataModelPathSegmentType.Invalid : DataModelPathSegmentType.Static;
|
//
|
||||||
|
// ...derived type declares a property that hides an inherited property with the same name, by using the new modifier
|
||||||
|
_property = previousType.GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(p => p.Name == Identifier);
|
||||||
|
Type = _property == null ? DataModelPathSegmentType.Invalid : DataModelPathSegmentType.Static;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
|
|||||||
@ -33,6 +33,8 @@ namespace Artemis.Core
|
|||||||
BlueScale = 1;
|
BlueScale = 1;
|
||||||
IsEnabled = true;
|
IsEnabled = true;
|
||||||
|
|
||||||
|
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(new Dictionary<LedId, ArtemisLed>());
|
||||||
|
Leds = new ReadOnlyCollection<ArtemisLed>(new List<ArtemisLed>());
|
||||||
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
||||||
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
||||||
Categories = new HashSet<DeviceCategory>();
|
Categories = new HashSet<DeviceCategory>();
|
||||||
@ -51,6 +53,8 @@ namespace Artemis.Core
|
|||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
|
|
||||||
|
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(new Dictionary<LedId, ArtemisLed>());
|
||||||
|
Leds = new ReadOnlyCollection<ArtemisLed>(new List<ArtemisLed>());
|
||||||
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
||||||
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
||||||
Categories = new HashSet<DeviceCategory>();
|
Categories = new HashSet<DeviceCategory>();
|
||||||
@ -258,6 +262,19 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a boolean indicating whether falling back to default layouts is enabled or not
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableDefaultLayout
|
||||||
|
{
|
||||||
|
get => DeviceEntity.DisableDefaultLayout;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
DeviceEntity.DisableDefaultLayout = value;
|
||||||
|
OnPropertyChanged(nameof(DisableDefaultLayout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the logical layout of the device e.g. DE, UK or US.
|
/// Gets or sets the logical layout of the device e.g. DE, UK or US.
|
||||||
/// <para>Only applicable to keyboards</para>
|
/// <para>Only applicable to keyboards</para>
|
||||||
@ -532,6 +549,11 @@ namespace Artemis.Core
|
|||||||
else
|
else
|
||||||
LogicalLayout = DeviceEntity.LogicalLayout;
|
LogicalLayout = DeviceEntity.LogicalLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ClearLayout()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -22,11 +22,6 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract string Description { get; }
|
public abstract string Description { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// [NYI] Gets a boolean indicating whether installing or uninstalling this prerequisite requires admin privileges
|
|
||||||
/// </summary>
|
|
||||||
public abstract bool RequiresElevation { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of actions to execute when <see cref="Install" /> is called
|
/// Gets a list of actions to execute when <see cref="Install" /> is called
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace Artemis.Core
|
|||||||
/// <param name="fileName">The target file to execute</param>
|
/// <param name="fileName">The target file to execute</param>
|
||||||
/// <param name="arguments">A set of command-line arguments to use when starting the application</param>
|
/// <param name="arguments">A set of command-line arguments to use when starting the application</param>
|
||||||
/// <param name="waitForExit">A boolean indicating whether the action should wait for the process to exit</param>
|
/// <param name="waitForExit">A boolean indicating whether the action should wait for the process to exit</param>
|
||||||
/// <param name="elevate">A boolean indicating whether the file should run with administrator privileges (does not require <see cref="PluginPrerequisite.RequiresElevation"/>)</param>
|
/// <param name="elevate">A boolean indicating whether the file should run with administrator privileges</param>
|
||||||
public ExecuteFileAction(string name, string fileName, string? arguments = null, bool waitForExit = true, bool elevate = false) : base(name)
|
public ExecuteFileAction(string name, string fileName, string? arguments = null, bool waitForExit = true, bool elevate = false) : base(name)
|
||||||
{
|
{
|
||||||
FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
|
FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
|
||||||
@ -72,7 +72,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task<int> RunProcessAsync(string fileName, string? arguments, bool elevate)
|
internal static Task<int> RunProcessAsync(string fileName, string? arguments, bool elevate)
|
||||||
{
|
{
|
||||||
TaskCompletionSource<int> tcs = new();
|
TaskCompletionSource<int> tcs = new();
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,81 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a plugin prerequisite action runs inline powershell
|
||||||
|
/// </summary>
|
||||||
|
public class RunInlinePowerShellAction : PluginPrerequisiteAction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of a copy folder action
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the action</param>
|
||||||
|
/// <param name="code">The inline code to run</param>
|
||||||
|
/// <param name="elevate">A boolean indicating whether the file should run with administrator privileges</param>
|
||||||
|
/// <param name="arguments">
|
||||||
|
/// Optional arguments to pass to your script, you are responsible for proper quoting etc.
|
||||||
|
/// <para>Arguments are available in PowerShell as <c>$args[0], $args[1]</c> etc.</para>
|
||||||
|
/// </param>
|
||||||
|
public RunInlinePowerShellAction(string name, string code, bool elevate = false, string? arguments = null) : base(name)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Elevate = elevate;
|
||||||
|
Arguments = arguments;
|
||||||
|
ProgressIndeterminate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the inline code to run
|
||||||
|
/// </summary>
|
||||||
|
public string Code { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether the file should run with administrator privileges
|
||||||
|
/// </summary>
|
||||||
|
public bool Elevate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets optional arguments to pass to your script, you are responsible for proper quoting etc.
|
||||||
|
/// <para>Arguments are available in PowerShell as <c>$args[0], $args[1]</c> etc.</para>
|
||||||
|
/// </summary>
|
||||||
|
public string? Arguments { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task Execute(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
string file = Path.GetTempFileName().Replace(".tmp", ".ps1");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string code =
|
||||||
|
@"try
|
||||||
|
{
|
||||||
|
" + Code + @"
|
||||||
|
Start-Sleep 1
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Error $_.Exception.ToString()
|
||||||
|
pause
|
||||||
|
}";
|
||||||
|
|
||||||
|
await File.WriteAllTextAsync(file, code, cancellationToken);
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
Status = "Running PowerShell script and waiting for exit..";
|
||||||
|
ShowProgressBar = true;
|
||||||
|
ProgressIndeterminate = true;
|
||||||
|
|
||||||
|
int result = await ExecuteFileAction.RunProcessAsync("powershell.exe", $"-ExecutionPolicy Unrestricted -File {file} {Arguments}", Elevate);
|
||||||
|
|
||||||
|
Status = $"PowerShell exited with code {result}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a plugin prerequisite action that runs a PowerShell script
|
||||||
|
/// <para>Note: To run an inline script instead, use <see cref="RunInlinePowerShellAction" /></para>
|
||||||
|
/// </summary>
|
||||||
|
public class RunPowerShellAction : PluginPrerequisiteAction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of a copy folder action
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the action</param>
|
||||||
|
/// <param name="scriptPath">The full path of the script to run</param>
|
||||||
|
/// <param name="elevate">A boolean indicating whether the file should run with administrator privileges</param>
|
||||||
|
/// <param name="arguments">
|
||||||
|
/// Optional arguments to pass to your script, you are responsible for proper quoting etc.
|
||||||
|
/// <para>Arguments are available in PowerShell as <c>$args[0], $args[1]</c> etc.</para>
|
||||||
|
/// </param>
|
||||||
|
public RunPowerShellAction(string name, string scriptPath, bool elevate = false, string? arguments = null) : base(name)
|
||||||
|
{
|
||||||
|
ScriptPath = scriptPath;
|
||||||
|
Elevate = elevate;
|
||||||
|
Arguments = arguments;
|
||||||
|
ProgressIndeterminate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the inline full path of the script to run
|
||||||
|
/// </summary>
|
||||||
|
public string ScriptPath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether the file should run with administrator privileges
|
||||||
|
/// </summary>
|
||||||
|
public bool Elevate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets optional arguments to pass to your script, you are responsible for proper quoting etc.
|
||||||
|
/// <para>Arguments are available in PowerShell as <c>$args[0], $args[1]</c> etc.</para>
|
||||||
|
/// </summary>
|
||||||
|
public string? Arguments { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task Execute(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!ScriptPath.EndsWith(".ps1"))
|
||||||
|
throw new ArtemisPluginException($"Script at path {ScriptPath} must have the .ps1 extension or PowerShell will refuse to run it");
|
||||||
|
if (!File.Exists(ScriptPath))
|
||||||
|
throw new ArtemisCoreException($"Script not found at path {ScriptPath}");
|
||||||
|
|
||||||
|
Status = "Running PowerShell script and waiting for exit..";
|
||||||
|
ShowProgressBar = true;
|
||||||
|
ProgressIndeterminate = true;
|
||||||
|
|
||||||
|
int result = await ExecuteFileAction.RunProcessAsync("powershell.exe", $"-ExecutionPolicy Unrestricted -File {ScriptPath} {Arguments}", Elevate);
|
||||||
|
|
||||||
|
Status = $"PowerShell exited with code {result}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -135,11 +135,16 @@ namespace Artemis.Core.Services
|
|||||||
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
|
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queues an action for the provided plugin for the next time Artemis starts, before plugins are loaded
|
/// Queues the provided plugin to be deleted the next time Artemis starts, before plugins are loaded
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="plugin">The plugin to queue the action for</param>
|
/// <param name="plugin">The plugin to delete</param>
|
||||||
/// <param name="pluginAction">The action to take</param>
|
void QueuePluginDeletion(Plugin plugin);
|
||||||
void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction);
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the provided plugin for the deletion queue it was added to via <see cref="QueuePluginDeletion" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plugin">The plugin to dequeue</param>
|
||||||
|
void DequeuePluginDeletion(Plugin plugin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when built-in plugins are being loaded
|
/// Occurs when built-in plugins are being loaded
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
|
using Artemis.Storage.Entities.General;
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using McMaster.NETCore.Plugins;
|
using McMaster.NETCore.Plugins;
|
||||||
@ -26,17 +27,19 @@ namespace Artemis.Core.Services
|
|||||||
private readonly IKernel _kernel;
|
private readonly IKernel _kernel;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IPluginRepository _pluginRepository;
|
private readonly IPluginRepository _pluginRepository;
|
||||||
|
private readonly IQueuedActionRepository _queuedActionRepository;
|
||||||
private readonly List<Plugin> _plugins;
|
private readonly List<Plugin> _plugins;
|
||||||
private bool _isElevated;
|
private bool _isElevated;
|
||||||
|
|
||||||
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
|
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository, IQueuedActionRepository queuedActionRepository)
|
||||||
{
|
{
|
||||||
_kernel = kernel;
|
_kernel = kernel;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_pluginRepository = pluginRepository;
|
_pluginRepository = pluginRepository;
|
||||||
|
_queuedActionRepository = queuedActionRepository;
|
||||||
_plugins = new List<Plugin>();
|
_plugins = new List<Plugin>();
|
||||||
|
|
||||||
ProcessQueuedActions();
|
ProcessPluginDeletionQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyBuiltInPlugin(ZipArchive zipArchive, string targetDirectory)
|
private void CopyBuiltInPlugin(ZipArchive zipArchive, string targetDirectory)
|
||||||
@ -664,27 +667,51 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
#region Queued actions
|
#region Queued actions
|
||||||
|
|
||||||
public void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction)
|
public void QueuePluginDeletion(Plugin plugin)
|
||||||
{
|
{
|
||||||
List<PluginQueuedActionEntity> existing = _pluginRepository.GetQueuedActions(plugin.Guid);
|
_queuedActionRepository.Add(new QueuedActionEntity
|
||||||
if (existing.Any(e => pluginAction == PluginManagementAction.Delete && e is PluginQueuedDeleteEntity))
|
{
|
||||||
return;
|
Type = "DeletePlugin",
|
||||||
|
CreatedAt = DateTimeOffset.Now,
|
||||||
if (pluginAction == PluginManagementAction.Delete)
|
Parameters = new Dictionary<string, object>()
|
||||||
_pluginRepository.AddQueuedAction(new PluginQueuedDeleteEntity {PluginGuid = plugin.Guid, Directory = plugin.Directory.FullName});
|
{
|
||||||
|
{"pluginGuid", plugin.Guid.ToString()},
|
||||||
|
{"plugin", plugin.ToString()},
|
||||||
|
{"directory", plugin.Directory.FullName}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessQueuedActions()
|
public void DequeuePluginDeletion(Plugin plugin)
|
||||||
{
|
{
|
||||||
foreach (PluginQueuedActionEntity pluginQueuedActionEntity in _pluginRepository.GetQueuedActions())
|
QueuedActionEntity? queuedActionEntity = _queuedActionRepository.GetByType("DeletePlugin").FirstOrDefault(q => q.Parameters["pluginGuid"].Equals(plugin.Guid.ToString()));
|
||||||
{
|
if (queuedActionEntity != null)
|
||||||
if (pluginQueuedActionEntity is PluginQueuedDeleteEntity deleteAction)
|
_queuedActionRepository.Remove(queuedActionEntity);
|
||||||
{
|
}
|
||||||
if (Directory.Exists(deleteAction.Directory))
|
|
||||||
Directory.Delete(deleteAction.Directory, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
_pluginRepository.RemoveQueuedAction(pluginQueuedActionEntity);
|
private void ProcessPluginDeletionQueue()
|
||||||
|
{
|
||||||
|
foreach (QueuedActionEntity queuedActionEntity in _queuedActionRepository.GetByType("DeletePlugin"))
|
||||||
|
{
|
||||||
|
string? directory = queuedActionEntity.Parameters["directory"].ToString();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
_logger.Information("Queued plugin deletion - deleting folder - {plugin}", queuedActionEntity.Parameters["plugin"]);
|
||||||
|
Directory.Delete(directory!, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Information("Queued plugin deletion - folder already deleted - {plugin}", queuedActionEntity.Parameters["plugin"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_queuedActionRepository.Remove(queuedActionEntity);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Warning(e, "Queued plugin deletion failed - {plugin}", queuedActionEntity.Parameters["plugin"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -354,19 +354,26 @@ namespace Artemis.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally fall back to a default layout
|
// Finally fall back to a default layout
|
||||||
layout = ArtemisLayout.GetDefaultLayout(device);
|
if (!device.DisableDefaultLayout)
|
||||||
if (layout != null)
|
layout = ArtemisLayout.GetDefaultLayout(device);
|
||||||
ApplyDeviceLayout(device, layout);
|
ApplyDeviceLayout(device, layout);
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout)
|
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout)
|
||||||
{
|
{
|
||||||
|
if (layout == null)
|
||||||
|
{
|
||||||
|
if (device.Layout != null)
|
||||||
|
device.ClearLayout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (layout.Source == LayoutSource.Default)
|
if (layout.Source == LayoutSource.Default)
|
||||||
device.ApplyLayout(layout, false, false);
|
device.ApplyLayout(layout, false, false);
|
||||||
else
|
else
|
||||||
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
||||||
|
|
||||||
UpdateLedGroup();
|
UpdateLedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
src/Artemis.Storage/Entities/General/QueuedActionEntity.cs
Normal file
19
src/Artemis.Storage/Entities/General/QueuedActionEntity.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.General
|
||||||
|
{
|
||||||
|
public class QueuedActionEntity
|
||||||
|
{
|
||||||
|
public QueuedActionEntity()
|
||||||
|
{
|
||||||
|
Parameters = new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, object> Parameters { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,21 +13,4 @@ namespace Artemis.Storage.Entities.Plugins
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a queued action for a plugin
|
|
||||||
/// </summary>
|
|
||||||
public abstract class PluginQueuedActionEntity
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public Guid PluginGuid { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a queued delete action for a plugin
|
|
||||||
/// </summary>
|
|
||||||
public class PluginQueuedDeleteEntity : PluginQueuedActionEntity
|
|
||||||
{
|
|
||||||
public string Directory { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -22,6 +22,7 @@ namespace Artemis.Storage.Entities.Surface
|
|||||||
public float BlueScale { get; set; }
|
public float BlueScale { get; set; }
|
||||||
public bool IsEnabled { get; set; }
|
public bool IsEnabled { get; set; }
|
||||||
|
|
||||||
|
public bool DisableDefaultLayout { get; set; }
|
||||||
public int PhysicalLayout { get; set; }
|
public int PhysicalLayout { get; set; }
|
||||||
public string LogicalLayout { get; set; }
|
public string LogicalLayout { get; set; }
|
||||||
public string CustomLayoutPath { get; set; }
|
public string CustomLayoutPath { get; set; }
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
|
|
||||||
namespace Artemis.Storage.Repositories.Interfaces
|
namespace Artemis.Storage.Repositories.Interfaces
|
||||||
@ -9,16 +8,11 @@ namespace Artemis.Storage.Repositories.Interfaces
|
|||||||
void AddPlugin(PluginEntity pluginEntity);
|
void AddPlugin(PluginEntity pluginEntity);
|
||||||
PluginEntity GetPluginByGuid(Guid pluginGuid);
|
PluginEntity GetPluginByGuid(Guid pluginGuid);
|
||||||
void SavePlugin(PluginEntity pluginEntity);
|
void SavePlugin(PluginEntity pluginEntity);
|
||||||
|
|
||||||
void AddSetting(PluginSettingEntity pluginSettingEntity);
|
void AddSetting(PluginSettingEntity pluginSettingEntity);
|
||||||
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 RemoveSettings(Guid pluginGuid);
|
||||||
|
|
||||||
void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
|
|
||||||
List<PluginQueuedActionEntity> GetQueuedActions();
|
|
||||||
List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid);
|
|
||||||
void RemoveQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Storage.Entities.General;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Repositories.Interfaces
|
||||||
|
{
|
||||||
|
public interface IQueuedActionRepository : IRepository
|
||||||
|
{
|
||||||
|
void Add(QueuedActionEntity queuedActionEntity);
|
||||||
|
void Remove(QueuedActionEntity queuedActionEntity);
|
||||||
|
List<QueuedActionEntity> GetAll();
|
||||||
|
List<QueuedActionEntity> GetByType(string type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Artemis.Storage.Entities.Module;
|
using Artemis.Storage.Entities.Module;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|||||||
@ -15,7 +15,6 @@ namespace Artemis.Storage.Repositories
|
|||||||
_repository = repository;
|
_repository = repository;
|
||||||
|
|
||||||
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => new {s.Name, s.PluginGuid}, true);
|
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => new {s.Name, s.PluginGuid}, true);
|
||||||
_repository.Database.GetCollection<PluginQueuedActionEntity>().EnsureIndex(s => s.PluginGuid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPlugin(PluginEntity pluginEntity)
|
public void AddPlugin(PluginEntity pluginEntity)
|
||||||
@ -59,24 +58,5 @@ namespace Artemis.Storage.Repositories
|
|||||||
{
|
{
|
||||||
_repository.DeleteMany<PluginSettingEntity>(s => s.PluginGuid == pluginGuid);
|
_repository.DeleteMany<PluginSettingEntity>(s => s.PluginGuid == pluginGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PluginQueuedActionEntity> GetQueuedActions()
|
|
||||||
{
|
|
||||||
return _repository.Query<PluginQueuedActionEntity>().ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid)
|
|
||||||
{
|
|
||||||
return _repository.Query<PluginQueuedActionEntity>().Where(q => q.PluginGuid == pluginGuid).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity)
|
|
||||||
{
|
|
||||||
_repository.Upsert(pluginQueuedActionEntity);
|
|
||||||
}
|
|
||||||
public void RemoveQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity)
|
|
||||||
{
|
|
||||||
_repository.Delete<PluginQueuedActionEntity>(pluginQueuedActionEntity.Id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
46
src/Artemis.Storage/Repositories/QueuedActionRepository.cs
Normal file
46
src/Artemis.Storage/Repositories/QueuedActionRepository.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Storage.Entities.General;
|
||||||
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Repositories
|
||||||
|
{
|
||||||
|
public class QueuedActionRepository : IQueuedActionRepository
|
||||||
|
{
|
||||||
|
private readonly LiteRepository _repository;
|
||||||
|
|
||||||
|
public QueuedActionRepository(LiteRepository repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
_repository.Database.GetCollection<QueuedActionEntity>().EnsureIndex(s => s.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IQueuedActionRepository
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Add(QueuedActionEntity queuedActionEntity)
|
||||||
|
{
|
||||||
|
_repository.Insert(queuedActionEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Remove(QueuedActionEntity queuedActionEntity)
|
||||||
|
{
|
||||||
|
_repository.Delete<QueuedActionEntity>(queuedActionEntity.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public List<QueuedActionEntity> GetAll()
|
||||||
|
{
|
||||||
|
return _repository.Query<QueuedActionEntity>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public List<QueuedActionEntity> GetByType(string type)
|
||||||
|
{
|
||||||
|
return _repository.Query<QueuedActionEntity>().Where(q => q.Type == type).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/Artemis.UI.Shared/Behaviors/OpenInBrowser.cs
Normal file
41
src/Artemis.UI.Shared/Behaviors/OpenInBrowser.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Microsoft.Xaml.Behaviors;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a behavior that opens the URI of the hyperlink in the browser when requested
|
||||||
|
/// </summary>
|
||||||
|
public class OpenInBrowser : Behavior<Hyperlink>
|
||||||
|
{
|
||||||
|
private Hyperlink? _hyperLink;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnAttached()
|
||||||
|
{
|
||||||
|
base.OnAttached();
|
||||||
|
|
||||||
|
_hyperLink = AssociatedObject;
|
||||||
|
if (_hyperLink == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_hyperLink.RequestNavigate += HyperLinkOnRequestNavigate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnDetaching()
|
||||||
|
{
|
||||||
|
if (_hyperLink == null) return;
|
||||||
|
_hyperLink.RequestNavigate -= HyperLinkOnRequestNavigate;
|
||||||
|
|
||||||
|
base.OnDetaching();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HyperLinkOnRequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||||
|
{
|
||||||
|
Utilities.OpenUrl(e.Uri.AbsoluteUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,5 +14,11 @@
|
|||||||
SelectedValuePath="Value"
|
SelectedValuePath="Value"
|
||||||
DisplayMemberPath="Description"
|
DisplayMemberPath="Description"
|
||||||
SelectedValue="{Binding Path=InputValue}"
|
SelectedValue="{Binding Path=InputValue}"
|
||||||
IsDropDownOpen="True"/>
|
IsDropDownOpen="True">
|
||||||
|
<ComboBox.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ComboBox.ItemsPanel>
|
||||||
|
</ComboBox>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -13,18 +13,13 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
public class PluginPrerequisiteViewModel : Conductor<PluginPrerequisiteActionViewModel>.Collection.OneActive
|
public class PluginPrerequisiteViewModel : Conductor<PluginPrerequisiteActionViewModel>.Collection.OneActive
|
||||||
{
|
{
|
||||||
private readonly bool _uninstall;
|
private readonly bool _uninstall;
|
||||||
private readonly ICoreService _coreService;
|
|
||||||
private readonly IDialogService _dialogService;
|
|
||||||
private bool _installing;
|
private bool _installing;
|
||||||
private bool _uninstalling;
|
private bool _uninstalling;
|
||||||
private bool _isMet;
|
private bool _isMet;
|
||||||
|
|
||||||
public PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall, ICoreService coreService, IDialogService dialogService)
|
public PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall)
|
||||||
{
|
{
|
||||||
_uninstall = uninstall;
|
_uninstall = uninstall;
|
||||||
_coreService = coreService;
|
|
||||||
_dialogService = dialogService;
|
|
||||||
|
|
||||||
PluginPrerequisite = pluginPrerequisite;
|
PluginPrerequisite = pluginPrerequisite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +60,6 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
if (Busy)
|
if (Busy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (PluginPrerequisite.RequiresElevation && !_coreService.IsElevated)
|
|
||||||
{
|
|
||||||
await _dialogService.ShowConfirmDialog("Install plugin prerequisite", "This plugin prerequisite admin rights to install (restart & elevate NYI)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Installing = true;
|
Installing = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -88,12 +77,6 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
if (Busy)
|
if (Busy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (PluginPrerequisite.RequiresElevation && !_coreService.IsElevated)
|
|
||||||
{
|
|
||||||
await _dialogService.ShowConfirmDialog("Install plugin prerequisite", "This plugin prerequisite admin rights to install (restart & elevate NYI)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uninstalling = true;
|
Uninstalling = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
|
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance local:DevicePropertiesTabViewModel}">
|
d:DataContext="{d:DesignInstance local:DevicePropertiesTabViewModel}">
|
||||||
@ -29,7 +30,7 @@
|
|||||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
TextAlignment="Justify">
|
TextAlignment="Justify">
|
||||||
Artemis uses categories to determine where the layers of imported profiles are applied to. <LineBreak/>
|
Artemis uses categories to determine where the layers of imported profiles are applied to. <LineBreak />
|
||||||
You can hover over a category for a more detailed description.
|
You can hover over a category for a more detailed description.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
@ -192,15 +193,25 @@
|
|||||||
|
|
||||||
<!-- Layout -->
|
<!-- Layout -->
|
||||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
||||||
Custom layout
|
Layout
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
TextAlignment="Justify">
|
TextAlignment="Justify">
|
||||||
Select a custom layout below if you want to change the appearance and/or LEDs of this device.
|
The device layout is used to determine the position of LEDs and to create the visual representation of the device you see on the left side of this window.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
|
<CheckBox Margin="0 0 0 5" Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding UseDefaultLayout}">
|
||||||
|
<CheckBox.Content>
|
||||||
|
<TextBlock Margin="0 -5 0 0">
|
||||||
|
Use default layout if needed
|
||||||
|
<materialDesign:PackIcon Kind="HelpCircle"
|
||||||
|
ToolTip="If there is no built-in layout and no custom layout, with this enabled Artemis will fall back to the default layout of this device type." />
|
||||||
|
</TextBlock>
|
||||||
|
</CheckBox.Content>
|
||||||
|
</CheckBox>
|
||||||
|
|
||||||
<TextBox Style="{StaticResource MaterialDesignFilledTextBox}"
|
<TextBox Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||||
Text="{Binding Device.CustomLayoutPath}"
|
Text="{Binding Device.CustomLayoutPath}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@ -210,10 +221,25 @@
|
|||||||
<materialDesign:HintAssist.Hint>
|
<materialDesign:HintAssist.Hint>
|
||||||
<StackPanel Orientation="Horizontal" Margin="-2 0 0 0">
|
<StackPanel Orientation="Horizontal" Margin="-2 0 0 0">
|
||||||
<materialDesign:PackIcon Kind="Xml" Width="20" />
|
<materialDesign:PackIcon Kind="Xml" Width="20" />
|
||||||
<TextBlock>Layout path</TextBlock>
|
<TextBlock>Custom layout path</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</materialDesign:HintAssist.Hint>
|
</materialDesign:HintAssist.Hint>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||||
|
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
TextAlignment="Justify">
|
||||||
|
Select a custom layout below if you want to change the appearance and/or LEDs of this device.
|
||||||
|
For info on how to create layouts, check out
|
||||||
|
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||||
|
NavigateUri="https://wiki.artemis-rgb.com/en/guides/developer/layouts">
|
||||||
|
this wiki article
|
||||||
|
<b:Interaction.Behaviors>
|
||||||
|
<shared:OpenInBrowser />
|
||||||
|
</b:Interaction.Behaviors>
|
||||||
|
</Hyperlink>
|
||||||
|
.
|
||||||
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
|
|||||||
@ -134,6 +134,16 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
set => SetCategory(DeviceCategory.Peripherals, value);
|
set => SetCategory(DeviceCategory.Peripherals, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool UseDefaultLayout
|
||||||
|
{
|
||||||
|
get => !Device.DisableDefaultLayout;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Device.DisableDefaultLayout = !value;
|
||||||
|
NotifyOfPropertyChange(nameof(UseDefaultLayout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyScaling()
|
public void ApplyScaling()
|
||||||
{
|
{
|
||||||
Device.RedScale = RedScale / 100f;
|
Device.RedScale = RedScale / 100f;
|
||||||
@ -255,7 +265,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
|
|
||||||
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(Device.CustomLayoutPath))
|
if (e.PropertyName == nameof(Device.CustomLayoutPath) || e.PropertyName == nameof(Device.DisableDefaultLayout))
|
||||||
_rgbService.ApplyBestDeviceLayout(Device);
|
_rgbService.ApplyBestDeviceLayout(Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user