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:
commit
3158e4247a
@ -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" />
|
||||||
|
|||||||
@ -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" />
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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}" />
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
7
src/Artemis.UI/Screens/Root/BlankView.axaml
Normal file
7
src/Artemis.UI/Screens/Root/BlankView.axaml
Normal 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>
|
||||||
13
src/Artemis.UI/Screens/Root/BlankView.axaml.cs
Normal file
13
src/Artemis.UI/Screens/Root/BlankView.axaml.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/Artemis.UI/Screens/Root/BlankViewModel.cs
Normal file
9
src/Artemis.UI/Screens/Root/BlankViewModel.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Artemis.UI.Shared;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Root;
|
||||||
|
|
||||||
|
public class BlankViewModel : ViewModelBase, IMainScreenViewModel
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ViewModelBase? TitleBarViewModel => null;
|
||||||
|
}
|
||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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; }
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user