easy_input_tools 0.1.0

A library for easy input tools
Documentation
use std::collections::HashSet;
use std::io::{stdout, Result, Write};

use colored::ColoredString;
use crossterm::{cursor, terminal, ExecutableCommand};
use k_board::keyboard::Keyboard;
use k_board::keys::Keys;

pub fn checkbox(
    boxes: &Vec<&str>,
    format_option: impl Fn(&str, usize, usize, &HashSet<usize>) -> ColoredString,
) -> Result<HashSet<usize>> {
    let mut stdout = stdout();

    stdout.execute(cursor::Hide)?;

    let mut selected = 0;
    let mut selection = HashSet::new();

    let mut output = render(boxes, selected, &selection, &format_option);

    'selection: loop {
        stdout.execute(terminal::Clear(terminal::ClearType::FromCursorDown))?;
        stdout.write(output.as_bytes())?;

        for key in Keyboard::new() {
            match key {
                Keys::Down => {
                    selected = (selected + boxes.len() + 1) % boxes.len();
                    break;
                }
                Keys::Up => {
                    selected = (selected + boxes.len() - 1) % boxes.len();
                    break;
                }
                Keys::Space => {
                    if selection.contains(&selected) {
                        selection.remove(&selected);
                    } else {
                        selection.insert(selected);
                    }
                    break;
                }
                Keys::Enter => break 'selection,
                _ => {}
            }
        }
        stdout.execute(cursor::MoveUp(output.lines().count() as u16 - 1))?;
        stdout.execute(cursor::MoveToColumn(0))?;
        output = render(boxes, selected, &selection, &format_option);
    }

    output = render(boxes, boxes.len(), &selection, &format_option);

    stdout.execute(cursor::MoveUp(output.lines().count() as u16 - 1))?;
    stdout.execute(cursor::MoveToColumn(0))?;
    stdout.execute(terminal::Clear(terminal::ClearType::FromCursorDown))?;
    stdout.write(format!("{}\n", output).as_bytes())?;
    stdout.execute(cursor::Show)?;
    Ok(selection)
}

fn render(
    boxes: &Vec<&str>,
    selected: usize,
    selection: &HashSet<usize>,
    format_option: &impl Fn(&str, usize, usize, &HashSet<usize>) -> ColoredString,
) -> ColoredString {
    let binding = boxes
        .iter()
        .enumerate()
        .map(|(i, option)| format_option(option, i, selected, &selection))
        .fold(String::new(), |acc, f| format!("{}\n{}", acc, f));
    binding.strip_prefix("\n").unwrap().into()
}