use std::{fmt::Display, io::stdout}; use crate::{ layout::Allocation, unit::{Point, Rect}, widget::Widget, }; pub enum Instr { DrawText(Point, String), } pub struct RenderScope { pub rect: Rect, draw_instructions: Vec, } impl RenderScope { pub fn draw_text, T: Display>(&mut self, at: P, text: T) -> u16 { let point: Point = at.into(); let lines = text .to_string() .lines() .map(|line| { let mut chars = line.chars().peekable(); let mut new_line = String::new(); let mut len = 0; while let Some(c) = chars.next() { if c == '\x1b' && chars.peek() == Some(&'[') { new_line.push(c); while let Some(c) = chars.next() { new_line.push(c); if c.is_ascii_alphabetic() { break; } } continue; } new_line.push(c); len += 1; if len >= self.rect.width as usize { new_line.push('\n'); len = 0; } } new_line }) .collect::>() .join("\n"); let lines = lines .lines() .take((self.rect.height - point.y) as usize) .collect::>(); let lines_len = lines.len(); self.draw_instructions .push(Instr::DrawText(point, lines.join("\n"))); lines_len as u16 } } impl RenderScope { pub fn new(rect: Rect) -> Self { Self { rect, draw_instructions: Vec::new(), } } } impl From<(u16, u16)> for Point { fn from(value: (u16, u16)) -> Self { Self { x: value.0, y: value.1, } } } impl From for Point { fn from(value: u16) -> Self { Self { x: value, y: value } } } impl From for Point { fn from(value: Rect) -> Self { Self { x: value.x, y: value.y, } } } impl Allocation { pub fn draw(&self, index: usize, widget: W) { let mut scope = RenderScope::new(self.widgets[index]); widget.render(&mut scope); scope.draw(); } pub fn draw_frame(&self, index: usize, widget: W) { let mut scope = RenderScope::new(self.frame[index]); widget.render(&mut scope); scope.draw(); } } impl RenderScope { pub fn draw(self) { for inst in self.draw_instructions { match inst { Instr::DrawText(point, text) => { for (i, line) in text.lines().enumerate() { print_at( self.rect.x + point.x, self.rect.y + point.y + i as u16, line, ); } } } } } } pub fn print_at(x: u16, y: u16, text: &str) { crossterm::execute!(stdout(), crossterm::cursor::MoveTo(x, y)).unwrap(); print!("{text}"); }