mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Plugins - Added platform support
Settings - Fixed updating issues in plugin tab UI - Don't initialize in design mode UI - Move ReactiveCoreWindow to Artemis.Shared Window service - Return window created by ShowWindow Window service - Added CreateOpenFileDialog API
This commit is contained in:
parent
e385165200
commit
75d85985a9
@ -26,6 +26,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks the feature to always be enabled as long as the plugin is enabled
|
/// Marks the feature to always be enabled as long as the plugin is enabled
|
||||||
|
/// <para>Note: always <see langword="true"/> if this is the plugin's only feature</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AlwaysEnabled { get; set; }
|
public bool AlwaysEnabled { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,10 +122,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks the feature to always be enabled as long as the plugin is enabled and cannot be disabled
|
/// Marks the feature to always be enabled as long as the plugin is enabled and cannot be disabled.
|
||||||
|
/// <para>Note: always <see langword="true"/> if this is the plugin's only feature</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public bool AlwaysEnabled { get; }
|
public bool AlwaysEnabled { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the feature is enabled in persistent storage
|
/// Gets a boolean indicating whether the feature is enabled in persistent storage
|
||||||
|
|||||||
@ -24,7 +24,7 @@ namespace Artemis.Core
|
|||||||
private Plugin _plugin = null!;
|
private Plugin _plugin = null!;
|
||||||
private Version _version = null!;
|
private Version _version = null!;
|
||||||
private bool _requiresAdmin;
|
private bool _requiresAdmin;
|
||||||
|
private PluginPlatform? _platforms;
|
||||||
|
|
||||||
internal PluginInfo()
|
internal PluginInfo()
|
||||||
{
|
{
|
||||||
@ -143,6 +143,16 @@ namespace Artemis.Core
|
|||||||
internal set => SetAndNotify(ref _requiresAdmin, value);
|
internal set => SetAndNotify(ref _requiresAdmin, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty]
|
||||||
|
public PluginPlatform? Platforms
|
||||||
|
{
|
||||||
|
get => _platforms;
|
||||||
|
internal set => _platforms = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin this info is associated with
|
/// Gets the plugin this info is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -165,6 +175,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether this plugin is compatible with the current operating system
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompatible => Platforms.MatchesCurrentOperatingSystem();
|
||||||
|
|
||||||
internal string PreferredPluginDirectory => $"{Main.Split(".dll")[0].Replace("/", "").Replace("\\", "")}-{Guid.ToString().Substring(0, 8)}";
|
internal string PreferredPluginDirectory => $"{Main.Split(".dll")[0].Replace("/", "").Replace("\\", "")}-{Guid.ToString().Substring(0, 8)}";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core;
|
namespace Artemis.Core;
|
||||||
|
|
||||||
@ -6,15 +7,36 @@ namespace Artemis.Core;
|
|||||||
/// Specifies OS platforms a plugin may support.
|
/// Specifies OS platforms a plugin may support.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
|
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||||
public enum PluginPlatform
|
public enum PluginPlatform
|
||||||
{
|
{
|
||||||
/// <summary>The Windows platform.</summary>
|
/// <summary>The Windows platform.</summary>
|
||||||
Windows = 0,
|
Windows = 1,
|
||||||
|
|
||||||
/// <summary>The Linux platform.</summary>
|
/// <summary>The Linux platform.</summary>
|
||||||
Linux = 1,
|
Linux = 2,
|
||||||
|
|
||||||
/// <summary>The OSX platform.</summary>
|
/// <summary>The OSX platform.</summary>
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
OSX = 2
|
OSX = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class PluginPlatformExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the provided platform matches the current operating system.
|
||||||
|
/// </summary>
|
||||||
|
internal static bool MatchesCurrentOperatingSystem(this PluginPlatform? platform)
|
||||||
|
{
|
||||||
|
if (platform == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
return platform.Value.HasFlag(PluginPlatform.Windows);
|
||||||
|
if (OperatingSystem.IsLinux())
|
||||||
|
return platform.Value.HasFlag(PluginPlatform.Linux);
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
return platform.Value.HasFlag(PluginPlatform.OSX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -101,16 +101,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AppliesToPlatform()
|
public bool AppliesToPlatform()
|
||||||
{
|
{
|
||||||
if (Platform == null)
|
return Platform.MatchesCurrentOperatingSystem();
|
||||||
return true;
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
return Platform.Value.HasFlag(PluginPlatform.Windows);
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
return Platform.Value.HasFlag(PluginPlatform.Linux);
|
|
||||||
if (OperatingSystem.IsMacOS())
|
|
||||||
return Platform.Value.HasFlag(PluginPlatform.OSX);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -260,7 +260,7 @@ namespace Artemis.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
|
foreach (Plugin plugin in _plugins.Where(p => p.Info.IsCompatible && p.Entity.IsEnabled))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -364,7 +364,13 @@ namespace Artemis.Core.Services
|
|||||||
// Load the enabled state and if not found, default to true
|
// Load the enabled state and if not found, default to true
|
||||||
PluginFeatureEntity featureEntity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
|
PluginFeatureEntity featureEntity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
|
||||||
new PluginFeatureEntity {IsEnabled = true, Type = featureType.FullName!};
|
new PluginFeatureEntity {IsEnabled = true, Type = featureType.FullName!};
|
||||||
plugin.AddFeature(new PluginFeatureInfo(plugin, featureType, featureEntity, (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute))));
|
PluginFeatureInfo feature = new(plugin, featureType, featureEntity, (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute)));
|
||||||
|
|
||||||
|
// If the plugin only has a single feature, it should always be enabled
|
||||||
|
if (featureTypes.Count == 1)
|
||||||
|
feature.AlwaysEnabled = true;
|
||||||
|
|
||||||
|
plugin.AddFeature(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!featureTypes.Any())
|
if (!featureTypes.Any())
|
||||||
@ -390,6 +396,9 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock)
|
public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock)
|
||||||
{
|
{
|
||||||
|
if (!plugin.Info.IsCompatible)
|
||||||
|
throw new ArtemisPluginException(plugin, $"This plugin only supports the following operating system(s): {plugin.Info.Platforms}");
|
||||||
|
|
||||||
if (plugin.Assembly == null)
|
if (plugin.Assembly == null)
|
||||||
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
|
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
|
||||||
|
|
||||||
@ -446,7 +455,18 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
// Activate features after they are all loaded
|
// Activate features after they are all loaded
|
||||||
foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && (f.EnabledInStorage || f.AlwaysEnabled)))
|
foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && (f.EnabledInStorage || f.AlwaysEnabled)))
|
||||||
EnablePluginFeature(pluginFeature.Instance!, false, !ignorePluginLock);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EnablePluginFeature(pluginFeature.Instance!, false, !ignorePluginLock);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
if (pluginFeature.AlwaysEnabled)
|
||||||
|
DisablePlugin(plugin, false);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (saveState)
|
if (saveState)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Linux.Providers.Input;
|
using Artemis.UI.Linux.Providers.Input;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
@ -26,6 +27,9 @@ namespace Artemis.UI.Linux
|
|||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
return;
|
||||||
|
|
||||||
ArtemisBootstrapper.Initialize();
|
ArtemisBootstrapper.Initialize();
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args);
|
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
@ -20,6 +21,9 @@ namespace Artemis.UI.MacOS
|
|||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
return;
|
||||||
|
|
||||||
ArtemisBootstrapper.Initialize();
|
ArtemisBootstrapper.Initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ApplicationIcon/>
|
<ApplicationIcon />
|
||||||
<StartupObject/>
|
<StartupObject />
|
||||||
<OutputPath>bin\</OutputPath>
|
<OutputPath>bin\</OutputPath>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
@ -15,20 +15,20 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14"/>
|
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14" />
|
||||||
<PackageReference Include="DynamicData" Version="7.8.6"/>
|
<PackageReference Include="DynamicData" Version="7.8.6" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1"/>
|
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2"/>
|
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1"/>
|
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||||
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease.32"/>
|
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease.32" />
|
||||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1"/>
|
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Controls\HotkeyBox.axaml.cs">
|
<Compile Update="Controls\HotkeyBox.axaml.cs">
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using Avalonia.Controls;
|
|||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI
|
namespace Artemis.UI.Shared
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A ReactiveUI <see cref="Window" /> that implements the <see cref="IViewFor{TViewModel}" /> interface and will
|
/// A ReactiveUI <see cref="Window" /> that implements the <see cref="IViewFor{TViewModel}" /> interface and will
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.Builders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a builder that can create a <see cref="OpenFolderDialog" />.
|
||||||
|
/// </summary>
|
||||||
|
public class OpenFolderDialogBuilder
|
||||||
|
{
|
||||||
|
private readonly OpenFolderDialog _openFolderDialog;
|
||||||
|
private readonly Window _parent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenFolderDialogBuilder" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent window that will host the dialog.</param>
|
||||||
|
internal OpenFolderDialogBuilder(Window parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
_openFolderDialog = new OpenFolderDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the title of the dialog
|
||||||
|
/// </summary>
|
||||||
|
public OpenFolderDialogBuilder WithTitle(string? title)
|
||||||
|
{
|
||||||
|
_openFolderDialog.Title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the initial directory of the dialog
|
||||||
|
/// </summary>
|
||||||
|
public OpenFolderDialogBuilder WithDirectory(string? directory)
|
||||||
|
{
|
||||||
|
_openFolderDialog.Directory = directory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously shows the folder dialog.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that on completion returns an array containing the full path to the selected
|
||||||
|
/// folder, or null if the dialog was canceled.
|
||||||
|
/// </returns>
|
||||||
|
public async Task<string?> ShowAsync()
|
||||||
|
{
|
||||||
|
return await _openFolderDialog.ShowAsync(_parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@ namespace Artemis.UI.Shared.Services
|
|||||||
/// Given a ViewModel, show its corresponding View as a window
|
/// Given a ViewModel, show its corresponding View as a window
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewModel">ViewModel to show the View for</param>
|
/// <param name="viewModel">ViewModel to show the View for</param>
|
||||||
void ShowWindow(object viewModel);
|
Window ShowWindow(object viewModel);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows a dialog displaying the given exception
|
/// Shows a dialog displaying the given exception
|
||||||
@ -61,6 +61,12 @@ namespace Artemis.UI.Shared.Services
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
Task<bool> ShowConfirmContentDialog(string title, string message, string confirm = "Confirm", string? cancel = "Cancel");
|
Task<bool> ShowConfirmContentDialog(string title, string message, string confirm = "Confirm", string? cancel = "Cancel");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an open folder dialog, use the fluent API to configure it
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The builder that can be used to configure the dialog</returns>
|
||||||
|
OpenFolderDialogBuilder CreateOpenFolderDialog();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an open file dialog, use the fluent API to configure it
|
/// Creates an open file dialog, use the fluent API to configure it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -30,7 +30,7 @@ namespace Artemis.UI.Shared.Services
|
|||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowWindow(object viewModel)
|
public Window ShowWindow(object viewModel)
|
||||||
{
|
{
|
||||||
Window? parent = GetCurrentWindow();
|
Window? parent = GetCurrentWindow();
|
||||||
|
|
||||||
@ -49,6 +49,8 @@ namespace Artemis.UI.Shared.Services
|
|||||||
window.Show(parent);
|
window.Show(parent);
|
||||||
else
|
else
|
||||||
window.Show();
|
window.Show();
|
||||||
|
|
||||||
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
||||||
@ -86,7 +88,6 @@ namespace Artemis.UI.Shared.Services
|
|||||||
Window window = (Window) Activator.CreateInstance(type)!;
|
Window window = (Window) Activator.CreateInstance(type)!;
|
||||||
window.DataContext = viewModel;
|
window.DataContext = viewModel;
|
||||||
viewModel.CloseRequested += (_, args) => window.Close(args.Result);
|
viewModel.CloseRequested += (_, args) => window.Close(args.Result);
|
||||||
viewModel.CancelRequested += (_, _) => window.Close();
|
|
||||||
|
|
||||||
return await window.ShowDialog<TResult>(parent);
|
return await window.ShowDialog<TResult>(parent);
|
||||||
}
|
}
|
||||||
@ -119,6 +120,14 @@ namespace Artemis.UI.Shared.Services
|
|||||||
return new ContentDialogBuilder(_kernel, currentWindow);
|
return new ContentDialogBuilder(_kernel, currentWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OpenFolderDialogBuilder CreateOpenFolderDialog()
|
||||||
|
{
|
||||||
|
Window? currentWindow = GetCurrentWindow();
|
||||||
|
if (currentWindow == null)
|
||||||
|
throw new ArtemisSharedUIException("Can't show an open folder dialog without any windows being shown.");
|
||||||
|
return new OpenFolderDialogBuilder(currentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
public OpenFileDialogBuilder CreateOpenFileDialog()
|
public OpenFileDialogBuilder CreateOpenFileDialog()
|
||||||
{
|
{
|
||||||
Window? currentWindow = GetCurrentWindow();
|
Window? currentWindow = GetCurrentWindow();
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
<Setter Property="MaxHeight" Value="0" />
|
<Setter Property="MaxHeight" Value="0" />
|
||||||
</KeyFrame>
|
</KeyFrame>
|
||||||
<KeyFrame Cue="100%">
|
<KeyFrame Cue="100%">
|
||||||
<Setter Property="MaxHeight" Value="50" />
|
<Setter Property="MaxHeight" Value="600" />
|
||||||
</KeyFrame>
|
</KeyFrame>
|
||||||
</Animation>
|
</Animation>
|
||||||
</Style.Animations>
|
</Style.Animations>
|
||||||
|
|||||||
@ -52,16 +52,7 @@ public abstract class DialogViewModelBase<TResult> : ValidatableViewModelBase
|
|||||||
CloseRequested?.Invoke(this, new DialogClosedEventArgs<TResult>(result));
|
CloseRequested?.Invoke(this, new DialogClosedEventArgs<TResult>(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes the dialog without a result
|
|
||||||
/// </summary>
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
CancelRequested?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal event EventHandler<DialogClosedEventArgs<TResult>>? CloseRequested;
|
internal event EventHandler<DialogClosedEventArgs<TResult>>? CloseRequested;
|
||||||
internal event EventHandler? CancelRequested;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ using Artemis.Core.Services;
|
|||||||
using Artemis.UI.Windows.Ninject;
|
using Artemis.UI.Windows.Ninject;
|
||||||
using Artemis.UI.Windows.Providers.Input;
|
using Artemis.UI.Windows.Providers.Input;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
@ -22,7 +23,7 @@ namespace Artemis.UI.Windows
|
|||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ArtemisBootstrapper.Initialize();
|
ArtemisBootstrapper.Initialize();
|
||||||
|
|||||||
@ -8,44 +8,44 @@
|
|||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**"/>
|
<AvaloniaResource Include="Assets\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Artemis.UI.Avalonia.csproj.DotSettings"/>
|
<None Remove="Artemis.UI.Avalonia.csproj.DotSettings" />
|
||||||
<None Remove="Artemis.UI.csproj.DotSettings"/>
|
<None Remove="Artemis.UI.csproj.DotSettings" />
|
||||||
<None Remove="Assets\Images\Logo\application.ico"/>
|
<None Remove="Assets\Images\Logo\application.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="10.14.0"/>
|
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="10.14.0" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
|
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14"/>
|
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14" />
|
||||||
<PackageReference Include="DynamicData" Version="7.8.6"/>
|
<PackageReference Include="DynamicData" Version="7.8.6" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1"/>
|
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.4"/>
|
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||||
<PackageReference Include="Live.Avalonia" Version="1.3.1"/>
|
<PackageReference Include="Live.Avalonia" Version="1.3.1" />
|
||||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2"/>
|
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1"/>
|
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||||
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease.32"/>
|
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease.32" />
|
||||||
<PackageReference Include="RGB.NET.Layout" Version="1.0.0-prerelease.32"/>
|
<PackageReference Include="RGB.NET.Layout" Version="1.0.0-prerelease.32" />
|
||||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1"/>
|
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
|
||||||
<PackageReference Include="Splat.Ninject" Version="14.1.45"/>
|
<PackageReference Include="Splat.Ninject" Version="14.1.45" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj"/>
|
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
||||||
<ProjectReference Include="..\Artemis.VisualScripting\Artemis.VisualScripting.csproj"/>
|
<ProjectReference Include="..\Artemis.VisualScripting\Artemis.VisualScripting.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Assets\Images\Logo\bow-white.ico"/>
|
<Content Include="Assets\Images\Logo\bow-white.ico" />
|
||||||
<Content Include="Assets\Images\Logo\bow.ico"/>
|
<Content Include="Assets\Images\Logo\bow.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Assets\Images\Logo\bow-black.ico"/>
|
<Resource Include="Assets\Images\Logo\bow-black.ico" />
|
||||||
<Resource Include="Assets\Images\Logo\bow-white.ico"/>
|
<Resource Include="Assets\Images\Logo\bow-white.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="DefaultTypes\PropertyInput\StringPropertyInputView.axaml.cs">
|
<Compile Update="DefaultTypes\PropertyInput\StringPropertyInputView.axaml.cs">
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.UI.Screens.Root;
|
using Artemis.UI.Screens.Root;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Shapes;
|
using Avalonia.Controls.Shapes;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Events;
|
using Artemis.UI.Shared.Events;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|||||||
@ -4,8 +4,10 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:plugins="clr-namespace:Artemis.UI.Screens.Plugins"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Plugins.PluginFeatureView">
|
x:Class="Artemis.UI.Screens.Plugins.PluginFeatureView"
|
||||||
|
x:DataType="plugins:PluginFeatureViewModel">
|
||||||
<Grid ColumnDefinitions="30,*,Auto">
|
<Grid ColumnDefinitions="30,*,Auto">
|
||||||
<Grid.ContextFlyout>
|
<Grid.ContextFlyout>
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
@ -24,14 +26,15 @@
|
|||||||
|
|
||||||
<!-- Icon column -->
|
<!-- Icon column -->
|
||||||
<shared:ArtemisIcon Grid.Column="0"
|
<shared:ArtemisIcon Grid.Column="0"
|
||||||
Icon="{Binding FeatureInfo.ResolvedIcon}"
|
Icon="{CompiledBinding FeatureInfo.ResolvedIcon}"
|
||||||
|
Fill="False"
|
||||||
Width="20"
|
Width="20"
|
||||||
Height="20"
|
Height="20"
|
||||||
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
|
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
|
||||||
|
|
||||||
<Button Grid.Column="0"
|
<Button Grid.Column="0"
|
||||||
Classes="AppBarButton icon-button"
|
Classes="AppBarButton icon-button"
|
||||||
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
|
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
Foreground="#E74C4C"
|
Foreground="#E74C4C"
|
||||||
ToolTip.Tip="An exception occurred while enabling this feature, click to view"
|
ToolTip.Tip="An exception occurred while enabling this feature, click to view"
|
||||||
Command="{Binding ViewLoadException}">
|
Command="{Binding ViewLoadException}">
|
||||||
@ -40,15 +43,15 @@
|
|||||||
|
|
||||||
<!-- Display name column -->
|
<!-- Display name column -->
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
Text="{Binding FeatureInfo.Name}"
|
Text="{CompiledBinding FeatureInfo.Name}"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
ToolTip.Tip="{Binding FeatureInfo.Description}" />
|
ToolTip.Tip="{CompiledBinding FeatureInfo.Description}" />
|
||||||
|
|
||||||
<!-- Enable toggle column -->
|
<!-- Enable toggle column -->
|
||||||
<StackPanel Grid.Column="2"
|
<StackPanel Grid.Column="2"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
IsVisible="{Binding !Enabling}"
|
IsVisible="{CompiledBinding !Enabling}"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
ToolTip.Tip="This feature cannot be disabled without disabling the whole plugin">
|
ToolTip.Tip="This feature cannot be disabled without disabling the whole plugin">
|
||||||
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
||||||
@ -57,12 +60,12 @@
|
|||||||
Margin="0 0 5 0"
|
Margin="0 0 5 0"
|
||||||
IsVisible="{Binding ShowShield}" />
|
IsVisible="{Binding ShowShield}" />
|
||||||
|
|
||||||
<CheckBox IsChecked="{Binding IsEnabled}" IsEnabled="{Binding CanToggleEnabled}">
|
<CheckBox IsChecked="{CompiledBinding IsEnabled}" IsEnabled="{CompiledBinding CanToggleEnabled}">
|
||||||
Feature enabled
|
Enable feature
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7" IsVisible="{Binding Enabling}">
|
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7" IsVisible="{CompiledBinding Enabling}">
|
||||||
<ProgressBar Value="0" IsIndeterminate="True" />
|
<ProgressBar Value="0" IsIndeterminate="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,7 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
|
|
||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
|
get => FeatureInfo.AlwaysEnabled || FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
|
||||||
set => Dispatcher.UIThread.InvokeAsync(() => UpdateEnabled(value));
|
set => Dispatcher.UIThread.InvokeAsync(() => UpdateEnabled(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/Artemis.UI/Screens/Plugins/PluginPlatformView.axaml
Normal file
11
src/Artemis.UI/Screens/Plugins/PluginPlatformView.axaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:plugins="clr-namespace:Artemis.UI.Screens.Plugins"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Plugins.PluginPlatformView"
|
||||||
|
x:DataType="plugins:PluginPlatformViewModel">
|
||||||
|
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" ToolTip.Tip="{CompiledBinding DisplayName}"/>
|
||||||
|
</UserControl>
|
||||||
18
src/Artemis.UI/Screens/Plugins/PluginPlatformView.axaml.cs
Normal file
18
src/Artemis.UI/Screens/Plugins/PluginPlatformView.axaml.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Plugins;
|
||||||
|
|
||||||
|
public partial class PluginPlatformView : UserControl
|
||||||
|
{
|
||||||
|
public PluginPlatformView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/Artemis.UI/Screens/Plugins/PluginPlatformViewModel.cs
Normal file
16
src/Artemis.UI/Screens/Plugins/PluginPlatformViewModel.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using Material.Icons;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Plugins;
|
||||||
|
|
||||||
|
public class PluginPlatformViewModel : ReactiveObject
|
||||||
|
{
|
||||||
|
public PluginPlatformViewModel(string displayName, MaterialIconKind icon)
|
||||||
|
{
|
||||||
|
DisplayName = displayName;
|
||||||
|
Icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public MaterialIconKind Icon { get; set; }
|
||||||
|
}
|
||||||
@ -9,10 +9,11 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Plugins.PluginSettingsView"
|
x:Class="Artemis.UI.Screens.Plugins.PluginSettingsView"
|
||||||
x:DataType="plugins:PluginSettingsViewModel">
|
x:DataType="plugins:PluginSettingsViewModel">
|
||||||
<Border Classes="card" Padding="15" Margin="0 5">
|
<Border Classes="card" Padding="15" Margin="0 5">
|
||||||
<Grid RowDefinitions="*,Auto" ColumnDefinitions="4*,5*">
|
<Grid RowDefinitions="*,Auto" ColumnDefinitions="4*,5*">
|
||||||
<Grid Grid.Row="0" RowDefinitions="Auto,Auto,*" ColumnDefinitions="80,*">
|
<Grid Grid.Row="0" RowDefinitions="Auto,Auto,*" ColumnDefinitions="80,*, Auto">
|
||||||
<shared:ArtemisIcon Icon="{CompiledBinding Plugin.Info.ResolvedIcon}"
|
<shared:ArtemisIcon Icon="{CompiledBinding Plugin.Info.ResolvedIcon}"
|
||||||
|
Fill="False"
|
||||||
Width="48"
|
Width="48"
|
||||||
Height="48"
|
Height="48"
|
||||||
Margin="0 5 0 0"
|
Margin="0 5 0 0"
|
||||||
@ -22,13 +23,23 @@
|
|||||||
|
|
||||||
<TextBlock Grid.Column="1" Grid.Row="0" Classes="h5 no-margin" Text="{CompiledBinding Plugin.Info.Name}" />
|
<TextBlock Grid.Column="1" Grid.Row="0" Classes="h5 no-margin" Text="{CompiledBinding Plugin.Info.Name}" />
|
||||||
|
|
||||||
|
<ItemsControl Grid.Column="2" Grid.Row="0" IsVisible="{CompiledBinding Platforms.Count}" Items="{CompiledBinding Platforms}" HorizontalAlignment="Right">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Spacing="5" Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Classes="subtitle"
|
Classes="subtitle"
|
||||||
Text="{CompiledBinding Plugin.Info.Author}"
|
Text="{CompiledBinding Plugin.Info.Author}"
|
||||||
IsVisible="{CompiledBinding Plugin.Info.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
IsVisible="{CompiledBinding Plugin.Info.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
|
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Margin="0 5"
|
Margin="0 5"
|
||||||
@ -88,13 +99,15 @@
|
|||||||
</controls:HyperlinkButton>
|
</controls:HyperlinkButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<CheckBox Grid.Row="0"
|
<CheckBox Name="EnabledToggle"
|
||||||
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
IsVisible="{CompiledBinding !Enabling}"
|
IsVisible="{CompiledBinding !Enabling}"
|
||||||
IsChecked="{CompiledBinding IsEnabled}">
|
IsChecked="{CompiledBinding IsEnabled, Mode=OneWay}"
|
||||||
|
IsEnabled="{CompiledBinding Plugin.Info.IsCompatible}">
|
||||||
<StackPanel x:Name="EnableText" Orientation="Horizontal">
|
<StackPanel x:Name="EnableText" Orientation="Horizontal">
|
||||||
<TextBlock>Plugin enabled</TextBlock>
|
<TextBlock>Enable plugin</TextBlock>
|
||||||
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
||||||
Margin="5 0 0 0"
|
Margin="5 0 0 0"
|
||||||
ToolTip.Tip="Plugin requires admin rights"
|
ToolTip.Tip="Plugin requires admin rights"
|
||||||
|
|||||||
@ -1,18 +1,30 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Plugins
|
namespace Artemis.UI.Screens.Plugins
|
||||||
{
|
{
|
||||||
public partial class PluginSettingsView : ReactiveUserControl<PluginSettingsViewModel>
|
public partial class PluginSettingsView : ReactiveUserControl<PluginSettingsViewModel>
|
||||||
{
|
{
|
||||||
|
private readonly CheckBox _enabledToggle;
|
||||||
|
|
||||||
public PluginSettingsView()
|
public PluginSettingsView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
_enabledToggle = this.Find<CheckBox>("EnabledToggle");
|
||||||
|
_enabledToggle.Click += EnabledToggleOnClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnabledToggleOnClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() => ViewModel?.UpdateEnabled(!ViewModel.Plugin.IsEnabled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,326 +12,347 @@ using Artemis.UI.Exceptions;
|
|||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.Shared.Services.Builders;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using Material.Icons;
|
||||||
|
using Material.Icons.Avalonia;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Plugins
|
namespace Artemis.UI.Screens.Plugins;
|
||||||
|
|
||||||
|
public class PluginSettingsViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
public class PluginSettingsViewModel : ActivatableViewModelBase
|
private readonly ICoreService _coreService;
|
||||||
|
private readonly INotificationService _notificationService;
|
||||||
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
|
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
private bool _canInstallPrerequisites;
|
||||||
|
private bool _canRemovePrerequisites;
|
||||||
|
private bool _enabling;
|
||||||
|
private bool _isSettingsPopupOpen;
|
||||||
|
private Plugin _plugin;
|
||||||
|
private Window? _window;
|
||||||
|
|
||||||
|
public PluginSettingsViewModel(Plugin plugin,
|
||||||
|
ISettingsVmFactory settingsVmFactory,
|
||||||
|
ICoreService coreService,
|
||||||
|
IWindowService windowService,
|
||||||
|
INotificationService notificationService,
|
||||||
|
IPluginManagementService pluginManagementService)
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
_plugin = plugin;
|
||||||
private readonly INotificationService _notificationService;
|
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
|
||||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
|
||||||
private readonly IWindowService _windowService;
|
|
||||||
private bool _canInstallPrerequisites;
|
|
||||||
private bool _canRemovePrerequisites;
|
|
||||||
private bool _enabling;
|
|
||||||
private bool _isSettingsPopupOpen;
|
|
||||||
private Plugin _plugin;
|
|
||||||
|
|
||||||
public PluginSettingsViewModel(Plugin plugin,
|
_settingsVmFactory = settingsVmFactory;
|
||||||
ISettingsVmFactory settingsVmFactory,
|
_coreService = coreService;
|
||||||
ICoreService coreService,
|
_windowService = windowService;
|
||||||
IWindowService windowService,
|
_notificationService = notificationService;
|
||||||
INotificationService notificationService,
|
_pluginManagementService = pluginManagementService;
|
||||||
IPluginManagementService pluginManagementService)
|
|
||||||
|
PluginFeatures = new ObservableCollection<PluginFeatureViewModel>();
|
||||||
|
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||||
|
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||||
|
|
||||||
|
Platforms = new ObservableCollection<PluginPlatformViewModel>();
|
||||||
|
if (Plugin.Info.Platforms != null)
|
||||||
{
|
{
|
||||||
_plugin = plugin;
|
if (Plugin.Info.Platforms.Value.HasFlag(PluginPlatform.Windows))
|
||||||
|
Platforms.Add(new PluginPlatformViewModel("Windows", MaterialIconKind.MicrosoftWindows));
|
||||||
|
if (Plugin.Info.Platforms.Value.HasFlag(PluginPlatform.Linux))
|
||||||
|
Platforms.Add(new PluginPlatformViewModel("Linux", MaterialIconKind.Linux));
|
||||||
|
if (Plugin.Info.Platforms.Value.HasFlag(PluginPlatform.OSX))
|
||||||
|
Platforms.Add(new PluginPlatformViewModel("OSX", MaterialIconKind.Apple));
|
||||||
|
}
|
||||||
|
|
||||||
_settingsVmFactory = settingsVmFactory;
|
Reload = ReactiveCommand.CreateFromTask(ExecuteReload);
|
||||||
_coreService = coreService;
|
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(vm => vm.IsEnabled, e => e && Plugin.ConfigurationDialog != null));
|
||||||
_windowService = windowService;
|
RemoveSettings = ReactiveCommand.CreateFromTask(ExecuteRemoveSettings);
|
||||||
_notificationService = notificationService;
|
Remove = ReactiveCommand.CreateFromTask(ExecuteRemove);
|
||||||
_pluginManagementService = pluginManagementService;
|
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites, this.WhenAnyValue(x => x.CanInstallPrerequisites));
|
||||||
|
RemovePrerequisites = ReactiveCommand.CreateFromTask<bool>(ExecuteRemovePrerequisites, this.WhenAnyValue(x => x.CanRemovePrerequisites));
|
||||||
|
ShowLogsFolder = ReactiveCommand.Create(ExecuteShowLogsFolder);
|
||||||
|
OpenPluginDirectory = ReactiveCommand.Create(ExecuteOpenPluginDirectory);
|
||||||
|
|
||||||
PluginFeatures = new ObservableCollection<PluginFeatureViewModel>();
|
this.WhenActivated(d =>
|
||||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
{
|
||||||
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
Plugin.Enabled += OnPluginToggled;
|
||||||
|
Plugin.Disabled += OnPluginToggled;
|
||||||
|
|
||||||
Reload = ReactiveCommand.CreateFromTask(ExecuteReload);
|
Disposable.Create(() =>
|
||||||
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(x => x.IsEnabled).Select(isEnabled => isEnabled && Plugin.ConfigurationDialog != null));
|
|
||||||
RemoveSettings = ReactiveCommand.CreateFromTask(ExecuteRemoveSettings);
|
|
||||||
Remove = ReactiveCommand.CreateFromTask(ExecuteRemove);
|
|
||||||
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites, this.WhenAnyValue(x => x.CanInstallPrerequisites));
|
|
||||||
RemovePrerequisites = ReactiveCommand.CreateFromTask<bool>(ExecuteRemovePrerequisites, this.WhenAnyValue(x => x.CanRemovePrerequisites));
|
|
||||||
ShowLogsFolder = ReactiveCommand.Create(ExecuteShowLogsFolder);
|
|
||||||
OpenPluginDirectory = ReactiveCommand.Create(ExecuteOpenPluginDirectory);
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
{
|
||||||
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
|
Plugin.Enabled -= OnPluginToggled;
|
||||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
Plugin.Disabled -= OnPluginToggled;
|
||||||
|
_window?.Close();
|
||||||
|
}).DisposeWith(d);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Disposable.Create(() =>
|
public ReactiveCommand<Unit, Unit> Reload { get; }
|
||||||
{
|
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
|
||||||
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
|
public ReactiveCommand<Unit, Unit> RemoveSettings { get; }
|
||||||
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
|
public ReactiveCommand<Unit, Unit> Remove { get; }
|
||||||
}).DisposeWith(d);
|
public ReactiveCommand<Unit, Unit> InstallPrerequisites { get; }
|
||||||
});
|
public ReactiveCommand<bool, Unit> RemovePrerequisites { get; }
|
||||||
}
|
public ReactiveCommand<Unit, Unit> ShowLogsFolder { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> OpenPluginDirectory { get; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit,Unit> Reload { get; }
|
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
|
||||||
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
|
public ObservableCollection<PluginPlatformViewModel> Platforms { get; }
|
||||||
public ReactiveCommand<Unit,Unit> RemoveSettings { get; }
|
|
||||||
public ReactiveCommand<Unit,Unit> Remove { get;}
|
|
||||||
public ReactiveCommand<Unit, Unit> InstallPrerequisites { get; }
|
|
||||||
public ReactiveCommand<bool, Unit> RemovePrerequisites { get; }
|
|
||||||
public ReactiveCommand<Unit,Unit> ShowLogsFolder { get; }
|
|
||||||
public ReactiveCommand<Unit,Unit> OpenPluginDirectory { get; }
|
|
||||||
|
|
||||||
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
|
public Plugin Plugin
|
||||||
|
{
|
||||||
|
get => _plugin;
|
||||||
|
set => RaiseAndSetIfChanged(ref _plugin, value);
|
||||||
|
}
|
||||||
|
|
||||||
public Plugin Plugin
|
public bool Enabling
|
||||||
|
{
|
||||||
|
get => _enabling;
|
||||||
|
set => RaiseAndSetIfChanged(ref _enabling, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
||||||
|
public bool IsEnabled => Plugin.IsEnabled;
|
||||||
|
|
||||||
|
public bool IsSettingsPopupOpen
|
||||||
|
{
|
||||||
|
get => _isSettingsPopupOpen;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get => _plugin;
|
if (!RaiseAndSetIfChanged(ref _isSettingsPopupOpen, value)) return;
|
||||||
set => RaiseAndSetIfChanged(ref _plugin, value);
|
CheckPrerequisites();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Enabling
|
public bool CanInstallPrerequisites
|
||||||
|
{
|
||||||
|
get => _canInstallPrerequisites;
|
||||||
|
set => RaiseAndSetIfChanged(ref _canInstallPrerequisites, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanRemovePrerequisites
|
||||||
|
{
|
||||||
|
get => _canRemovePrerequisites;
|
||||||
|
set => RaiseAndSetIfChanged(ref _canRemovePrerequisites, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteOpenSettings()
|
||||||
|
{
|
||||||
|
if (Plugin.ConfigurationDialog == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_window != null)
|
||||||
{
|
{
|
||||||
get => _enabling;
|
_window.WindowState = WindowState.Normal;
|
||||||
set => RaiseAndSetIfChanged(ref _enabling, value);
|
_window.Activate();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
try
|
||||||
|
|
||||||
public bool IsEnabled
|
|
||||||
{
|
{
|
||||||
get => Plugin.IsEnabled;
|
PluginConfigurationViewModel? viewModel = Plugin.Kernel!.Get(Plugin.ConfigurationDialog.Type) as PluginConfigurationViewModel;
|
||||||
set => Task.Run(() => UpdateEnabled(value));
|
if (viewModel == null)
|
||||||
}
|
throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}");
|
||||||
|
|
||||||
public bool IsSettingsPopupOpen
|
_window = _windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
|
||||||
|
_window.Closed += (_, _) => _window = null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
get => _isSettingsPopupOpen;
|
_windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
||||||
set
|
throw;
|
||||||
{
|
|
||||||
if (!RaiseAndSetIfChanged(ref _isSettingsPopupOpen, value)) return;
|
|
||||||
CheckPrerequisites();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanInstallPrerequisites
|
private void ExecuteOpenPluginDirectory()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
get => _canInstallPrerequisites;
|
Utilities.OpenFolder(Plugin.Directory.FullName);
|
||||||
set => RaiseAndSetIfChanged(ref _canInstallPrerequisites, value);
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
public bool CanRemovePrerequisites
|
|
||||||
{
|
{
|
||||||
get => _canRemovePrerequisites;
|
_windowService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
||||||
set => RaiseAndSetIfChanged(ref _canRemovePrerequisites, value);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ExecuteOpenSettings()
|
private async Task ExecuteReload()
|
||||||
|
{
|
||||||
|
bool wasEnabled = IsEnabled;
|
||||||
|
|
||||||
|
await Task.Run(() => _pluginManagementService.UnloadPlugin(Plugin));
|
||||||
|
|
||||||
|
PluginFeatures.Clear();
|
||||||
|
|
||||||
|
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
|
||||||
|
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||||
|
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||||
|
|
||||||
|
if (wasEnabled)
|
||||||
|
await UpdateEnabled(true);
|
||||||
|
|
||||||
|
_notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteInstallPrerequisites()
|
||||||
|
{
|
||||||
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
|
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
|
||||||
|
|
||||||
|
if (subjects.Any(s => s.PlatformPrerequisites.Any()))
|
||||||
|
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteRemovePrerequisites(bool forPluginRemoval = false)
|
||||||
|
{
|
||||||
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
|
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
|
||||||
|
|
||||||
|
if (subjects.Any(s => s.PlatformPrerequisites.Any(p => p.UninstallActions.Any())))
|
||||||
{
|
{
|
||||||
if (Plugin.ConfigurationDialog == null)
|
await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, subjects, forPluginRemoval ? "Skip, remove plugin" : "Cancel");
|
||||||
return;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
private async Task ExecuteRemoveSettings()
|
||||||
{
|
{
|
||||||
PluginConfigurationViewModel? viewModel = Plugin.Kernel!.Get(Plugin.ConfigurationDialog.Type) as PluginConfigurationViewModel;
|
bool confirmed = await _windowService.ShowConfirmContentDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
|
||||||
if (viewModel == null)
|
if (!confirmed)
|
||||||
throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}");
|
return;
|
||||||
|
|
||||||
_windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
|
bool wasEnabled = IsEnabled;
|
||||||
}
|
|
||||||
catch (Exception e)
|
if (IsEnabled)
|
||||||
{
|
await UpdateEnabled(false);
|
||||||
_windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
|
||||||
throw;
|
_pluginManagementService.RemovePluginSettings(Plugin);
|
||||||
}
|
|
||||||
|
if (wasEnabled)
|
||||||
|
await UpdateEnabled(true);
|
||||||
|
|
||||||
|
_notificationService.CreateNotification().WithTitle("Cleared plugin settings.").Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteRemove()
|
||||||
|
{
|
||||||
|
bool confirmed = await _windowService.ShowConfirmContentDialog("Remove plugin", "Are you sure you want to remove this plugin?");
|
||||||
|
if (!confirmed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the plugin or any of its features has uninstall actions, offer to run these
|
||||||
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
|
subjects.AddRange(Plugin.Features);
|
||||||
|
if (subjects.Any(s => s.PlatformPrerequisites.Any(p => p.UninstallActions.Any())))
|
||||||
|
await ExecuteRemovePrerequisites(true);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_pluginManagementService.RemovePlugin(Plugin, false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_windowService.ShowExceptionDialog("Failed to remove plugin", e);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteOpenPluginDirectory()
|
_notificationService.CreateNotification().WithTitle("Removed plugin.").Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteShowLogsFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Utilities.OpenFolder(Constants.LogsFolder);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateEnabled(bool enable)
|
||||||
|
{
|
||||||
|
if (Enabling)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Utilities.OpenFolder(Plugin.Directory.FullName);
|
await Task.Run(() => _pluginManagementService.DisablePlugin(Plugin, true));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_windowService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
|
||||||
|
.WithSeverity(NotificationSeverity.Error)
|
||||||
|
.WithMessage($"Failed to disable plugin {Plugin.Info.Name}\r\n{e.Message}")
|
||||||
|
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
|
||||||
|
.Show());
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteReload()
|
try
|
||||||
{
|
{
|
||||||
bool wasEnabled = IsEnabled;
|
Enabling = true;
|
||||||
|
if (Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||||
|
{
|
||||||
|
bool confirmed = await _windowService.ShowConfirmContentDialog("Enable plugin", "This plugin requires admin rights, are you sure you want to enable it?");
|
||||||
|
if (!confirmed)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await Task.Run(() => _pluginManagementService.UnloadPlugin(Plugin));
|
// Check if all prerequisites are met async
|
||||||
|
|
||||||
PluginFeatures.Clear();
|
|
||||||
|
|
||||||
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
|
|
||||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
|
||||||
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
|
||||||
|
|
||||||
if (wasEnabled)
|
|
||||||
await UpdateEnabled(true);
|
|
||||||
|
|
||||||
_notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ExecuteInstallPrerequisites()
|
|
||||||
{
|
|
||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
|
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled || f.EnabledInStorage));
|
||||||
|
|
||||||
if (subjects.Any(s => s.PlatformPrerequisites.Any()))
|
if (subjects.Any(s => !s.ArePrerequisitesMet()))
|
||||||
|
{
|
||||||
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
|
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
|
||||||
}
|
if (!subjects.All(s => s.ArePrerequisitesMet()))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ExecuteRemovePrerequisites(bool forPluginRemoval = false)
|
await Task.Run(() => _pluginManagementService.EnablePlugin(Plugin, true, true));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
|
||||||
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
|
.WithSeverity(NotificationSeverity.Error)
|
||||||
|
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
|
||||||
if (subjects.Any(s => s.PlatformPrerequisites.Any(p => p.UninstallActions.Any())))
|
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
|
||||||
{
|
.Show());
|
||||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, subjects, forPluginRemoval ? "Skip, remove plugin" : "Cancel");
|
|
||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
private async Task ExecuteRemoveSettings()
|
|
||||||
{
|
|
||||||
bool confirmed = await _windowService.ShowConfirmContentDialog("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);
|
|
||||||
|
|
||||||
_notificationService.CreateNotification().WithTitle("Cleared plugin settings.").Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ExecuteRemove()
|
|
||||||
{
|
|
||||||
bool confirmed = await _windowService.ShowConfirmContentDialog("Remove plugin", "Are you sure you want to remove this plugin?");
|
|
||||||
if (!confirmed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If the plugin or any of its features has uninstall actions, offer to run these
|
|
||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
|
||||||
subjects.AddRange(Plugin.Features);
|
|
||||||
if (subjects.Any(s => s.PlatformPrerequisites.Any(p => p.UninstallActions.Any())))
|
|
||||||
await ExecuteRemovePrerequisites(true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_pluginManagementService.RemovePlugin(Plugin, false);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_windowService.ShowExceptionDialog("Failed to remove plugin", e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
_notificationService.CreateNotification().WithTitle("Removed plugin.").Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteShowLogsFolder()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Utilities.OpenFolder(Constants.LogsFolder);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PluginManagementServiceOnPluginToggled(object? sender, PluginEventArgs e)
|
|
||||||
{
|
|
||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateEnabled(bool enable)
|
|
||||||
{
|
|
||||||
if (IsEnabled == enable)
|
|
||||||
{
|
|
||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable)
|
|
||||||
{
|
|
||||||
Enabling = true;
|
|
||||||
|
|
||||||
if (Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
|
||||||
{
|
|
||||||
bool confirmed = await _windowService.ShowConfirmContentDialog("Enable plugin", "This plugin requires admin rights, are you sure you want to enable it?");
|
|
||||||
if (!confirmed)
|
|
||||||
{
|
|
||||||
CancelEnable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if all prerequisites are met async
|
|
||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
|
||||||
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled || f.EnabledInStorage));
|
|
||||||
|
|
||||||
if (subjects.Any(s => !s.ArePrerequisitesMet()))
|
|
||||||
{
|
|
||||||
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
|
|
||||||
if (!subjects.All(s => s.ArePrerequisitesMet()))
|
|
||||||
{
|
|
||||||
CancelEnable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_pluginManagementService.EnablePlugin(Plugin, true, true);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
|
|
||||||
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
|
|
||||||
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
|
|
||||||
.Show());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Enabling = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_pluginManagementService.DisablePlugin(Plugin, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CancelEnable()
|
|
||||||
{
|
{
|
||||||
Enabling = false;
|
Enabling = false;
|
||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckPrerequisites()
|
|
||||||
|
private void CheckPrerequisites()
|
||||||
|
{
|
||||||
|
CanInstallPrerequisites = Plugin.Info.PlatformPrerequisites.Any() ||
|
||||||
|
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.PlatformPrerequisites.Any());
|
||||||
|
CanRemovePrerequisites = Plugin.Info.PlatformPrerequisites.Any(p => p.UninstallActions.Any()) ||
|
||||||
|
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.PlatformPrerequisites.Any(p => p.UninstallActions.Any()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPluginToggled(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
CanInstallPrerequisites = Plugin.Info.PlatformPrerequisites.Any() ||
|
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||||
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.PlatformPrerequisites.Any());
|
if (!IsEnabled)
|
||||||
CanRemovePrerequisites = Plugin.Info.PlatformPrerequisites.Any(p => p.UninstallActions.Any()) ||
|
_window?.Close();
|
||||||
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.PlatformPrerequisites.Any(p => p.UninstallActions.Any()));
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Avalonia;
|
using Artemis.UI.Shared;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|||||||
@ -4,12 +4,8 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Settings.DevicesTabView">
|
x:Class="Artemis.UI.Screens.Settings.DevicesTabView">
|
||||||
<Grid MaxWidth="1050">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<Grid.RowDefinitions>
|
<StackPanel MaxWidth="1050">
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Classes="h4">Device management</TextBlock>
|
<TextBlock Classes="h4">Device management</TextBlock>
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
Below you view and manage the devices that were detected by Artemis.
|
Below you view and manage the devices that were detected by Artemis.
|
||||||
@ -17,10 +13,10 @@
|
|||||||
<TextBlock>
|
<TextBlock>
|
||||||
Disabling a device will cause it to stop updating. Some SDKs will even go back to using manufacturer lighting (Artemis restart may be required).
|
Disabling a device will cause it to stop updating. Some SDKs will even go back to using manufacturer lighting (Artemis restart may be required).
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
<ItemsControl Items="{Binding Devices}" Margin="-5 0">
|
<ItemsControl Items="{Binding Devices}" Margin="-5 0" MaxWidth="1050">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<WrapPanel />
|
<WrapPanel />
|
||||||
|
|||||||
@ -6,11 +6,18 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Settings.PluginsTabView"
|
x:Class="Artemis.UI.Screens.Settings.PluginsTabView"
|
||||||
x:DataType="settings:PluginsTabViewModel">
|
x:DataType="settings:PluginsTabViewModel">
|
||||||
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Width="900">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<TextBox Grid.Row="0" Grid.Column="0" Classes="clearButton" Text="{CompiledBinding SearchPluginInput}" Watermark="Search plugins" Margin="0 10" />
|
<Grid Grid.Row="0" Grid.Column="0" MaxWidth="900" Margin="0 10">
|
||||||
<Button Grid.Row="0" Grid.Column="1" Classes="accent" Command="{CompiledBinding ImportPlugin}" HorizontalAlignment="Right">Import plugin</Button>
|
<Grid.ColumnDefinitions>
|
||||||
<ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
<ColumnDefinition MinWidth="165" MaxWidth="400"/>
|
||||||
<ItemsControl Items="{CompiledBinding Plugins}" />
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox Classes="clearButton" Text="{CompiledBinding SearchPluginInput}" Watermark="Search plugins" Margin="0 0 10 0"/>
|
||||||
|
<Button Grid.Row="0" Grid.Column="1" Classes="accent" Command="{CompiledBinding ImportPlugin}" HorizontalAlignment="Right">Import plugin</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" Grid.Column="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
|
<ItemsControl Items="{CompiledBinding Plugins}" MaxWidth="900" VerticalAlignment="Center"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -90,7 +90,7 @@ namespace Artemis.UI.Screens.Settings
|
|||||||
// Enable it via the VM to enable the prerequisite dialog
|
// Enable it via the VM to enable the prerequisite dialog
|
||||||
PluginSettingsViewModel? pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
|
PluginSettingsViewModel? pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
|
||||||
if (pluginViewModel is {IsEnabled: false})
|
if (pluginViewModel is {IsEnabled: false})
|
||||||
pluginViewModel.IsEnabled = true;
|
await pluginViewModel.UpdateEnabled(true);
|
||||||
|
|
||||||
_notificationService.CreateNotification()
|
_notificationService.CreateNotification()
|
||||||
.WithTitle("Plugin imported")
|
.WithTitle("Plugin imported")
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Artemis.UI.Shared;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user