neophyte_ui/
grid.rs

1#![allow(unused)]
2
3use super::{
4    window::{Window, WindowOffset},
5    HlId,
6};
7use neophyte_linalg::{CellVec, Vec2};
8use neophyte_ui_event::{grid_line, hl_attr_define::Attributes, Anchor, GridScroll, HlAttrDefine};
9use packed_char::{Contents, PackedChar, U22};
10use std::{
11    collections::HashMap,
12    fmt::{self, Debug, Display, Formatter},
13    io::Write,
14    iter::TakeWhile,
15    marker::PhantomData,
16    mem::transmute,
17    ops::Not,
18    str::Chars,
19    vec::IntoIter,
20};
21
22pub type Id = u32;
23
24#[derive(Debug, Default, Clone)]
25pub struct Grid {
26    pub id: Id,
27    pub scroll_delta: i32,
28    pub dirty: DirtyFlags,
29    window: Window,
30    contents: GridContents,
31}
32
33#[derive(PartialEq, Eq, Debug, Default, Clone, Copy, PartialOrd, Ord)]
34pub struct DirtyFlags(u8);
35
36#[rustfmt::skip]
37impl DirtyFlags {
38    const CONTENTS: u8 = 0b01;
39    const WINDOW:   u8 = 0b10;
40}
41
42impl DirtyFlags {
43    pub fn set_contents(&mut self) {
44        self.0 |= Self::CONTENTS
45    }
46
47    pub fn contents(self) -> bool {
48        self.0 & Self::CONTENTS > 0
49    }
50
51    pub fn set_window(&mut self) {
52        self.0 |= Self::WINDOW
53    }
54
55    pub fn window(self) -> bool {
56        self.0 & Self::WINDOW > 0
57    }
58
59    pub fn clear(&mut self) {
60        self.0 = 0;
61    }
62}
63
64impl Grid {
65    pub fn new(id: Id) -> Self {
66        Self {
67            id,
68            ..Default::default()
69        }
70    }
71
72    pub fn contents(&self) -> &GridContents {
73        &self.contents
74    }
75
76    pub fn contents_mut(&mut self) -> &mut GridContents {
77        self.dirty.set_contents();
78        &mut self.contents
79    }
80
81    pub fn window(&self) -> &Window {
82        &self.window
83    }
84
85    pub fn window_mut(&mut self) -> &mut Window {
86        self.dirty.set_window();
87        &mut self.window
88    }
89
90    /// Reset dirty flags and scroll delta
91    pub fn clear_dirty(&mut self) {
92        self.dirty.clear();
93        self.scroll_delta = 0;
94    }
95}
96
97/// The contents of a grid
98#[derive(Default, Clone)]
99pub struct GridContents {
100    /// Grid dimensions
101    pub size: CellVec<u16>,
102    /// Grid cells in rows then columns
103    buffer: Vec<Cell>,
104    /// Contains cell contents for cells that require more than one char of
105    /// storage. This optimizes for the common case by keeping the main buffer
106    /// tightly packed.
107    overflow: Vec<String>,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
111pub struct Cell {
112    pub text: PackedChar,
113    pub highlight: HlId,
114}
115
116impl GridContents {
117    pub fn new() -> Self {
118        Self::default()
119    }
120
121    /// Resize the grid to the new dimensions
122    pub fn resize(&mut self, size: CellVec<u16>) {
123        let mut old = std::mem::take(&mut self.buffer);
124        self.buffer = vec![Cell::default(); size.0.area() as usize];
125        for (new, old) in self
126            .buffer
127            .chunks_mut(size.0.x as usize)
128            .zip(old.chunks(self.size.0.x.max(1) as usize))
129        {
130            for (new, old) in new.iter_mut().zip(old.iter()) {
131                *new = *old;
132            }
133        }
134        self.size = size;
135    }
136
137    /// Apply a grid_scroll event
138    pub fn scroll(&mut self, top: u16, bot: u16, left: u16, right: u16, rows: i32) {
139        let left = left as usize;
140        let right = right as usize;
141        let size: Vec2<usize> = self.size.0.cast_as();
142        let dst_top = top as i32 - rows;
143        let dst_bot = bot as i32 - rows;
144        if rows > 0 {
145            // Move a region up
146            let rows = rows as usize;
147            let top = top as usize + rows;
148            let bot = bot as usize;
149            for src_y in top..bot {
150                let dst_y = src_y - rows;
151                let (dst, src) = self.buffer.split_at_mut(src_y * size.x);
152                let dst = &mut dst[dst_y * size.x..];
153                let dst = &mut dst[left..right];
154                let src = &src[left..right];
155                dst.copy_from_slice(src);
156            }
157        } else {
158            // Move a region down
159            let rows = (-rows) as usize;
160            let top = top as usize;
161            let bot = bot as usize - rows;
162            for src_y in (top..bot).rev() {
163                let dst_y = src_y + rows;
164                let (src, dst) = self.buffer.split_at_mut(dst_y * size.x);
165                let src = &src[src_y * size.x..];
166                let src = &src[left..right];
167                let dst = &mut dst[left..right];
168                dst.copy_from_slice(src);
169            }
170        }
171    }
172
173    /// Apply a grid_line event
174    pub fn grid_line(&mut self, row: u16, col_start: u16, cells: Vec<grid_line::Cell>) {
175        let w = self.size.0.x as usize;
176        let start = row as usize * w;
177        let end = start + w;
178        let mut row = self.buffer[start..end].iter_mut().skip(col_start as usize);
179
180        let mut highlight = 0;
181        for cell in cells {
182            if let Some(hl_id) = cell.hl_id {
183                highlight = hl_id;
184            }
185
186            let repeat = cell.repeat.unwrap_or(1);
187            let mut chars = cell.text.chars();
188            let packed = match (chars.next(), chars.next()) {
189                (None, None) => PackedChar::from_char('\0'),
190                (None, Some(c)) => unreachable!(),
191                (Some(c), None) => PackedChar::from_char(c),
192                (Some(c1), Some(c2)) => {
193                    let i = self.overflow.len().try_into().unwrap();
194                    self.overflow.push(cell.text);
195                    PackedChar::from_u22(U22::from_u32(i).unwrap())
196                }
197            };
198            let cell = Cell {
199                text: packed,
200                highlight,
201            };
202
203            for _ in 0..repeat {
204                *row.next().unwrap() = cell;
205            }
206        }
207    }
208
209    /// Reset the contents of the grid
210    pub fn clear(&mut self) {
211        for dst in self.buffer.iter_mut() {
212            *dst = Cell::default();
213        }
214    }
215
216    /// Iterate over the grid contents row by row
217    pub fn rows(
218        &self,
219    ) -> impl Iterator<Item = impl Iterator<Item = CellContents<'_>> + '_ + Clone> + '_ + Clone
220    {
221        self.buffer.chunks(self.size.0.x as usize).map(|chunk| {
222            chunk.iter().map(|cell| {
223                let text = match cell.text.contents() {
224                    Contents::Char(c) => c.into(),
225                    Contents::U22(u22) => self.overflow[u22.as_u32() as usize].chars().into(),
226                };
227                CellContents {
228                    text,
229                    highlight: cell.highlight,
230                }
231            })
232        })
233    }
234}
235
236impl Debug for GridContents {
237    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
238        writeln!(f, "┏{:━<1$}┓", "", self.size.0.x as usize);
239        for row in self.rows() {
240            write!(f, "┃");
241            for mut cell in row {
242                let c = cell
243                    .text
244                    .next()
245                    .map(|c| if c == '\0' { ' ' } else { c })
246                    .unwrap_or(' ');
247                write!(f, "{}", c)?;
248            }
249            writeln!(f, "┃")?;
250        }
251        write!(f, "┗{:━<1$}┛", "", self.size.0.x as usize);
252        Ok(())
253    }
254}
255
256#[derive(Clone)]
257pub enum OnceOrChars<'a> {
258    Char(std::iter::Once<char>),
259    Chars(std::str::Chars<'a>),
260}
261
262impl From<char> for OnceOrChars<'_> {
263    fn from(c: char) -> Self {
264        Self::Char(std::iter::once(c))
265    }
266}
267
268impl<'a> From<std::str::Chars<'a>> for OnceOrChars<'a> {
269    fn from(value: std::str::Chars<'a>) -> Self {
270        Self::Chars(value)
271    }
272}
273
274impl Iterator for OnceOrChars<'_> {
275    type Item = char;
276
277    fn next(&mut self) -> Option<Self::Item> {
278        match self {
279            OnceOrChars::Char(iter) => iter.next(),
280            OnceOrChars::Chars(iter) => iter.next(),
281        }
282    }
283}
284
285#[derive(Clone)]
286pub struct CellContents<'a> {
287    pub highlight: u32,
288    pub text: OnceOrChars<'a>,
289}