Rust终端UI组件库tui-tree-widget的使用,打造高效可交互的树形结构终端界面

Rust终端UI组件库tui-tree-widget的使用,打造高效可交互的树形结构终端界面

Ratatui Tree Widget

一个用于显示树形数据结构的Ratatui Widget组件。

Screenshot

最初是为mqttui项目开发的专用组件。

安装

在项目目录中运行以下Cargo命令:

cargo add tui-tree-widget

或者在Cargo.toml中添加以下行:

tui-tree-widget = "0.23.1"

完整示例代码

下面是一个使用tui-tree-widget创建交互式树形结构的完整示例:

use ratatui::{
    backend::TestBackend,
    buffer::Buffer,
    layout::Rect,
    style::{Color, Modifier, Style},
    symbols,
    text::{Line, Span},
    widgets::{Block, Borders},
    Terminal,
};
use tui_tree_widget::{Tree, TreeItem};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建终端后端
    let backend = TestBackend::new(30, 10);
    let mut terminal = Terminal::new(backend)?;

    // 构建树形数据
    let items = vec![
        // 第一级节点
        TreeItem::new_leaf("Item 1"),
        // 可展开的第二级节点
        TreeItem::new(
            "Item 2",
            vec![
                TreeItem::new_leaf("Child 1"),
                TreeItem::new_leaf("Child 2"),
                TreeItem::new(
                    "Child 3",
                    vec![
                        TreeItem::new_leaf("Grandchild 1"),
                        TreeItem::new_leaf("Grandchild 2"),
                    ],
                )
                .expect("All item identifiers must be unique"),
            ],
        )
        .expect("All item identifiers must be unique"),
        TreeItem::new_leaf("Item 3"),
    ];

    // 创建树形Widget
    let tree = Tree::new(items)
        .block(
            Block::default()
                .title("Tree")
                .borders(Borders::ALL)
        )
        .highlight_style(
            Style::default()
                .fg(Color::Black)
                .bg(Color::LightGreen)
                .add_modifier(Modifier::BOLD),
        )
        .highlight_symbol(">>");

    // 渲染树形结构
    terminal.draw(|f| {
        let size = f.size();
        f.render_widget(tree, size);
    })?;

    Ok(())
}

代码说明

  1. 后端设置:使用TestBackend创建终端环境,实际应用中可以使用CrosstermBackendTermionBackend

  2. 树形数据构建

    • TreeItem::new_leaf()创建叶子节点
    • TreeItem::new()创建可展开的父节点,包含子节点列表
  3. 树形Widget配置

    • block()添加边框和标题
    • highlight_style()设置选中项样式
    • highlight_symbol()设置选中项前缀符号
  4. 渲染:通过terminal.draw()将树形Widget渲染到终端

交互控制

实际应用中,可以通过键盘事件来控制树形结构的展开/折叠和选择:

match key_event.code {
    KeyCode::Up => tree_state.key_up(),
    KeyCode::Down => tree_state.key_down(),
    KeyCode::Left => tree_state.key_left(),
    KeyCode::Right => tree_state.key_right(),
    KeyCode::Enter => {
        if let Some(item) = tree_state.selected() {
            println!("Selected: {}", item);
        }
    }
    _ => {}
}

更多信息

这个组件非常适合需要展示层级数据的命令行工具,如文件浏览器、配置查看器等,能有效提升用户体验。


1 回复

Rust终端UI组件库tui-tree-widget的使用指南

tui-tree-widget是一个用于在终端中创建交互式树形结构的Rust组件库,基于流行的tui-rs终端用户界面库构建。

功能特点

  • 支持可折叠/展开的树形节点
  • 键盘导航(上下箭头、左右箭头)
  • 自定义节点渲染
  • 支持异步加载节点
  • crosstermtermion后端兼容

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
tui = "0.19"
tui-tree-widget = "0.7"
crossterm = "0.26"  # 或其他你喜欢的后端

基本示例

use tui::{
    backend::Backend,
    widgets::{Block, Borders},
    Terminal,
};
use tui_tree_widget::{Tree, TreeItem};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 初始化终端
    let mut terminal = setup_terminal()?;
    
    // 创建树形数据
    let items = vec![
        TreeItem::new_leaf("a", "Leaf A"),
        TreeItem::new(
            "b",
            "Parent B",
            vec![
                TreeItem::new_leaf("b-1", "Child B-1"),
                TreeItem::new_leaf("b-2", "Child B-2"),
            ],
        )?,
        TreeItem::new_leaf("c", "Leaf C"),
    ];
    
    // 创建树形组件
    let tree = Tree::new(items)
        .block(Block::default().title("Tree").borders(Borders::ALL));
    
    // 主循环
    loop {
        terminal.draw(|f| {
            let area = f.size();
            f.render_widget(tree.clone(), area);
        })?;
        
        // 处理输入事件...
    }
    
    // 恢复终端
    restore_terminal(&mut terminal)?;
    Ok(())
}

高级用法

自定义节点渲染

use tui::style::{Style, Color};

let tree = Tree::new(items)
    .block(Block::default().title("Tree").borders(Borders::ALL))
    .highlight_style(Style::default().fg(Color::Yellow))
    .unwrap();

处理交互事件

use crossterm::event::{self, Event, KeyCode};

match event::read()? {
    Event::Key(key) => match key.code {
        KeyCode::Up => tree.prev(),
        KeyCode::Down => tree.next(),
        KeyCode::Left => tree.close(),
        KeyCode::Right => tree.open(),
        KeyCode::Enter => {
            if let Some(selected) = tree.selected() {
                println!("Selected: {}", selected);
            }
        }
        _ => {}
    },
    _ => {}
}

异步加载节点

use std::sync::{Arc, Mutex};

let items = Arc::new(Mutex::new(vec![
    TreeItem::new("async", "Load More", vec![])?,
]));

// 在事件处理中
if let Some(selected) = tree.selected() {
    if selected == "async" {
        let mut items = items.lock().unwrap();
        *items = vec![
            TreeItem::new_leaf("loaded-1", "Loaded Item 1"),
            TreeItem::new_leaf("loaded-2", "Loaded Item 2"),
        ];
        tree.set_items items.clone());
    }
}

实用技巧

  1. 性能优化:对于大型树结构,考虑使用延迟加载技术
  2. 自定义样式:可以自定义不同层级节点的缩进和样式
  3. 集成到现有应用:可以很容易地与其他tui-rs组件结合使用

完整示例

use tui::{
    backend::CrosstermBackend,
    Terminal,
    widgets::{Block, Borders},
    layout::Rect,
};
use tui_tree_widget::{Tree, TreeItem};
use crossterm::{
    event::{self, Event, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::{io, sync::{Arc, Mutex}};

fn main() -> io::Result<()> {
    // 设置终端
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen)?;
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;
    
    // 创建树形数据
    let items = Arc::new(Mutex::new(vec![
        TreeItem::new_leaf("file1", "Document.txt"),
        TreeItem::new(
            "dir1", 
            "Projects",
            vec![
                TreeItem::new_leaf("proj1", "Project A"),
                TreeItem::new_leaf("proj2", "Project B"),
            ],
        )?,
        TreeItem::new_leaf("file2", "Notes.md"),
    ]));
    
    let mut tree = Tree::new(items.lock().unwrap().clone())
        .block(Block::default().title("File Explorer").borders(Borders::ALL));
    
    // 主循环
    loop {
        terminal.draw(|f| {
            let size = f.size();
            f.render_widget(tree.clone(), size);
        })?;
        
        if let Event::Key(key) = event::read()? {
            match key.code {
                KeyCode::Char('q') => break,
                KeyCode::Up => tree.prev(),
                KeyCode::Down => tree.next(),
                KeyCode::Left => tree.close(),
                KeyCode::Right => tree.open(),
                KeyCode::Enter => {
                    if let Some(selected) = tree.selected() {
                        println!("Selected: {}", selected);
                    }
                }
                _ => {}
            }
        }
    }
    
    // 恢复终端
    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    Ok(())
}

tui-tree-widget为终端应用提供了强大的树形结构展示能力,结合Rust的性能优势,可以构建出既美观又高效的终端用户界面。

回到顶部