//! Darstellung des Textbildschirms als Ratatui-Widget. //! //! Der Zellenpuffer selbst lebt in `tb_runtime::screen` (Entscheidung //! 2026-09-03) — hier bleibt nur die Anbindung an das Terminal: die //! Farbabbildung und das Widget. Da `Widget` und `TextScreen` beide fremd //! sind, trägt das Widget ein Newtype. pub use tb_runtime::screen::{Cell, TextScreen, MIN_COLS, MIN_ROWS}; use ratatui::buffer::Buffer; use ratatui::layout::Rect; use ratatui::style::{Color, Style}; use ratatui::widgets::Widget; /// Abbildung der klassischen 16-Farben-Palette auf ANSI-Indexfarben. /// (Klassisch: 1 = Blau, 4 = Rot — ANSI: 1 = Rot, 4 = Blau usw.) pub fn basic_color(n: u8) -> Color { const MAP: [u8; 16] = [0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15]; Color::Indexed(MAP[(n & 0x0F) as usize]) } /// Zeichenbares Gegenstück zu [`TextScreen`]. pub struct ScreenWidget<'a>(pub &'a TextScreen); /// Clipped viewport; only the terminal owner enforces physical minimum size. impl Widget for ScreenWidget<'_> { fn render(self, area: Rect, buf: &mut Buffer) { let cols = self.0.cols().min(area.width as usize); let rows = self.0.rows().min(area.height as usize); for row in 0..rows { for col in 0..cols { let c = self.0.cell(row + 1, col + 1); if c.fortsetzung { // Die Zelle gehört zum breiten Zeichen davor; das Terminal // belegt sie beim Zeichnen selbst mit. continue; } if let Some(cell) = buf.cell_mut((area.x + col as u16, area.y + row as u16)) { let mut s = String::new(); s.push(c.ch); cell.set_symbol(&s); cell.set_style(Style::default().fg(basic_color(c.fg)).bg(basic_color(c.bg))); } } } // Cursor als invertierte Zelle darstellen (Terminal-Cursor wird in // der Forms-/Runtime-Schicht später gezielt gesteuert). if self.0.cursor_visible && self.0.csrlin() <= rows && self.0.pos() <= cols { let (cx, cy) = ( area.x + (self.0.pos() - 1) as u16, area.y + (self.0.csrlin() - 1) as u16, ); if let Some(cell) = buf.cell_mut((cx, cy)) { cell.set_style( cell.style() .add_modifier(ratatui::style::Modifier::REVERSED), ); } } } } #[cfg(test)] mod tests { use super::*; #[test] fn farbabbildung() { assert_eq!(basic_color(1), Color::Indexed(4)); // klassisch Blau assert_eq!(basic_color(4), Color::Indexed(1)); // klassisch Rot assert_eq!(basic_color(14), Color::Indexed(11)); // Gelb assert_eq!(basic_color(15), Color::Indexed(15)); // Weiß } }