use dotzuki_engine::render::painter::Painter;
use dotzuki_engine::render::Rgba;
use crate::layout_engine::elements::text::parse_color;
use crate::layout_engine::types::{BracketParams, DataContext, LayoutElement, PixelRectParams};
const TILE: u32 = 8;
pub fn render_bracket(
element: &LayoutElement,
params: &BracketParams,
ctx: &DataContext,
painter: &mut dyn Painter,
) {
let tx = element.rect.tx.resolve(ctx);
let ty = element.rect.ty.resolve(ctx);
let tw = element.rect.tw.unwrap_or(1);
let th = element.rect.th.unwrap_or(1);
let color = params
.color
.as_deref()
.map(parse_color)
.unwrap_or(Rgba::INK_BLACK);
let left_px = tx * TILE;
let right_px = (tx + tw - 1) * TILE + 6;
let top_px = ty * TILE;
let bot_px = (ty + th - 1) * TILE + 6;
if params.right {
painter.draw_pixel_rect(right_px, top_px, 1, bot_px - top_px + 1, color);
}
if params.left {
painter.draw_pixel_rect(left_px, top_px, 1, bot_px - top_px + 1, color);
}
if params.top {
painter.draw_pixel_rect(left_px, top_px, right_px - left_px + 1, 1, color);
}
if params.bottom {
painter.draw_pixel_rect(left_px, bot_px, right_px - left_px + 1, 1, color);
if params.with_arrow && left_px >= 3 {
let arrow_left = left_px - 3;
painter.draw_pixel_rect(arrow_left, bot_px, 4, 1, color);
painter.draw_pixel_rect(arrow_left, bot_px - 1, 1, 1, color);
painter.draw_pixel_rect(arrow_left, bot_px + 1, 1, 1, color);
}
}
}
pub fn render_pixel_rect(params: &PixelRectParams, painter: &mut dyn Painter) {
let color = params
.color
.as_deref()
.map(parse_color)
.unwrap_or(Rgba::INK_BLACK);
painter.draw_pixel_rect(params.px, params.py, params.pw, params.ph, color);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::layout_engine::types::ElementParams;
use crate::layout_engine::types::{Coord, ElementRect, LayoutElement, Visibility};
use dotzuki_engine::render::{TilePos, TileRect};
use std::cell::RefCell;
#[derive(Default)]
struct PxRec {
rects: RefCell<Vec<(u32, u32, u32, u32, Rgba)>>,
}
impl Painter for PxRec {
fn clear(&mut self, _c: Rgba) {}
fn draw_text_box(&mut self, _r: TileRect, _c: Rgba) {}
fn draw_text(&mut self, _p: TilePos, _t: &str, _c: Rgba) {}
fn draw_glyph(&mut self, _p: TilePos, _g: char, _c: Rgba) {}
fn draw_pixel_rect(&mut self, x: u32, y: u32, w: u32, h: u32, c: Rgba) {
self.rects.borrow_mut().push((x, y, w, h, c));
}
fn draw_gb_tile(&mut self, _p: TilePos, _t: u8, _f: &str, _c: Rgba) {}
}
#[test]
fn bracket_draws_requested_sides_only() {
let e = LayoutElement {
id: String::new(),
element_type: "bracket".into(),
rect: ElementRect {
tx: Coord::Literal(2),
ty: Coord::Literal(3),
tw: Some(4),
th: Some(2),
},
visible: Visibility::Static(true),
z_index: 0,
params: ElementParams::Bracket(BracketParams {
color: None,
left: true,
right: false,
top: false,
bottom: true,
with_arrow: false,
}),
};
let ElementParams::Bracket(ref p) = e.params else { unreachable!() };
let mut painter = PxRec::default();
render_bracket(&e, p, &DataContext::new(), &mut painter);
let rects = painter.rects.borrow();
assert_eq!(rects.len(), 2);
assert_eq!(rects[0].2, 1, "left edge is 1px wide");
assert_eq!(rects[1].3, 1, "bottom edge is 1px tall");
}
#[test]
fn pixel_rect_draws_raw_rect() {
let params = PixelRectParams {
color: Some("white".into()),
px: 10,
py: 20,
pw: 30,
ph: 2,
};
let mut painter = PxRec::default();
render_pixel_rect(¶ms, &mut painter);
assert_eq!(
*painter.rects.borrow(),
vec![(10, 20, 30, 2, Rgba::INK_WHITE)]
);
}
}