This commit is contained in:
Jöran Malek 2024-02-26 18:08:18 +01:00
parent b1d3ec73c9
commit 693d12b61c
35 changed files with 389 additions and 269 deletions

View file

@ -6,6 +6,7 @@
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
<DockFluentTheme />
</Application.Styles>
<Application.Resources>

View file

@ -1,25 +1,17 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Metadata;
using InkForge.Desktop.ViewModels;
using InkForge.Desktop.Views;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using ReactiveUI;
using Splat;
using Splat.Microsoft.Extensions.DependencyInjection;
[assembly: XmlnsPrefix("app:InkForge", "inkforge")]
[assembly: XmlnsDefinition("app:InkForge", "InkForge.Desktop.Controls")]
[assembly: XmlnsDefinition("app:InkForge", "InkForge.Desktop.MarkupExtensions")]
[assembly: XmlnsDefinition("app:InkForge", "InkForge.Desktop.Services")]
namespace InkForge.Desktop;
public partial class App : Application
@ -31,21 +23,8 @@ public partial class App : Application
public IServiceProvider ServiceProvider => GetValue(ServiceProviderProperty);
public static void Configure(IServiceCollection services, IConfigurationManager configuration)
public static void Configure(IServiceCollection services)
{
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();
@ -60,18 +39,11 @@ public partial class App : Application
public override void OnFrameworkInitializationCompleted()
{
// This kills Avalonia VSCode Previewer.
var viewModel = ActivatorUtilities.GetServiceOrCreateInstance<AppViewModel>(ServiceProvider);
var view = ViewLocator.Current.ResolveView(viewModel)!;
view.ViewModel = viewModel;
_ = ApplicationLifetime switch
{
IClassicDesktopStyleApplicationLifetime desktop => desktop.MainWindow = view as Window,
ISingleViewApplicationLifetime singleView => singleView.MainView = view as Control,
IClassicDesktopStyleApplicationLifetime desktop => desktop.MainWindow = new MainWindow(),
_ => throw new NotSupportedException(),
};
base.OnFrameworkInitializationCompleted();
}
private static IServiceProvider OnServiceProviderChanged(AvaloniaObject @object, IServiceProvider provider)

View file

@ -0,0 +1,29 @@
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

@ -17,6 +17,7 @@
<PackageReference Include="Avalonia.ReactiveUI" />
<PackageReference Include="Avalonia.Themes.Fluent" />
<PackageReference Include="Dock.Avalonia" />
<PackageReference Include="Dock.Model.ReactiveUI" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" />

View file

@ -0,0 +1,11 @@
namespace InkForge.Desktop.Managers;
public class DocumentManager
{
private readonly WorkspaceManager _workspaceManager;
public DocumentManager(WorkspaceManager workspaceManager)
{
_workspaceManager = workspaceManager;
}
}

View file

@ -46,7 +46,7 @@ public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject
file.Directory!.Create();
IServiceScope? scope = null;
IWorkspaceAccessor workspaceAccessor;
IWorkspaceContext workspaceContext;
try
{
scope = _serviceProvider.CreateScope();
@ -54,8 +54,8 @@ public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject
var options = serviceProvider.GetRequiredService<LocalWorkspaceOptions>();
options.DbPath = path;
workspaceAccessor = serviceProvider.GetRequiredService<IWorkspaceAccessor>();
workspaceAccessor.Workspace = new Workspace(scope)
workspaceContext = serviceProvider.GetRequiredService<IWorkspaceContext>();
workspaceContext.Workspace = new Workspace(scope)
{
Name = Path.GetFileNameWithoutExtension(file.Name),
Options = options,
@ -87,6 +87,6 @@ public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject
scope?.Dispose();
}
return workspaceAccessor.Workspace;
return workspaceContext.Workspace;
}
}

View file

@ -1,6 +1,7 @@
using InkForge.Data;
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;
@ -19,13 +20,23 @@ public static class InkForgeServiceCollections
{
services.AddHttpClient();
services.AddScoped<IWorkspaceAccessor, WorkspaceAccessor>();
services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>();
// Singletons
// - Concrete
services.AddSingleton<DocumentManager>();
services.AddSingleton<WorkspaceFactory>();
services.AddSingleton<WorkspaceManager>();
services.AddSingleton<WorkspacesViewModel>();
// Scoped
// - Concrete
services.AddScoped<LocalWorkspaceOptions>();
services.AddSingleton<LandingViewModel>();
services.AddSingleton<WorkspaceManager>();
// - Service
services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>();
services.AddScoped<IWorkspaceContext, WorkspaceContext>();
// - Forwarders
services.AddScoped(s => s.GetRequiredService<IWorkspaceContext>().Workspace!);
Locator.CurrentMutable.RegisterViewsForViewModels(typeof(InkForgeServiceCollections).Assembly);

View file

@ -1,5 +1,13 @@
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>
{
@ -12,6 +20,13 @@ namespace Microsoft.Extensions.DependencyInjection
}
}
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>
{

View file

@ -0,0 +1,6 @@
namespace InkForge.Desktop.Models;
public class Note
{
}

View file

@ -24,6 +24,15 @@ public sealed class Workspace : IDisposable
_dbContextFactory = Services.GetRequiredService<IDbContextFactory<NoteDbContext>>();
}
// public Note AddNote(Note? parent)
// {
// }
public T CreateViewModel<T>()
{
return TypeFactory.Create<T>(Services);
}
public void Dispose()
{
Dispose(disposing: true);
@ -34,10 +43,7 @@ public sealed class Workspace : IDisposable
{
if (!_disposedValue)
{
{
_scope!.Dispose();
}
_scope!.Dispose();
_scope = null;
_disposedValue = true;
}

View file

@ -1,11 +1,11 @@
namespace InkForge.Desktop.Models
{
public interface IWorkspaceAccessor
public interface IWorkspaceContext
{
Workspace? Workspace { get; set; }
}
public class WorkspaceAccessor : IWorkspaceAccessor
public class WorkspaceContext : IWorkspaceContext
{
public Workspace? Workspace { get; set; }
}

View file

@ -1,14 +1,18 @@
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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ReactiveUI;
static class Program
{
[STAThread]
@ -44,7 +48,7 @@ static class Program
configuration = new();
ServiceCollection services = [];
services.AddSingleton<IConfiguration>(configuration);
App.Configure(services, configuration);
App.Configure(services);
builder.AfterSetup(services.SetupApp);
return builder;
@ -58,18 +62,18 @@ static class Program
private class ServiceProviderDisposer
{
private readonly ServiceProvider _serviceProvider;
private readonly TaskCompletionSource<ValueTask> _shutdownTask = new();
private ValueTask? _shutdownTask;
public ServiceProviderDisposer(ServiceProvider serviceProvider, Dispatcher dispatcher)
{
dispatcher.ShutdownStarted += OnShutdownStarted;
dispatcher.ShutdownFinished += OnShutdownFinished;
dispatcher.ShutdownStarted += OnShutdownStarted;
_serviceProvider = serviceProvider;
}
private void OnShutdownFinished(object? sender, EventArgs e)
{
if (_shutdownTask.Task.Result is { IsCompleted: false } disposeTask)
if (_shutdownTask is { IsCompleted: false } disposeTask)
{
disposeTask.GetAwaiter().GetResult();
}
@ -78,7 +82,7 @@ static class Program
private void OnShutdownStarted(object? sender, EventArgs e)
{
#pragma warning disable CA2012 // This will only ever be awaited once in ShutdownFinished
_shutdownTask.SetResult(_serviceProvider.DisposeAsync());
_shutdownTask = _serviceProvider.DisposeAsync();
#pragma warning restore CA2012
}
}

View file

@ -0,0 +1,6 @@
using Avalonia.Metadata;
[assembly: XmlnsPrefix("app:InkForge", "inkforge")]
[assembly: XmlnsDefinition("app:InkForge", "InkForge.Desktop.Controls")]
[assembly: XmlnsDefinition("app:InkForge", "InkForge.Desktop.MarkupExtensions")]
[assembly: XmlnsDefinition("app:InkForge", "InkForge.Desktop.Services")]

View file

@ -1,6 +0,0 @@
namespace InkForge.Desktop.Services;
public class WorkspaceContext
{
public string DbPath { get; set; }
}

View file

@ -1,36 +0,0 @@
using InkForge.Desktop.Managers;
using InkForge.Desktop.Models;
using ReactiveUI;
namespace InkForge.Desktop.ViewModels;
public class AppViewModel : ReactiveObject
{
private readonly LandingViewModel _landingViewModel;
private readonly WorkspaceManager _workspace;
private object _view;
public object View
{
get => _view;
set => this.RaiseAndSetIfChanged(ref _view, value);
}
public AppViewModel(WorkspaceManager workspace, LandingViewModel landingViewModel)
{
_workspace = workspace;
_landingViewModel = landingViewModel;
this.WhenAnyValue(v => v._workspace.Workspace).Subscribe(OnWorkspaceChanged);
}
private void OnWorkspaceChanged(Workspace workspace)
{
View = workspace switch
{
null => _landingViewModel,
{ } => new WorkspaceViewModel(workspace) // scoped?
};
}
}

View file

@ -1,28 +1,28 @@
using System.Collections.ObjectModel;
using System.Reactive;
using Avalonia.Platform.Storage;
using Dock.Model.ReactiveUI.Controls;
using InkForge.Desktop.Managers;
using InkForge.Desktop.Services;
using ReactiveUI;
namespace InkForge.Desktop.ViewModels;
namespace InkForge.Desktop.ViewModels.Documents;
public class LandingViewModel : ReactiveObject
public class WelcomePageDocumentViewModel : Document
{
private ReadOnlyObservableCollection<RecentItemViewModel> _recentItems;
private readonly WorkspaceManager _workspaceController;
public ReactiveCommand<Unit, Unit> CreateNew { get; }
public ReactiveCommand<Unit, Unit> OpenNew { get; }
public ReadOnlyObservableCollection<RecentItemViewModel> RecentItems => _recentItems;
public LandingViewModel(WorkspaceManager workspaceController)
public WelcomePageDocumentViewModel(WorkspaceManager workspaceController)
{
Title = "Welcome";
_workspaceController = workspaceController;
CreateNew = ReactiveCommand.CreateFromTask(OnCreateNew);
OpenNew = ReactiveCommand.CreateFromTask(OnOpenNew);

View file

@ -0,0 +1,23 @@
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

@ -1,19 +0,0 @@
using InkForge.Desktop.Models;
using ReactiveUI;
namespace InkForge.Desktop.ViewModels;
public class WorkspaceViewModel : ReactiveObject
{
private readonly Workspace _workspace;
private readonly ObservableAsPropertyHelper<string> _workspaceNameProperty;
public string WorkspaceName => _workspaceNameProperty.Value;
public WorkspaceViewModel(Workspace workspace)
{
_workspace = workspace;
_workspaceNameProperty = this.WhenAnyValue(v => v._workspace.Name).ToProperty(this, nameof(WorkspaceName));
}
}

View file

@ -0,0 +1,26 @@
using InkForge.Desktop.Models;
namespace InkForge.Desktop.ViewModels.Workspaces;
public class WorkspaceViewModel(Workspace workspace)
{
// private readonly Workspace _workspace;
// private readonly ObservableAsPropertyHelper<string> _workspaceNameProperty;
// public string WorkspaceName => _workspaceNameProperty.Value;
// public ReactiveCommand<Unit, Unit> AddDocument { get; }
// public WorkspacesViewModel(Workspace workspace)
// {
// _workspace = workspace;
// _workspaceNameProperty = this.WhenAnyValue(v => v._workspace.Name).ToProperty(this, nameof(WorkspaceName));
// AddDocument = ReactiveCommand.Create(OnAddDocument);
// }
// private void OnAddDocument()
// {
// }
}

View file

@ -0,0 +1,24 @@
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

@ -0,0 +1,14 @@
<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:vm="using:InkForge.Desktop.ViewModels.Documents"
xmlns:inkforge="app:InkForge"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.Documents.WelcomePageDocument"
x:DataType="vm:WelcomePageDocumentViewModel"
inkforge:TopLevels.Register="{CompiledBinding}">
Welcome to Avalonia!
</UserControl>

View file

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

View file

@ -0,0 +1,12 @@
<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

@ -0,0 +1,24 @@
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

@ -1,42 +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:vm="using:InkForge.Desktop.ViewModels"
xmlns:inkforge="app:InkForge"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.LandingView"
x:DataType="vm:LandingViewModel"
inkforge:TopLevels.Register="{CompiledBinding}">
<Grid RowDefinitions="Auto, *, Auto">
<Label Content="Open Recent"
Grid.Row="0" />
<DataGrid IsEnabled="False"
IsReadOnly="True"
ItemsSource="{CompiledBinding RecentItems}"
Grid.Row="1">
<DataGrid.Columns>
<DataGridTextColumn Header="Created"
Binding="{CompiledBinding Created, StringFormat={}{0:d}}" />
<DataGridTextColumn Header="Name"
Width="*"
Binding="{CompiledBinding Name}" />
<DataGridTextColumn Header="Last Used"
Binding="{CompiledBinding LastUsed, StringFormat={}{0:d}}" />
</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,13 +0,0 @@
using Avalonia.ReactiveUI;
using InkForge.Desktop.ViewModels;
namespace InkForge.Desktop.Views;
public partial class LandingView : ReactiveUserControl<LandingViewModel>
{
public LandingView()
{
InitializeComponent();
}
}

View file

@ -2,19 +2,26 @@
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:inkforge="app:InkForge"
xmlns:vm="using:InkForge.Desktop.ViewModels"
xmlns:local="using:InkForge.Desktop.Views"
mc:Ignorable="d"
Width="800"
Height="450"
x:Class="InkForge.Desktop.Views.MainWindow"
x:DataType="vm:AppViewModel"
Title="MainWindow"
inkforge:TopLevels.Register="{CompiledBinding}">
<DockPanel>
<NativeMenuBar />
Title="MainWindow">
<NativeMenu.Menu>
<NativeMenu />
</NativeMenu.Menu>
<reactiveui:ViewModelViewHost ViewModel="{CompiledBinding View}" />
<DockPanel>
<NativeMenuBar DockPanel.Dock="Top" />
<SplitView IsPaneOpen="true"
DisplayMode="Inline">
<SplitView.Pane>
<local:WorkspacesView />
</SplitView.Pane>
<local:DocumentsView />
</SplitView>
</DockPanel>
</Window>

View file

@ -1,11 +1,8 @@
using Avalonia.Input;
using Avalonia.ReactiveUI;
using InkForge.Desktop.ViewModels;
using Avalonia.Controls;
namespace InkForge.Desktop.Views;
public partial class MainWindow : ReactiveWindow<AppViewModel>
public partial class MainWindow : Window
{
public MainWindow()
{

View file

@ -1,69 +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:inkforge="app:InkForge"
xmlns:vm="using:InkForge.Desktop.ViewModels"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.WorkspaceView"
x:DataType="vm:WorkspaceViewModel"
inkforge:TopLevels.Register="{CompiledBinding}">
<SplitView IsPaneOpen="true"
DisplayMode="Inline"
OpenPaneLength="300">
<SplitView.Pane>
<DockPanel x:Name="FilesView"
Background="Transparent">
<Grid ColumnDefinitions="*, Auto"
DockPanel.Dock="Top">
<TextBlock Text="Notes"
FontWeight="Bold"
Margin="3"
Grid.Column="0" />
<StackPanel x:Name="ToolBar"
Orientation="Horizontal"
Spacing="3"
Grid.Column="1">
<Button>
<inkforge:FluentSymbolIcon Symbol="document_add" />
</Button>
<Button>
<inkforge:FluentSymbolIcon Symbol="arrow_clockwise" />
</Button>
<Button>
<inkforge:FluentSymbolIcon Symbol="subtract_square_multiple" />
</Button>
<StackPanel.Styles>
<Style Selector="#ToolBar > :is(TemplatedControl)">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Padding"
Value="1" />
<Setter Property="VerticalAlignment"
Value="Center" />
</Style>
<Style Selector="#FilesView:not(:pointerover) StackPanel">
<Setter Property="IsVisible"
Value="False" />
</Style>
</StackPanel.Styles>
</StackPanel>
</Grid>
<TreeView ScrollViewer.VerticalScrollBarVisibility="Visible" />
</DockPanel>
</SplitView.Pane>
<TabControl>
<TabItem Header="Some Note.md">
Hello There!
</TabItem>
</TabControl>
</SplitView>
</UserControl>

View file

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

View file

@ -0,0 +1,53 @@
<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:vm="using:InkForge.Desktop.ViewModels.Workspaces"
xmlns:inkforge="app:InkForge"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Desktop.Views.Workspaces.WorkspaceViewModel"
x:DataType="vm:WorkspaceViewModel">
<Grid ColumnDefinitions="*, Auto"
RowDefinitions="Auto, *">
<TextBlock Grid.Column="0"
Grid.Row="0" />
<StackPanel Classes="WorkspaceToolbar"
Orientation="Horizontal"
Spacing="3"
Grid.Column="1"
Grid.Row="0">
<Button>
<inkforge:FluentSymbolIcon Symbol="document_add" />
</Button>
<Button>
<inkforge:FluentSymbolIcon Symbol="arrow_clockwise" />
</Button>
<Button>
<inkforge:FluentSymbolIcon Symbol="subtract_square_multiple" />
</Button>
<!-- <StackPanel.Styles>
<Style Selector="#ToolBar > :is(TemplatedControl)">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Padding"
Value="1" />
<Setter Property="VerticalAlignment"
Value="Center" />
</Style>
<Style Selector="#FilesView:not(:pointerover) StackPanel">
<Setter Property="IsVisible"
Value="False" />
</Style>
</StackPanel.Styles> -->
</StackPanel>
<TreeView Grid.ColumnSpan="2"
Grid.Row="1" />
</Grid>
</UserControl>

View file

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace InkForge.Desktop.Views.Workspaces;
public partial class WorkspaceViewModel : UserControl
{
public WorkspaceViewModel()
{
InitializeComponent();
}
}

View file

@ -0,0 +1,33 @@
<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

@ -0,0 +1,16 @@
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>();
}
}