add TerminalCompositor

and some nice utilities for ports
This commit is contained in:
Michael Sippel 2020-12-09 17:31:08 +01:00
parent 62f3a85736
commit 4a29d7a5cf
Signed by: senvas
GPG key ID: F96CF119C34B64A6
4 changed files with 143 additions and 43 deletions

View file

@ -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<i16>;
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::<i16>::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<i16>) -> Option<TerminalAtom> {
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<u32>
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::<i16>::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;
}

View file

@ -31,6 +31,13 @@ where K: Send + Sync + 'static,
}
}
pub fn with_view(view: Arc<dyn View<Key = K, Value = V>>) -> Self {
ViewPort {
view: Arc::new(RwLock::new(Some(view))),
observers: Arc::new(RwLock::new(Vec::new()))
}
}
pub fn set_view(&self, view: Arc<dyn View<Key = K, Value = V>>) {
*self.view.write().unwrap() = Some(view);
}
@ -46,6 +53,14 @@ where K: Send + Sync + 'static,
pub fn outer(&self) -> OuterViewPort<K, V> {
OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
}
pub fn into_inner(self) -> InnerViewPort<K, V> {
InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
}
pub fn into_outer(self) -> OuterViewPort<K, V> {
OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
@ -74,7 +89,7 @@ impl<K: Send + Sync + 'static, V: Send + Sync + 'static> OuterViewPort<K, V> {
}
impl<K: Eq + Hash + Send + Sync + 'static, V: Send + Sync + 'static> OuterViewPort<K, V> {
pub fn stream(&self) -> ChannelReceiver<HashSet<K>> {
pub fn stream(self) -> ChannelReceiver<HashSet<K>> {
let (s, r) = crate::channel::set_channel();
self.0.add_observer(Arc::new(s));
r

View file

@ -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<RwLock<Vec<Arc<dyn View<Key = Vector2<i16>, Value = TerminalAtom>>>>>,
port: Arc<InnerViewPort<Vector2<i16>, TerminalAtom>>
}
impl TerminalCompositor {
pub fn new(port: InnerViewPort<Vector2<i16>, TerminalAtom>) -> Self {
let layers = Arc::new(RwLock::new(Vec::<Arc<dyn View<Key = Vector2<i16>, 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<Vector2<i16>, TerminalAtom>) {
self.layers.write().unwrap().push(v.add_observer(self.port.clone()));
}
pub fn make_port(&mut self) -> InnerViewPort<Vector2<i16>, TerminalAtom> {
let port = ViewPort::new();
self.push(port.outer());
port.inner()
}
}

View file

@ -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
};