diff --git a/Directory.Packages.props b/Directory.Packages.props index 1650f75..54ac008 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -2,35 +2,29 @@ true true - 11.0.9 - 8.0.2 - - - - - - - - - - - + + + + + + + + - - - - - - + + + + + + - + - diff --git a/app/InkForge.Desktop/App.axaml.cs b/app/InkForge.Desktop/App.axaml.cs index 487453e..e38d88e 100644 --- a/app/InkForge.Desktop/App.axaml.cs +++ b/app/InkForge.Desktop/App.axaml.cs @@ -1,11 +1,12 @@ using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Controls.Templates; using Avalonia.Markup.Xaml; -using Avalonia.Metadata; -using InkForge.Desktop.Views; +using DynamicData; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using ReactiveUI; @@ -16,15 +17,34 @@ namespace InkForge.Desktop; public partial class App : Application { + public static readonly StyledProperty AppDataTemplateProperty + = AvaloniaProperty.Register( + name: nameof(AppDataTemplate), + coerce: OnAppDataTemplateChanged); public static readonly StyledProperty ServiceProviderProperty = AvaloniaProperty.Register( name: nameof(ServiceProvider), coerce: OnServiceProviderChanged); + public IDataTemplate AppDataTemplate => GetValue(AppDataTemplateProperty); + public IServiceProvider ServiceProvider => GetValue(ServiceProviderProperty); - public static void Configure(IServiceCollection services) + public static void Configure(IServiceCollection services, ConfigurationManager configuration) { + configuration.SetBasePath(AppContext.BaseDirectory); + configuration.AddJsonFile( + new ManifestEmbeddedFileProvider(typeof(App).Assembly), + "Properties/appsettings.json", false, false); + configuration.AddJsonFile( + Path.Combine( + Environment.GetFolderPath( + Environment.SpecialFolder.ApplicationData, + Environment.SpecialFolderOption.DoNotVerify), + "InkForge", + "usersettings.json"), true, true); + configuration.AddJsonFile("appsettings.json", true, true); + services.UseMicrosoftDependencyResolver(); Locator.CurrentMutable.InitializeSplat(); Locator.CurrentMutable.InitializeReactiveUI(); @@ -37,18 +57,34 @@ public partial class App : Application AvaloniaXamlLoader.Load(this); } - public override void OnFrameworkInitializationCompleted() + private static IDataTemplate OnAppDataTemplateChanged(AvaloniaObject @object, IDataTemplate dataTemplate) { - _ = ApplicationLifetime switch + var host = (IDataTemplateHost)@object; + var original = @object.GetValue(AppDataTemplateProperty); + + if (original is null && dataTemplate is not null) { - IClassicDesktopStyleApplicationLifetime desktop => desktop.MainWindow = new MainWindow(), - _ => throw new NotSupportedException(), - }; + host.DataTemplates.Add(dataTemplate); + } + else if (original is not null) + { + if (dataTemplate is null) + { + host.DataTemplates.Remove(original); + } + else + { + host.DataTemplates.ReplaceOrAdd(original, dataTemplate); + } + } + + return dataTemplate!; } private static IServiceProvider OnServiceProviderChanged(AvaloniaObject @object, IServiceProvider provider) { provider.UseMicrosoftDependencyResolver(); + @object.SetValue(AppDataTemplateProperty, provider.GetRequiredService()); return provider; } } diff --git a/app/InkForge.Desktop/AppViewLocator.cs b/app/InkForge.Desktop/AppViewLocator.cs new file mode 100644 index 0000000..6fd1ce3 --- /dev/null +++ b/app/InkForge.Desktop/AppViewLocator.cs @@ -0,0 +1,44 @@ +using Avalonia.Controls; +using Avalonia.Controls.Templates; + +using InkForge.Desktop.ViewModels; +using InkForge.Desktop.ViewModels.Documents; +using InkForge.Desktop.ViewModels.Workspaces; +using InkForge.Desktop.Views.Documents; +using InkForge.Desktop.Views.Workspaces; + +using ReactiveUI; + +namespace InkForge.Desktop; + +public class AppViewLocator : IDataTemplate +{ + public Control? Build(object? param) + { +#pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + return param switch +#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + { + ViewModels.Tools.WorkspaceTool viewModel => _(new Views.Tools.WorkspaceTool(), viewModel), + WelcomePageDocumentViewModel viewModel => _(new WelcomePageDocument(), viewModel), + WorkspaceViewModel viewModel => _(new WorkspaceView(), viewModel), + }; + + static TView _(TView view, TViewModel viewModel) + where TViewModel : class + where TView : IViewFor + { + view.ViewModel = viewModel; + return view; + } + } + + public bool Match(object? data) + { + return data is + RecentItemViewModel or + ViewModels.Tools.WorkspaceTool or + WelcomePageDocumentViewModel or + WorkspaceViewModel; + } +} diff --git a/app/InkForge.Desktop/Dock/WorkspaceFactory.cs b/app/InkForge.Desktop/Dock/WorkspaceFactory.cs deleted file mode 100644 index c539f13..0000000 --- a/app/InkForge.Desktop/Dock/WorkspaceFactory.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Dock.Model.Controls; -using Dock.Model.Core; -using Dock.Model.ReactiveUI; - -namespace InkForge.Desktop.Dock; - -public class WorkspaceFactory : Factory -{ - public override IRootDock CreateLayout() - { - var documents = CreateDocumentDock(); - documents.Id = "Documents"; - documents.Title = "Documents"; - - var root = CreateRootDock(); - - root.VisibleDockables = [documents]; - root.ActiveDockable = documents; - root.DefaultDockable = documents; - - DockableLocator = new Dictionary> - { - ["Root"] = () => root, - ["Documents"] = () => documents, - }; - - return root; - } -} diff --git a/app/InkForge.Desktop/InkForgeFactory.cs b/app/InkForge.Desktop/InkForgeFactory.cs new file mode 100644 index 0000000..4511489 --- /dev/null +++ b/app/InkForge.Desktop/InkForgeFactory.cs @@ -0,0 +1,90 @@ +using Dock.Model.ReactiveUI; +using Dock.Model.Controls; +using Dock.Model.Core; +using Dock.Model.ReactiveUI.Controls; +using Dock.Avalonia.Controls; +using Microsoft.Extensions.DependencyInjection; +using Avalonia; +using InkForge.Desktop.ViewModels; + +namespace InkForge.Desktop; + +public class InkForgeFactory : Factory +{ + private readonly IDocumentDock _documentDock; + private readonly IRootDock _rootDock; + private readonly ViewModels.Tools.WorkspaceTool _workspaceTool; + + public InkForgeFactory() + { + _rootDock = new RootDock + { + IsCollapsable = false, + }; + + _documentDock = new InkForgeDocumentDock + { + Id = "Documents", + Title = "Documents", + CanCreateDocument = false, + IsCollapsable = false, + Proportion = double.NaN, + }; + + _workspaceTool = CreateWorkspaceTool(); + } + + public override IRootDock CreateLayout() + { + ProportionalDock workspaceLayout = new() + { + Proportion = 0.3, + VisibleDockables = [_workspaceTool], + }; + + ProportionalDock windowLayoutContent = new() + { + Orientation = Orientation.Horizontal, + IsCollapsable = false, + VisibleDockables = [workspaceLayout, new ProportionalDockSplitter(), _documentDock] + }; + + RootDock windowLayout = new() + { + Title = "Default", + IsCollapsable = false, + VisibleDockables = [windowLayoutContent], + ActiveDockable = windowLayoutContent, + }; + + _rootDock.VisibleDockables = [windowLayout]; + _rootDock.ActiveDockable = windowLayout; + _rootDock.DefaultDockable = windowLayout; + + return _rootDock; + } + + public override void InitLayout(IDockable layout) + { + DockableLocator = new Dictionary> + { + ["Root"] = () => _rootDock, + ["Documents"] = () => _documentDock, + ["Workspace"] = () => _workspaceTool, + }; + + HostWindowLocator = new Dictionary> + { + [nameof(IDockWindow)] = () => new HostWindow() + }; + + base.InitLayout(layout); + } + + private static ViewModels.Tools.WorkspaceTool CreateWorkspaceTool() + { + return ActivatorUtilities.CreateInstance( + Application.Current!.GetValue(App.ServiceProviderProperty) + ); + } +} diff --git a/app/InkForge.Desktop/Managers/DocumentManager.cs b/app/InkForge.Desktop/Managers/DocumentManager.cs index 9e71ade..7aff6e9 100644 --- a/app/InkForge.Desktop/Managers/DocumentManager.cs +++ b/app/InkForge.Desktop/Managers/DocumentManager.cs @@ -1,11 +1,48 @@ +using Avalonia; + +using Dock.Model.Core; + +using InkForge.Desktop.Models; +using InkForge.Desktop.ViewModels.Documents; + +using Microsoft.Extensions.DependencyInjection; + +using ReactiveUI; + namespace InkForge.Desktop.Managers; public class DocumentManager { + private readonly IDock _documents; + private readonly InkForgeFactory _factory; + private readonly WelcomePageDocumentViewModel _welcomePage; private readonly WorkspaceManager _workspaceManager; - public DocumentManager(WorkspaceManager workspaceManager) + public DocumentManager(WorkspaceManager workspaceManager, InkForgeFactory factory) { _workspaceManager = workspaceManager; + _factory = factory; + _documents = factory.GetDockable("Documents")!; + _welcomePage = CreateWelcomePageDocumentViewModel(); + workspaceManager.WhenAnyValue(v => v.Workspace).Subscribe(OnWorkspaceChanged); + } + + private void OnWorkspaceChanged(Workspace? workspace) + { + if (workspace is null) + { + _factory.AddDockable(_documents, _welcomePage); + } + else + { + _factory.RemoveDockable(_welcomePage, false); + } + } + + private static WelcomePageDocumentViewModel CreateWelcomePageDocumentViewModel() + { + return ActivatorUtilities.CreateInstance( + Application.Current!.GetValue(App.ServiceProviderProperty) + ); } } diff --git a/app/InkForge.Desktop/Managers/WorkspaceManager.cs b/app/InkForge.Desktop/Managers/WorkspaceManager.cs index 33350e4..90b9261 100644 --- a/app/InkForge.Desktop/Managers/WorkspaceManager.cs +++ b/app/InkForge.Desktop/Managers/WorkspaceManager.cs @@ -20,14 +20,14 @@ public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject private set => this.RaiseAndSetIfChanged(ref _workspace, value); } - public Task CloseWorkspace() + public ValueTask CloseWorkspace() { _workspace?.Dispose(); Workspace = null; - return Task.CompletedTask; + return ValueTask.CompletedTask; } - public async Task OpenWorkspace(string path, bool createFile = false) + public async ValueTask OpenWorkspace(string path, bool createFile = false) { await CloseWorkspace().ConfigureAwait(false); if (await CreateLocalWorkspace(path, createFile).ConfigureAwait(false) is { } workspace) diff --git a/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs b/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs index fa16721..31f3313 100644 --- a/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs +++ b/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/InkForgeServiceCollection.cs @@ -1,17 +1,18 @@ +using Avalonia.Controls.Templates; + +using Dock.Model.Core; + using InkForge.Data; +using InkForge.Desktop; using InkForge.Desktop.Data; using InkForge.Desktop.Data.Options; -using InkForge.Desktop.Dock; using InkForge.Desktop.Managers; using InkForge.Desktop.Models; -using InkForge.Desktop.ViewModels; +using InkForge.Desktop.ViewModels.Workspaces; +using InkForge.Desktop.ViewModels.Workspaces.Internal; using Microsoft.EntityFrameworkCore; -using ReactiveUI; - -using Splat; - namespace Microsoft.Extensions.DependencyInjection; public static class InkForgeServiceCollections @@ -23,9 +24,12 @@ public static class InkForgeServiceCollections // Singletons // - Concrete services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + + // - Service + services.AddSingleton(); + services.AddSingleton(); // Scoped // - Concrete @@ -38,8 +42,6 @@ public static class InkForgeServiceCollections // - Forwarders services.AddScoped(s => s.GetRequiredService().Workspace!); - Locator.CurrentMutable.RegisterViewsForViewModels(typeof(InkForgeServiceCollections).Assembly); - return services; } } diff --git a/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/TypeFactories.cs b/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/TypeFactories.cs deleted file mode 100644 index cb40b84..0000000 --- a/app/InkForge.Desktop/Microsoft/Extensions/DependencyInjection/TypeFactories.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection -{ - public static class TypeFactory - { - public static T Create(IServiceProvider serviceProvider) - { - return TypeFactory.Create(serviceProvider, default); - } - } - - public static class TypeFactory - where TArguments : IFactoryArguments - { - private static ObjectFactory? s_objectFactory; - - public static T Create(IServiceProvider serviceProvider, in TArguments factory) - { - s_objectFactory ??= ActivatorUtilities.CreateFactory(TArguments.Types); - return s_objectFactory(serviceProvider, (object[])factory); - } - } - - public readonly struct EmptyArguments : IFactoryArguments - { - public static Type[] Types => []; - - public static implicit operator object[](in EmptyArguments _) => []; - } - - public interface IFactoryArguments - where T : IFactoryArguments - { - abstract static Type[] Types { get; } - - abstract static implicit operator object[](in T self); - } -} diff --git a/app/InkForge.Desktop/Models/Workspace.cs b/app/InkForge.Desktop/Models/Workspace.cs index f8e9dbf..cb3b8b3 100644 --- a/app/InkForge.Desktop/Models/Workspace.cs +++ b/app/InkForge.Desktop/Models/Workspace.cs @@ -28,11 +28,6 @@ public sealed class Workspace : IDisposable // { // } - public T CreateViewModel() - { - return TypeFactory.Create(Services); - } - public void Dispose() { Dispose(disposing: true); @@ -43,8 +38,12 @@ public sealed class Workspace : IDisposable { if (!_disposedValue) { - _scope!.Dispose(); - _scope = null; + if (_scope is { }) + { + _scope.Dispose(); + _scope = null; + } + _disposedValue = true; } } diff --git a/app/InkForge.Desktop/Program.cs b/app/InkForge.Desktop/Program.cs index 8a09976..12dc592 100644 --- a/app/InkForge.Desktop/Program.cs +++ b/app/InkForge.Desktop/Program.cs @@ -1,18 +1,14 @@ using Avalonia; -using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Metadata; using Avalonia.ReactiveUI; using Avalonia.Threading; using InkForge.Desktop; -using InkForge.Desktop.ViewModels; +using InkForge.Desktop.Views; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using ReactiveUI; - static class Program { [STAThread] @@ -41,6 +37,11 @@ static class Program var serviceProvider = services.BuildServiceProvider(); app.SetValue(App.ServiceProviderProperty, serviceProvider); _ = new ServiceProviderDisposer(serviceProvider, dispatcher); + _ = app.ApplicationLifetime switch + { + IClassicDesktopStyleApplicationLifetime desktop => desktop.MainWindow = new MainWindow(), + _ => throw new NotSupportedException(), + }; } private static AppBuilder UseMicrosoftDependencyInjection(this AppBuilder builder, out ConfigurationManager configuration) @@ -48,7 +49,7 @@ static class Program configuration = new(); ServiceCollection services = []; services.AddSingleton(configuration); - App.Configure(services); + App.Configure(services, configuration); builder.AfterSetup(services.SetupApp); return builder; diff --git a/app/InkForge.Desktop/ReactiveUI/RoutableReactiveObject.cs b/app/InkForge.Desktop/ReactiveUI/RoutableReactiveObject.cs deleted file mode 100644 index 417591a..0000000 --- a/app/InkForge.Desktop/ReactiveUI/RoutableReactiveObject.cs +++ /dev/null @@ -1,10 +0,0 @@ -using ReactiveUI; - -namespace InkForge.Desktop.ReactiveUI; - -public abstract class RoutableReactiveObject(IScreen screen) : ReactiveObject, IRoutableViewModel -{ - public abstract string? UrlPathSegment { get; } - - public IScreen HostScreen => screen; -} diff --git a/app/InkForge.Desktop/ReactiveUI/ViewModelFactory.cs b/app/InkForge.Desktop/ReactiveUI/ViewModelFactory.cs deleted file mode 100644 index 0cdca18..0000000 --- a/app/InkForge.Desktop/ReactiveUI/ViewModelFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace InkForge.Desktop.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.Desktop/ViewModels/Documents/WelcomePageDocumentViewModel.cs b/app/InkForge.Desktop/ViewModels/Documents/WelcomePageDocumentViewModel.cs index fa8a6fd..8108738 100644 --- a/app/InkForge.Desktop/ViewModels/Documents/WelcomePageDocumentViewModel.cs +++ b/app/InkForge.Desktop/ViewModels/Documents/WelcomePageDocumentViewModel.cs @@ -21,8 +21,9 @@ public class WelcomePageDocumentViewModel : Document public WelcomePageDocumentViewModel(WorkspaceManager workspaceController) { + CanClose = false; Title = "Welcome"; - + _workspaceController = workspaceController; CreateNew = ReactiveCommand.CreateFromTask(OnCreateNew); OpenNew = ReactiveCommand.CreateFromTask(OnOpenNew); @@ -33,7 +34,7 @@ public class WelcomePageDocumentViewModel : Document var storageProvider = this.GetStorageProvider()!; var documents = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents); - var file = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions() + var file = await storageProvider.SaveFilePickerAsync(new() { DefaultExtension = ".ifdb", FileTypeChoices = @@ -60,7 +61,7 @@ public class WelcomePageDocumentViewModel : Document var storageProvider = this.GetStorageProvider()!; var documents = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents); - var files = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() + var files = await storageProvider.OpenFilePickerAsync(new() { AllowMultiple = false, SuggestedStartLocation = documents, diff --git a/app/InkForge.Desktop/ViewModels/DocumentsViewModel.cs b/app/InkForge.Desktop/ViewModels/DocumentsViewModel.cs deleted file mode 100644 index 97d853a..0000000 --- a/app/InkForge.Desktop/ViewModels/DocumentsViewModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Dock.Model.Controls; - -using InkForge.Desktop.Dock; -using InkForge.Desktop.Managers; -using InkForge.Desktop.ViewModels.Documents; - -namespace InkForge.Desktop.ViewModels; - -public class DocumentsViewModel -{ - private readonly WorkspaceFactory _workspaceFactory; - - public IRootDock Layout { get; } - - public DocumentsViewModel(WorkspaceFactory workspaceFactory, WorkspaceManager workspaceManager) - { - _workspaceFactory = workspaceFactory; - - Layout = workspaceFactory.CreateLayout(); - var documents = workspaceFactory.GetDockable("Documents")!; - workspaceFactory.AddDockable(documents, new WelcomePageDocumentViewModel(workspaceManager)); - } -} diff --git a/app/InkForge.Desktop/ViewModels/InkForgeDocumentDock.cs b/app/InkForge.Desktop/ViewModels/InkForgeDocumentDock.cs new file mode 100644 index 0000000..bd0f1f6 --- /dev/null +++ b/app/InkForge.Desktop/ViewModels/InkForgeDocumentDock.cs @@ -0,0 +1,13 @@ +using Dock.Model.Core; +using Dock.Model.ReactiveUI.Controls; + +namespace InkForge.Desktop.ViewModels; + +public class InkForgeDocumentDock : DocumentDock, IDock +{ + bool IDock.IsEmpty + { + get => false; + set { } + } +} diff --git a/app/InkForge.Desktop/ViewModels/MainViewModel.cs b/app/InkForge.Desktop/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..6925461 --- /dev/null +++ b/app/InkForge.Desktop/ViewModels/MainViewModel.cs @@ -0,0 +1,32 @@ +using Avalonia; + +using Dock.Model.Core; + +using InkForge.Desktop.Managers; + +using Microsoft.Extensions.DependencyInjection; + +using ReactiveUI; + +namespace InkForge.Desktop.ViewModels; + +public class MainViewModel : ReactiveObject +{ + private readonly DocumentManager _documentManager; + public IDock Layout { get; } + + public MainViewModel(InkForgeFactory factory) + { + Layout = factory.CreateLayout(); + factory.InitLayout(Layout); + + _documentManager = CreateDocumentManager(); + } + + private static DocumentManager CreateDocumentManager() + { + return ActivatorUtilities.CreateInstance( + Application.Current!.GetValue(App.ServiceProviderProperty) + ); + } +} diff --git a/app/InkForge.Desktop/ViewModels/Tools/WorkspaceTool.cs b/app/InkForge.Desktop/ViewModels/Tools/WorkspaceTool.cs new file mode 100644 index 0000000..b5f0777 --- /dev/null +++ b/app/InkForge.Desktop/ViewModels/Tools/WorkspaceTool.cs @@ -0,0 +1,32 @@ +using Dock.Model.ReactiveUI.Controls; + +using InkForge.Desktop.Managers; +using InkForge.Desktop.ViewModels.Workspaces; + +using ReactiveUI; + +namespace InkForge.Desktop.ViewModels.Tools; + +public class WorkspaceTool : Tool +{ + private WorkspaceViewModel? _workspace; + + public WorkspaceViewModel? Workspace + { + get => _workspace; + private set => this.RaiseAndSetIfChanged(ref _workspace, value); + } + + public WorkspaceTool(WorkspaceManager workspaceManager, IWorkspaceViewModelFactory workspaceViewModelFactory) + { + Title = "Workspace"; + CanClose = false; + + workspaceManager.WhenAnyValue(v => v.Workspace, + v => v switch + { + { } => workspaceViewModelFactory.Create(v), + _ => null + }).BindTo(this, v => v.Workspace); + } +} diff --git a/app/InkForge.Desktop/ViewModels/Workspaces/WorkspaceViewModel.cs b/app/InkForge.Desktop/ViewModels/Workspaces/WorkspaceViewModel.cs index 3749880..320ca4e 100644 --- a/app/InkForge.Desktop/ViewModels/Workspaces/WorkspaceViewModel.cs +++ b/app/InkForge.Desktop/ViewModels/Workspaces/WorkspaceViewModel.cs @@ -1,26 +1,50 @@ using InkForge.Desktop.Models; -namespace InkForge.Desktop.ViewModels.Workspaces; +using Microsoft.Extensions.DependencyInjection; -public class WorkspaceViewModel(Workspace workspace) +namespace InkForge.Desktop.ViewModels.Workspaces { - // private readonly Workspace _workspace; - // private readonly ObservableAsPropertyHelper _workspaceNameProperty; + public class WorkspaceViewModel(Workspace workspace) + { + // private readonly Workspace _workspace; + // private readonly ObservableAsPropertyHelper _workspaceNameProperty; - // public string WorkspaceName => _workspaceNameProperty.Value; + // public string WorkspaceName => _workspaceNameProperty.Value; - // public ReactiveCommand AddDocument { get; } + // public ReactiveCommand AddDocument { get; } - // public WorkspacesViewModel(Workspace workspace) - // { - // _workspace = workspace; - // _workspaceNameProperty = this.WhenAnyValue(v => v._workspace.Name).ToProperty(this, nameof(WorkspaceName)); + // public WorkspacesViewModel(Workspace workspace) + // { + // _workspace = workspace; + // _workspaceNameProperty = this.WhenAnyValue(v => v._workspace.Name).ToProperty(this, nameof(WorkspaceName)); - // AddDocument = ReactiveCommand.Create(OnAddDocument); - // } + // AddDocument = ReactiveCommand.Create(OnAddDocument); + // } - // private void OnAddDocument() - // { + // private void OnAddDocument() + // { - // } + // } + } + + public interface IWorkspaceViewModelFactory + { + WorkspaceViewModel Create(Workspace workspace); + } + + namespace Internal + { + internal class WorkspaceViewModelFactory(IServiceProvider services) : IWorkspaceViewModelFactory + { + private static ObjectFactory? s_workspaceViewModelFactory; + + public WorkspaceViewModel Create(Workspace workspace) + { + s_workspaceViewModelFactory ??= ActivatorUtilities.CreateFactory([typeof(Workspace)]); + return s_workspaceViewModelFactory(services, [workspace]); + } + + WorkspaceViewModel IWorkspaceViewModelFactory.Create(Workspace workspace) => Create(workspace); + } + } } diff --git a/app/InkForge.Desktop/ViewModels/WorkspacesViewModel.cs b/app/InkForge.Desktop/ViewModels/WorkspacesViewModel.cs deleted file mode 100644 index a0a76e3..0000000 --- a/app/InkForge.Desktop/ViewModels/WorkspacesViewModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using InkForge.Desktop.Managers; -using InkForge.Desktop.ViewModels.Workspaces; - -using ReactiveUI; - -namespace InkForge.Desktop.ViewModels; - -public class WorkspacesViewModel : ReactiveObject -{ - private readonly WorkspaceManager _workspaceManager; - private WorkspaceViewModel? _workspace; - - public WorkspaceViewModel? Workspace - { - get => _workspace; - private set => this.RaiseAndSetIfChanged(ref _workspace, value); - } - - public WorkspacesViewModel(WorkspaceManager workspaceManager) - { - _workspaceManager = workspaceManager; - workspaceManager.WhenAnyValue(v => v.Workspace, v => v is null ? null : new WorkspaceViewModel(v)).BindTo(this, v => v.Workspace); - } -} diff --git a/app/InkForge.Desktop/Views/Documents/WelcomePageDocument.axaml b/app/InkForge.Desktop/Views/Documents/WelcomePageDocument.axaml index 277f09b..63d4366 100644 --- a/app/InkForge.Desktop/Views/Documents/WelcomePageDocument.axaml +++ b/app/InkForge.Desktop/Views/Documents/WelcomePageDocument.axaml @@ -10,5 +10,29 @@ x:Class="InkForge.Desktop.Views.Documents.WelcomePageDocument" x:DataType="vm:WelcomePageDocumentViewModel" inkforge:TopLevels.Register="{CompiledBinding}"> - Welcome to Avalonia! + + \ No newline at end of file diff --git a/app/InkForge.Desktop/Views/DocumentsView.axaml b/app/InkForge.Desktop/Views/DocumentsView.axaml deleted file mode 100644 index a4f00e0..0000000 --- a/app/InkForge.Desktop/Views/DocumentsView.axaml +++ /dev/null @@ -1,12 +0,0 @@ - - - \ No newline at end of file diff --git a/app/InkForge.Desktop/Views/DocumentsView.axaml.cs b/app/InkForge.Desktop/Views/DocumentsView.axaml.cs deleted file mode 100644 index 49b6895..0000000 --- a/app/InkForge.Desktop/Views/DocumentsView.axaml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Avalonia; -using Avalonia.Controls; - -using InkForge.Desktop.ViewModels; - -using Microsoft.Extensions.DependencyInjection; - -namespace InkForge.Desktop.Views; - -public partial class DocumentsView : UserControl -{ - public DocumentsView() - { - InitializeComponent(); - DataContext = CreateViewModel(); - } - - private static DocumentsViewModel CreateViewModel() - { - return ActivatorUtilities.CreateInstance( - Application.Current!.GetValue(App.ServiceProviderProperty) - ); - } -} diff --git a/app/InkForge.Desktop/Views/MainWindow.axaml b/app/InkForge.Desktop/Views/MainWindow.axaml index 0ffa47f..2088466 100644 --- a/app/InkForge.Desktop/Views/MainWindow.axaml +++ b/app/InkForge.Desktop/Views/MainWindow.axaml @@ -2,12 +2,16 @@ 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:inkforge="app:InkForge" xmlns:local="using:InkForge.Desktop.Views" + xmlns:vm="using:InkForge.Desktop.ViewModels" mc:Ignorable="d" Width="800" Height="450" x:Class="InkForge.Desktop.Views.MainWindow" - Title="MainWindow"> + x:DataType="vm:MainViewModel" + Title="MainWindow" + inkforge:TopLevels.Register="{CompiledBinding}"> @@ -15,13 +19,6 @@ - - - - - - - + \ No newline at end of file diff --git a/app/InkForge.Desktop/Views/MainWindow.axaml.cs b/app/InkForge.Desktop/Views/MainWindow.axaml.cs index 712ecf3..42b4735 100644 --- a/app/InkForge.Desktop/Views/MainWindow.axaml.cs +++ b/app/InkForge.Desktop/Views/MainWindow.axaml.cs @@ -1,11 +1,24 @@ -using Avalonia.Controls; +using Avalonia; +using Avalonia.ReactiveUI; + +using InkForge.Desktop.ViewModels; + +using Microsoft.Extensions.DependencyInjection; namespace InkForge.Desktop.Views; -public partial class MainWindow : Window +public partial class MainWindow : ReactiveWindow { public MainWindow() { InitializeComponent(); + ViewModel = CreateViewModel(); + } + + private static MainViewModel CreateViewModel() + { + return ActivatorUtilities.CreateInstance( + Application.Current!.GetValue(App.ServiceProviderProperty) + ); } } diff --git a/app/InkForge.Desktop/Views/Tools/WorkspaceTool.axaml b/app/InkForge.Desktop/Views/Tools/WorkspaceTool.axaml new file mode 100644 index 0000000..c5e7d85 --- /dev/null +++ b/app/InkForge.Desktop/Views/Tools/WorkspaceTool.axaml @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/app/InkForge.Desktop/Views/Tools/WorkspaceTool.axaml.cs b/app/InkForge.Desktop/Views/Tools/WorkspaceTool.axaml.cs new file mode 100644 index 0000000..a0cfc1d --- /dev/null +++ b/app/InkForge.Desktop/Views/Tools/WorkspaceTool.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.ReactiveUI; + +namespace InkForge.Desktop.Views.Tools; + +public partial class WorkspaceTool : ReactiveUserControl +{ + public WorkspaceTool() + { + InitializeComponent(); + } +} diff --git a/app/InkForge.Desktop/Views/Workspaces/WorkspaceViewModel.axaml b/app/InkForge.Desktop/Views/Workspaces/WorkspaceView.axaml similarity index 99% rename from app/InkForge.Desktop/Views/Workspaces/WorkspaceViewModel.axaml rename to app/InkForge.Desktop/Views/Workspaces/WorkspaceView.axaml index 35e5083..7cee934 100644 --- a/app/InkForge.Desktop/Views/Workspaces/WorkspaceViewModel.axaml +++ b/app/InkForge.Desktop/Views/Workspaces/WorkspaceView.axaml @@ -7,7 +7,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="InkForge.Desktop.Views.Workspaces.WorkspaceViewModel" + x:Class="InkForge.Desktop.Views.Workspaces.WorkspaceView" x:DataType="vm:WorkspaceViewModel"> +{ + public WorkspaceView() + { + InitializeComponent(); + } +} diff --git a/app/InkForge.Desktop/Views/Workspaces/WorkspaceViewModel.cs b/app/InkForge.Desktop/Views/Workspaces/WorkspaceViewModel.cs deleted file mode 100644 index fed04b6..0000000 --- a/app/InkForge.Desktop/Views/Workspaces/WorkspaceViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -namespace InkForge.Desktop.Views.Workspaces; - -public partial class WorkspaceViewModel : UserControl -{ - public WorkspaceViewModel() - { - InitializeComponent(); - } -} diff --git a/app/InkForge.Desktop/Views/WorkspacesView.axaml b/app/InkForge.Desktop/Views/WorkspacesView.axaml deleted file mode 100644 index 436690c..0000000 --- a/app/InkForge.Desktop/Views/WorkspacesView.axaml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/InkForge.Desktop/Views/WorkspacesView.axaml.cs b/app/InkForge.Desktop/Views/WorkspacesView.axaml.cs deleted file mode 100644 index 8be6766..0000000 --- a/app/InkForge.Desktop/Views/WorkspacesView.axaml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Avalonia.Controls; - -using InkForge.Desktop.ViewModels; - -using Splat; - -namespace InkForge.Desktop.Views; - -public partial class WorkspacesView : UserControl -{ - public WorkspacesView() - { - InitializeComponent(); - DataContext = Locator.Current.GetService(); - } -}