mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 11:46:42 +01:00
146 lines
5.4 KiB
Rust
146 lines
5.4 KiB
Rust
use crate::*;
|
|
use std::time::Duration;
|
|
use std::thread::{spawn, JoinHandle};
|
|
use unicode_width::*;
|
|
#[derive(Default)]
|
|
pub struct TuiOut {
|
|
pub buffer: Buffer,
|
|
pub area: [u16;4]
|
|
}
|
|
impl Output for TuiOut {
|
|
type Unit = u16;
|
|
type Size = [Self::Unit;2];
|
|
type Area = [Self::Unit;4];
|
|
#[inline] fn area (&self) -> [u16;4] {
|
|
self.area
|
|
}
|
|
#[inline] fn area_mut (&mut self) -> &mut [u16;4] {
|
|
&mut self.area
|
|
}
|
|
#[inline] fn place <'t, T: Render<Self> + ?Sized> (&mut self, area: [u16;4], content: &'t T) {
|
|
let last = self.area();
|
|
*self.area_mut() = area;
|
|
content.render(self);
|
|
*self.area_mut() = last;
|
|
}
|
|
}
|
|
impl TuiOut {
|
|
/// Spawn the output thread.
|
|
pub fn run_output <T: Render<TuiOut> + Send + Sync + 'static> (
|
|
engine: &Arc<RwLock<Tui>>,
|
|
state: &Arc<RwLock<T>>,
|
|
timer: Duration
|
|
) -> JoinHandle<()> {
|
|
let exited = engine.read().unwrap().exited.clone();
|
|
let engine = engine.clone();
|
|
let state = state.clone();
|
|
let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed");
|
|
let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height });
|
|
spawn(move || loop {
|
|
if exited.fetch_and(true, Relaxed) {
|
|
break
|
|
}
|
|
//let t0 = engine.read().unwrap().perf.get_t0();
|
|
let Size { width, height } = engine.read().unwrap().backend.size()
|
|
.expect("get size failed");
|
|
if let Ok(state) = state.try_read() {
|
|
let size = Rect { x: 0, y: 0, width, height };
|
|
if buffer.area != size {
|
|
engine.write().unwrap().backend.clear_region(ClearType::All)
|
|
.expect("clear failed");
|
|
buffer.resize(size);
|
|
buffer.reset();
|
|
}
|
|
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
|
|
state.render(&mut output);
|
|
buffer = engine.write().unwrap().flip(output.buffer, size);
|
|
}
|
|
//let t1 = engine.read().unwrap().perf.get_t1(t0).unwrap();
|
|
//buffer.set_string(0, 0, &format!("{:>3}.{:>3}ms", t1.as_millis(), t1.as_micros() % 1000), Style::default());
|
|
std::thread::sleep(timer);
|
|
})
|
|
}
|
|
#[inline]
|
|
pub fn with_rect (&mut self, area: [u16;4]) -> &mut Self {
|
|
self.area = area;
|
|
self
|
|
}
|
|
pub fn blit (
|
|
&mut self, text: &impl AsRef<str>, x: u16, y: u16, style: Option<Style>
|
|
) {
|
|
let text = text.as_ref();
|
|
let buf = &mut self.buffer;
|
|
let style = style.unwrap_or(Style::default());
|
|
if x < buf.area.width && y < buf.area.height {
|
|
buf.set_string(x, y, text, style);
|
|
}
|
|
}
|
|
/// Write a line of text
|
|
///
|
|
/// TODO: do a paragraph (handle newlines)
|
|
pub fn text (&mut self, text: &impl AsRef<str>, x0: u16, y: u16, max_width: u16) {
|
|
let text = text.as_ref();
|
|
let buf = &mut self.buffer;
|
|
let mut string_width: u16 = 0;
|
|
for character in text.chars() {
|
|
let x = x0 + string_width;
|
|
let character_width = character.width().unwrap_or(0) as u16;
|
|
string_width += character_width;
|
|
if string_width > max_width {
|
|
break
|
|
}
|
|
if let Some(cell) = buf.cell_mut(ratatui::prelude::Position { x, y }) {
|
|
cell.set_char(character);
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
pub fn buffer_update (&mut self, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
|
buffer_update(&mut self.buffer, area, callback);
|
|
}
|
|
pub fn fill_char (&mut self, area: [u16;4], c: char) {
|
|
self.buffer_update(area, &|cell,_,_|{cell.set_char(c);})
|
|
}
|
|
pub fn fill_bg (&mut self, area: [u16;4], color: Color) {
|
|
self.buffer_update(area, &|cell,_,_|{cell.set_bg(color);})
|
|
}
|
|
pub fn fill_fg (&mut self, area: [u16;4], color: Color) {
|
|
self.buffer_update(area, &|cell,_,_|{cell.set_fg(color);})
|
|
}
|
|
pub fn fill_mod (&mut self, area: [u16;4], on: bool, modifier: Modifier) {
|
|
if on {
|
|
self.buffer_update(area, &|cell,_,_|cell.modifier.insert(modifier))
|
|
} else {
|
|
self.buffer_update(area, &|cell,_,_|cell.modifier.remove(modifier))
|
|
}
|
|
}
|
|
pub fn fill_bold (&mut self, area: [u16;4], on: bool) {
|
|
self.fill_mod(area, on, Modifier::BOLD)
|
|
}
|
|
pub fn fill_reversed (&mut self, area: [u16;4], on: bool) {
|
|
self.fill_mod(area, on, Modifier::REVERSED)
|
|
}
|
|
pub fn fill_crossed_out (&mut self, area: [u16;4], on: bool) {
|
|
self.fill_mod(area, on, Modifier::CROSSED_OUT)
|
|
}
|
|
pub fn fill_ul (&mut self, area: [u16;4], color: Option<Color>) {
|
|
if let Some(color) = color {
|
|
self.buffer_update(area, &|cell,_,_|{
|
|
cell.modifier.insert(ratatui::prelude::Modifier::UNDERLINED);
|
|
cell.underline_color = color;
|
|
})
|
|
} else {
|
|
self.buffer_update(area, &|cell,_,_|{
|
|
cell.modifier.remove(ratatui::prelude::Modifier::UNDERLINED);
|
|
})
|
|
}
|
|
}
|
|
pub fn tint_all (&mut self, fg: Color, bg: Color, modifier: Modifier) {
|
|
for cell in self.buffer.content.iter_mut() {
|
|
cell.fg = fg;
|
|
cell.bg = bg;
|
|
cell.modifier = modifier;
|
|
}
|
|
}
|
|
}
|