tuviv/terminal/
buffer.rs

1//! Terminal representation.
2
3use std::fmt::{self, Display};
4
5use le::layout::{Rect, Vec2};
6use unicode_segmentation::UnicodeSegmentation;
7use unicode_width::UnicodeWidthStr;
8
9use super::{Style, StyledText};
10
11/// The text stored in a cell
12#[derive(Default, Debug, Clone, PartialEq, Eq)]
13pub enum CellText {
14    /// Only used if you start with the zero width chars
15    String(String),
16    /// A character
17    Char(char),
18    /// Literally nothing
19    #[default]
20    None,
21}
22
23impl CellText {
24    /// Determines if the cell is empty
25    pub fn is_empty(&self) -> bool {
26        match self {
27            CellText::String(x) => x.is_empty(),
28            CellText::Char(_) => false,
29            CellText::None => true,
30        }
31    }
32}
33
34impl Display for CellText {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            CellText::String(x) => x.fmt(f),
38            CellText::Char(x) => x.fmt(f),
39            CellText::None => ' '.fmt(f),
40        }
41    }
42}
43
44/// A single cell on the buffer with text and style.
45#[derive(Debug, Clone, Default, PartialEq, Eq)]
46pub struct Cell {
47    /// The text.
48    ///
49    /// This is a string because certain combinations
50    /// of unicode characters may still fit in a cell.
51    pub text: CellText,
52    /// The style of the cell
53    pub style: Style,
54}
55
56/// A buffer of cells storing its size and text.
57#[derive(Debug, Clone)]
58pub struct Buffer {
59    /// The size of the buffer
60    size: Vec2,
61    /// The text in the buffer
62    // TODO: Make this a 1d array
63    text: Vec<Vec<Cell>>,
64}
65
66impl Buffer {
67    /// Creates a new buffer of the given size.
68    ///
69    /// The size will normally be the size of the terminal.
70    pub fn new(size: Vec2) -> Self {
71        Self {
72            size,
73            text: vec![vec![Cell::default(); size.y]; size.x],
74        }
75    }
76
77    /// Clears the buffer, writing all cells as empty.
78    pub fn clear(&mut self) {
79        for column in &mut self.text {
80            for cell in column {
81                *cell = Cell::default();
82            }
83        }
84    }
85
86    /// Gets the size of the Buffer
87    pub fn size(&self) -> Vec2 {
88        self.size
89    }
90
91    /// Gets all the text
92    pub fn text(&self) -> &[Vec<Cell>] {
93        &self.text
94    }
95
96    /// Gets a specfic cell
97    pub fn get(&self, pos: Vec2) -> Option<&Cell> {
98        self.text.get(pos.x).and_then(|x| x.get(pos.y))
99    }
100
101    /// Gets a mutable version of a cell
102    pub fn get_mut(&mut self, pos: Vec2) -> Option<&mut Cell> {
103        self.text.get_mut(pos.x).and_then(|x| x.get_mut(pos.y))
104    }
105
106    /// Writes the text to the buffer. The text should **NOT**
107    /// be multiline. Multiline text will not be handled correctly.
108    pub fn write<'a>(&mut self, pos: Vec2, text: impl Into<StyledText<'a>>) {
109        self.write_span(pos, &text.into());
110    }
111
112    /// Writes the span to the buffer. The text should **NOT**
113    /// be multiline. Multiline text will not be handled correctly.
114    pub fn write_span(&mut self, mut pos: Vec2, text: &StyledText) {
115        let graphemes = text.text.graphemes(true);
116        for grapheme in graphemes {
117            if let Some(previous) = self.get_mut(pos) {
118                let mut grapheme_chars = grapheme.chars();
119                let char1 = grapheme_chars.next();
120                let char2 = grapheme_chars.next();
121                match (char1, char2) {
122                    (Some(_char1), Some(_char2)) => {
123                        previous.text = CellText::String(grapheme.to_owned());
124                    }
125                    (Some(char1), None) => {
126                        previous.text = CellText::Char(char1);
127                    }
128                    _ => (),
129                }
130                previous.style = previous.style.overlay(text.style);
131            }
132            pos.x += grapheme.width();
133        }
134    }
135
136    /// Writes another buffer over this buffer
137    pub fn write_buffer(&mut self, them: Buffer, pos: Vec2) {
138        for (x, column) in them.text.into_iter().enumerate() {
139            for (y, cell) in column.into_iter().enumerate() {
140                let cell_pos = pos + Vec2::new(x, y);
141
142                if let Some(previous) = self.get_mut(cell_pos) {
143                    previous.text = cell.text;
144                    previous.style = previous.style.overlay(cell.style);
145                };
146            }
147        }
148    }
149
150    /// Writes another buffer over this buffer, only cloning when need be
151    pub fn write_buffer_ref(&mut self, them: &Buffer, pos: Vec2) {
152        for (x, column) in them.text.iter().enumerate() {
153            for (y, cell) in column.iter().enumerate() {
154                let cell_pos = pos + Vec2::new(x, y);
155
156                if let Some(previous) = self.get_mut(cell_pos) {
157                    previous.text = cell.text.clone();
158                    previous.style = previous.style.overlay(cell.style);
159                };
160            }
161        }
162    }
163
164    /// Writes a character to the buffer
165    pub fn write_char(&mut self, c: char, pos: Vec2) {
166        if let Some(cell) = self.get_mut(pos) {
167            cell.text = CellText::Char(c);
168        }
169    }
170
171    /// Style a specific cell
172    pub fn style_cell(&mut self, style: Style, pos: Vec2) {
173        if let Some(cell) = self.get_mut(pos) {
174            cell.style = style;
175        }
176    }
177
178    /// Subtracts the positions of some vectors on this buffer.
179    ///
180    /// This is not particularly cheap.
181    pub fn subtract_pos(&mut self, pos: Vec2) {
182        for _x in 0..(pos.x.min(self.text.len())) {
183            self.text.remove(0);
184        }
185        for column in &mut self.text {
186            for _y in 0..(pos.y.min(column.len())) {
187                column.remove(0);
188            }
189        }
190    }
191
192    /// Clips a buffer to a specific size, discarding the
193    /// other information.
194    pub fn clip(&mut self, clip: Vec2) {
195        let mut column = 0;
196        self.text.retain(|_| {
197            column += 1;
198            (column - 1) < clip.x
199        });
200
201        for column in &mut self.text {
202            let mut row = 0;
203            column.retain(|_| {
204                row += 1;
205                (row - 1) < clip.y
206            });
207        }
208    }
209
210    /// Crops the buffer to a specific rect
211    pub fn crop(&mut self, crop: Rect) {
212        let mut column = 0;
213        self.text.retain(|_| {
214            let res = crop.start.x <= column && column < crop.end().x;
215            column += 1;
216            res
217        });
218
219        for column in &mut self.text {
220            let mut row = 0;
221            column.retain(|_| {
222                let res = crop.start.y <= row && row < crop.end().y;
223                row += 1;
224                res
225            });
226        }
227    }
228}