Meta - Fix remaing warnings in non-WPF projects

This commit is contained in:
Robert committed 2022-01-02 23:56:06 +01:00
1 parent 1c15fbe983
commit 353eec2529
37 files changed
+539 -172

No files matched your search

@@ -88,22 +88,26 @@ namespace Artemis.UI.Shared.Controls
/// <summary>
/// Gets or sets the currently displayed icon as either a <see cref="MaterialIconKind" /> or an <see cref="Uri" />
/// pointing
/// to an SVG
/// pointing to an SVG
/// </summary>
public static readonly StyledProperty<Hotkey?> HotkeyProperty =
AvaloniaProperty.Register<HotkeyBox, Hotkey?>(nameof(Hotkey), defaultBindingMode: BindingMode.TwoWay, notifying: HotkeyChanging);
/// <summary>
/// Gets or sets the watermark of the hotkey box when it is empty.
/// </summary>
public static readonly StyledProperty<string?> WatermarkProperty =
AvaloniaProperty.Register<HotkeyBox, string?>(nameof(Watermark));
/// <summary>
/// Gets or sets a boolean indicating whether the watermark should float above the hotkey box when it is not empty.
/// </summary>
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
AvaloniaProperty.Register<HotkeyBox, bool>(nameof(UseFloatingWatermark));
/// <summary>
/// Gets or sets the currently displayed icon as either a <see cref="MaterialIconKind" /> or an <see cref="Uri" />
/// pointing
/// to an SVG
/// pointing to an SVG
/// </summary>
public Hotkey? Hotkey
{
@@ -111,12 +115,18 @@ namespace Artemis.UI.Shared.Controls
set => SetValue(HotkeyProperty, value);
}
/// <summary>
/// Gets or sets the watermark of the hotkey box when it is empty.
/// </summary>
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
/// <summary>
/// Gets or sets a boolean indicating whether the watermark should float above the hotkey box when it is not empty.
/// </summary>
public bool UseFloatingWatermark
{
get => GetValue(UseFloatingWatermarkProperty);
@@ -13,7 +13,7 @@ namespace Artemis.UI.Shared.Converters
public class ColorToSKColorConverter : IValueConverter
{
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is Color avaloniaColor)
return new SKColor(avaloniaColor.R, avaloniaColor.G, avaloniaColor.B, avaloniaColor.A);
@@ -24,7 +24,7 @@ namespace Artemis.UI.Shared.Converters
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
Color result = new(0, 0, 0, 0);
if (value is SKColor skColor)
@@ -11,13 +11,13 @@ namespace Artemis.UI.Shared.Converters
public class EnumToBooleanConverter : IValueConverter
{
/// <inheritdoc />
public object Convert(object? value, Type targetType, object parameter, CultureInfo culture)
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Equals(value, parameter);
}
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return value?.Equals(true) == true ? parameter : BindingOperations.DoNothing;
}
@@ -13,7 +13,7 @@ namespace Artemis.UI.Shared.Converters
public class SKColorToColorConverter : IValueConverter
{
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
Color result = new(0, 0, 0, 0);
if (value is SKColor skColor)
@@ -25,7 +25,7 @@ namespace Artemis.UI.Shared.Converters
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is Color avaloniaColor)
return new SKColor(avaloniaColor.R, avaloniaColor.G, avaloniaColor.B, avaloniaColor.A);
@@ -21,7 +21,7 @@ namespace Artemis.UI.Shared.Converters
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
@@ -1,11 +1,16 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
{
public partial class DefaultDataModelDisplayView : UserControl
/// <summary>
/// Represents a default data model display view.
/// </summary>
public class DefaultDataModelDisplayView : UserControl
{
/// <summary>
/// Creates a new instance of the <see cref="DefaultDataModelDisplayView" /> class.
/// </summary>
public DefaultDataModelDisplayView()
{
InitializeComponent();
@@ -16,4 +21,4 @@ namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
AvaloniaXamlLoader.Load(this);
}
}
}
}
@@ -22,6 +22,7 @@ namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
PreserveReferencesHandling = PreserveReferencesHandling.None
};
_display = "null";
}
public string Display
@@ -10,6 +10,9 @@ using ReactiveUI;
namespace Artemis.UI.Shared.Services.Builders
{
/// <summary>
/// Represents a builder that can be used to create Fluent UI dialogs.
/// </summary>
public class ContentDialogBuilder
{
private readonly ContentDialog _contentDialog;
@@ -27,24 +30,44 @@ namespace Artemis.UI.Shared.Services.Builders
};
}
/// <summary>
/// Changes the title of the dialog.
/// </summary>
/// <param name="title">The new title.</param>
/// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder WithTitle(string? title)
{
_contentDialog.Title = title;
return this;
}
/// <summary>
/// Changes the content of the dialog.
/// </summary>
/// <param name="content">The new content.</param>
/// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder WithContent(string? content)
{
_contentDialog.Content = content;
return this;
}
/// <summary>
/// Changes the default button of the dialog that is pressed on enter.
/// </summary>
/// <param name="defaultButton">The default button.</param>
/// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder WithDefaultButton(ContentDialogButton defaultButton)
{
_contentDialog.DefaultButton = defaultButton;
_contentDialog.DefaultButton = (FluentAvalonia.UI.Controls.ContentDialogButton) defaultButton;
return this;
}
/// <summary>
/// Changes the primary button of the dialog.
/// </summary>
/// <param name="configure">An action to configure the button.</param>
/// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder HavingPrimaryButton(Action<ContentDialogButtonBuilder> configure)
{
ContentDialogButtonBuilder builder = new();
@@ -65,6 +88,11 @@ namespace Artemis.UI.Shared.Services.Builders
return this;
}
/// <summary>
/// Changes the secondary button of the dialog.
/// </summary>
/// <param name="configure">An action to configure the button.</param>
/// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder HavingSecondaryButton(Action<ContentDialogButtonBuilder> configure)
{
ContentDialogButtonBuilder builder = new();
@@ -85,12 +113,24 @@ namespace Artemis.UI.Shared.Services.Builders
return this;
}
/// <summary>
/// Changes the text of the close button of the dialog.
/// </summary>
/// <param name="text">The new text.</param>
/// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder WithCloseButtonText(string? text)
{
_contentDialog.CloseButtonText = text;
return this;
}
/// <summary>
/// Changes the view model of the content dialog, hosting it inside the dialog.
/// </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>
/// <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
{
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
@@ -102,6 +142,11 @@ namespace Artemis.UI.Shared.Services.Builders
return this;
}
/// <summary>
/// Asynchronously shows the content dialog.
/// </summary>
/// <returns>A task containing the result of the content dialog.</returns>
/// <exception cref="ArtemisSharedUIException">Thrown when the parent window does not contain a panel at its root.</exception>
public async Task<ContentDialogResult> ShowAsync()
{
if (_parent.Content is not Panel panel)
@@ -111,9 +156,9 @@ namespace Artemis.UI.Shared.Services.Builders
{
panel.Children.Add(_contentDialog);
ContentDialogResult result = await _contentDialog.ShowAsync();
// Take the dialog away from the VM in case it's going to try to hide it again or whatever...
if (_viewModel != null)
if (_viewModel != null)
_viewModel.ContentDialog = null;
return result;
@@ -125,6 +170,35 @@ namespace Artemis.UI.Shared.Services.Builders
}
}
/// <summary>
/// Represents a content dialog button.
/// </summary>
public enum ContentDialogButton
{
/// <summary>
/// No button.
/// </summary>
None,
/// <summary>
/// The primary button.
/// </summary>
Primary,
/// <summary>
/// The secondary button.
/// </summary>
Secondary,
/// <summary>
/// The close button.
/// </summary>
Close,
}
/// <summary>
/// Represents a builder that can be used to create buttons inside content dialogs.
/// </summary>
public class ContentDialogButtonBuilder
{
internal ContentDialogButtonBuilder()
@@ -133,20 +207,48 @@ namespace Artemis.UI.Shared.Services.Builders
internal string? Text { get; set; }
internal ICommand? Command { get; set; }
internal Action? Action { get; set; }
internal object? CommandParameter { get; set; }
/// <summary>
/// Changes text message of the button.
/// </summary>
/// <param name="text">The new text.</param>
/// <returns>The notification builder that can be used to further build the button.</returns>
public ContentDialogButtonBuilder WithText(string? text)
{
Text = text;
return this;
}
/// <summary>
/// Changes action that is called when the button is clicked.
/// </summary>
/// <param name="action">The action to call when the button is clicked.</param>
/// <returns>The builder that can be used to further build the button.</returns>
public ContentDialogButtonBuilder WithAction(Action action)
{
Action = action;
Command = ReactiveCommand.Create(() => Action());
return this;
}
/// <summary>
/// Changes command that is called when the button is clicked.
/// </summary>
/// <param name="command">The command to call when the button is clicked.</param>
/// <returns>The builder that can be used to further build the button.</returns>
public ContentDialogButtonBuilder WithCommand(ICommand? command)
{
Command = command;
return this;
}
/// <summary>
/// Changes parameter of the command that is called when the button is clicked.
/// </summary>
/// <param name="commandParameter">The parameter of the command to call when the button is clicked.</param>
/// <returns>The builder that can be used to further build the button.</returns>
public ContentDialogButtonBuilder WithCommandParameter(object? commandParameter)
{
CommandParameter = commandParameter;
@@ -101,7 +101,7 @@ namespace Artemis.UI.Shared.Services.Builders
}
/// <summary>
/// Changes the action button of the dialog.
/// Changes the action button of the notification.
/// </summary>
/// <param name="configure">An action to configure the button.</param>
/// <returns>The notification builder that can be used to further build the notification.</returns>
@@ -154,6 +154,7 @@ namespace Artemis.UI.Shared.Services.Builders
private Action? _action;
private ICommand? _command;
private string _text = "Text";
private object? _commandParameter;
/// <summary>
/// Changes text message of the button.
@@ -170,7 +171,7 @@ namespace Artemis.UI.Shared.Services.Builders
/// Changes action that is called when the button is clicked.
/// </summary>
/// <param name="action">The action to call when the button is clicked.</param>
/// <returns>The notification builder that can be used to further build the button.</returns>
/// <returns>The builder that can be used to further build the button.</returns>
public NotificationButtonBuilder WithAction(Action action)
{
_command = null;
@@ -182,7 +183,7 @@ namespace Artemis.UI.Shared.Services.Builders
/// Changes command that is called when the button is clicked.
/// </summary>
/// <param name="command">The command to call when the button is clicked.</param>
/// <returns>The notification builder that can be used to further build the button.</returns>
/// <returns>The builder that can be used to further build the button.</returns>
public NotificationButtonBuilder WithCommand(ICommand command)
{
_action = null;
@@ -190,21 +191,50 @@ namespace Artemis.UI.Shared.Services.Builders
return this;
}
/// <summary>
/// Changes parameter of the command that is called when the button is clicked.
/// </summary>
/// <param name="commandParameter">The parameter of the command to call when the button is clicked.</param>
/// <returns>The builder that can be used to further build the button.</returns>
public NotificationButtonBuilder WithCommandParameter(object? commandParameter)
{
_commandParameter = commandParameter;
return this;
}
internal IControl Build()
{
if (_action != null)
return new Button {Content = _text, Command = ReactiveCommand.Create(() => _action())};
if (_command != null)
return new Button {Content = _text, Command = _command};
return new Button {Content = _text, Command = _command, CommandParameter = _commandParameter};
return new Button {Content = _text};
}
}
/// <summary>
/// Represents a severity of a notification.
/// </summary>
public enum NotificationSeverity
{
/// <summary>
/// A severity for informational messages.
/// </summary>
Informational,
/// <summary>
/// A severity for success messages.
/// </summary>
Success,
/// <summary>
/// A severity for warning messages.
/// </summary>
Warning,
/// <summary>
/// A severity for error messages.
/// </summary>
Error
}
}
@@ -12,6 +12,10 @@ namespace Artemis.UI.Shared.Services.Builders
private readonly OpenFileDialog _openFileDialog;
private readonly Window _parent;
/// <summary>
/// Creates a new instance of the <see cref="OpenFileDialogBuilder"/> class.
/// </summary>
/// <param name="parent">The parent window that will host the dialog.</param>
public OpenFileDialogBuilder(Window parent)
{
_parent = parent;
@@ -67,7 +71,7 @@ namespace Artemis.UI.Shared.Services.Builders
}
/// <summary>
/// Shows the file dialog
/// Asynchronously shows the file dialog.
/// </summary>
/// <returns>
/// A task that on completion returns an array containing the full path to the selected
@@ -12,6 +12,10 @@ namespace Artemis.UI.Shared.Services.Builders
private readonly Window _parent;
private readonly SaveFileDialog _saveFileDialog;
/// <summary>
/// Creates a new instance of the <see cref="SaveFileDialogBuilder" /> class.
/// </summary>
/// <param name="parent">The parent window that will host the notification.</param>
public SaveFileDialogBuilder(Window parent)
{
_parent = parent;
@@ -67,7 +71,7 @@ namespace Artemis.UI.Shared.Services.Builders
}
/// <summary>
/// Shows the save file dialog.
/// Asynchronously shows the save file dialog.
/// </summary>
/// <returns>
/// A task that on completion contains the full path of the save location, or null if the
@@ -2,8 +2,15 @@
namespace Artemis.UI.Shared.Services.Interfaces
{
/// <summary>
/// A service that can be used to create notifications in either the application or on the desktop.
/// </summary>
public interface INotificationService : IArtemisSharedUIService
{
/// <summary>
/// Creates an in-app notification using a builder.
/// </summary>
/// <returns>A builder used to configure and show the notification.</returns>
NotificationBuilder CreateNotification();
}
}
@@ -5,30 +5,34 @@ using Avalonia.Controls;
namespace Artemis.UI.Shared.Services.Interfaces
{
/// <summary>
/// A service that can be used to show windows and dialogs.
/// </summary>
public interface IWindowService : IArtemisSharedUIService
{
/// <summary>
/// Creates a view model instance of type <typeparamref name="TViewModel" /> and shows its corresponding View as a window
/// 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>
TViewModel ShowWindow<TViewModel>(params (string name, object value)[] parameters);
/// <summary>
/// Given a ViewModel, show its corresponding View as a window
/// Given a ViewModel, show its corresponding View as a window
/// </summary>
/// <param name="viewModel">ViewModel to show the View for</param>
void ShowWindow(object viewModel);
/// <summary>
/// Shows a dialog displaying the given exception
/// Shows a dialog displaying the given exception
/// </summary>
/// <param name="title">The title of the dialog</param>
/// <param name="exception">The exception to display</param>
void ShowExceptionDialog(string title, Exception exception);
/// <summary>
/// Given an existing ViewModel, show its corresponding View as a Dialog
/// 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>
@@ -36,7 +40,8 @@ namespace Artemis.UI.Shared.Services.Interfaces
Task<TResult> ShowDialogAsync<TResult>(DialogViewModelBase<TResult> viewModel);
/// <summary>
/// Creates a view model instance of type <typeparamref name="TViewModel"/> and shows its corresponding View as a Dialog
/// 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>
@@ -44,37 +49,40 @@ namespace Artemis.UI.Shared.Services.Interfaces
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
/// 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>
/// <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>
/// Creates an open file dialog, use the fluent API to configure it
/// Creates an open file dialog, use the fluent API to configure it
/// </summary>
/// <returns>The builder that can be used to configure the dialog</returns>
OpenFileDialogBuilder CreateOpenFileDialog();
/// <summary>
/// Creates a save file dialog, use the fluent API to configure it
/// Creates a save file dialog, use the fluent API to configure it
/// </summary>
/// <returns>The builder that can be used to configure the dialog</returns>
SaveFileDialogBuilder CreateSaveFileDialog();
/// <summary>
/// Creates a content dialog, use the fluent API to configure it
/// Creates a content dialog, use the fluent API to configure it
/// </summary>
/// <returns>The builder that can be used to configure the dialog</returns>
ContentDialogBuilder CreateContentDialog();
/// <summary>
/// Gets the current window of the application
/// Gets the current window of the application
/// </summary>
/// <returns>The current window of the application</returns>
Window GetCurrentWindow();
Window? GetCurrentWindow();
}
}
@@ -3,6 +3,9 @@ using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Shared.Services.MainWindowService
{
/// <summary>
/// A service that can be used to manage the state of the main window.
/// </summary>
public interface IMainWindowService : IArtemisSharedUIService
{
/// <summary>
@@ -1,9 +1,10 @@
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.Interfaces;
using Avalonia.Controls;
namespace Artemis.UI.Shared.Services
{
public class NotificationService : INotificationService
internal class NotificationService : INotificationService
{
private readonly IWindowService _windowService;
@@ -14,7 +15,11 @@ namespace Artemis.UI.Shared.Services
public NotificationBuilder CreateNotification()
{
return new NotificationBuilder(_windowService.GetCurrentWindow());
Window? currentWindow = _windowService.GetCurrentWindow();
if (currentWindow == null)
throw new ArtemisSharedUIException("Can't show an in-app notification without any windows being shown.");
return new NotificationBuilder(currentWindow);
}
}
}
@@ -24,6 +24,9 @@ namespace Artemis.UI.Shared.Services
public async Task CopyException()
{
if (Application.Current?.Clipboard == null)
return;
await Application.Current.Clipboard.SetTextAsync(Exception.ToString());
_notificationService.CreateNotification()
.WithMessage("Copied stack trace to clipboard.")
@@ -39,14 +39,10 @@ namespace Artemis.UI.Shared.Services
Type? type = viewModel.GetType().Assembly.GetType(name);
if (type == null)
{
throw new ArtemisSharedUIException($"Failed to find a window named {name}.");
}
if (!type.IsAssignableTo(typeof(Window)))
{
throw new ArtemisSharedUIException($"Type {name} is not a window.");
}
Window window = (Window) Activator.CreateInstance(type)!;
window.DataContext = viewModel;
@@ -77,20 +73,16 @@ namespace Artemis.UI.Shared.Services
public async Task<TResult> ShowDialogAsync<TResult>(DialogViewModelBase<TResult> viewModel)
{
Window parent = GetCurrentWindow();
Window? parent = GetCurrentWindow();
string name = viewModel.GetType().FullName!.Split('`')[0].Replace("ViewModel", "View");
Type? type = viewModel.GetType().Assembly.GetType(name);
if (type == null)
{
throw new ArtemisSharedUIException($"Failed to find a window named {name}.");
}
if (!type.IsAssignableTo(typeof(Window)))
{
throw new ArtemisSharedUIException($"Type {name} is not a window.");
}
Window window = (Window) Activator.CreateInstance(type)!;
window.DataContext = viewModel;
@@ -122,25 +114,32 @@ namespace Artemis.UI.Shared.Services
public ContentDialogBuilder CreateContentDialog()
{
return new ContentDialogBuilder(_kernel, GetCurrentWindow());
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);
}
public OpenFileDialogBuilder CreateOpenFileDialog()
{
return new OpenFileDialogBuilder(GetCurrentWindow());
Window? currentWindow = GetCurrentWindow();
if (currentWindow == null)
throw new ArtemisSharedUIException("Can't show an open file dialog without any windows being shown.");
return new OpenFileDialogBuilder(currentWindow);
}
public SaveFileDialogBuilder CreateSaveFileDialog()
{
return new SaveFileDialogBuilder(GetCurrentWindow());
Window? currentWindow = GetCurrentWindow();
if (currentWindow == null)
throw new ArtemisSharedUIException("Can't show a save file dialog without any windows being shown.");
return new SaveFileDialogBuilder(currentWindow);
}
public Window? GetCurrentWindow()
{
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic)
{
throw new ArtemisSharedUIException("Can't show a dialog when application lifetime is not IClassicDesktopStyleApplicationLifetime.");
}
if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic)
throw new ArtemisSharedUIException("Find an open window when application lifetime is not IClassicDesktopStyleApplicationLifetime.");
Window? parent = classic.Windows.FirstOrDefault(w => w.IsActive && w.ShowInTaskbar) ?? classic.MainWindow;
return parent;
@@ -34,7 +34,7 @@ namespace Artemis.UI.Windows
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
// On Windows shutdown dispose the kernel just so device providers get a chance to clean up
if (Application.Current.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
{
controlledApplicationLifetime.Exit += (_, _) =>
{
@@ -159,7 +159,7 @@ namespace Artemis.UI.Windows
}
// Lets try a graceful shutdown, PowerShell will kill if needed
if (Application.Current.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
}
@@ -168,7 +168,7 @@ namespace Artemis.UI.Windows
// Use PowerShell to kill the process after 8 sec just in case
RunForcedShutdownIfEnabled();
if (Application.Current.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
}
@@ -3,6 +3,7 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
@@ -113,13 +113,13 @@ namespace Artemis.UI.Windows.Providers.Input
if (key == KeyboardKey.LeftCtrl && keyboardData.Keyboard.ScanCode == 56)
return;
string identifier = data.Device?.DevicePath;
string? identifier = data.Device?.DevicePath;
// Let the core know there is an identifier so it can store new identifications if applicable
if (identifier != null)
OnIdentifierReceived(identifier, InputDeviceType.Keyboard);
ArtemisDevice device = null;
ArtemisDevice? device = null;
if (identifier != null)
try
{
@@ -180,9 +180,10 @@ namespace Artemis.UI.Windows.Providers.Input
return;
}
ArtemisDevice device = null;
string identifier = data.Device?.DevicePath;
ArtemisDevice? device = null;
string? identifier = data.Device?.DevicePath;
if (identifier != null)
{
try
{
device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Mouse);
@@ -191,6 +192,7 @@ namespace Artemis.UI.Windows.Providers.Input
{
_logger.Warning(e, "Failed to retrieve input device by its identifier");
}
}
// Debug.WriteLine($"Buttons: {mouseData.Mouse.Buttons}, Data: {mouseData.Mouse.ButtonData}, Flags: {mouseData.Mouse.Flags}, XY: {mouseData.Mouse.LastX},{mouseData.Mouse.LastY}");
@@ -91,7 +91,7 @@ namespace Artemis.UI.Windows.Utilities
}
PROCESS_INFORMATION pi = new();
if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, $"\"{fileName}\" {arguments}", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, $"\"{fileName}\" {arguments}", 0, IntPtr.Zero, Path.GetDirectoryName(fileName)!, ref si, out pi))
{
// Get the last error and display it.
int error = Marshal.GetLastWin32Error();
@@ -203,7 +203,7 @@ namespace Artemis.UI.Windows.Utilities
private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LookupPrivilegeValue(string host, string name, ref LUID pluid);
private static extern bool LookupPrivilegeValue(string? host, string name, ref LUID pluid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);
@@ -12,7 +12,7 @@ namespace Artemis.UI.Converters
public class ColorToSolidColorBrushConverter : IValueConverter
{
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return new SolidColorBrush(!(value is RGBColor color)
? new Color(0, 0, 0, 0)
@@ -20,7 +20,7 @@ namespace Artemis.UI.Converters
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return !(value is SolidColorBrush brush)
? RGBColor.Transparent
@@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Converters
{
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
private static string Description(Enum value)
{
object[] attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return (attributes.First() as DescriptionAttribute)?.Description;
// If no description is found, the least we can do is replace underscores with spaces
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
}
private static IEnumerable<Tuple<object, object>> GetAllValuesAndDescriptions(Type t)
{
if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select(e => new Tuple<object, object>(e, Description(e))).ToList();
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return GetAllValuesAndDescriptions(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
@@ -10,15 +10,15 @@ namespace Artemis.UI.Converters
#region Implementation of IValueConverter
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return value?.ToString();
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (Enum.TryParse(typeof(LedId), value?.ToString(), true, out object parsedLedId))
if (Enum.TryParse(typeof(LedId), value?.ToString(), true, out object? parsedLedId))
return parsedLedId;
return LedId.Unknown1;
}
@@ -8,7 +8,7 @@ namespace Artemis.UI.Converters
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double number)
return number * 100.0;
@@ -16,7 +16,7 @@ namespace Artemis.UI.Converters
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double number)
return number / 100.0;
@@ -7,14 +7,14 @@ namespace Artemis.UI.Converters
{
public class UriToFileNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is Uri uri && uri.IsFile)
return Path.GetFileName(uri.LocalPath);
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return value;
}
@@ -9,8 +9,10 @@ namespace Artemis.UI.Converters
public class ValuesAdditionConverter : IMultiValueConverter
{
/// <inheritdoc />
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
public object Convert(IList<object?>? values, Type targetType, object? parameter, CultureInfo culture)
{
if (values == null)
return 0.0;
return values.Where(v => v is double).Cast<double>().Sum();
}
}
@@ -16,7 +16,7 @@ namespace Artemis.UI.Screens.Debugger.Render
private Bitmap? _currentFrame;
private string? _frameTargetPath;
private string _renderer;
private string? _renderer;
private int _renderHeight;
private int _renderWidth;
@@ -56,7 +56,7 @@ namespace Artemis.UI.Screens.Debugger.Render
set => this.RaiseAndSetIfChanged(ref _renderHeight, value);
}
public string Renderer
public string? Renderer
{
get => _renderer;
set => this.RaiseAndSetIfChanged(ref _renderer, value);
@@ -28,6 +28,9 @@ namespace Artemis.UI.Screens.Device
public async Task CopyToClipboard(string content)
{
if (Application.Current?.Clipboard == null)
return;
await Application.Current.Clipboard.SetTextAsync(content);
_notificationService.CreateNotification().WithMessage("Copied path to clipboard.").Show();
}
@@ -16,7 +16,7 @@ namespace Artemis.UI.Screens.Device
private readonly IRgbService _rgbService;
private readonly IInputService _inputService;
private readonly ObservableCollection<ArtemisLed> _selectedLeds;
private ArtemisLed _selectedLed;
private ArtemisLed? _selectedLed;
public InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds, IRgbService rgbService, IInputService inputService)
{
@@ -34,7 +34,7 @@ namespace Artemis.UI.Screens.Device
public ArtemisDevice Device { get; }
public ArtemisLed SelectedLed
public ArtemisLed? SelectedLed
{
get => _selectedLed;
set => this.RaiseAndSetIfChanged(ref _selectedLed, value);
@@ -57,7 +57,7 @@ namespace Artemis.UI.Screens.Device
bool foundLedId = InputKeyUtilities.KeyboardKeyLedIdMap.TryGetValue(e.Key, out LedId ledId);
if (!foundLedId)
return;
ArtemisLed artemisLed = Device.GetLed(ledId, false);
ArtemisLed? artemisLed = Device.GetLed(ledId, false);
if (artemisLed == null)
return;
@@ -53,7 +53,7 @@ namespace Artemis.UI.Screens.Root
_assetLoader = assetLoader;
_defaultTitleBarViewModel = defaultTitleBarViewModel;
_sidebarVmFactory = sidebarVmFactory;
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime;
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!;
coreService.StartupArguments = _lifeTime.Args.ToList();
mainWindowService.ConfigureMainWindowProvider(this);
@@ -127,15 +127,15 @@ namespace Artemis.UI.Screens.Root
Icon = new WindowIcon(_assetLoader.Open(new Uri("avares://Artemis.UI/Assets/Images/Logo/bow.ico"))),
Command = ReactiveCommand.Create(OpenMainWindow)
};
_trayIcon.Menu = (NativeMenu?) Application.Current.FindResource("TrayIconMenu");
_trayIcon.Menu = (NativeMenu?) Application.Current!.FindResource("TrayIconMenu");
_trayIcons = new TrayIcons {_trayIcon};
TrayIcon.SetIcons(Application.Current, _trayIcons);
TrayIcon.SetIcons(Application.Current!, _trayIcons);
}
private void HideTrayIcon()
{
_trayIcon?.Dispose();
TrayIcon.SetIcons(Application.Current, null!);
TrayIcon.SetIcons(Application.Current!, null!);
_trayIcon = null;
_trayIcons = null;
@@ -69,7 +69,7 @@ namespace Artemis.UI.Screens.Settings
SearchPluginInput = plugin.Info.Name;
// Enable it via the VM to enable the prerequisite dialog
PluginSettingsViewModel pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
PluginSettingsViewModel? pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
if (pluginViewModel is {IsEnabled: false})
pluginViewModel.IsEnabled = true;
@@ -22,18 +22,18 @@ namespace Artemis.UI.Screens.Sidebar
private readonly ProfileCategory _profileCategory;
private readonly IProfileService _profileService;
private readonly IWindowService _windowService;
private ProfileConfigurationIconType _iconType;
private Hotkey? _disableHotkey;
private Hotkey? _enableHotkey;
private ProfileConfigurationHotkeyMode _hotkeyMode;
private ProfileConfigurationIconType _iconType;
private ObservableCollection<ProfileIconViewModel>? _materialIcons;
private ProfileConfiguration _profileConfiguration;
private string _profileName;
private Bitmap? _selectedBitmapSource;
private string? _selectedIconPath;
private ProfileIconViewModel? _selectedMaterialIcon;
private ProfileModuleViewModel? _selectedModule;
private string? _selectedIconPath;
private SvgImage? _selectedSvgSource;
private Hotkey? _enableHotkey;
private Hotkey? _disableHotkey;
public ProfileConfigurationEditViewModel(ProfileCategory profileCategory, ProfileConfiguration? profileConfiguration, IWindowService windowService,
IProfileService profileService, IPluginManagementService pluginManagementService)
@@ -46,9 +46,9 @@ namespace Artemis.UI.Screens.Sidebar
_iconType = _profileConfiguration.Icon.IconType;
_hotkeyMode = _profileConfiguration.HotkeyMode;
if (_profileConfiguration.EnableHotkey != null)
_enableHotkey = new Hotkey() {Key = _profileConfiguration.EnableHotkey.Key, Modifiers = profileConfiguration.EnableHotkey.Modifiers};
_enableHotkey = new Hotkey {Key = _profileConfiguration.EnableHotkey.Key, Modifiers = _profileConfiguration.EnableHotkey.Modifiers};
if (_profileConfiguration.DisableHotkey != null)
_disableHotkey = new Hotkey() {Key = _profileConfiguration.DisableHotkey.Key, Modifiers = profileConfiguration.DisableHotkey.Modifiers};
_disableHotkey = new Hotkey {Key = _profileConfiguration.DisableHotkey.Key, Modifiers = _profileConfiguration.DisableHotkey.Modifiers};
IsNew = profileConfiguration == null;
DisplayName = IsNew ? "Artemis | Add profile" : "Artemis | Edit profile";
@@ -142,7 +142,7 @@ namespace Artemis.UI.Screens.Sidebar
return;
if (!await _windowService.ShowConfirmContentDialog("Delete profile", "Are you sure you want to permanently delete this profile?"))
return;
_profileService.RemoveProfileConfiguration(_profileConfiguration);
Close(true);
}
@@ -210,9 +210,13 @@ namespace Artemis.UI.Screens.Sidebar
}
else if (_profileConfiguration.Icon.IconType == ProfileConfigurationIconType.SvgImage)
{
SvgSource newSource = new();
newSource.Load(_profileConfiguration.Icon.GetIconStream());
SelectedSvgSource = new SvgImage {Source = newSource};
Stream? iconStream = _profileConfiguration.Icon.GetIconStream();
if (iconStream != null)
{
SvgSource newSource = new();
newSource.Load(iconStream);
SelectedSvgSource = new SvgImage {Source = newSource};
}
}
// Prepare the contents of the dropdown box, it should be virtualized so no need to wait with this
@@ -231,7 +235,9 @@ namespace Artemis.UI.Screens.Sidebar
private async Task SaveIcon()
{
if (IconType == ProfileConfigurationIconType.MaterialIcon && SelectedMaterialIcon != null)
{
ProfileConfiguration.Icon.SetIconByName(SelectedMaterialIcon.Icon.ToString());
}
else if (_selectedIconPath != null)
{
await using FileStream fileStream = File.OpenRead(_selectedIconPath);
@@ -6,23 +6,24 @@ using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Services;
using Artemis.UI.Services.ProfileEditor;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.Interfaces;
using FluentAvalonia.UI.Controls;
using ReactiveUI;
namespace Artemis.UI.Screens.Sidebar
{
public class SidebarCategoryViewModel : ActivatableViewModelBase
{
private readonly SidebarViewModel _sidebarViewModel;
private readonly IProfileService _profileService;
private readonly IWindowService _windowService;
private readonly SidebarViewModel _sidebarViewModel;
private readonly ISidebarVmFactory _vmFactory;
private readonly IWindowService _windowService;
private SidebarProfileConfigurationViewModel? _selectedProfileConfiguration;
public SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory, IProfileService profileService, IWindowService windowService, IProfileEditorService profileEditorService, ISidebarVmFactory vmFactory)
public SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory, IProfileService profileService, IWindowService windowService,
IProfileEditorService profileEditorService, ISidebarVmFactory vmFactory)
{
_sidebarViewModel = sidebarViewModel;
_profileService = profileService;
@@ -86,7 +87,7 @@ namespace Artemis.UI.Screens.Sidebar
{
await _windowService.CreateContentDialog()
.WithTitle("Edit category")
.WithViewModel<SidebarCategoryEditViewModel>(out var vm, ("category", ProfileCategory))
.WithViewModel(out SidebarCategoryEditViewModel vm, ("category", ProfileCategory))
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.HavingSecondaryButton(b => b.WithText("Delete").WithCommand(vm.Delete))
.WithCloseButtonText("Cancel")
@@ -2,7 +2,6 @@
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;
@@ -12,11 +11,10 @@ using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.Workshop;
using Artemis.UI.Services;
using Artemis.UI.Services.ProfileEditor;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.Interfaces;
using FluentAvalonia.UI.Controls;
using Material.Icons;
using Ninject;
using ReactiveUI;
@@ -27,11 +25,11 @@ namespace Artemis.UI.Screens.Sidebar
public class SidebarViewModel : ActivatableViewModelBase
{
private readonly IScreen _hostScreen;
private readonly IProfileEditorService _profileEditorService;
private readonly IProfileService _profileService;
private readonly IRgbService _rgbService;
private readonly ISidebarVmFactory _sidebarVmFactory;
private readonly IWindowService _windowService;
private readonly IProfileEditorService _profileEditorService;
private ArtemisDevice? _headerDevice;
private SidebarScreenViewModel? _selectedSidebarScreen;
@@ -111,7 +109,7 @@ namespace Artemis.UI.Screens.Sidebar
{
await _windowService.CreateContentDialog()
.WithTitle("Add new category")
.WithViewModel<SidebarCategoryEditViewModel>(out var vm, ("category", null))
.WithViewModel(out SidebarCategoryEditViewModel vm, ("category", null))
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary)
@@ -35,6 +35,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
_settingsService = settingsService;
_deviceVmFactory = deviceVmFactory;
_windowService = windowService;
_cursor = Cursor.Default;
Device = device;