1use std::borrow::Cow;
2use std::cmp::min;
3
4use ratatui::{layout::Rect, prelude::Widget, style::Color, text::Line, widgets::Paragraph, Frame};
5
6use crate::config::{ColorG, Gradient, MENU_STYLES};
7use crate::io::Offseted;
8use crate::modes::{Content, ContentWindow};
9
10#[macro_export]
12macro_rules! colored_skip_take {
13 ($t:ident, $u:ident) => {
14 std::iter::zip(
15 $t.iter().enumerate(),
16 Gradient::new(
17 ColorG::from_ratatui(
18 MENU_STYLES
19 .get()
20 .expect("Menu colors should be set")
21 .first
22 .fg
23 .unwrap_or(Color::Rgb(0, 0, 0)),
24 )
25 .unwrap_or_default(),
26 ColorG::from_ratatui(
27 MENU_STYLES
28 .get()
29 .expect("Menu colors should be set")
30 .palette_3
31 .fg
32 .unwrap_or(Color::Rgb(0, 0, 0)),
33 )
34 .unwrap_or_default(),
35 $t.len(),
36 )
37 .gradient()
38 .map(|color| color.into()),
39 )
40 .map(|((index, line), style)| (index, line, style))
41 .skip($u.top)
42 .take(min($u.len, $t.len()))
43 };
44}
45
46pub trait CowStr {
49 fn cow_str(&self) -> Cow<str>;
50}
51
52impl CowStr for (char, std::path::PathBuf) {
53 fn cow_str(&self) -> Cow<str> {
54 format!("{c} {p}", c = self.0, p = self.1.display()).into()
55 }
56}
57
58impl CowStr for std::path::PathBuf {
59 fn cow_str(&self) -> Cow<str> {
60 self.to_string_lossy()
61 }
62}
63
64impl CowStr for String {
65 fn cow_str(&self) -> Cow<str> {
66 self.into()
67 }
68}
69
70impl CowStr for &str {
71 fn cow_str(&self) -> Cow<str> {
72 (*self).into()
73 }
74}
75
76pub trait DrawMenu<T: CowStr> {
82 fn draw_menu(&self, f: &mut Frame, rect: &Rect, window: &ContentWindow)
83 where
84 Self: Content<T>,
85 {
86 let mut p_rect = rect.offseted(4, 3);
87 p_rect.height = p_rect.height.saturating_sub(2);
88 let content = self.content();
89 let lines: Vec<_> = colored_skip_take!(content, window)
90 .map(|(index, item, style)| Line::styled(item.cow_str(), self.style(index, &style)))
91 .collect();
92 Paragraph::new(lines).render(p_rect, f.buffer_mut());
93 }
94}
95
96#[macro_export]
104macro_rules! impl_draw_menu_with_char {
105 ($struct:ident, $field_type:ty) => {
106 use std::{cmp::min, iter::zip};
107
108 use ratatui::{
109 layout::{Offset, Rect},
110 prelude::Widget,
111 style::Color,
112 text::Line,
113 widgets::Paragraph,
114 Frame,
115 };
116
117 use $crate::colored_skip_take;
118 use $crate::config::{ColorG, Gradient, MENU_STYLES};
119 use $crate::io::{CowStr, DrawMenu};
120 use $crate::modes::ContentWindow;
121
122 impl DrawMenu<$field_type> for $struct {
123 fn draw_menu(&self, f: &mut Frame, rect: &Rect, window: &ContentWindow)
124 where
125 Self: Content<$field_type>,
126 {
127 let mut p_rect = rect.offset(Offset { x: 2, y: 3 }).intersection(*rect);
128 p_rect.height = p_rect.height.saturating_sub(2);
129 let content = self.content();
130 let lines: Vec<_> = zip(
131 ('a'..='z').cycle().skip(window.top),
132 colored_skip_take!(content, window),
133 )
134 .filter(|(_, (index, _, _))| {
135 ((*index) as u16 + ContentWindow::WINDOW_MARGIN_TOP_U16 + 1)
136 .saturating_sub(window.top as u16)
137 + 2
138 <= rect.height
139 })
140 .map(|(letter, (index, path, style))| {
141 Line::styled(
142 format!("{letter} {path}", path = path.cow_str()),
143 self.style(index, &style),
144 )
145 })
146 .collect();
147 Paragraph::new(lines).render(p_rect, f.buffer_mut());
148 }
149 }
150 };
151}