add TerminalCompositor
and some nice utilities for ports
This commit is contained in:
parent
62f3a85736
commit
4a29d7a5cf
4 changed files with 143 additions and 43 deletions
92
src/main.rs
92
src/main.rs
|
@ -19,53 +19,83 @@ use {
|
||||||
cgmath::{Vector2},
|
cgmath::{Vector2},
|
||||||
crate::{
|
crate::{
|
||||||
view::{View, Observer},
|
view::{View, Observer},
|
||||||
port::{InnerViewPort, OuterViewPort},
|
port::{ViewPort, InnerViewPort, OuterViewPort},
|
||||||
singleton_buffer::SingletonBuffer,
|
singleton_buffer::SingletonBuffer,
|
||||||
vec_buffer::VecBuffer,
|
vec_buffer::VecBuffer,
|
||||||
terminal::{Terminal, TerminalAtom, TerminalStyle}
|
terminal::{Terminal, TerminalAtom, TerminalStyle, TerminalCompositor}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Fill(TerminalAtom);
|
||||||
|
impl View for Fill {
|
||||||
|
type Key = Vector2<i16>;
|
||||||
|
type Value = TerminalAtom;
|
||||||
|
|
||||||
|
fn view(&self, _: Vector2<i16>) -> Option<TerminalAtom> {
|
||||||
|
Some(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn 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 digits = port::ViewPort::new();
|
||||||
let mut buf = VecBuffer::new(digits.inner());
|
let mut buf = VecBuffer::new(digits.inner());
|
||||||
|
compositor.push(
|
||||||
let digit_view = digits.outer()
|
digits.outer()
|
||||||
// digit encoding
|
.map_value( // digit encoding
|
||||||
.map_value(
|
|
||||||
|digit|
|
|digit|
|
||||||
if let Some(digit) = digit {
|
if let Some(digit) = digit {
|
||||||
Some(TerminalAtom::new(char::from_digit(digit, 16).unwrap(), TerminalStyle::bg_color((100,30,30))))
|
Some(TerminalAtom::new(
|
||||||
|
char::from_digit(digit, 16).unwrap(),
|
||||||
|
TerminalStyle::bg_color((100,30,30)).add(
|
||||||
|
TerminalStyle::fg_color((255,255,255)))))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// simple horizontal layout
|
.map_key( // a lightly tilted layout
|
||||||
.map_key(
|
// mapping from index to position in 2D-grid
|
||||||
|idx| Vector2::<i16>::new(idx as i16, 0),
|
|idx| Vector2::<i16>::new(idx as i16, idx as i16 / 2),
|
||||||
|
// reverse mapping from position to idx
|
||||||
|pos| pos.x as usize
|
|pos| pos.x as usize
|
||||||
);
|
));
|
||||||
|
|
||||||
let fut = task::spawn(Terminal::show(digit_view));
|
// TODO: use the real terminal size...
|
||||||
|
for x in 0 .. 10 {
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
for y in 0 .. 10 {
|
||||||
buf.push(0);
|
fp.inner().notify(Vector2::new(x,y));
|
||||||
buf.push(10);
|
}
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
}
|
||||||
buf.push(2);
|
|
||||||
buf.push(3);
|
// now some modifications on our VecBuffer, which will automatically update the View
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
buf.push(0);
|
||||||
buf.push(4);
|
buf.push(10);
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
task::sleep(std::time::Duration::from_millis(400)).await;
|
||||||
buf.insert(0, 15);
|
buf.push(2);
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
buf.push(3);
|
||||||
buf.remove(2);
|
task::sleep(std::time::Duration::from_millis(400)).await;
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
buf.push(4);
|
||||||
|
task::sleep(std::time::Duration::from_millis(400)).await;
|
||||||
drop(buf);
|
buf.insert(0, 15);
|
||||||
drop(digits);
|
task::sleep(std::time::Duration::from_millis(400)).await;
|
||||||
|
buf.remove(2);
|
||||||
fut.await;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/port.rs
17
src/port.rs
|
@ -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>>) {
|
pub fn set_view(&self, view: Arc<dyn View<Key = K, Value = V>>) {
|
||||||
*self.view.write().unwrap() = Some(view);
|
*self.view.write().unwrap() = Some(view);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +53,14 @@ where K: Send + Sync + 'static,
|
||||||
pub fn outer(&self) -> OuterViewPort<K, V> {
|
pub fn outer(&self) -> OuterViewPort<K, V> {
|
||||||
OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
|
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> {
|
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();
|
let (s, r) = crate::channel::set_channel();
|
||||||
self.0.add_observer(Arc::new(s));
|
self.0.add_observer(Arc::new(s));
|
||||||
r
|
r
|
||||||
|
|
53
src/terminal/compositor.rs
Normal file
53
src/terminal/compositor.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod atom;
|
pub mod atom;
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
|
pub mod compositor;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
style::{TerminalStyle},
|
style::{TerminalStyle},
|
||||||
atom::{TerminalAtom},
|
atom::{TerminalAtom},
|
||||||
terminal::{Terminal, TerminalEvent},
|
terminal::{Terminal, TerminalEvent},
|
||||||
|
compositor::TerminalCompositor
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue