mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Startup wizard - Show device providers from all plugins
This commit is contained in:
parent
3022c7df65
commit
35d5ef1743
@ -26,6 +26,8 @@ public partial class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly Func<PluginFeatureInfo, WizardPluginFeatureViewModel> _getPluginFeatureViewModel;
|
||||
|
||||
[Notify] private int _currentStep;
|
||||
[Notify] private bool _showContinue;
|
||||
[Notify] private bool _showFinish;
|
||||
@ -36,12 +38,13 @@ public partial class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
IPluginManagementService pluginManagementService,
|
||||
IWindowService windowService,
|
||||
IDeviceService deviceService,
|
||||
ISettingsVmFactory settingsVmFactory,
|
||||
LayoutFinderViewModel layoutFinderViewModel)
|
||||
LayoutFinderViewModel layoutFinderViewModel,
|
||||
Func<PluginFeatureInfo, WizardPluginFeatureViewModel> getPluginFeatureViewModel)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_windowService = windowService;
|
||||
_deviceService = deviceService;
|
||||
_getPluginFeatureViewModel = getPluginFeatureViewModel;
|
||||
_autoRunProvider = container.Resolve<IAutoRunProvider>(IfUnresolved.ReturnDefault);
|
||||
_protocolProvider = container.Resolve<IProtocolProvider>(IfUnresolved.ReturnDefault);
|
||||
|
||||
@ -51,11 +54,12 @@ public partial class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
SelectLayout = ReactiveCommand.Create<string>(ExecuteSelectLayout);
|
||||
Version = $"Version {Constants.CurrentVersion}";
|
||||
|
||||
// Take all compatible plugins that have an always-enabled device provider
|
||||
DeviceProviders = new ObservableCollection<PluginViewModel>(pluginManagementService.GetAllPlugins()
|
||||
.Where(p => p.Info.IsCompatible && p.Features.Any(f => f.AlwaysEnabled && f.FeatureType.IsAssignableTo(typeof(DeviceProvider))))
|
||||
.OrderBy(p => p.Info.Name)
|
||||
.Select(p => settingsVmFactory.PluginViewModel(p, ReactiveCommand.Create(() => new Unit()))));
|
||||
// Take all compatible device providers and create a view model for them
|
||||
DeviceProviders = new ObservableCollection<WizardPluginFeatureViewModel>(pluginManagementService.GetAllPlugins()
|
||||
.Where(p => p.Info.IsCompatible)
|
||||
.SelectMany(p => p.Features.Where(f => f.FeatureType.IsAssignableTo(typeof(DeviceProvider))))
|
||||
.OrderBy(f => f.Name)
|
||||
.Select(f => _getPluginFeatureViewModel(f)));
|
||||
LayoutFinderViewModel = layoutFinderViewModel;
|
||||
|
||||
CurrentStep = 1;
|
||||
@ -84,7 +88,7 @@ public partial class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
public ReactiveCommand<string, Unit> SelectLayout { get; }
|
||||
|
||||
public string Version { get; }
|
||||
public ObservableCollection<PluginViewModel> DeviceProviders { get; }
|
||||
public ObservableCollection<WizardPluginFeatureViewModel> DeviceProviders { get; }
|
||||
public LayoutFinderViewModel LayoutFinderViewModel { get; }
|
||||
|
||||
public bool IsAutoRunSupported => _autoRunProvider != null;
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
<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:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:startupWizard="clr-namespace:Artemis.UI.Screens.StartupWizard"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.StartupWizard.WizardPluginFeatureView"
|
||||
x:DataType="startupWizard:WizardPluginFeatureViewModel">
|
||||
<Grid RowDefinitions="*,Auto">
|
||||
<Grid Grid.Row="0" RowDefinitions="Auto,Auto,*" ColumnDefinitions="80,*, Auto">
|
||||
<shared:ArtemisIcon Icon="{CompiledBinding Plugin.Info.ResolvedIcon}"
|
||||
Width="48"
|
||||
Height="48"
|
||||
Margin="0 5 0 0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Classes="no-margin">
|
||||
<Run Classes="h5" Text="{CompiledBinding PluginFeature.Name}" />
|
||||
<Run Classes="subtitle" Text="{CompiledBinding Plugin.Info.Name}" />
|
||||
</TextBlock>
|
||||
|
||||
<ItemsControl Grid.Column="2" Grid.Row="0" IsVisible="{CompiledBinding Platforms.Count}" ItemsSource="{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"
|
||||
Text="{CompiledBinding Plugin.Info.Description}" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,Auto">
|
||||
<Button Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
IsVisible="{CompiledBinding OpenSettings.CanExecute^}" Command="{CompiledBinding OpenSettings}">
|
||||
Settings
|
||||
</Button>
|
||||
<CheckBox Name="EnabledToggle"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{CompiledBinding !Enabling}"
|
||||
IsChecked="{CompiledBinding IsEnabled, Mode=OneWay}"
|
||||
IsEnabled="{CompiledBinding Plugin.Info.IsCompatible}">
|
||||
<StackPanel x:Name="EnableText" Orientation="Horizontal">
|
||||
<TextBlock>Enable feature</TextBlock>
|
||||
<avalonia:MaterialIcon Kind="ShieldHalfFull"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip.Tip="Plugin requires admin rights"
|
||||
IsVisible="{CompiledBinding Plugin.Info.RequiresAdmin}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
|
||||
<ProgressBar Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{CompiledBinding Enabling}"
|
||||
IsIndeterminate="True" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,22 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Artemis.UI.Screens.StartupWizard;
|
||||
|
||||
public partial class WizardPluginFeatureView : ReactiveUserControl<WizardPluginFeatureViewModel>
|
||||
{
|
||||
public WizardPluginFeatureView()
|
||||
{
|
||||
InitializeComponent();
|
||||
EnabledToggle.Click += EnabledToggleOnClick;
|
||||
}
|
||||
|
||||
private void EnabledToggleOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => ViewModel?.UpdateEnabled(!ViewModel.IsEnabled));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Screens.Plugins;
|
||||
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 PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.StartupWizard;
|
||||
|
||||
public partial class WizardPluginFeatureViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IWindowService _windowService;
|
||||
private Window? _settingsWindow;
|
||||
[Notify] private bool _canInstallPrerequisites;
|
||||
[Notify] private bool _canRemovePrerequisites;
|
||||
[Notify] private bool _enabling;
|
||||
|
||||
public WizardPluginFeatureViewModel(PluginFeatureInfo pluginFeature, ICoreService coreService, IWindowService windowService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
PluginFeature = pluginFeature;
|
||||
Plugin = pluginFeature.Plugin;
|
||||
|
||||
_coreService = coreService;
|
||||
_windowService = windowService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
|
||||
Platforms = new ObservableCollection<PluginPlatformViewModel>();
|
||||
if (Plugin.Info.Platforms != null)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(vm => vm.IsEnabled, e => e && Plugin.ConfigurationDialog != null));
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureChanged;
|
||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureChanged;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
pluginManagementService.PluginFeatureEnabled -= PluginManagementServiceOnPluginFeatureChanged;
|
||||
pluginManagementService.PluginFeatureDisabled -= PluginManagementServiceOnPluginFeatureChanged;
|
||||
_settingsWindow?.Close();
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
|
||||
|
||||
public ObservableCollection<PluginPlatformViewModel> Platforms { get; }
|
||||
|
||||
public Plugin Plugin { get; }
|
||||
public PluginFeatureInfo PluginFeature { get; }
|
||||
public bool IsEnabled => PluginFeature.Instance != null && PluginFeature.Instance.IsEnabled;
|
||||
|
||||
public async Task UpdateEnabled(bool enable)
|
||||
{
|
||||
if (Enabling)
|
||||
return;
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PluginFeature.AlwaysEnabled)
|
||||
await Task.Run(() => _pluginManagementService.DisablePlugin(Plugin, true));
|
||||
else if (PluginFeature.Instance != null)
|
||||
await Task.Run(() => _pluginManagementService.DisablePluginFeature(PluginFeature.Instance, true));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await ShowUpdateEnableFailure(enable, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Enabling = true;
|
||||
if (Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||
{
|
||||
bool confirmed = await _windowService.ShowConfirmContentDialog("Enable feature", "This feature requires admin rights, are you sure you want to enable it?");
|
||||
if (!confirmed)
|
||||
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()))
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (!Plugin.IsEnabled)
|
||||
_pluginManagementService.EnablePlugin(Plugin, true, true);
|
||||
if (PluginFeature.Instance != null && !PluginFeature.Instance.IsEnabled)
|
||||
_pluginManagementService.EnablePluginFeature(PluginFeature.Instance, true);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await ShowUpdateEnableFailure(enable, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabling = false;
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteOpenSettings()
|
||||
{
|
||||
if (Plugin.ConfigurationDialog == null)
|
||||
return;
|
||||
|
||||
if (_settingsWindow != null)
|
||||
{
|
||||
_settingsWindow.WindowState = WindowState.Normal;
|
||||
_settingsWindow.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Plugin.Resolve(Plugin.ConfigurationDialog.Type) is not PluginConfigurationViewModel viewModel)
|
||||
throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}");
|
||||
|
||||
_settingsWindow = _windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
|
||||
_settingsWindow.Closed += (_, _) => _settingsWindow = null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowUpdateEnableFailure(bool enable, Exception e)
|
||||
{
|
||||
string action = enable ? "enable" : "disable";
|
||||
ContentDialogBuilder builder = _windowService.CreateContentDialog()
|
||||
.WithTitle($"Failed to {action} plugin {Plugin.Info.Name}")
|
||||
.WithContent(e.Message)
|
||||
.HavingPrimaryButton(b => b.WithText("View logs").WithAction(ShowLogsFolder));
|
||||
// If available, add a secondary button pointing to the support page
|
||||
if (Plugin.Info.HelpPage != null)
|
||||
builder = builder.HavingSecondaryButton(b => b.WithText("Open support page").WithAction(() => Utilities.OpenUrl(Plugin.Info.HelpPage.ToString())));
|
||||
|
||||
await builder.ShowAsync();
|
||||
}
|
||||
|
||||
private void ShowLogsFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Utilities.OpenFolder(Constants.LogsFolder);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureChanged(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature.Info != PluginFeature)
|
||||
return;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
if (!IsEnabled)
|
||||
_settingsWindow?.Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user