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>
|
||||
/// 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>
|
||||
public bool AlwaysEnabled { get; set; }
|
||||
}
|
||||
|
||||
@ -122,10 +122,11 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <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>
|
||||
[JsonProperty]
|
||||
public bool AlwaysEnabled { get; }
|
||||
public bool AlwaysEnabled { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the feature is enabled in persistent storage
|
||||
|
||||
@ -24,7 +24,7 @@ namespace Artemis.Core
|
||||
private Plugin _plugin = null!;
|
||||
private Version _version = null!;
|
||||
private bool _requiresAdmin;
|
||||
|
||||
private PluginPlatform? _platforms;
|
||||
|
||||
internal PluginInfo()
|
||||
{
|
||||
@ -142,7 +142,17 @@ namespace Artemis.Core
|
||||
get => _requiresAdmin;
|
||||
internal set => SetAndNotify(ref _requiresAdmin, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public PluginPlatform? Platforms
|
||||
{
|
||||
get => _platforms;
|
||||
internal set => _platforms = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin this info is associated with
|
||||
/// </summary>
|
||||
@ -164,6 +174,11 @@ namespace Artemis.Core
|
||||
return Icon.Contains('.') ? Plugin.ResolveRelativePath(Icon) : Icon;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)}";
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core;
|
||||
|
||||
@ -6,15 +7,36 @@ namespace Artemis.Core;
|
||||
/// Specifies OS platforms a plugin may support.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum PluginPlatform
|
||||
{
|
||||
/// <summary>The Windows platform.</summary>
|
||||
Windows = 0,
|
||||
Windows = 1,
|
||||
|
||||
/// <summary>The Linux platform.</summary>
|
||||
Linux = 1,
|
||||
Linux = 2,
|
||||
|
||||
/// <summary>The OSX platform.</summary>
|
||||
// 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>
|
||||
public bool AppliesToPlatform()
|
||||
{
|
||||
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;
|
||||
return Platform.MatchesCurrentOperatingSystem();
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
@ -364,7 +364,13 @@ namespace Artemis.Core.Services
|
||||
// Load the enabled state and if not found, default to true
|
||||
PluginFeatureEntity featureEntity = plugin.Entity.Features.FirstOrDefault(i => i.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())
|
||||
@ -390,6 +396,9 @@ namespace Artemis.Core.Services
|
||||
|
||||
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)
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Linux.Providers.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
@ -26,6 +27,9 @@ namespace Artemis.UI.Linux
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (Design.IsDesignMode)
|
||||
return;
|
||||
|
||||
ArtemisBootstrapper.Initialize();
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Ninject;
|
||||
@ -20,6 +21,9 @@ namespace Artemis.UI.MacOS
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (Design.IsDesignMode)
|
||||
return;
|
||||
|
||||
ArtemisBootstrapper.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationIcon/>
|
||||
<StartupObject/>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
@ -15,20 +15,20 @@
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14"/>
|
||||
<PackageReference Include="DynamicData" Version="7.8.6"/>
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1"/>
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2"/>
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1"/>
|
||||
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease.32"/>
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1"/>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14" />
|
||||
<PackageReference Include="DynamicData" Version="7.8.6" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="1.0.0-prerelease.32" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Controls\HotkeyBox.axaml.cs">
|
||||
|
||||
@ -4,7 +4,7 @@ using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="viewModel">ViewModel to show the View for</param>
|
||||
void ShowWindow(object viewModel);
|
||||
Window ShowWindow(object viewModel);
|
||||
|
||||
/// <summary>
|
||||
/// Shows a dialog displaying the given exception
|
||||
@ -61,6 +61,12 @@ namespace Artemis.UI.Shared.Services
|
||||
/// </returns>
|
||||
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>
|
||||
/// Creates an open file dialog, use the fluent API to configure it
|
||||
/// </summary>
|
||||
|
||||
@ -30,7 +30,7 @@ namespace Artemis.UI.Shared.Services
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public void ShowWindow(object viewModel)
|
||||
public Window ShowWindow(object viewModel)
|
||||
{
|
||||
Window? parent = GetCurrentWindow();
|
||||
|
||||
@ -49,6 +49,8 @@ namespace Artemis.UI.Shared.Services
|
||||
window.Show(parent);
|
||||
else
|
||||
window.Show();
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
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.DataContext = viewModel;
|
||||
viewModel.CloseRequested += (_, args) => window.Close(args.Result);
|
||||
viewModel.CancelRequested += (_, _) => window.Close();
|
||||
|
||||
return await window.ShowDialog<TResult>(parent);
|
||||
}
|
||||
@ -118,6 +119,14 @@ namespace Artemis.UI.Shared.Services
|
||||
throw new ArtemisSharedUIException("Can't show a content dialog without any windows being shown.");
|
||||
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()
|
||||
{
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<Setter Property="MaxHeight" Value="0" />
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="MaxHeight" Value="50" />
|
||||
<Setter Property="MaxHeight" Value="600" />
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
|
||||
@ -51,17 +51,8 @@ public abstract class DialogViewModelBase<TResult> : ValidatableViewModelBase
|
||||
{
|
||||
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? CancelRequested;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -2,6 +2,7 @@ using Artemis.Core.Services;
|
||||
using Artemis.UI.Windows.Ninject;
|
||||
using Artemis.UI.Windows.Providers.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
@ -22,9 +23,9 @@ namespace Artemis.UI.Windows
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode)
|
||||
return;
|
||||
|
||||
|
||||
ArtemisBootstrapper.Initialize();
|
||||
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args);
|
||||
RegisterProviders(_kernel!);
|
||||
|
||||
@ -8,44 +8,44 @@
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Artemis.UI.Avalonia.csproj.DotSettings"/>
|
||||
<None Remove="Artemis.UI.csproj.DotSettings"/>
|
||||
<None Remove="Assets\Images\Logo\application.ico"/>
|
||||
<None Remove="Artemis.UI.Avalonia.csproj.DotSettings" />
|
||||
<None Remove="Artemis.UI.csproj.DotSettings" />
|
||||
<None Remove="Assets\Images\Logo\application.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="10.14.0"/>
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14"/>
|
||||
<PackageReference Include="DynamicData" Version="7.8.6"/>
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1"/>
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4"/>
|
||||
<PackageReference Include="Live.Avalonia" Version="1.3.1"/>
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2"/>
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1"/>
|
||||
<PackageReference Include="RGB.NET.Core" 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="Splat.Ninject" Version="14.1.45"/>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="10.14.0" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14" />
|
||||
<PackageReference Include="DynamicData" Version="7.8.6" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Live.Avalonia" Version="1.3.1" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
|
||||
<PackageReference Include="RGB.NET.Core" 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="Splat.Ninject" Version="14.1.45" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.VisualScripting\Artemis.VisualScripting.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
||||
<ProjectReference Include="..\Artemis.VisualScripting\Artemis.VisualScripting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\Images\Logo\bow-white.ico"/>
|
||||
<Content Include="Assets\Images\Logo\bow.ico"/>
|
||||
<Content Include="Assets\Images\Logo\bow-white.ico" />
|
||||
<Content Include="Assets\Images\Logo\bow.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Assets\Images\Logo\bow-black.ico"/>
|
||||
<Resource Include="Assets\Images\Logo\bow-white.ico"/>
|
||||
<Resource Include="Assets\Images\Logo\bow-black.ico" />
|
||||
<Resource Include="Assets\Images\Logo\bow-white.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="DefaultTypes\PropertyInput\StringPropertyInputView.axaml.cs">
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Artemis.UI.Screens.Root;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Shapes;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
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"
|
||||
x:Class="Artemis.UI.Screens.Plugins.PluginFeatureView">
|
||||
x:Class="Artemis.UI.Screens.Plugins.PluginFeatureView"
|
||||
x:DataType="plugins:PluginFeatureViewModel">
|
||||
<Grid ColumnDefinitions="30,*,Auto">
|
||||
<Grid.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
@ -24,14 +26,15 @@
|
||||
|
||||
<!-- Icon column -->
|
||||
<shared:ArtemisIcon Grid.Column="0"
|
||||
Icon="{Binding FeatureInfo.ResolvedIcon}"
|
||||
Icon="{CompiledBinding FeatureInfo.ResolvedIcon}"
|
||||
Fill="False"
|
||||
Width="20"
|
||||
Height="20"
|
||||
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
|
||||
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
|
||||
|
||||
<Button Grid.Column="0"
|
||||
Classes="AppBarButton icon-button"
|
||||
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Foreground="#E74C4C"
|
||||
ToolTip.Tip="An exception occurred while enabling this feature, click to view"
|
||||
Command="{Binding ViewLoadException}">
|
||||
@ -40,15 +43,15 @@
|
||||
|
||||
<!-- Display name column -->
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding FeatureInfo.Name}"
|
||||
Text="{CompiledBinding FeatureInfo.Name}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{Binding FeatureInfo.Description}" />
|
||||
ToolTip.Tip="{CompiledBinding FeatureInfo.Description}" />
|
||||
|
||||
<!-- Enable toggle column -->
|
||||
<StackPanel Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{Binding !Enabling}"
|
||||
IsVisible="{CompiledBinding !Enabling}"
|
||||
Orientation="Horizontal"
|
||||
ToolTip.Tip="This feature cannot be disabled without disabling the whole plugin">
|
||||
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
||||
@ -57,12 +60,12 @@
|
||||
Margin="0 0 5 0"
|
||||
IsVisible="{Binding ShowShield}" />
|
||||
|
||||
<CheckBox IsChecked="{Binding IsEnabled}" IsEnabled="{Binding CanToggleEnabled}">
|
||||
Feature enabled
|
||||
<CheckBox IsChecked="{CompiledBinding IsEnabled}" IsEnabled="{CompiledBinding CanToggleEnabled}">
|
||||
Enable feature
|
||||
</CheckBox>
|
||||
|
||||
</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" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ namespace Artemis.UI.Screens.Plugins
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
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"
|
||||
x:Class="Artemis.UI.Screens.Plugins.PluginSettingsView"
|
||||
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 Grid.Row="0" RowDefinitions="Auto,Auto,*" ColumnDefinitions="80,*">
|
||||
<shared:ArtemisIcon Icon="{CompiledBinding Plugin.Info.ResolvedIcon}"
|
||||
<Grid Grid.Row="0" RowDefinitions="Auto,Auto,*" ColumnDefinitions="80,*, Auto">
|
||||
<shared:ArtemisIcon Icon="{CompiledBinding Plugin.Info.ResolvedIcon}"
|
||||
Fill="False"
|
||||
Width="48"
|
||||
Height="48"
|
||||
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}" />
|
||||
|
||||
<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"
|
||||
Grid.ColumnSpan="2"
|
||||
Grid.Row="1"
|
||||
Classes="subtitle"
|
||||
Text="{CompiledBinding Plugin.Info.Author}"
|
||||
IsVisible="{CompiledBinding Plugin.Info.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Grid.Row="2"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0 5"
|
||||
@ -88,13 +99,15 @@
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="0"
|
||||
<CheckBox Name="EnabledToggle"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{CompiledBinding !Enabling}"
|
||||
IsChecked="{CompiledBinding IsEnabled}">
|
||||
IsChecked="{CompiledBinding IsEnabled, Mode=OneWay}"
|
||||
IsEnabled="{CompiledBinding Plugin.Info.IsCompatible}">
|
||||
<StackPanel x:Name="EnableText" Orientation="Horizontal">
|
||||
<TextBlock>Plugin enabled</TextBlock>
|
||||
<TextBlock>Enable plugin</TextBlock>
|
||||
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip.Tip="Plugin requires admin rights"
|
||||
|
||||
@ -1,18 +1,30 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Artemis.UI.Screens.Plugins
|
||||
{
|
||||
public partial class PluginSettingsView : ReactiveUserControl<PluginSettingsViewModel>
|
||||
{
|
||||
private readonly CheckBox _enabledToggle;
|
||||
|
||||
public PluginSettingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_enabledToggle = this.Find<CheckBox>("EnabledToggle");
|
||||
_enabledToggle.Click += EnabledToggleOnClick;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
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.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using Material.Icons;
|
||||
using Material.Icons.Avalonia;
|
||||
using Ninject;
|
||||
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;
|
||||
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;
|
||||
_plugin = plugin;
|
||||
|
||||
public PluginSettingsViewModel(Plugin plugin,
|
||||
ISettingsVmFactory settingsVmFactory,
|
||||
ICoreService coreService,
|
||||
IWindowService windowService,
|
||||
INotificationService notificationService,
|
||||
IPluginManagementService pluginManagementService)
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
_coreService = coreService;
|
||||
_windowService = windowService;
|
||||
_notificationService = notificationService;
|
||||
_pluginManagementService = 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;
|
||||
_coreService = coreService;
|
||||
_windowService = windowService;
|
||||
_notificationService = notificationService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
Reload = ReactiveCommand.CreateFromTask(ExecuteReload);
|
||||
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(vm => vm.IsEnabled, e => e && 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);
|
||||
|
||||
PluginFeatures = new ObservableCollection<PluginFeatureViewModel>();
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||
|
||||
Reload = ReactiveCommand.CreateFromTask(ExecuteReload);
|
||||
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 =>
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Plugin.Enabled += OnPluginToggled;
|
||||
Plugin.Disabled += OnPluginToggled;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
||||
Plugin.Enabled -= OnPluginToggled;
|
||||
Plugin.Disabled -= OnPluginToggled;
|
||||
_window?.Close();
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
public ReactiveCommand<Unit, Unit> Reload { get; }
|
||||
public ReactiveCommand<Unit, Unit> OpenSettings { 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 ReactiveCommand<Unit,Unit> Reload { get; }
|
||||
public ReactiveCommand<Unit, Unit> OpenSettings { 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 ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
|
||||
public ObservableCollection<PluginPlatformViewModel> Platforms { get; }
|
||||
|
||||
public Plugin Plugin
|
||||
public Plugin Plugin
|
||||
{
|
||||
get => _plugin;
|
||||
set => RaiseAndSetIfChanged(ref _plugin, value);
|
||||
}
|
||||
|
||||
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;
|
||||
set => RaiseAndSetIfChanged(ref _plugin, value);
|
||||
if (!RaiseAndSetIfChanged(ref _isSettingsPopupOpen, value)) return;
|
||||
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;
|
||||
set => RaiseAndSetIfChanged(ref _enabling, value);
|
||||
_window.WindowState = WindowState.Normal;
|
||||
_window.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
||||
|
||||
public bool IsEnabled
|
||||
try
|
||||
{
|
||||
get => Plugin.IsEnabled;
|
||||
set => Task.Run(() => UpdateEnabled(value));
|
||||
}
|
||||
PluginConfigurationViewModel? viewModel = Plugin.Kernel!.Get(Plugin.ConfigurationDialog.Type) as PluginConfigurationViewModel;
|
||||
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;
|
||||
set
|
||||
{
|
||||
if (!RaiseAndSetIfChanged(ref _isSettingsPopupOpen, value)) return;
|
||||
CheckPrerequisites();
|
||||
}
|
||||
_windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInstallPrerequisites
|
||||
private void ExecuteOpenPluginDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
get => _canInstallPrerequisites;
|
||||
set => RaiseAndSetIfChanged(ref _canInstallPrerequisites, value);
|
||||
Utilities.OpenFolder(Plugin.Directory.FullName);
|
||||
}
|
||||
|
||||
public bool CanRemovePrerequisites
|
||||
catch (Exception e)
|
||||
{
|
||||
get => _canRemovePrerequisites;
|
||||
set => RaiseAndSetIfChanged(ref _canRemovePrerequisites, value);
|
||||
_windowService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, subjects, forPluginRemoval ? "Skip, remove plugin" : "Cancel");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PluginConfigurationViewModel? viewModel = Plugin.Kernel!.Get(Plugin.ConfigurationDialog.Type) as PluginConfigurationViewModel;
|
||||
if (viewModel == null)
|
||||
throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}");
|
||||
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;
|
||||
|
||||
_windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
||||
throw;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
Utilities.OpenFolder(Plugin.Directory.FullName);
|
||||
await Task.Run(() => _pluginManagementService.DisablePlugin(Plugin, true));
|
||||
}
|
||||
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));
|
||||
|
||||
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()
|
||||
{
|
||||
// Check if all prerequisites are met async
|
||||
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);
|
||||
}
|
||||
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};
|
||||
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
|
||||
|
||||
if (subjects.Any(s => s.PlatformPrerequisites.Any(p => p.UninstallActions.Any())))
|
||||
{
|
||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, subjects, forPluginRemoval ? "Skip, remove plugin" : "Cancel");
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
|
||||
.WithSeverity(NotificationSeverity.Error)
|
||||
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
|
||||
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
|
||||
.Show());
|
||||
}
|
||||
|
||||
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()
|
||||
finally
|
||||
{
|
||||
Enabling = false;
|
||||
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() ||
|
||||
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()));
|
||||
}
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
if (!IsEnabled)
|
||||
_window?.Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Avalonia;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
@ -4,12 +4,8 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Settings.DevicesTabView">
|
||||
<Grid MaxWidth="1050">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<StackPanel MaxWidth="1050">
|
||||
<TextBlock Classes="h4">Device management</TextBlock>
|
||||
<TextBlock>
|
||||
Below you view and manage the devices that were detected by Artemis.
|
||||
@ -17,10 +13,10 @@
|
||||
<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).
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<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>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
|
||||
@ -6,11 +6,18 @@
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Settings.PluginsTabView"
|
||||
x:DataType="settings:PluginsTabViewModel">
|
||||
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Width="900">
|
||||
<TextBox Grid.Row="0" Grid.Column="0" Classes="clearButton" Text="{CompiledBinding SearchPluginInput}" Watermark="Search plugins" Margin="0 10" />
|
||||
<Button Grid.Row="0" Grid.Column="1" Classes="accent" Command="{CompiledBinding ImportPlugin}" HorizontalAlignment="Right">Import plugin</Button>
|
||||
<ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl Items="{CompiledBinding Plugins}" />
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" Grid.Column="0" MaxWidth="900" Margin="0 10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MinWidth="165" MaxWidth="400"/>
|
||||
<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>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -90,7 +90,7 @@ namespace Artemis.UI.Screens.Settings
|
||||
// Enable it via the VM to enable the prerequisite dialog
|
||||
PluginSettingsViewModel? pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
|
||||
if (pluginViewModel is {IsEnabled: false})
|
||||
pluginViewModel.IsEnabled = true;
|
||||
await pluginViewModel.UpdateEnabled(true);
|
||||
|
||||
_notificationService.CreateNotification()
|
||||
.WithTitle("Plugin imported")
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user