ascii_forge/renderer/
render.rs

1use std::{fmt::Display, marker::PhantomData};
2
3use crate::prelude::*;
4
5/// A macro to simplify rendering lots of items at once.
6/// The Buffer can be anything that implements AsMut<Buffer>
7/// This render will return the location of which the last element finished rendering.
8/**
9`Example`
10```rust
11use crate::prelude::*;
12
13// Create a window
14let window = Window::init()?;
15
16// Render This works! and Another Element! To the window's buffer
17render!(
18    window,
19        (16, 16) => [ "This works!" ]
20        (0, 0) => [ "Another Element!" ]
21);
22```
23*/
24#[macro_export]
25macro_rules! render {
26    ($buffer:expr, $( $loc:expr => [$($render:expr),* $(,)?]),* $(,)?  ) => {{
27        #[allow(unused_mut)]
28        let mut loc;
29        $(
30            loc = Vec2::from($loc);
31            $(loc = $render.render(loc, $buffer.as_mut()));*;
32            let _ = loc;
33        )*
34        loc
35    }};
36}
37
38/// The main system that will render an element at a location to the buffer.
39/// Render's return type is the location the render ended at.
40pub trait Render {
41    fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2;
42    fn size(&self) -> Vec2 {
43        let mut buf = Buffer::new((u16::MAX, u16::MAX));
44        render!(buf, vec2(0, 0) => [ self ]);
45        buf.shrink();
46        buf.size()
47    }
48}
49
50/* --------------- Implementations --------------- */
51impl Render for char {
52    fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
53        buffer.set(loc, *self);
54        loc
55    }
56    fn size(&self) -> Vec2 {
57        vec2(1, 1)
58    }
59}
60
61impl Render for &str {
62    fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
63        render!(buffer, loc => [ StyledContent::new(ContentStyle::default(), self) ])
64    }
65    fn size(&self) -> Vec2 {
66        StyledContent::new(ContentStyle::default(), self).size()
67    }
68}
69
70impl<R: Render + 'static> From<R> for Box<dyn Render> {
71    fn from(value: R) -> Self {
72        Box::new(value)
73    }
74}
75
76impl<R: Into<Box<dyn Render>> + Clone> Render for Vec<R> {
77    fn render(&self, mut loc: Vec2, buffer: &mut Buffer) -> Vec2 {
78        let items: Vec<Box<dyn Render>> = self.iter().map(|x| x.clone().into()).collect();
79        for item in items {
80            loc = render!(buffer, loc => [ item ]);
81        }
82        loc
83    }
84}
85
86/// A Render type that doesn't get split. It purely renders the one item to the screen.
87/// Useful for multi-character emojis.
88pub struct CharString<D: Display, F: Into<StyledContent<D>> + Clone> {
89    pub text: F,
90    marker: PhantomData<D>,
91}
92
93impl<D: Display, F: Into<StyledContent<D>> + Clone> CharString<D, F> {
94    pub fn new(text: F) -> Self {
95        Self {
96            text,
97            marker: PhantomData {},
98        }
99    }
100}
101
102impl<D: Display, F: Into<StyledContent<D>> + Clone> Render for CharString<D, F> {
103    fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
104        render!(buffer, loc => [ Cell::styled(self.text.clone().into()) ])
105    }
106}
107
108impl Render for String {
109    fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
110        render!(buffer, loc => [ self.as_str() ])
111    }
112}
113
114impl<D: Display> Render for StyledContent<D> {
115    fn render(&self, mut loc: Vec2, buffer: &mut Buffer) -> Vec2 {
116        let base_x = loc.x;
117        for line in format!("{}", self.content()).split('\n') {
118            loc.x = base_x;
119            for char in line.chars().collect::<Vec<char>>() {
120                buffer.set(loc, StyledContent::new(*self.style(), char));
121                loc.x += 1;
122            }
123            loc.y += 1;
124        }
125        loc.y -= 1;
126        loc
127    }
128    fn size(&self) -> Vec2 {
129        let mut width = 0;
130        let mut height = 0;
131        for line in format!("{}", self.content()).split('\n') {
132            width = line.chars().count().max(width);
133            height += 1;
134        }
135        vec2(width as u16, height)
136    }
137}