Claude-skill-registry editor-panel-creation
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.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/editor-panel-creation" ~/.claude/skills/majiayu000-claude-skill-registry-editor-panel-creation && rm -rf "$T"
skills/data/editor-panel-creation/SKILL.mdEditor 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; /// <summary> /// Interface for the [PanelName] panel. /// </summary> public interface IMyNewPanel { /// <summary> /// Renders the panel using ImGui. /// </summary> void OnImGuiRender(); /// <summary> /// Gets or sets whether the panel is currently open. /// </summary> bool IsOpen { get; set; } }
Naming Convention:
- Interface:
orI[PanelName]PanelI[PanelName] - Implementation:
or[PanelName]Panel[PanelName] - Examples:
,ISceneHierarchyPanel
,IConsolePanelITileMapPanel
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
for sizing, spacing, colors (Drawers handle this automatically)EditorUIConstants - 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; /// <summary> /// Panel for managing and displaying [functionality]. /// </summary> public class MyNewPanel( ISceneManager sceneManager, IProjectManager projectManager) : IMyNewPanel { // Panel state private bool _isOpen = true; private bool _showConfirmModal = false; private string _filterText = string.Empty; private int _selectedIndex = -1; // Input buffers (use EditorUIConstants for sizes) private readonly byte[] _nameBuffer = new byte[EditorUIConstants.MaxNameLength]; /// <inheritdoc/> public bool IsOpen { get => _isOpen; set => _isOpen = value; } /// <inheritdoc/> public void OnImGuiRender() { if (!_isOpen) return; ImGuiWindowFlags flags = ImGuiWindowFlags.None; if (ImGui.Begin("My Panel", ref _isOpen, flags)) { DrawToolbar(); LayoutDrawer.DrawSeparator(); DrawContent(); } ImGui.End(); // Render modals (must be outside Begin/End) ModalDrawer.RenderConfirmationModal( title: "Confirm Action", showModal: ref _showConfirmModal, message: "Are you sure?", onOk: () => PerformAction()); } private void DrawToolbar() { // Use ButtonDrawer for styled buttons if (ButtonDrawer.DrawButton("Save", ButtonDrawer.ButtonType.Primary)) { SaveData(); } ImGui.SameLine(); if (ButtonDrawer.DrawButton("Clear", ButtonDrawer.ButtonType.Secondary)) { _showConfirmModal = true; } } private void DrawContent() { // Use LayoutDrawer for spacing LayoutDrawer.DrawSpacing(EditorUIConstants.StandardPadding); // Panel-specific content here } private void SaveData() { // Implementation } private void PerformAction() { // Implementation } }
Step 3: Register in Dependency Injection
Location:
Editor/Program.cs
Registration Pattern:
private static void ConfigureServices(Container container) { // ... existing registrations // Register new panel 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 EditorLayer
Location:
Editor/EditorLayer.cs
Constructor Injection:
public class EditorLayer( // ... existing parameters ISceneHierarchyPanel sceneHierarchyPanel, IPropertiesPanel propertiesPanel, IConsolePanel consolePanel, IMyNewPanel myNewPanel) : Layer { public override void OnImGuiRender() { // ... existing panel renders sceneHierarchyPanel.OnImGuiRender(); propertiesPanel.OnImGuiRender(); consolePanel.OnImGuiRender(); myNewPanel.OnImGuiRender(); } }
Step 5: Add Menu Integration
Location:
Editor/EditorLayer.cs (in menu bar rendering)
Add Panel Toggle Menu:
private void DrawMenuBar() { if (ImGui.BeginMenu("Window")) { // Existing menu items if (ImGui.MenuItem("Scene Hierarchy", "", _sceneHierarchyPanel.IsOpen)) _sceneHierarchyPanel.IsOpen = !_sceneHierarchyPanel.IsOpen; if (ImGui.MenuItem("Properties", "", _propertiesPanel.IsOpen)) _propertiesPanel.IsOpen = !_propertiesPanel.IsOpen; // New panel menu item if (ImGui.MenuItem("My Panel", "", _myNewPanel.IsOpen)) _myNewPanel.IsOpen = !_myNewPanel.IsOpen; ImGui.EndMenu(); } }
Keyboard Shortcut (optional):
private void HandleShortcuts() { // Existing shortcuts // Ctrl+Shift+M to toggle My Panel if (ImGui.IsKeyDown(ImGuiKey.LeftCtrl) && ImGui.IsKeyDown(ImGuiKey.LeftShift) && ImGui.IsKeyPressed(ImGuiKey.M)) { _myNewPanel.IsOpen = !_myNewPanel.IsOpen; } }
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
with button types (Primary, Secondary, Danger, Success)ButtonDrawer.DrawButton() - Modals - Use
for all confirmation dialogsModalDrawer.RenderConfirmationModal() - Tables - Use
/TableDrawer.BeginTable()
/TableDrawer.DrawRow()TableDrawer.EndTable() - Spacing - Use
/LayoutDrawer.DrawSpacing()LayoutDrawer.DrawSeparator() - Asset References - Use
,TextureDropTarget.Draw()
, etc.AudioDropTarget.Draw() - 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)) { // Use ButtonDrawer for styled buttons if (ButtonDrawer.DrawButton("Save", ButtonDrawer.ButtonType.Primary)) { Save(); } LayoutDrawer.DrawSeparator(); // Use ImGui for properties ImGui.DragFloat("Speed", ref _speed, 0.1f); // Use drag-drop targets for assets TextureDropTarget.Draw("Icon", _iconPath, (newPath) => _iconPath = newPath); } ImGui.End(); // Modals outside Begin/End ModalDrawer.RenderConfirmationModal( title: "Confirm", showModal: ref _showConfirmModal, message: "Are you sure?", onOk: () => PerformAction()); } }
Key Rules:
- ❌ Never use
- useImGui.Button()ButtonDrawer.DrawButton() - ❌ Never use
- useImGui.BeginPopupModal()ModalDrawer.RenderConfirmationModal() - ✅ Use
,ImGui.DragFloat()
, etc. for property editing (IFieldEditor is for script inspector only)ImGui.InputText() - ❌ Never manually implement drag-drop - use
, etc.TextureDropTarget.Draw()
Advanced Panel Patterns
Dockable Panel
public void OnImGuiRender() { if (!_isOpen) return; ImGuiWindowFlags flags = ImGuiWindowFlags.None; if (ImGui.Begin("My Panel", ref _isOpen, flags)) { // Panel is dockable by default in ImGui 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() { // Trigger modal with styled button if (ButtonDrawer.DrawButton("Delete", ButtonDrawer.ButtonType.Danger)) _showDeleteConfirmation = true; // Render modal using ModalDrawer 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
// Scene management private readonly ISceneManager _sceneManager; // Project management private readonly IProjectManager _projectManager; // Factories private readonly ITextureFactory _textureFactory; private readonly IShaderFactory _shaderFactory; private readonly IAudioClipFactory _audioClipFactory; // Systems private readonly SystemManager _systemManager; // Other panels (for cross-panel communication) private readonly ISceneHierarchyPanel _sceneHierarchyPanel;
Constructor Pattern (Use Primary Constructor)
public class MyPanel( ISceneManager sceneManager, IProjectManager projectManager, ITextureFactory textureFactory) : IMyPanel { // Dependencies are automatically available as private readonly fields // No null validation needed - non-nullable reference types handle this }
Never Create Static Singletons
// ❌ WRONG - Do not create static singletons public static class MyPanelManager { public static MyPanelManager Instance { get; } = new(); } // ✅ CORRECT - Use DI container registration container.Register<IMyPanel, MyPanel>(Reuse.Singleton);
Testing Checklist
- Panel interface defined
- Panel implementation with constructor injection
- All dependencies properly injected (no nulls)
- UI Drawers used instead of manual ImGui code (ButtonDrawer, ModalDrawer, etc.)
- UI Elements used for complex interactions (drag-drop targets, component selector)
- ImGui widgets used for property editing (IFieldEditor not needed in most panels)
-
used throughout (no magic numbers)EditorUIConstants - Registered in
DI containerProgram.cs - Injected into
EditorLayer - Menu item added to Window menu
- Panel opens and closes correctly
- Panel state persists during session
- Panel works with docking system
- Keyboard shortcuts added (if applicable)
- Panel performs expected functionality
- Cross-panel communication works (if needed)
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
- ❌ Missing null checks - Validate constructor parameters
- ❌ 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