Batch Update

This commit is contained in:
Jöran Malek 2024-03-17 22:27:01 +01:00
parent 693d12b61c
commit 4c2b5cca93
32 changed files with 483 additions and 332 deletions

View file

@ -2,35 +2,29 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
<AvaloniaVersion>11.0.9</AvaloniaVersion>
<DotNetVersion>8.0.2</DotNetVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Labs.Controls" Version="11.0.3" />
<PackageVersion Include="Avalonia.Labs.Panels" Version="11.0.3" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Themes.Simple" Version="11.0.9" />
<PackageVersion Include="Dock.Avalonia" Version="11.0.0.5" />
<PackageVersion Include="Dock.Model.ReactiveUI" Version="11.0.0.5" />
<PackageVersion Include="Avalonia" Version="11.0.10" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.10" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.0.10" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.0.10" />
<PackageVersion Include="Dock.Avalonia" Version="11.0.0.6" />
<PackageVersion Include="Dock.Model.ReactiveUI" Version="11.0.0.6" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.1" />
<PackageVersion Include="ReactiveUI" Version="19.5.41" />
<PackageVersion Include="SmartFormat" Version="3.3.2" />
<PackageVersion Include="Splat.Microsoft.Extensions.DependencyInjection" Version="14.8.12" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />

View file

@ -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<IDataTemplate> AppDataTemplateProperty
= AvaloniaProperty.Register<App, IDataTemplate>(
name: nameof(AppDataTemplate),
coerce: OnAppDataTemplateChanged);
public static readonly StyledProperty<IServiceProvider> ServiceProviderProperty
= AvaloniaProperty.Register<App, IServiceProvider>(
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<IDataTemplate>());
return provider;
}
}

View file

@ -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, TViewModel>(TView view, TViewModel viewModel)
where TViewModel : class
where TView : IViewFor<TViewModel>
{
view.ViewModel = viewModel;
return view;
}
}
public bool Match(object? data)
{
return data is
RecentItemViewModel or
ViewModels.Tools.WorkspaceTool or
WelcomePageDocumentViewModel or
WorkspaceViewModel;
}
}

View file

@ -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<string, Func<IDockable?>>
{
["Root"] = () => root,
["Documents"] = () => documents,
};
return root;
}
}

View file

@ -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<string, Func<IDockable?>>
{
["Root"] = () => _rootDock,
["Documents"] = () => _documentDock,
["Workspace"] = () => _workspaceTool,
};
HostWindowLocator = new Dictionary<string, Func<IHostWindow?>>
{
[nameof(IDockWindow)] = () => new HostWindow()
};
base.InitLayout(layout);
}
private static ViewModels.Tools.WorkspaceTool CreateWorkspaceTool()
{
return ActivatorUtilities.CreateInstance<ViewModels.Tools.WorkspaceTool>(
Application.Current!.GetValue(App.ServiceProviderProperty)
);
}
}

View file

@ -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<IDock>("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<WelcomePageDocumentViewModel>(
Application.Current!.GetValue(App.ServiceProviderProperty)
);
}
}

View file

@ -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)

View file

@ -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<DocumentManager>();
services.AddSingleton<WorkspaceFactory>();
services.AddSingleton<InkForgeFactory>();
services.AddSingleton<WorkspaceManager>();
services.AddSingleton<WorkspacesViewModel>();
// - Service
services.AddSingleton<IDataTemplate, AppViewLocator>();
services.AddSingleton<IWorkspaceViewModelFactory, WorkspaceViewModelFactory>();
// Scoped
// - Concrete
@ -38,8 +42,6 @@ public static class InkForgeServiceCollections
// - Forwarders
services.AddScoped(s => s.GetRequiredService<IWorkspaceContext>().Workspace!);
Locator.CurrentMutable.RegisterViewsForViewModels(typeof(InkForgeServiceCollections).Assembly);
return services;
}
}

View file

@ -1,37 +0,0 @@
namespace Microsoft.Extensions.DependencyInjection
{
public static class TypeFactory
{
public static T Create<T>(IServiceProvider serviceProvider)
{
return TypeFactory<EmptyArguments, T>.Create(serviceProvider, default);
}
}
public static class TypeFactory<TArguments, T>
where TArguments : IFactoryArguments<TArguments>
{
private static ObjectFactory<T>? s_objectFactory;
public static T Create(IServiceProvider serviceProvider, in TArguments factory)
{
s_objectFactory ??= ActivatorUtilities.CreateFactory<T>(TArguments.Types);
return s_objectFactory(serviceProvider, (object[])factory);
}
}
public readonly struct EmptyArguments : IFactoryArguments<EmptyArguments>
{
public static Type[] Types => [];
public static implicit operator object[](in EmptyArguments _) => [];
}
public interface IFactoryArguments<T>
where T : IFactoryArguments<T>
{
abstract static Type[] Types { get; }
abstract static implicit operator object[](in T self);
}
}

View file

@ -28,11 +28,6 @@ public sealed class Workspace : IDisposable
// {
// }
public T CreateViewModel<T>()
{
return TypeFactory.Create<T>(Services);
}
public void Dispose()
{
Dispose(disposing: true);
@ -43,8 +38,12 @@ public sealed class Workspace : IDisposable
{
if (!_disposedValue)
{
_scope!.Dispose();
if (_scope is { })
{
_scope.Dispose();
_scope = null;
}
_disposedValue = true;
}
}

View file

@ -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<IConfiguration>(configuration);
App.Configure(services);
App.Configure(services, configuration);
builder.AfterSetup(services.SetupApp);
return builder;

View file

@ -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;
}

View file

@ -1,23 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
namespace InkForge.Desktop.ReactiveUI;
public interface IViewModelFactory<T, TCreator>
{
abstract static ObjectFactory<T> CreateObjectFactory();
abstract static TCreator GetCreator(ObjectFactory<T> factory, IServiceProvider serviceProvider);
}
public class ViewModelFactory<T, TFactory, TCreator>
where TFactory : IViewModelFactory<T, TCreator>
where TCreator : Delegate
{
private static ObjectFactory<T>? s_factory;
public TCreator CreateFactory(IServiceProvider serviceProvider)
{
s_factory ??= TFactory.CreateObjectFactory();
return TFactory.GetCreator(s_factory, serviceProvider);
}
}

View file

@ -21,6 +21,7 @@ public class WelcomePageDocumentViewModel : Document
public WelcomePageDocumentViewModel(WorkspaceManager workspaceController)
{
CanClose = false;
Title = "Welcome";
_workspaceController = workspaceController;
@ -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,

View file

@ -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<IDocumentDock>("Documents")!;
workspaceFactory.AddDockable(documents, new WelcomePageDocumentViewModel(workspaceManager));
}
}

View file

@ -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 { }
}
}

View file

@ -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<DocumentManager>(
Application.Current!.GetValue(App.ServiceProviderProperty)
);
}
}

View file

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

View file

@ -1,7 +1,9 @@
using InkForge.Desktop.Models;
namespace InkForge.Desktop.ViewModels.Workspaces;
using Microsoft.Extensions.DependencyInjection;
namespace InkForge.Desktop.ViewModels.Workspaces
{
public class WorkspaceViewModel(Workspace workspace)
{
// private readonly Workspace _workspace;
@ -24,3 +26,25 @@ public class WorkspaceViewModel(Workspace workspace)
// }
}
public interface IWorkspaceViewModelFactory
{
WorkspaceViewModel Create(Workspace workspace);
}
namespace Internal
{
internal class WorkspaceViewModelFactory(IServiceProvider services) : IWorkspaceViewModelFactory
{
private static ObjectFactory<WorkspaceViewModel>? s_workspaceViewModelFactory;
public WorkspaceViewModel Create(Workspace workspace)
{
s_workspaceViewModelFactory ??= ActivatorUtilities.CreateFactory<WorkspaceViewModel>([typeof(Workspace)]);
return s_workspaceViewModelFactory(services, [workspace]);
}
WorkspaceViewModel IWorkspaceViewModelFactory.Create(Workspace workspace) => Create(workspace);
}
}
}

View file

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

View file

@ -10,5 +10,29 @@
x:Class="InkForge.Desktop.Views.Documents.WelcomePageDocument"
x:DataType="vm:WelcomePageDocumentViewModel"
inkforge:TopLevels.Register="{CompiledBinding}">
Welcome to Avalonia!
<Grid RowDefinitions="Auto, *, Auto">
<Label Target="RecentItemsList"
Content="Open Recent"
Grid.Row="0" />
<DataGrid Name="RecentItemsList"
IsEnabled="False"
IsReadOnly="True"
Grid.Row="1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Width="*" />
<DataGridTextColumn Header="Last Used" />
</DataGrid.Columns>
</DataGrid>
<Menu Grid.Row="2">
<MenuItem Header="Create New"
Command="{CompiledBinding CreateNew}" />
<MenuItem Header="Open"
IsEnabled="False" />
<MenuItem Header="Open File"
Command="{CompiledBinding OpenNew}" />
</Menu>
</Grid>
</UserControl>

View file

@ -1,12 +0,0 @@
<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"
xmlns:inkforge="using:InkForge.Desktop.ViewModels"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.DocumentsView"
x:DataType="inkforge:DocumentsViewModel">
<DockControl Layout="{CompiledBinding Layout}" />
</UserControl>

View file

@ -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<DocumentsViewModel>(
Application.Current!.GetValue(App.ServiceProviderProperty)
);
}
}

View file

@ -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}">
<NativeMenu.Menu>
<NativeMenu />
</NativeMenu.Menu>
@ -15,13 +19,6 @@
<DockPanel>
<NativeMenuBar DockPanel.Dock="Top" />
<SplitView IsPaneOpen="true"
DisplayMode="Inline">
<SplitView.Pane>
<local:WorkspacesView />
</SplitView.Pane>
<local:DocumentsView />
</SplitView>
<DockControl Layout="{CompiledBinding Layout}" />
</DockPanel>
</Window>

View file

@ -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<MainViewModel>
{
public MainWindow()
{
InitializeComponent();
ViewModel = CreateViewModel();
}
private static MainViewModel CreateViewModel()
{
return ActivatorUtilities.CreateInstance<MainViewModel>(
Application.Current!.GetValue(App.ServiceProviderProperty)
);
}
}

View file

@ -0,0 +1,32 @@
<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"
xmlns:local="using:InkForge.Desktop.Views.Tools"
xmlns:workspaces="using:InkForge.Desktop.Views.Workspaces"
xmlns:vm="using:InkForge.Desktop.ViewModels.Tools"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.Tools.WorkspaceTool"
x:DataType="vm:WorkspaceTool"
Classes.HasWorkspace="{CompiledBinding Workspace, Converter={x:Static ObjectConverters.IsNotNull}}">
<UserControl.Styles>
<Style Selector="local|WorkspaceTool">
<Setter Property="Content">
<Template>
<TextBlock>No workspace selected.</TextBlock>
</Template>
</Setter>
<Style Selector="^.HasWorkspace">
<Setter Property="Content">
<Template>
<workspaces:WorkspaceView DataContext="{CompiledBinding Workspace}" />
</Template>
</Setter>
</Style>
</Style>
</UserControl.Styles>
</UserControl>

View file

@ -0,0 +1,11 @@
using Avalonia.ReactiveUI;
namespace InkForge.Desktop.Views.Tools;
public partial class WorkspaceTool : ReactiveUserControl<ViewModels.Tools.WorkspaceTool>
{
public WorkspaceTool()
{
InitializeComponent();
}
}

View file

@ -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">
<Grid ColumnDefinitions="*, Auto"

View file

@ -0,0 +1,13 @@
using Avalonia.ReactiveUI;
using InkForge.Desktop.ViewModels.Workspaces;
namespace InkForge.Desktop.Views.Workspaces;
public partial class WorkspaceView : ReactiveUserControl<WorkspaceViewModel>
{
public WorkspaceView()
{
InitializeComponent();
}
}

View file

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

View file

@ -1,33 +0,0 @@
<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"
xmlns:reactiveui="http://reactiveui.net"
xmlns:local="using:InkForge.Desktop.Views"
xmlns:inkforge="app:InkForge"
xmlns:vm="using:InkForge.Desktop.ViewModels"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.WorkspacesView"
x:DataType="vm:WorkspacesViewModel"
Classes.HasWorkspace="{CompiledBinding Workspace}">
<UserControl.Styles>
<Style Selector="local|WorkspacesView">
<Style Selector="^:not(.HasWorkspace)">
<Setter Property="Content">
<Template>
<TextBlock>
No workspace selected.
</TextBlock>
</Template>
</Setter>
</Style>
<Style Selector="^.HasWorkspace">
<Setter Property="Content"
Value="{CompiledBinding Workspace}" />
</Style>
</Style>
</UserControl.Styles>
</UserControl>

View file

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