From 4a29d7a5cf28c4bf0f197f38144ccc432b1a4241 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Wed, 9 Dec 2020 17:31:08 +0100 Subject: [PATCH] add TerminalCompositor and some nice utilities for ports --- src/main.rs | 114 +++++++++++++++++++++++-------------- src/port.rs | 17 +++++- src/terminal/compositor.rs | 53 +++++++++++++++++ src/terminal/mod.rs | 2 + 4 files changed, 143 insertions(+), 43 deletions(-) create mode 100644 src/terminal/compositor.rs diff --git a/src/main.rs b/src/main.rs index dbfdeb6..99c0644 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,53 +19,83 @@ use { cgmath::{Vector2}, crate::{ view::{View, Observer}, - port::{InnerViewPort, OuterViewPort}, + port::{ViewPort, InnerViewPort, OuterViewPort}, singleton_buffer::SingletonBuffer, vec_buffer::VecBuffer, - terminal::{Terminal, TerminalAtom, TerminalStyle} + terminal::{Terminal, TerminalAtom, TerminalStyle, TerminalCompositor} } }; -#[async_std::main] -async fn main() { - let digits = port::ViewPort::new(); - let mut buf = VecBuffer::new(digits.inner()); +struct Fill(TerminalAtom); +impl View for Fill { + type Key = Vector2; + type Value = TerminalAtom; - let digit_view = digits.outer() - // digit encoding - .map_value( - |digit| - if let Some(digit) = digit { - Some(TerminalAtom::new(char::from_digit(digit, 16).unwrap(), TerminalStyle::bg_color((100,30,30)))) - } else { - None - } - ) - // simple horizontal layout - .map_key( - |idx| Vector2::::new(idx as i16, 0), - |pos| pos.x as usize - ); - - let fut = task::spawn(Terminal::show(digit_view)); - - task::sleep(std::time::Duration::from_secs(1)).await; - buf.push(0); - buf.push(10); - task::sleep(std::time::Duration::from_secs(1)).await; - buf.push(2); - buf.push(3); - task::sleep(std::time::Duration::from_secs(1)).await; - buf.push(4); - task::sleep(std::time::Duration::from_secs(1)).await; - buf.insert(0, 15); - task::sleep(std::time::Duration::from_secs(1)).await; - buf.remove(2); - task::sleep(std::time::Duration::from_secs(1)).await; - - drop(buf); - drop(digits); - - fut.await; + fn view(&self, _: Vector2) -> Option { + Some(self.0.clone()) + } +} + +#[async_std::main] +async fn main() { + let composite_view = port::ViewPort::new(); + let mut compositor = TerminalCompositor::new(composite_view.inner()); + + task::spawn(async move { + // background + let fp = port::ViewPort::with_view(Arc::new(Fill(TerminalAtom::new('.', TerminalStyle::fg_color((50,50,50)))))); + compositor.push(fp.outer()); + + // view of Vec + let digits = port::ViewPort::new(); + let mut buf = VecBuffer::new(digits.inner()); + compositor.push( + digits.outer() + .map_value( // digit encoding + |digit| + if let Some(digit) = digit { + Some(TerminalAtom::new( + char::from_digit(digit, 16).unwrap(), + TerminalStyle::bg_color((100,30,30)).add( + TerminalStyle::fg_color((255,255,255))))) + } else { + None + } + ) + .map_key( // a lightly tilted layout + // mapping from index to position in 2D-grid + |idx| Vector2::::new(idx as i16, idx as i16 / 2), + // reverse mapping from position to idx + |pos| pos.x as usize + )); + + // TODO: use the real terminal size... + for x in 0 .. 10 { + for y in 0 .. 10 { + fp.inner().notify(Vector2::new(x,y)); + } + } + + // now some modifications on our VecBuffer, which will automatically update the View + buf.push(0); + buf.push(10); + task::sleep(std::time::Duration::from_millis(400)).await; + buf.push(2); + buf.push(3); + task::sleep(std::time::Duration::from_millis(400)).await; + buf.push(4); + task::sleep(std::time::Duration::from_millis(400)).await; + buf.insert(0, 15); + task::sleep(std::time::Duration::from_millis(400)).await; + buf.remove(2); + task::sleep(std::time::Duration::from_millis(400)).await; + + for x in 0 .. 4 { + buf.remove(0); + task::sleep(std::time::Duration::from_millis(400)).await; + } + }); + + Terminal::show(composite_view.into_outer()).await; } diff --git a/src/port.rs b/src/port.rs index 5874217..2980068 100644 --- a/src/port.rs +++ b/src/port.rs @@ -31,6 +31,13 @@ where K: Send + Sync + 'static, } } + pub fn with_view(view: Arc>) -> Self { + ViewPort { + view: Arc::new(RwLock::new(Some(view))), + observers: Arc::new(RwLock::new(Vec::new())) + } + } + pub fn set_view(&self, view: Arc>) { *self.view.write().unwrap() = Some(view); } @@ -46,6 +53,14 @@ where K: Send + Sync + 'static, pub fn outer(&self) -> OuterViewPort { OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) } + + pub fn into_inner(self) -> InnerViewPort { + InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) + } + + pub fn into_outer(self) -> OuterViewPort { + OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) + } } //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> @@ -74,7 +89,7 @@ impl OuterViewPort { } impl OuterViewPort { - pub fn stream(&self) -> ChannelReceiver> { + pub fn stream(self) -> ChannelReceiver> { let (s, r) = crate::channel::set_channel(); self.0.add_observer(Arc::new(s)); r diff --git a/src/terminal/compositor.rs b/src/terminal/compositor.rs new file mode 100644 index 0000000..c60e657 --- /dev/null +++ b/src/terminal/compositor.rs @@ -0,0 +1,53 @@ +use { + std::sync::{Arc, RwLock}, + cgmath::Vector2, + crate::{ + view::{View, Observer}, + port::{ViewPort, InnerViewPort, OuterViewPort}, + terminal::{TerminalAtom} + } +}; + +pub struct TerminalCompositor { + layers: Arc, Value = TerminalAtom>>>>>, + port: Arc, TerminalAtom>> +} + +impl TerminalCompositor { + pub fn new(port: InnerViewPort, TerminalAtom>) -> Self { + let layers = Arc::new(RwLock::new(Vec::, Value = TerminalAtom>>>::new())); + + port.set_view_fn({ + let layers = layers.clone(); + move |pos| { + let mut atom = None; + + for l in layers.read().unwrap().iter() { + match (atom, l.view(pos)) { + (None, next) => atom = next, + (Some(last), Some(next)) => atom = Some(next.add_style_back(last.style)), + _ => {} + } + } + + atom + } + }); + + TerminalCompositor { + layers, + port: Arc::new(port) + } + } + + pub fn push(&mut self, v: OuterViewPort, TerminalAtom>) { + self.layers.write().unwrap().push(v.add_observer(self.port.clone())); + } + + pub fn make_port(&mut self) -> InnerViewPort, TerminalAtom> { + let port = ViewPort::new(); + self.push(port.outer()); + port.inner() + } +} + diff --git a/src/terminal/mod.rs b/src/terminal/mod.rs index 55daf75..3296bc0 100644 --- a/src/terminal/mod.rs +++ b/src/terminal/mod.rs @@ -1,10 +1,12 @@ pub mod style; pub mod atom; pub mod terminal; +pub mod compositor; pub use { style::{TerminalStyle}, atom::{TerminalAtom}, terminal::{Terminal, TerminalEvent}, + compositor::TerminalCompositor };