1use std::convert::TryInto;
2use std::str::FromStr;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum ScrollDirection {
7 Up(u16),
9 Right(u16),
11 Down(u16),
13 Left(u16),
15 Top,
17 Bottom,
19}
20
21impl FromStr for ScrollDirection {
22 type Err = ();
23 fn from_str(s: &str) -> Result<Self, Self::Err> {
24 let s = s.split_whitespace().collect::<Vec<&str>>();
25 let value = s.get(1).cloned().unwrap_or_default().parse().unwrap_or(1);
26 match s.first().cloned() {
27 Some("up") | Some("u") => Ok(Self::Up(value)),
28 Some("right") | Some("r") => Ok(Self::Right(value)),
29 Some("down") | Some("d") => Ok(Self::Down(value)),
30 Some("left") | Some("l") => Ok(Self::Left(value)),
31 Some("top") | Some("t") => Ok(Self::Top),
32 Some("bottom") | Some("b") => Ok(Self::Bottom),
33 _ => Err(()),
34 }
35 }
36}
37
38#[derive(Clone, Copy, Debug, Default)]
40pub struct ScrollAmount {
41 pub vertical: u16,
43 pub horizontal: u16,
45}
46
47#[derive(Clone, Debug)]
49pub struct RowItem {
50 pub data: Vec<String>,
52 max_width: Option<u16>,
54 max_height: u16,
56 height_overflow: u16,
58 scroll: ScrollAmount,
60}
61
62impl RowItem {
63 pub fn new(
65 data: Vec<String>,
66 max_width: Option<u16>,
67 max_height: u16,
68 scroll: ScrollAmount,
69 ) -> Self {
70 let mut item = Self {
71 max_width,
72 max_height,
73 height_overflow: (data.len().saturating_sub(max_height.into()) + 1)
74 .try_into()
75 .unwrap_or_default(),
76 scroll,
77 data,
78 };
79 item.process();
80 item
81 }
82
83 fn process(&mut self) {
88 if self.height_overflow != 1 {
89 if self.scroll.vertical != 0 {
90 self.scroll_vertical();
91 }
92 if self.scroll.vertical < self.height_overflow {
93 self.limit_height(self.max_height);
94 }
95 }
96 if let Some(width) = self.max_width {
97 if self.scroll.horizontal != 0
98 && match self.data.iter().max_by(|x, y| x.len().cmp(&y.len())) {
99 Some(line) => line.len() >= width.into(),
100 None => false,
101 } {
102 self.scroll_horizontal();
103 }
104 self.limit_width(width);
105 }
106 }
107
108 fn scroll_vertical(&mut self) {
110 self.data = self
111 .data
112 .iter()
113 .skip(if self.scroll.vertical <= self.height_overflow {
114 self.scroll.vertical.into()
115 } else {
116 self.height_overflow.into()
117 })
118 .enumerate()
119 .map(|(i, line)| {
120 if i == 0 {
121 String::from("...")
122 } else {
123 line.to_string()
124 }
125 })
126 .collect::<Vec<String>>()
127 }
128
129 fn scroll_horizontal(&mut self) {
131 self.data = self
132 .data
133 .iter()
134 .map(|line| {
135 match line
136 .char_indices()
137 .nth((self.scroll.horizontal + 1).into())
138 {
139 Some((pos, _)) => {
140 format!(".{}", &line[pos..])
141 }
142 None => String::new(),
143 }
144 })
145 .collect::<Vec<String>>();
146 }
147
148 fn limit_width(&mut self, width: u16) {
150 self.data = self
151 .data
152 .iter()
153 .map(|line| match line.char_indices().nth(width.into()) {
154 Some((pos, _)) => format!("{}..", &line[0..pos]),
155 None => line.to_string(),
156 })
157 .collect::<Vec<String>>()
158 }
159
160 fn limit_height(&mut self, height: u16) {
162 self.data = self
163 .data
164 .drain(0..(height).into())
165 .enumerate()
166 .map(|(i, line)| {
167 if i == (height - 1) as usize {
168 String::from("...")
169 } else {
170 line
171 }
172 })
173 .collect::<Vec<String>>()
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use pretty_assertions::assert_eq;
181 #[test]
182 fn test_widget_row() -> Result<(), ()> {
183 assert_eq!(
184 vec!["..", ".ne3", ".ne4", ".."],
185 RowItem::new(
186 vec![
187 String::from("line1"),
188 String::from("line2"),
189 String::from("line3"),
190 String::from("line4"),
191 String::from("line5"),
192 ],
193 Some(4),
194 4,
195 ScrollAmount {
196 vertical: 1,
197 horizontal: 1,
198 },
199 )
200 .data
201 );
202 assert_eq!(
203 ScrollDirection::Right(5),
204 ScrollDirection::from_str("right 5")?
205 );
206 assert_eq!(
207 ScrollDirection::Left(9),
208 ScrollDirection::from_str("left 9")?
209 );
210 assert_eq!(ScrollDirection::Down(1), ScrollDirection::from_str("d")?);
211 assert_eq!(ScrollDirection::Top, ScrollDirection::from_str("top")?);
212 assert_eq!(
213 ScrollDirection::Bottom,
214 ScrollDirection::from_str("bottom")?
215 );
216 assert!(ScrollDirection::from_str("xyz").is_err());
217 Ok(())
218 }
219}