| name | editor-panel-creation |
| description | Step-by-step workflow for creating new editor panels including interface design, DI registration, EditorLayer integration, and menu bar setup. Focuses on panel architecture and lifecycle, not UI component APIs. |
Editor Panel Creation
Overview
This skill provides comprehensive guidance for creating new ImGui-based editor panels, ensuring consistency with the engine's dependency injection architecture, UI styling standards, and editor integration patterns.
CRITICAL REQUIREMENT: All editor panels MUST use the UI infrastructure (Drawers, Elements, FieldEditors) instead of manual ImGui code. This ensures consistency, maintainability, and productivity across the entire editor.
UI Infrastructure Reference
The editor provides comprehensive UI infrastructure:
- UI Drawers: ButtonDrawer, ModalDrawer, TableDrawer, TreeDrawer, TextDrawer, LayoutDrawer, DragDropDrawer
- UI Elements: TextureDropTarget, AudioDropTarget, ComponentSelector, EntityContextMenu, PrefabManager
- Field Editors: IFieldEditor (non-generic, primarily for script inspector - rarely used in panels)
- Constants: EditorUIConstants for all sizing, spacing, and colors
When to Use
Invoke this skill when:
- Adding a new editor panel or tool window
- Creating asset browsers or managers
- Building debugging or profiling panels
- Implementing workflow tools for artists/designers
- Questions about editor panel architecture and lifecycle
- Integrating panels with the editor layer system
- Questions about DI registration and menu integration
Panel Creation Workflow
Follow these 6 steps to create a new panel. Use the Testing Checklist to verify completeness.
Step 1: Define Panel Interface
Location: Editor/Panels/
Pattern: All panels use interface-based design for testability and DI
Interface Template:
namespace Editor.Panels;
public interface IMyNewPanel
{
void OnImGuiRender();
bool IsOpen { get; set; }
}
Naming Convention:
- Interface:
I[PanelName]Panel or I[PanelName]
- Implementation:
[PanelName]Panel or [PanelName]
- Examples:
ISceneHierarchyPanel, IConsolePanel, ITileMapPanel
Step 2: Implement Panel Class
Location: Editor/Panels/
Guidelines:
- MUST use UI Drawers (ButtonDrawer, ModalDrawer, etc.) instead of manual ImGui code
- MUST use UI Elements (TextureDropTarget, ComponentSelector, etc.) for complex interactions
- Use constructor injection for ALL dependencies
- Use
EditorUIConstants for sizing, spacing, colors (Drawers handle this automatically)
- Maintain panel state in private fields
- Implement proper disposal if managing resources
- Follow ImGui immediate-mode UI patterns
Panel Template:
namespace Editor.Panels;
using Editor.UI;
using Editor.UI.Drawers;
using Editor.UI.Elements;
using Editor.UI.FieldEditors;
using ImGuiNET;
using Editor.Managers;
public class MyNewPanel(
ISceneManager sceneManager,
IProjectManager projectManager) : IMyNewPanel
{
private bool _isOpen = true;
private bool _showConfirmModal = false;
private string _filterText = string.Empty;
private int _selectedIndex = -1;
private readonly byte[] _nameBuffer = new byte[EditorUIConstants.MaxNameLength];
public bool IsOpen
{
get => _isOpen;
set => _isOpen = value;
}
public void OnImGuiRender()
{
if (!_isOpen)
return;
ImGuiWindowFlags flags = ImGuiWindowFlags.None;
if (ImGui.Begin("My Panel", ref _isOpen, flags))
{
DrawToolbar();
LayoutDrawer.DrawSeparator();
DrawContent();
}
ImGui.End();
ModalDrawer.RenderConfirmationModal(
title: "Confirm Action",
showModal: ref _showConfirmModal,
message: "Are you sure?",
onOk: () => PerformAction());
}
private void DrawToolbar()
{
if (ButtonDrawer.DrawButton("Save"))
{
SaveData();
}
ImGui.SameLine();
if (ButtonDrawer.DrawColoredButton("Clear", MessageType.Warning))
{
_showConfirmModal = true;
}
}
private void DrawContent()
{
LayoutDrawer.DrawSpacing(EditorUIConstants.StandardPadding);
}
private void SaveData()
{
}
private void PerformAction()
{
}
}
Step 3: Register in Dependency Injection
Location: Editor/Program.cs
Registration Pattern:
private static void ConfigureServices(Container container)
{
container.Register<IMyNewPanel, MyNewPanel>(Reuse.Singleton);
}
Guidelines:
- Always register as singleton (one instance per editor session)
- Register interface → implementation mapping
- Ensure all dependencies are registered before the panel
Step 4: Inject into EditorPanels
Location: Editor/EditorPanels.cs
Constructor Injection:
public class EditorPanels(
// ... existing panels
IMyNewPanel myNewPanel)
{
public void Draw(Entity? hoveredEntity, EditorCamera camera)
{
myNewPanel.Draw();
}
}
Register the panel in Editor/DI/EditorIoCContainer.cs and add it to EditorPanels constructor and Draw().
Step 5: Add Menu Integration
Location: Editor/Features/Shell/EditorMenuBar.cs
Add Panel Toggle Menu (e.g. in View menu or add Window menu):
private void RenderViewMenu()
{
if (!ImGui.BeginMenu("View")) return;
if (ImGui.MenuItem("My Panel", "", myNewPanel.IsVisible))
myNewPanel.IsVisible = !myNewPanel.IsVisible;
ImGui.EndMenu();
}
Keyboard Shortcut (optional): register via EditorShortcutRegistrar in Editor/Input/EditorShortcutRegistrar.cs.
Step 6: Use UI Infrastructure (MANDATORY)
CRITICAL: All panels MUST use the UI infrastructure - never write manual ImGui code for patterns covered by Drawers, Elements, or FieldEditors!
Common UI Components:
- Buttons - Use
ButtonDrawer.DrawButton() with button types (Primary, Secondary, Danger, Success)
- Modals - Use
ModalDrawer.RenderConfirmationModal() for all confirmation dialogs
- Tables - Use
TableDrawer.BeginTable() / TableDrawer.DrawRow() / TableDrawer.EndTable()
- Spacing - Use
LayoutDrawer.DrawSpacing() / LayoutDrawer.DrawSeparator()
- Asset References - Use
TextureDropTarget.Draw(), AudioDropTarget.Draw(), etc.
- Property Editing - Use ImGui widgets directly (ImGui.DragFloat, ImGui.InputText, etc.) or create custom UI patterns
Example: Minimal Panel with UI Infrastructure
using Editor.UI.Drawers;
using Editor.UI.Elements;
public class MyPanel : IMyPanel
{
private bool _showConfirmModal;
private float _speed = 1.0f;
private string _iconPath = "";
public void OnImGuiRender()
{
if (!_isOpen) return;
if (ImGui.Begin("My Panel", ref _isOpen))
{
if (ButtonDrawer.DrawButton("Save"))
{
Save();
}
LayoutDrawer.DrawSeparator();
ImGui.DragFloat("Speed", ref _speed, 0.1f);
TextureDropTarget.Draw("Icon", _iconPath, (newPath) => _iconPath = newPath);
}
ImGui.End();
ModalDrawer.RenderConfirmationModal(
title: "Confirm",
showModal: ref _showConfirmModal,
message: "Are you sure?",
onOk: () => PerformAction());
}
}
Key Rules:
- ❌ Never use
ImGui.Button() - use ButtonDrawer.DrawButton()
- ❌ Never use
ImGui.BeginPopupModal() - use ModalDrawer.RenderConfirmationModal()
- ✅ Use
ImGui.DragFloat(), ImGui.InputText(), etc. for property editing (IFieldEditor is for script inspector only)
- ❌ Never manually implement drag-drop - use
TextureDropTarget.Draw(), etc.
Advanced Panel Patterns
Dockable Panel
public void OnImGuiRender()
{
if (!_isOpen)
return;
ImGuiWindowFlags flags = ImGuiWindowFlags.None;
if (ImGui.Begin("My Panel", ref _isOpen, flags))
{
DrawContent();
}
ImGui.End();
}
Panel with Tabs
private void DrawContent()
{
if (ImGui.BeginTabBar("##MyTabs"))
{
if (ImGui.BeginTabItem("Tab 1"))
{
DrawTab1Content();
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Tab 2"))
{
DrawTab2Content();
ImGui.EndTabItem();
}
ImGui.EndTabBar();
}
}
Panel with Context Menu
private void DrawItem(string itemName)
{
ImGui.Selectable(itemName);
if (ImGui.BeginPopupContextItem($"##{itemName}Context"))
{
if (ImGui.MenuItem("Edit"))
EditItem(itemName);
if (ImGui.MenuItem("Delete"))
DeleteItem(itemName);
ImGui.EndPopup();
}
}
Panel with Modal Dialog
ALWAYS use ModalDrawer instead of manual ImGui popups.
using Editor.UI.Drawers;
private bool _showDeleteConfirmation = false;
private void DrawContent()
{
if (ButtonDrawer.DrawButton("Delete", ButtonDrawer.ButtonType.Danger))
_showDeleteConfirmation = true;
ModalDrawer.RenderConfirmationModal(
title: "Delete Confirmation",
showModal: ref _showDeleteConfirmation,
message: "Are you sure you want to delete?",
onOk: () => PerformDelete());
}
Existing Panels Reference
The editor has 17 panels in Editor/Panels/ and Editor/Features/. Reference these for implementation patterns:
- Core: SceneHierarchyPanel, PropertiesPanel, ViewportPanel, GameViewPanel
- Assets: ContentBrowserPanel, AssetPanel, TileMapPanel
- Tools: ConsolePanel, StatsPanel, AudioPanel, PhysicsPanel
- Settings: ProjectSettingsPanel, BuildSettingsPanel, PreferencesPanel, SceneSettingsPanel
- Utilities: ShortcutsPanel, AboutPanel
See implementations in Editor/Panels/ for UI consistency patterns.
Dependency Injection Best Practices
Common Service Dependencies
private readonly ISceneManager _sceneManager;
private readonly IProjectManager _projectManager;
private readonly ITextureFactory _textureFactory;
private readonly IShaderFactory _shaderFactory;
private readonly IAudioClipFactory _audioClipFactory;
private readonly SystemManager _systemManager;
private readonly ISceneHierarchyPanel _sceneHierarchyPanel;
Constructor Pattern (Use Primary Constructor)
public class MyPanel(
ISceneManager sceneManager,
IProjectManager projectManager,
ITextureFactory textureFactory) : IMyPanel
{
}
Never Create Static Singletons
public static class MyPanelManager
{
public static MyPanelManager Instance { get; } = new();
}
container.Register<IMyPanel, MyPanel>(Reuse.Singleton);
Testing Checklist
Documentation Requirements
Code Documentation
- XML comments on interface and public methods
- Clear parameter descriptions
- Usage examples in comments
Common Pitfalls to Avoid
- ❌ Manual ImGui code instead of Drawers - ALWAYS use ButtonDrawer, ModalDrawer, TableDrawer, etc.
- ❌ Manual drag-drop instead of Elements - Use TextureDropTarget, AudioDropTarget, etc.
- ❌ Confusing IFieldEditor usage - IFieldEditor is for script inspector only, use ImGui widgets for panel properties
- ❌ Hardcoded UI values - Always use EditorUIConstants
- ❌ Static state - Use instance fields, inject dependencies
- ❌ Null checks in constructors - Primary constructors with non-nullable types handle this automatically; no manual null checks needed
- ❌ Inconsistent styling - Follow existing panel patterns and use UI infrastructure
- ❌ Direct service access - Use dependency injection
- ❌ Forgetting IsOpen check - Always check before rendering
- ❌ ImGui misuse - Follow Begin/End pairing strictly
- ❌ Performance issues - Avoid heavy computation in OnImGuiRender
- ❌ Duplicating UI patterns - Check if a Drawer/Element already exists first