1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Merge branch 'development' into feature/workshop

This commit is contained in:
Robert 2023-08-26 22:39:08 +02:00
commit 3158e4247a
14 changed files with 104 additions and 20 deletions

View File

@ -14,7 +14,7 @@
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>
<TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}" CommandParameter="Home"> <TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}">
<TrayIcon.Menu> <TrayIcon.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="home" /> <NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="home" />

View File

@ -14,7 +14,7 @@
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>
<TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}" CommandParameter="Home"> <TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}">
<TrayIcon.Menu> <TrayIcon.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="home" /> <NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="home" />

View File

@ -59,4 +59,9 @@ public interface IRouter
/// <typeparam name="TScreen">The type of the root screen. It must be a class.</typeparam> /// <typeparam name="TScreen">The type of the root screen. It must be a class.</typeparam>
/// <typeparam name="TParam">The type of the parameters for the root screen. It must have a parameterless constructor.</typeparam> /// <typeparam name="TParam">The type of the parameters for the root screen. It must have a parameterless constructor.</typeparam>
void SetRoot<TScreen, TParam>(RoutableScreen<TScreen, TParam> root) where TScreen : class where TParam : new(); void SetRoot<TScreen, TParam>(RoutableScreen<TScreen, TParam> root) where TScreen : class where TParam : new();
/// <summary>
/// Clears the route used by the previous window, so that it is not restored when the main window opens.
/// </summary>
void ClearPreviousWindowRoute();
} }

View File

@ -35,11 +35,12 @@ internal class Navigation
public async Task Navigate(NavigationArguments args) public async Task Navigate(NavigationArguments args)
{ {
_logger.Information("Navigating to {Path}", _resolution.Path); if (_options.EnableLogging)
_logger.Information("Navigating to {Path}", _resolution.Path);
_cts = new CancellationTokenSource(); _cts = new CancellationTokenSource();
await NavigateResolution(_resolution, args, _root); await NavigateResolution(_resolution, args, _root);
if (!Cancelled) if (!Cancelled && _options.EnableLogging)
_logger.Information("Navigated to {Path}", _resolution.Path); _logger.Information("Navigated to {Path}", _resolution.Path);
} }
@ -48,7 +49,8 @@ internal class Navigation
if (Cancelled || Completed) if (Cancelled || Completed)
return; return;
_logger.Information("Cancelled navigation to {Path}", _resolution.Path); if (_options.EnableLogging)
_logger.Information("Cancelled navigation to {Path}", _resolution.Path);
_cts.Cancel(); _cts.Cancel();
} }

View File

@ -3,27 +3,34 @@ using System.Collections.Generic;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared.Services.MainWindow;
using Avalonia.Threading; using Avalonia.Threading;
using Serilog; using Serilog;
namespace Artemis.UI.Shared.Routing; namespace Artemis.UI.Shared.Routing;
internal class Router : CorePropertyChanged, IRouter internal class Router : CorePropertyChanged, IRouter, IDisposable
{ {
private readonly Stack<string> _backStack = new(); private readonly Stack<string> _backStack = new();
private readonly BehaviorSubject<string?> _currentRouteSubject; private readonly BehaviorSubject<string?> _currentRouteSubject;
private readonly Stack<string> _forwardStack = new(); private readonly Stack<string> _forwardStack = new();
private readonly Func<IRoutableScreen, RouteResolution, RouterNavigationOptions, Navigation> _getNavigation; private readonly Func<IRoutableScreen, RouteResolution, RouterNavigationOptions, Navigation> _getNavigation;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IMainWindowService _mainWindowService;
private Navigation? _currentNavigation; private Navigation? _currentNavigation;
private IRoutableScreen? _root; private IRoutableScreen? _root;
private string? _previousWindowRoute;
public Router(ILogger logger, Func<IRoutableScreen, RouteResolution, RouterNavigationOptions, Navigation> getNavigation) public Router(ILogger logger, IMainWindowService mainWindowService, Func<IRoutableScreen, RouteResolution, RouterNavigationOptions, Navigation> getNavigation)
{ {
_logger = logger; _logger = logger;
_mainWindowService = mainWindowService;
_getNavigation = getNavigation; _getNavigation = getNavigation;
_currentRouteSubject = new BehaviorSubject<string?>(null); _currentRouteSubject = new BehaviorSubject<string?>(null);
mainWindowService.MainWindowOpened += MainWindowServiceOnMainWindowOpened;
mainWindowService.MainWindowClosed += MainWindowServiceOnMainWindowClosed;
} }
private RouteResolution Resolve(string path) private RouteResolution Resolve(string path)
@ -128,7 +135,7 @@ internal class Router : CorePropertyChanged, IRouter
await Navigate(path, new RouterNavigationOptions {AddToHistory = false}); await Navigate(path, new RouterNavigationOptions {AddToHistory = false});
if (previousPath != null) if (previousPath != null)
_forwardStack.Push(previousPath); _forwardStack.Push(previousPath);
return true; return true;
} }
@ -142,7 +149,7 @@ internal class Router : CorePropertyChanged, IRouter
await Navigate(path, new RouterNavigationOptions {AddToHistory = false}); await Navigate(path, new RouterNavigationOptions {AddToHistory = false});
if (previousPath != null) if (previousPath != null)
_backStack.Push(previousPath); _backStack.Push(previousPath);
return true; return true;
} }
@ -164,4 +171,29 @@ internal class Router : CorePropertyChanged, IRouter
{ {
_root = root; _root = root;
} }
/// <inheritdoc />
public void ClearPreviousWindowRoute()
{
_previousWindowRoute = null;
}
public void Dispose()
{
_currentRouteSubject.Dispose();
_mainWindowService.MainWindowOpened -= MainWindowServiceOnMainWindowOpened;
_mainWindowService.MainWindowClosed -= MainWindowServiceOnMainWindowClosed;
}
private void MainWindowServiceOnMainWindowOpened(object? sender, EventArgs e)
{
if (_previousWindowRoute != null && _currentRouteSubject.Value == "blank")
Dispatcher.UIThread.InvokeAsync(async () => await Navigate(_previousWindowRoute, new RouterNavigationOptions {AddToHistory = false, EnableLogging = false}));
}
private void MainWindowServiceOnMainWindowClosed(object? sender, EventArgs e)
{
_previousWindowRoute = _currentRouteSubject.Value;
Dispatcher.UIThread.InvokeAsync(async () => await Navigate("blank", new RouterNavigationOptions {AddToHistory = false, EnableLogging = false}));
}
} }

View File

@ -20,4 +20,10 @@ public class RouterNavigationOptions
/// </summary> /// </summary>
/// <example>If set to true, a route change from <c>page/subpage1/subpage2</c> to <c>page/subpage1</c> will be ignored.</example> /// <example>If set to true, a route change from <c>page/subpage1/subpage2</c> to <c>page/subpage1</c> will be ignored.</example>
public bool IgnoreOnPartialMatch { get; set; } = false; public bool IgnoreOnPartialMatch { get; set; } = false;
/// <summary>
/// Gets or sets a boolean value indicating whether logging should be enabled.
/// <remarks>Errors and warnings are always logged.</remarks>
/// </summary>
public bool EnableLogging { get; set; } = true;
} }

View File

@ -14,13 +14,13 @@
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>
<TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}" CommandParameter="Home"> <TrayIcon Icon="avares://Artemis.UI/Assets/Images/Logo/application.ico" ToolTipText="Artemis" Command="{CompiledBinding OpenScreen}">
<TrayIcon.Menu> <TrayIcon.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="home" /> <NativeMenuItem Header="Home" Command="{CompiledBinding OpenScreen}" CommandParameter="home" />
<!-- <NativeMenuItem Header="Workshop" Command="{CompiledBinding OpenScreen}" CommandParameter="workshop" /> --> <!-- <NativeMenuItem Header="Workshop" Command="{CompiledBinding OpenScreen}" CommandParameter="workshop" /> -->
<NativeMenuItem Header="Surface Editor" Command="{CompiledBinding OpenScreen}" CommandParameter="surface-editor" /> <NativeMenuItem Header="Surface Editor" Command="{CompiledBinding OpenScreen}" CommandParameter="surface-editor" />
<NativeMenuItem Header="Settings" Command="{CompiledBinding OpenScreen}" CommandParameter="settings/releases" /> <NativeMenuItem Header="Settings" Command="{CompiledBinding OpenScreen}" CommandParameter="settings" />
<NativeMenuItemSeparator /> <NativeMenuItemSeparator />
<NativeMenuItem Header="Debugger" Command="{CompiledBinding OpenDebugger}" /> <NativeMenuItem Header="Debugger" Command="{CompiledBinding OpenDebugger}" />
<NativeMenuItem Header="Exit" Command="{CompiledBinding Exit}" /> <NativeMenuItem Header="Exit" Command="{CompiledBinding Exit}" />

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Artemis.UI.Screens.Home; using Artemis.UI.Screens.Home;
using Artemis.UI.Screens.ProfileEditor; using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.Root;
using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.Settings.Updating; using Artemis.UI.Screens.Settings.Updating;
using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.SurfaceEditor;
@ -16,6 +17,7 @@ public static class Routes
{ {
public static List<IRouterRegistration> ArtemisRoutes = new() public static List<IRouterRegistration> ArtemisRoutes = new()
{ {
new RouteRegistration<BlankViewModel>("blank"),
new RouteRegistration<HomeViewModel>("home"), new RouteRegistration<HomeViewModel>("home"),
#if DEBUG #if DEBUG
new RouteRegistration<WorkshopViewModel>("workshop") new RouteRegistration<WorkshopViewModel>("workshop")

View File

@ -0,0 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Root.BlankView">
</UserControl>

View File

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Root;
public partial class BlankView : UserControl
{
public BlankView()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,9 @@
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Root;
public class BlankViewModel : ViewModelBase, IMainScreenViewModel
{
/// <inheritdoc />
public ViewModelBase? TitleBarViewModel => null;
}

View File

@ -58,7 +58,7 @@ public class RootViewModel : RoutableScreen<IMainScreenViewModel>, IMainWindowPr
mainWindowService.ConfigureMainWindowProvider(this); mainWindowService.ConfigureMainWindowProvider(this);
DisplayAccordingToSettings(); DisplayAccordingToSettings();
OpenScreen = ReactiveCommand.Create<string>(ExecuteOpenScreen); OpenScreen = ReactiveCommand.Create<string?>(ExecuteOpenScreen);
OpenDebugger = ReactiveCommand.CreateFromTask(ExecuteOpenDebugger); OpenDebugger = ReactiveCommand.CreateFromTask(ExecuteOpenDebugger);
Exit = ReactiveCommand.CreateFromTask(ExecuteExit); Exit = ReactiveCommand.CreateFromTask(ExecuteExit);
this.WhenAnyValue(vm => vm.Screen).Subscribe(UpdateTitleBarViewModel); this.WhenAnyValue(vm => vm.Screen).Subscribe(UpdateTitleBarViewModel);
@ -72,11 +72,13 @@ public class RootViewModel : RoutableScreen<IMainScreenViewModel>, IMainWindowPr
registrationService.RegisterBuiltInDataModelDisplays(); registrationService.RegisterBuiltInDataModelDisplays();
registrationService.RegisterBuiltInDataModelInputs(); registrationService.RegisterBuiltInDataModelInputs();
registrationService.RegisterBuiltInPropertyEditors(); registrationService.RegisterBuiltInPropertyEditors();
_router.Navigate("home");
}); });
} }
public SidebarViewModel SidebarViewModel { get; } public SidebarViewModel SidebarViewModel { get; }
public ReactiveCommand<string, Unit> OpenScreen { get; } public ReactiveCommand<string?, Unit> OpenScreen { get; }
public ReactiveCommand<Unit, Unit> OpenDebugger { get; } public ReactiveCommand<Unit, Unit> OpenDebugger { get; }
public ReactiveCommand<Unit, Unit> Exit { get; } public ReactiveCommand<Unit, Unit> Exit { get; }
@ -133,8 +135,11 @@ public class RootViewModel : RoutableScreen<IMainScreenViewModel>, IMainWindowPr
#region Tray commands #region Tray commands
private void ExecuteOpenScreen(string path) private void ExecuteOpenScreen(string? path)
{ {
if (path != null)
_router.ClearPreviousWindowRoute();
// The window will open on the UI thread at some point, respond to that to select the chosen screen // The window will open on the UI thread at some point, respond to that to select the chosen screen
MainWindowOpened += OnEventHandler; MainWindowOpened += OnEventHandler;
OpenMainWindow(); OpenMainWindow();
@ -143,7 +148,8 @@ public class RootViewModel : RoutableScreen<IMainScreenViewModel>, IMainWindowPr
{ {
MainWindowOpened -= OnEventHandler; MainWindowOpened -= OnEventHandler;
// Avoid threading issues by running this on the UI thread // Avoid threading issues by running this on the UI thread
Dispatcher.UIThread.InvokeAsync(async () => await _router.Navigate(path)); if (path != null)
Dispatcher.UIThread.InvokeAsync(async () => await _router.Navigate(path));
} }
} }

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
@ -88,7 +86,6 @@ public class SidebarViewModel : ActivatableViewModelBase
SidebarCategories = categoryViewModels; SidebarCategories = categoryViewModels;
}); });
SelectedScreen = SidebarScreen.Screens.First();
} }
public SidebarScreenViewModel SidebarScreen { get; } public SidebarScreenViewModel SidebarScreen { get; }

View File

@ -2,6 +2,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Screens.Root;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
@ -11,9 +12,13 @@ namespace Artemis.UI;
public class ViewLocator : IDataTemplate public class ViewLocator : IDataTemplate
{ {
public Control Build(object data) public Control Build(object? data)
{ {
if (data == null)
return new TextBlock {Text = "No data provided"};
Type dataType = data.GetType(); Type dataType = data.GetType();
string name = dataType.FullName!.Split('`')[0].Replace("ViewModel", "View"); string name = dataType.FullName!.Split('`')[0].Replace("ViewModel", "View");
Type? type = dataType.Assembly.GetType(name); Type? type = dataType.Assembly.GetType(name);
@ -27,7 +32,7 @@ public class ViewLocator : IDataTemplate
return new TextBlock {Text = "Not Found: " + name}; return new TextBlock {Text = "Not Found: " + name};
} }
public bool Match(object data) public bool Match(object? data)
{ {
return data is ReactiveObject; return data is ReactiveObject;
} }