mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Merge branch 'feature/updating' into feature/gh-actions
This commit is contained in:
commit
f97d328f32
200 files changed
+3843
-1713
No files matched your search
@@ -8,6 +8,7 @@ using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using FluentAvalonia.Core;
|
||||
using Humanizer;
|
||||
using Material.Icons;
|
||||
|
||||
@@ -47,7 +48,8 @@ public class HotkeyBox : UserControl
|
||||
Hotkey.Key = (KeyboardKey?) e.Key;
|
||||
Hotkey.Modifiers = (KeyboardModifierKey?) e.KeyModifiers;
|
||||
UpdateDisplayTextBox();
|
||||
|
||||
HotkeyChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
@@ -134,4 +136,13 @@ public class HotkeyBox : UserControl
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the hotkey changes.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<HotkeyBox, EventArgs>? HotkeyChanged;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Reflection;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.UI.Shared.DryIoc;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
|
||||
/// </summary>
|
||||
public static class UIContainerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers shared UI services into the container.
|
||||
/// </summary>
|
||||
/// <param name="container">The builder building the current container</param>
|
||||
public static void RegisterSharedUI(this IContainer container)
|
||||
{
|
||||
Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly();
|
||||
container.RegisterMany(new[] { artemisShared }, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ninject.Extensions.Conventions;
|
||||
using Ninject.Modules;
|
||||
|
||||
namespace Artemis.UI.Shared.Ninject;
|
||||
|
||||
/// <summary>
|
||||
/// The main <see cref="NinjectModule" /> of the Artemis Shared UI toolkit that binds all services
|
||||
/// </summary>
|
||||
public class SharedUIModule : NinjectModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Load()
|
||||
{
|
||||
if (Kernel == null)
|
||||
throw new ArgumentNullException("Kernel shouldn't be null here.");
|
||||
|
||||
// Bind all shared UI services as singletons
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromAssemblyContaining<IArtemisSharedUIService>()
|
||||
.IncludingNonPublicTypes()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IArtemisSharedUIService>()
|
||||
.BindAllInterfaces()
|
||||
.Configure(c => c.InSingletonScope());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.UI.Shared.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a provider for custom cursors.
|
||||
/// </summary>
|
||||
public interface IUpdateProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously checks whether an update is available.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
|
||||
/// <returns>A task returning <see langword="true" /> if an update is available; otherwise <see langword="false" />.</returns>
|
||||
Task<bool> CheckForUpdate(string channel);
|
||||
|
||||
/// <summary>
|
||||
/// Applies any available updates.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
|
||||
/// <param name="silent">Whether or not to update silently.</param>
|
||||
Task ApplyUpdate(string channel, bool silent);
|
||||
|
||||
/// <summary>
|
||||
/// Offer to install the update to the user.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
|
||||
/// <param name="windowOpen">A boolean indicating whether the main window is open.</param>
|
||||
/// <returns>A task returning <see langword="true" /> if the user chose to update; otherwise <see langword="false" />.</returns>
|
||||
Task OfferUpdate(string channel, bool windowOpen);
|
||||
}
|
||||
@@ -3,9 +3,8 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
using DryIoc;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders;
|
||||
@@ -16,13 +15,13 @@ namespace Artemis.UI.Shared.Services.Builders;
|
||||
public class ContentDialogBuilder
|
||||
{
|
||||
private readonly ContentDialog _contentDialog;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IContainer _container;
|
||||
private readonly Window _parent;
|
||||
private ContentDialogViewModelBase? _viewModel;
|
||||
|
||||
internal ContentDialogBuilder(IKernel kernel, Window parent)
|
||||
internal ContentDialogBuilder(IContainer container, Window parent)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_container = container;
|
||||
_parent = parent;
|
||||
_contentDialog = new ContentDialog
|
||||
{
|
||||
@@ -129,12 +128,11 @@ public class ContentDialogBuilder
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the view model to host.</typeparam>
|
||||
/// <param name="viewModel">The resulting view model.</param>
|
||||
/// <param name="parameters">Optional parameters to pass to the constructor of the view model, case and order sensitive.</param>
|
||||
/// <param name="parameters">Optional parameters to pass to the constructor of the view model.</param>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithViewModel<T>(out T viewModel, params (string name, object? value)[] parameters) where T : ContentDialogViewModelBase
|
||||
public ContentDialogBuilder WithViewModel<T>(out T viewModel, params object[] parameters) where T : ContentDialogViewModelBase
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
viewModel = _kernel.Get<T>(paramsArray);
|
||||
viewModel = _container.Resolve<T>(parameters);
|
||||
viewModel.ContentDialog = _contentDialog;
|
||||
_contentDialog.Content = viewModel;
|
||||
|
||||
|
||||
@@ -8,22 +8,21 @@ using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
using Artemis.UI.Shared.DefaultTypes.DataModel.Display;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.UI.Shared.Services;
|
||||
|
||||
internal class DataModelUIService : IDataModelUIService
|
||||
{
|
||||
private readonly IDataModelService _dataModelService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IContainer _container;
|
||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
|
||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
|
||||
|
||||
public DataModelUIService(IDataModelService dataModelService, IKernel kernel)
|
||||
public DataModelUIService(IDataModelService dataModelService, IContainer container)
|
||||
{
|
||||
_dataModelService = dataModelService;
|
||||
_kernel = kernel;
|
||||
_container = container;
|
||||
_registeredDataModelEditors = new List<DataModelVisualizationRegistration>();
|
||||
_registeredDataModelDisplays = new List<DataModelVisualizationRegistration>();
|
||||
|
||||
@@ -36,18 +35,8 @@ internal class DataModelUIService : IDataModelUIService
|
||||
// This assumes the type can be converted, that has been checked when the VM was created
|
||||
if (initialValue != null && initialValue.GetType() != registration.SupportedType)
|
||||
initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
|
||||
|
||||
IParameter[] parameters =
|
||||
{
|
||||
new ConstructorArgument("targetDescription", description),
|
||||
new ConstructorArgument("initialValue", initialValue)
|
||||
};
|
||||
|
||||
// If this ever happens something is likely wrong with the plugin unload detection
|
||||
if (registration.Plugin.Kernel == null)
|
||||
throw new ArtemisSharedUIException("Cannot InstantiateDataModelInputViewModel for a registration by an uninitialized plugin");
|
||||
|
||||
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Kernel.Get(registration.ViewModelType, parameters);
|
||||
|
||||
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Resolve(registration.ViewModelType, description, initialValue);
|
||||
viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes;
|
||||
return viewModel;
|
||||
}
|
||||
@@ -133,7 +122,7 @@ internal class DataModelUIService : IDataModelUIService
|
||||
return existing;
|
||||
}
|
||||
|
||||
_kernel.Bind(viewModelType).ToSelf();
|
||||
_container.Register(viewModelType, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
|
||||
|
||||
// Create the registration
|
||||
DataModelVisualizationRegistration registration = new(this, RegistrationType.Input, plugin, supportedType, viewModelType)
|
||||
@@ -162,7 +151,7 @@ internal class DataModelUIService : IDataModelUIService
|
||||
return existing;
|
||||
}
|
||||
|
||||
_kernel.Bind(viewModelType).ToSelf();
|
||||
_container.Register(viewModelType);
|
||||
DataModelVisualizationRegistration registration = new(this, RegistrationType.Display, plugin, supportedType, viewModelType);
|
||||
_registeredDataModelDisplays.Add(registration);
|
||||
return registration;
|
||||
@@ -178,7 +167,8 @@ internal class DataModelUIService : IDataModelUIService
|
||||
registration.Unsubscribe();
|
||||
_registeredDataModelEditors.Remove(registration);
|
||||
|
||||
_kernel.Unbind(registration.ViewModelType);
|
||||
_container.Unregister(registration.ViewModelType);
|
||||
_container.ClearCache(registration.ViewModelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,7 +182,8 @@ internal class DataModelUIService : IDataModelUIService
|
||||
registration.Unsubscribe();
|
||||
_registeredDataModelDisplays.Remove(registration);
|
||||
|
||||
_kernel.Unbind(registration.ViewModelType);
|
||||
_container.Unregister(registration.ViewModelType);
|
||||
_container.ClearCache(registration.ViewModelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,21 +196,11 @@ internal class DataModelUIService : IDataModelUIService
|
||||
|
||||
DataModelVisualizationRegistration? match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
|
||||
if (match != null)
|
||||
{
|
||||
// If this ever happens something is likely wrong with the plugin unload detection
|
||||
if (match.Plugin.Kernel == null)
|
||||
throw new ArtemisSharedUIException("Cannot GetDataModelDisplayViewModel for a registration by an uninitialized plugin");
|
||||
|
||||
result = (DataModelDisplayViewModel) match.Plugin.Kernel.Get(match.ViewModelType);
|
||||
}
|
||||
result = (DataModelDisplayViewModel) match.Plugin.Resolve(match.ViewModelType);
|
||||
else if (!fallBackToDefault)
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _kernel.Get<DefaultDataModelDisplayViewModel>();
|
||||
}
|
||||
result = _container.Resolve<DefaultDataModelDisplayViewModel>();
|
||||
|
||||
if (result != null)
|
||||
result.PropertyDescription = description;
|
||||
|
||||
@@ -16,7 +16,7 @@ public interface IWindowService : IArtemisSharedUIService
|
||||
/// </summary>
|
||||
/// <typeparam name="TViewModel">The type of view model to create</typeparam>
|
||||
/// <returns>The created view model</returns>
|
||||
TViewModel ShowWindow<TViewModel>(params (string name, object value)[] parameters);
|
||||
TViewModel ShowWindow<TViewModel>(params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Given a ViewModel, show its corresponding View as a window
|
||||
@@ -37,7 +37,7 @@ public interface IWindowService : IArtemisSharedUIService
|
||||
/// </summary>
|
||||
/// <typeparam name="TViewModel">The type of view model to create</typeparam>
|
||||
/// <returns>The created view model</returns>
|
||||
Task<TViewModel> ShowDialogAsync<TViewModel>(params (string name, object value)[] parameters);
|
||||
Task<TViewModel> ShowDialogAsync<TViewModel>(params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Given a ViewModel, show its corresponding View as a dialog
|
||||
@@ -60,7 +60,7 @@ public interface IWindowService : IArtemisSharedUIService
|
||||
/// <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>
|
||||
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>;
|
||||
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params object[] parameters) where TViewModel : DialogViewModelBase<TResult>;
|
||||
|
||||
/// <summary>
|
||||
/// Shows a content dialog asking the user to confirm an action
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||
|
||||
|
||||
@@ -3,19 +3,18 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.PropertyInput;
|
||||
|
||||
internal class PropertyInputService : IPropertyInputService
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IContainer _container;
|
||||
private readonly List<PropertyInputRegistration> _registeredPropertyEditors;
|
||||
|
||||
public PropertyInputService(IKernel kernel)
|
||||
public PropertyInputService(IContainer container)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_container = container;
|
||||
_registeredPropertyEditors = new List<PropertyInputRegistration>();
|
||||
RegisteredPropertyEditors = new ReadOnlyCollection<PropertyInputRegistration>(_registeredPropertyEditors);
|
||||
}
|
||||
@@ -55,7 +54,7 @@ internal class PropertyInputService : IPropertyInputService
|
||||
return existing;
|
||||
}
|
||||
|
||||
_kernel.Bind(viewModelType).ToSelf();
|
||||
_container.Register(viewModelType);
|
||||
PropertyInputRegistration registration = new(this, plugin, supportedType, viewModelType);
|
||||
_registeredPropertyEditors.Add(registration);
|
||||
return registration;
|
||||
@@ -71,7 +70,8 @@ internal class PropertyInputService : IPropertyInputService
|
||||
registration.Unsubscribe();
|
||||
_registeredPropertyEditors.Remove(registration);
|
||||
|
||||
_kernel.Unbind(registration.ViewModelType);
|
||||
_container.Unregister(registration.ViewModelType);
|
||||
_container.ClearCache(registration.ViewModelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,10 +110,9 @@ internal class PropertyInputService : IPropertyInputService
|
||||
if (viewModelType == null)
|
||||
return null;
|
||||
|
||||
ConstructorArgument parameter = new("layerProperty", layerProperty);
|
||||
// ReSharper disable once InconsistentlySynchronizedField
|
||||
// When you've just spent the last 2 hours trying to figure out a deadlock and reach this line, I'm so, so sorry. I thought this would be fine.
|
||||
IKernel kernel = registration?.Plugin.Kernel ?? _kernel;
|
||||
return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter);
|
||||
IContainer container = registration?.Plugin.Container ?? _container;
|
||||
return (PropertyInputViewModel<T>) container.Resolve(viewModelType, args: new object[] { layerProperty });
|
||||
}
|
||||
}
|
||||
@@ -6,34 +6,33 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using DryIoc;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
namespace Artemis.UI.Shared.Services;
|
||||
|
||||
internal class WindowService : IWindowService
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IContainer _container;
|
||||
private bool _exceptionDialogOpen;
|
||||
|
||||
public WindowService(IKernel kernel)
|
||||
public WindowService(IContainer container)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_container = container;
|
||||
}
|
||||
|
||||
public T ShowWindow<T>(params (string name, object value)[] parameters)
|
||||
public T ShowWindow<T>(params object[] parameters)
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
T viewModel = _kernel.Get<T>(paramsArray)!;
|
||||
T viewModel = _container.Resolve<T>(parameters);
|
||||
if (viewModel == null)
|
||||
throw new ArtemisSharedUIException($"Failed to show window for VM of type {typeof(T).Name}, could not create instance.");
|
||||
|
||||
ShowWindow(viewModel);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public Window ShowWindow(object viewModel)
|
||||
{
|
||||
Window? parent = GetCurrentWindow();
|
||||
|
||||
string name = viewModel.GetType().FullName!.Split('`')[0].Replace("ViewModel", "View");
|
||||
Type? type = viewModel.GetType().Assembly.GetType(name);
|
||||
|
||||
@@ -45,18 +44,17 @@ internal class WindowService : IWindowService
|
||||
|
||||
Window window = (Window) Activator.CreateInstance(type)!;
|
||||
window.DataContext = viewModel;
|
||||
if (parent != null)
|
||||
window.Show(parent);
|
||||
else
|
||||
window.Show();
|
||||
window.Show();
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public async Task<T> ShowDialogAsync<T>(params (string name, object value)[] parameters)
|
||||
public async Task<T> ShowDialogAsync<T>(params object[] parameters)
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
T viewModel = _kernel.Get<T>(paramsArray)!;
|
||||
T viewModel = _container.Resolve<T>(parameters);
|
||||
if (viewModel == null)
|
||||
throw new ArtemisSharedUIException($"Failed to show window for VM of type {typeof(T).Name}, could not create instance.");
|
||||
|
||||
await ShowDialogAsync(viewModel);
|
||||
return viewModel;
|
||||
}
|
||||
@@ -79,10 +77,12 @@ internal class WindowService : IWindowService
|
||||
await window.ShowDialog(parent);
|
||||
}
|
||||
|
||||
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
||||
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params object[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
TViewModel viewModel = _kernel.Get<TViewModel>(paramsArray)!;
|
||||
TViewModel viewModel = _container.Resolve<TViewModel>(parameters);
|
||||
if (viewModel == null)
|
||||
throw new ArtemisSharedUIException($"Failed to show window for VM of type {typeof(TViewModel).Name}, could not create instance.");
|
||||
|
||||
return await ShowDialogAsync(viewModel);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ internal class WindowService : IWindowService
|
||||
{
|
||||
try
|
||||
{
|
||||
await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _kernel.Get<INotificationService>()));
|
||||
await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _container.Resolve<INotificationService>()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -143,7 +143,7 @@ internal class WindowService : IWindowService
|
||||
Window? currentWindow = GetCurrentWindow();
|
||||
if (currentWindow == null)
|
||||
throw new ArtemisSharedUIException("Can't show a content dialog without any windows being shown.");
|
||||
return new ContentDialogBuilder(_kernel, currentWindow);
|
||||
return new ContentDialogBuilder(_container, currentWindow);
|
||||
}
|
||||
|
||||
public OpenFolderDialogBuilder CreateOpenFolderDialog()
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
|
||||
<dataModelPicker:DataModelPickerButton></dataModelPicker:DataModelPickerButton>
|
||||
<dataModelPicker:DataModelPickerButton Classes="condensed"></dataModelPicker:DataModelPickerButton>
|
||||
|
||||
<controls1:HotkeyBox></controls1:HotkeyBox>
|
||||
<controls1:HotkeyBox Classes="condensed"></controls1:HotkeyBox>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
@@ -110,4 +113,10 @@
|
||||
<Setter Property="MinHeight" Value="24" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls1|HotkeyBox.condensed > TextBox#DisplayTextBox">
|
||||
<Setter Property="Padding" Value="6 0" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
<Setter Property="MinHeight" Value="24" />
|
||||
</Style>
|
||||
|
||||
</Styles>
|
||||
Reference in new issue
Block a user