mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
UI - Some VM restructure and added plugin VMs (WIP)
This commit is contained in:
parent
8ebd098186
commit
93cfc0e001
@ -26,4 +26,9 @@
|
||||
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Services\WindowService\ExceptionDialogView.axaml.cs">
|
||||
<DependentUpon>%(Filename)</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindowservice/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Avalonia.Shared.Utilities;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Shared.Services.Builders
|
||||
{
|
||||
@ -36,6 +38,19 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add a filter to the dialog
|
||||
/// </summary>
|
||||
public NotificationBuilder HavingButton(Action<NotificationButtonBuilder> configure)
|
||||
{
|
||||
NotificationButtonBuilder builder = new();
|
||||
configure(builder);
|
||||
_infoBar.ActionButton = builder.Build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBuilder WithSeverity(NotificationSeverity severity)
|
||||
{
|
||||
_infoBar.Severity = (InfoBarSeverity) severity;
|
||||
@ -71,6 +86,31 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationButtonBuilder
|
||||
{
|
||||
private string _text = "Text";
|
||||
private Action? _action;
|
||||
|
||||
public NotificationButtonBuilder WithText(string text)
|
||||
{
|
||||
_text = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationButtonBuilder WithAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IControl Build()
|
||||
{
|
||||
return _action != null
|
||||
? new Button {Content = _text, Command = new DelegateCommand(_ => _action())}
|
||||
: new Button {Content = _text};
|
||||
}
|
||||
}
|
||||
|
||||
public enum NotificationSeverity
|
||||
{
|
||||
Informational,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Avalonia.Shared.Services.Builders;
|
||||
using Avalonia.Controls;
|
||||
|
||||
@ -7,20 +8,27 @@ namespace Artemis.UI.Avalonia.Shared.Services.Interfaces
|
||||
public interface IWindowService : IArtemisSharedUIService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a view model instance of type <typeparamref name="T" /> and shows its corresponding View as a window
|
||||
/// Creates a view model instance of type <typeparamref name="T" /> and shows its corresponding View as a window
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of view model to create</typeparam>
|
||||
/// <returns>The created view model</returns>
|
||||
T ShowWindow<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Given a ViewModel, show its corresponding View as a window
|
||||
/// 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);
|
||||
|
||||
/// <summary>
|
||||
/// Given a ViewModel, show its corresponding View as a Dialog
|
||||
/// Shows a dialog displaying the given exception
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the dialog</param>
|
||||
/// <param name="exception">The exception to display</param>
|
||||
void ShowExceptionDialog(string title, Exception exception);
|
||||
|
||||
/// <summary>
|
||||
/// Given a ViewModel, show its corresponding View as a Dialog
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type</typeparam>
|
||||
/// <param name="viewModel">ViewModel to show the View for</param>
|
||||
@ -28,13 +36,13 @@ namespace Artemis.UI.Avalonia.Shared.Services.Interfaces
|
||||
Task<T> ShowDialogAsync<T>(object viewModel);
|
||||
|
||||
/// <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>
|
||||
/// <returns>The builder that can be used to configure the dialog</returns>
|
||||
OpenFileDialogBuilder CreateOpenFileDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a save file dialog, use the fluent API to configure it
|
||||
/// Creates a save file dialog, use the fluent API to configure it
|
||||
/// </summary>
|
||||
/// <returns>The builder that can be used to configure the dialog</returns>
|
||||
SaveFileDialogBuilder CreateSaveFileDialog();
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
<Window 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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Shared.Services.ExceptionDialogView"
|
||||
Title="ExceptionDialogView">
|
||||
Eh you got an exception but I didn't write the viewer yet :(
|
||||
</Window>
|
||||
@ -0,0 +1,20 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Shared.Services
|
||||
{
|
||||
public partial class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
||||
{
|
||||
public ExceptionDialogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Shared.Services
|
||||
{
|
||||
public class ExceptionDialogViewModel : ActivatableViewModelBase
|
||||
{
|
||||
public ExceptionDialogViewModel(string title, Exception exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ namespace Artemis.UI.Avalonia.Shared.Services
|
||||
internal class WindowService : IWindowService
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private bool _exceptionDialogOpen;
|
||||
|
||||
public WindowService(IKernel kernel)
|
||||
{
|
||||
@ -42,6 +43,23 @@ namespace Artemis.UI.Avalonia.Shared.Services
|
||||
window.Show();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ShowExceptionDialog(string title, Exception exception)
|
||||
{
|
||||
if (_exceptionDialogOpen)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_exceptionDialogOpen = true;
|
||||
ShowDialogAsync<object>(new ExceptionDialogViewModel(title, exception)).GetAwaiter().GetResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_exceptionDialogOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T> ShowDialogAsync<T>(object viewModel)
|
||||
{
|
||||
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic)
|
||||
60
src/Artemis.UI.Avalonia.Shared/Utilities/DelegateCommand.cs
Normal file
60
src/Artemis.UI.Avalonia.Shared/Utilities/DelegateCommand.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Shared.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a command that simply calls a delegate when invoked
|
||||
/// </summary>
|
||||
public class DelegateCommand : ICommand
|
||||
{
|
||||
private readonly Predicate<object?>? _canExecute;
|
||||
private readonly Action<object?> _execute;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DelegateCommand" /> class
|
||||
/// </summary>
|
||||
/// <param name="execute">The delegate to execute</param>
|
||||
public DelegateCommand(Action<object?> execute) : this(execute, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DelegateCommand" /> class with a predicate indicating whether the command
|
||||
/// can be executed
|
||||
/// </summary>
|
||||
/// <param name="execute">The delegate to execute</param>
|
||||
/// <param name="canExecute">The predicate that determines whether the command can execute</param>
|
||||
public DelegateCommand(Action<object?> execute, Predicate<object?>? canExecute)
|
||||
{
|
||||
_execute = execute;
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="CanExecuteChanged" /> event
|
||||
/// </summary>
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
if (_canExecute == null)
|
||||
return true;
|
||||
|
||||
return _canExecute(parameter);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
_execute(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Images\home-banner.png" />
|
||||
<None Remove="Screens\Settings\Tabs\Plugins\Views\PluginFeatureView.xaml" />
|
||||
<None Remove="Screens\Settings\Tabs\Plugins\Views\PluginSettingsView.xaml" />
|
||||
<None Remove="Screens\Settings\Tabs\Plugins\Views\PluginSettingsWindowView.xaml" />
|
||||
<None Remove="Screens\SurfaceEditor\Views\ListDeviceView.xaml" />
|
||||
<None Remove="Screens\SurfaceEditor\Views\SurfaceDeviceView.xaml" />
|
||||
</ItemGroup>
|
||||
@ -59,6 +62,9 @@
|
||||
<Compile Update="Screens\Root\Views\SidebarView.axaml.cs">
|
||||
<DependentUpon>%(Filename)</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Screens\Settings\Tabs\Plugins\Views\PluginsTabView.axaml.cs">
|
||||
<DependentUpon>%(Filename)</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Screens\Sidebar\Views\SidebarView.axaml.cs">
|
||||
<DependentUpon>SidebarView.axaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@ -77,6 +83,18 @@
|
||||
<Content Include="Assets\Images\Logo\bow.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Screens\Settings\Tabs\Plugins\Views\PluginFeatureView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Screens\Settings\Tabs\Plugins\Views\PluginSettingsView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Screens\Settings\Tabs\Plugins\Views\PluginSettingsWindowView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Screens\SurfaceEditor\Views\ListDeviceView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@ -6,7 +6,7 @@ using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Avalonia.Ninject.Factories;
|
||||
using Artemis.UI.Avalonia.Screens.Home.ViewModels;
|
||||
using Artemis.UI.Avalonia.Screens.Settings.ViewModels;
|
||||
using Artemis.UI.Avalonia.Screens.Settings;
|
||||
using Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels;
|
||||
using Artemis.UI.Avalonia.Screens.Workshop.ViewModels;
|
||||
using Material.Icons;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Views.SettingsView">
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.SettingsView">
|
||||
<TabControl Margin="12" Items="{Binding SettingTabs}">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@ -1,8 +1,7 @@
|
||||
using Artemis.UI.Avalonia.Screens.Settings.ViewModels;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Views
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings
|
||||
{
|
||||
public class SettingsView : ReactiveUserControl<SettingsViewModel>
|
||||
{
|
||||
@ -1,7 +1,8 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings
|
||||
{
|
||||
public class SettingsViewModel : MainScreenViewModel
|
||||
{
|
||||
@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Avalonia.Shared.Services.Builders;
|
||||
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.ViewModels
|
||||
{
|
||||
public class PluginFeatureViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IWindowService _windowService;
|
||||
private bool _enabling;
|
||||
|
||||
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
|
||||
bool showShield,
|
||||
ICoreService coreService,
|
||||
IWindowService windowService,
|
||||
INotificationService notificationService,
|
||||
IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_coreService = coreService;
|
||||
_windowService = windowService;
|
||||
_notificationService = notificationService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
|
||||
FeatureInfo = pluginFeatureInfo;
|
||||
ShowShield = FeatureInfo.Plugin.Info.RequiresAdmin && showShield;
|
||||
|
||||
_pluginManagementService.PluginFeatureEnabling += OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled += PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled += PluginOnToggled;
|
||||
}
|
||||
|
||||
public PluginFeatureInfo FeatureInfo { get; }
|
||||
public Exception? LoadException => FeatureInfo.LoadException;
|
||||
|
||||
public bool ShowShield { get; }
|
||||
|
||||
public bool Enabling
|
||||
{
|
||||
get => _enabling;
|
||||
set => this.RaiseAndSetIfChanged(ref _enabling, value);
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
|
||||
set => Task.Run(() => UpdateEnabled(value));
|
||||
}
|
||||
|
||||
public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled;
|
||||
public bool CanInstallPrerequisites => FeatureInfo.Prerequisites.Any();
|
||||
public bool CanRemovePrerequisites => FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any());
|
||||
public bool IsPopupEnabled => CanInstallPrerequisites || CanRemovePrerequisites;
|
||||
|
||||
public void ShowLogsFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ViewLoadException()
|
||||
{
|
||||
if (LoadException != null)
|
||||
_windowService.ShowExceptionDialog("Feature failed to enable", LoadException);
|
||||
}
|
||||
|
||||
public async Task InstallPrerequisites()
|
||||
{
|
||||
if (FeatureInfo.Prerequisites.Any())
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo});
|
||||
}
|
||||
|
||||
public async Task RemovePrerequisites()
|
||||
{
|
||||
if (FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any()))
|
||||
{
|
||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo});
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_pluginManagementService.PluginFeatureEnabling -= OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled -= PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled -= PluginOnToggled;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private async Task UpdateEnabled(bool enable)
|
||||
{
|
||||
if (IsEnabled == enable)
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FeatureInfo.Instance == null)
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
_notificationService.CreateNotification()
|
||||
.WithMessage($"Feature '{FeatureInfo.Name}' is in a broken state and cannot enable.")
|
||||
.HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder))
|
||||
.WithSeverity(NotificationSeverity.Error)
|
||||
.Show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
Enabling = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (FeatureInfo.Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("Enable feature", "The plugin of this feature requires admin rights, are you sure you want to enable it?");
|
||||
if (!confirmed)
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all prerequisites are met async
|
||||
if (!FeatureInfo.ArePrerequisitesMet())
|
||||
{
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo});
|
||||
if (!FeatureInfo.ArePrerequisitesMet())
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_notificationService.CreateNotification()
|
||||
.WithMessage($"Failed to enable '{FeatureInfo.Name}'.\r\n{e.Message}")
|
||||
.HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder))
|
||||
.WithSeverity(NotificationSeverity.Error)
|
||||
.Show();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabling = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true);
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFeatureEnabling(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature != FeatureInfo.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Enabling = true;
|
||||
}
|
||||
|
||||
private void OnFeatureEnableStopped(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature != FeatureInfo.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Enabling = false;
|
||||
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
this.RaisePropertyChanged(nameof(LoadException));
|
||||
}
|
||||
|
||||
private void PluginOnToggled(object? sender, EventArgs e)
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(CanToggleEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.ViewModels;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Plugins;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ninject;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
public class PluginSettingsViewModel : Conductor<PluginFeatureViewModel>.Collection.AllActive
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||
private readonly IWindowManager _windowManager;
|
||||
private bool _canInstallPrerequisites;
|
||||
private bool _canRemovePrerequisites;
|
||||
private bool _enabling;
|
||||
private bool _isSettingsPopupOpen;
|
||||
private Plugin _plugin;
|
||||
|
||||
public PluginSettingsViewModel(Plugin plugin,
|
||||
ISettingsVmFactory settingsVmFactory,
|
||||
ICoreService coreService,
|
||||
IWindowManager windowManager,
|
||||
IDialogService dialogService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IMessageService messageService)
|
||||
{
|
||||
Plugin = plugin;
|
||||
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
_coreService = coreService;
|
||||
_windowManager = windowManager;
|
||||
_dialogService = dialogService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_messageService = messageService;
|
||||
}
|
||||
|
||||
public Plugin Plugin
|
||||
{
|
||||
get => _plugin;
|
||||
set => SetAndNotify(ref _plugin, value);
|
||||
}
|
||||
|
||||
public bool Enabling
|
||||
{
|
||||
get => _enabling;
|
||||
set => SetAndNotify(ref _enabling, value);
|
||||
}
|
||||
|
||||
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
||||
public bool CanOpenSettings => IsEnabled && Plugin.ConfigurationDialog != null;
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => Plugin.IsEnabled;
|
||||
set => Task.Run(() => UpdateEnabled(value));
|
||||
}
|
||||
|
||||
public bool IsSettingsPopupOpen
|
||||
{
|
||||
get => _isSettingsPopupOpen;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _isSettingsPopupOpen, value)) return;
|
||||
CheckPrerequisites();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInstallPrerequisites
|
||||
{
|
||||
get => _canInstallPrerequisites;
|
||||
set => SetAndNotify(ref _canInstallPrerequisites, value);
|
||||
}
|
||||
|
||||
public bool CanRemovePrerequisites
|
||||
{
|
||||
get => _canRemovePrerequisites;
|
||||
set => SetAndNotify(ref _canRemovePrerequisites, value);
|
||||
}
|
||||
|
||||
public void OpenSettings()
|
||||
{
|
||||
PluginConfigurationDialog configurationViewModel = (PluginConfigurationDialog) Plugin.ConfigurationDialog;
|
||||
if (configurationViewModel == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
PluginConfigurationViewModel viewModel = (PluginConfigurationViewModel) Plugin.Kernel.Get(configurationViewModel.Type);
|
||||
_windowManager.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenPluginDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Plugin.Directory.FullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Reload()
|
||||
{
|
||||
bool wasEnabled = IsEnabled;
|
||||
|
||||
_pluginManagementService.UnloadPlugin(Plugin);
|
||||
Items.Clear();
|
||||
|
||||
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||
|
||||
if (wasEnabled)
|
||||
await UpdateEnabled(true);
|
||||
|
||||
_messageService.ShowMessage("Reloaded plugin.");
|
||||
}
|
||||
|
||||
public async Task InstallPrerequisites()
|
||||
{
|
||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
|
||||
|
||||
if (subjects.Any(s => s.Prerequisites.Any()))
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, subjects);
|
||||
}
|
||||
|
||||
public async Task RemovePrerequisites(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.Prerequisites.Any(p => p.UninstallActions.Any())))
|
||||
{
|
||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_dialogService, subjects, forPluginRemoval ? "SKIP, REMOVE PLUGIN" : "CANCEL");
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveSettings()
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
|
||||
if (!confirmed)
|
||||
return;
|
||||
|
||||
bool wasEnabled = IsEnabled;
|
||||
|
||||
if (IsEnabled)
|
||||
await UpdateEnabled(false);
|
||||
|
||||
_pluginManagementService.RemovePluginSettings(Plugin);
|
||||
|
||||
if (wasEnabled)
|
||||
await UpdateEnabled(true);
|
||||
|
||||
_messageService.ShowMessage("Cleared plugin settings.");
|
||||
}
|
||||
|
||||
public async Task Remove()
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("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.Prerequisites.Any(p => p.UninstallActions.Any())))
|
||||
await RemovePrerequisites(true);
|
||||
|
||||
try
|
||||
{
|
||||
_pluginManagementService.RemovePlugin(Plugin, false);
|
||||
((PluginSettingsTabViewModel) Parent).GetPluginInstances();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Failed to remove plugin", e);
|
||||
throw;
|
||||
}
|
||||
|
||||
_messageService.ShowMessage("Removed plugin.");
|
||||
}
|
||||
|
||||
public void ShowLogsFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenUri(Uri uri)
|
||||
{
|
||||
Core.Utilities.OpenUrl(uri.ToString());
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginToggled(object sender, PluginEventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
|
||||
private async Task UpdateEnabled(bool enable)
|
||||
{
|
||||
if (IsEnabled == enable)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
Enabling = true;
|
||||
|
||||
if (Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("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(_dialogService, subjects);
|
||||
if (!subjects.All(s => s.ArePrerequisitesMet()))
|
||||
{
|
||||
CancelEnable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_pluginManagementService.EnablePlugin(Plugin, true, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_messageService.ShowMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabling = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
_pluginManagementService.DisablePlugin(Plugin, true);
|
||||
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
|
||||
private void CancelEnable()
|
||||
{
|
||||
Enabling = false;
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
|
||||
private void CheckPrerequisites()
|
||||
{
|
||||
CanInstallPrerequisites = Plugin.Info.Prerequisites.Any() ||
|
||||
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.Prerequisites.Any());
|
||||
CanRemovePrerequisites = Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()) ||
|
||||
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.Prerequisites.Any(p => p.UninstallActions.Any()));
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||
|
||||
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
public class PluginSettingsWindowViewModel : Conductor<PluginConfigurationViewModel>
|
||||
{
|
||||
private readonly PluginConfigurationViewModel _configurationViewModel;
|
||||
|
||||
public PluginSettingsWindowViewModel(PluginConfigurationViewModel configurationViewModel)
|
||||
{
|
||||
_configurationViewModel = configurationViewModel ?? throw new ArgumentNullException(nameof(configurationViewModel));
|
||||
Plugin = configurationViewModel.Plugin;
|
||||
}
|
||||
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
ActiveItem = _configurationViewModel;
|
||||
ActiveItem.Closed += ActiveItemOnClosed;
|
||||
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
private void ActiveItemOnClosed(object sender, CloseEventArgs e)
|
||||
{
|
||||
ActiveItem.Closed -= ActiveItemOnClosed;
|
||||
RequestClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
|
||||
{
|
||||
public class PluginsTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
public PluginsTabViewModel()
|
||||
{
|
||||
DisplayName = "Plugins";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.Views.PluginFeatureView">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@ -0,0 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.Views
|
||||
{
|
||||
public partial class PluginFeatureView : UserControl
|
||||
{
|
||||
public PluginFeatureView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginFeatureView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:viewModels="clr-namespace:Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance viewModels:PluginFeatureViewModel}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="-3 -8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="30" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon column -->
|
||||
<shared:ArtemisIcon Grid.Column="0"
|
||||
Icon="{Binding FeatureInfo.ResolvedIcon}"
|
||||
Width="20"
|
||||
Height="20"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" />
|
||||
|
||||
<Button Grid.Column="0"
|
||||
Margin="-8"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, FallbackValue=Collapsed}"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Foreground="#E74C4C"
|
||||
ToolTip="An exception occurred while enabling this feature, click to view"
|
||||
Command="{s:Action ViewLoadException}">
|
||||
<materialDesign:PackIcon Kind="AlertCircle" />
|
||||
</Button>
|
||||
|
||||
<!-- Display name column -->
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding FeatureInfo.Name}"
|
||||
Style="{StaticResource MaterialDesignTextBlock}"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip="{Binding FeatureInfo.Description}" />
|
||||
|
||||
<!-- Enable toggle column -->
|
||||
<StackPanel Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Top"
|
||||
ToolTip="This feature cannot be disabled without disabling the whole plugin"
|
||||
ToolTipService.IsEnabled="{Binding FeatureInfo.AlwaysEnabled}">
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||
ToolTip="Plugin requires admin rights"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 5 0"
|
||||
Visibility="{Binding ShowShield, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
||||
IsChecked="{Binding IsEnabled}"
|
||||
IsEnabled="{Binding CanToggleEnabled}">
|
||||
Feature enabled
|
||||
</CheckBox>
|
||||
|
||||
<materialDesign:PopupBox Style="{StaticResource MaterialDesignToolPopupBox}"
|
||||
Margin="0 -4 -10 -4"
|
||||
Foreground="{StaticResource MaterialDesignBody}"
|
||||
IsEnabled="{Binding IsPopupEnabled}">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action InstallPrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="CheckAll" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Install prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action RemovePrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Delete" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Remove prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay, FallbackValue=Collapsed}">
|
||||
<ProgressBar Style="{StaticResource MaterialDesignCircularProgressBar}" Value="0" IsIndeterminate="True" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,8 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.Views.PluginSettingsView">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@ -0,0 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.Views
|
||||
{
|
||||
public partial class PluginSettingsView : UserControl
|
||||
{
|
||||
public PluginSettingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
||||
d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<materialDesign:Card Width="900">
|
||||
<Grid Margin="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4*" />
|
||||
<ColumnDefinition Width="5*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" Margin="0 10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<shared:ArtemisIcon Icon="{Binding Plugin.Info.ResolvedIcon}"
|
||||
Width="48"
|
||||
Height="48"
|
||||
Margin="0 5 0 0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Style="{StaticResource MaterialDesignBody2TextBlock}"
|
||||
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
|
||||
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Name}"
|
||||
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
|
||||
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignBody2TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
Text="{Binding Plugin.Info.Author}"
|
||||
Visibility="{Binding Plugin.Info.Author, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="2"
|
||||
TextWrapping="Wrap"
|
||||
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
|
||||
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Description}"
|
||||
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
|
||||
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"
|
||||
Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel VerticalAlignment="Bottom" Orientation="Horizontal">
|
||||
<Button VerticalAlignment="Bottom"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
ToolTip="Open the plugins settings window"
|
||||
Margin="4"
|
||||
Command="{s:Action OpenSettings}">
|
||||
SETTINGS
|
||||
</Button>
|
||||
|
||||
<materialDesign:PopupBox Style="{StaticResource MaterialDesignToolPopupBox}"
|
||||
Padding="2 0 2 0"
|
||||
Foreground="{StaticResource MaterialDesignBody}"
|
||||
IsPopupOpen="{Binding IsSettingsPopupOpen, Mode=TwoWay}">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action OpenPluginDirectory}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="FolderOpen" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Open plugin directory</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Reload}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Reload" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Reload plugin</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Command="{s:Action InstallPrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="CheckAll" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Install prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action RemovePrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Delete" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Remove prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Command="{s:Action RemoveSettings}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="DatabaseRemove" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Clear plugin settings</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Remove}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="DeleteForever" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Remove plugin</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="{Binding Plugin.Info.Website}"
|
||||
Visibility="{Binding Plugin.Info.Website, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}"
|
||||
Command="{s:Action OpenUri}"
|
||||
CommandParameter="{Binding Plugin.Info.Website}">
|
||||
<materialDesign:PackIcon Kind="Web" Width="20" Height="20" />
|
||||
</Button>
|
||||
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="{Binding Plugin.Info.Repository}"
|
||||
Visibility="{Binding Plugin.Info.Repository, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}"
|
||||
Command="{s:Action OpenUri}"
|
||||
CommandParameter="{Binding Plugin.Info.Repository}">
|
||||
<materialDesign:PackIcon Kind="Git" Width="20" Height="20" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
|
||||
Style="{StaticResource MaterialDesignAccentCheckBox}" IsChecked="{Binding IsEnabled}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock>Plugin enabled</TextBlock>
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip="Plugin requires admin rights"
|
||||
Visibility="{Binding Plugin.Info.RequiresAdmin, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
|
||||
<ProgressBar Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
|
||||
Style="{StaticResource MaterialDesignCircularProgressBar}" Value="0"
|
||||
IsIndeterminate="True" />
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" BorderBrush="{StaticResource MaterialDesignDivider}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Margin="10 10 0 5" Style="{StaticResource MaterialDesignBody2TextBlock}">Plugin features</TextBlock>
|
||||
<ListBox Grid.Row="1"
|
||||
MaxHeight="135"
|
||||
ItemsSource="{Binding Items}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VirtualizingPanel.ScrollUnit="Pixel">
|
||||
<b:Interaction.Behaviors>
|
||||
<shared:ScrollParentWhenAtMax />
|
||||
</b:Interaction.Behaviors>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
</UserControl>
|
||||
@ -0,0 +1,9 @@
|
||||
<Window 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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.Views.PluginSettingsWindowView"
|
||||
Title="PluginSettingsWindowView">
|
||||
Welcome to Avalonia!
|
||||
</Window>
|
||||
@ -0,0 +1,22 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Plugins.Views
|
||||
{
|
||||
public partial class PluginSettingsWindowView : Window
|
||||
{
|
||||
public PluginSettingsWindowView()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<controls:MaterialWindow x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginSettingsWindowView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
Title="Plugin Configuration | Artemis"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
|
||||
UseLayoutRounding="True"
|
||||
Width="800"
|
||||
Height="800"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PluginSettingsWindowViewModel}"
|
||||
Icon="/Resources/Images/Logo/bow.ico">
|
||||
<materialDesign:DialogHost IsTabStop="False"
|
||||
Focusable="False"
|
||||
Identifier="PluginSettingsDialog"
|
||||
DialogTheme="Inherit">
|
||||
<DockPanel>
|
||||
<controls:AppBar Type="Dense" Title="{Binding ActiveItem.Plugin.Info.Name}" DockPanel.Dock="Top" Margin="-18 0 0 0" ShowShadow="False">
|
||||
<controls:AppBar.AppIcon>
|
||||
<shared:ArtemisIcon Icon="{Binding Plugin.Info.ResolvedIcon}"
|
||||
Margin="0 5 0 0"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top" />
|
||||
</controls:AppBar.AppIcon>
|
||||
</controls:AppBar>
|
||||
|
||||
<ContentControl s:View.Model="{Binding ActiveItem}" />
|
||||
</DockPanel>
|
||||
</materialDesign:DialogHost>
|
||||
</controls:MaterialWindow>
|
||||
@ -3,6 +3,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Views.PluginsTabView">
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Views.PluginsTabView">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@ -1,8 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Views
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Views
|
||||
{
|
||||
public partial class PluginsTabView : UserControl
|
||||
{
|
||||
@ -6,7 +6,7 @@ using Avalonia.Media.Imaging;
|
||||
using Flurl.Http;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
|
||||
{
|
||||
public class AboutTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
@ -0,0 +1,10 @@
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
|
||||
{
|
||||
public class DevicesTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
public DevicesTabViewModel()
|
||||
{
|
||||
DisplayName = "Devices";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ using Artemis.UI.Avalonia.Services.Interfaces;
|
||||
using ReactiveUI;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
|
||||
{
|
||||
public class GeneralTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
@ -6,7 +6,7 @@
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="1400"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Views.AboutTabView">
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Views.AboutTabView">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Margin="15" MaxWidth="800">
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="Auto,*,Auto">
|
||||
@ -1,10 +1,8 @@
|
||||
using Artemis.UI.Avalonia.Screens.Settings.ViewModels;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Views
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Views
|
||||
{
|
||||
public partial class AboutTabView : ReactiveUserControl<AboutTabViewModel>
|
||||
{
|
||||
@ -3,6 +3,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Views.DevicesTabView">
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Views.DevicesTabView">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@ -1,8 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Views
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Views
|
||||
{
|
||||
public partial class DevicesTabView : UserControl
|
||||
{
|
||||
@ -7,7 +7,7 @@
|
||||
xmlns:layerBrushes="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400"
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Views.GeneralTabView">
|
||||
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Views.GeneralTabView">
|
||||
|
||||
<StackPanel Margin="15" MaxWidth="1000">
|
||||
<!-- General settings -->
|
||||
@ -1,10 +1,8 @@
|
||||
using Artemis.UI.Avalonia.Screens.Settings.ViewModels;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Views
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Views
|
||||
{
|
||||
public partial class GeneralTabView : ReactiveUserControl<GeneralTabViewModel>
|
||||
{
|
||||
@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
|
||||
{
|
||||
public class DevicesTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
public DevicesTabViewModel()
|
||||
{
|
||||
DisplayName = "Devices";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
|
||||
{
|
||||
public class PluginsTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
public PluginsTabViewModel()
|
||||
{
|
||||
DisplayName = "Plugins";
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user