Files
TerminalBasic/crates/tb-ui/src/screen.rs

75 lines
2.8 KiB
Rust

//! 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ß
}
}