use std::io;
use bevy::{
prelude::{ *},
utils::{ HashMap},
};
use ratatui::{
backend::{Backend, ClearType, WindowSize},
buffer::{Buffer, Cell},
layout::{Rect, Size},
};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BevyBackend {
pub height: u16,
pub width: u16,
pub term_font_size: u16,
pub entity_map: HashMap<(u16, u16), Entity>,
pub buffer: Buffer,
pub vcupdate: Vec<(u16, u16, Cell)>,
pub cursor_ref: Entity,
pub cursor: bool,
pub cursor_pos: (u16, u16),
pub bevy_initialized: bool,
pub normal_font_path: Option<String>,
pub italic_font_path: Option<String>,
pub bold_font_path: Option<String>,
pub italicbold_font_path: Option<String>,
pub normal_handle: Handle<Font>,
pub italic_handle: Handle<Font>,
pub bold_handle: Handle<Font>,
pub italicbold_handle: Handle<Font>,
pub manual_window_sizing: bool,
}
impl Default for BevyBackend {
fn default() -> Self {
BevyBackend {
height: 25,
width: 40,
term_font_size: 30,
entity_map: HashMap::new(),
buffer: Buffer::empty(Rect::new(0, 0, 40, 25)),
vcupdate: Vec::default(),
cursor: false,
cursor_pos: (0, 0),
bevy_initialized: false,
cursor_ref: Entity::PLACEHOLDER,
normal_font_path: None,
italic_font_path: None,
bold_font_path: None,
italicbold_font_path: None,
normal_handle: Handle::weak_from_u128(101),
italic_handle: Handle::weak_from_u128(101),
bold_handle: Handle::weak_from_u128(101),
italicbold_handle: Handle::weak_from_u128(101),
manual_window_sizing: false,
}
}
}
impl BevyBackend {
pub fn new(
width: u16,
height: u16,
font_size: u16,
normal_font_path: &str,
italic_font_path: &str,
bold_font_path: &str,
italicbold_font_path: &str,
) -> BevyBackend {
BevyBackend {
height: height,
width: width,
term_font_size: font_size,
entity_map: HashMap::new(),
buffer: Buffer::empty(Rect::new(0, 0, width, height)),
vcupdate: Vec::default(),
cursor: false,
cursor_pos: (0, 0),
cursor_ref: Entity::PLACEHOLDER,
bevy_initialized: false,
normal_font_path: Some(normal_font_path.to_string()),
italic_font_path: Some(italic_font_path.to_string()),
bold_font_path: Some(bold_font_path.to_string()),
italicbold_font_path: Some(italicbold_font_path.to_string()),
normal_handle: Handle::weak_from_u128(101),
italic_handle: Handle::weak_from_u128(101),
bold_handle: Handle::weak_from_u128(101),
italicbold_handle: Handle::weak_from_u128(101),
manual_window_sizing: false,
}
}
pub fn resize(&mut self, width: u16, height: u16) {
self.buffer.resize(Rect::new(0, 0, width, height));
self.width = width;
self.height = height;
}
pub fn manual_window_sizing(&mut self, value: bool) {
self.manual_window_sizing = value;
}
}
impl Backend for BevyBackend {
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
for (x, y, c) in content {
self.vcupdate.push((x, y, c.clone()));
let cell = self.buffer.get_mut(x, y);
*cell = c.clone();
}
Ok(())
}
fn hide_cursor(&mut self) -> Result<(), io::Error> {
self.cursor = false;
Ok(())
}
fn show_cursor(&mut self) -> Result<(), io::Error> {
self.cursor = true;
Ok(())
}
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
Ok(self.cursor_pos)
}
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
self.cursor_pos = (x, y);
Ok(())
}
fn clear(&mut self) -> Result<(), io::Error> {
self.buffer.reset();
Ok(())
}
fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> {
match clear_type {
ClearType::All => self.clear()?,
ClearType::AfterCursor => {
let index = self.buffer.index_of(self.cursor_pos.0, self.cursor_pos.1) + 1;
self.buffer.content[index..].fill(Cell::default());
}
ClearType::BeforeCursor => {
let index = self.buffer.index_of(self.cursor_pos.0, self.cursor_pos.1);
self.buffer.content[..index].fill(Cell::default());
}
ClearType::CurrentLine => {
let line_start_index = self.buffer.index_of(0, self.cursor_pos.1);
let line_end_index = self.buffer.index_of(self.width - 1, self.cursor_pos.1);
self.buffer.content[line_start_index..=line_end_index].fill(Cell::default());
}
ClearType::UntilNewLine => {
let index = self.buffer.index_of(self.cursor_pos.0, self.cursor_pos.1);
let line_end_index = self.buffer.index_of(self.width - 1, self.cursor_pos.1);
self.buffer.content[index..=line_end_index].fill(Cell::default());
}
}
Ok(())
}
fn append_lines(&mut self, n: u16) -> io::Result<()> {
let (cur_x, cur_y) = self.get_cursor()?;
let new_cursor_x = cur_x.saturating_add(1).min(self.width.saturating_sub(1));
let max_y = self.height.saturating_sub(1);
let lines_after_cursor = max_y.saturating_sub(cur_y);
if n > lines_after_cursor {
let rotate_by = n.saturating_sub(lines_after_cursor).min(max_y);
if rotate_by == self.height - 1 {
self.clear()?;
}
self.set_cursor(0, rotate_by)?;
self.clear_region(ClearType::BeforeCursor)?;
self.buffer
.content
.rotate_left((self.width * rotate_by).into());
}
let new_cursor_y = cur_y.saturating_add(n).min(max_y);
self.set_cursor(new_cursor_x, new_cursor_y)?;
Ok(())
}
fn size(&self) -> Result<Rect, io::Error> {
Ok(Rect::new(0, 0, self.width, self.height))
}
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
let window_pixel_size: Size = Size {
width: 600,
height: 480,
};
Ok(WindowSize {
columns_rows: (self.width, self.height).into(),
pixels: window_pixel_size,
})
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}