From 2529b728baa8487e1fed87b3b000e778ec7d2da1 Mon Sep 17 00:00:00 2001 From: AliveDevil Date: Sat, 10 Feb 2024 10:38:28 +0100 Subject: [PATCH] Factories and Views --- .../InkForgeServiceCollection.cs | 3 +++ .../DependencyInjection/TypeFactories.cs | 22 ++++++++++++++++++ .../ReactiveUI/RoutableReactiveObject.cs | 10 ++++++++ .../ReactiveUI/ViewModelFactory.cs | 23 +++++++++++++++++++ .../ViewModels/AppViewModel.cs | 4 ++-- .../Landing/CreateWorkspaceViewModel.cs | 14 +++++++++++ .../Landing/LandingViewModelBase.cs | 8 +++++++ .../Landing/LandingViewModelFactory.cs | 21 +++++++++++++++++ .../ViewModels/Landing/OpenRecentViewModel.cs | 12 ++++++++++ .../ViewModels/LandingViewModel.cs | 22 +++++++++++++++++- app/InkForge.Common/Views/LandingView.axaml | 5 ++++ .../LandingViews/CreateWorkspaceView.axaml | 10 ++++++++ .../LandingViews/CreateWorkspaceView.axaml.cs | 13 +++++++++++ .../Views/LandingViews/OpenRecentView.axaml | 10 ++++++++ .../LandingViews/OpenRecentView.axaml.cs | 13 +++++++++++ app/InkForge.Desktop/Views/MainWindow.axaml | 2 +- 16 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 app/InkForge.Common/Microsoft/Extensions/DependencyInjection/TypeFactories.cs create mode 100644 app/InkForge.Common/ReactiveUI/RoutableReactiveObject.cs create mode 100644 app/InkForge.Common/ReactiveUI/ViewModelFactory.cs create mode 100644 app/InkForge.Common/ViewModels/Landing/CreateWorkspaceViewModel.cs create mode 100644 app/InkForge.Common/ViewModels/Landing/LandingViewModelBase.cs create mode 100644 app/InkForge.Common/ViewModels/Landing/LandingViewModelFactory.cs create mode 100644 app/InkForge.Common/ViewModels/Landing/OpenRecentViewModel.cs create mode 100644 app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml create mode 100644 app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml.cs create mode 100644 app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml create mode 100644 app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml.cs diff --git a/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs b/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs index 918eddf..625ddd5 100644 --- a/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs +++ b/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs @@ -1,6 +1,7 @@ using InkForge.Common.Controllers; using InkForge.Common.Data; using InkForge.Common.ViewModels; +using InkForge.Common.ViewModels.Landing; using InkForge.Common.Views; using InkForge.Data; @@ -16,6 +17,8 @@ public static class InkForgeServiceCollections services.AddDbContextFactory(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddTransient, LandingView>(); diff --git a/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/TypeFactories.cs b/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/TypeFactories.cs new file mode 100644 index 0000000..6b57189 --- /dev/null +++ b/app/InkForge.Common/Microsoft/Extensions/DependencyInjection/TypeFactories.cs @@ -0,0 +1,22 @@ +namespace Microsoft.Extensions.DependencyInjection +{ + public static class TypeFactory + where TFactory : struct, IObjectParameters + { + private static ObjectFactory? s_objectFactory; + + public static T Create(in TFactory factory, IServiceProvider serviceProvider) + { + s_objectFactory ??= ActivatorUtilities.CreateFactory(TFactory.Types); + return s_objectFactory(serviceProvider, (object[])factory); + } + } + + public interface IObjectParameters + where T : struct, IObjectParameters + { + abstract static Type[] Types { get; } + + abstract static implicit operator object[](in T self); + } +} diff --git a/app/InkForge.Common/ReactiveUI/RoutableReactiveObject.cs b/app/InkForge.Common/ReactiveUI/RoutableReactiveObject.cs new file mode 100644 index 0000000..3b1e147 --- /dev/null +++ b/app/InkForge.Common/ReactiveUI/RoutableReactiveObject.cs @@ -0,0 +1,10 @@ +using ReactiveUI; + +namespace InkForge.Common.ReactiveUI; + +public abstract class RoutableReactiveObject(IScreen screen) : ReactiveObject, IRoutableViewModel +{ + public abstract string? UrlPathSegment { get; } + + public IScreen HostScreen => screen; +} diff --git a/app/InkForge.Common/ReactiveUI/ViewModelFactory.cs b/app/InkForge.Common/ReactiveUI/ViewModelFactory.cs new file mode 100644 index 0000000..bcbc69c --- /dev/null +++ b/app/InkForge.Common/ReactiveUI/ViewModelFactory.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace InkForge.Common.ReactiveUI; + +public interface IViewModelFactory +{ + abstract static ObjectFactory CreateObjectFactory(); + + abstract static TCreator GetCreator(ObjectFactory factory, IServiceProvider serviceProvider); +} + +public class ViewModelFactory + where TFactory : IViewModelFactory + where TCreator : Delegate +{ + private static ObjectFactory? s_factory; + + public TCreator CreateFactory(IServiceProvider serviceProvider) + { + s_factory ??= TFactory.CreateObjectFactory(); + return TFactory.GetCreator(s_factory, serviceProvider); + } +} diff --git a/app/InkForge.Common/ViewModels/AppViewModel.cs b/app/InkForge.Common/ViewModels/AppViewModel.cs index 0447cb3..05adbf7 100644 --- a/app/InkForge.Common/ViewModels/AppViewModel.cs +++ b/app/InkForge.Common/ViewModels/AppViewModel.cs @@ -14,8 +14,8 @@ public class AppViewModel : ReactiveObject set => this.RaiseAndSetIfChanged(ref _view, value); } - public AppViewModel(WorkspaceController workspace) + public AppViewModel(WorkspaceController workspace, LandingViewModel landingViewModel) { - View = new LandingViewModel(); + View = landingViewModel; } } diff --git a/app/InkForge.Common/ViewModels/Landing/CreateWorkspaceViewModel.cs b/app/InkForge.Common/ViewModels/Landing/CreateWorkspaceViewModel.cs new file mode 100644 index 0000000..e3165cd --- /dev/null +++ b/app/InkForge.Common/ViewModels/Landing/CreateWorkspaceViewModel.cs @@ -0,0 +1,14 @@ +using InkForge.Common.ReactiveUI; + +using ReactiveUI; + +namespace InkForge.Common.ViewModels.Landing; + +public class CreateWorkspaceViewModel : LandingViewModelBase +{ + public override string? UrlPathSegment => null; + + public CreateWorkspaceViewModel(LandingViewModel landing) : base(landing) + { + } +} diff --git a/app/InkForge.Common/ViewModels/Landing/LandingViewModelBase.cs b/app/InkForge.Common/ViewModels/Landing/LandingViewModelBase.cs new file mode 100644 index 0000000..4d5fff3 --- /dev/null +++ b/app/InkForge.Common/ViewModels/Landing/LandingViewModelBase.cs @@ -0,0 +1,8 @@ +using InkForge.Common.ReactiveUI; + +namespace InkForge.Common.ViewModels.Landing; + +public abstract class LandingViewModelBase(LandingViewModel landing) : RoutableReactiveObject(landing) +{ + public LandingViewModel Landing => landing; +} diff --git a/app/InkForge.Common/ViewModels/Landing/LandingViewModelFactory.cs b/app/InkForge.Common/ViewModels/Landing/LandingViewModelFactory.cs new file mode 100644 index 0000000..e6b0866 --- /dev/null +++ b/app/InkForge.Common/ViewModels/Landing/LandingViewModelFactory.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace InkForge.Common.ViewModels.Landing; + +public class LandingViewModelFactory(IServiceProvider serviceProvider) +{ + public T Create(LandingViewModel landing) where T : LandingViewModelBase + { + LandingViewModelsObjectParameters objectParameters = new(landing); + return TypeFactory.Create(objectParameters, serviceProvider); + } + + readonly record struct LandingViewModelsObjectParameters( + LandingViewModel Landing + ) : IObjectParameters + { + public static Type[] Types => [typeof(LandingViewModel)]; + + public static implicit operator object[](in LandingViewModelsObjectParameters self) => [self.Landing]; + } +} diff --git a/app/InkForge.Common/ViewModels/Landing/OpenRecentViewModel.cs b/app/InkForge.Common/ViewModels/Landing/OpenRecentViewModel.cs new file mode 100644 index 0000000..39cc0bd --- /dev/null +++ b/app/InkForge.Common/ViewModels/Landing/OpenRecentViewModel.cs @@ -0,0 +1,12 @@ +using InkForge.Common.ReactiveUI; + +namespace InkForge.Common.ViewModels.Landing; + +public class OpenRecentViewModel : LandingViewModelBase +{ + public override string? UrlPathSegment => null; + + public OpenRecentViewModel(LandingViewModel landing) : base(landing) + { + } +} diff --git a/app/InkForge.Common/ViewModels/LandingViewModel.cs b/app/InkForge.Common/ViewModels/LandingViewModel.cs index 0d4de9a..ca40237 100644 --- a/app/InkForge.Common/ViewModels/LandingViewModel.cs +++ b/app/InkForge.Common/ViewModels/LandingViewModel.cs @@ -1,8 +1,28 @@ +using System.Reactive.Linq; + +using InkForge.Common.ViewModels.Landing; + +using Microsoft.Extensions.DependencyInjection; + using ReactiveUI; namespace InkForge.Common.ViewModels; -public class LandingViewModel : ReactiveObject +public class LandingViewModel : ReactiveObject, IScreen { + private readonly LandingViewModelFactory _factory; + public RoutingState Router { get; } = new(); + + public LandingViewModel(LandingViewModelFactory factory) + { + _factory = factory; + + Router.CurrentViewModel.Where(x => x is null) + .InvokeCommand(Router.NavigateAndReset); + } + + public void Navigate() where T : LandingViewModelBase + { + } } diff --git a/app/InkForge.Common/Views/LandingView.axaml b/app/InkForge.Common/Views/LandingView.axaml index 1750269..cae67b1 100644 --- a/app/InkForge.Common/Views/LandingView.axaml +++ b/app/InkForge.Common/Views/LandingView.axaml @@ -2,6 +2,7 @@ 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" + xmlns:reactiveui="http://reactiveui.net" xmlns:ifcvm="using:InkForge.Common.ViewModels" mc:Ignorable="d" d:DesignWidth="800" @@ -9,5 +10,9 @@ x:Class="InkForge.Common.Views.LandingView" x:DataType="ifcvm:LandingViewModel"> + \ No newline at end of file diff --git a/app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml b/app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml new file mode 100644 index 0000000..ce77e44 --- /dev/null +++ b/app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml @@ -0,0 +1,10 @@ + + Welcome to Avalonia! + \ No newline at end of file diff --git a/app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml.cs b/app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml.cs new file mode 100644 index 0000000..f9621f2 --- /dev/null +++ b/app/InkForge.Common/Views/LandingViews/CreateWorkspaceView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.ReactiveUI; + +using InkForge.Common.ViewModels.Landing; + +namespace InkForge.Common.Views.LandingViews; + +public partial class CreateWorkspaceView : ReactiveUserControl +{ + public CreateWorkspaceView() + { + InitializeComponent(); + } +} diff --git a/app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml b/app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml new file mode 100644 index 0000000..f4d7390 --- /dev/null +++ b/app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml @@ -0,0 +1,10 @@ + + Welcome to Avalonia! + \ No newline at end of file diff --git a/app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml.cs b/app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml.cs new file mode 100644 index 0000000..417e757 --- /dev/null +++ b/app/InkForge.Common/Views/LandingViews/OpenRecentView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.ReactiveUI; + +using InkForge.Common.ViewModels.Landing; + +namespace InkForge.Common.Views.LandingViews; + +public partial class OpenRecentView : ReactiveUserControl +{ + public OpenRecentView() + { + InitializeComponent(); + } +} diff --git a/app/InkForge.Desktop/Views/MainWindow.axaml b/app/InkForge.Desktop/Views/MainWindow.axaml index 0acd472..4312524 100644 --- a/app/InkForge.Desktop/Views/MainWindow.axaml +++ b/app/InkForge.Desktop/Views/MainWindow.axaml @@ -2,8 +2,8 @@ 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" - xmlns:ifcvm="using:InkForge.Common.ViewModels" xmlns:reactiveui="http://reactiveui.net" + xmlns:ifcvm="using:InkForge.Common.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"