fyodor/
canvas.rs

1use std::{
2    cell::{Ref, RefCell},
3    rc::Rc,
4};
5
6use crate::{
7    cell::Cell,
8    drawable::Drawable,
9    layout::{
10        sized::{KnownHeight, KnownWidth},
11        Dims, Pos,
12    },
13};
14
15pub struct Buffer {
16    buffer: Vec<Vec<Cell>>,
17    size: Dims,
18}
19
20impl Buffer {
21    pub fn new(size: impl Into<Dims>) -> Self {
22        let size = size.into();
23        let mut buffer = Vec::new();
24        for _ in 0..size.y {
25            buffer.push(vec![Cell::new(' '); size.x as usize]);
26        }
27        Buffer { buffer, size }
28    }
29
30    pub fn buf_ref(&self) -> &[Vec<Cell>] {
31        &self.buffer
32    }
33
34    pub fn buf_mut(&mut self) -> &mut [Vec<Cell>] {
35        &mut self.buffer
36    }
37
38    pub fn resize(&mut self, size: impl Into<Dims>) {
39        let size = size.into();
40        if self.size == size {
41            return;
42        }
43
44        self.size = size;
45        self.buffer.resize(size.y as usize, Vec::new());
46        for row in self.buffer.iter_mut() {
47            row.resize(size.x as usize, Cell::new(' '));
48        }
49    }
50
51    pub fn size(&self) -> Dims {
52        self.size
53    }
54}
55
56pub trait CanvasLike: KnownWidth + KnownHeight {
57    fn set(&mut self, pos: Dims, cell: Cell);
58    fn pos(&self) -> Dims;
59    fn size(&self) -> Dims;
60
61    // we need Self: Sized so that rust knows that we are
62    // not using this in a trait object
63    fn setd(&mut self, pos: impl Into<Dims>, cell: Cell)
64    where
65        Self: Sized,
66    {
67        self.set(pos.into(), cell);
68    }
69
70    fn fill(&mut self, cell: Cell) {
71        let size = self.size();
72        for y in 0..size.y {
73            for x in 0..size.x {
74                self.set(Pos::new(x, y), cell);
75            }
76        }
77    }
78}
79
80#[derive(Clone)]
81pub struct Canvas {
82    pub buffer: Rc<RefCell<Buffer>>,
83}
84
85impl Canvas {
86    pub fn new(buf: Buffer) -> Self {
87        Self {
88            buffer: Rc::new(RefCell::new(buf)),
89        }
90    }
91
92    pub fn from_dims(size: impl Into<Dims>) -> Self {
93        Self::new(Buffer::new(size))
94    }
95
96    pub fn set(&mut self, pos: impl Into<Dims>, cell: Cell) {
97        let pos = pos.into();
98        if let Some(c) = self
99            .buffer
100            .borrow_mut() // from RefCell
101            .buf_mut() // from Buffer
102            .get_mut(pos.y as usize) // from &mut [Vec<Cell>]
103            .and_then(|r| r.get_mut(pos.x as usize))
104        {
105            *c = cell;
106        }
107    }
108
109    pub fn size(&self) -> Dims {
110        self.buffer.borrow().size()
111    }
112
113    pub fn get(&self, pos: impl Into<Dims>) -> Option<Cell> {
114        let pos = pos.into();
115        self.buffer
116            .borrow() // from RefCell
117            .buf_ref() // from Buffer
118            .get(pos.y as usize) // from &[Vec<Cell>]
119            .and_then(|r| r.get(pos.x as usize))
120            .copied()
121    }
122
123    pub fn get_buf(&self) -> Ref<Buffer> {
124        self.buffer.borrow()
125    }
126
127    pub fn resize(&mut self, size: impl Into<Dims>) {
128        self.buffer.borrow_mut().resize(size);
129    }
130
131    pub fn clear(&mut self) {
132        for row in self.buffer.borrow_mut().buf_mut().iter_mut() {
133            for cell in row.iter_mut() {
134                *cell = Cell::new(' ');
135            }
136        }
137    }
138}
139
140impl CanvasLike for Canvas {
141    fn set(&mut self, pos: Dims, cell: Cell) {
142        Canvas::set(self, pos, cell); // Otherwise it would be recursive
143    }
144    fn pos(&self) -> Dims {
145        (0, 0).into()
146    }
147
148    fn size(&self) -> Dims {
149        self.size()
150    }
151}
152
153impl<T> CanvasLike for &mut T
154where
155    T: CanvasLike,
156{
157    fn set(&mut self, pos: Dims, cell: Cell) {
158        (**self).set(pos, cell);
159    }
160
161    fn pos(&self) -> Dims {
162        (**self).pos()
163    }
164
165    fn size(&self) -> Dims {
166        (**self).size()
167    }
168}
169
170pub trait CanvasLikeExt: CanvasLike {
171    fn show<D>(&mut self, pos: impl Into<Pos<D::X, D::Y>>, content: &D)
172    where
173        D: Drawable;
174}
175
176impl<C> CanvasLikeExt for C
177where
178    C: CanvasLike,
179{
180    #[inline]
181    fn show<D>(&mut self, pos: impl Into<Pos<D::X, D::Y>>, content: &D)
182    where
183        D: Drawable,
184    {
185        content.draw(pos, self);
186    }
187}