mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
UI - Implemented exception dialog
UI - Fixed notification opacity UI - Added position control to notifications
This commit is contained in:
parent
b963aa0909
commit
63eb0ca9b3
@ -455,17 +455,17 @@
|
|||||||
</member>
|
</member>
|
||||||
<member name="T:Artemis.UI.Avalonia.Shared.ViewModelBase">
|
<member name="T:Artemis.UI.Avalonia.Shared.ViewModelBase">
|
||||||
<summary>
|
<summary>
|
||||||
Represents the base class for Artemis view models
|
Represents the base class for Artemis view models
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:Artemis.UI.Avalonia.Shared.ViewModelBase.DisplayName">
|
<member name="P:Artemis.UI.Avalonia.Shared.ViewModelBase.DisplayName">
|
||||||
<summary>
|
<summary>
|
||||||
Gets or sets the display name of the view model
|
Gets or sets the display name of the view model
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase">
|
<member name="T:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase">
|
||||||
<summary>
|
<summary>
|
||||||
Represents the base class for Artemis view models that are interested in the activated event
|
Represents the base class for Artemis view models that are interested in the activated event
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.#ctor">
|
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.#ctor">
|
||||||
@ -473,11 +473,11 @@
|
|||||||
</member>
|
</member>
|
||||||
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose(System.Boolean)">
|
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose(System.Boolean)">
|
||||||
<summary>
|
<summary>
|
||||||
Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||||
</summary>
|
</summary>
|
||||||
<param name="disposing">
|
<param name="disposing">
|
||||||
<see langword="true" /> to release both managed and unmanaged resources;
|
<see langword="true" /> to release both managed and unmanaged resources;
|
||||||
<see langword="false" /> to release only unmanaged resources.
|
<see langword="false" /> to release only unmanaged resources.
|
||||||
</param>
|
</param>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Activator">
|
<member name="P:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Activator">
|
||||||
@ -488,20 +488,18 @@
|
|||||||
</member>
|
</member>
|
||||||
<member name="T:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1">
|
<member name="T:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1">
|
||||||
<summary>
|
<summary>
|
||||||
Represents the base class for Artemis view models used to drive dialogs
|
Represents the base class for Artemis view models used to drive dialogs
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.#ctor">
|
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Close(`0)">
|
||||||
<inheritdoc />
|
|
||||||
</member>
|
|
||||||
<member name="P:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Close">
|
|
||||||
<summary>
|
<summary>
|
||||||
Closes the dialog with a given result
|
Closes the dialog with the given <paramref name="result" />
|
||||||
</summary>
|
</summary>
|
||||||
|
<param name="result">The result of the dialog</param>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Cancel">
|
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Cancel">
|
||||||
<summary>
|
<summary>
|
||||||
Closes the dialog without a result
|
Closes the dialog without a result
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Shared.Events
|
||||||
|
{
|
||||||
|
internal class DialogClosedEventArgs<TResult> : EventArgs
|
||||||
|
{
|
||||||
|
public TResult Result { get; }
|
||||||
|
|
||||||
|
public DialogClosedEventArgs(TResult result)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
@ -17,7 +18,12 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
|
|||||||
public NotificationBuilder(Window parent)
|
public NotificationBuilder(Window parent)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
_infoBar = new InfoBar {Classes = Classes.Parse("notification-info-bar")};
|
_infoBar = new InfoBar
|
||||||
|
{
|
||||||
|
Classes = Classes.Parse("notification-info-bar"),
|
||||||
|
VerticalAlignment = VerticalAlignment.Bottom,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Right
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public NotificationBuilder WithTitle(string? title)
|
public NotificationBuilder WithTitle(string? title)
|
||||||
@ -38,6 +44,17 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NotificationBuilder WithVerticalPosition(VerticalAlignment position)
|
||||||
|
{
|
||||||
|
_infoBar.VerticalAlignment = position;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotificationBuilder WithHorizontalPosition(HorizontalAlignment position)
|
||||||
|
{
|
||||||
|
_infoBar.HorizontalAlignment = position;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a filter to the dialog
|
/// Add a filter to the dialog
|
||||||
@ -105,8 +122,8 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
|
|||||||
|
|
||||||
public IControl Build()
|
public IControl Build()
|
||||||
{
|
{
|
||||||
return _action != null
|
return _action != null
|
||||||
? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action)}
|
? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action)}
|
||||||
: new Button {Content = _text};
|
: new Button {Content = _text};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,47 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
||||||
x:Class="Artemis.UI.Avalonia.Shared.Services.ExceptionDialogView"
|
x:Class="Artemis.UI.Avalonia.Shared.Services.ExceptionDialogView"
|
||||||
Title="ExceptionDialogView">
|
Title="{Binding Title}"
|
||||||
Eh you got an exception but I didn't write the viewer yet :(
|
ExtendClientAreaToDecorationsHint="True"
|
||||||
</Window>
|
Width="800"
|
||||||
|
Height="800"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid>
|
||||||
|
<TextBlock Margin="10" IsHitTestVisible="False" Text="{Binding Title}" />
|
||||||
|
<Grid Margin="0 32 0 0" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<Border Classes="card" Grid.Row="0" Grid.ColumnSpan="2" Margin="10">
|
||||||
|
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0 5">
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
|
<TextBlock Grid.Row="0" Classes="h3">Awww :(</TextBlock>
|
||||||
|
<TextBlock Grid.Row="1">
|
||||||
|
It looks like Artemis ran into an unhandled exception. If this keeps happening feel free to hit us up on Discord.
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBox Grid.Row="2" Text="{Binding Exception, Mode=OneTime}"
|
||||||
|
Margin="0 10"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
IsReadOnly="True"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
FontSize="12"
|
||||||
|
BorderThickness="0" />
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" TextWrapping="Wrap" Margin="15" VerticalAlignment="Center">
|
||||||
|
When reporting errors please don't take a screenshot of the error, instead copy the text, thanks!
|
||||||
|
</TextBlock>
|
||||||
|
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="15">
|
||||||
|
<Button Command="{Binding CopyException}" Classes="AppBarButton" Width="150" Margin="0 0 5 0">
|
||||||
|
Copy exception
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding Close}" Width="150" Margin="5 0 0 0">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -4,7 +4,7 @@ using Avalonia.ReactiveUI;
|
|||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Shared.Services
|
namespace Artemis.UI.Avalonia.Shared.Services
|
||||||
{
|
{
|
||||||
public partial class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
internal class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
|
||||||
{
|
{
|
||||||
public ExceptionDialogView()
|
public ExceptionDialogView()
|
||||||
{
|
{
|
||||||
@ -19,4 +19,4 @@ namespace Artemis.UI.Avalonia.Shared.Services
|
|||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,35 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.UI.Avalonia.Shared.Services.Builders;
|
||||||
|
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Shared.Services
|
namespace Artemis.UI.Avalonia.Shared.Services
|
||||||
{
|
{
|
||||||
public class ExceptionDialogViewModel : DialogViewModelBase<object>
|
internal class ExceptionDialogViewModel : DialogViewModelBase<object>
|
||||||
{
|
{
|
||||||
public ExceptionDialogViewModel(string title, Exception exception)
|
private readonly INotificationService _notificationService;
|
||||||
|
|
||||||
|
public ExceptionDialogViewModel(string title, Exception exception, INotificationService notificationService)
|
||||||
{
|
{
|
||||||
|
_notificationService = notificationService;
|
||||||
|
|
||||||
|
Title = $"Artemis | {title}";
|
||||||
|
Exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title { get; }
|
||||||
|
public Exception Exception { get; }
|
||||||
|
|
||||||
|
public async Task CopyException()
|
||||||
|
{
|
||||||
|
await Application.Current.Clipboard.SetTextAsync(Exception.ToString());
|
||||||
|
_notificationService.CreateNotification()
|
||||||
|
.WithMessage("Copied stack trace to clipboard.")
|
||||||
|
.WithSeverity(NotificationSeverity.Success)
|
||||||
|
.WithHorizontalPosition(HorizontalAlignment.Center)
|
||||||
|
.Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.UI.Avalonia.Shared.Exceptions;
|
using Artemis.UI.Avalonia.Shared.Exceptions;
|
||||||
using Artemis.UI.Avalonia.Shared.Services.Builders;
|
using Artemis.UI.Avalonia.Shared.Services.Builders;
|
||||||
@ -7,9 +8,11 @@ using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Ninject.Parameters;
|
using Ninject.Parameters;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Shared.Services
|
namespace Artemis.UI.Avalonia.Shared.Services
|
||||||
{
|
{
|
||||||
@ -91,25 +94,30 @@ namespace Artemis.UI.Avalonia.Shared.Services
|
|||||||
|
|
||||||
Window window = (Window) Activator.CreateInstance(type)!;
|
Window window = (Window) Activator.CreateInstance(type)!;
|
||||||
window.DataContext = viewModel;
|
window.DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += (_, args) => window.Close(args.Result);
|
||||||
|
viewModel.CancelRequested += (_, _) => window.Close();
|
||||||
|
|
||||||
return await window.ShowDialog<TResult>(parent);
|
return await window.ShowDialog<TResult>(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowExceptionDialog(string title, Exception exception)
|
public void ShowExceptionDialog(string title, Exception exception)
|
||||||
{
|
{
|
||||||
if (_exceptionDialogOpen)
|
if (_exceptionDialogOpen)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
try
|
_exceptionDialogOpen = true;
|
||||||
|
// Fire and forget the dialog
|
||||||
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
_exceptionDialogOpen = true;
|
try
|
||||||
ShowDialogAsync(new ExceptionDialogViewModel(title, exception)).GetAwaiter().GetResult();
|
{
|
||||||
}
|
await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _kernel.Get<INotificationService>()));
|
||||||
finally
|
}
|
||||||
{
|
finally
|
||||||
_exceptionDialogOpen = false;
|
{
|
||||||
}
|
_exceptionDialogOpen = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentDialogBuilder CreateContentDialog()
|
public ContentDialogBuilder CreateContentDialog()
|
||||||
|
|||||||
@ -13,9 +13,6 @@
|
|||||||
<Setter Property="Opacity" Value="0" />
|
<Setter Property="Opacity" Value="0" />
|
||||||
<Setter Property="MaxWidth" Value="600" />
|
<Setter Property="MaxWidth" Value="600" />
|
||||||
<Setter Property="Margin" Value="15"/>
|
<Setter Property="Margin" Value="15"/>
|
||||||
<Setter Property="Background" Value="#25a3a3a3"/>
|
|
||||||
<Setter Property="VerticalAlignment" Value="Bottom" />
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
|
||||||
<Setter Property="Transitions">
|
<Setter Property="Transitions">
|
||||||
<Transitions>
|
<Transitions>
|
||||||
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
|
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
|
||||||
@ -25,4 +22,7 @@
|
|||||||
<Style Selector="controls|InfoBar.notification-info-bar[IsOpen=True]">
|
<Style Selector="controls|InfoBar.notification-info-bar[IsOpen=True]">
|
||||||
<Setter Property="Opacity" Value="1" />
|
<Setter Property="Opacity" Value="1" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="controls|InfoBar.notification-info-bar:informational /template/ Border#ContentRoot">
|
||||||
|
<Setter Property="Background" Value="#ff3c3c3c" />
|
||||||
|
</Style>
|
||||||
</Styles>
|
</Styles>
|
||||||
@ -1,19 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive;
|
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
|
using Artemis.UI.Avalonia.Shared.Events;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Shared
|
namespace Artemis.UI.Avalonia.Shared
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the base class for Artemis view models
|
/// Represents the base class for Artemis view models
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ViewModelBase : ReactiveObject
|
public abstract class ViewModelBase : ReactiveObject
|
||||||
{
|
{
|
||||||
private string? _displayName;
|
private string? _displayName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the display name of the view model
|
/// Gets or sets the display name of the view model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? DisplayName
|
public string? DisplayName
|
||||||
{
|
{
|
||||||
@ -23,7 +23,7 @@ namespace Artemis.UI.Avalonia.Shared
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the base class for Artemis view models that are interested in the activated event
|
/// Represents the base class for Artemis view models that are interested in the activated event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable
|
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable
|
||||||
{
|
{
|
||||||
@ -34,11 +34,11 @@ namespace Artemis.UI.Avalonia.Shared
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="disposing">
|
/// <param name="disposing">
|
||||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||||
/// <see langword="false" /> to release only unmanaged resources.
|
/// <see langword="false" /> to release only unmanaged resources.
|
||||||
/// </param>
|
/// </param>
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
@ -56,25 +56,28 @@ namespace Artemis.UI.Avalonia.Shared
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the base class for Artemis view models used to drive dialogs
|
/// Represents the base class for Artemis view models used to drive dialogs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DialogViewModelBase<TResult> : ActivatableViewModelBase
|
public abstract class DialogViewModelBase<TResult> : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
protected DialogViewModelBase()
|
/// Closes the dialog with the given <paramref name="result" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The result of the dialog</param>
|
||||||
|
public void Close(TResult result)
|
||||||
{
|
{
|
||||||
Close = ReactiveCommand.Create<TResult, TResult>(t => t);
|
CloseRequested?.Invoke(this, new DialogClosedEventArgs<TResult>(result));
|
||||||
Cancel = ReactiveCommand.Create(() => { });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Closes the dialog with a given result
|
/// Closes the dialog without a result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveCommand<TResult, TResult> Close { get; }
|
public void Cancel()
|
||||||
|
{
|
||||||
|
CancelRequested?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
internal event EventHandler<DialogClosedEventArgs<TResult>>? CloseRequested;
|
||||||
/// Closes the dialog without a result
|
internal event EventHandler? CancelRequested;
|
||||||
/// </summary>
|
|
||||||
public ReactiveCommand<Unit, Unit> Cancel { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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/=events/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@ -118,7 +118,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
|
|
||||||
public void Accept()
|
public void Accept()
|
||||||
{
|
{
|
||||||
Close.Execute(true);
|
Close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<bool> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects)
|
public static async Task<bool> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects)
|
||||||
|
|||||||
@ -105,7 +105,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
|
|
||||||
// This shouldn't be happening and the experience isn't very nice for the user (too lazy to make a nice UI for such an edge case)
|
// This shouldn't be happening and the experience isn't very nice for the user (too lazy to make a nice UI for such an edge case)
|
||||||
// but at least give some feedback
|
// but at least give some feedback
|
||||||
Close.Execute(false);
|
Close(false);
|
||||||
await _windowService.CreateContentDialog()
|
await _windowService.CreateContentDialog()
|
||||||
.WithTitle("Plugin prerequisites")
|
.WithTitle("Plugin prerequisites")
|
||||||
.WithContent("The plugin was not able to fully remove all prerequisites. \r\nPlease try again or contact the plugin creator.")
|
.WithContent("The plugin was not able to fully remove all prerequisites. \r\nPlease try again or contact the plugin creator.")
|
||||||
@ -126,7 +126,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
|
|
||||||
public void Accept()
|
public void Accept()
|
||||||
{
|
{
|
||||||
Close.Execute(true);
|
Close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<object> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects, string cancelLabel = "Cancel")
|
public static async Task<object> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects, string cancelLabel = "Cancel")
|
||||||
|
|||||||
@ -12,6 +12,7 @@ using Artemis.UI.Avalonia.Exceptions;
|
|||||||
using Artemis.UI.Avalonia.Ninject.Factories;
|
using Artemis.UI.Avalonia.Ninject.Factories;
|
||||||
using Artemis.UI.Avalonia.Shared;
|
using Artemis.UI.Avalonia.Shared;
|
||||||
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
||||||
|
using Avalonia.Threading;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
@ -53,9 +54,13 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
||||||
|
|
||||||
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(x => x.IsEnabled).Select(isEnabled => isEnabled && Plugin.ConfigurationDialog != null));
|
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(x => x.IsEnabled).Select(isEnabled => isEnabled && Plugin.ConfigurationDialog != null));
|
||||||
|
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites, this.WhenAnyValue(x => x.CanInstallPrerequisites));
|
||||||
|
RemovePrerequisites = ReactiveCommand.CreateFromTask<bool>(ExecuteRemovePrerequisites, this.WhenAnyValue(x => x.CanRemovePrerequisites));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
|
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> InstallPrerequisites { get; }
|
||||||
|
public ReactiveCommand<bool, Unit> RemovePrerequisites { get; }
|
||||||
|
|
||||||
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
|
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
|
||||||
|
|
||||||
@ -72,7 +77,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
||||||
|
|
||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get => Plugin.IsEnabled;
|
get => Plugin.IsEnabled;
|
||||||
@ -137,7 +142,8 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
{
|
{
|
||||||
bool wasEnabled = IsEnabled;
|
bool wasEnabled = IsEnabled;
|
||||||
|
|
||||||
_pluginManagementService.UnloadPlugin(Plugin);
|
await Task.Run(() => _pluginManagementService.UnloadPlugin(Plugin));
|
||||||
|
|
||||||
PluginFeatures.Clear();
|
PluginFeatures.Clear();
|
||||||
|
|
||||||
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
|
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
|
||||||
@ -150,7 +156,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
_notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show();
|
_notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallPrerequisites()
|
public async Task ExecuteInstallPrerequisites()
|
||||||
{
|
{
|
||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
|
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
|
||||||
@ -159,7 +165,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
|
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemovePrerequisites(bool forPluginRemoval = false)
|
public async Task ExecuteRemovePrerequisites(bool forPluginRemoval = false)
|
||||||
{
|
{
|
||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
|
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
|
||||||
@ -200,7 +206,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||||
subjects.AddRange(Plugin.Features);
|
subjects.AddRange(Plugin.Features);
|
||||||
if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any())))
|
if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any())))
|
||||||
await RemovePrerequisites(true);
|
await ExecuteRemovePrerequisites(true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -284,7 +290,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Run(() =>
|
await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -292,10 +298,10 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_notificationService.CreateNotification()
|
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
|
||||||
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
|
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
|
||||||
.HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder))
|
.HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder))
|
||||||
.Show();
|
.Show());
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@ -27,12 +27,10 @@
|
|||||||
Icon="{Binding FeatureInfo.ResolvedIcon}"
|
Icon="{Binding FeatureInfo.ResolvedIcon}"
|
||||||
Width="20"
|
Width="20"
|
||||||
Height="20"
|
Height="20"
|
||||||
|
|
||||||
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
|
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
|
||||||
|
|
||||||
<Button Grid.Column="0"
|
<Button Grid.Column="0"
|
||||||
Margin="-8"
|
Classes="AppBarButton icon-button"
|
||||||
|
|
||||||
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
|
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
Foreground="#E74C4C"
|
Foreground="#E74C4C"
|
||||||
ToolTip.Tip="An exception occurred while enabling this feature, click to view"
|
ToolTip.Tip="An exception occurred while enabling this feature, click to view"
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using Artemis.UI.Avalonia.Screens.Plugins.ViewModels;
|
using Artemis.UI.Avalonia.Screens.Plugins.ViewModels;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
@ -19,22 +20,16 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.Views
|
|||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
ViewModel!.ConfigurationViewModel.CloseRequested += ConfigurationViewModelOnCloseRequested;
|
Observable.FromEventPattern(
|
||||||
Disposable.Create(HandleDeactivation).DisposeWith(disposables);
|
x => ViewModel!.ConfigurationViewModel.CloseRequested += x,
|
||||||
|
x => ViewModel!.ConfigurationViewModel.CloseRequested -= x
|
||||||
|
)
|
||||||
|
.Subscribe(_ => Close())
|
||||||
|
.DisposeWith(disposables);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleDeactivation()
|
|
||||||
{
|
|
||||||
ViewModel!.ConfigurationViewModel.CloseRequested -= ConfigurationViewModelOnCloseRequested;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfigurationViewModelOnCloseRequested(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user