leetcode_tui_core/
popup.rs1use 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}