Skip to main content

cu_bevymon/
terminal.rs

1use std::ops::{Deref, DerefMut};
2
3use bevy::prelude::{Resource, Result, Vec2};
4use ratatui::Terminal;
5use soft_ratatui::rusttype::Font;
6use soft_ratatui::{EmbeddedTTF, SoftBackend};
7
8const DEFAULT_TERMINAL_COLS: u16 = 100;
9const DEFAULT_TERMINAL_ROWS: u16 = 50;
10const DEFAULT_FONT_SIZE_PX: u32 = 24;
11
12const FONT_REGULAR: &[u8] = include_bytes!("../assets/fonts/CopperMono-Light.ttf");
13const FONT_BOLD: &[u8] = include_bytes!("../assets/fonts/CopperMono-SemiBold.ttf");
14const FONT_ITALIC: &[u8] = include_bytes!("../assets/fonts/CopperMono-LightItalic.ttf");
15
16#[derive(Resource, Clone, Debug)]
17pub struct CuBevyMonFontOptions {
18    pub size_px: u32,
19}
20
21impl CuBevyMonFontOptions {
22    pub fn new(size_px: u32) -> Self {
23        Self { size_px }
24    }
25
26    fn clamped_size_px(&self) -> u32 {
27        self.size_px.max(8)
28    }
29}
30
31impl Default for CuBevyMonFontOptions {
32    fn default() -> Self {
33        Self::new(DEFAULT_FONT_SIZE_PX)
34    }
35}
36
37#[derive(Resource)]
38pub struct CuBevyMonTerminal(pub Terminal<SoftBackend<EmbeddedTTF>>);
39
40impl CuBevyMonTerminal {
41    pub fn from_options(options: &CuBevyMonFontOptions) -> Result<Self> {
42        let font_size = options.clamped_size_px();
43        let backend = SoftBackend::<EmbeddedTTF>::new(
44            DEFAULT_TERMINAL_COLS,
45            DEFAULT_TERMINAL_ROWS,
46            font_size,
47            load_font(FONT_REGULAR, "CopperMono-Light.ttf")?,
48            Some(load_font(FONT_BOLD, "CopperMono-SemiBold.ttf")?),
49            Some(load_font(FONT_ITALIC, "CopperMono-LightItalic.ttf")?),
50        );
51        Ok(Self(Terminal::new(backend)?))
52    }
53}
54
55impl Deref for CuBevyMonTerminal {
56    type Target = Terminal<SoftBackend<EmbeddedTTF>>;
57
58    fn deref(&self) -> &Self::Target {
59        &self.0
60    }
61}
62
63impl DerefMut for CuBevyMonTerminal {
64    fn deref_mut(&mut self) -> &mut Self::Target {
65        &mut self.0
66    }
67}
68
69pub fn sync_terminal_to_panel(terminal: &mut CuBevyMonTerminal, panel_size: Vec2) {
70    if panel_size.x <= 1.0 || panel_size.y <= 1.0 {
71        return;
72    }
73
74    let char_width = terminal.backend().char_width.max(1) as f32;
75    let char_height = terminal.backend().char_height.max(1) as f32;
76    let cols = (panel_size.x / char_width).floor().max(1.0) as u16;
77    let rows = (panel_size.y / char_height).floor().max(1.0) as u16;
78
79    let current_area = terminal.backend().buffer().area;
80    if current_area.width == cols && current_area.height == rows {
81        return;
82    }
83
84    terminal.backend_mut().resize(cols, rows);
85}
86
87fn load_font(bytes: &'static [u8], label: &str) -> Result<Font<'static>> {
88    Font::try_from_bytes(bytes).ok_or_else(|| {
89        std::io::Error::other(format!("failed to load bundled cu_bevymon font {label}")).into()
90    })
91}