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();
- }
-}