xiaohongshu
xiaohongshu
editor-ui-drawers
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.
Full skill instructions
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.
Never use raw ImGui calls when a Drawer exists. Drawers ensure consistency and maintainability.
Provides 11+ button variants with consistent sizing and behavior.
// 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();
// 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 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 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();
// Toolbar button group (automatic spacing)
ButtonDrawer.DrawToolbarButtonGroup(
("Play", () => Play()),
("Pause", () => Pause()),
("Stop", () => Stop())
);
// ✅ 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();
}
Handles modal dialogs, popups, and confirmation prompts.
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();
}
}
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");
}
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 = "");
}
private bool _showMessage;
ModalDrawer.RenderMessageBox(
title: "Error",
showModal: ref _showMessage,
message: "Failed to load file!",
messageType: MessageType.Error,
onClose: () => Console.WriteLine("Message closed"));
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");
Renders tables with consistent styling and behavior.
// 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();
}
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");
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);
});
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();
}
// 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);
Renders tree structures with expand/collapse behavior.
// 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();
}
// 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));
// Highlight search results or special states
if (TreeDrawer.DrawColoredTreeNode("Important", EditorUIConstants.WarningColor,
isSelected: false))
{
// Render children...
ImGui.TreePop();
}
// 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));
// ✅ 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();
}
Layout utilities for spacing, inputs, checkboxes, and context menus.
// 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);
// Separator with spacing before and after
LayoutDrawer.DrawSeparatorWithSpacing();
// Only spacing before
LayoutDrawer.DrawSeparatorWithSpacing(addSpacingBefore: true, addSpacingAfter: false);
// Checkbox with custom color (for log filters, etc.)
private bool _showErrors = true;
LayoutDrawer.DrawColoredCheckbox("Errors", ref _showErrors, EditorUIConstants.ErrorColor);
// Indent content block
LayoutDrawer.DrawIndentedSection(() =>
{
ImGui.Text("Indented text");
ImGui.Text("More indented text");
});
// 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 menu for last item
ImGui.Text("Right-click me");
LayoutDrawer.DrawContextMenu("item1",
("Delete", () => Delete()),
("Rename", () => Rename()),
("Duplicate", () => Duplicate()));
// Tooltip for last item
ImGui.Button("Hover Me");
LayoutDrawer.DrawTooltip("This is a helpful tooltip");
Text rendering with semantic color coding.
// 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");
// ✅ 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();
Handles drag-and-drop visualization and logic.
// 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)}"));
}
// 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));
// 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");
// Standard payload type for content browser items
DragDropDrawer.ContentBrowserItemPayload // Value: "CONTENT_BROWSER_ITEM"
See constants-reference.md for complete documentation.
Button Sizes:
StandardButtonWidth = 120StandardButtonHeight = 0 (auto-height)WideButtonWidth = 150SmallButtonSize = 20IconSize = 16Spacing:
StandardPadding = 4LargePadding = 8SmallPadding = 2Colors:
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 = 60WideColumnWidth = 120FilterInputWidth = 2007 Drawer Classes:
Key Principles:
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience