rustty/core/
cellbuffer.rs

1use std::ops::{Index, IndexMut, Deref, DerefMut};
2
3use core::position::{Pos, Size, HasSize};
4
5// I tried really hard to implement Index + IndexMut directly in the trait, but I coudn't get it
6// to compile...
7
8pub trait CellAccessor: HasSize {
9    fn cellvec(&self) -> &Vec<Cell>;
10    fn cellvec_mut(&mut self) -> &mut Vec<Cell>;
11
12    /// Clears `self`, using the given `Cell` as a blank.
13    fn clear(&mut self, blank: Cell) {
14        for cell in self.cellvec_mut().iter_mut() {
15            *cell = blank;
16        }
17    }
18
19    fn pos_to_index(&self, x: usize, y: usize) -> Option<usize> {
20        let (cols, rows) = self.size();
21        if x < cols && y < rows {
22            Some((cols * y) + x)
23        } else {
24            None
25        }
26    }
27
28    /// Returns a reference to the `Cell` at the given coordinates, or `None` if the index is out of
29    /// bounds.
30    ///
31    /// # Examples
32    ///
33    /// ```no_run
34    /// use rustty::{Terminal, CellAccessor};
35    ///
36    /// let mut term = Terminal::new().unwrap();
37    ///
38    /// let a_cell = term.get(5, 5);
39    /// ```
40    fn get(&self, x: usize, y: usize) -> Option<&Cell> {
41        match self.pos_to_index(x, y) {
42            Some(i) => self.cellvec().get(i),
43            None => None,
44        }
45    }
46
47    /// Returns a mutable reference to the `Cell` at the given coordinates, or `None` if the index
48    /// is out of bounds.
49    ///
50    /// # Examples
51    ///
52    /// ```no_run
53    /// use rustty::{Terminal, CellAccessor};
54    ///
55    /// let mut term = Terminal::new().unwrap();
56    ///
57    /// let a_mut_cell = term.get_mut(5, 5);
58    /// ```
59    fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut Cell> {
60        match self.pos_to_index(x, y) {
61            Some(i) => self.cellvec_mut().get_mut(i),
62            None => None,
63        }
64    }
65}
66
67/// An array of `Cell`s that represents a terminal display.
68///
69/// A `CellBuffer` is a two-dimensional array of `Cell`s, each pair of indices correspond to a
70/// single point on the underlying terminal.
71///
72/// The first index, `Cellbuffer[y]`, corresponds to a row, and thus the y-axis. The second
73/// index, `Cellbuffer[y][x]`, corresponds to a column within a row and thus the x-axis.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct CellBuffer {
76    cols: usize,
77    rows: usize,
78    buf: Vec<Cell>,
79}
80
81impl CellBuffer {
82    /// Constructs a new `CellBuffer` with the given number of columns and rows, using the given
83    /// `cell` as a blank.
84    pub fn new(cols: usize, rows: usize, cell: Cell) -> CellBuffer {
85        CellBuffer {
86            cols: cols,
87            rows: rows,
88            buf: vec![cell; cols * rows],
89        }
90    }
91
92    /// Resizes `CellBuffer` to the given number of rows and columns, using the given `Cell` as
93    /// a blank.
94    pub fn resize(&mut self, newcols: usize, newrows: usize, blank: Cell) {
95        let newlen = newcols * newrows;
96        let mut newbuf: Vec<Cell> = Vec::with_capacity(newlen);
97        for y in 0..newrows {
98            for x in 0..newcols {
99                let cell = self.get(x, y).unwrap_or(&blank);
100                newbuf.push(*cell);
101            }
102        }
103        self.buf = newbuf;
104        self.cols = newcols;
105        self.rows = newrows;
106    }
107}
108
109impl HasSize for CellBuffer {
110    fn size(&self) -> Size {
111        (self.cols, self.rows)
112    }
113}
114
115impl CellAccessor for CellBuffer {
116    fn cellvec(&self) -> &Vec<Cell> {
117        &self.buf
118    }
119
120    fn cellvec_mut(&mut self) -> &mut Vec<Cell> {
121        &mut self.buf
122    }
123}
124
125impl Deref for CellBuffer {
126    type Target = [Cell];
127
128    fn deref<'a>(&'a self) -> &'a [Cell] {
129        &self.buf
130    }
131}
132
133impl DerefMut for CellBuffer {
134    fn deref_mut<'a>(&'a mut self) -> &'a mut [Cell] {
135        &mut self.buf
136    }
137}
138
139impl Index<Pos> for CellBuffer {
140    type Output = Cell;
141
142    fn index<'a>(&'a self, index: Pos) -> &'a Cell {
143        let (x, y) = index;
144        self.get(x, y).expect("index out of bounds")
145    }
146}
147
148impl IndexMut<Pos> for CellBuffer {
149    fn index_mut<'a>(&'a mut self, index: Pos) -> &'a mut Cell {
150        let (x, y) = index;
151        self.get_mut(x, y).expect("index out of bounds")
152    }
153}
154
155impl Default for CellBuffer {
156    /// Constructs a new `CellBuffer` with a size of `(0, 0)`, using the default `Cell` as a blank.
157    fn default() -> CellBuffer {
158        CellBuffer::new(0, 0, Cell::default())
159    }
160}
161
162/// A single point on a terminal display.
163///
164/// A `Cell` contains a character and style.
165#[derive(Debug, Copy, Clone, PartialEq, Eq)]
166pub struct Cell {
167    ch: char,
168    fg: Color,
169    bg: Color,
170    attrs: Attr,
171}
172
173impl Cell {
174    /// Creates a new `Cell` with the given `char`, `Color`s and `Attr`.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use rustty::{Cell, Color, Attr};
180    ///
181    /// let cell = Cell::new('x', Color::Default, Color::Green, Attr::Default);
182    /// assert_eq!(cell.ch(), 'x');
183    /// assert_eq!(cell.fg(), Color::Default);
184    /// assert_eq!(cell.bg(), Color::Green);
185    /// assert_eq!(cell.attrs(), Attr::Default);
186    /// ```
187    pub fn new(ch: char, fg: Color, bg: Color, attrs: Attr) -> Cell {
188        Cell {
189            ch: ch,
190            fg: fg,
191            bg: bg,
192            attrs: attrs,
193        }
194    }
195
196    /// Creates a new `Cell` with the given `char` and default style.
197    ///
198    /// # Examples
199    ///
200    /// ```
201    /// use rustty::{Cell, Color, Attr};
202    ///
203    /// let mut cell = Cell::with_char('x');
204    /// assert_eq!(cell.ch(), 'x');
205    /// assert_eq!(cell.fg(), Color::Default);
206    /// assert_eq!(cell.bg(), Color::Default);
207    /// assert_eq!(cell.attrs(), Attr::Default);
208    /// ```
209    pub fn with_char(ch: char) -> Cell {
210        Cell::new(ch, Color::Default, Color::Default, Attr::Default)
211    }
212
213    /// Creates a new `Cell` with the given style and a blank `char`.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use rustty::{Cell, Color, Attr};
219    ///
220    /// let mut cell = Cell::with_style(Color::Default, Color::Red, Attr::Bold);
221    /// assert_eq!(cell.fg(), Color::Default);
222    /// assert_eq!(cell.bg(), Color::Red);
223    /// assert_eq!(cell.attrs(), Attr::Bold);
224    /// assert_eq!(cell.ch(), ' ');
225    /// ```
226    pub fn with_style(fg: Color, bg: Color, attr: Attr) -> Cell {
227        Cell::new(' ', fg, bg, attr)
228    }
229
230    /// Returns the `Cell`'s character.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use rustty::Cell;
236    ///
237    /// let mut cell = Cell::with_char('x');
238    /// assert_eq!(cell.ch(), 'x');
239    /// ```
240    pub fn ch(&self) -> char {
241        self.ch
242    }
243
244    /// Sets the `Cell`'s character to the given `char`
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// use rustty::Cell;
250    ///
251    /// let mut cell = Cell::with_char('x');
252    /// assert_eq!(cell.ch(), 'x');
253    ///
254    /// cell.set_ch('y');
255    /// assert_eq!(cell.ch(), 'y');
256    /// ```
257    pub fn set_ch(&mut self, newch: char) -> &mut Cell {
258        self.ch = newch;
259        self
260    }
261
262    /// Returns the `Cell`'s foreground `Color`.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use rustty::{Cell, Color, Attr};
268    ///
269    /// let mut cell = Cell::with_style(Color::Blue, Color::Default, Attr::Default);
270    /// assert_eq!(cell.fg(), Color::Blue);
271    /// ```
272    pub fn fg(&self) -> Color {
273        self.fg
274    }
275
276    /// Sets the `Cell`'s foreground `Color` to the given `Color`.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use rustty::{Cell, Color, Attr};
282    ///
283    /// let mut cell = Cell::default();
284    /// assert_eq!(cell.fg(), Color::Default);
285    ///
286    /// cell.set_fg(Color::White);
287    /// assert_eq!(cell.fg(), Color::White);
288    /// ```
289    pub fn set_fg(&mut self, newfg: Color) -> &mut Cell {
290        self.fg = newfg;
291        self
292    }
293
294    /// Returns the `Cell`'s background `Color`.
295    ///
296    /// # Examples
297    ///
298    /// ```
299    /// use rustty::{Cell, Color, Attr};
300    ///
301    /// let mut cell = Cell::with_style(Color::Default, Color::Green, Attr::Default);
302    /// assert_eq!(cell.bg(), Color::Green);
303    /// ```
304    pub fn bg(&self) -> Color {
305        self.bg
306    }
307
308    /// Sets the `Cell`'s background `Color` to the given `Color`.
309    ///
310    /// # Examples
311    ///
312    /// ```
313    /// use rustty::{Cell, Color, Attr};
314    ///
315    /// let mut cell = Cell::default();
316    /// assert_eq!(cell.bg(), Color::Default);
317    ///
318    /// cell.set_bg(Color::Black);
319    /// assert_eq!(cell.bg(), Color::Black);
320    /// ```
321    pub fn set_bg(&mut self, newbg: Color) -> &mut Cell {
322        self.bg = newbg;
323        self
324    }
325
326    pub fn attrs(&self) -> Attr {
327        self.attrs
328    }
329
330    pub fn set_attrs(&mut self, newattrs: Attr) -> &mut Cell {
331        self.attrs = newattrs;
332        self
333    }
334}
335
336impl Default for Cell {
337    /// Constructs a new `Cell` with a blank `char` and default `Color`s.
338    ///
339    /// # Examples
340    ///
341    /// ```
342    /// use rustty::{Cell, Color};
343    ///
344    /// let mut cell = Cell::default();
345    /// assert_eq!(cell.ch(), ' ');
346    /// assert_eq!(cell.fg(), Color::Default);
347    /// assert_eq!(cell.bg(), Color::Default);
348    /// ```
349    fn default() -> Cell {
350        Cell::new(' ', Color::Default, Color::Default, Attr::Default)
351    }
352}
353
354/// The color of a `Cell`.
355///
356/// `Color::Default` represents the default color of the underlying terminal.
357///
358/// The eight basic colors may be used directly and correspond to 0x00..0x07 in the 8-bit (256)
359/// color range; in addition, the eight basic colors coupled with `Attr::Bold` correspond to
360/// 0x08..0x0f in the 8-bit color range.
361///
362/// `Color::Byte(..)` may be used to specify a color in the 8-bit range.
363///
364/// # Examples
365///
366/// ```
367/// use rustty::Color;
368///
369/// // The default color.
370/// let default = Color::Default;
371///
372/// // A basic color.
373/// let red = Color::Red;
374///
375/// // An 8-bit color.
376/// let fancy = Color::Byte(0x01);
377///
378/// // Basic colors are also 8-bit colors (but not vice-versa).
379/// assert_eq!(red.as_byte(), fancy.as_byte())
380/// ```
381#[derive(Debug, Copy, Clone, PartialEq, Eq)]
382pub enum Color {
383    Black,
384    Red,
385    Green,
386    Yellow,
387    Blue,
388    Magenta,
389    Cyan,
390    White,
391    Byte(u8),
392    Default,
393}
394
395impl Color {
396    /// Returns the `u8` representation of the `Color`.
397    pub fn as_byte(&self) -> u8 {
398        match *self {
399            Color::Black => 0x00,
400            Color::Red => 0x01,
401            Color::Green => 0x02,
402            Color::Yellow => 0x03,
403            Color::Blue => 0x04,
404            Color::Magenta => 0x05,
405            Color::Cyan => 0x06,
406            Color::White => 0x07,
407            Color::Byte(b) => b,
408            Color::Default => panic!("Attempted to cast default color to u8"),
409        }
410    }
411}
412
413/// The attributes of a `Cell`.
414///
415/// `Attr` enumerates all combinations of attributes a given style may have.
416///
417/// `Attr::Default` represents no attribute.
418///
419/// # Examples
420///
421/// ```
422/// use rustty::Attr;
423///
424/// // Default attribute.
425/// let def = Attr::Default;
426///
427/// // Base attribute.
428/// let base = Attr::Bold;
429///
430/// // Combination.
431/// let comb = Attr::UnderlineReverse;
432/// ```
433#[derive(Debug, Copy, Clone, PartialEq, Eq)]
434pub enum Attr {
435    Default = 0b000,
436    Bold = 0b001,
437    Underline = 0b010,
438    BoldUnderline = 0b011,
439    Reverse = 0b100,
440    BoldReverse = 0b101,
441    UnderlineReverse = 0b110,
442    BoldReverseUnderline = 0b111,
443}