1use std::{borrow::Cow, ops::RangeInclusive};
2
3use crate::{
4 layout::Axis,
5 math::{pos2, Pos2, Rect},
6 renderer::{Attribute, Cell, Color, Grapheme, Pixel, Rgba},
7 view::ViewId,
8 Str,
9};
10
11#[derive(Clone, PartialEq)]
13pub enum Shape {
14 FillBg {
16 rect: Rect,
18 color: Rgba,
20 },
21 FillWith {
23 rect: Rect,
25 pixel: Pixel,
27 },
28 Line {
30 start: Pos2,
32 end: Pos2,
34 pixel: Pixel,
36 },
37 Text {
39 rect: Rect,
41 shape: TextShape<'static>,
43 },
44 Set {
46 pos: Pos2,
48 cell: Cell,
50 },
51}
52
53impl std::fmt::Debug for Shape {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 struct CompactRect<'a>(&'a Rect);
56 impl<'a> std::fmt::Debug for CompactRect<'a> {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(
59 f,
60 "{{ x: {}, y: {}, w: {}, h: {} }}",
61 self.0.min.x,
62 self.0.min.y,
63 self.0.width(),
64 self.0.height()
65 )
66 }
67 }
68
69 match self {
70 Self::FillBg { rect, color } => f
71 .debug_struct("FillBg")
72 .field("rect", &CompactRect(rect))
73 .field("color", color)
74 .finish(),
75 Self::FillWith { rect, pixel } => f
76 .debug_struct("FillWith")
77 .field("rect", &CompactRect(rect))
78 .field("pixel", pixel)
79 .finish(),
80 Self::Line { start, end, pixel } => f
81 .debug_struct("Line")
82 .field("start", start)
83 .field("end", end)
84 .field("pixel", pixel)
85 .finish(),
86 Self::Text { rect, shape } => f
87 .debug_struct("Text")
88 .field("rect", &CompactRect(rect))
89 .field("shape", shape)
90 .finish(),
91 Self::Set { pos, cell } => f
92 .debug_struct("Set")
93 .field("pos", pos)
94 .field("cell", cell)
95 .finish(),
96 }
97 }
98}
99
100pub trait Rasterizer {
102 fn begin(&mut self, _id: ViewId) {}
104 fn end(&mut self, _id: ViewId) {}
106
107 fn set_rect(&mut self, rect: Rect);
109 fn rect(&self) -> Rect;
111
112 fn clear(&mut self, color: Rgba) {
114 self.fill_bg(color);
115 }
116
117 fn fill_bg(&mut self, color: Rgba);
119 fn fill_with(&mut self, pixel: Pixel);
121
122 fn horizontal_line(&mut self, y: i32, range: RangeInclusive<i32>, pixel: Pixel) {
124 self.line(Axis::Horizontal, pos2(0, y), range, pixel)
125 }
126 fn vertical_line(&mut self, x: i32, range: RangeInclusive<i32>, pixel: Pixel) {
128 self.line(Axis::Vertical, pos2(x, 0), range, pixel)
129 }
130
131 fn line(&mut self, axis: Axis, offset: Pos2, range: RangeInclusive<i32>, pixel: Pixel);
133
134 fn text(&mut self, shape: TextShape<'_>);
136
137 fn pixel(&mut self, pos: Pos2, pixel: Pixel);
139 fn grapheme(&mut self, pos: Pos2, grapheme: Grapheme);
141 fn get_mut(&mut self, pos: Pos2) -> Option<&mut Cell>;
143}
144
145#[derive(Clone, Debug, PartialEq)]
147pub struct TextShape<'a> {
148 pub(crate) label: Cow<'a, str>,
149 pub(crate) fg: Color,
150 pub(crate) bg: Color,
151 pub(crate) attribute: Option<Attribute>,
152}
153
154impl<'a> From<&'a str> for TextShape<'a> {
155 fn from(value: &'a str) -> Self {
156 Self::new(value)
157 }
158}
159
160impl<'a> From<&'a Str> for TextShape<'a> {
161 fn from(value: &'a Str) -> Self {
162 Self::new(value)
163 }
164}
165
166impl<'a> TextShape<'a> {
167 pub const fn new(label: &'a str) -> Self {
173 Self {
174 label: Cow::Borrowed(label),
175 fg: Color::Reset,
176 bg: Color::Reuse,
177 attribute: None,
178 }
179 }
180
181 pub fn fg(mut self, fg: impl Into<Rgba>) -> Self {
183 self.fg = Color::Set(fg.into());
184 self
185 }
186
187 pub fn bg(mut self, bg: impl Into<Rgba>) -> Self {
189 self.bg = Color::Set(bg.into());
190 self
191 }
192
193 pub fn attribute(mut self, attribute: Attribute) -> Self {
195 match &mut self.attribute {
196 Some(attr) => *attr |= attribute,
197 None => self.attribute = Some(attribute),
198 }
199 self
200 }
201
202 pub fn maybe_attribute(mut self, attribute: Option<Attribute>) -> Self {
204 match attribute {
205 Some(attr) => self.attribute(attr),
206 None => {
207 self.attribute.take();
208 self
209 }
210 }
211 }
212}