| Crates.io | egui-arbor |
| lib.rs | egui-arbor |
| version | 0.2.1 |
| created_at | 2025-11-01 09:47:30.903427+00 |
| updated_at | 2025-11-12 20:39:03.869339+00 |
| description | A tree outliner widget for egui - hierarchical data visualization and editing |
| homepage | |
| repository | https://github.com/kyjohnso/egui-arbor |
| max_upload_size | |
| id | 1911850 |
| size | 5,692,183 |
A flexible tree/outliner widget for egui - hierarchical data visualization and editing inspired by Blender's outliner.
| egui-arbor | egui | bevy | bevy_egui |
|---|---|---|---|
| 0.2.0 | 0.31 | 0.16 | 0.34 |
Note: The
bevyandbevy_eguiversions are only required if you're using the Bevy integration example.
OutlinerNodeAdd to your Cargo.toml:
[dependencies]
egui-arbor = "0.2"
use egui_arbor::{Outliner, OutlinerNode, OutlinerActions, ActionIcon};
// 1. Define your data structure
#[derive(Clone)]
struct TreeNode {
id: u64,
name: String,
children: Vec<TreeNode>,
}
// 2. Implement OutlinerNode trait
impl OutlinerNode for TreeNode {
type Id = u64;
fn id(&self) -> Self::Id { self.id }
fn name(&self) -> &str { &self.name }
fn is_collection(&self) -> bool { !self.children.is_empty() }
fn children(&self) -> &[Self] { &self.children }
fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
fn action_icons(&self) -> Vec<ActionIcon> {
vec![ActionIcon::Visibility, ActionIcon::Lock, ActionIcon::Selection]
}
}
// 3. Implement OutlinerActions trait
struct MyActions {
selected: Option<u64>,
}
impl OutlinerActions<TreeNode> for MyActions {
fn on_select(&mut self, id: &u64, selected: bool) {
self.selected = if selected { Some(*id) } else { None };
}
fn is_selected(&self, id: &u64) -> bool {
self.selected == Some(*id)
}
// Implement other required methods...
fn on_rename(&mut self, id: &u64, new_name: String) { /* ... */ }
fn on_move(&mut self, id: &u64, target: &u64, position: DropPosition) { /* ... */ }
fn is_visible(&self, id: &u64) -> bool { true }
fn is_locked(&self, id: &u64) -> bool { false }
fn on_visibility_toggle(&mut self, id: &u64) { /* ... */ }
fn on_lock_toggle(&mut self, id: &u64) { /* ... */ }
fn on_selection_toggle(&mut self, id: &u64) { /* ... */ }
fn on_custom_action(&mut self, id: &u64, icon: &str) { /* ... */ }
}
// 4. Use in your egui code
fn show_tree(ui: &mut egui::Ui, nodes: &[TreeNode], actions: &mut MyActions) {
let response = Outliner::new("my_tree")
.show(ui, nodes, actions);
// Handle events
if let Some(id) = response.selected() {
println!("Selected node: {:?}", id);
}
if let Some((id, new_name)) = response.renamed() {
println!("Renamed node {} to {}", id, new_name);
}
if let Some(drop_event) = response.drop_event() {
println!("Moved node {} to {}", drop_event.source, drop_event.target);
}
}
Run the basic example to see all core features in action:
cargo run --example basic
Features demonstrated:
Run the Bevy integration example to see egui-arbor working with a 3D scene:
cargo run --example bevy_3d_outliner
Features demonstrated:
The example creates 9 objects total (3 shapes × 3 colors) arranged in a grid pattern, with a tree outliner on the left side that controls their visibility in real-time.
Implement this trait on your data structure to make it work with the outliner:
pub trait OutlinerNode {
type Id: Hash + Eq + Clone;
fn id(&self) -> Self::Id;
fn name(&self) -> &str;
fn is_collection(&self) -> bool;
fn children(&self) -> &[Self];
fn children_mut(&mut self) -> &mut Vec<Self>;
fn icon(&self) -> Option<IconType> { None }
fn action_icons(&self) -> Vec<ActionIcon> { vec![] }
}
Handle user interactions by implementing this trait:
pub trait OutlinerActions<N: OutlinerNode> {
fn on_rename(&mut self, id: &N::Id, new_name: String);
fn on_move(&mut self, id: &N::Id, target: &N::Id, position: DropPosition);
fn on_select(&mut self, id: &N::Id, selected: bool);
fn is_selected(&self, id: &N::Id) -> bool;
fn is_visible(&self, id: &N::Id) -> bool;
fn is_locked(&self, id: &N::Id) -> bool;
fn on_visibility_toggle(&mut self, id: &N::Id);
fn on_lock_toggle(&mut self, id: &N::Id);
fn on_selection_toggle(&mut self, id: &N::Id);
fn on_custom_action(&mut self, id: &N::Id, icon: &str);
}
Built-in action icons:
Three drop positions supported:
Automatic validation prevents invalid operations (e.g., parent into child).
use egui_arbor::{Outliner, Style, ExpandIconStyle};
let style = Style::default()
.with_indent(20.0)
.with_row_height(24.0)
.with_selection_color(egui::Color32::from_rgb(100, 150, 255));
Outliner::new("styled_tree")
.with_style(style)
.show(ui, &nodes, &mut actions);
impl OutlinerNode for MyNode {
fn icon(&self) -> Option<IconType> {
if self.is_folder {
Some(IconType::Collection)
} else {
Some(IconType::Entity)
}
}
fn action_icons(&self) -> Vec<ActionIcon> {
vec![
ActionIcon::Visibility,
ActionIcon::Custom {
icon: "⭐".to_string(),
tooltip: Some("Favorite".to_string()),
},
]
}
}
egui-arbor follows egui ecosystem conventions:
See ARCHITECTURE.md for detailed design documentation.
serde: Enable serialization support for state persistence[dependencies]
egui-arbor = { version = "0.2", features = ["serde"] }
Rust 1.76 or later (edition 2024).
Licensed under either of:
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.
Inspired by Blender's outliner and designed to integrate seamlessly with the egui ecosystem.