leetcode_tui_core/
popup.rs

1use ratatui::widgets::{ListState, ScrollbarState};
2use std::fmt::Display;
3
4use crate::emit;
5
6#[derive(Default)]
7pub struct Popup {
8    pub visible: bool,
9    lines: Vec<String>,
10    pub v_scroll_state: ScrollbarState,
11    pub v_scroll: usize,
12    title: Option<String>,
13}
14
15impl Popup {
16    pub fn get_title(&self) -> Option<&str> {
17        self.title.as_deref()
18    }
19}
20
21impl Popup {
22    pub fn new(lines: Vec<String>) -> Self {
23        let mut p = Popup {
24            lines,
25            ..Default::default()
26        };
27        p.v_scroll_state = p.v_scroll_state.content_length(p.lines.len());
28        p
29    }
30
31    pub fn toggle(&mut self) -> bool {
32        self.visible = !self.visible;
33        true
34    }
35
36    pub fn get_text(&self) -> &Vec<String> {
37        &self.lines
38    }
39
40    pub fn reset(&mut self, title: Option<String>, lines: Vec<String>) {
41        let mut p = Self::new(lines);
42        p.visible = self.visible;
43        p.title = title;
44        *self = p;
45    }
46
47    pub fn get_lines(&self) -> &Vec<String> {
48        &self.lines
49    }
50
51    pub fn scroll_down(&mut self) -> bool {
52        if self.v_scroll == self.lines.len().saturating_sub(1) {
53            return false;
54        }
55        self.v_scroll = self.v_scroll.saturating_add(1);
56        self.v_scroll_state = self.v_scroll_state.position(self.v_scroll);
57        true
58    }
59
60    pub fn scroll_up(&mut self) -> bool {
61        if self.v_scroll == 0 {
62            return false;
63        }
64        self.v_scroll = self.v_scroll.saturating_sub(1);
65        self.v_scroll_state = self.v_scroll_state.position(self.v_scroll);
66        true
67    }
68}
69
70#[derive(Default)]
71pub struct SelectPopup<T: Display> {
72    pub visible: bool,
73    pub state: ListState,
74    items: Vec<T>,
75    sender: Option<tokio::sync::oneshot::Sender<Option<usize>>>,
76    title: Option<String>,
77}
78
79impl<T: Display> SelectPopup<T> {
80    pub fn get_title(&self) -> Option<&str> {
81        self.title.as_deref()
82    }
83}
84
85impl<T: Display> SelectPopup<T> {
86    pub fn with_items(
87        &mut self,
88        maybe_title: Option<String>,
89        items: Vec<T>,
90        sender: tokio::sync::oneshot::Sender<Option<usize>>,
91    ) {
92        *self = SelectPopup {
93            visible: self.visible,
94            state: ListState::default(),
95            items,
96            sender: Some(sender),
97            title: maybe_title,
98        };
99        if !self.items.is_empty() {
100            self.state.select(Some(0))
101        }
102    }
103
104    pub fn get_lines(&self) -> &Vec<T> {
105        &self.items
106    }
107
108    pub fn toggle(&mut self) -> bool {
109        self.visible = !self.visible;
110        true
111    }
112
113    pub fn close_unselected(&mut self) -> bool {
114        if let Some(sender) = self.sender.take() {
115            if let Err(e) = sender.send(None) {
116                emit!(Error(format!("Could not send {e:?} via one shot channel.")));
117            } else {
118                self.toggle();
119            }
120        }
121        true
122    }
123
124    pub fn close(&mut self) -> bool {
125        let mut error_message = None;
126        if let Some(sender) = self.sender.take() {
127            let k = sender.send(self.state.selected());
128            if let Err(e) = k {
129                error_message = Some(format!(
130                    "index: {:?} could not be sent through the channel",
131                    e
132                ));
133            };
134        } else {
135            error_message = Some(
136                "Sender not present in Stateful list. Cannot send the selected item.".to_string(),
137            );
138        }
139        if let Some(em) = error_message {
140            emit!(Error(em));
141        }
142        self.toggle();
143        true
144    }
145
146    pub fn next_item(&mut self) -> bool {
147        let i = match self.state.selected() {
148            Some(i) => {
149                if i >= self.items.len() - 1 {
150                    0
151                } else {
152                    i + 1
153                }
154            }
155            None => 0,
156        };
157        self.state.select(Some(i));
158        true
159    }
160
161    pub fn prev_item(&mut self) -> bool {
162        let i = match self.state.selected() {
163            Some(i) => {
164                if i == 0 {
165                    self.items.len() - 1
166                } else {
167                    i - 1
168                }
169            }
170            None => 0,
171        };
172        self.state.select(Some(i));
173        true
174    }
175
176    pub fn unselect(&mut self) {
177        self.state.select(None);
178    }
179}