Add a new dockable UI panel, tab, inspector, or settings panel to the application. Trigger when creating new panels, tool windows, or any new dockable UI tab.
Step-by-step guide to add a new dockable panel to the egui dock layout.
src/ui/<panel_name>.rs)Create a new file with a draw() function:
use eframe::egui;
use crate::app::actions::ActionSink;
use crate::graph::scene::Scene;
pub fn draw(
ui: &mut egui::Ui,
scene: &mut Scene, // If reading/writing scene data
actions: &mut ActionSink, // If pushing structural actions
) {
// Panel content using egui widgets
ui.heading("My Panel");
if ui.button("Do Something").clicked() {
actions.push(Action::MyAction);
}
}
| Need | Parameters | Example Panel |
|---|---|---|
| Read-only display | &Scene | history_panel |
| Data-level edits |
&mut Scene + &mut ActionSink |
properties |
| Settings | &mut Settings + &mut ActionSink | render_settings |
| Selection | &mut Option<NodeId> + &mut HashSet<NodeId> | scene_tree |
| Sculpt state | &mut SculptState | brush_settings |
Key rule: Data-level edits (sliders, colors) mutate &mut Scene directly for zero-latency. Structural changes (add/delete/reparent) push to ActionSink.
src/ui/mod.rs)Add the module declaration:
pub mod <panel_name>;
src/ui/dock.rs)Add variant to Tab enum:
pub enum Tab {
// ... existing variants
MyPanel,
}
pub const ALL: &[Tab] = &[
// ... existing entries
Tab::MyPanel,
];
Tab::MyPanel => "My Panel",
SdfTabViewer)Tab::MyPanel => "My Panel".into(),
SdfTabViewer)Add the match arm that calls your draw function. Import the module at the top of dock.rs if needed.
Simple panel:
Tab::MyPanel => {
my_panel::draw(ui, self.scene, self.actions);
}
Panel needing extra context:
Tab::MyPanel => {
crate::ui::my_panel::draw(
ui,
self.scene,
&mut self.node_graph_state.selected,
self.actions,
);
}
If your panel needs state not already in SdfTabViewer, add a field to the viewer struct and thread it from SdfApp.
Add the tab to a workspace preset in create_dock_state(), create_dock_sculpting(), or create_dock_rendering().
When your panel needs to trigger structural mutations:
src/app/actions.rs)pub enum Action {
// ... existing variants
MyAction(SomeData),
}
src/app/action_handler.rs)Add match arm in process_actions():
Action::MyAction(data) => {
// Mutate self.doc.scene, self.ui, etc.
self.gpu.buffer_dirty = true; // If scene data changed
}
cargo check && cargo clippy -- -D warnings && cargo test && cargo build
src/ui/<panel_name>.rs with draw() functionsrc/ui/mod.rsTab enum variant added + ALL + label() + title() + ui() dispatcher