Comprehensive guide for using Editor UI Drawers (ButtonDrawer, ModalDrawer, TableDrawer, TreeDrawer, LayoutDrawer, TextDrawer, DragDropDrawer). Use when implementing any UI in editor panels to ensure consistency.
Scanned 2/12/2026
Install via CLI
openskills install majiayu000/claude-skill-registry---
name: editor-ui-drawers
description: Comprehensive guide for using Editor UI Drawers (ButtonDrawer, ModalDrawer, TableDrawer, TreeDrawer, LayoutDrawer, TextDrawer, DragDropDrawer). Use when implementing any UI in editor panels to ensure consistency.
---
# Editor UI Drawers
## Table of Contents
1. [Overview](#overview)
2. [When to Use This Skill](#when-to-use-this-skill)
3. [ButtonDrawer](#1-buttondrawer)
4. [ModalDrawer](#2-modaldrawer)
5. [TableDrawer](#3-tabledrawer)
6. [TreeDrawer](#4-treedrawer)
7. [LayoutDrawer](#5-layoutdrawer)
8. [TextDrawer](#6-textdrawer)
9. [DragDropDrawer](#7-dragdropdrawer)
10. [EditorUIConstants Reference](#editoruiconstants-reference)
11. [Summary](#summary)
## Overview
Drawers are static utility classes that provide common UI patterns with consistent styling. They wrap ImGui calls and automatically apply EditorUIConstants for sizing, spacing, and colors.
## When to Use This Skill
- Adding buttons to any editor panel
- Creating modal dialogs or confirmations
- Rendering tables or trees
- Need spacing, separators, or tooltips
- Drawing colored text
- Implementing drag-drop visualization
## Core Principle
**Never use raw ImGui calls when a Drawer exists.** Drawers ensure consistency and maintainability.
---
## 1. ButtonDrawer
Provides 11+ button variants with consistent sizing and behavior.
### Available Methods
#### Standard Buttons
```csharp
// Standard button (uses EditorUIConstants.StandardButtonWidth/Height)
if (ButtonDrawer.DrawButton("Save"))
Save();
// Custom size button
if (ButtonDrawer.DrawButton("Export", width: 150, height: 30))
Export();
// With onClick callback
ButtonDrawer.DrawButton("Load", onClick: Load);
// With disabled state
ButtonDrawer.DrawButton("Delete", disabled: !canDelete);
// Full-width button (stretches to available width)
if (ButtonDrawer.DrawFullWidthButton("Create New Scene"))
CreateScene();
```
#### Specialized Buttons
```csharp
// Compact button (minimal padding, toolbar use)
if (ButtonDrawer.DrawCompactButton("×"))
Close();
// Small utility button (uses EditorUIConstants.SmallButtonSize)
if (ButtonDrawer.DrawSmallButton("+", tooltip: "Add Item"))
AddItem();
// Colored semantic button
if (ButtonDrawer.DrawColoredButton("Delete", MessageType.Error))
Delete();
if (ButtonDrawer.DrawColoredButton("Save", MessageType.Success))
Save();
if (ButtonDrawer.DrawColoredButton("Reload", MessageType.Warning))
Reload();
```
#### Modal Buttons
```csharp
// Modal button with standard sizing
ButtonDrawer.DrawModalButton("OK", onClick: () => Confirm());
// Modal button pair (OK/Cancel with proper spacing)
ButtonDrawer.DrawModalButtonPair(
okLabel: "Create",
cancelLabel: "Cancel",
onOk: () => CreateProject(),
onCancel: () => CloseModal(),
okDisabled: !isValid);
```
#### Toggle & Icon Buttons
```csharp
// Toggle button (changes label based on state)
ButtonDrawer.DrawToggleButton("Loop ON", "Loop OFF", ref isLooping);
// Icon button with selection state
if (ButtonDrawer.DrawIconButton("play_btn", playIconTexture, size, isSelected: isPlaying))
TogglePlay();
// Transparent icon button (for toolbars)
if (ButtonDrawer.DrawTransparentIconButton("stop_btn", stopIconTexture, size, tooltip: "Stop"))
Stop();
```
#### Button Groups
```csharp
// Toolbar button group (automatic spacing)
ButtonDrawer.DrawToolbarButtonGroup(
("Play", () => Play()),
("Pause", () => Pause()),
("Stop", () => Stop())
);
```
### Usage Examples
```csharp
// ✅ CORRECT - Use ButtonDrawer
if (ButtonDrawer.DrawButton("Save"))
{
Save();
}
// ✅ CORRECT - Use semantic colored button
ButtonDrawer.DrawColoredButton("Delete", MessageType.Error);
// ❌ WRONG - Don't use raw ImGui
if (ImGui.Button("Save", new Vector2(120, 30))) // Don't do this!
{
Save();
}
```
---
## 2. ModalDrawer
Handles modal dialogs, popups, and confirmation prompts.
### Available Methods
#### Centered Modal (Manual)
```csharp
private bool _showModal;
public void OnImGuiRender()
{
if (ButtonDrawer.DrawButton("Open"))
_showModal = true;
// Begin modal (centers automatically)
if (ModalDrawer.BeginCenteredModal("My Modal", ref _showModal))
{
ImGui.Text("Modal content here");
if (ButtonDrawer.DrawButton("Close"))
_showModal = false;
ModalDrawer.EndModal();
}
}
```
#### Confirmation Modal (Complete)
```csharp
private bool _showConfirm;
public void OnImGuiRender()
{
// Trigger modal
if (ButtonDrawer.DrawButton("Delete"))
_showConfirm = true;
// Render confirmation modal (call every frame)
ModalDrawer.RenderConfirmationModal(
title: "Confirm Delete",
showModal: ref _showConfirm,
message: "Are you sure you want to delete this item?",
onOk: () => DeleteItem(),
onCancel: () => Console.WriteLine("Cancelled"),
okLabel: "Delete",
cancelLabel: "Cancel");
}
```
#### Input Modal
```csharp
private bool _showInput;
private string _inputValue = "";
public void OnImGuiRender()
{
if (ButtonDrawer.DrawButton("Rename"))
_showInput = true;
ModalDrawer.RenderInputModal(
title: "Rename Entity",
showModal: ref _showInput,
promptText: "Enter new name:",
inputValue: ref _inputValue,
maxLength: 100,
validationMessage: string.IsNullOrEmpty(_inputValue) ? "Name cannot be empty" : null,
errorMessage: null,
isValid: !string.IsNullOrEmpty(_inputValue),
onOk: () => Rename(_inputValue),
onCancel: () => _inputValue = "");
}
```
#### Message Box
```csharp
private bool _showMessage;
ModalDrawer.RenderMessageBox(
title: "Error",
showModal: ref _showMessage,
message: "Failed to load file!",
messageType: MessageType.Error,
onClose: () => Console.WriteLine("Message closed"));
```
#### List Selection Modal
```csharp
private bool _showSelector;
private string[] _items = { "Option 1", "Option 2", "Option 3" };
ModalDrawer.RenderListSelectionModal(
title: "Select Item",
showModal: ref _showSelector,
items: _items,
onItemSelected: item => Console.WriteLine($"Selected: {item}"),
onCancel: () => Console.WriteLine("Cancelled"),
emptyMessage: "No items available");
```
---
## 3. TableDrawer
Renders tables with consistent styling and behavior.
### Available Methods
#### Basic Tables
```csharp
// Two-column key-value table
TableDrawer.DrawTwoColumnTable(
id: "Shortcuts",
leftHeader: "Key",
rightHeader: "Action",
leftWidth: 100,
renderRows: () =>
{
TableDrawer.DrawTableRow(
() => ImGui.Text("Ctrl+S"),
() => ImGui.Text("Save"));
TableDrawer.DrawTableRow(
() => ImGui.Text("Ctrl+O"),
() => ImGui.Text("Open"));
});
// Standard table (manual)
if (TableDrawer.BeginStandardTable("MyTable", columnCount: 3))
{
ImGui.TableSetupColumn("Name");
ImGui.TableSetupColumn("Type");
ImGui.TableSetupColumn("Size");
ImGui.TableHeadersRow();
TableDrawer.DrawTableRow(
() => ImGui.Text("File1"),
() => ImGui.Text("Image"),
() => ImGui.Text("1.2 MB"));
ImGui.EndTable();
}
```
#### Generic Data Table
```csharp
var columns = new TableDrawer.ColumnDefinition[]
{
new() { Label = "Name", Flags = ImGuiTableColumnFlags.WidthStretch },
new() { Label = "Type", Flags = ImGuiTableColumnFlags.WidthFixed, InitWidth = 100 }
};
TableDrawer.DrawDataTable(
id: "Files",
columns: columns,
items: files,
renderRow: file =>
{
TableDrawer.DrawTableRow(
() => ImGui.Text(file.Name),
() => ImGui.Text(file.Type));
},
emptyMessage: "No files found");
```
#### Selectable Data Table
```csharp
TableDrawer.DrawSelectableDataTable(
id: "Entities",
columns: columns,
items: entities,
selectedItem: _selectedEntity,
getItemId: e => e.Id.ToString(),
onItemSelected: e => _selectedEntity = e,
renderRow: entity =>
{
// Cell renderers for each column
ImGui.TableSetColumnIndex(0);
ImGui.Text(entity.Name);
ImGui.TableSetColumnIndex(1);
ImGui.Text(entity.Type);
});
```
#### Sortable Table Headers
```csharp
private string _sortColumn = "Name";
private bool _sortAscending = true;
if (TableDrawer.BeginStandardTable("SortableTable", 2))
{
ImGui.TableSetupColumn("Name");
ImGui.TableSetupColumn("Size");
ImGui.TableHeadersRow();
// Custom sortable headers
ImGui.TableSetColumnIndex(0);
TableDrawer.DrawSortableHeader("Name", "Name", _sortColumn, _sortAscending,
(col, asc) => { _sortColumn = col; _sortAscending = asc; SortData(); });
// ... render rows ...
ImGui.EndTable();
}
```
#### Helper Methods
```csharp
// Selectable row with custom rendering
TableDrawer.DrawSelectableRow(
rowId: "row1",
isSelected: true,
onClicked: () => OnRowClick(),
() => ImGui.Text("Cell 1"),
() => ImGui.Text("Cell 2"));
// Colored cell
ImGui.TableSetColumnIndex(0);
TableDrawer.DrawColoredCell("Error", EditorUIConstants.ErrorColor);
// Empty table message
TableDrawer.DrawEmptyTableMessage("No data available", MessageType.Info);
```
---
## 4. TreeDrawer
Renders tree structures with expand/collapse behavior.
### Available Methods
#### Selectable Tree Nodes
```csharp
// Tree node with selection
if (TreeDrawer.DrawSelectableTreeNode("Entity", isSelected: _selected == entity,
onClicked: () => SelectEntity(entity),
onContextMenu: () => ShowContextMenu()))
{
// Render children
TreeDrawer.DrawSelectableTreeNode("Child Entity", isSelected: false);
ImGui.TreePop();
}
```
#### Selectable Items (Flat List)
```csharp
// For flat lists with context menu and double-click
TreeDrawer.DrawSelectableItem(
label: "File.txt",
isSelected: _selected == file,
onClicked: () => SelectFile(file),
onContextMenu: () => ShowFileContextMenu(),
onDoubleClick: () => OpenFile(file));
```
#### Colored Tree Nodes
```csharp
// Highlight search results or special states
if (TreeDrawer.DrawColoredTreeNode("Important", EditorUIConstants.WarningColor,
isSelected: false))
{
// Render children...
ImGui.TreePop();
}
```
#### Recursive Hierarchy
```csharp
// Render entire hierarchy recursively
TreeDrawer.DrawHierarchy(
node: rootEntity,
getChildren: e => e.Children,
getLabel: e => e.Name,
isSelected: e => e == _selectedEntity,
onClicked: e => _selectedEntity = e,
onContextMenu: e => ShowEntityContextMenu(e));
```
### Usage Example
```csharp
// ✅ CORRECT - Use TreeDrawer with context menu
if (TreeDrawer.DrawSelectableTreeNode("Root", isSelected: _selected == root,
onClicked: () => Select(root),
onContextMenu: () =>
{
if (ImGui.MenuItem("Delete"))
Delete(root);
}))
{
// Children...
ImGui.TreePop();
}
```
---
## 5. LayoutDrawer
Layout utilities for spacing, inputs, checkboxes, and context menus.
### Available Methods
#### Search & Filter Inputs
```csharp
// Search input with clear button
private string _searchQuery = "";
LayoutDrawer.DrawSearchInput(
hint: "Search files...",
searchQuery: ref _searchQuery,
onQueryChanged: query => FilterFiles(query));
// Filter input
private string _filter = "";
LayoutDrawer.DrawFilterInput("##filter", ref _filter, width: 200);
```
#### Separators & Spacing
```csharp
// Separator with spacing before and after
LayoutDrawer.DrawSeparatorWithSpacing();
// Only spacing before
LayoutDrawer.DrawSeparatorWithSpacing(addSpacingBefore: true, addSpacingAfter: false);
```
#### Colored Checkbox
```csharp
// Checkbox with custom color (for log filters, etc.)
private bool _showErrors = true;
LayoutDrawer.DrawColoredCheckbox("Errors", ref _showErrors, EditorUIConstants.ErrorColor);
```
#### Indented Sections
```csharp
// Indent content block
LayoutDrawer.DrawIndentedSection(() =>
{
ImGui.Text("Indented text");
ImGui.Text("More indented text");
});
```
#### Combo Boxes
```csharp
// Standard combo box
private string _current = "Option1";
private string[] _options = { "Option1", "Option2", "Option3" };
LayoutDrawer.DrawComboBox(
label: "Choose",
currentItem: _current,
items: _options,
onSelected: item => _current = item,
width: 200);
```
#### Context Menus
```csharp
// Context menu for last item
ImGui.Text("Right-click me");
LayoutDrawer.DrawContextMenu("item1",
("Delete", () => Delete()),
("Rename", () => Rename()),
("Duplicate", () => Duplicate()));
```
#### Tooltips
```csharp
// Tooltip for last item
ImGui.Button("Hover Me");
LayoutDrawer.DrawTooltip("This is a helpful tooltip");
```
---
## 6. TextDrawer
Text rendering with semantic color coding.
### Available Methods
```csharp
// Generic colored text
TextDrawer.DrawColoredText("Custom Color", new Vector4(1, 0.5f, 0, 1));
// Semantic colored text
TextDrawer.DrawErrorText("Error occurred!");
TextDrawer.DrawWarningText("Warning: Low memory");
TextDrawer.DrawSuccessText("Operation successful");
TextDrawer.DrawInfoText("Information message");
```
### Usage Example
```csharp
// ✅ CORRECT - Use TextDrawer for semantic colors
TextDrawer.DrawErrorText("Failed to load file");
// ❌ WRONG - Don't manually push/pop colors
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0, 0, 1)); // Don't do this!
ImGui.Text("Error");
ImGui.PopStyleColor();
```
---
## 7. DragDropDrawer
Handles drag-and-drop visualization and logic.
### Available Methods
#### Drag Source
```csharp
// Create drag source for file
ImGui.Button("Drag Me");
if (ImGui.IsItemActive())
{
DragDropDrawer.CreateDragDropSource(
dragDropId: DragDropDrawer.ContentBrowserItemPayload,
payloadData: filePath,
dragPreview: () => ImGui.Text($"Moving {Path.GetFileName(filePath)}"));
}
```
#### Drop Target
```csharp
// File drop target with validation
ImGui.Button("Drop Here");
DragDropDrawer.HandleFileDropTarget(
payloadType: DragDropDrawer.ContentBrowserItemPayload,
validator: path => path.EndsWith(".png"),
onDropped: path => LoadTexture(path));
// Drop target without validation
ImGui.Button("Drop Anything");
DragDropDrawer.HandleFileDropTarget(
payloadType: DragDropDrawer.ContentBrowserItemPayload,
validator: null,
onDropped: path => ProcessFile(path));
```
#### Extension Validators
```csharp
// Create reusable validator
var imageValidator = DragDropDrawer.CreateExtensionValidator(
extensions: new[] { ".png", ".jpg", ".bmp" },
checkFileExists: true);
// Use validator
DragDropDrawer.HandleFileDropTarget(
payloadType: DragDropDrawer.ContentBrowserItemPayload,
validator: imageValidator,
onDropped: path => LoadImage(path));
// Helper validation methods
if (DragDropDrawer.HasValidExtension(path, ".png", ".jpg"))
Console.WriteLine("Valid image");
if (DragDropDrawer.IsValidFile(path, ".cs", ".txt"))
Console.WriteLine("File exists and has valid extension");
```
### Constants
```csharp
// Standard payload type for content browser items
DragDropDrawer.ContentBrowserItemPayload // Value: "CONTENT_BROWSER_ITEM"
```
---
## EditorUIConstants Reference
See [constants-reference.md](./constants-reference.md) for complete documentation.
### Quick Reference
**Button Sizes**:
- `StandardButtonWidth` = 120
- `StandardButtonHeight` = 0 (auto-height)
- `WideButtonWidth` = 150
- `SmallButtonSize` = 20
- `IconSize` = 16
**Spacing**:
- `StandardPadding` = 4
- `LargePadding` = 8
- `SmallPadding` = 2
**Colors**:
- `ErrorColor` = Bright red (1.0, 0.3, 0.3)
- `WarningColor` = Yellow (1.0, 1.0, 0.0)
- `SuccessColor` = Bright green (0.3, 1.0, 0.3)
- `InfoColor` = Light gray (0.7, 0.7, 0.7)
**Axis Colors** (for vectors):
- `AxisXColor` = Red (0.8, 0.1, 0.15)
- `AxisYColor` = Green (0.2, 0.7, 0.2)
- `AxisZColor` = Blue (0.1, 0.25, 0.8)
**Layout**:
- `PropertyLabelRatio` = 0.33 (1/3 width)
- `PropertyInputRatio` = 0.67 (2/3 width)
- `DefaultColumnWidth` = 60
- `WideColumnWidth` = 120
- `FilterInputWidth` = 200
---
## Summary
**7 Drawer Classes**:
1. **ButtonDrawer** - 11+ button variants (standard, colored, icon, toggle, modal)
2. **ModalDrawer** - 5 modal types (centered, confirmation, input, message, list selection)
3. **TableDrawer** - Data tables, sortable headers, selectable rows
4. **TreeDrawer** - Hierarchical trees with selection and context menus
5. **LayoutDrawer** - Search, filters, separators, combos, context menus, tooltips
6. **TextDrawer** - Semantic colored text (error, warning, success, info)
7. **DragDropDrawer** - File drag-drop with validation
**Key Principles**:
- Always use Drawers instead of raw ImGui
- All sizing uses EditorUIConstants
- Semantic colors via MessageType enum or dedicated methods
- Consistent patterns across the editor
No comments yet. Be the first to comment!