1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

pub struct List<T> {
    message: String,
    selection_list: Vec<(String, T)>,
    term_data: TermData,
}

impl<T> List<T> {
    pub fn new(message: String) -> Self {
        Self {
            message,
            selection_list: Vec::new(),
            term_data: TermData::new()
        }
    }

    pub fn add_item(mut self, selection_name: &str, item: T) -> Self {
        self.selection_list.push((String::from(selection_name), item));
        self
    }

    pub fn render(mut self) -> Result<T, InqueryMessage> {
        if !self.term_data.enable_raw() {
            return Err(InqueryMessage::TermEnableRawErr);
        }

        AnsiBuilder::new()
            .text("[")
            .color().fg().bright_green()
            .text("?")
            .reset_attributes()
            .text("] ")
            .text(&self.message)
            .cursor().save()
            .cursor().hide()
            .println();
        let mut selected_index = 0;

        let list_len = self.selection_list.len();
        for _ in 0..list_len - 1 {
            println!();
        }

        loop {
            AnsiBuilder::new()
                .cursor().up(list_len)
                .print();

            for i in 0..list_len {
                AnsiBuilder::new()
                    .text("\n\r")
                    .erase_line(ClearMode::EntireLine)
                    .print();

                if i == selected_index {
                    AnsiBuilder::new()
                        .color().fg().bright_green()
                        .style().bold()
                        .text("  →  ")
                        .reset_attributes()
                        .text(&self.selection_list[i].0)
                        .print();
                    continue
                }

                AnsiBuilder::new().text("    ")
                    .color().fg().gray()
                    .text(&self.selection_list[i].0)
                    .reset_attributes()
                    .print();
            }

            match stdout().lock().flush() {
                Ok(..) => {},
                Err(..) => return Err(InqueryMessage::FlushLockErr)
            };

            let key = Keys::from(stdin());

            match key {
                Keys::Up => if selected_index > 0 { selected_index -= 1 },
                Keys::Down => if selected_index < self.selection_list.len() - 1 {
                    selected_index += 1;
                },
                Keys::Enter => {
                    let (name, value) = self.selection_list
                        .remove(selected_index);

                    AnsiBuilder::new()
                        .cursor().restore()
                        .color().fg().blue()
                        .text(&format!(" {}", name))
                        .reset_attributes()
                        .println()
                        .cursor().save()
                        .erase_in_display(EraseMode::CursorToEnd)
                        .cursor().restore()
                        .cursor().show()
                        .print();

                    return Ok(value)
                },
                Keys::CtrlC | Keys::CtrlZ => {
                    AnsiBuilder::new()
                        .cursor().show()
                        .print();

                    if !self.term_data.disable_raw() {
                        return Err(InqueryMessage::TermDisableRawErr)
                    }

                    return Err(InqueryMessage::CloseRequested)
                },
                 // Uncomment to view missing key data that is not handled.
                 // Keys::Unhandled(data) => {
                 //     panic!("{}-{}-{}-{}", data[0], data[1], data[2], data[3])
                 // },
                _ => {/* we do nothing and proceed with loop */}
            }
        }
    }
}

use std::io::{ Write, stdin, stdout };

use ansi_builder::{ AnsiBuilder, ClearMode, EraseMode };

use crate::{ InqueryMessage, Keys, term_data::TermData };