Skip to content

Component Tree

MeowLynxSea edited this page Feb 18, 2026 · 8 revisions

Tree

A hierarchical data structure for displaying nested information.

Design Rationale

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.

Example

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);

Virtualized Tree Example

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.

Type Aliases

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.

When to use

  • File browsers
  • Organizational charts
  • Nested categories
  • Directory structures

API

Constructor

  • tree(state, nodes): Create a new tree
  • tree_node_data(label): Create a simple ArcTreeNode with label (convenience function)

State Management

  • 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

Tree Methods

  • .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's ElementId along 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's ElementId
  • .on_check(|id, checked_state| ...): Checkbox state change handler that receives the item's ElementId and new TreeCheckedState
  • .on_toggle_expand(handler): Expand/collapse handler
  • .select(id): Programmatically select a node (respects SelectionMode)
  • .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

  • SelectionMode::Single: One node at a time
  • SelectionMode::Multiple: Multiple nodes
  • SelectionMode::None: No selection

TreeCheckedState

  • TreeCheckedState::Checked
  • TreeCheckedState::Unchecked
  • TreeCheckedState::Indeterminate

TreeNode

  • TreeNode::new(id, data): Create node (via TreeNodeBuilder)
  • .expanded(bool): Initial expansion state
  • .selected(bool): Initial selection
  • .checked(TreeCheckedState): Checkbox state
  • .child(TreeNode): Add a child node (automatically sets has_children to true)
  • .has_children(bool): Mark as having children (usually auto-set by .child())

ArcTreeNode

  • ArcTreeNode::new(label): Create a simple tree node with label
  • .with_icon(icon): Set icon path
  • .disabled(bool): Set disabled state

FlatTreeNode

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

flatten_tree

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

TreeNodeData Trait

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 }
}

Notes

  • 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 TreeDragController and TreeDragState types from the tree_drag module

Drag and Drop

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
}

Drag and Drop API

  • 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

  • DropPosition::Before: Dropped above the target node
  • DropPosition::After: Dropped below the target node
  • DropPosition::On: Dropped onto the target node (as a child)

Clone this wiki locally