Browse Source

Selecte elements, return selected elements

master
Julio Biason 9 months ago
parent
commit
8121b5fe85
  1. 142
      ratatuitest/src/main.rs

142
ratatuitest/src/main.rs

@ -3,40 +3,129 @@ use crossterm::event::KeyEventKind;
use crossterm::terminal::disable_raw_mode; use crossterm::terminal::disable_raw_mode;
use crossterm::terminal::enable_raw_mode; use crossterm::terminal::enable_raw_mode;
use ratatui::prelude::Backend; use ratatui::prelude::Backend;
use ratatui::prelude::Constraint;
use ratatui::prelude::CrosstermBackend; use ratatui::prelude::CrosstermBackend;
use ratatui::prelude::Direction; use ratatui::prelude::Rect;
use ratatui::prelude::Layout;
use ratatui::style::Color;
use ratatui::style::Modifier; use ratatui::style::Modifier;
use ratatui::style::Style; use ratatui::style::Style;
use ratatui::widgets::List; use ratatui::widgets::List;
use ratatui::widgets::ListItem; use ratatui::widgets::ListItem;
use ratatui::widgets::ListState;
use ratatui::Frame; use ratatui::Frame;
use ratatui::Terminal; use ratatui::Terminal;
use std::error::Error; use std::error::Error;
struct Cursor {
pub state: ListState,
pub values: Vec<String>,
pub selected: Vec<usize>,
}
impl Cursor {
pub fn new(values: Vec<String>) -> 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<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
enable_raw_mode()?; enable_raw_mode()?;
let mut stdout = std::io::stdout(); let stdout = std::io::stdout();
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?; 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 // finish
disable_raw_mode()?; disable_raw_mode()?;
println!("Selected: {:?}", options.marked());
Ok(()) Ok(())
} }
fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> std::io::Result<()> { fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
pos: &Rect,
values: &mut Cursor,
) -> std::io::Result<()> {
loop { loop {
terminal.draw(|f| ui(f))?; terminal.draw(|f| ui(f, pos, values))?;
match crossterm::event::read()? { match crossterm::event::read()? {
crossterm::event::Event::Key(key) => { crossterm::event::Event::Key(key) => {
if key.kind == KeyEventKind::Press { if key.kind == KeyEventKind::Press {
match key.code { match key.code {
KeyCode::Esc => break, 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<B: Backend>(terminal: &mut Terminal<B>) -> std::io::Result<()> {
Ok(()) Ok(())
} }
fn ui<B: Backend>(f: &mut Frame<B>) { fn ui<B: Backend>(f: &mut Frame<B>, pos: &Rect, cursor: &mut Cursor) {
let items = [ let items = cursor
ListItem::new("Item 1"), .values
ListItem::new("Item 2"), .iter()
ListItem::new("Item 3"), .enumerate()
]; .map(|(pos, desc)| {
let block = Layout::default() ListItem::new(format!(
.direction(Direction::Horizontal) "{} {}",
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) if cursor.selected.contains(&pos) {
.split(f.size()); "✔"
} else {
"✕"
},
desc.to_string()
))
})
.collect::<Vec<ListItem>>();
let list = List::new(items) let list = List::new(items)
.highlight_style( // .block(Block::default().borders(Borders::ALL))
Style::default() .highlight_style(Style::default().add_modifier(Modifier::BOLD))
.bg(Color::LightGreen)
.add_modifier(Modifier::BOLD),
)
.highlight_symbol("> "); .highlight_symbol("> ");
f.render_widget(list, block[0]); f.render_stateful_widget(list, *pos, &mut cursor.state);
} }

Loading…
Cancel
Save