term_inquiry/
list.rs

1pub struct List<'a, T> {
2    message: &'a str,
3    selection_list: Vec<(&'a str, T)>,
4    term_data: TermData,
5}
6
7impl<'a, T> List<'a, T> {
8    pub fn new(message: &'a str) -> Self {
9        Self {
10            message,
11            selection_list: Vec::new(),
12            term_data: TermData::new(),
13        }
14    }
15
16    pub fn add_item(mut self, selection_name: &'a str, item: T) -> Self {
17        self.selection_list.push((selection_name, item));
18        self
19    }
20
21    pub fn inquire(mut self) -> Result<T, InquiryMessage> {
22        if !self.term_data.enable_raw() {
23            return Err(InquiryMessage::TermEnableRawErr);
24        }
25
26        AnsiBuilder::new()
27            .text("[")
28            .color()
29            .fg()
30            .bright_green()
31            .text("?")
32            .reset_attributes()
33            .text("] ")
34            .text(&self.message)
35            .cursor()
36            .save()
37            .cursor()
38            .hide()
39            .println();
40        let mut selected_index = 0;
41
42        let list_len = self.selection_list.len();
43        for _ in 0..list_len - 1 {
44            println!();
45        }
46
47        loop {
48            AnsiBuilder::new().cursor().up(list_len).print();
49
50            for i in 0..list_len {
51                AnsiBuilder::new()
52                    .text("\n\r")
53                    .erase_line(ClearMode::EntireLine)
54                    .print();
55
56                if i == selected_index {
57                    AnsiBuilder::new()
58                        .color()
59                        .fg()
60                        .bright_green()
61                        .style()
62                        .bold()
63                        .text("  →  ")
64                        .reset_attributes()
65                        .text(&self.selection_list[i].0)
66                        .print();
67                    continue;
68                }
69
70                AnsiBuilder::new()
71                    .text("    ")
72                    .color()
73                    .fg()
74                    .gray()
75                    .text(&self.selection_list[i].0)
76                    .reset_attributes()
77                    .print();
78            }
79
80            match stdout().lock().flush() {
81                Ok(..) => {}
82                Err(..) => return Err(InquiryMessage::FlushLockErr),
83            };
84
85            let key = Keys::from(stdin());
86
87            match key {
88                Keys::Up => {
89                    if selected_index > 0 {
90                        selected_index -= 1
91                    }
92                }
93                Keys::Down => {
94                    if selected_index < self.selection_list.len() - 1 {
95                        selected_index += 1;
96                    }
97                }
98                Keys::Enter => {
99                    let (name, value) = self.selection_list.remove(selected_index);
100
101                    AnsiBuilder::new()
102                        .cursor()
103                        .restore()
104                        .color()
105                        .fg()
106                        .blue()
107                        .text(&format!(" {}", name))
108                        .reset_attributes()
109                        .println()
110                        .cursor()
111                        .save()
112                        .erase_in_display(EraseMode::CursorToEnd)
113                        .cursor()
114                        .restore()
115                        .cursor()
116                        .show()
117                        .print();
118
119                    if !self.term_data.disable_raw() {
120                        return Err(InquiryMessage::TermDisableRawErr);
121                    }
122
123                    return Ok(value);
124                }
125                Keys::CtrlC | Keys::CtrlZ => {
126                    AnsiBuilder::new().cursor().show().print();
127
128                    if !self.term_data.disable_raw() {
129                        return Err(InquiryMessage::TermDisableRawErr);
130                    }
131
132                    return Err(InquiryMessage::CloseRequested);
133                }
134                // Uncomment to view missing key data that is not handled.
135                // Keys::Unhandled(data) => {
136                //     panic!("{}-{}-{}-{}", data[0], data[1], data[2], data[3])
137                // },
138                _ => { /* we do nothing and proceed with loop */ }
139            }
140        }
141    }
142}
143
144use std::io::{stdin, stdout, Write};
145
146use ansi_builder::{AnsiBuilder, ClearMode, EraseMode};
147
148use crate::{term_data::TermData, InquiryMessage, Keys};