promkit_core/
pane.rs

1use std::sync::LazyLock;
2
3use crate::grapheme::StyledGraphemes;
4
5pub static EMPTY_PANE: LazyLock<Pane> = LazyLock::new(|| Pane::new(vec![], 0));
6
7#[derive(Clone)]
8pub struct Pane {
9    /// The layout of graphemes within the pane.
10    /// This vector stores the styled graphemes that make up the content of the pane.
11    layout: Vec<StyledGraphemes>,
12    /// The offset from the top of the pane, used when extracting graphemes to display.
13    /// This value determines the starting point for grapheme extraction, allowing for scrolling behavior.
14    offset: usize,
15}
16
17impl Pane {
18    /// Constructs a new `Pane` with the specified layout, offset, and optional fixed height.
19    /// - `layout`: A vector of `StyledGraphemes` representing the content of the pane.
20    /// - `offset`: The initial offset from the top of the pane.
21    pub fn new(layout: Vec<StyledGraphemes>, offset: usize) -> Self {
22        Pane { layout, offset }
23    }
24
25    pub fn visible_row_count(&self) -> usize {
26        self.layout.len()
27    }
28
29    /// Checks if the pane is empty.
30    pub fn is_empty(&self) -> bool {
31        self.layout.is_empty()
32    }
33
34    pub fn extract(&self, viewport_height: usize) -> Vec<StyledGraphemes> {
35        let lines = self.layout.len().min(viewport_height);
36        let mut start = self.offset;
37        let end = self.offset + lines;
38        if end > self.layout.len() {
39            start = self.layout.len().saturating_sub(lines);
40        }
41
42        self.layout
43            .iter()
44            .enumerate()
45            .filter(|(i, _)| start <= *i && *i < end)
46            .map(|(_, row)| row.clone())
47            .collect::<Vec<_>>()
48    }
49}
50
51#[cfg(test)]
52mod test {
53    use super::*;
54
55    mod visible_row_count {
56        use super::*;
57
58        #[test]
59        fn test() {
60            let pane = Pane::new(vec![], 0);
61            assert_eq!(0, pane.visible_row_count())
62        }
63    }
64
65    mod is_empty {
66        use super::*;
67
68        #[test]
69        fn test() {
70            assert_eq!(
71                true,
72                Pane {
73                    layout: StyledGraphemes::from("").matrixify(10, 10, 0).0,
74                    offset: 0,
75                }
76                .is_empty()
77            );
78        }
79    }
80    mod extract {
81        use super::*;
82
83        #[test]
84        fn test_with_less_extraction_size_than_layout() {
85            let expect = vec![
86                StyledGraphemes::from("aa"),
87                StyledGraphemes::from("bb"),
88                StyledGraphemes::from("cc"),
89            ];
90            assert_eq!(
91                expect,
92                Pane {
93                    layout: vec![
94                        StyledGraphemes::from("aa"),
95                        StyledGraphemes::from("bb"),
96                        StyledGraphemes::from("cc"),
97                        StyledGraphemes::from("dd"),
98                        StyledGraphemes::from("ee"),
99                    ],
100                    offset: 0,
101                }
102                .extract(3)
103            );
104        }
105
106        #[test]
107        fn test_with_much_extraction_size_than_layout() {
108            let expect = vec![
109                StyledGraphemes::from("aa"),
110                StyledGraphemes::from("bb"),
111                StyledGraphemes::from("cc"),
112                StyledGraphemes::from("dd"),
113                StyledGraphemes::from("ee"),
114            ];
115            assert_eq!(
116                expect,
117                Pane {
118                    layout: vec![
119                        StyledGraphemes::from("aa"),
120                        StyledGraphemes::from("bb"),
121                        StyledGraphemes::from("cc"),
122                        StyledGraphemes::from("dd"),
123                        StyledGraphemes::from("ee"),
124                    ],
125                    offset: 0,
126                }
127                .extract(10)
128            );
129        }
130
131        #[test]
132        fn test_with_within_extraction_size_and_offset_non_zero() {
133            let expect = vec![StyledGraphemes::from("cc"), StyledGraphemes::from("dd")];
134            assert_eq!(
135                expect,
136                Pane {
137                    layout: vec![
138                        StyledGraphemes::from("aa"),
139                        StyledGraphemes::from("bb"),
140                        StyledGraphemes::from("cc"),
141                        StyledGraphemes::from("dd"),
142                        StyledGraphemes::from("ee"),
143                    ],
144                    offset: 2, // indicate `cc`
145                }
146                .extract(2)
147            );
148        }
149
150        #[test]
151        fn test_with_beyond_extraction_size_and_offset_non_zero() {
152            let expect = vec![
153                StyledGraphemes::from("cc"),
154                StyledGraphemes::from("dd"),
155                StyledGraphemes::from("ee"),
156            ];
157            assert_eq!(
158                expect,
159                Pane {
160                    layout: vec![
161                        StyledGraphemes::from("aa"),
162                        StyledGraphemes::from("bb"),
163                        StyledGraphemes::from("cc"),
164                        StyledGraphemes::from("dd"),
165                        StyledGraphemes::from("ee"),
166                    ],
167                    offset: 3, // indicate `dd`
168                }
169                .extract(3)
170            );
171        }
172    }
173}