diff --git a/src/ImeSense.ShaderPlayground/App.axaml b/src/ImeSense.ShaderPlayground/App.axaml
index 7890a52..0284278 100644
--- a/src/ImeSense.ShaderPlayground/App.axaml
+++ b/src/ImeSense.ShaderPlayground/App.axaml
@@ -10,6 +10,7 @@
+
diff --git a/src/ImeSense.ShaderPlayground/App.axaml.cs b/src/ImeSense.ShaderPlayground/App.axaml.cs
index 825d742..d3ae78f 100644
--- a/src/ImeSense.ShaderPlayground/App.axaml.cs
+++ b/src/ImeSense.ShaderPlayground/App.axaml.cs
@@ -34,10 +34,19 @@ public override void Initialize() =>
public override void OnFrameworkInitializationCompleted() {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
- desktop.MainWindow = _serviceProvider
- .GetRequiredService();
- desktop.MainWindow.DataContext = _serviceProvider
- .GetRequiredService();
+ var mainViewModel = _serviceProvider.GetRequiredService();
+
+ var mainWindow = new MainWindow {
+ DataContext = mainViewModel,
+ };
+ mainWindow.Closing += (_, _) => {
+ mainViewModel.CloseLayout();
+ };
+
+ desktop.MainWindow = mainWindow;
+ desktop.Exit += (_, _) => {
+ mainViewModel.CloseLayout();
+ };
}
base.OnFrameworkInitializationCompleted();
diff --git a/src/ImeSense.ShaderPlayground/ImeSense.ShaderPlayground.csproj b/src/ImeSense.ShaderPlayground/ImeSense.ShaderPlayground.csproj
index a620fb7..a757a4e 100644
--- a/src/ImeSense.ShaderPlayground/ImeSense.ShaderPlayground.csproj
+++ b/src/ImeSense.ShaderPlayground/ImeSense.ShaderPlayground.csproj
@@ -32,6 +32,8 @@
+
+
diff --git a/src/ImeSense.ShaderPlayground/ViewModels/AppFactory.cs b/src/ImeSense.ShaderPlayground/ViewModels/AppFactory.cs
new file mode 100644
index 0000000..bff6e79
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/ViewModels/AppFactory.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+
+using Dock.Avalonia.Controls;
+using Dock.Model.Controls;
+using Dock.Model.Core;
+using Dock.Model.ReactiveUI;
+using Dock.Model.ReactiveUI.Controls;
+
+using ImeSense.ShaderPlayground.ViewModels.Docks;
+using ImeSense.ShaderPlayground.ViewModels.Documents;
+using ImeSense.ShaderPlayground.ViewModels.Toolbars;
+
+namespace ImeSense.ShaderPlayground.ViewModels;
+
+public class AppFactory : Factory {
+ private IRootDock? _rootDock;
+
+ private IDocumentDock? _shaderDock;
+
+ private ITool? _viewportToolbar;
+ private ITool? _logToolbar;
+
+ public override IDocumentDock CreateDocumentDock() {
+ return new ShaderDocumentDock();
+ }
+
+ public override IRootDock CreateLayout() {
+ var untitledShaderViewModel = new ShaderViewModel {
+ Title = "Untitled",
+ };
+
+ var viewportViewModel = new ViewportViewModel {
+ Id = "Viewport",
+ Title = "Viewport",
+ };
+ var logViewModel = new LogViewModel {
+ Id = "Log",
+ Title = "Log",
+ };
+
+ // Tabs
+ var documentDock = new ShaderDocumentDock() {
+ Id = "Shaders",
+ Title = "Shaders",
+ IsCollapsable = false,
+ Proportion = double.NaN,
+ ActiveDockable = untitledShaderViewModel,
+ VisibleDockables = CreateList(untitledShaderViewModel),
+ CanCreateDocument = false,
+ };
+
+ var tools = new ProportionalDock {
+ Proportion = 0.5,
+ Orientation = Orientation.Vertical,
+ VisibleDockables = CreateList(
+ new ToolDock {
+ ActiveDockable = viewportViewModel,
+ VisibleDockables = CreateList(viewportViewModel),
+ Alignment = Alignment.Right,
+ GripMode = GripMode.Visible,
+ },
+ new ProportionalDockSplitter(),
+ new ToolDock {
+ ActiveDockable = logViewModel,
+ VisibleDockables = CreateList(logViewModel),
+ Alignment = Alignment.Right,
+ GripMode = GripMode.Visible,
+ }
+ ),
+ };
+
+ var windowLayout = CreateRootDock();
+ windowLayout.Title = "Default";
+ windowLayout.IsCollapsable = false;
+
+ // Content
+ var windowLayoutContent = new ProportionalDock {
+ Orientation = Orientation.Horizontal,
+ IsCollapsable = false,
+ VisibleDockables = CreateList(
+ documentDock,
+ new ProportionalDockSplitter(),
+ tools
+ ),
+ };
+ windowLayout.VisibleDockables = CreateList(windowLayoutContent);
+ windowLayout.ActiveDockable = windowLayoutContent;
+
+ // Root
+ var rootDock = CreateRootDock();
+ rootDock.IsCollapsable = false;
+ rootDock.VisibleDockables = CreateList(windowLayout);
+ rootDock.ActiveDockable = windowLayout;
+ rootDock.DefaultDockable = windowLayout;
+
+ _shaderDock = documentDock;
+ _rootDock = rootDock;
+ _viewportToolbar = viewportViewModel;
+ _logToolbar = logViewModel;
+
+ return rootDock;
+ }
+
+ public override void InitLayout(IDockable layout) {
+ ContextLocator = new Dictionary> {
+ ["Viewport"] = () => layout,
+ ["Log"] = () => layout
+ };
+
+ DockableLocator = new Dictionary> {
+ ["Root"] = () => _rootDock,
+ ["Files"] = () => _shaderDock,
+ ["Viewport"] = () => _viewportToolbar,
+ ["Log"] = () => _logToolbar,
+ };
+
+ HostWindowLocator = new Dictionary> {
+ [nameof(IDockWindow)] = () => new HostWindow(),
+ };
+
+ base.InitLayout(layout);
+ }
+}
diff --git a/src/ImeSense.ShaderPlayground/ViewModels/Docks/ShaderDocumentDock.cs b/src/ImeSense.ShaderPlayground/ViewModels/Docks/ShaderDocumentDock.cs
new file mode 100644
index 0000000..68dc31f
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/ViewModels/Docks/ShaderDocumentDock.cs
@@ -0,0 +1,26 @@
+using Dock.Model.ReactiveUI.Controls;
+
+using ImeSense.ShaderPlayground.ViewModels.Documents;
+
+using ReactiveUI;
+
+namespace ImeSense.ShaderPlayground.ViewModels.Docks;
+
+public class ShaderDocumentDock : DocumentDock {
+ private void CreateNewShader() {
+ if (!CanCreateDocument) {
+ return;
+ }
+
+ var shader = new ShaderViewModel {
+ Title = "Shader",
+ };
+ Factory?.AddDockable(this, shader);
+ Factory?.SetActiveDockable(shader);
+ Factory?.SetFocusedDockable(this, shader);
+ }
+
+ public ShaderDocumentDock() {
+ CreateDocument = ReactiveCommand.Create(CreateNewShader);
+ }
+}
diff --git a/src/ImeSense.ShaderPlayground/ViewModels/Documents/ShaderViewModel.cs b/src/ImeSense.ShaderPlayground/ViewModels/Documents/ShaderViewModel.cs
new file mode 100644
index 0000000..55e48f7
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/ViewModels/Documents/ShaderViewModel.cs
@@ -0,0 +1,6 @@
+using Dock.Model.ReactiveUI.Controls;
+
+namespace ImeSense.ShaderPlayground.ViewModels.Documents;
+
+public class ShaderViewModel : Document {
+}
diff --git a/src/ImeSense.ShaderPlayground/ViewModels/MainViewModel.cs b/src/ImeSense.ShaderPlayground/ViewModels/MainViewModel.cs
index 8420340..274cda8 100644
--- a/src/ImeSense.ShaderPlayground/ViewModels/MainViewModel.cs
+++ b/src/ImeSense.ShaderPlayground/ViewModels/MainViewModel.cs
@@ -1,4 +1,5 @@
-using System.Windows.Input;
+using Dock.Model.Controls;
+using Dock.Model.Core;
using ReactiveUI;
@@ -7,6 +8,14 @@ namespace ImeSense.ShaderPlayground.ViewModels;
public class MainViewModel : ReactiveObject {
private readonly MenuViewModel _menuViewModel;
+ private readonly IFactory? _factory;
+ private IRootDock? _layout;
+
+ public IRootDock? Layout {
+ get => _layout;
+ set => this.RaiseAndSetIfChanged(ref _layout, value);
+ }
+
public MainViewModel(MenuViewModel menuViewModel) {
_menuViewModel = menuViewModel;
@@ -16,27 +25,24 @@ public MainViewModel(MenuViewModel menuViewModel) {
MenuContext = _menuViewModel;
- Resolution = "800x600";
- Frame = "60";
+ _factory = new AppFactory();
- PlayCommand = ReactiveCommand.Create(() => {
- });
- StopCommand = ReactiveCommand.Create(() => {
- });
- SettingsCommand = ReactiveCommand.Create(() => {
- });
+ Layout = _factory?.CreateLayout();
+ if (Layout is { }) {
+ _factory?.InitLayout(Layout);
+ }
}
-#if DEBUG
public MainViewModel() {
_menuViewModel = null!;
MenuContext = null!;
- PlayCommand = null!;
- StopCommand = null!;
- SettingsCommand = null!;
}
-#endif
+
+ public void CloseLayout() {
+ Layout?.Close.Execute(null);
+ Layout = null;
+ }
private int _windowHeight;
@@ -74,22 +80,4 @@ public ReactiveObject MenuContext {
get => _menuContext;
init => this.RaiseAndSetIfChanged(ref _menuContext, value);
}
-
- private string _resolution = string.Empty;
-
- public string Resolution {
- get => _resolution;
- set => this.RaiseAndSetIfChanged(ref _resolution, value);
- }
-
- private string _frame = string.Empty;
-
- public string Frame {
- get => _frame;
- set => this.RaiseAndSetIfChanged(ref _frame, value);
- }
-
- public ICommand PlayCommand { get; private set; }
- public ICommand StopCommand { get; private set; }
- public ICommand SettingsCommand { get; private set; }
}
diff --git a/src/ImeSense.ShaderPlayground/ViewModels/Toolbars/LogViewModel.cs b/src/ImeSense.ShaderPlayground/ViewModels/Toolbars/LogViewModel.cs
new file mode 100644
index 0000000..e92de95
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/ViewModels/Toolbars/LogViewModel.cs
@@ -0,0 +1,6 @@
+using Dock.Model.ReactiveUI.Controls;
+
+namespace ImeSense.ShaderPlayground.ViewModels.Toolbars;
+
+public class LogViewModel : Tool {
+}
diff --git a/src/ImeSense.ShaderPlayground/ViewModels/Toolbars/ViewportViewModel.cs b/src/ImeSense.ShaderPlayground/ViewModels/Toolbars/ViewportViewModel.cs
new file mode 100644
index 0000000..666e75f
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/ViewModels/Toolbars/ViewportViewModel.cs
@@ -0,0 +1,39 @@
+using System.Windows.Input;
+
+using Dock.Model.ReactiveUI.Controls;
+
+using ReactiveUI;
+
+namespace ImeSense.ShaderPlayground.ViewModels.Toolbars;
+
+public class ViewportViewModel : Tool {
+ public ViewportViewModel() {
+ Resolution = "800x600";
+ Frame = "60";
+
+ PlayCommand = ReactiveCommand.Create(() => {
+ });
+ StopCommand = ReactiveCommand.Create(() => {
+ });
+ SettingsCommand = ReactiveCommand.Create(() => {
+ });
+ }
+
+ private string _resolution = string.Empty;
+
+ public string Resolution {
+ get => _resolution;
+ set => this.RaiseAndSetIfChanged(ref _resolution, value);
+ }
+
+ private string _frame = string.Empty;
+
+ public string Frame {
+ get => _frame;
+ set => this.RaiseAndSetIfChanged(ref _frame, value);
+ }
+
+ public ICommand PlayCommand { get; private set; }
+ public ICommand StopCommand { get; private set; }
+ public ICommand SettingsCommand { get; private set; }
+}
diff --git a/src/ImeSense.ShaderPlayground/Views/Documents/ShaderView.axaml b/src/ImeSense.ShaderPlayground/Views/Documents/ShaderView.axaml
new file mode 100644
index 0000000..4c83d7d
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/Views/Documents/ShaderView.axaml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ImeSense.ShaderPlayground/Views/Documents/ShaderView.axaml.cs b/src/ImeSense.ShaderPlayground/Views/Documents/ShaderView.axaml.cs
new file mode 100644
index 0000000..beb0e66
--- /dev/null
+++ b/src/ImeSense.ShaderPlayground/Views/Documents/ShaderView.axaml.cs
@@ -0,0 +1,336 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+
+using AvaloniaEdit;
+using AvaloniaEdit.CodeCompletion;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Editing;
+using AvaloniaEdit.Folding;
+using AvaloniaEdit.Indentation.CSharp;
+using AvaloniaEdit.Rendering;
+using AvaloniaEdit.TextMate;
+
+using TextMateSharp.Grammars;
+
+namespace ImeSense.ShaderPlayground.Views.Documents;
+
+public partial class ShaderView : UserControl {
+ private readonly TextEditor _textEditor;
+ private FoldingManager _foldingManager = null!;
+ private readonly TextMate.Installation _textMateInstallation;
+ private CompletionWindow _completionWindow = null!;
+ private OverloadInsightWindow _insightWindow = null!;
+ private Button _changeThemeButton;
+ private ComboBox _syntaxModeCombo;
+ private TextBlock _statusTextBlock;
+ private ElementGenerator _generator = new();
+ private RegistryOptions _registryOptions;
+ private int _currentTheme = (int) ThemeName.DarkPlus;
+
+ public ShaderView() {
+ InitializeComponent();
+
+ _textEditor = this.FindControl("Editor")!;
+ _textEditor.HorizontalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Visible;
+ _textEditor.Background = Brushes.Transparent;
+ _textEditor.ShowLineNumbers = true;
+
+ _textEditor.ContextMenu = new ContextMenu {
+ ItemsSource = new List