EditorPlugin: panel and menu command
Complete edited-level inspection example with symmetric cleanup.
This example adds a Scene audit panel and Docs scene audit item to the project's tools menu. The button/menu read the edited hierarchy root and its direct-child count. They do not change level content.
Plugin files
Create asset://addons/docs_scene_audit/. Save the code as SceneAuditPlugin.cs and the configuration as plugin.cfg beside it:
using Reckfell;
[Tool]
public partial class SceneAuditPlugin : EditorPlugin
{
private const string MenuName = "Docs scene audit";
private EditorDock? _dock;
private Button? _button;
private Label? _status;
private int _calls;
public override void _EnterTree()
{
_dock = new EditorDock { Name = "DocsSceneAuditDock", Title = "Scene audit", DefaultSlot = EditorDock.DockSlot.LeftUl };
var layout = new VBoxContainer { Name = "Layout" }; _dock.AddChild(layout);
_button = new Button { Name = "Inspect", Text = "Inspect current level" }; layout.AddChild(_button);
_status = new Label { Name = "Status", Text = "No request" }; layout.AddChild(_status);
_button.Pressed += InspectLevel;
AddDock(_dock);
AddToolMenuItem(MenuName, Callable.From(InspectLevel));
}
private void InspectLevel()
{
Entity? root = EditorInterface.Singleton.GetEditedSceneRoot();
_calls++;
_status!.Text = root is null ? "No level open" : $"Calls: {_calls}; children: {root.GetChildCount()}";
}
public override void _ExitTree()
{
RemoveToolMenuItem(MenuName);
if (_button is not null) _button.Pressed -= InspectLevel;
if (_dock is not null) { RemoveDock(_dock); _dock.QueueFree(); }
_dock = null; _button = null; _status = null;
}
}[plugin]
name="Docs scene audit"
description="Read-only current-level inspection example"
author="Reckfell documentation"
version="1.0.0"
script="SceneAuditPlugin.cs"Build the C# project and enable the addon: installation. [Tool] permits editor execution; partial supports SDK-generated bindings.
Check behavior
Open a level with one child. Press Inspect current level: expect Calls: 1; children: 1. Docs scene audit invokes the same handler and increases Calls to 2. With no edited hierarchy, it displays No level open instead of dereferencing null.
GetEditedSceneRoot() refers to edited content, not a running game's GetTree().CurrentScene. When editing a prefab it likewise returns the edited hierarchy root; the filename does not turn it into a different Entity type.
Symmetric cleanup
AddDock() registers an EditorDock. On disable, remove the menu command and button subscription, unregister with RemoveDock(), then free the panel and children with QueueFree(). Unregistration alone does not free an owned object.
Enable, disable, and enable again. Expect one panel, one command, and one call per press. A retained C# panel reference does not prove its native object still exists after disabling.
DefaultSlot specifies initial placement. Saved user layout can affect actual position; do not promise a fixed physical column. The example does not force the user's layout every frame.
Activation by folder, fixture-level reads, programmatic button/menu signals, deactivation, freeing, and duplicate-free reenablement were executed. Physical clicks, styling, saved layouts, scene-changing commands, and exports were not tested.