use std::{any::TypeId, fmt::Debug, io::Write, sync::Arc, thread, time::Duration};
use crossterm::{
cursor, execute,
style::{self},
};
use parking_lot::{Mutex, RwLock};
use crate::{
node::{LayoutAttribute, Node, StyleAttribute},
style::TString,
utils::NodeRef,
};
use super::{
interactor::Dispatcher,
message::{Content, Message, Participant, RawEvent, Sender},
ICmdWindow,
};
#[derive(Debug, Clone)]
pub struct FrameBuffer {
pub data: Vec<TString>,
pub left: i32,
pub top: i32,
pub right: i32,
pub bottom: i32,
}
impl FrameBuffer {
pub fn new(data: Vec<TString>, left: i32, top: i32, right: i32, bottom: i32) -> Self {
FrameBuffer {
data,
left,
top,
right,
bottom,
}
}
pub fn from_node_measure(data: Vec<TString>, node: &dyn Node) -> Self {
let left = Node::get_left(node);
let top = Node::get_top(node);
let right = Node::get_right(node);
let bottom = Node::get_bottom(node);
FrameBuffer {
data,
left,
top,
right,
bottom,
}
}
pub fn extend_top(&mut self, top: i32) {
self.top = top;
}
}
pub trait Renderable: Debug + Send {
fn render(&self) -> FrameBuffer;
fn get_visibility(&self) -> &RwLock<bool>;
fn get_layout_attributes(&self) -> &RwLock<LayoutAttribute>;
fn get_style_attributes(&self) -> Option<&Mutex<StyleAttribute>>;
fn parent(&self) -> Option<&NodeRef>;
fn children(&self) -> &RwLock<Vec<NodeRef>>;
fn get_left(&self) -> i32 {
self.get_layout_attributes().read().l.get()
}
fn get_top(&self) -> i32 {
self.get_layout_attributes().read().t.get()
}
fn get_right(&self) -> i32 {
self.get_layout_attributes().read().r.get()
}
fn get_bottom(&self) -> i32 {
self.get_layout_attributes().read().b.get()
}
}
pub struct Renderer {
pub window: Arc<RwLock<ICmdWindow>>,
win_width: u16,
win_height: u16,
timer: Mutex<u64>,
bg_color: String,
}
impl Debug for Renderer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Renderer")
.field("win_width", &self.win_width)
.field("win_height", &self.win_height)
.field("bg_color", &self.bg_color)
.finish()
}
}
impl Renderer {
pub fn new(window: Arc<RwLock<ICmdWindow>>) -> Self {
let window_locked = window.read();
let (win_width, win_height) = window_locked.get_size();
let bg_color = window_locked.bg_color.clone();
drop(window_locked);
Renderer {
window,
win_width,
win_height,
timer: 0.into(),
bg_color,
}
}
pub fn render(&self, node: NodeRef) {
let mut nodes = self.get_all_subnodes(&node);
let mut base = nodes.remove(0).render();
let overlays = nodes.iter().map(|n| n.render()).collect::<Vec<_>>();
draw_onto(&mut base, overlays);
self.update(base);
}
pub fn launch(&self, node: NodeRef) {
loop {
if !Participant::update(self, node.clone()) {
thread::sleep(Duration::from_millis(1));
break;
}
}
}
fn update(&self, raw: FrameBuffer) {
let stdout = &self.window.read().stdout;
let mut stdout = stdout.lock();
stdout.flush().unwrap();
execute!(stdout, cursor::MoveTo(0, 0)).unwrap();
for y in 0..raw.data.len() {
let line = raw.data[y as usize].make_string();
execute!(stdout, cursor::MoveTo(0, y as u16)).unwrap();
execute!(stdout, style::Print(line)).unwrap();
stdout.flush().unwrap();
}
}
fn get_all_subnodes(&self, node: &NodeRef) -> Vec<NodeRef> {
let mut result = Vec::new();
if !node.is_visible() {
return result;
}
result.push(node.clone());
node.children().read().iter().for_each(|child| {
result.extend(self.get_all_subnodes(child));
});
result
}
}
impl Sender for Renderer {
fn get_window(&self) -> Arc<RwLock<ICmdWindow>> {
self.window.clone()
}
}
impl Participant for Renderer {
type Params = NodeRef;
fn resolve(&self, msg: Message, params: Self::Params) -> bool {
match msg.content {
Content::IsReady => {
self.send(Message {
from: self.get_id(),
to: msg.from,
content: Content::Ready,
});
true
}
Content::Update => {
self.render(params);
self.send(Message {
from: self.get_id(),
to: TypeId::of::<Dispatcher>(),
content: Content::Event(RawEvent::Update(self.timer.lock().clone())),
});
*self.timer.lock() += 1;
true
}
Content::Quit => false,
_ => true,
}
}
}
pub fn draw_onto(base: &mut FrameBuffer, overlays: Vec<FrameBuffer>) {
for overlay in &overlays {
let (left, top, right, bottom) = (overlay.left, overlay.top, overlay.right, overlay.bottom);
if left >= right || top >= bottom || overlay.data.is_empty() {
continue;
}
for y in top..bottom {
for x in left..right {
if x < base.left
|| y < base.top
|| x > base.right
|| y > base.bottom
|| x < left
|| y < top
|| x > right
|| y > bottom
{
continue;
}
let x_base = (x - base.left) as usize;
let y_base = (y - base.top) as usize;
let x_overlay = (x - left) as usize;
let y_overlay = (y - top) as usize;
if x_overlay >= overlay.data[0].chars.len()
|| y_overlay >= overlay.data.len()
|| x_base >= base.data[0].chars.len()
|| y_base >= base.data.len()
{
continue;
}
base.data[y_base].chars[x_base] = overlay.data[y_overlay].chars[x_overlay].clone();
}
}
}
}