dotzuki_renderer/layout_engine/elements/
primitives.rs1use dotzuki_engine::render::painter::Painter;
10use dotzuki_engine::render::Rgba;
11
12use crate::layout_engine::elements::text::parse_color;
13use crate::layout_engine::types::{BracketParams, DataContext, LayoutElement, PixelRectParams};
14
15const TILE: u32 = 8;
16
17pub fn render_bracket(
20 element: &LayoutElement,
21 params: &BracketParams,
22 ctx: &DataContext,
23 painter: &mut dyn Painter,
24) {
25 let tx = element.rect.tx.resolve(ctx);
26 let ty = element.rect.ty.resolve(ctx);
27 let tw = element.rect.tw.unwrap_or(1);
28 let th = element.rect.th.unwrap_or(1);
29 let color = params
30 .color
31 .as_deref()
32 .map(parse_color)
33 .unwrap_or(Rgba::INK_BLACK);
34
35 let left_px = tx * TILE;
36 let right_px = (tx + tw - 1) * TILE + 6;
37 let top_px = ty * TILE;
38 let bot_px = (ty + th - 1) * TILE + 6;
39
40 if params.right {
41 painter.draw_pixel_rect(right_px, top_px, 1, bot_px - top_px + 1, color);
42 }
43 if params.left {
44 painter.draw_pixel_rect(left_px, top_px, 1, bot_px - top_px + 1, color);
45 }
46 if params.top {
47 painter.draw_pixel_rect(left_px, top_px, right_px - left_px + 1, 1, color);
48 }
49 if params.bottom {
50 painter.draw_pixel_rect(left_px, bot_px, right_px - left_px + 1, 1, color);
51 if params.with_arrow && left_px >= 3 {
52 let arrow_left = left_px - 3;
53 painter.draw_pixel_rect(arrow_left, bot_px, 4, 1, color);
54 painter.draw_pixel_rect(arrow_left, bot_px - 1, 1, 1, color);
55 painter.draw_pixel_rect(arrow_left, bot_px + 1, 1, 1, color);
56 }
57 }
58}
59
60pub fn render_pixel_rect(params: &PixelRectParams, painter: &mut dyn Painter) {
62 let color = params
63 .color
64 .as_deref()
65 .map(parse_color)
66 .unwrap_or(Rgba::INK_BLACK);
67 painter.draw_pixel_rect(params.px, params.py, params.pw, params.ph, color);
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::layout_engine::types::ElementParams;
74 use crate::layout_engine::types::{Coord, ElementRect, LayoutElement, Visibility};
75 use dotzuki_engine::render::{TilePos, TileRect};
76 use std::cell::RefCell;
77
78 #[derive(Default)]
79 struct PxRec {
80 rects: RefCell<Vec<(u32, u32, u32, u32, Rgba)>>,
81 }
82 impl Painter for PxRec {
83 fn clear(&mut self, _c: Rgba) {}
84 fn draw_text_box(&mut self, _r: TileRect, _c: Rgba) {}
85 fn draw_text(&mut self, _p: TilePos, _t: &str, _c: Rgba) {}
86 fn draw_glyph(&mut self, _p: TilePos, _g: char, _c: Rgba) {}
87 fn draw_pixel_rect(&mut self, x: u32, y: u32, w: u32, h: u32, c: Rgba) {
88 self.rects.borrow_mut().push((x, y, w, h, c));
89 }
90 fn draw_gb_tile(&mut self, _p: TilePos, _t: u8, _f: &str, _c: Rgba) {}
91 }
92
93 #[test]
94 fn bracket_draws_requested_sides_only() {
95 let e = LayoutElement {
96 id: String::new(),
97 element_type: "bracket".into(),
98 rect: ElementRect {
99 tx: Coord::Literal(2),
100 ty: Coord::Literal(3),
101 tw: Some(4),
102 th: Some(2),
103 },
104 visible: Visibility::Static(true),
105 z_index: 0,
106 params: ElementParams::Bracket(BracketParams {
107 color: None,
108 left: true,
109 right: false,
110 top: false,
111 bottom: true,
112 with_arrow: false,
113 }),
114 };
115 let ElementParams::Bracket(ref p) = e.params else { unreachable!() };
116 let mut painter = PxRec::default();
117 render_bracket(&e, p, &DataContext::new(), &mut painter);
118 let rects = painter.rects.borrow();
119 assert_eq!(rects.len(), 2);
121 assert_eq!(rects[0].2, 1, "left edge is 1px wide");
122 assert_eq!(rects[1].3, 1, "bottom edge is 1px tall");
123 }
124
125 #[test]
126 fn pixel_rect_draws_raw_rect() {
127 let params = PixelRectParams {
128 color: Some("white".into()),
129 px: 10,
130 py: 20,
131 pw: 30,
132 ph: 2,
133 };
134 let mut painter = PxRec::default();
135 render_pixel_rect(¶ms, &mut painter);
136 assert_eq!(
137 *painter.rects.borrow(),
138 vec![(10, 20, 30, 2, Rgba::INK_WHITE)]
139 );
140 }
141}