-
Notifications
You must be signed in to change notification settings - Fork 9
Component Tree
A hierarchical data structure for displaying nested information.
The Tree component uses a different construction pattern from other components:
tree(state, nodes) requires both TreeState and &[TreeNode] as constructor parameters.
This design choice was made because:
-
State and data are fundamental: Unlike styling properties that can be added later,
the tree's data structure (
TreeNode) and state management (TreeState) are core to its functionality and must exist at creation time - Consistency with data-driven components: Tree represents hierarchical data, similar to how a data table or list view requires data at initialization
- Avoiding partial states: Allowing creation without data could lead to inconsistent states where the component exists but has no content
If you need to create a tree dynamically, consider passing an empty slice initially and populating it later through the state management.
use yororen_ui::component::{
tree,
tree_data::{TreeNodeBuilder, TreeState, SelectionMode, ArcTreeNode, SimpleTreeNode}
};
// Simple usage with default ArcTreeNode
let state = TreeState::new();
let root = TreeNodeBuilder::new("root", ArcTreeNode::new("Root"))
.expanded(true)
.child(TreeNodeBuilder::new("child1", ArcTreeNode::new("Child 1")).build())
.child(TreeNodeBuilder::new("child2", ArcTreeNode::new("Child 2")).build())
.build();
let nodes: Vec<SimpleTreeNode> = vec![root];
tree(state, &nodes)
.selection_mode(SelectionMode::Multiple)
.show_checkbox(true);For large trees with thousands of nodes, enable virtualization:
use gpui::{ListAlignment, ListState, px};
let state = TreeState::new();
// Build large tree with many nodes...
let nodes: Vec<SimpleTreeNode> = build_large_tree();
tree(state, &nodes)
.id("my-tree") // Required for multiple trees in same window
.virtualized(true)
.list_state(ListState::new(0, ListAlignment::Top, px(32.)))
.height(px(400.))
.on_item_click(|id, _event, _window, _cx| {
println!("Clicked: {:?}", id);
});Note: When using virtualization, set .height() to provide a bounded container height for the virtualized list to calculate scroll bounds.
For most use cases, use the type aliases to avoid complex generics:
-
SimpleTreeNode-TreeNode<ArcTreeNode>for common tree nodes -
SimpleFlatTreeNode-FlatTreeNode<ArcTreeNode>for flattened nodes
Also see flatten_tree() function for converting hierarchical nodes to a flattened list.
- File browsers
- Organizational charts
- Nested categories
- Directory structures
-
tree(state, nodes): Create a new tree -
tree_node_data(label): Create a simpleArcTreeNodewith label (convenience function)
-
TreeState: Manages expanded/collapsed/selected/checked state -
TreeState::new(): Create new state -
state.set_expanded(id, bool): Set node expansion -
state.is_expanded(id): Check if expanded -
state.toggle_expanded(id): Toggle node expansion -
state.set_selected(id, bool): Set selection -
state.is_selected(id): Check if selected -
state.set_checked(id, TreeCheckedState): Set checkbox state -
state.get_checked(id): Get checkbox state -
state.clear_selection(): Clear all selected nodes -
state.selected_ids(): Get all selected node IDs -
state.checked_ids(): Get all checked node IDs -
state.expanded_nodes(): Get all expanded node IDs
-
.id(ElementId): Set a stable element ID for internal keyed state (required if multiple trees exist in the same window) -
.selection_mode(SelectionMode): Single/Multiple/None -
.show_checkbox(bool): Display checkboxes -
.draggable(bool): Enable drag and drop (currently a Tree-level capability) -
.indent(px): Indent width (default: 20px) -
.row_height(px): Row height for virtualization (default: 32px) -
.virtualized(bool): Enable virtualization for large trees. When enabled, the tree will use a virtualized list rendering for efficient scrolling of large datasets -
.list_state(ListState): Set the list state for virtualized rendering. Should be called when.virtualized(true)is enabled -
.height(px): Set a fixed height for the virtualized tree. This is needed for the virtualized list to calculate scroll bounds -
.on_click(handler): Click handler that receives only the click event (without element ID). Useful for handling clicks on empty space in the tree -
.on_item_click(|id, event, window, cx| ...): Click handler that receives the clicked item'sElementIdalong with the click event and context -
.on_item_context_menu(|id, event, window, cx| ...): Right-click (context menu) handler for individual items. Triggered when the user right-clicks a row -
.on_select(|id| ...): Selection handler that receives the selected item'sElementId -
.on_check(|id, checked_state| ...): Checkbox state change handler that receives the item'sElementIdand newTreeCheckedState -
.on_toggle_expand(handler): Expand/collapse handler -
.select(id): Programmatically select a node (respectsSelectionMode) -
.toggle_expand(id): Toggle programmatically -
.expand(id): Expand node -
.collapse(id): Collapse node -
.expand_all(): Expand all nodes -
.collapse_all(): Collapse all nodes -
.state(): Returns&TreeState— read access to the current tree state -
.state_mut(): Returns&mut TreeState— mutable access to the tree state -
.flattened_nodes(): Returns&[FlatTreeNode]— the flattened list of currently visible nodes (useful for virtualization or external iteration)
-
SelectionMode::Single: One node at a time -
SelectionMode::Multiple: Multiple nodes -
SelectionMode::None: No selection
TreeCheckedState::CheckedTreeCheckedState::UncheckedTreeCheckedState::Indeterminate
-
TreeNode::new(id, data): Create node (viaTreeNodeBuilder) -
.expanded(bool): Initial expansion state -
.selected(bool): Initial selection -
.checked(TreeCheckedState): Checkbox state -
.child(TreeNode): Add a child node (automatically setshas_childrento true) -
.has_children(bool): Mark as having children (usually auto-set by.child())
-
ArcTreeNode::new(label): Create a simple tree node with label -
.with_icon(icon): Set icon path -
.disabled(bool): Set disabled state
A flattened representation of a tree node for rendering:
-
FlatTreeNode::id: The original node ID -
FlatTreeNode::data: The node data -
FlatTreeNode::depth: Depth level in the tree -
FlatTreeNode::expanded: Whether the node is expanded -
FlatTreeNode::selected: Whether the node is selected -
FlatTreeNode::checked: Checkbox state -
FlatTreeNode::has_children: Whether the node has children -
FlatTreeNode::index: Index in the flattened list
Utility function to flatten a tree structure into a list:
use yororen_ui::component::tree_data::flatten_tree;
let flattened = flatten_tree(&nodes, &expanded_ids, false);Parameters:
-
nodes: The tree nodes to flatten -
expanded_ids: Map of node IDs to their expanded state -
include_hidden: Whether to include hidden (collapsed) nodes
Implement for custom data types:
impl TreeNodeData for MyData {
fn label(&self) -> &str { &self.name }
fn icon(&self) -> Option<IconName> { self.icon }
fn disabled(&self) -> bool { self.is_disabled }
}- Tree uses flattening for rendering - only visible nodes are rendered
- State is managed externally via TreeState
- For large trees, consider virtualization with
.virtualized(true) - When using virtualization, rounded corners and padding styles set via Styled are NOT preserved. If you need these styles in virtualized mode, wrap the tree in a parent div instead
- The tree's expansion and selection state is persisted via keyed state, so it survives across re-renders even when the tree is reconstructed with new nodes
- For drag and drop functionality, use the
TreeDragControllerandTreeDragStatetypes from thetree_dragmodule
The tree provides drag and drop support through separate types in the tree_drag module:
use yororen_ui::component::tree_drag::{TreeDragController, TreeDragPreview};
use gpui::px;
// Create a drag controller
let mut drag_controller = TreeDragController::new(px(32.)).on_drop(|dragged_id, target_id, position| {
println!("Dropped {:?} onto {:?} at {:?}", dragged_id, target_id, position);
});
// Start a drag operation
drag_controller.start_drag(node_id.clone(), mouse_position);
// Create a preview element
let preview = TreeDragPreview::new("Node Name").width(px(200.)).height(px(32.));
// End drag and handle drop
if let Some((dragged, target, position)) = drag_controller.end_drag() {
// Handle the drop
}-
TreeDragController::new(row_height): Create a new drag controller -
.on_drop(handler): Set callback for when a drop occurs -
.start_drag(id, position): Start a drag operation -
.end_drag(): End the drag operation and return drop result -
.cancel_drag(): Cancel the drag operation -
.is_dragging(): Check if currently dragging -
.dragged_id(): Get the dragged node ID -
.drop_target(): Get the current drop target -
TreeDragPreview::new(text): Create a preview element -
.width(px): Set preview width -
.height(px): Set preview height
-
DropPosition::Before: Dropped above the target node -
DropPosition::After: Dropped below the target node -
DropPosition::On: Dropped onto the target node (as a child)
Yororen UI v0.3.0 · repository · Apache-2.0 · This wiki documents Yororen UI v0.3.0.
This wiki documents Yororen UI v0.3.0 — the headless-core, swappable-renderer build.