1use super::{FrameworkError, FrameworkItem};
2use ratatui::layout::{Constraint, Direction, Layout, Rect};
3
4#[derive(Clone)]
6pub struct RowItem {
7 pub item: Box<dyn FrameworkItem>,
9 pub width: Constraint,
11}
12
13#[derive(Clone)]
15pub struct Row {
16 pub items: Vec<RowItem>,
18 pub centered: bool,
20 pub height: Constraint,
22}
23
24#[derive(Clone)]
26pub struct State(pub Vec<Row>);
27
28impl State {
29 pub fn selectables(&self) -> Vec<Vec<(usize, usize)>> {
34 let mut selectables = Vec::new();
35
36 self.0.iter().enumerate().for_each(|(y, row)| {
37 let mut row_selectables = Vec::new();
38 row.items.iter().enumerate().for_each(|(x, row_item)| {
39 if row_item.item.selectable() {
40 row_selectables.push((x, y));
41 }
42 });
43 if !row_selectables.is_empty() {
44 selectables.push(row_selectables);
45 }
46 });
47
48 selectables
49 }
50
51 pub fn get_chunks(&self, area: Rect) -> Vec<Vec<Rect>> {
53 let mut row_constraints = vec![Constraint::Length(0)];
55 row_constraints.extend(self.0.iter().map(|row| row.height));
56 row_constraints.push(Constraint::Length(0));
57
58 let row_constraints_length = row_constraints.len() - 2;
59
60 Layout::default()
61 .direction(Direction::Vertical)
62 .constraints(row_constraints)
63 .split(area)
64 .iter()
65 .skip(1)
66 .take(row_constraints_length)
67 .zip(self.0.iter().map(|row| {
68 let begin_length = if row.centered {
69 Constraint::Length(
70 (area.width
71 - row
72 .items
73 .iter()
74 .map(|item| item.width.apply(area.width))
75 .sum::<u16>())
76 / 2,
77 )
78 } else {
79 Constraint::Length(0)
80 };
81
82 let mut out = vec![begin_length];
83 out.extend(row.items.iter().map(|item| item.width));
84 out.push(Constraint::Length(0));
85 out
86 }))
87 .map(|(row_chunk, constraints)| {
88 let constraints_length = constraints.len() - 2;
89
90 Layout::default()
91 .direction(Direction::Horizontal)
92 .constraints(constraints)
93 .split(*row_chunk)
94 .iter()
95 .skip(1)
96 .take(constraints_length)
97 .copied()
98 .collect()
99 })
100 .collect::<Vec<_>>()
101 }
102
103 pub fn get(&self, x: usize, y: usize) -> &dyn FrameworkItem {
105 &*self.0[y].items[x].item
106 }
107
108 pub fn get_mut(&mut self, x: usize, y: usize) -> &mut Box<dyn FrameworkItem> {
110 &mut self.0[y].items[x].item
111 }
112}
113
114#[derive(Clone, Copy, PartialEq, Eq)]
118pub enum CursorState {
119 None,
121 Hover(usize, usize),
123 Selected(usize, usize),
125}
126
127impl Default for CursorState {
128 fn default() -> Self {
129 Self::None
130 }
131}
132
133impl CursorState {
134 pub fn is_selected(&self) -> bool {
135 matches!(self, Self::Selected(_, _))
136 }
137
138 pub fn is_hover(&self) -> bool {
139 matches!(self, Self::Hover(_, _))
140 }
141
142 pub fn is_none(&self) -> bool {
143 self == &Self::None
144 }
145}
146
147impl CursorState {
148 pub fn select(&mut self) -> Result<(), FrameworkError> {
151 match self {
152 Self::Hover(x, y) => *self = Self::Selected(*x, *y),
153 _ => return Err(FrameworkError::CursorStateMismatch),
154 }
155
156 Ok(())
157 }
158
159 pub fn deselect(&mut self) -> Result<(), FrameworkError> {
161 match self {
162 Self::Selected(x, y) => *self = Self::Hover(*x, *y),
163 _ => return Err(FrameworkError::CursorStateMismatch),
164 }
165
166 Ok(())
167 }
168}
169
170impl CursorState {
171 pub fn to_hover(location: (usize, usize)) -> Self {
172 Self::Hover(location.0, location.1)
173 }
174
175 pub fn to_selected(location: (usize, usize)) -> Self {
176 Self::Selected(location.0, location.1)
177 }
178
179 pub fn hover(&self, selectables: &[Vec<(usize, usize)>]) -> Option<(usize, usize)> {
180 match self {
181 Self::Hover(x, y) if !selectables.is_empty() => {
182 Some(Self::selectables_to_coors(selectables, (*x, *y)))
183 }
184 _ => None,
185 }
186 }
187
188 pub fn selected(&self, selectables: &[Vec<(usize, usize)>]) -> Option<(usize, usize)> {
189 match self {
190 Self::Selected(x, y) if !selectables.is_empty() => {
191 Some(Self::selectables_to_coors(selectables, (*x, *y)))
192 }
193 _ => None,
194 }
195 }
196
197 fn selectables_to_coors(
198 selectables: &[Vec<(usize, usize)>],
199 location: (usize, usize),
200 ) -> (usize, usize) {
201 let (location_x, location_y) = location;
202
203 selectables[location_y][location_x]
204 }
205}
206
207impl CursorState {
208 pub fn r#move(
210 &mut self,
211 direction: FrameworkDirection,
212 selectables: &[Vec<(usize, usize)>],
213 ) -> Result<(), FrameworkError> {
214 match direction {
215 FrameworkDirection::Up => self.up(),
216 FrameworkDirection::Down => self.down(),
217 FrameworkDirection::Left => self.left(),
218 FrameworkDirection::Right => self.right(),
219 }?;
220
221 self.move_check(selectables);
222
223 Ok(())
224 }
225
226 fn move_check(&mut self, selectables: &[Vec<(usize, usize)>]) {
227 if let Self::Hover(x, y) = self {
228 if selectables.is_empty() {
229 *x = 0;
230 *y = 0;
231 return;
232 }
233 let y_max = selectables.len() - 1;
234 if *y > y_max {
235 *y = y_max;
236 }
237
238 let x_max = selectables[*y].len() - 1;
239 if *x > x_max {
240 *x = x_max;
241 }
242 } else {
243 unreachable!("move_check is only called after a hovering cursor is moved, when cursor is at hover state")
244 }
245 }
246
247 fn left(&mut self) -> Result<(), FrameworkError> {
248 match self {
249 Self::Hover(x, _) => {
250 if *x != 0 {
251 *x -= 1
252 }
253 }
254 Self::None => *self = Self::Hover(0, 0),
255 Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
256 }
257
258 Ok(())
259 }
260
261 fn right(&mut self) -> Result<(), FrameworkError> {
262 match self {
263 Self::Hover(x, _) => *x += 1,
264 Self::None => *self = Self::Hover(usize::MAX, 0),
265 Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
266 }
267
268 Ok(())
269 }
270
271 fn up(&mut self) -> Result<(), FrameworkError> {
272 match self {
273 Self::Hover(_, y) => {
274 if *y != 0 {
275 *y -= 1
276 }
277 }
278 Self::None => *self = Self::Hover(0, 0),
279 Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
280 }
281
282 Ok(())
283 }
284
285 fn down(&mut self) -> Result<(), FrameworkError> {
286 match self {
287 Self::Hover(_, y) => *y += 1,
288 Self::None => *self = Self::Hover(0, usize::MAX),
289 Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
290 }
291
292 Ok(())
293 }
294}
295
296#[derive(Clone, Copy)]
298pub enum FrameworkDirection {
299 Up,
300 Down,
301 Left,
302 Right,
303}
304
305#[derive(Clone, Copy)]
307pub struct ItemInfo {
308 pub selected: bool,
309 pub hover: bool,
310 pub x: usize,
311 pub y: usize,
312}