use std::{
any::{Any, TypeId},
fmt::Debug,
mem,
sync::Arc,
};
use downcast_trait::{downcast_trait_impl_convert_to, DowncastTrait};
use parking_lot::{Mutex, RwLock};
use crate::{
canvas::Canvas,
core::{
interactor::GeneralListener,
renderer::{FrameBuffer, Renderable},
},
event::{KeyPressEvent, KeyboardPress, MouseClick, MouseEvent, NodeUpdate},
measure::{self, HorizontalMeasure, Left, Top, VerticalMeasure},
style::{
color::{self},
pattern::NoBorder,
GeneralBorderPattern, GeneralStyle, NoStyle, TString,
},
utils::{LazyMeasure, NodeRef},
};
pub trait Node: DowncastTrait + Renderable + Send + Sync {
fn get_left(&self) -> i32 {
Renderable::get_left(self)
}
fn get_top(&self) -> i32 {
Renderable::get_top(self)
}
fn get_right(&self) -> i32 {
Renderable::get_right(self)
}
fn get_bottom(&self) -> i32 {
Renderable::get_bottom(self)
}
fn hide(&self) {
*self.get_visibility().write() = false;
}
fn show(&self) {
*self.get_visibility().write() = true;
}
fn is_visible(&self) -> bool {
*self.get_visibility().read()
}
fn backward(&self)
where
Self: Sized,
{
let parent = self.parent().unwrap();
let mut children = parent.children().write();
let index = children
.iter()
.position(|child| std::ptr::eq(child.as_ref(), self))
.unwrap();
if index > 0 {
let child = children.remove(index);
children.insert(index - 1, child);
}
}
fn put_rear(&self)
where
Self: Sized,
{
let parent = self.parent().unwrap();
let mut children = parent.children().write();
let index = children
.iter()
.position(|child| std::ptr::eq(child.as_ref(), self))
.unwrap();
if index < children.len() - 1 {
let child = children.remove(index);
children.insert(0, child);
}
}
fn put_front(&self)
where
Self: Sized,
{
let parent = self.parent().unwrap();
let mut children = parent.children().write();
let index = children
.iter()
.position(|child| std::ptr::eq(child.as_ref(), self))
.unwrap();
if index > 0 {
let child = children.remove(index);
children.push(child);
}
}
}
pub trait HasStyle: Node {
fn set_background(&self, background: Background) {
self.get_style_attributes().unwrap().lock().background = background;
}
fn fill_background(&self, style: impl Into<GeneralStyle>) {
let style_ = style.into().clone();
self.get_style_attributes().unwrap().lock().background =
Background::FillStyle(style_.clone());
self.get_style_attributes().unwrap().lock().border_style = style_;
}
fn set_foreground(&self, style: impl Into<GeneralStyle>) {
self.get_style_attributes().unwrap().lock().foreground = style.into();
}
}
pub trait HasListener: Node {
fn get_listeners(&self) -> &RwLock<Vec<GeneralListener>>;
fn on_mouse_event(
&self,
func: Box<dyn FnMut(NodeRef, MouseEvent) -> bool + Send + Sync + 'static>,
) {
self.get_listeners().write().push(MouseClick(func).into())
}
fn on_key_event(
&self,
func: Box<dyn FnMut(NodeRef, KeyPressEvent) -> bool + Send + Sync + 'static>,
) {
self.get_listeners()
.write()
.push(KeyboardPress(func).into())
}
fn on_update_event(&self, func: Box<dyn FnMut(NodeRef, u64) -> bool + Send + Sync + 'static>) {
self.get_listeners().write().push(NodeUpdate(func).into())
}
}
pub trait Alignable: Node {
fn left_to(self: &Arc<Self>, r: impl Into<LazyMeasure> + HorizontalMeasure) {
self.get_layout_attributes().write().l = r.into();
}
fn right_to(self: &Arc<Self>, r: impl Into<LazyMeasure> + HorizontalMeasure) {
self.get_layout_attributes().write().r = r.into();
}
fn top_to(self: &Arc<Self>, r: impl Into<LazyMeasure> + VerticalMeasure) {
self.get_layout_attributes().write().t = r.into();
}
fn bottom_to(self: &Arc<Self>, r: impl Into<LazyMeasure> + VerticalMeasure) {
self.get_layout_attributes().write().b = r.into();
}
fn set_width(self: &Arc<Self>, width: u16)
where
Self: Sized + Sync + 'static,
{
self.right_to(Left::away_from(&self, -(width as i32)));
}
fn set_height(self: &Arc<Self>, height: u16)
where
Self: Sized + Sync + 'static,
{
self.bottom_to(Top::away_from(&self, -(height as i32)));
}
fn offset_left(&self, offset: i32) {
take_mut::take(&mut self.get_layout_attributes().write().l, |l| {
LazyMeasure::new(move || l.get() + offset)
});
}
fn offset_top(&self, offset: i32) {
take_mut::take(&mut self.get_layout_attributes().write().t, |t| {
LazyMeasure::new(move || t.get() + offset)
});
}
fn offset_right(&self, offset: i32) {
take_mut::take(&mut self.get_layout_attributes().write().r, |r| {
LazyMeasure::new(move || r.get() + offset)
});
}
fn offset_bottom(&self, offset: i32) {
take_mut::take(&mut self.get_layout_attributes().write().b, |b| {
LazyMeasure::new(move || b.get() + offset)
});
}
fn get_width(&self) -> i32 {
Node::get_right(self) - Node::get_left(self)
}
fn get_height(&self) -> i32 {
Node::get_bottom(self) - Node::get_top(self)
}
fn is_within(&self, x: i32, y: i32) -> bool {
let left = Node::get_left(self);
let right = Node::get_right(self);
let top = Node::get_top(self);
let bottom = Node::get_bottom(self);
x >= left && x <= right && y >= top && y <= bottom
}
}
#[derive(Debug, Clone)]
pub enum Background {
FillStyle(GeneralStyle),
Text(TString),
}
#[derive(Debug)]
pub struct LayoutAttribute {
pub(crate) l: LazyMeasure,
pub(crate) t: LazyMeasure,
pub(crate) r: LazyMeasure,
pub(crate) b: LazyMeasure,
}
#[derive(Debug, Clone)]
pub struct StyleAttribute {
pub background: Background,
pub foreground: GeneralStyle,
pub border_style: GeneralStyle,
pub border_pattern: GeneralBorderPattern,
}
impl Default for StyleAttribute {
fn default() -> Self {
Self {
background: Background::FillStyle(color::AsBackground(color::Black).into()),
foreground: color::White.into(),
border_style: NoStyle.into(),
border_pattern: NoBorder.into(),
}
}
}
macro_rules! node_struct {
($name:ident) => {
#[derive(Debug)]
pub struct $name {
/// The name of the node.
pub name: String,
pub children: RwLock<Vec<NodeRef>>,
pub parent: Option<NodeRef>,
pub listeners: RwLock<Vec<GeneralListener>>,
style: Mutex<StyleAttribute>,
layout: RwLock<LayoutAttribute>,
visibility: RwLock<bool>,
}
};
($name:ident, $($field:ident: $type:ty),*) => {
#[derive(Debug)]
pub struct $name {
/// The name of the node.
pub name: String,
pub children: RwLock<Vec<NodeRef>>,
pub parent: Option<NodeRef>,
pub listeners: RwLock<Vec<GeneralListener>>,
style: Mutex<StyleAttribute>,
layout: RwLock<LayoutAttribute>,
visibility: RwLock<bool>,
$(pub $field: $type),*
}
};
}
#[macro_export]
macro_rules! node_impl_traits {
($name:ident) => {
impl Alignable for $name {}
impl DowncastTrait for $name {
downcast_trait_impl_convert_to!(dyn HasListener);
}
impl Node for $name {}
impl HasStyle for $name {}
impl HasListener for $name {
fn get_listeners(&self) -> &RwLock<Vec<GeneralListener>> {
&self.listeners
}
}
};
}
node_struct!(Container);
node_impl_traits!(Container);
impl Container {
pub fn create(parent: &Arc<impl Node + Sync + 'static>, name: &str) -> Arc<Self> {
let v = Arc::new(Self {
name: name.to_string(),
children: RwLock::new(vec![]),
style: Mutex::new(StyleAttribute::default()),
layout: RwLock::new(LayoutAttribute {
l: measure::Left::of(parent).into(),
t: measure::Top::of(parent).into(),
r: measure::Fixed(4).into(),
b: measure::Fixed(4).into(),
}),
parent: Some(parent.clone()),
listeners: RwLock::new(vec![]),
visibility: RwLock::new(true),
});
parent.children().write().push(v.clone());
v
}
pub fn set_border(
self: &Arc<Self>,
pattern: impl Into<GeneralBorderPattern>,
style: impl Into<GeneralStyle>,
) {
self.style.lock().border_pattern = pattern.into();
self.style.lock().border_style = style.into();
}
pub fn new(parent: &Arc<impl Node + Sync + 'static>, size: (u16, u16)) -> Arc<Self> {
let v = Self::create(parent, "container");
v.left_to(Left::of(parent));
v.top_to(Top::of(parent));
v.set_width(size.0);
v.set_height(size.1);
let style = v.style.lock();
let background = style.background.clone();
drop(style);
match background {
Background::FillStyle(ref style) => v.set_border(NoBorder, style.clone()),
Background::Text(_) => panic!("Text background not supported"),
}
v
}
}
impl Renderable for Container {
fn render(&self) -> FrameBuffer {
let style = self.style.lock();
let border_pattern = style.border_pattern.clone();
let border_style = style.border_style.clone();
drop(style);
Canvas::new_and_fill(self, ' ', {
match self.style.lock().background {
Background::FillStyle(ref style) => style.clone(),
Background::Text(ref _text) => todo!("Image"),
}
})
.draw_border(
Alignable::get_width(self),
Alignable::get_height(self),
border_pattern,
border_style,
)
.finish()
}
fn get_style_attributes(&self) -> Option<&Mutex<StyleAttribute>> {
Some(&self.style)
}
fn get_layout_attributes(&self) -> &RwLock<LayoutAttribute> {
&self.layout
}
fn parent(&self) -> Option<&NodeRef> {
self.parent.as_ref()
}
fn children(&self) -> &RwLock<Vec<NodeRef>> {
&self.children
}
fn get_visibility(&self) -> &RwLock<bool> {
&self.visibility
}
}
node_struct!(Text, text: Mutex<String>);
node_impl_traits!(Text);
impl Text {
pub fn create(parent: &Arc<impl Node + Sync + 'static>, name: &str) -> Arc<Self> {
let v = Arc::new(Self {
name: name.to_string(),
children: RwLock::new(vec![]),
style: Mutex::new(StyleAttribute::default()),
layout: RwLock::new(LayoutAttribute {
l: measure::Left::of(parent).into(),
t: measure::Top::of(parent).into(),
r: measure::Fixed(4).into(),
b: measure::Fixed(4).into(),
}),
parent: Some(parent.clone()),
listeners: RwLock::new(vec![]),
text: "text".to_string().into(),
visibility: RwLock::new(true),
});
parent.children().write().push(v.clone());
v
}
pub fn new(
parent: &Arc<impl Node + Sync + 'static>,
text: &'static str,
position: (i32, i32),
) -> Arc<Self> {
let v = Self::create(parent, "text");
*v.text.lock() = text.to_string();
v.left_to(Left::away_from(parent, -position.0));
v.top_to(Top::away_from(parent, -position.1));
v.set_width(text.len() as u16);
v.set_height(1);
v
}
pub fn get_text(&self) -> String {
self.text.lock().clone()
}
pub fn set_text(self: &Arc<Self>, text: &str) {
*self.text.lock() = text.to_string();
self.set_width(text.len() as u16);
}
}
impl Renderable for Text {
fn render(&self) -> FrameBuffer {
let text = self.text.lock();
let text_cloned = text.clone();
let style = self.style.lock();
let style_cloned = style.foreground.clone();
drop(text);
drop(style);
FrameBuffer {
data: vec![TString::from_str(
text_cloned.as_str(),
GeneralStyle::from_style(style_cloned),
)],
left: Renderable::get_left(self),
top: Renderable::get_top(self),
right: Renderable::get_right(self),
bottom: Renderable::get_bottom(self),
}
}
fn get_style_attributes(&self) -> Option<&Mutex<StyleAttribute>> {
Some(&self.style)
}
fn get_layout_attributes(&self) -> &RwLock<LayoutAttribute> {
&self.layout
}
fn parent(&self) -> Option<&NodeRef> {
self.parent.as_ref()
}
fn children(&self) -> &RwLock<Vec<NodeRef>> {
&self.children
}
fn get_visibility(&self) -> &RwLock<bool> {
&self.visibility
}
}
node_struct!(DrawingBoard, canvas: Mutex<Option<Canvas>>);
node_impl_traits!(DrawingBoard);
impl DrawingBoard {
pub fn new(
parent: &Arc<impl Node + Sync + 'static>,
name: &str,
size: (u16, u16),
) -> Arc<Self> {
let v = Arc::new(Self {
name: name.to_string(),
children: RwLock::new(vec![]),
style: Mutex::new(StyleAttribute::default()),
layout: RwLock::new(LayoutAttribute {
l: measure::Left::of(parent).into(),
t: measure::Top::of(parent).into(),
r: measure::Fixed(size.0 as i32).into(),
b: measure::Fixed(size.1 as i32).into(),
}),
canvas: Mutex::new(None),
parent: Some(parent.clone()),
listeners: RwLock::new(vec![]),
visibility: RwLock::new(true),
});
parent.children().write().push(v.clone());
*v.canvas.lock() = Some(Canvas::new_template(v.as_ref()));
v
}
pub fn set_canvas(self: &Arc<Self>, canvas: Canvas) {
*self.canvas.lock() = Some(canvas);
}
pub fn update_canvas(self: &Arc<Self>, map: impl Fn(Canvas) -> Canvas) {
let mut canvas = self.canvas.lock();
take_mut::take(&mut *canvas, |canvas| {
canvas.map(|mut c| {
c.buffer.top = Renderable::get_top(self.as_ref());
c.buffer.left = Renderable::get_left(self.as_ref());
c.buffer.right = Renderable::get_right(self.as_ref());
c.buffer.bottom = Renderable::get_bottom(self.as_ref());
map(c)
})
});
}
}
impl Renderable for DrawingBoard {
fn render(&self) -> FrameBuffer {
let canvas = self.canvas.lock();
let mut buffer = canvas.as_ref().unwrap().finish();
buffer.top = Renderable::get_top(self);
buffer.left = Renderable::get_left(self);
buffer.right = Renderable::get_right(self);
buffer.bottom = Renderable::get_bottom(self);
drop(canvas);
buffer
}
fn get_style_attributes(&self) -> Option<&Mutex<StyleAttribute>> {
Some(&self.style)
}
fn get_layout_attributes(&self) -> &RwLock<LayoutAttribute> {
&self.layout
}
fn parent(&self) -> Option<&NodeRef> {
self.parent.as_ref()
}
fn children(&self) -> &RwLock<Vec<NodeRef>> {
&self.children
}
fn get_visibility(&self) -> &RwLock<bool> {
&self.visibility
}
}