diff --git a/macros/src/vtui.rs b/macros/src/vtui.rs index fb15c50..a1e49c4 100644 --- a/macros/src/vtui.rs +++ b/macros/src/vtui.rs @@ -2,7 +2,6 @@ use quote::{ToTokens, quote}; use syn::{ Token, braced, parse::{Parse, ParseStream, discouraged::Speculative}, - punctuated::Punctuated, spanned::Spanned, }; diff --git a/src/arena.rs b/src/arena.rs index d360acd..e05cc8f 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -1,71 +1,66 @@ -use ratatui::buffer::Buffer; +use ratatui::{Frame, buffer::Buffer}; use slotmap::{SlotMap, new_key_type}; use crate::{ canvas::{Canvas, LogicalRect}, component::{Child, Node}, - context::Context, - events::Message, + context::{Context, UpdatePass, UpdateState}, layout::Measure, + transport::Message, }; new_key_type! { struct NodeId; } -#[derive(Default)] pub(crate) struct Arena { root: NodeId, - inner: SlotMap, + nodes: SlotMap, + traversal: Option>, } impl Arena { pub fn new(root: Node) -> Self { - let mut arena = Self::default(); + let mut arena = Self { + root: NodeId::default(), + nodes: SlotMap::default(), + traversal: None, + }; + arena.root = arena.push(root); + arena.compute_traversal(); + arena } - pub fn update_for_each(&mut self, mut update_fn: F) - where - F: FnMut(&mut ArenaNode), - { - let mut stack = vec![(self.root, false)]; - - while let Some((id, visited)) = stack.pop() { - if visited { - let node = &mut self.inner[id]; - update_fn(node); - } else { - stack.push((id, true)); - for &(child, _) in self.inner[id].children.iter().rev() { - stack.push((child, false)); - } - } + pub fn dispatch(&mut self, msg: &Message, ctx: &mut Context) { + if self.traversal.is_none() { + self.compute_traversal(); } - } - pub fn draw_for_each(&mut self, rect: LogicalRect, mut draw_fn: F) - where - F: FnMut(&ArenaNode), - { - let mut stack = vec![self.root]; + let order = self.traversal.as_ref().unwrap().iter().rev(); + let mut state = UpdateState::default(); - self.compute_layout(rect); + for &id in order { + let node = &mut self.nodes[id]; + let pass = UpdatePass::new(ctx, &mut state, node.rect); + node.dispatch(msg, pass); + } + } - while let Some(id) = stack.pop() { - let node = &self.inner[id]; - draw_fn(node); + pub fn render(&mut self, frame: &mut Frame) { + if self.traversal.is_none() { + self.compute_traversal(); + } - let mut children = self.inner[id] - .children - .iter() - .map(|(c, _)| *c) - .collect::>(); + let rect = frame.area().into(); + let buf = frame.buffer_mut(); - children.sort_by_key(|&child_id| self.inner[child_id].inner.get_layer()); + self.compute_layout(rect); - for &child in children.iter().rev() { - stack.push(child); - } + let order = self.traversal.as_ref().unwrap(); + + for &id in order { + let node = &self.nodes[id]; + node.render(buf); } } } @@ -73,10 +68,10 @@ impl Arena { impl Arena { fn compute_layout(&mut self, rect: LogicalRect) { let root = self.root; - self.inner[root].rect = rect; + self.nodes[root].rect = rect; fn visit(arena: &mut Arena, id: NodeId) { - let node = &arena.inner[id]; + let node = &arena.nodes[id]; let rect = node.rect; let composition = node.inner.composition(); @@ -91,7 +86,7 @@ impl Arena { debug_assert_eq!(rects.len(), children.len()); for ((child_id, _), rect) in children.iter().zip(rects) { - arena.inner[*child_id].rect = rect; + arena.nodes[*child_id].rect = rect; visit(arena, *child_id); } } @@ -99,15 +94,39 @@ impl Arena { visit(self, root); } + fn compute_traversal(&mut self) { + let root = self.root; + + let mut order = Vec::with_capacity(self.nodes.len()); + let mut stack = vec![root]; + + while let Some(id) = stack.pop() { + order.push(id); + + let mut children = self.nodes[id] + .children + .iter() + .map(|(c, _)| *c) + .collect::>(); + + children.sort_by_key(|&child_id| self.nodes[child_id].inner.get_layer()); + + for &child in children.iter().rev() { + stack.push(child); + } + } + + self.traversal = Some(order); + } + fn push(&mut self, node: Node) -> NodeId { - let id = self.inner.insert(ArenaNode { + let id = self.nodes.insert(ArenaNode { inner: node, - parent: None, children: Vec::new(), rect: LogicalRect::new(0, 0, 0, 0), }); - let children = self.inner[id] + let children = self.nodes[id] .inner .composition() .children() @@ -121,32 +140,34 @@ impl Arena { for (child, measure) in children { let child_id = self.push(child); - self.inner[child_id].parent = Some(id); - self.inner[id].children.push((child_id, measure)); + let parent = &mut self.nodes[id]; + + parent.children.push((child_id, measure)); } + self.traversal = None; + id } } -pub(crate) struct ArenaNode { +struct ArenaNode { inner: Node, - parent: Option, children: Vec<(NodeId, Measure)>, rect: LogicalRect, } impl ArenaNode { - pub(crate) fn render(&self, buffer: &mut Buffer) { + fn render(&self, buffer: &mut Buffer) { if let Some(renderer) = &self.inner.get_renderer() { let mut canvas = Canvas::new(self.rect, buffer); renderer(&mut canvas); } } - pub(crate) fn dispatch(&mut self, msg: &Message, ctx: &mut Context) { + fn dispatch(&mut self, msg: &Message, pass: UpdatePass<'_>) { if let Some(listeners) = self.inner.get_listeners(msg) { - listeners.dispatch(msg, ctx); + listeners.dispatch(msg, pass); } } } diff --git a/src/component.rs b/src/component.rs index 5882337..4de3b6f 100644 --- a/src/component.rs +++ b/src/component.rs @@ -3,10 +3,11 @@ use std::cell::{RefCell, RefMut}; use crate::{ canvas::{Canvas, LogicalRect}, context::EventContext, - events::{Event, Message}, + events::Event, layout::{Flow, Layer, Measure, compute_split}, listeners::{DrawListener, ErasedListenerBucket, ListenerStore}, state::{State, StateOwner}, + transport::Message, }; pub type FactoryFn

= fn(Component, P) -> Node; diff --git a/src/context.rs b/src/context.rs index 43ed445..9f3c9bc 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,27 +1,39 @@ use std::ops::Deref; -use crate::events::Event; +use crate::{ + canvas::LogicalRect, + events::{Event, MouseEvent}, +}; -pub struct EventContext<'rt, E: Event> { - event: &'rt E, - context: &'rt mut Context, +pub struct EventContext<'e, E: Event> { + event: &'e E, + pass: UpdatePass<'e>, } -impl<'rt, E: Event> Deref for EventContext<'rt, E> { +impl<'e, E: Event> Deref for EventContext<'e, E> { type Target = E; - fn deref(&self) -> &'rt Self::Target { + fn deref(&self) -> &'e Self::Target { self.event } } -impl<'rt, E: Event> EventContext<'rt, E> { - pub(crate) fn new(event: &'rt E, context: &'rt mut Context) -> Self { - Self { event, context } +impl<'e, E: Event> EventContext<'e, E> { + pub(crate) fn new(event: &'e E, pass: UpdatePass<'e>) -> Self { + Self { event, pass } } pub fn request_shutdown(&mut self) { - self.context.shutdown_requested = true; + self.pass.context.shutdown_requested = true; + } +} + +impl<'e, E: MouseEvent> EventContext<'e, E> { + pub fn is_mouse_hit(&self) -> bool { + let (x, y) = self.coords(); + let cursor = LogicalRect::new(x as i32, y as i32, 1, 1); + + self.pass.rect.intersects(cursor) } } @@ -29,3 +41,22 @@ impl<'rt, E: Event> EventContext<'rt, E> { pub(crate) struct Context { pub shutdown_requested: bool, } + +pub(crate) struct UpdatePass<'e> { + context: &'e mut Context, + state: &'e mut UpdateState, + rect: LogicalRect, +} + +impl<'e> UpdatePass<'e> { + pub fn new(context: &'e mut Context, state: &'e mut UpdateState, rect: LogicalRect) -> Self { + Self { + context, + state, + rect, + } + } +} + +#[derive(Default)] +pub(crate) struct UpdateState {} diff --git a/src/events.rs b/src/events.rs index 2bf422d..df1fcbc 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,35 +1,9 @@ -use std::any::{Any, TypeId}; - use crate::input::{KeyCode, MouseButton, MouseScrollDirection}; pub trait Event: Send + 'static {} -pub struct Message { - event_type_id: TypeId, - event: Box, -} - -impl From for Message { - fn from(value: E) -> Self { - Self { - event_type_id: TypeId::of::(), - event: Box::new(value), - } - } -} - -impl Message { - pub fn new(event: E) -> Self { - Self::from(event) - } - - pub(crate) fn event_type_id(&self) -> TypeId { - self.event_type_id - } - - pub(crate) fn downcast_ref(&self) -> Option<&E> { - self.event.downcast_ref::() - } +pub trait MouseEvent: Event { + fn coords(&self) -> (u16, u16); } pub struct Tick {} @@ -44,6 +18,12 @@ pub struct MouseDown { impl Event for MouseDown {} +impl MouseEvent for MouseDown { + fn coords(&self) -> (u16, u16) { + (self.x, self.y) + } +} + pub struct MouseUp { pub x: u16, pub y: u16, @@ -52,6 +32,12 @@ pub struct MouseUp { impl Event for MouseUp {} +impl MouseEvent for MouseUp { + fn coords(&self) -> (u16, u16) { + (self.x, self.y) + } +} + pub struct MouseHover { pub x: u16, pub y: u16, @@ -59,6 +45,12 @@ pub struct MouseHover { impl Event for MouseHover {} +impl MouseEvent for MouseHover { + fn coords(&self) -> (u16, u16) { + (self.x, self.y) + } +} + pub struct MouseDrag { pub x: u16, pub y: u16, @@ -67,6 +59,12 @@ pub struct MouseDrag { impl Event for MouseDrag {} +impl MouseEvent for MouseDrag { + fn coords(&self) -> (u16, u16) { + (self.x, self.y) + } +} + pub struct MouseScroll { pub x: u16, pub y: u16, @@ -75,6 +73,12 @@ pub struct MouseScroll { impl Event for MouseScroll {} +impl MouseEvent for MouseScroll { + fn coords(&self) -> (u16, u16) { + (self.x, self.y) + } +} + pub struct KeyPress { pub key: KeyCode, } diff --git a/src/input.rs b/src/input.rs index c93301e..ffe985b 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,6 +1,9 @@ -use crate::events::{ - KeyPress, KeyRelease, KeyRepeat, Message, MouseDown, MouseDrag, MouseHover, MouseScroll, - MouseUp, Resize, +use crate::{ + events::{ + KeyPress, KeyRelease, KeyRepeat, MouseDown, MouseDrag, MouseHover, MouseScroll, MouseUp, + Resize, + }, + transport::Message, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -122,9 +125,9 @@ pub enum Input { }, } -impl Input { - pub fn to_message(self) -> Message { - match self { +impl From for Message { + fn from(value: Input) -> Self { + match value { Input::MouseDown { x, y, button } => Message::new(MouseDown { x, y, button }), Input::MouseUp { x, y, button } => Message::new(MouseUp { x, y, button }), Input::MouseHover { x, y } => Message::new(MouseHover { x, y }), @@ -137,3 +140,9 @@ impl Input { } } } + +impl Input { + pub fn to_message(self) -> Message { + Message::from(self) + } +} diff --git a/src/listeners.rs b/src/listeners.rs index 1116f61..c820858 100644 --- a/src/listeners.rs +++ b/src/listeners.rs @@ -5,15 +5,16 @@ use std::{ use crate::{ canvas::Canvas, - context::{Context, EventContext}, - events::{Event, Message}, + context::{EventContext, UpdatePass}, + events::Event, + transport::Message, }; pub(crate) type DrawListener = Box; pub(crate) type Listener = Box)>; pub(crate) trait ErasedListenerBucket { - fn dispatch(&mut self, msg: &Message, ctx: &mut Context); + fn dispatch(&mut self, msg: &Message, pass: UpdatePass<'_>); fn as_any_mut(&mut self) -> &mut dyn Any; } @@ -56,9 +57,9 @@ impl ListenerBucket { } impl ErasedListenerBucket for ListenerBucket { - fn dispatch(&mut self, msg: &Message, ctx: &mut Context) { + fn dispatch<'a>(&mut self, msg: &Message, pass: UpdatePass<'_>) { let event = msg.downcast_ref::().expect("TypeId mismatch"); - let mut ctx = EventContext::new(event, ctx); + let mut ctx = EventContext::new(event, pass); for listener in &mut self.inner { listener(&mut ctx); diff --git a/src/runtime.rs b/src/runtime.rs index 9edba93..16d98ce 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -8,7 +8,6 @@ use crate::{ context::Context, drivers::Driver, error::RuntimeError, - events::Message, transport::EventSource, }; @@ -39,12 +38,7 @@ impl Runtime { let terminal = driver.terminal(); terminal.draw(|f| { - let rect = f.area().into(); - let buffer = f.buffer_mut(); - - self.arena.draw_for_each(rect, |node| { - node.render(buffer); - }); + self.arena.render(f); })?; Ok(()) @@ -53,14 +47,15 @@ impl Runtime { pub fn update(&mut self) { let deadline = Instant::now() + Duration::from_millis(16); let msg = self.source.recv(); + let ctx = &mut self.context; - self.dispatch(&msg); + self.arena.dispatch(&msg, ctx); while Instant::now() < deadline { let msg = self.source.recv_timeout(deadline - Instant::now()); if let Some(msg) = msg { - self.dispatch(&msg); + self.arena.dispatch(&msg, ctx); } } } @@ -69,11 +64,3 @@ impl Runtime { self.context.shutdown_requested } } - -impl Runtime { - fn dispatch(&mut self, msg: &Message) { - self.arena.update_for_each(|node| { - node.dispatch(msg, &mut self.context); - }) - } -} diff --git a/src/transport.rs b/src/transport.rs index 40436fb..8736c79 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -1,10 +1,39 @@ use std::{ + any::{Any, TypeId}, sync::mpsc::{Receiver, Sender}, thread::JoinHandle, time::Duration, }; -use crate::{error::SendError, events::Message}; +use crate::{error::SendError, events::Event}; + +pub struct Message { + event_type_id: TypeId, + event: Box, +} + +impl From for Message { + fn from(value: E) -> Self { + Self { + event_type_id: TypeId::of::(), + event: Box::new(value), + } + } +} + +impl Message { + pub fn new(event: E) -> Self { + Self::from(event) + } + + pub(crate) fn event_type_id(&self) -> TypeId { + self.event_type_id + } + + pub(crate) fn downcast_ref(&self) -> Option<&E> { + self.event.downcast_ref::() + } +} pub struct EventSource { tx: Sender,