如何向tui::widgets::ListItem传递结构体并展示与获取详情
解决方案
要实现在ListItem中存储完整SongStruct、显示title、选中时获取结构体详情的需求,你可以利用tui.rs中ListItem的data字段携带完整结构体,同时配合事件循环处理选中逻辑。以下是具体修改步骤和完整代码:
1. 调整ListItem创建逻辑
直接遍历SongStruct向量,为每个结构体创建ListItem:用title作为显示内容,通过.data()方法将完整的SongStruct存入ListItem的自定义数据字段。
2. 添加事件循环处理交互
引入crossterm的event模块监听键盘事件,实现列表项选中切换,以及选中时获取结构体并调用自定义函数。
修改后的完整代码
pub struct SongStruct { pub title: String, pub artist: String, pub file_path: String, } use std::{ io, time::{Duration, Instant}, }; use tui::{ backend::CrosstermBackend, Terminal, widgets::{Block, Borders, List, ListItem}, layout::{Layout, Direction, Constraint, Alignment}, style::{Style, Color, Modifier}, text::Span, }; use crossterm::{ terminal::{enable_raw_mode, disable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, execute, event::{self, Event, KeyCode, KeyEvent}, }; use crate::audio::Song::SongStruct; // 自定义函数:处理选中的歌曲结构体 fn handle_selected_song(song: &SongStruct) { println!("\n选中了歌曲:"); println!("标题:{}", song.title); println!("艺术家:{}", song.artist); println!("文件路径:{}", song.file_path); } pub fn ui(mut song_list: Vec<SongStruct>) -> Result<(), io::Error> { enable_raw_mode().expect("无法启用原始模式"); let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; // 列表选中索引 let mut selected_idx = 0; let refresh_rate = Duration::from_millis(16); loop { terminal.draw(|rect| { let size = rect.size(); let chunks = Layout::default() .direction(Direction::Vertical) .margin(1) .constraints( [ Constraint::Percentage(30), Constraint::Percentage(70) ].as_ref() ) .split(size); let top_block = Block::default().title("rust-play").borders(Borders::ALL); rect.render_widget(top_block, chunks[0]); // 创建携带完整SongStruct的ListItem let list_items: Vec<ListItem<_>> = song_list .iter() .map(|song| ListItem::new(Span::styled(&song.title, Style::default().fg(Color::Yellow))) .data(song.clone())) // 存储完整结构体 .collect(); let title_span = Span::styled("歌曲列表", Style::default().fg(Color::Red)); let list = List::new(list_items) .block(Block::default().title(title_span).title_alignment(Alignment::Center).borders(Borders::ALL)) .highlight_style(Style::default().add_modifier(Modifier::ITALIC).fg(Color::Green)) .highlight_symbol(">>") .select(selected_idx); // 设置当前选中项 rect.render_widget(list, chunks[1]); })?; // 监听事件 if event::poll(refresh_rate)? { if let Event::Key(KeyEvent { code, .. }) = event::read()? { match code { // 上下键切换选中项 KeyCode::Up => { if selected_idx > 0 { selected_idx -= 1; } } KeyCode::Down => { if selected_idx < song_list.len() - 1 { selected_idx += 1; } } // 回车键触发选中处理 KeyCode::Enter => { let selected_song = &song_list[selected_idx]; handle_selected_song(selected_song); } // Esc键退出程序 KeyCode::Esc => break, _ => {} } } } } // 清理终端 disable_raw_mode()?; execute!(terminal.backend_mut(), LeaveAlternateScreen)?; terminal.show_cursor()?; Ok(()) }
关键修改点说明
- ListItem携带结构体:通过
ListItem::new(显示内容).data(song.clone())将完整的SongStruct存入ListItem的data字段,选中时可直接取出。 - 事件循环:添加事件监听逻辑,处理上下键切换选中索引,回车键触发自定义的
handle_selected_song函数并传入选中的歌曲结构体。 - 选中状态标记:使用
List::select(selected_idx)标记当前选中项,配合高亮样式提升交互体验。
内容的提问来源于stack exchange,提问作者devMe
相关产品推荐
相关产品推荐

