1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 17:53:32 +00:00

UI - Refactorings, project builds and runs again :P

This commit is contained in:
Robert 2021-11-08 18:18:23 +01:00
parent c178fc6cf8
commit 41171a5ade
48 changed files with 895 additions and 205 deletions

View File

@ -1,9 +1,15 @@
namespace Artemis.Core using System;
namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents a configuration dialog for a <see cref="Plugin" /> /// Represents a configuration dialog for a <see cref="Plugin" />
/// </summary> /// </summary>
public interface IPluginConfigurationDialog public interface IPluginConfigurationDialog
{ {
/// <summary>
/// The type of view model the tab contains
/// </summary>
Type Type { get; }
} }
} }

View File

@ -89,6 +89,28 @@ namespace Artemis.Core
} }
} }
/// <summary>
/// Occurs when the core has requested an application shutdown
/// </summary>
public static event EventHandler? ShutdownRequested;
/// <summary>
/// Occurs when the core has requested an application restart
/// </summary>
public static event EventHandler<RestartEventArgs>? RestartRequested;
/// <summary>
/// Opens the provided folder in the user's file explorer
/// </summary>
/// <param name="path">The full path of the folder to open</param>
public static void OpenFolder(string path)
{
if (OperatingSystem.IsWindows())
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", path);
else
throw new PlatformNotSupportedException("Can't open folders yet on non-Windows systems Q.Q");
}
/// <summary> /// <summary>
/// Gets the current application location /// Gets the current application location
/// </summary> /// </summary>
@ -103,6 +125,11 @@ namespace Artemis.Core
RestartRequested?.Invoke(null, e); RestartRequested?.Invoke(null, e);
} }
private static void OnShutdownRequested()
{
ShutdownRequested?.Invoke(null, EventArgs.Empty);
}
#region Scaling #region Scaling
internal static int RenderScaleMultiplier { get; set; } = 2; internal static int RenderScaleMultiplier { get; set; } = 2;
@ -118,32 +145,13 @@ namespace Artemis.Core
return SKRectI.Create(roundX, roundY, roundWidth, roundHeight); return SKRectI.Create(roundX, roundY, roundWidth, roundHeight);
return SKRectI.Create( return SKRectI.Create(
roundX - (roundX % RenderScaleMultiplier), roundX - roundX % RenderScaleMultiplier,
roundY - (roundY % RenderScaleMultiplier), roundY - roundY % RenderScaleMultiplier,
roundWidth - (roundWidth % RenderScaleMultiplier), roundWidth - roundWidth % RenderScaleMultiplier,
roundHeight - (roundHeight % RenderScaleMultiplier) roundHeight - roundHeight % RenderScaleMultiplier
); );
} }
#endregion #endregion
#region Events
/// <summary>
/// Occurs when the core has requested an application shutdown
/// </summary>
public static event EventHandler? ShutdownRequested;
/// <summary>
/// Occurs when the core has requested an application restart
/// </summary>
public static event EventHandler<RestartEventArgs>? RestartRequested;
private static void OnShutdownRequested()
{
ShutdownRequested?.Invoke(null, EventArgs.Empty);
}
#endregion
} }
} }

View File

@ -1,2 +1,3 @@
<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"> <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/=plugins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindowservice/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindowservice/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,491 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Artemis.UI.Avalonia.Shared</name>
</assembly>
<members>
<member name="T:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer">
<summary>
Visualizes an <see cref="T:Artemis.Core.ArtemisDevice" /> with optional per-LED colors
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.#ctor">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.Render(Avalonia.Media.DrawingContext)">
<inheritdoc />
</member>
<member name="E:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.LedClicked">
<summary>
Occurs when a LED of the device has been clicked
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.OnLedClicked(Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs)">
<summary>
Invokes the <see cref="E:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.LedClicked" /> event
</summary>
<param name="e"></param>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.DeviceProperty">
<summary>
Gets or sets the <see cref="T:Artemis.Core.ArtemisDevice" /> to display
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.Device">
<summary>
Gets or sets the <see cref="T:Artemis.Core.ArtemisDevice" /> to display
</summary>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.ShowColorsProperty">
<summary>
Gets or sets boolean indicating whether or not to show per-LED colors
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.ShowColors">
<summary>
Gets or sets a boolean indicating whether or not to show per-LED colors
</summary>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.HighlightedLedsProperty">
<summary>
Gets or sets a list of LEDs to highlight
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.HighlightedLeds">
<summary>
Gets or sets a list of LEDs to highlight
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.OnAttachedToLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.OnDetachedFromLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.DeviceVisualizer.MeasureOverride(Avalonia.Size)">
<inheritdoc />
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.ValueProperty">
<summary>
Gets or sets the currently selected value
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.Value">
<summary>
Gets or sets the currently selected value
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.OnAttachedToLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.EnumComboBox.OnDetachedFromLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs)">
<inheritdoc />
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.ProfileConfigurationIcon.ConfigurationIconProperty">
<summary>
Gets or sets the <see cref="T:Artemis.Core.ProfileConfigurationIcon" /> to display
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.ProfileConfigurationIcon.ConfigurationIcon">
<summary>
Gets or sets the <see cref="T:Artemis.Core.ProfileConfigurationIcon" /> to display
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle">
<summary>
Visualizes an <see cref="T:Artemis.Core.ArtemisDevice" /> with optional per-LED colors
</summary>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BackgroundProperty">
<summary>
Defines the <see cref="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.Background" /> property.
</summary>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrushProperty">
<summary>
Defines the <see cref="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrush" /> property.
</summary>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderThicknessProperty">
<summary>
Defines the <see cref="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrush" /> property.
</summary>
</member>
<member name="F:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.InputElementProperty">
<summary>
Defines the <see cref="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.get_InputElement" /> property.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.#ctor">
<inheritdoc />
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.Background">
<summary>
Gets or sets a brush used to paint the control's background.
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderBrush">
<summary>
Gets or sets a brush used to paint the control's border
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.BorderThickness">
<summary>
Gets or sets the width of the control's border
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.OnAttachedToVisualTree(Avalonia.VisualTreeAttachmentEventArgs)">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Controls.SelectionRectangle.OnDetachedFromVisualTree(Avalonia.VisualTreeAttachmentEventArgs)">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Converters.ColorToSKColorConverter">
<summary>
Converts <see cref="T:Avalonia.Media.Color" /> into <see cref="T:SkiaSharp.SKColor" />.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Converters.ColorToSKColorConverter.Convert(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Converters.ColorToSKColorConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Converters.SKColorToColorConverter">
<summary>
Converts <see cref="T:SkiaSharp.SKColor" /> into <see cref="T:Avalonia.Media.Color" />.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Converters.SKColorToColorConverter.Convert(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Converters.SKColorToColorConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Events.DataModelInputDynamicEventArgs">
<summary>
Provides data about selection events raised by <see cref="!:DataModelDynamicViewModel" />
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.DataModelInputDynamicEventArgs.DataModelPath">
<summary>
Gets the data model path that was selected
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Events.DataModelInputStaticEventArgs">
<summary>
Provides data about submit events raised by <see cref="!:DataModelStaticViewModel" />
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.DataModelInputStaticEventArgs.Value">
<summary>
The value that was submitted
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs">
<summary>
Provides data on LED click events raised by the device visualizer
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs.Device">
<summary>
The device that was clicked
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.LedClickedEventArgs.Led">
<summary>
The LED that was clicked
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Events.ProfileConfigurationEventArgs">
<summary>
Provides data on profile related events raised by the profile editor
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.ProfileConfigurationEventArgs.ProfileConfiguration">
<summary>
Gets the profile the event was raised for
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.ProfileConfigurationEventArgs.PreviousProfileConfiguration">
<summary>
If applicable, the previous active profile before the event was raised
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Events.RenderProfileElementEventArgs">
<summary>
Provides data on profile element related events raised by the profile editor
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.RenderProfileElementEventArgs.RenderProfileElement">
<summary>
Gets the profile element the event was raised for
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.Events.RenderProfileElementEventArgs.PreviousRenderProfileElement">
<summary>
If applicable, the previous active profile element before the event was raised
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Exceptions.ArtemisSharedUIException">
<summary>
Represents errors that occur within the Artemis Shared UI library
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Ninject.SharedUIModule">
<summary>
The main <see cref="T:Ninject.Modules.NinjectModule" /> of the Artemis Shared UI toolkit that binds all services
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Ninject.SharedUIModule.Load">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog`1">
<inheritdoc />
</member>
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog`1.Type">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog">
<summary>
Describes a configuration dialog for a specific plugin
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationDialog.Type">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel">
<summary>
Represents a view model for a plugin configuration window
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.#ctor(Artemis.Core.Plugin)">
<summary>
Creates a new instance of the <see cref="T:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel" /> class
</summary>
<param name="plugin"></param>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.Plugin">
<summary>
Gets the plugin this configuration view model is associated with
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.PluginConfigurationViewModel.Close">
<summary>
A command that closes the window
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder">
<summary>
Represents a builder that can create a <see cref="T:Avalonia.Controls.FileDialogFilter" />.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder.WithName(System.String)">
<summary>
Sets the name of the filter
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder.WithExtension(System.String)">
<summary>
Adds the provided extension to the filter
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.NotificationBuilder.HavingButton(System.Action{Artemis.UI.Avalonia.Shared.Services.Builders.NotificationButtonBuilder})">
<summary>
Add a filter to the dialog
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder">
<summary>
Represents a builder that can create a <see cref="T:Avalonia.Controls.OpenFileDialog" />.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithAllowMultiple">
<summary>
Indicate that the user can select multiple files.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithTitle(System.String)">
<summary>
Set the title of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithDirectory(System.String)">
<summary>
Set the initial directory of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.WithInitialFileName(System.String)">
<summary>
Set the initial file name of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.HavingFilter(System.Action{Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder})">
<summary>
Add a filter to the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.OpenFileDialogBuilder.ShowAsync">
<summary>
Shows the file dialog
</summary>
<returns>
A task that on completion returns an array containing the full path to the selected
files, or null if the dialog was canceled.
</returns>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder">
<summary>
Represents a builder that can create a <see cref="T:Avalonia.Controls.SaveFileDialog" />.
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithTitle(System.String)">
<summary>
Set the title of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithDirectory(System.String)">
<summary>
Set the initial directory of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithInitialFileName(System.String)">
<summary>
Set the initial file name of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.WithDefaultExtension(System.String)">
<summary>
Set the default extension of the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.HavingFilter(System.Action{Artemis.UI.Avalonia.Shared.Services.Builders.FileDialogFilterBuilder})">
<summary>
Add a filter to the dialog
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Builders.SaveFileDialogBuilder.ShowAsync">
<summary>
Shows the save file dialog.
</summary>
<returns>
A task that on completion contains the full path of the save location, or null if the
dialog was canceled.
</returns>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.Services.Interfaces.IArtemisSharedUIService">
<summary>
Represents a service provided by the Artemis Shared UI library
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowWindow``1(System.ValueTuple{System.String,System.Object}[])">
<summary>
Creates a view model instance of type <typeparamref name="TViewModel" /> and shows its corresponding View as a window
</summary>
<typeparam name="TViewModel">The type of view model to create</typeparam>
<returns>The created view model</returns>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowWindow(System.Object)">
<summary>
Given a ViewModel, show its corresponding View as a window
</summary>
<param name="viewModel">ViewModel to show the View for</param>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowExceptionDialog(System.String,System.Exception)">
<summary>
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>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowDialogAsync``1(Artemis.UI.Avalonia.Shared.DialogViewModelBase{``0})">
<summary>
Given an existing ViewModel, show its corresponding View as a Dialog
</summary>
<typeparam name="TResult">The return type</typeparam>
<param name="viewModel">ViewModel to show the View for</param>
<returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowDialogAsync``2(System.ValueTuple{System.String,System.Object}[])">
<summary>
Creates a view model instance of type <typeparamref name="TViewModel"/> and shows its corresponding View as a Dialog
</summary>
<typeparam name="TViewModel">The view model type</typeparam>
<typeparam name="TResult">The return type</typeparam>
<returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.ShowConfirmContentDialog(System.String,System.String,System.String,System.String)">
<summary>
Shows a content dialog asking the user to confirm an action
</summary>
<param name="title">The title of the dialog</param>
<param name="message">The message of the dialog</param>
<param name="confirm">The text of the confirm button</param>
<param name="cancel">The text of the cancel button, if <see langword="null"/> the cancel button will not be shown</param>
<returns>A task containing the result of the dialog, <see langword="true"/> if confirmed; otherwise <see langword="false"/></returns>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.CreateOpenFileDialog">
<summary>
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>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.Services.Interfaces.IWindowService.CreateSaveFileDialog">
<summary>
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>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.ViewModelBase">
<summary>
Represents the base class for Artemis view models
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.ViewModelBase.DisplayName">
<summary>
Gets or sets the display name of the view model
</summary>
</member>
<member name="T:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase">
<summary>
Represents the base class for Artemis view models that are interested in the activated event
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.#ctor">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose(System.Boolean)">
<summary>
Releases the unmanaged resources used by the object and optionally releases the managed resources.
</summary>
<param name="disposing">
<see langword="true" /> to release both managed and unmanaged resources;
<see langword="false" /> to release only unmanaged resources.
</param>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Activator">
<inheritdoc />
</member>
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose">
<inheritdoc />
</member>
<member name="T:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1">
<summary>
Represents the base class for Artemis view models used to drive dialogs
</summary>
</member>
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.#ctor">
<inheritdoc />
</member>
<member name="P:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Close">
<summary>
Closes the dialog with a given result
</summary>
</member>
<member name="P:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Cancel">
<summary>
Closes the dialog without a result
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,21 @@
using System;
using Artemis.Core;
namespace Artemis.UI.Avalonia.Shared
{
/// <inheritdoc />
public class PluginConfigurationDialog<T> : PluginConfigurationDialog where T : PluginConfigurationViewModel
{
/// <inheritdoc />
public override Type Type => typeof(T);
}
/// <summary>
/// Describes a configuration dialog for a specific plugin
/// </summary>
public abstract class PluginConfigurationDialog : IPluginConfigurationDialog
{
/// <inheritdoc />
public abstract Type Type { get; }
}
}

View File

@ -0,0 +1,32 @@
using System.Reactive;
using Artemis.Core;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Shared
{
/// <summary>
/// Represents a view model for a plugin configuration window
/// </summary>
public abstract class PluginConfigurationViewModel : ViewModelBase, IPluginConfigurationViewModel
{
/// <summary>
/// Creates a new instance of the <see cref="PluginConfigurationViewModel" /> class
/// </summary>
/// <param name="plugin"></param>
protected PluginConfigurationViewModel(Plugin plugin)
{
Plugin = plugin;
Close = ReactiveCommand.Create(() => { });
}
/// <summary>
/// Gets the plugin this configuration view model is associated with
/// </summary>
public Plugin Plugin { get; }
/// <summary>
/// A command that closes the window
/// </summary>
public ReactiveCommand<Unit, Unit> Close { get; }
}
}

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.UI.Avalonia.Shared.Utilities;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;

View File

@ -43,6 +43,16 @@ namespace Artemis.UI.Avalonia.Shared.Services.Interfaces
/// <returns>A task containing the return value of type <typeparamref name="TResult" /></returns> /// <returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object value)[] parameters) where TViewModel : DialogViewModelBase<TResult>; Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object value)[] parameters) where TViewModel : DialogViewModelBase<TResult>;
/// <summary>
/// Shows a content dialog asking the user to confirm an action
/// </summary>
/// <param name="title">The title of the dialog</param>
/// <param name="message">The message of the dialog</param>
/// <param name="confirm">The text of the confirm button</param>
/// <param name="cancel">The text of the cancel button, if <see langword="null"/> the cancel button will not be shown</param>
/// <returns>A task containing the result of the dialog, <see langword="true"/> if confirmed; otherwise <see langword="false"/></returns>
Task<bool> ShowConfirmContentDialog(string title, string message, string confirm = "Confirm", string? cancel = "Cancel");
/// <summary> /// <summary>
/// Creates an open file dialog, use the fluent API to configure it /// Creates an open file dialog, use the fluent API to configure it
/// </summary> /// </summary>
@ -57,8 +67,6 @@ namespace Artemis.UI.Avalonia.Shared.Services.Interfaces
ContentDialogBuilder CreateContentDialog(); ContentDialogBuilder CreateContentDialog();
ConfirmDialogBuilder CreateConfirmDialog();
Window GetCurrentWindow(); Window GetCurrentWindow();
} }
} }

View File

@ -1,4 +1,6 @@
using Avalonia;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Avalonia.Shared.Services namespace Artemis.UI.Avalonia.Shared.Services
{ {

View File

@ -7,6 +7,7 @@ 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 FluentAvalonia.UI.Controls;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
@ -57,6 +58,18 @@ namespace Artemis.UI.Avalonia.Shared.Services
return await ShowDialogAsync(viewModel); return await ShowDialogAsync(viewModel);
} }
public async Task<bool> ShowConfirmContentDialog(string title, string message, string confirm = "Confirm", string? cancel = "Cancel")
{
ContentDialogResult contentDialogResult = await CreateContentDialog()
.WithTitle(title)
.WithContent(message)
.HavingPrimaryButton(b => b.WithText(confirm))
.WithCloseButtonText(cancel)
.ShowAsync();
return contentDialogResult == ContentDialogResult.Primary;
}
public async Task<TResult> ShowDialogAsync<TResult>(DialogViewModelBase<TResult> viewModel) public async Task<TResult> ShowDialogAsync<TResult>(DialogViewModelBase<TResult> viewModel)
{ {
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic) if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic)

View File

@ -22,6 +22,13 @@ namespace Artemis.UI.Avalonia.Ninject.Factories
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds); InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
} }
public interface ISettingsVmFactory : IVmFactory
{
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
// DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
}
public interface ISidebarVmFactory : IVmFactory public interface ISidebarVmFactory : IVmFactory
{ {
SidebarViewModel SidebarViewModel(IScreen hostScreen); SidebarViewModel SidebarViewModel(IScreen hostScreen);

View File

@ -2,6 +2,7 @@
using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Screens; using Artemis.UI.Avalonia.Screens;
using Artemis.UI.Avalonia.Services.Interfaces; using Artemis.UI.Avalonia.Services.Interfaces;
using Artemis.UI.Avalonia.Shared;
using Ninject.Extensions.Conventions; using Ninject.Extensions.Conventions;
using Ninject.Modules; using Ninject.Modules;
using Ninject.Planning.Bindings.Resolvers; using Ninject.Planning.Bindings.Resolvers;

View File

@ -5,6 +5,7 @@ using Artemis.UI.Avalonia.Screens.Debugger.Tabs.Logs;
using Artemis.UI.Avalonia.Screens.Debugger.Tabs.Performance; using Artemis.UI.Avalonia.Screens.Debugger.Tabs.Performance;
using Artemis.UI.Avalonia.Screens.Debugger.Tabs.Render; using Artemis.UI.Avalonia.Screens.Debugger.Tabs.Render;
using Artemis.UI.Avalonia.Services.Interfaces; using Artemis.UI.Avalonia.Services.Interfaces;
using Artemis.UI.Avalonia.Shared;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;

View File

@ -1,4 +1,5 @@
using ReactiveUI; using Artemis.UI.Avalonia.Shared;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Debugger.Tabs.DataModel namespace Artemis.UI.Avalonia.Screens.Debugger.Tabs.DataModel
{ {

View File

@ -1,4 +1,5 @@
using ReactiveUI; using Artemis.UI.Avalonia.Shared;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Debugger.Tabs.Logs namespace Artemis.UI.Avalonia.Screens.Debugger.Tabs.Logs
{ {

View File

@ -1,4 +1,5 @@
using ReactiveUI; using Artemis.UI.Avalonia.Shared;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Debugger.Tabs.Performance namespace Artemis.UI.Avalonia.Screens.Debugger.Tabs.Performance
{ {

View File

@ -3,6 +3,7 @@ using System.Reactive.Disposables;
using System.Timers; using System.Timers;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
using SkiaSharp; using SkiaSharp;

View File

@ -1,12 +1,13 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Shared;
using RGB.NET.Core; using RGB.NET.Core;
using ArtemisLed = Artemis.Core.ArtemisLed; using ArtemisLed = Artemis.Core.ArtemisLed;
namespace Artemis.UI.Avalonia.Screens.Device namespace Artemis.UI.Avalonia.Screens.Device
{ {
public class DevicePropertiesViewModel : ActivatableViewModelBase public class DevicePropertiesViewModel : DialogViewModelBase<object>
{ {
public DevicePropertiesViewModel(ArtemisDevice device, IDeviceVmFactory deviceVmFactory) public DevicePropertiesViewModel(ArtemisDevice device, IDeviceVmFactory deviceVmFactory)
{ {

View File

@ -1,5 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Shared;
using Avalonia; using Avalonia;
using RGB.NET.Core; using RGB.NET.Core;

View File

@ -4,6 +4,7 @@ using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Shared;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;

View File

@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Shared;
using Artemis.UI.Avalonia.Shared.Services.Builders; using Artemis.UI.Avalonia.Shared.Services.Builders;
using Artemis.UI.Avalonia.Shared.Services.Interfaces; using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using ReactiveUI; using ReactiveUI;

View File

@ -5,6 +5,7 @@ using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Exceptions; using Artemis.UI.Avalonia.Exceptions;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
using RGB.NET.Core; using RGB.NET.Core;

View File

@ -1,4 +1,5 @@
using ReactiveUI; using Artemis.UI.Avalonia.Shared;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens namespace Artemis.UI.Avalonia.Screens
{ {

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -70,7 +69,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
try try
{ {
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs")); Utilities.OpenFolder(Path.Combine(Constants.DataFolder, "logs"));
} }
catch (Exception e) catch (Exception e)
{ {
@ -80,21 +79,21 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public void ViewLoadException() public void ViewLoadException()
{ {
if (LoadException != null) if (LoadException != null)
_windowService.ShowExceptionDialog("Feature failed to enable", LoadException); _windowService.ShowExceptionDialog("Feature failed to enable", LoadException);
} }
public async Task InstallPrerequisites() public async Task InstallPrerequisites()
{ {
if (FeatureInfo.Prerequisites.Any()) if (FeatureInfo.Prerequisites.Any())
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo}); await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, new List<IPrerequisitesSubject> {FeatureInfo});
} }
public async Task RemovePrerequisites() public async Task RemovePrerequisites()
{ {
if (FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any())) if (FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any()))
{ {
await PluginPrerequisitesUninstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo}); await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, new List<IPrerequisitesSubject> {FeatureInfo});
this.RaisePropertyChanged(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
} }
} }
@ -142,7 +141,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
if (FeatureInfo.Plugin.Info.RequiresAdmin && !_coreService.IsElevated) 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?"); bool confirmed = await _windowService.ShowConfirmContentDialog("Enable feature", "The plugin of this feature requires admin rights, are you sure you want to enable it?");
if (!confirmed) if (!confirmed)
{ {
this.RaisePropertyChanged(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
@ -153,7 +152,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
// Check if all prerequisites are met async // Check if all prerequisites are met async
if (!FeatureInfo.ArePrerequisitesMet()) if (!FeatureInfo.ArePrerequisitesMet())
{ {
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo}); await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, new List<IPrerequisitesSubject> {FeatureInfo});
if (!FeatureInfo.ArePrerequisitesMet()) if (!FeatureInfo.ArePrerequisitesMet())
{ {
this.RaisePropertyChanged(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
@ -185,20 +184,14 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
private void OnFeatureEnabling(object? sender, PluginFeatureEventArgs e) private void OnFeatureEnabling(object? sender, PluginFeatureEventArgs e)
{ {
if (e.PluginFeature != FeatureInfo.Instance) if (e.PluginFeature != FeatureInfo.Instance) return;
{
return;
}
Enabling = true; Enabling = true;
} }
private void OnFeatureEnableStopped(object? sender, PluginFeatureEventArgs e) private void OnFeatureEnableStopped(object? sender, PluginFeatureEventArgs e)
{ {
if (e.PluginFeature != FeatureInfo.Instance) if (e.PluginFeature != FeatureInfo.Instance) return;
{
return;
}
Enabling = false; Enabling = false;

View File

@ -5,8 +5,6 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
public class PluginPrerequisiteActionViewModel : ViewModelBase public class PluginPrerequisiteActionViewModel : ViewModelBase
{ {
public PluginPrerequisiteActionViewModel(PluginPrerequisiteAction action) public PluginPrerequisiteActionViewModel(PluginPrerequisiteAction action)
{ {
Action = action; Action = action;

View File

@ -5,23 +5,22 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Shared; using Artemis.UI.Avalonia.Shared;
using DynamicData;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
public class PluginPrerequisiteViewModel : ActivatableViewModelBase public class PluginPrerequisiteViewModel : ActivatableViewModelBase
{ {
private readonly bool _uninstall;
private readonly ObservableAsPropertyHelper<bool> _busy;
private readonly ObservableAsPropertyHelper<int> _activeStepNumber; private readonly ObservableAsPropertyHelper<int> _activeStepNumber;
private readonly ObservableAsPropertyHelper<bool> _busy;
private bool _installing; private readonly bool _uninstall;
private bool _uninstalling;
private bool _isMet;
private PluginPrerequisiteActionViewModel? _activeAction; private PluginPrerequisiteActionViewModel? _activeAction;
private bool _installing;
private bool _isMet;
private bool _uninstalling;
public PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall) public PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall)
{ {
_uninstall = uninstall; _uninstall = uninstall;
@ -45,7 +44,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public PluginPrerequisiteActionViewModel? ActiveAction public PluginPrerequisiteActionViewModel? ActiveAction
{ {
get => _activeAction; get => _activeAction;
set => this.RaiseAndSetIfChanged(ref _activeAction , value); set => this.RaiseAndSetIfChanged(ref _activeAction, value);
} }
public PluginPrerequisite PluginPrerequisite { get; } public PluginPrerequisite PluginPrerequisite { get; }
@ -106,6 +105,14 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
} }
} }
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
if (disposing) PluginPrerequisite.PropertyChanged -= PluginPrerequisiteOnPropertyChanged;
base.Dispose(disposing);
}
private void PluginPrerequisiteOnPropertyChanged(object? sender, PropertyChangedEventArgs e) private void PluginPrerequisiteOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(PluginPrerequisite.CurrentAction)) if (e.PropertyName == nameof(PluginPrerequisite.CurrentAction))
@ -120,21 +127,5 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
ActiveAction = activeAction; ActiveAction = activeAction;
} }
#region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
if (disposing)
{
PluginPrerequisite.PropertyChanged -= PluginPrerequisiteOnPropertyChanged;
}
base.Dispose(disposing);
}
#endregion
} }
} }

View File

@ -14,7 +14,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
public class PluginPrerequisitesInstallDialogViewModel : DialogViewModelBase<bool> public class PluginPrerequisitesInstallDialogViewModel : DialogViewModelBase<bool>
{ {
private PluginPrerequisiteViewModel _activePrerequisite; private PluginPrerequisiteViewModel? _activePrerequisite;
private bool _canInstall; private bool _canInstall;
private bool _showFailed; private bool _showFailed;
private bool _showInstall = true; private bool _showInstall = true;
@ -34,7 +34,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public ObservableCollection<PluginPrerequisiteViewModel> Prerequisites { get; } public ObservableCollection<PluginPrerequisiteViewModel> Prerequisites { get; }
public PluginPrerequisiteViewModel ActivePrerequisite public PluginPrerequisiteViewModel? ActivePrerequisite
{ {
get => _activePrerequisite; get => _activePrerequisite;
set => this.RaiseAndSetIfChanged(ref _activePrerequisite, value); set => this.RaiseAndSetIfChanged(ref _activePrerequisite, value);

View File

@ -9,7 +9,6 @@ using Artemis.Core.Services;
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 DynamicData;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
@ -19,9 +18,9 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly List<IPrerequisitesSubject> _subjects; private readonly List<IPrerequisitesSubject> _subjects;
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
private PluginPrerequisiteViewModel? _activePrerequisite;
private bool _canUninstall; private bool _canUninstall;
private bool _isFinished; private bool _isFinished;
private PluginPrerequisiteViewModel? _activePrerequisite;
private CancellationTokenSource? _tokenSource; private CancellationTokenSource? _tokenSource;
public PluginPrerequisitesUninstallDialogViewModel(List<IPrerequisitesSubject> subjects, string cancelLabel, IPrerequisitesVmFactory prerequisitesVmFactory, IWindowService windowService, public PluginPrerequisitesUninstallDialogViewModel(List<IPrerequisitesSubject> subjects, string cancelLabel, IPrerequisitesVmFactory prerequisitesVmFactory, IWindowService windowService,
@ -61,18 +60,6 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
set => this.RaiseAndSetIfChanged(ref _isFinished, value); set => this.RaiseAndSetIfChanged(ref _isFinished, value);
} }
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
if (disposing)
{
_tokenSource?.Cancel();
_tokenSource?.Dispose();
}
base.Dispose(disposing);
}
public async Task Uninstall() public async Task Uninstall()
{ {
CanUninstall = false; CanUninstall = false;
@ -80,29 +67,18 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
// Disable all subjects that are plugins, this will disable their features too // Disable all subjects that are plugins, this will disable their features too
foreach (IPrerequisitesSubject prerequisitesSubject in _subjects) foreach (IPrerequisitesSubject prerequisitesSubject in _subjects)
{ {
if (prerequisitesSubject is PluginInfo pluginInfo) if (prerequisitesSubject is PluginInfo pluginInfo) _pluginManagementService.DisablePlugin(pluginInfo.Plugin, true);
{
_pluginManagementService.DisablePlugin(pluginInfo.Plugin, true);
}
} }
// Disable all subjects that are features if still required // Disable all subjects that are features if still required
foreach (IPrerequisitesSubject prerequisitesSubject in _subjects) foreach (IPrerequisitesSubject prerequisitesSubject in _subjects)
{ {
if (prerequisitesSubject is not PluginFeatureInfo featureInfo) if (prerequisitesSubject is not PluginFeatureInfo featureInfo) continue;
{
continue;
}
// Disable the parent plugin if the feature is AlwaysEnabled // Disable the parent plugin if the feature is AlwaysEnabled
if (featureInfo.AlwaysEnabled) if (featureInfo.AlwaysEnabled)
{
_pluginManagementService.DisablePlugin(featureInfo.Plugin, true); _pluginManagementService.DisablePlugin(featureInfo.Plugin, true);
} else if (featureInfo.Instance != null) _pluginManagementService.DisablePluginFeature(featureInfo.Instance, true);
else if (featureInfo.Instance != null)
{
_pluginManagementService.DisablePluginFeature(featureInfo.Instance, true);
}
} }
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
@ -112,19 +88,13 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
foreach (PluginPrerequisiteViewModel pluginPrerequisiteViewModel in Prerequisites) foreach (PluginPrerequisiteViewModel pluginPrerequisiteViewModel in Prerequisites)
{ {
pluginPrerequisiteViewModel.IsMet = pluginPrerequisiteViewModel.PluginPrerequisite.IsMet(); pluginPrerequisiteViewModel.IsMet = pluginPrerequisiteViewModel.PluginPrerequisite.IsMet();
if (!pluginPrerequisiteViewModel.IsMet) if (!pluginPrerequisiteViewModel.IsMet) continue;
{
continue;
}
ActivePrerequisite = pluginPrerequisiteViewModel; ActivePrerequisite = pluginPrerequisiteViewModel;
await ActivePrerequisite.Uninstall(_tokenSource.Token); await ActivePrerequisite.Uninstall(_tokenSource.Token);
// Wait after the task finished for the user to process what happened // Wait after the task finished for the user to process what happened
if (pluginPrerequisiteViewModel != Prerequisites.Last()) if (pluginPrerequisiteViewModel != Prerequisites.Last()) await Task.Delay(1000);
{
await Task.Delay(1000);
}
} }
if (Prerequisites.All(p => !p.IsMet)) if (Prerequisites.All(p => !p.IsMet))
@ -163,5 +133,17 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
return await windowService.ShowDialogAsync<PluginPrerequisitesUninstallDialogViewModel, bool>(("subjects", subjects), ("cancelLabel", cancelLabel)); return await windowService.ShowDialogAsync<PluginPrerequisitesUninstallDialogViewModel, bool>(("subjects", subjects), ("cancelLabel", cancelLabel));
} }
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
if (disposing)
{
_tokenSource?.Cancel();
_tokenSource?.Dispose();
}
base.Dispose(disposing);
}
} }
} }

View File

@ -1,13 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Exceptions;
using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Shared; using Artemis.UI.Avalonia.Shared;
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using Ninject; using Ninject;
using ReactiveUI; using ReactiveUI;
@ -16,8 +18,10 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public class PluginSettingsViewModel : ActivatableViewModelBase public class PluginSettingsViewModel : ActivatableViewModelBase
{ {
private readonly ICoreService _coreService; private readonly ICoreService _coreService;
private readonly INotificationService _notificationService;
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly ISettingsVmFactory _settingsVmFactory; private readonly ISettingsVmFactory _settingsVmFactory;
private readonly IWindowService _windowService;
private bool _canInstallPrerequisites; private bool _canInstallPrerequisites;
private bool _canRemovePrerequisites; private bool _canRemovePrerequisites;
private bool _enabling; private bool _enabling;
@ -27,13 +31,24 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public PluginSettingsViewModel(Plugin plugin, public PluginSettingsViewModel(Plugin plugin,
ISettingsVmFactory settingsVmFactory, ISettingsVmFactory settingsVmFactory,
ICoreService coreService, ICoreService coreService,
IWindowService windowService,
INotificationService notificationService,
IPluginManagementService pluginManagementService) IPluginManagementService pluginManagementService)
{ {
Plugin = plugin; _plugin = plugin;
_settingsVmFactory = settingsVmFactory; _settingsVmFactory = settingsVmFactory;
_coreService = coreService; _coreService = coreService;
_windowService = windowService;
_notificationService = notificationService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
PluginFeatures = new ObservableCollection<PluginFeatureViewModel>();
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
} }
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; } public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
@ -49,7 +64,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
get => _enabling; get => _enabling;
set => this.RaiseAndSetIfChanged(ref _enabling, value); set => this.RaiseAndSetIfChanged(ref _enabling, value);
} }
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name; public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
public bool CanOpenSettings => IsEnabled && Plugin.ConfigurationDialog != null; public bool CanOpenSettings => IsEnabled && Plugin.ConfigurationDialog != null;
@ -83,18 +98,20 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public void OpenSettings() public void OpenSettings()
{ {
PluginConfigurationDialog configurationViewModel = (PluginConfigurationDialog) Plugin.ConfigurationDialog; if (Plugin.ConfigurationDialog == null)
if (configurationViewModel == null)
return; return;
try try
{ {
PluginConfigurationViewModel viewModel = (PluginConfigurationViewModel) Plugin.Kernel.Get(configurationViewModel.Type); PluginConfigurationViewModel? viewModel = Plugin.Kernel!.Get(Plugin.ConfigurationDialog.Type) as PluginConfigurationViewModel;
_windowManager.ShowWindow(new PluginSettingsWindowViewModel(viewModel)); if (viewModel == null)
throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}");
_windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
} }
catch (Exception e) catch (Exception e)
{ {
_dialogService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e); _windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
throw; throw;
} }
} }
@ -103,11 +120,11 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
try try
{ {
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Plugin.Directory.FullName); Utilities.OpenFolder(Plugin.Directory.FullName);
} }
catch (Exception e) catch (Exception e)
{ {
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e); _windowService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
} }
} }
@ -116,16 +133,16 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
bool wasEnabled = IsEnabled; bool wasEnabled = IsEnabled;
_pluginManagementService.UnloadPlugin(Plugin); _pluginManagementService.UnloadPlugin(Plugin);
Items.Clear(); PluginFeatures.Clear();
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory); Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features) foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false)); PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
if (wasEnabled) if (wasEnabled)
await UpdateEnabled(true); await UpdateEnabled(true);
_messageService.ShowMessage("Reloaded plugin."); _notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show();
} }
public async Task InstallPrerequisites() public async Task InstallPrerequisites()
@ -134,7 +151,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled)); subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
if (subjects.Any(s => s.Prerequisites.Any())) if (subjects.Any(s => s.Prerequisites.Any()))
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, subjects); await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
} }
public async Task RemovePrerequisites(bool forPluginRemoval = false) public async Task RemovePrerequisites(bool forPluginRemoval = false)
@ -144,15 +161,15 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any()))) if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any())))
{ {
await PluginPrerequisitesUninstallDialogViewModel.Show(_dialogService, subjects, forPluginRemoval ? "SKIP, REMOVE PLUGIN" : "CANCEL"); await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, subjects, forPluginRemoval ? "Skip, remove plugin" : "Cancel");
NotifyOfPropertyChange(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
NotifyOfPropertyChange(nameof(CanOpenSettings)); this.RaisePropertyChanged(nameof(CanOpenSettings));
} }
} }
public async Task RemoveSettings() public async Task RemoveSettings()
{ {
bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?"); bool confirmed = await _windowService.ShowConfirmContentDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
if (!confirmed) if (!confirmed)
return; return;
@ -166,12 +183,12 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
if (wasEnabled) if (wasEnabled)
await UpdateEnabled(true); await UpdateEnabled(true);
_messageService.ShowMessage("Cleared plugin settings."); _notificationService.CreateNotification().WithTitle("Cleared plugin settings.").Show();
} }
public async Task Remove() public async Task Remove()
{ {
bool confirmed = await _dialogService.ShowConfirmDialog("Remove plugin", "Are you sure you want to remove this plugin?"); bool confirmed = await _windowService.ShowConfirmContentDialog("Remove plugin", "Are you sure you want to remove this plugin?");
if (!confirmed) if (!confirmed)
return; return;
@ -184,45 +201,55 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
try try
{ {
_pluginManagementService.RemovePlugin(Plugin, false); _pluginManagementService.RemovePlugin(Plugin, false);
((PluginSettingsTabViewModel) Parent).GetPluginInstances();
} }
catch (Exception e) catch (Exception e)
{ {
_dialogService.ShowExceptionDialog("Failed to remove plugin", e); _windowService.ShowExceptionDialog("Failed to remove plugin", e);
throw; throw;
} }
_messageService.ShowMessage("Removed plugin."); _notificationService.CreateNotification().WithTitle("Removed plugin.").Show();
} }
public void ShowLogsFolder() public void ShowLogsFolder()
{ {
try try
{ {
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs")); Utilities.OpenFolder(Path.Combine(Constants.DataFolder, "logs"));
} }
catch (Exception e) catch (Exception e)
{ {
_dialogService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e); _windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
} }
} }
public void OpenUri(Uri uri) public void OpenUri(Uri uri)
{ {
Core.Utilities.OpenUrl(uri.ToString()); Utilities.OpenUrl(uri.ToString());
} }
private void PluginManagementServiceOnPluginToggled(object sender, PluginEventArgs e) protected override void Dispose(bool disposing)
{ {
NotifyOfPropertyChange(nameof(IsEnabled)); if (disposing)
NotifyOfPropertyChange(nameof(CanOpenSettings)); {
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
}
base.Dispose(disposing);
}
private void PluginManagementServiceOnPluginToggled(object? sender, PluginEventArgs e)
{
this.RaisePropertyChanged(nameof(IsEnabled));
this.RaisePropertyChanged(nameof(CanOpenSettings));
} }
private async Task UpdateEnabled(bool enable) private async Task UpdateEnabled(bool enable)
{ {
if (IsEnabled == enable) if (IsEnabled == enable)
{ {
NotifyOfPropertyChange(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
return; return;
} }
@ -232,7 +259,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
if (Plugin.Info.RequiresAdmin && !_coreService.IsElevated) 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?"); bool confirmed = await _windowService.ShowConfirmContentDialog("Enable plugin", "This plugin requires admin rights, are you sure you want to enable it?");
if (!confirmed) if (!confirmed)
{ {
CancelEnable(); CancelEnable();
@ -246,7 +273,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
if (subjects.Any(s => !s.ArePrerequisitesMet())) if (subjects.Any(s => !s.ArePrerequisitesMet()))
{ {
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, subjects); await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
if (!subjects.All(s => s.ArePrerequisitesMet())) if (!subjects.All(s => s.ArePrerequisitesMet()))
{ {
CancelEnable(); CancelEnable();
@ -262,7 +289,10 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
_messageService.ShowMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder); _notificationService.CreateNotification()
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
.HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder))
.Show();
} }
finally finally
{ {
@ -271,17 +301,19 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
}); });
} }
else else
{
_pluginManagementService.DisablePlugin(Plugin, true); _pluginManagementService.DisablePlugin(Plugin, true);
}
NotifyOfPropertyChange(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
NotifyOfPropertyChange(nameof(CanOpenSettings)); this.RaisePropertyChanged(nameof(CanOpenSettings));
} }
private void CancelEnable() private void CancelEnable()
{ {
Enabling = false; Enabling = false;
NotifyOfPropertyChange(nameof(IsEnabled)); this.RaisePropertyChanged(nameof(IsEnabled));
NotifyOfPropertyChange(nameof(CanOpenSettings)); this.RaisePropertyChanged(nameof(CanOpenSettings));
} }
private void CheckPrerequisites() private void CheckPrerequisites()
@ -291,26 +323,5 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
CanRemovePrerequisites = Plugin.Info.Prerequisites.Any(p => p.UninstallActions.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())); 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
} }
} }

View File

@ -1,32 +1,20 @@
using System; using System;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Shared;
namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
public class PluginSettingsWindowViewModel : Conductor<PluginConfigurationViewModel> public class PluginSettingsWindowViewModel : ActivatableViewModelBase
{ {
private readonly PluginConfigurationViewModel _configurationViewModel;
public PluginSettingsWindowViewModel(PluginConfigurationViewModel configurationViewModel) public PluginSettingsWindowViewModel(PluginConfigurationViewModel configurationViewModel)
{ {
_configurationViewModel = configurationViewModel ?? throw new ArgumentNullException(nameof(configurationViewModel)); ConfigurationViewModel = configurationViewModel ?? throw new ArgumentNullException(nameof(configurationViewModel));
Plugin = configurationViewModel.Plugin; Plugin = configurationViewModel.Plugin;
DisplayName = $"{Plugin.Info.Name} | Settings";
} }
public PluginConfigurationViewModel ConfigurationViewModel { get; }
public Plugin Plugin { get; } 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();
}
} }
} }

View File

@ -4,6 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Avalonia.Screens.Plugins.Views.PluginSettingsWindowView" x:Class="Artemis.UI.Avalonia.Screens.Plugins.Views.PluginSettingsWindowView"
Title="PluginSettingsWindowView"> Title="{Binding DisplayName}">
Welcome to Avalonia! <ContentControl Content="{Binding ConfigurationViewModel}"></ContentControl>
</Window> </Window>

View File

@ -1,10 +1,14 @@
using System;
using Artemis.UI.Avalonia.Screens.Plugins.ViewModels;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls.Mixins;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Plugins.Views namespace Artemis.UI.Avalonia.Screens.Plugins.Views
{ {
public partial class PluginSettingsWindowView : Window public class PluginSettingsWindowView : ReactiveWindow<PluginSettingsWindowViewModel>
{ {
public PluginSettingsWindowView() public PluginSettingsWindowView()
{ {
@ -12,6 +16,8 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.Views
#if DEBUG #if DEBUG
this.AttachDevTools(); this.AttachDevTools();
#endif #endif
this.WhenActivated(disposables => { ViewModel!.ConfigurationViewModel.Close.Subscribe(_ => Close()).DisposeWith(disposables); });
} }
private void InitializeComponent() private void InitializeComponent()
@ -19,4 +25,4 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.Views
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
} }
} }

View File

@ -1,4 +1,6 @@
namespace Artemis.UI.Avalonia.Screens.ProfileEditor.ViewModels using Artemis.UI.Avalonia.Shared;
namespace Artemis.UI.Avalonia.Screens.ProfileEditor.ViewModels
{ {
public class ProfileEditorViewModel : ActivatableViewModelBase public class ProfileEditorViewModel : ActivatableViewModelBase
{ {

View File

@ -1,5 +1,6 @@
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Root.ViewModels namespace Artemis.UI.Avalonia.Screens.Root.ViewModels

View File

@ -3,6 +3,7 @@ using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Root.ViewModels namespace Artemis.UI.Avalonia.Screens.Root.ViewModels

View File

@ -1,5 +1,6 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Shared;
namespace Artemis.UI.Avalonia.Screens.Root.ViewModels namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
{ {

View File

@ -1,4 +1,5 @@
using System; using System;
using Artemis.UI.Avalonia.Shared;
using Material.Icons; using Material.Icons;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;

View File

@ -9,6 +9,7 @@ using Artemis.UI.Avalonia.Screens.Home.ViewModels;
using Artemis.UI.Avalonia.Screens.Settings; using Artemis.UI.Avalonia.Screens.Settings;
using Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels; using Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels;
using Artemis.UI.Avalonia.Screens.Workshop.ViewModels; using Artemis.UI.Avalonia.Screens.Workshop.ViewModels;
using Artemis.UI.Avalonia.Shared;
using Material.Icons; using Material.Icons;
using Ninject; using Ninject;
using ReactiveUI; using ReactiveUI;

View File

@ -1,5 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels; using Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Settings namespace Artemis.UI.Avalonia.Screens.Settings

View File

@ -1,7 +1,9 @@
using System; using System;
using System.Reactive.Disposables;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Shared;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Flurl.Http; using Flurl.Http;
using ReactiveUI; using ReactiveUI;
@ -19,7 +21,7 @@ namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
public AboutTabViewModel() public AboutTabViewModel()
{ {
DisplayName = "About"; DisplayName = "About";
this.WhenActivated((Action<IDisposable> _) => Task.Run(Activate)); this.WhenActivated((CompositeDisposable _) => Task.Run(Activate));
} }
public string? Version public string? Version

View File

@ -1,4 +1,6 @@
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels using Artemis.UI.Avalonia.Shared;
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
{ {
public class DevicesTabViewModel : ActivatableViewModelBase public class DevicesTabViewModel : ActivatableViewModelBase
{ {

View File

@ -11,6 +11,7 @@ using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Services.Interfaces; using Artemis.UI.Avalonia.Services.Interfaces;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
using Serilog.Events; using Serilog.Events;

View File

@ -1,10 +1,116 @@
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Avalonia.Extensions;
using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Screens.Plugins.ViewModels;
using Artemis.UI.Avalonia.Shared;
using Artemis.UI.Avalonia.Shared.Services.Builders;
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels
{ {
public class PluginsTabViewModel : ActivatableViewModelBase public class PluginsTabViewModel : ActivatableViewModelBase
{ {
public PluginsTabViewModel() private readonly IPluginManagementService _pluginManagementService;
private readonly INotificationService _notificationService;
private readonly IWindowService _windowService;
private readonly ISettingsVmFactory _settingsVmFactory;
private string? _searchPluginInput;
private List<PluginSettingsViewModel>? _instances;
public PluginsTabViewModel(IPluginManagementService pluginManagementService, INotificationService notificationService, IWindowService windowService, ISettingsVmFactory settingsVmFactory)
{ {
_pluginManagementService = pluginManagementService;
_notificationService = notificationService;
_windowService = windowService;
_settingsVmFactory = settingsVmFactory;
DisplayName = "Plugins"; DisplayName = "Plugins";
Plugins = new ObservableCollection<PluginSettingsViewModel>();
this.WhenAnyValue(x => x.SearchPluginInput).Throttle(TimeSpan.FromMilliseconds(300)).Subscribe(SearchPlugins);
this.WhenActivated((CompositeDisposable _) => GetPluginInstances());
}
public ObservableCollection<PluginSettingsViewModel> Plugins { get; }
public string? SearchPluginInput
{
get => _searchPluginInput;
set => this.RaiseAndSetIfChanged(ref _searchPluginInput, value);
}
public void OpenUrl(string url) => Utilities.OpenUrl(url);
public async Task ImportPlugin()
{
string[]? files = await _windowService.CreateOpenFileDialog().WithTitle("Import Artemis plugin").HavingFilter(f => f.WithExtension("zip").WithName("ZIP files")).ShowAsync();
if (files == null)
return;
// Take the actual import off of the UI thread
await Task.Run(() =>
{
Plugin plugin = _pluginManagementService.ImportPlugin(files[0]);
GetPluginInstances();
SearchPluginInput = plugin.Info.Name;
// Enable it via the VM to enable the prerequisite dialog
PluginSettingsViewModel pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
if (pluginViewModel is { IsEnabled: false })
pluginViewModel.IsEnabled = true;
_notificationService.CreateNotification()
.WithTitle("Success")
.WithMessage($"Imported plugin: {plugin.Info.Name}")
.WithSeverity(NotificationSeverity.Success)
.Show();
});
}
public void GetPluginInstances()
{
_instances = _pluginManagementService.GetAllPlugins()
.Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
.OrderBy(i => i.Plugin.Info.Name)
.ToList();
SearchPlugins(SearchPluginInput);
}
private void SearchPlugins(string? searchPluginInput)
{
if (_instances == null)
return;
List<PluginSettingsViewModel> instances = _instances;
string? search = searchPluginInput?.ToLower();
if (!string.IsNullOrWhiteSpace(search))
instances = instances.Where(i => i.Plugin.Info.Name.ToLower().Contains(search) ||
i.Plugin.Info.Description != null && i.Plugin.Info.Description.ToLower().Contains(search)).ToList();
foreach (PluginSettingsViewModel pluginSettingsViewModel in instances)
{
if (!Plugins.Contains(pluginSettingsViewModel))
Plugins.Add(pluginSettingsViewModel);
}
foreach (PluginSettingsViewModel pluginSettingsViewModel in Plugins.ToList())
{
if (!instances.Contains(pluginSettingsViewModel))
Plugins.Remove(pluginSettingsViewModel);
}
Plugins.Sort(i => i.Plugin.Info.Name);
} }
} }
} }

View File

@ -4,5 +4,5 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Views.PluginsTabView"> x:Class="Artemis.UI.Avalonia.Screens.Settings.Tabs.Views.PluginsTabView">
Welcome to Avalonia! <ItemsControl Items="{Binding Plugins}"></ItemsControl>
</UserControl> </UserControl>

View File

@ -1,9 +1,11 @@
using Artemis.UI.Avalonia.Screens.Settings.Tabs.ViewModels;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Views namespace Artemis.UI.Avalonia.Screens.Settings.Tabs.Views
{ {
public partial class PluginsTabView : UserControl public partial class PluginsTabView : ReactiveUserControl<PluginsTabViewModel>
{ {
public PluginsTabView() public PluginsTabView()
{ {

View File

@ -1,4 +1,5 @@
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Avalonia.Shared;
using ReactiveUI; using ReactiveUI;
using SkiaSharp; using SkiaSharp;

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Shared;
using Artemis.UI.Avalonia.Shared.Services.Interfaces; using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using Avalonia.Input; using Avalonia.Input;
using ReactiveUI; using ReactiveUI;
@ -105,7 +106,7 @@ namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
private async Task ExecuteViewProperties(ArtemisDevice device) private async Task ExecuteViewProperties(ArtemisDevice device)
{ {
await _windowService.ShowDialogAsync<bool>(_deviceVmFactory.DevicePropertiesViewModel(device)); await _windowService.ShowDialogAsync(_deviceVmFactory.DevicePropertiesViewModel(device));
} }
private bool Fits(float x, float y) private bool Fits(float x, float y)

View File

@ -15,9 +15,7 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public abstract class PluginConfigurationDialog : IPluginConfigurationDialog public abstract class PluginConfigurationDialog : IPluginConfigurationDialog
{ {
/// <summary> /// <inheritdoc />
/// The type of view model the tab contains
/// </summary>
public abstract Type Type { get; } public abstract Type Type { get; }
} }
} }