---
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](#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**:
```csharp
namespace Editor.Panels;
///
/// Interface for the [PanelName] panel.
///
public interface IMyNewPanel
{
///
/// Renders the panel using ImGui.
///
void OnImGuiRender();
///
/// Gets or sets whether the panel is currently open.
///
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**:
```csharp
namespace Editor.Panels;
using Editor.UI;
using Editor.UI.Drawers;
using Editor.UI.Elements;
using Editor.UI.FieldEditors;
using ImGuiNET;
using Editor.Managers;
///
/// Panel for managing and displaying [functionality].
///
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];
///
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();
// 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**:
```csharp
private static void ConfigureServices(Container container)
{
// ... existing registrations
// Register new panel
container.Register(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**:
```csharp
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**:
```csharp
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):
```csharp
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:**
1. **Buttons** - Use `ButtonDrawer.DrawButton()` with button types (Primary, Secondary, Danger, Success)
2. **Modals** - Use `ModalDrawer.RenderConfirmationModal()` for all confirmation dialogs
3. **Tables** - Use `TableDrawer.BeginTable()` / `TableDrawer.DrawRow()` / `TableDrawer.EndTable()`
4. **Spacing** - Use `LayoutDrawer.DrawSpacing()` / `LayoutDrawer.DrawSeparator()`
5. **Asset References** - Use `TextureDropTarget.Draw()`, `AudioDropTarget.Draw()`, etc.
6. **Property Editing** - Use ImGui widgets directly (ImGui.DragFloat, ImGui.InputText, etc.) or create custom UI patterns
**Example: Minimal Panel with UI Infrastructure**
```csharp
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 `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
```csharp
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
```csharp
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
```csharp
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.**
```csharp
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
```csharp
// 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)
```csharp
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
```csharp
// ❌ WRONG - Do not create static singletons
public static class MyPanelManager
{
public static MyPanelManager Instance { get; } = new();
}
// ✅ CORRECT - Use DI container registration
container.Register(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)
- [ ] `EditorUIConstants` used throughout (no magic numbers)
- [ ] Registered in `Program.cs` DI container
- [ ] 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
1. **❌ Manual ImGui code instead of Drawers** - ALWAYS use ButtonDrawer, ModalDrawer, TableDrawer, etc.
2. **❌ Manual drag-drop instead of Elements** - Use TextureDropTarget, AudioDropTarget, etc.
3. **❌ Confusing IFieldEditor usage** - IFieldEditor is for script inspector only, use ImGui widgets for panel properties
4. **❌ Hardcoded UI values** - Always use EditorUIConstants
5. **❌ Static state** - Use instance fields, inject dependencies
6. **❌ Missing null checks** - Validate constructor parameters
7. **❌ Inconsistent styling** - Follow existing panel patterns and use UI infrastructure
8. **❌ Direct service access** - Use dependency injection
9. **❌ Forgetting IsOpen check** - Always check before rendering
10. **❌ ImGui misuse** - Follow Begin/End pairing strictly
11. **❌ Performance issues** - Avoid heavy computation in OnImGuiRender
12. **❌ Duplicating UI patterns** - Check if a Drawer/Element already exists first