From 8121b5fe85fdaad7674101b3196d771538d50c5f Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Tue, 22 Aug 2023 10:58:58 -0300 Subject: [PATCH] Selecte elements, return selected elements --- ratatuitest/src/main.rs | 142 +++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 25 deletions(-) diff --git a/ratatuitest/src/main.rs b/ratatuitest/src/main.rs index f865668..df27525 100644 --- a/ratatuitest/src/main.rs +++ b/ratatuitest/src/main.rs @@ -3,40 +3,129 @@ use crossterm::event::KeyEventKind; use crossterm::terminal::disable_raw_mode; use crossterm::terminal::enable_raw_mode; use ratatui::prelude::Backend; -use ratatui::prelude::Constraint; use ratatui::prelude::CrosstermBackend; -use ratatui::prelude::Direction; -use ratatui::prelude::Layout; -use ratatui::style::Color; +use ratatui::prelude::Rect; use ratatui::style::Modifier; use ratatui::style::Style; use ratatui::widgets::List; use ratatui::widgets::ListItem; +use ratatui::widgets::ListState; use ratatui::Frame; use ratatui::Terminal; use std::error::Error; +struct Cursor { + pub state: ListState, + pub values: Vec, + pub selected: Vec, +} + +impl Cursor { + pub fn new(values: Vec) -> Self { + Self { + state: ListState::default().with_selected(Some(0)), + values, + selected: Vec::new(), + } + } + + pub fn down(&mut self) { + let current_selected = self.selected(); + if current_selected >= self.values.len() - 1 { + self.state.select(Some(0)) + } else { + self.state.select(Some(current_selected + 1)); + } + } + + pub fn up(&mut self) { + let current_selected = self.selected(); + if current_selected <= 0 { + self.state.select(Some(self.values.len() - 1)); + } else { + self.state.select(Some(current_selected - 1)) + } + } + + fn selected(&self) -> usize { + match self.state.selected() { + Some(i) => i, + None => 0, + } + } + + fn mark(&mut self) { + let current_selected = self.selected(); + match self + .selected + .iter() + .position(|&value| value == current_selected) + { + Some(x) => _ = self.selected.remove(x), + None => self.selected.push(current_selected), + }; + } + + fn marked(&self) -> Vec<&str> { + self.selected + .iter() + .map(|p| self.values[*p].as_str()) + .collect() + } +} + fn main() -> Result<(), Box> { enable_raw_mode()?; - let mut stdout = std::io::stdout(); + let stdout = std::io::stdout(); let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; - let res = run_app(&mut terminal); + + let terminal_rect = terminal.size().unwrap(); + + println!("---"); + // since we need 5 lines... + for _ in 0..5 { + println!(""); + } + let cursor_pos = terminal.get_cursor().unwrap(); + + let mut options = Cursor::new(vec![ + "Option 1".into(), + "Option 2".into(), + "Option 3".into(), + "Option 4".into(), + "Option 5".into(), + "Option 6".into(), + ]); + let lower_rect = Rect::new(0, cursor_pos.1 - 5, terminal_rect.width, 5); + run_app(&mut terminal, &lower_rect, &mut options)?; + + terminal.set_cursor(0, cursor_pos.1)?; // finish disable_raw_mode()?; + + println!("Selected: {:?}", options.marked()); Ok(()) } -fn run_app(terminal: &mut Terminal) -> std::io::Result<()> { +fn run_app( + terminal: &mut Terminal, + pos: &Rect, + values: &mut Cursor, +) -> std::io::Result<()> { loop { - terminal.draw(|f| ui(f))?; + terminal.draw(|f| ui(f, pos, values))?; match crossterm::event::read()? { crossterm::event::Event::Key(key) => { if key.kind == KeyEventKind::Press { match key.code { KeyCode::Esc => break, + KeyCode::Enter => break, + KeyCode::Down => values.down(), + KeyCode::Up => values.up(), + KeyCode::Char(' ') => values.mark(), _ => (), } } @@ -47,23 +136,26 @@ fn run_app(terminal: &mut Terminal) -> std::io::Result<()> { Ok(()) } -fn ui(f: &mut Frame) { - let items = [ - ListItem::new("Item 1"), - ListItem::new("Item 2"), - ListItem::new("Item 3"), - ]; - let block = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) - .split(f.size()); - +fn ui(f: &mut Frame, pos: &Rect, cursor: &mut Cursor) { + let items = cursor + .values + .iter() + .enumerate() + .map(|(pos, desc)| { + ListItem::new(format!( + "{} {}", + if cursor.selected.contains(&pos) { + "✔" + } else { + "✕" + }, + desc.to_string() + )) + }) + .collect::>(); let list = List::new(items) - .highlight_style( - Style::default() - .bg(Color::LightGreen) - .add_modifier(Modifier::BOLD), - ) + // .block(Block::default().borders(Borders::ALL)) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)) .highlight_symbol("> "); - f.render_widget(list, block[0]); + f.render_stateful_widget(list, *pos, &mut cursor.state); }