diff --git a/src/SharpFM/Editors/ScriptTextEditor.cs b/src/SharpFM/Editors/ScriptTextEditor.cs
new file mode 100644
index 0000000..dbfe71a
--- /dev/null
+++ b/src/SharpFM/Editors/ScriptTextEditor.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Avalonia;
+using Avalonia.Controls;
+using AvaloniaEdit;
+using AvaloniaEdit.TextMate;
+using SharpFM.Scripting;
+using SharpFM.Scripting.Editor;
+using SharpFM.ViewModels;
+using TextMateSharp.Grammars;
+
+namespace SharpFM.Editors;
+
+///
+/// A specialized for script clips. Owns its
+/// TextMate installation and so it
+/// can be dropped into a DataTemplate without any parent wiring —
+/// each instance self-installs on construction and tears down on detach.
+/// DataContext is expected to be a .
+///
+[ExcludeFromCodeCoverage]
+public class ScriptTextEditor : TextEditor
+{
+ // Avalonia 12 matches styles by exact type. Without this, a subclass of
+ // TextEditor gets no control template and renders blank — the editor's
+ // TextArea/TextView visual tree is never built.
+ protected override Type StyleKeyOverride => typeof(TextEditor);
+
+ private readonly TextMate.Installation _textMate;
+ private readonly ScriptEditorController _controller;
+
+ public ScriptTextEditor()
+ {
+ var registry = new RegistryOptions((ThemeName)(int)ThemeName.DarkPlus);
+ var fmScriptRegistry = new FmScriptRegistryOptions(registry);
+ _textMate = this.InstallTextMate(fmScriptRegistry);
+ _textMate.SetGrammar(FmScriptRegistryOptions.ScopeName);
+
+ _controller = new ScriptEditorController(this);
+ _controller.StatusMessageRaised += OnStatusMessageRaised;
+ }
+
+ protected override void OnDataContextChanged(EventArgs e)
+ {
+ base.OnDataContextChanged(e);
+
+ if (DataContext is not ScriptClipEditor clipEditor) return;
+
+ Document = clipEditor.Document;
+ _controller.AttachClipEditor(clipEditor);
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _controller.StatusMessageRaised -= OnStatusMessageRaised;
+ _controller.Dispose();
+ _textMate.Dispose();
+ }
+
+ private void OnStatusMessageRaised(object? sender, StatusMessageEventArgs e)
+ {
+ var window = TopLevel.GetTopLevel(this) as Window;
+ if (window?.DataContext is MainWindowViewModel vm)
+ vm.ShowStatusMessage(e.Message, e.IsError);
+ }
+}
diff --git a/src/SharpFM/MainWindow.axaml b/src/SharpFM/MainWindow.axaml
index e80e582..346e6da 100644
--- a/src/SharpFM/MainWindow.axaml
+++ b/src/SharpFM/MainWindow.axaml
@@ -6,6 +6,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SharpFM.ViewModels"
+ xmlns:editors="using:SharpFM.Editors"
xmlns:schema="using:SharpFM.Schema.Editor"
Icon="/Assets/noun-sharp-teeth-monster-4226695.small.png"
Title="SharpFM"
@@ -206,33 +207,40 @@
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SharpFM/MainWindow.axaml.cs b/src/SharpFM/MainWindow.axaml.cs
index 0927958..4e3002f 100644
--- a/src/SharpFM/MainWindow.axaml.cs
+++ b/src/SharpFM/MainWindow.axaml.cs
@@ -1,29 +1,21 @@
using System;
-using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
-using AvaloniaEdit;
-using AvaloniaEdit.TextMate;
using SharpFM.Diagnostics;
using SharpFM.Plugin;
using SharpFM.Plugin.UI;
using SharpFM.PluginManager;
-using SharpFM.Scripting;
using SharpFM.Services;
using SharpFM.ViewModels;
-using TextMateSharp.Grammars;
namespace SharpFM;
[ExcludeFromCodeCoverage]
public partial class MainWindow : Window
{
- private readonly RegistryOptions _registryOptions;
- private ScriptEditorController? _scriptController;
- private TextMate.Installation? _scriptTextMateInstallation;
private PluginService? _pluginService;
private PluginUIHost? _pluginHost;
private PluginConfigService? _pluginConfigService;
@@ -32,19 +24,6 @@ public MainWindow()
{
InitializeComponent();
- _registryOptions = new RegistryOptions((ThemeName)(int)ThemeName.DarkPlus);
-
- // Script editor
- var scriptEditor = this.FindControl("scriptEditor");
- if (scriptEditor != null)
- {
- var fmScriptRegistry = new FmScriptRegistryOptions(_registryOptions);
- _scriptTextMateInstallation = scriptEditor.InstallTextMate(fmScriptRegistry);
- _scriptTextMateInstallation.SetGrammar(FmScriptRegistryOptions.ScopeName);
- _scriptController = new ScriptEditorController(scriptEditor);
- _scriptController.StatusMessageRaised += OnScriptControllerStatusMessage;
- }
-
// "Manage Plugins..." menu item
var managePlugins = this.FindControl