ascii_forge/renderer/
render.rs1use std::{fmt::Display, marker::PhantomData};
2
3use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
4
5use crate::prelude::*;
6
7#[macro_export]
30macro_rules! render {
31 ($buffer:expr, $( $loc:expr => [$($render:expr),* $(,)?]),* $(,)? ) => {{
32 #[allow(unused_mut)]
33 let mut loc;
34 $(
35 loc = Vec2::from($loc);
36 $(loc = $render.render(loc, $buffer.as_mut()));*;
37 let _ = loc;
38 )*
39 loc
40 }};
41}
42
43pub trait Render {
46 fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2;
47 fn size(&self) -> Vec2 {
48 let mut buf = Buffer::new((u16::MAX, u16::MAX));
49 render!(buf, vec2(0, 0) => [ self ]);
50 buf.shrink();
51 buf.size()
52 }
53}
54
55impl Render for char {
57 fn render(&self, mut loc: Vec2, buffer: &mut Buffer) -> Vec2 {
58 buffer.set(loc, *self);
59 loc.x += self.width().unwrap_or(1).saturating_sub(1) as u16;
60 loc
61 }
62 fn size(&self) -> Vec2 {
63 vec2(self.width().unwrap_or(1) as u16, 1)
64 }
65}
66
67impl Render for &str {
68 fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
69 render!(buffer, loc => [ StyledContent::new(ContentStyle::default(), self) ])
70 }
71 fn size(&self) -> Vec2 {
72 StyledContent::new(ContentStyle::default(), self).size()
73 }
74}
75
76impl<R: Render + 'static> From<R> for Box<dyn Render> {
77 fn from(value: R) -> Self {
78 Box::new(value)
79 }
80}
81
82impl<R: Into<Box<dyn Render>> + Clone> Render for Vec<R> {
83 fn render(&self, mut loc: Vec2, buffer: &mut Buffer) -> Vec2 {
84 let items: Vec<Box<dyn Render>> = self.iter().map(|x| x.clone().into()).collect();
85 for item in items {
86 loc = render!(buffer, loc => [ item ]);
87 }
88 loc
89 }
90}
91
92pub struct CharString<D: Display, F: Into<StyledContent<D>> + Clone> {
95 pub text: F,
96 marker: PhantomData<D>,
97}
98
99impl<D: Display, F: Into<StyledContent<D>> + Clone> CharString<D, F> {
100 pub fn new(text: F) -> Self {
101 Self {
102 text,
103 marker: PhantomData {},
104 }
105 }
106}
107
108impl<D: Display, F: Into<StyledContent<D>> + Clone> Render for CharString<D, F> {
109 fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
110 render!(buffer, loc => [ Cell::styled(self.text.clone().into()) ])
111 }
112}
113
114impl Render for String {
115 fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
116 render!(buffer, loc => [ self.as_str() ])
117 }
118}
119
120impl<D: Display> Render for StyledContent<D> {
121 fn render(&self, mut loc: Vec2, buffer: &mut Buffer) -> Vec2 {
122 let base_x = loc.x;
123 for line in format!("{}", self.content()).split('\n') {
124 loc.x = base_x;
125 for chr in line.chars().collect::<Vec<char>>() {
126 buffer.set(loc, StyledContent::new(*self.style(), chr));
127 loc.x += chr.width().unwrap_or(1) as u16;
128 }
129 loc.y += 1;
130 }
131 loc.y -= 1;
132 loc
133 }
134 fn size(&self) -> Vec2 {
135 let mut width = 0;
136 let mut height = 0;
137 for line in format!("{}", self.content()).split('\n') {
138 width = line.chars().count().max(width);
139 height += line.width() as u16;
140 }
141 vec2(width as u16, height)
142 }
143}