Remove .Common-project
Currently of no use
This commit is contained in:
parent
232231d20d
commit
b1d3ec73c9
31 changed files with 16020 additions and 109 deletions
|
|
@ -10,9 +10,13 @@
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Fonts.Inter" 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.ReactiveUI" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Themes.Fluent" 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.Avalonia" Version="11.0.0.5" />
|
||||||
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="$(DotNetVersion)" />
|
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="$(DotNetVersion)" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="$(DotNetVersion)" />
|
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="$(DotNetVersion)" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="$(DotNetVersion)" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="$(DotNetVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "design", "design", "{C78684
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkForge.Migrations", "design\InkForge.Migrations\InkForge.Migrations.csproj", "{8DF3397E-2717-49F0-9592-82ABE9327A73}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkForge.Migrations", "design\InkForge.Migrations\InkForge.Migrations.csproj", "{8DF3397E-2717-49F0-9592-82ABE9327A73}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkForge.Common", "app\InkForge.Common\InkForge.Common.csproj", "{DCE2DCD6-D15C-4F0D-8D7F-22FF82F62F6F}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -66,10 +64,6 @@ Global
|
||||||
{8DF3397E-2717-49F0-9592-82ABE9327A73}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8DF3397E-2717-49F0-9592-82ABE9327A73}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{8DF3397E-2717-49F0-9592-82ABE9327A73}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{8DF3397E-2717-49F0-9592-82ABE9327A73}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{8DF3397E-2717-49F0-9592-82ABE9327A73}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8DF3397E-2717-49F0-9592-82ABE9327A73}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{DCE2DCD6-D15C-4F0D-8D7F-22FF82F62F6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{DCE2DCD6-D15C-4F0D-8D7F-22FF82F62F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{DCE2DCD6-D15C-4F0D-8D7F-22FF82F62F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{DCE2DCD6-D15C-4F0D-8D7F-22FF82F62F6F}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{DD595B76-5FDE-4C37-822E-CB58BBB02C8C} = {C73D8E17-EA0A-4206-91D4-9E5BD63B3DB0}
|
{DD595B76-5FDE-4C37-822E-CB58BBB02C8C} = {C73D8E17-EA0A-4206-91D4-9E5BD63B3DB0}
|
||||||
|
|
@ -78,6 +72,5 @@ Global
|
||||||
{5AFA8AD9-9230-4218-BBFD-BD75F1E752DC} = {84CBD204-9573-4472-9334-68FB360BD6ED}
|
{5AFA8AD9-9230-4218-BBFD-BD75F1E752DC} = {84CBD204-9573-4472-9334-68FB360BD6ED}
|
||||||
{F8A7563F-2647-4623-88E7-470D20F25E93} = {A9F8087F-F148-47A5-94AE-F7B6E1D33096}
|
{F8A7563F-2647-4623-88E7-470D20F25E93} = {A9F8087F-F148-47A5-94AE-F7B6E1D33096}
|
||||||
{8DF3397E-2717-49F0-9592-82ABE9327A73} = {C7868400-84D7-45C5-B594-C30777EE5191}
|
{8DF3397E-2717-49F0-9592-82ABE9327A73} = {C7868400-84D7-45C5-B594-C30777EE5191}
|
||||||
{DCE2DCD6-D15C-4F0D-8D7F-22FF82F62F6F} = {84CBD204-9573-4472-9334-68FB360BD6ED}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
x:Class="InkForge.Desktop.App"
|
x:Class="InkForge.Desktop.App"
|
||||||
RequestedThemeVariant="Default">
|
RequestedThemeVariant="Default">
|
||||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
<FluentTheme />
|
||||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|
||||||
|
<Application.Resources>
|
||||||
|
<FontFamily x:Key="FluentSystemIcons-Filled">/Assets/Fonts#FluentSystemIcons-Filled</FontFamily>
|
||||||
|
<FontFamily x:Key="FluentSystemIcons-Regular">/Assets/Fonts#FluentSystemIcons-Regular</FontFamily>
|
||||||
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
|
|
@ -2,6 +2,7 @@ using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
using InkForge.Desktop.ViewModels;
|
using InkForge.Desktop.ViewModels;
|
||||||
|
|
||||||
|
|
@ -14,6 +15,11 @@ using ReactiveUI;
|
||||||
using Splat;
|
using Splat;
|
||||||
using Splat.Microsoft.Extensions.DependencyInjection;
|
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;
|
namespace InkForge.Desktop;
|
||||||
|
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
|
|
@ -54,6 +60,7 @@ public partial class App : Application
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
|
// This kills Avalonia VSCode Previewer.
|
||||||
var viewModel = ActivatorUtilities.GetServiceOrCreateInstance<AppViewModel>(ServiceProvider);
|
var viewModel = ActivatorUtilities.GetServiceOrCreateInstance<AppViewModel>(ServiceProvider);
|
||||||
var view = ViewLocator.Current.ResolveView(viewModel)!;
|
var view = ViewLocator.Current.ResolveView(viewModel)!;
|
||||||
view.ViewModel = viewModel;
|
view.ViewModel = viewModel;
|
||||||
|
|
|
||||||
7836
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Filled.json
Normal file
7836
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Filled.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Filled.ttf
Normal file
BIN
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Filled.ttf
Normal file
Binary file not shown.
7715
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Regular.json
Normal file
7715
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Regular.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Regular.ttf
Normal file
BIN
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons-Regular.ttf
Normal file
Binary file not shown.
1
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons.txt
Normal file
1
app/InkForge.Desktop/Assets/Fonts/FluentSystemIcons.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1.1.227
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
using InkForge.Data;
|
|
||||||
using InkForge.Desktop.Models;
|
|
||||||
using InkForge.Desktop.Services;
|
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace InkForge.Desktop.Controllers;
|
|
||||||
|
|
||||||
public class WorkspaceController : ReactiveObject
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private Workspace _workspace;
|
|
||||||
|
|
||||||
public Workspace Workspace
|
|
||||||
{
|
|
||||||
get => _workspace;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _workspace, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorkspaceController(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OpenWorkspace(string path, bool createFile = false)
|
|
||||||
{
|
|
||||||
if (await CreateWorkspace(path, createFile) is { } workspace)
|
|
||||||
{
|
|
||||||
Workspace = workspace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask<Workspace?> CreateWorkspace(string path, bool createFile)
|
|
||||||
{
|
|
||||||
FileInfo file = new(path);
|
|
||||||
if (!(createFile || file.Exists))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Directory!.Create();
|
|
||||||
var scope = _serviceProvider.CreateScope();
|
|
||||||
var scopeServiceProvider = scope.ServiceProvider;
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<WorkspaceContext>();
|
|
||||||
context.DbPath = path;
|
|
||||||
|
|
||||||
var db = scopeServiceProvider.GetRequiredService<NoteDbContext>();
|
|
||||||
await using (var transaction = await db.Database.BeginTransactionAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await db.Database.MigrateAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
await transaction.RollbackAsync().ConfigureAwait(false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
await transaction.CommitAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new(scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
175
app/InkForge.Desktop/Controls/FluentSymbolIcon.cs
Normal file
175
app/InkForge.Desktop/Controls/FluentSymbolIcon.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
using System.IO.Hashing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Media.TextFormatting;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
|
||||||
|
namespace InkForge.Desktop.Controls;
|
||||||
|
|
||||||
|
public class FluentSymbolIcon : IconElement
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<int> IconSizeProperty
|
||||||
|
= AvaloniaProperty.Register<FluentSymbolIcon, int>(nameof(IconSize), defaultValue: 20);
|
||||||
|
public static readonly StyledProperty<FontIconStyle> IconStyleProperty
|
||||||
|
= AvaloniaProperty.Register<FluentSymbolIcon, FontIconStyle>(nameof(IconStyle));
|
||||||
|
public static readonly StyledProperty<string> SymbolProperty
|
||||||
|
= AvaloniaProperty.Register<FluentSymbolIcon, string>(nameof(Symbol));
|
||||||
|
private static readonly Dictionary<(FontIconStyle, uint Key), string> _glyphCache = [];
|
||||||
|
private static readonly Dictionary<FontIconStyle, FontFamily> _iconFonts = [];
|
||||||
|
private TextLayout? _textLayout;
|
||||||
|
|
||||||
|
public int IconSize
|
||||||
|
{
|
||||||
|
get => GetValue(IconSizeProperty);
|
||||||
|
set => SetValue(IconSizeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontIconStyle IconStyle
|
||||||
|
{
|
||||||
|
get => GetValue(IconStyleProperty);
|
||||||
|
set => SetValue(IconStyleProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Symbol
|
||||||
|
{
|
||||||
|
get => GetValue(SymbolProperty);
|
||||||
|
set => SetValue(SymbolProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FluentSymbolIcon()
|
||||||
|
{
|
||||||
|
AffectsMeasure<FluentSymbolIcon>([
|
||||||
|
IconStyleProperty,
|
||||||
|
SymbolProperty,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Render(DrawingContext context)
|
||||||
|
{
|
||||||
|
_textLayout ??= GenerateText();
|
||||||
|
|
||||||
|
var dstRect = new Rect(Bounds.Size);
|
||||||
|
using (context.PushClip(dstRect))
|
||||||
|
{
|
||||||
|
var pt = new Point(dstRect.Center.X - _textLayout.Width / 2,
|
||||||
|
dstRect.Center.Y - _textLayout.Height / 2);
|
||||||
|
_textLayout.Draw(context, pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
|
{
|
||||||
|
_textLayout ??= GenerateText();
|
||||||
|
|
||||||
|
return new Size(_textLayout.Width, _textLayout.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
switch (change.Property.Name)
|
||||||
|
{
|
||||||
|
case nameof(IconSize):
|
||||||
|
case nameof(IconStyle):
|
||||||
|
case nameof(Symbol):
|
||||||
|
InvalidateSymbolLayout();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMeasureInvalidated()
|
||||||
|
{
|
||||||
|
_textLayout?.Dispose();
|
||||||
|
_textLayout = null;
|
||||||
|
|
||||||
|
base.OnMeasureInvalidated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextLayout GenerateText()
|
||||||
|
{
|
||||||
|
var glyph = GetIconGlyph(this);
|
||||||
|
|
||||||
|
if (!_iconFonts.TryGetValue(IconStyle, out var fontFamily))
|
||||||
|
{
|
||||||
|
_iconFonts[IconStyle] = fontFamily = new FontFamily($"avares://InkForge/Assets/Fonts#FluentSystemIcons-{IconStyle}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextLayout(glyph, new Typeface(fontFamily), FontSize, Foreground, TextAlignment.Left);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InvalidateSymbolLayout()
|
||||||
|
{
|
||||||
|
InvalidateMeasure();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetIconGlyph(FluentSymbolIcon icon)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<char> glyphKey = $"ic_fluent_{icon.Symbol}_{icon.IconSize:0}";
|
||||||
|
var hash = Hash(glyphKey, icon.IconStyle);
|
||||||
|
if (!_glyphCache.TryGetValue((icon.IconStyle, hash), out var glyph))
|
||||||
|
{
|
||||||
|
glyph = LoadIcons(icon.IconStyle, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(glyph))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint Hash(ReadOnlySpan<char> key, FontIconStyle iconStyle)
|
||||||
|
{
|
||||||
|
return XxHash32.HashToUInt32(MemoryMarshal.AsBytes(key), (int)iconStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string LoadIcons(FontIconStyle iconStyle, uint key)
|
||||||
|
{
|
||||||
|
Optional<string>? glyph = Optional<string>.Empty;
|
||||||
|
using (var stream = AssetLoader.Open(new($"avares://InkForge/Assets/Fonts/FluentSystemIcons-{iconStyle}.json")))
|
||||||
|
using (var document = JsonDocument.Parse(stream))
|
||||||
|
{
|
||||||
|
foreach (var element in document.RootElement.EnumerateObject())
|
||||||
|
{
|
||||||
|
if (element.Value.ValueKind is not JsonValueKind.Number)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeSeparator = element.Name.LastIndexOf('_');
|
||||||
|
if (typeSeparator == -1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<char> elementKey = element.Name.AsSpan(0, typeSeparator);
|
||||||
|
var hash = Hash(elementKey, iconStyle);
|
||||||
|
var elementGlyph = char.ConvertFromUtf32(element.Value.GetInt32())!;
|
||||||
|
if (hash == key)
|
||||||
|
{
|
||||||
|
glyph = glyph switch
|
||||||
|
{
|
||||||
|
{ HasValue: false } => elementGlyph,
|
||||||
|
_ => default(Optional<string>?)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_glyphCache[(iconStyle, hash)] = elementGlyph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return glyph?.GetValueOrDefault() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FontIconStyle
|
||||||
|
{
|
||||||
|
Regular, Filled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
using InkForge.Desktop.Services;
|
|
||||||
using InkForge.Data;
|
using InkForge.Data;
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
using SmartFormat;
|
using SmartFormat;
|
||||||
|
using InkForge.Desktop.Data.Options;
|
||||||
|
|
||||||
namespace InkForge.Desktop.Data;
|
namespace InkForge.Desktop.Data;
|
||||||
|
|
||||||
public class NoteDbContextFactory(WorkspaceContext context, IConfiguration configuration) : IDbContextFactory<NoteDbContext>
|
public class NoteDbContextFactory(LocalWorkspaceOptions options, IConfiguration configuration) : IDbContextFactory<NoteDbContext>
|
||||||
{
|
{
|
||||||
private string? _connectionString;
|
private string? _connectionString;
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ public class NoteDbContextFactory(WorkspaceContext context, IConfiguration confi
|
||||||
{
|
{
|
||||||
_connectionString ??= Smart.Format(configuration.GetConnectionString("DefaultConnection")!, new
|
_connectionString ??= Smart.Format(configuration.GetConnectionString("DefaultConnection")!, new
|
||||||
{
|
{
|
||||||
WorkspaceFile = context.DbPath
|
WorkspaceFile = options.DbPath
|
||||||
});
|
});
|
||||||
|
|
||||||
DbContextOptionsBuilder<NoteDbContext> builder = new();
|
DbContextOptionsBuilder<NoteDbContext> builder = new();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace InkForge.Desktop.Data.Options;
|
||||||
|
|
||||||
|
public class LocalWorkspaceOptions
|
||||||
|
{
|
||||||
|
public string DbPath { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" />
|
<PackageReference Include="Microsoft.Extensions.Http" />
|
||||||
<PackageReference Include="SmartFormat" />
|
<PackageReference Include="SmartFormat" />
|
||||||
<PackageReference Include="Splat.Microsoft.Extensions.DependencyInjection" />
|
<PackageReference Include="Splat.Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<PackageReference Include="System.IO.Hashing" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -30,6 +31,10 @@
|
||||||
<ProjectReference Include="..\..\shared\migrations\InkForge.Sqlite\InkForge.Sqlite.csproj" />
|
<ProjectReference Include="..\..\shared\migrations\InkForge.Sqlite\InkForge.Sqlite.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Properties\appsettings.json" />
|
<EmbeddedResource Include="Properties\appsettings.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
92
app/InkForge.Desktop/Managers/WorkspaceManager.cs
Normal file
92
app/InkForge.Desktop/Managers/WorkspaceManager.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
using InkForge.Data;
|
||||||
|
using InkForge.Desktop.Data.Options;
|
||||||
|
using InkForge.Desktop.Models;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace InkForge.Desktop.Managers;
|
||||||
|
|
||||||
|
public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider = serviceProvider;
|
||||||
|
private Workspace? _workspace;
|
||||||
|
|
||||||
|
public Workspace? Workspace
|
||||||
|
{
|
||||||
|
get => _workspace;
|
||||||
|
private set => this.RaiseAndSetIfChanged(ref _workspace, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CloseWorkspace()
|
||||||
|
{
|
||||||
|
_workspace?.Dispose();
|
||||||
|
Workspace = null;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OpenWorkspace(string path, bool createFile = false)
|
||||||
|
{
|
||||||
|
await CloseWorkspace().ConfigureAwait(false);
|
||||||
|
if (await CreateLocalWorkspace(path, createFile).ConfigureAwait(false) is { } workspace)
|
||||||
|
{
|
||||||
|
Workspace = workspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ValueTask<Workspace?> CreateLocalWorkspace(string path, bool createFile)
|
||||||
|
{
|
||||||
|
FileInfo file = new(path);
|
||||||
|
if (!(createFile || file.Exists))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Directory!.Create();
|
||||||
|
IServiceScope? scope = null;
|
||||||
|
IWorkspaceAccessor workspaceAccessor;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
scope = _serviceProvider.CreateScope();
|
||||||
|
var serviceProvider = scope.ServiceProvider;
|
||||||
|
var options = serviceProvider.GetRequiredService<LocalWorkspaceOptions>();
|
||||||
|
options.DbPath = path;
|
||||||
|
|
||||||
|
workspaceAccessor = serviceProvider.GetRequiredService<IWorkspaceAccessor>();
|
||||||
|
workspaceAccessor.Workspace = new Workspace(scope)
|
||||||
|
{
|
||||||
|
Name = Path.GetFileNameWithoutExtension(file.Name),
|
||||||
|
Options = options,
|
||||||
|
};
|
||||||
|
|
||||||
|
var dbFactory = serviceProvider.GetRequiredService<IDbContextFactory<NoteDbContext>>();
|
||||||
|
await using (var dbContext = dbFactory.CreateDbContext())
|
||||||
|
{
|
||||||
|
var db = dbContext.Database;
|
||||||
|
await using var transaction = await db.BeginTransactionAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await db.MigrateAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Show Error through TopLevels.ActiveTopLevel
|
||||||
|
await transaction.RollbackAsync().ConfigureAwait(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await transaction.CommitAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
scope?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return workspaceAccessor.Workspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace InkForge.Desktop.MarkupExtensions;
|
||||||
|
|
||||||
|
public class FluentSymbolIconExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
using InkForge.Desktop.Controllers;
|
|
||||||
using InkForge.Desktop.Data;
|
|
||||||
using InkForge.Desktop.Services;
|
|
||||||
using InkForge.Desktop.ViewModels;
|
|
||||||
using InkForge.Data;
|
using InkForge.Data;
|
||||||
|
using InkForge.Desktop.Data;
|
||||||
|
using InkForge.Desktop.Data.Options;
|
||||||
|
using InkForge.Desktop.Managers;
|
||||||
|
using InkForge.Desktop.Models;
|
||||||
|
using InkForge.Desktop.ViewModels;
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
@ -18,13 +19,13 @@ public static class InkForgeServiceCollections
|
||||||
{
|
{
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
|
|
||||||
|
services.AddScoped<IWorkspaceAccessor, WorkspaceAccessor>();
|
||||||
services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>();
|
services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>();
|
||||||
services.AddScoped(s => s.GetRequiredService<IDbContextFactory<NoteDbContext>>().CreateDbContext());
|
|
||||||
|
|
||||||
services.AddScoped<WorkspaceContext>();
|
services.AddScoped<LocalWorkspaceOptions>();
|
||||||
|
|
||||||
services.AddSingleton<LandingViewModel>();
|
services.AddSingleton<LandingViewModel>();
|
||||||
services.AddSingleton<WorkspaceController>();
|
services.AddSingleton<WorkspaceManager>();
|
||||||
|
|
||||||
Locator.CurrentMutable.RegisterViewsForViewModels(typeof(InkForgeServiceCollections).Assembly);
|
Locator.CurrentMutable.RegisterViewsForViewModels(typeof(InkForgeServiceCollections).Assembly);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class TypeFactory<TFactory, T>
|
public static class TypeFactory<TArguments, T>
|
||||||
where TFactory : struct, IObjectParameters<TFactory>
|
where TArguments : IFactoryArguments<TArguments>
|
||||||
{
|
{
|
||||||
private static ObjectFactory<T>? s_objectFactory;
|
private static ObjectFactory<T>? s_objectFactory;
|
||||||
|
|
||||||
public static T Create(in TFactory factory, IServiceProvider serviceProvider)
|
public static T Create(IServiceProvider serviceProvider, in TArguments factory)
|
||||||
{
|
{
|
||||||
s_objectFactory ??= ActivatorUtilities.CreateFactory<T>(TFactory.Types);
|
s_objectFactory ??= ActivatorUtilities.CreateFactory<T>(TArguments.Types);
|
||||||
return s_objectFactory(serviceProvider, (object[])factory);
|
return s_objectFactory(serviceProvider, (object[])factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IObjectParameters<T>
|
public interface IFactoryArguments<T>
|
||||||
where T : struct, IObjectParameters<T>
|
where T : IFactoryArguments<T>
|
||||||
{
|
{
|
||||||
abstract static Type[] Types { get; }
|
abstract static Type[] Types { get; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,45 @@
|
||||||
|
using InkForge.Data;
|
||||||
|
using InkForge.Desktop.Data.Options;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace InkForge.Desktop.Models;
|
namespace InkForge.Desktop.Models;
|
||||||
|
|
||||||
public class Workspace(IServiceScope scope)
|
public sealed class Workspace : IDisposable
|
||||||
{
|
{
|
||||||
public IServiceProvider ServiceProvider => scope.ServiceProvider;
|
private readonly IDbContextFactory<NoteDbContext> _dbContextFactory;
|
||||||
|
private bool _disposedValue;
|
||||||
|
private IServiceScope? _scope;
|
||||||
|
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
public LocalWorkspaceOptions Options { get; set; } = default!;
|
||||||
|
|
||||||
|
public IServiceProvider Services => _scope!.ServiceProvider;
|
||||||
|
|
||||||
|
public Workspace(IServiceScope scope)
|
||||||
|
{
|
||||||
|
_scope = scope;
|
||||||
|
_dbContextFactory = Services.GetRequiredService<IDbContextFactory<NoteDbContext>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposedValue)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
_scope!.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_scope = null;
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
app/InkForge.Desktop/Models/WorkspaceAccessor.cs
Normal file
12
app/InkForge.Desktop/Models/WorkspaceAccessor.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace InkForge.Desktop.Models
|
||||||
|
{
|
||||||
|
public interface IWorkspaceAccessor
|
||||||
|
{
|
||||||
|
Workspace? Workspace { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WorkspaceAccessor : IWorkspaceAccessor
|
||||||
|
{
|
||||||
|
public Workspace? Workspace { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Metadata;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
|
@ -7,12 +9,14 @@ public class TopLevels
|
||||||
{
|
{
|
||||||
public static readonly AttachedProperty<object?> RegisterProperty
|
public static readonly AttachedProperty<object?> RegisterProperty
|
||||||
= AvaloniaProperty.RegisterAttached<TopLevels, Visual, object?>("Register");
|
= AvaloniaProperty.RegisterAttached<TopLevels, Visual, object?>("Register");
|
||||||
|
|
||||||
private static readonly Dictionary<object, Visual> RegistrationMapper = [];
|
private static readonly Dictionary<object, Visual> RegistrationMapper = [];
|
||||||
|
|
||||||
|
public static TopLevel? ActiveTopLevel { get; private set; }
|
||||||
|
|
||||||
static TopLevels()
|
static TopLevels()
|
||||||
{
|
{
|
||||||
RegisterProperty.Changed.AddClassHandler<Visual>(RegisterChanged);
|
RegisterProperty.Changed.AddClassHandler<Visual>(RegisterChanged);
|
||||||
|
WindowBase.IsActiveProperty.Changed.Subscribe(WindowActiveChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object? GetRegister(AvaloniaObject element)
|
public static object? GetRegister(AvaloniaObject element)
|
||||||
|
|
@ -51,4 +55,14 @@ public class TopLevels
|
||||||
RegistrationMapper.Add(e.NewValue, sender);
|
RegistrationMapper.Add(e.NewValue, sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void WindowActiveChanged(AvaloniaPropertyChangedEventArgs<bool> e)
|
||||||
|
{
|
||||||
|
ActiveTopLevel = (e.GetOldAndNewValue<bool>(), e.Sender) switch
|
||||||
|
{
|
||||||
|
((false, true), TopLevel topLevel) => topLevel,
|
||||||
|
((true, false), { } topLevel) when topLevel == ActiveTopLevel => null,
|
||||||
|
_ => ActiveTopLevel,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using InkForge.Desktop.Controllers;
|
using InkForge.Desktop.Managers;
|
||||||
using InkForge.Desktop.Models;
|
using InkForge.Desktop.Models;
|
||||||
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
@ -8,7 +8,7 @@ namespace InkForge.Desktop.ViewModels;
|
||||||
public class AppViewModel : ReactiveObject
|
public class AppViewModel : ReactiveObject
|
||||||
{
|
{
|
||||||
private readonly LandingViewModel _landingViewModel;
|
private readonly LandingViewModel _landingViewModel;
|
||||||
private readonly WorkspaceController _workspace;
|
private readonly WorkspaceManager _workspace;
|
||||||
private object _view;
|
private object _view;
|
||||||
|
|
||||||
public object View
|
public object View
|
||||||
|
|
@ -17,7 +17,7 @@ public class AppViewModel : ReactiveObject
|
||||||
set => this.RaiseAndSetIfChanged(ref _view, value);
|
set => this.RaiseAndSetIfChanged(ref _view, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppViewModel(WorkspaceController workspace, LandingViewModel landingViewModel)
|
public AppViewModel(WorkspaceManager workspace, LandingViewModel landingViewModel)
|
||||||
{
|
{
|
||||||
_workspace = workspace;
|
_workspace = workspace;
|
||||||
_landingViewModel = landingViewModel;
|
_landingViewModel = landingViewModel;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ using System.Reactive;
|
||||||
|
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
|
||||||
using InkForge.Desktop.Controllers;
|
using InkForge.Desktop.Managers;
|
||||||
using InkForge.Desktop.Services;
|
using InkForge.Desktop.Services;
|
||||||
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
@ -13,7 +13,7 @@ namespace InkForge.Desktop.ViewModels;
|
||||||
public class LandingViewModel : ReactiveObject
|
public class LandingViewModel : ReactiveObject
|
||||||
{
|
{
|
||||||
private ReadOnlyObservableCollection<RecentItemViewModel> _recentItems;
|
private ReadOnlyObservableCollection<RecentItemViewModel> _recentItems;
|
||||||
private readonly WorkspaceController _workspaceController;
|
private readonly WorkspaceManager _workspaceController;
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> CreateNew { get; }
|
public ReactiveCommand<Unit, Unit> CreateNew { get; }
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ public class LandingViewModel : ReactiveObject
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<RecentItemViewModel> RecentItems => _recentItems;
|
public ReadOnlyObservableCollection<RecentItemViewModel> RecentItems => _recentItems;
|
||||||
|
|
||||||
public LandingViewModel(WorkspaceController workspaceController)
|
public LandingViewModel(WorkspaceManager workspaceController)
|
||||||
{
|
{
|
||||||
_workspaceController = workspaceController;
|
_workspaceController = workspaceController;
|
||||||
CreateNew = ReactiveCommand.CreateFromTask(OnCreateNew);
|
CreateNew = ReactiveCommand.CreateFromTask(OnCreateNew);
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,13 @@ namespace InkForge.Desktop.ViewModels;
|
||||||
public class WorkspaceViewModel : ReactiveObject
|
public class WorkspaceViewModel : ReactiveObject
|
||||||
{
|
{
|
||||||
private readonly Workspace _workspace;
|
private readonly Workspace _workspace;
|
||||||
|
private readonly ObservableAsPropertyHelper<string> _workspaceNameProperty;
|
||||||
|
|
||||||
|
public string WorkspaceName => _workspaceNameProperty.Value;
|
||||||
|
|
||||||
public WorkspaceViewModel(Workspace workspace)
|
public WorkspaceViewModel(Workspace workspace)
|
||||||
{
|
{
|
||||||
_workspace = workspace;
|
_workspace = workspace;
|
||||||
|
_workspaceNameProperty = this.WhenAnyValue(v => v._workspace.Name).ToProperty(this, nameof(WorkspaceName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:reactiveui="http://reactiveui.net"
|
xmlns:reactiveui="http://reactiveui.net"
|
||||||
xmlns:vm="using:InkForge.Desktop.ViewModels"
|
xmlns:vm="using:InkForge.Desktop.ViewModels"
|
||||||
xmlns:services="using:InkForge.Desktop.Services"
|
xmlns:inkforge="app:InkForge"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
x:Class="InkForge.Desktop.Views.LandingView"
|
x:Class="InkForge.Desktop.Views.LandingView"
|
||||||
x:DataType="vm:LandingViewModel"
|
x:DataType="vm:LandingViewModel"
|
||||||
services:TopLevels.Register="{CompiledBinding}">
|
inkforge:TopLevels.Register="{CompiledBinding}">
|
||||||
<Grid RowDefinitions="Auto, *, Auto">
|
<Grid RowDefinitions="Auto, *, Auto">
|
||||||
<Label Content="Open Recent"
|
<Label Content="Open Recent"
|
||||||
Grid.Row="0" />
|
Grid.Row="0" />
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,15 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:reactiveui="http://reactiveui.net"
|
xmlns:reactiveui="http://reactiveui.net"
|
||||||
|
xmlns:inkforge="app:InkForge"
|
||||||
xmlns:vm="using:InkForge.Desktop.ViewModels"
|
xmlns:vm="using:InkForge.Desktop.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="800"
|
Width="800"
|
||||||
d:DesignHeight="450"
|
Height="450"
|
||||||
x:Class="InkForge.Desktop.Views.MainWindow"
|
x:Class="InkForge.Desktop.Views.MainWindow"
|
||||||
x:DataType="vm:AppViewModel"
|
x:DataType="vm:AppViewModel"
|
||||||
Title="MainWindow">
|
Title="MainWindow"
|
||||||
|
inkforge:TopLevels.Register="{CompiledBinding}">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<NativeMenuBar />
|
<NativeMenuBar />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
using InkForge.Desktop.ViewModels;
|
using InkForge.Desktop.ViewModels;
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,67 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:reactiveui="http://reactiveui.net"
|
xmlns:reactiveui="http://reactiveui.net"
|
||||||
|
xmlns:inkforge="app:InkForge"
|
||||||
xmlns:vm="using:InkForge.Desktop.ViewModels"
|
xmlns:vm="using:InkForge.Desktop.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
x:Class="InkForge.Desktop.Views.WorkspaceView"
|
x:Class="InkForge.Desktop.Views.WorkspaceView"
|
||||||
x:DataType="vm:WorkspaceViewModel">
|
x:DataType="vm:WorkspaceViewModel"
|
||||||
Welcome to Avalonia!
|
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>
|
</UserControl>
|
||||||
|
|
@ -8,4 +8,11 @@ Performs OpenAPI calls to Sync server.
|
||||||
|
|
||||||
*Consideration*: Allow for syncing to local backend.
|
*Consideration*: Allow for syncing to local backend.
|
||||||
|
|
||||||
|
## Technical
|
||||||
|
|
||||||
|
Figure out a way to get navigation/commands relative to the window they are in.<br>
|
||||||
|
I.e. make Windows scoped, then get a shell-object in each Window, which then
|
||||||
|
consumes a Menu-service, which navigates the shell-object tree to find eligible
|
||||||
|
menu-objects to present.
|
||||||
|
|
||||||
## Research
|
## Research
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue