move nested into a lib crate

This commit is contained in:
Michael Sippel 2021-04-19 05:01:09 +02:00
parent c63fd6d1f2
commit e21b888b6c
Signed by: senvas
GPG key ID: F96CF119C34B64A6
30 changed files with 30 additions and 377 deletions

17
nested/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
authors = ["Michael Sippel <micha@fragmental.art>"]
edition = "2018"
name = "nested"
version = "0.1.0"
[dependencies]
no_deadlocks = "*"
cgmath = "0.17.0"
termion = "1.5.5"
signal-hook = "0.3.1"
signal-hook-async-std = "0.2.0"
[dependencies.async-std]
version = "1.7.0"
features = ["unstable", "attributes"]

239
nested/src/cell_layout.rs Normal file
View file

@ -0,0 +1,239 @@
use {
async_std::stream::StreamExt,
std::{
sync::Arc,
collections::HashMap,
cmp::{min, max}
},
cgmath::{Point2, Vector2},
std::sync::RwLock,
crate::{
core::{InnerViewPort, OuterViewPort, Observer, ObserverExt, ObserverBroadcast, ChannelReceiver, ChannelSender},
terminal::{TerminalView, TerminalAtom},
index::{ImplIndexView},
grid::GridWindowIterator,
projection::ProjectionArg
}
};
pub struct Cell {
view: Arc<dyn TerminalView>,
_arg: Arc<RwLock<ProjectionArg<dyn TerminalView, CellLayout>>>
}
pub struct CellLayout {
cells: HashMap<Point2<i16>, Cell>,
col_widths: Vec<usize>,
row_heights: Vec<usize>,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
send: ChannelSender<Vec<(Point2<i16>, Point2<i16>)>>
}
impl ImplIndexView for CellLayout {
type Key = Point2<i16>;
type Value = TerminalAtom;
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
let cell_pos = self.get_cell_containing(pos);
let cell_off = self.get_cell_offset(&cell_pos);
self.cells.get(&cell_pos)?.view.get(&(pos - cell_off))
}
fn area(&self) -> Option<Vec<Point2<i16>>> {
Some(
self.cells.iter()
.flat_map(
|(cell_pos, cell)| {
let off = self.get_cell_offset(cell_pos);
cell.view.area()
.unwrap_or(Vec::new())
.into_iter()
.map(move |p| p + off)
}
).collect()
)
}
}
impl CellLayout {
pub fn with_port(port: InnerViewPort<dyn TerminalView>) -> Arc<RwLock<Self>> {
let (send, mut recv) = crate::core::channel::channel();
let v = Arc::new(RwLock::new(CellLayout {
cells: HashMap::new(),
col_widths: Vec::new(),
row_heights: Vec::new(),
cast: port.get_broadcast(),
send
}));
/*
* its is a bit ugly to spawn a task here, but we need the stream to decouple
* in order to avoid deadlocks
*/
async_std::task::spawn({
let l = v.clone();
async move {
while let Some((cell_idx, pos)) = recv.next().await {
l.write().unwrap().update_cell(&cell_idx, &pos);
}
}
});
port.set_view(Some(v.clone()));
v
}
pub fn set_cell(layout: &Arc<RwLock<Self>>, cell_pos: Point2<i16>, port: OuterViewPort<dyn TerminalView>) {
let sender = layout.read().unwrap().send.clone();
let arg = ProjectionArg::new(
move |s: Arc<RwLock<Self>>, pos: &Point2<i16>| {
sender.send((cell_pos, *pos));
}
);
layout.write().unwrap().cells.insert(
cell_pos,
Cell {
view: arg.read().unwrap().src.clone(),
_arg: arg.clone()
});
arg.write().unwrap().proj = Arc::downgrade(&layout);
port.add_observer(arg);
}
fn update_col_width(&mut self, col_idx: i16) -> bool {
let mut max_width = 0;
for row_idx in 0 .. self.row_heights.len() as i16 {
if let Some(cell) = self.cells.get(&Point2::new(col_idx, row_idx)) {
if let Some(area) = cell.view.area() {
max_width = max(
max_width,
area.iter()
.map(|pt| pt.x as usize + 1)
.max()
.unwrap_or(0)
);
}
}
}
let changed = (self.col_widths[col_idx as usize] != max_width);
self.col_widths[col_idx as usize] = max_width;
changed
}
fn update_row_height(&mut self, row_idx: i16) -> bool {
let mut max_height = 0;
for col_idx in 0 .. self.col_widths.len() as i16 {
if let Some(cell) = self.cells.get(&Point2::new(col_idx, row_idx)) {
if let Some(area) = cell.view.area() {
max_height = max(
max_height,
area.iter()
.map(|pt| pt.y as usize + 1)
.max()
.unwrap_or(0)
);
}
}
}
let changed = (self.row_heights[row_idx as usize] != max_height);
self.row_heights[row_idx as usize] = max_height;
changed
}
fn update_cell(&mut self, cell_pos: &Point2<i16>, pos: &Point2<i16>) {
for _ in self.col_widths.len() as i16 ..= cell_pos.x { self.col_widths.push(0); }
for _ in self.row_heights.len() as i16 ..= cell_pos.y { self.row_heights.push(0); }
let cell_off = self.get_cell_offset(cell_pos);
self.cast.notify(&(pos + cell_off));
let old_n = self.get_cell_offset(&(cell_pos + Vector2::new(1, 1)));
let old_width = self.get_width();
let old_height = self.get_height();
// does this really have to be recalculated every time ??
let width_changed = self.update_col_width(cell_pos.x);
let height_changed = self.update_row_height(cell_pos.y);
let extent = Point2::new(
max(self.get_width(), old_width) as i16,
max(self.get_height(), old_height) as i16
);
let new_n = self.get_cell_offset(&(cell_pos + Vector2::new(1, 1)));
/* if a cell updates its size, the complete rectangle to the right is refreshed
* todo: optimize to use area() of cell views
*/
if width_changed {
self.cast.notify_each(GridWindowIterator::from(
Point2::new(
min(old_n.x, new_n.x),
0
)
..
extent
));
}
if height_changed {
self.cast.notify_each(GridWindowIterator::from(
Point2::new(
0,
min(old_n.y, new_n.y)
)
..
extent
));
}
}
fn get_width(&self) -> usize {
self.col_widths.iter().sum()
}
fn get_height(&self) -> usize {
self.row_heights.iter().sum()
}
fn get_cell_offset(&self, cell_pos: &Point2<i16>) -> Vector2<i16> {
Vector2::new(
self.col_widths.iter().take(cell_pos.x as usize).sum::<usize>() as i16,
self.row_heights.iter().take(cell_pos.y as usize).sum::<usize>() as i16
)
}
fn get_cell_containing(&self, glob_pos: &Point2<i16>) -> Point2<i16> {
Point2::new(
self.col_widths.iter()
.fold(
(0, 0),
|(cell_idx, x), width|
(
cell_idx + if (x + *width as i16) <= glob_pos.x { 1 } else { 0 },
x + *width as i16
)).0,
self.row_heights.iter()
.fold(
(0, 0),
|(cell_idx, y), height|
(
cell_idx + if (y + *height as i16) <= glob_pos.y { 1 } else { 0 },
y + *height as i16
)).0
)
}
}

212
nested/src/core/channel.rs Normal file
View file

@ -0,0 +1,212 @@
use {
core::{
task::{Poll, Context, Waker},
pin::Pin
},
std::{
sync::{Arc, Mutex},
collections::HashSet,
hash::Hash
},
async_std::{
stream::Stream
},
crate::{
core::{Observer}
}
};
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Traits
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
pub trait ChannelData : Default + IntoIterator + Send + Sync {
fn channel_insert(&mut self, x: Self::Item);
}
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Queue Channel
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
impl<T> ChannelData for Vec<T>
where T: Send + Sync {
fn channel_insert(&mut self, x: T) {
self.push(x);
}
}
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Set Channel
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
impl<T> ChannelData for HashSet<T>
where T: Eq + Hash + Send + Sync {
fn channel_insert(&mut self, x: T) {
self.insert(x);
}
}
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Singleton Channel
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
impl<T> ChannelData for Option<T>
where T: Send + Sync {
fn channel_insert(&mut self, x: T) {
*self = Some(x);
}
}
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Channel
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
struct ChannelState<Data: ChannelData> {
send_buf: Option<Data>,
recv_iter: Option<Data::IntoIter>,
num_senders: usize,
waker: Option<Waker>
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct ChannelSender<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
pub struct ChannelReceiver<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Data: ChannelData> ChannelSender<Data>
where Data::IntoIter: Send + Sync {
pub fn send(&self, msg: Data::Item) {
let mut state = self.0.lock().unwrap();
if state.send_buf.is_none() {
state.send_buf = Some(Data::default());
}
state.send_buf.as_mut().unwrap().channel_insert(msg);
if let Some(waker) = state.waker.take() {
waker.wake();
}
}
}
use crate::core::View;
impl<V: View + ?Sized, Data: ChannelData<Item = V::Msg>> Observer<V> for ChannelSender<Data>
where V::Msg: Clone, Data::IntoIter: Send + Sync {
fn notify(&self, msg: &V::Msg) {
self.send(msg.clone());
}
}
impl<Data: ChannelData> Clone for ChannelSender<Data> {
fn clone(&self) -> Self {
self.0.lock().unwrap().num_senders += 1;
ChannelSender(self.0.clone())
}
}
impl<Data: ChannelData> Drop for ChannelSender<Data> {
fn drop(&mut self) {
let mut state = self.0.lock().unwrap();
state.num_senders -= 1;
if let Some(waker) = state.waker.take() {
waker.wake();
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Data: ChannelData> ChannelReceiver<Data> {
pub async fn recv(&self) -> Option<Data> {
ChannelRead(self.0.clone()).await
}
}
struct ChannelRead<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
impl<Data: ChannelData> std::future::Future for ChannelRead<Data> {
type Output = Option<Data>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut state = self.0.lock().unwrap();
if let Some(buf) = state.send_buf.take() {
Poll::Ready(Some(buf))
} else if state.num_senders == 0 {
Poll::Ready(None)
} else {
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Data: ChannelData> Stream for ChannelReceiver<Data> {
type Item = Data::Item;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Option<Self::Item>> {
let mut state = self.0.lock().unwrap();
if let Some(recv_iter) = state.recv_iter.as_mut() {
if let Some(val) = recv_iter.next() {
return Poll::Ready(Some(val))
} else {
state.recv_iter = None
}
}
if let Some(send_buf) = state.send_buf.take() {
state.recv_iter = Some(send_buf.into_iter());
// recv_iter.next() is guaranteed to be Some(x)
Poll::Ready(state.recv_iter.as_mut().unwrap().next())
} else if state.num_senders == 0 {
Poll::Ready(None)
} else {
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Factory Functions
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
pub fn channel<Data: ChannelData>() -> (ChannelSender<Data>, ChannelReceiver<Data>) {
let state = Arc::new(Mutex::new(ChannelState{
send_buf: None,
recv_iter: None,
num_senders: 1,
waker: None
}));
(ChannelSender(state.clone()), ChannelReceiver(state))
}
pub fn set_channel<T: Eq + Hash + Send + Sync>() -> (ChannelSender<HashSet<T>>, ChannelReceiver<HashSet<T>>) {
channel::<HashSet<T>>()
}
pub fn queue_channel<T: Send + Sync>() -> (ChannelSender<Vec<T>>, ChannelReceiver<Vec<T>>) {
channel::<Vec<T>>()
}
pub fn singleton_channel<T: Send + Sync>() -> (ChannelSender<Option<T>>, ChannelReceiver<Option<T>>) {
channel::<Option<T>>()
}

30
nested/src/core/mod.rs Normal file
View file

@ -0,0 +1,30 @@
pub mod view;
pub mod observer;
pub mod channel;
pub mod port;
pub use {
view::{View},
observer::{
Observer,
ObserverExt,
ObserverBroadcast,
NotifyFnObserver,
ResetFnObserver
},
channel::{
ChannelReceiver,
ChannelSender,
set_channel,
queue_channel,
singleton_channel
},
port::{
ViewPort,
InnerViewPort,
OuterViewPort
}
};

147
nested/src/core/observer.rs Normal file
View file

@ -0,0 +1,147 @@
use {
crate::core::View,
std::{
sync::{Arc, Weak}
},
std::sync::RwLock
};
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Observer
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
pub trait Observer<V: View + ?Sized> : Send + Sync {
fn reset(&mut self, _view: Option<Arc<V>>) {}
fn notify(&self, msg: &V::Msg);
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<V: View + ?Sized, O: Observer<V>> Observer<V> for Arc<RwLock<O>> {
fn reset(&mut self, view: Option<Arc<V>>) {
self.write().unwrap().reset(view);
}
fn notify(&self, msg: &V::Msg) {
self.read().unwrap().notify(&msg);
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub trait ObserverExt<V: View + ?Sized> : Observer<V> {
fn notify_each(&self, it: impl IntoIterator<Item = V::Msg>);
}
impl<V: View + ?Sized, T: Observer<V>> ObserverExt<V> for T {
fn notify_each(&self, it: impl IntoIterator<Item = V::Msg>) {
for msg in it {
self.notify(&msg);
}
}
}
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Broadcast
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
pub struct ObserverBroadcast<V: View + ?Sized> {
observers: Vec<Weak<RwLock<dyn Observer<V>>>>
}
impl<V: View + ?Sized> ObserverBroadcast<V> {
pub fn new() -> Self {
ObserverBroadcast {
observers: Vec::new()
}
}
pub fn add_observer(&mut self, obs: Weak<RwLock<dyn Observer<V>>>) {
self.cleanup();
self.observers.push(obs);
}
fn cleanup(&mut self) {
self.observers.retain(|o| o.strong_count() > 0);
}
fn iter(&self) -> impl Iterator<Item = Arc<RwLock<dyn Observer<V>>>> + '_ {
self.observers.iter().filter_map(|o| o.upgrade())
}
}
impl<V: View + ?Sized> Observer<V> for ObserverBroadcast<V> {
fn reset(&mut self, view: Option<Arc<V>>) {
for o in self.iter() {
o.write().unwrap().reset(view.clone());
}
}
fn notify(&self, msg: &V::Msg) {
for o in self.iter() {
o.read().unwrap().notify(&msg);
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct NotifyFnObserver<V, F>
where V: View + ?Sized,
F: Fn(&V::Msg) + Send + Sync {
f: F,
_phantom: std::marker::PhantomData<V>
}
impl<V, F> NotifyFnObserver<V, F>
where V: View + ?Sized,
F: Fn(&V::Msg) + Send + Sync {
pub fn new(f: F) -> Self {
NotifyFnObserver {
f,
_phantom: std::marker::PhantomData
}
}
}
impl<V, F> Observer<V> for NotifyFnObserver<V, F>
where V: View + ?Sized,
F: Fn(&V::Msg) + Send + Sync {
fn notify(&self, msg: &V::Msg) {
(self.f)(msg);
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct ResetFnObserver<V, F>
where V: View + ?Sized,
F: Fn(Option<Arc<V>>) + Send + Sync {
f: F,
_phantom: std::marker::PhantomData<V>
}
impl<V, F> ResetFnObserver<V, F>
where V: View + ?Sized,
F: Fn(Option<Arc<V>>) + Send + Sync {
pub fn new(f: F) -> Self {
ResetFnObserver {
f,
_phantom: std::marker::PhantomData
}
}
}
impl<V, F> Observer<V> for ResetFnObserver<V, F>
where V: View + ?Sized,
F: Fn(Option<Arc<V>>) + Send + Sync {
fn notify(&self, _msg: &V::Msg) {}
fn reset(&mut self, view: Option<Arc<V>>) {
(self.f)(view);
}
}

160
nested/src/core/port.rs Normal file
View file

@ -0,0 +1,160 @@
use {
std::sync::Arc,
std::any::Any,
std::sync::RwLock,
crate::core::{
View,
Observer,
ObserverBroadcast,
NotifyFnObserver,
ResetFnObserver
}
};
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
View Port
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
pub struct ViewPort<V: View + ?Sized> {
view: Arc<RwLock<Option<Arc<V>>>>,
observers: Arc<RwLock<ObserverBroadcast<V>>>
}
impl<V: View + ?Sized> ViewPort<V> {
pub fn new() -> Self {
ViewPort {
view: Arc::new(RwLock::new(None)),
observers: Arc::new(RwLock::new(ObserverBroadcast::new()))
}
}
pub fn with_view(view: Arc<V>) -> Self {
let port = ViewPort::new();
port.set_view(Some(view));
port
}
pub fn set_view(&self, view: Option<Arc<V>>) {
*self.view.write().unwrap() = view.clone();
self.observers.write().unwrap().reset(view);
}
pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) {
self.observers.write().unwrap().add_observer(Arc::downgrade(&observer));
observer.write().unwrap().reset(self.view.read().unwrap().clone());
}
pub fn inner(&self) -> InnerViewPort<V> {
InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
}
pub fn outer(&self) -> OuterViewPort<V> {
OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
}
pub fn into_inner(self) -> InnerViewPort<V> {
InnerViewPort(ViewPort{ view: self.view, observers: self.observers })
}
pub fn into_outer(self) -> OuterViewPort<V> {
OuterViewPort(ViewPort{ view: self.view, observers: self.observers })
}
}
impl<V: View + ?Sized> Clone for ViewPort<V> {
fn clone(&self) -> Self {
ViewPort {
view: self.view.clone(),
observers: self.observers.clone()
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct InnerViewPort<V: View + ?Sized>(ViewPort<V>);
pub struct OuterViewPort<V: View + ?Sized>(ViewPort<V>);
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<V: View + ?Sized> InnerViewPort<V> {
pub fn get_broadcast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
self.0.observers.clone()
}
pub fn set_view(&self, view: Option<Arc<V>>) -> Arc<RwLock<ObserverBroadcast<V>>> {
self.0.set_view(view);
self.get_broadcast()
}
pub fn get_view(&self) -> Option<Arc<V>> {
self.0.view.read().unwrap().clone()
}
pub fn notify(&self, msg: &V::Msg) {
self.0.observers.read().unwrap().notify(msg);
}
}
impl<V: View + ?Sized> Clone for InnerViewPort<V> {
fn clone(&self) -> Self {
InnerViewPort(self.0.clone())
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<V: View + ?Sized + 'static> OuterViewPort<V> {
pub fn get_view(&self) -> Option<Arc<V>> {
self.0.view.read().unwrap().clone()
}
pub fn get_view_arc(&self) -> Arc<RwLock<Option<Arc<V>>>> {
self.0.view.clone()
}
pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) -> Arc<RwLock<Option<Arc<V>>>> {
self.0.add_observer(observer);
self.get_view_arc()
}
pub fn add_reset_fn<F: Fn(Option<Arc<V>>) + Send + Sync + 'static>(&self, reset: F) -> Arc<RwLock<ResetFnObserver<V, F>>> {
let obs = Arc::new(RwLock::new(ResetFnObserver::new(reset)));
self.add_observer(obs.clone());
obs
}
pub fn add_notify_fn<F: Fn(&V::Msg) + Send + Sync + 'static>(&self, notify: F) -> Arc<RwLock<NotifyFnObserver<V, F>>> {
let obs = Arc::new(RwLock::new(NotifyFnObserver::new(notify)));
self.add_observer(obs.clone());
obs
}
}
impl<V: View + ?Sized> Clone for OuterViewPort<V> {
fn clone(&self) -> Self {
OuterViewPort(self.0.clone())
}
}
/*
impl<V: View + ?Sized + 'static> OuterViewPort<V>
where V::Msg: Clone {
pub fn into_stream<Data>(
self,
reset: impl Fn(Option<Arc<V>>, ChannelSender<Data>) + Send + Sync + 'static
) -> ChannelReceiver<Data>
where Data: ChannelData<Item = V::Msg> + 'static,
Data::IntoIter: Send + Sync + 'static
{
let (s, r) = crate::core::channel::channel::<Data>();
self.add_observer(Arc::new(s.clone()));
self.add_reset_fn(
move |view| { reset(view, s.clone()); }
);
r
}
}
*/

28
nested/src/core/view.rs Normal file
View file

@ -0,0 +1,28 @@
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
View
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
pub trait View : Send + Sync {
/// Notification message for the observers
type Msg;
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
use std::sync::Arc;
use std::sync::RwLock;
impl<V: View + ?Sized> View for RwLock<V> {
type Msg = V::Msg;
}
impl<V: View + ?Sized> View for Arc<V> {
type Msg = V::Msg;
}
impl<V: View> View for Option<V> {
type Msg = V::Msg;
}

81
nested/src/grid/mod.rs Normal file
View file

@ -0,0 +1,81 @@
use {
std::{
ops::{Range, RangeInclusive}
},
cgmath::{Point2},
crate::{
index::{IndexView}
}
};
pub mod offset;
pub use offset::GridOffset;
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub trait GridView = IndexView<Point2<i16>>;
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Item> dyn GridView<Item = Item> {
pub fn range(&self) -> RangeInclusive<Point2<i16>> {
let area = self.area().unwrap_or(Vec::new());
Point2::new(
area.iter().map(|p| p.x).min().unwrap_or(i16::MIN),
area.iter().map(|p| p.y).min().unwrap_or(i16::MIN)
) ..=
Point2::new(
area.iter().map(|p| p.x).max().unwrap_or(i16::MAX),
area.iter().map(|p| p.y).max().unwrap_or(i16::MAX)
)
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct GridWindowIterator {
next: Point2<i16>,
range: Range<Point2<i16>>
}
impl From<Range<Point2<i16>>> for GridWindowIterator {
fn from(range: Range<Point2<i16>>) -> Self {
GridWindowIterator {
next: range.start,
range
}
}
}
impl From<RangeInclusive<Point2<i16>>> for GridWindowIterator {
fn from(range: RangeInclusive<Point2<i16>>) -> Self {
GridWindowIterator {
next: *range.start(),
range: *range.start() .. Point2::new(range.end().x+1, range.end().y+1)
}
}
}
impl Iterator for GridWindowIterator {
type Item = Point2<i16>;
fn next(&mut self) -> Option<Point2<i16>> {
if self.next.y < self.range.end.y {
let next = self.next;
if self.next.x+1 < self.range.end.x {
self.next.x += 1;
} else {
self.next.x = self.range.start.x;
self.next.y += 1;
}
Some(next)
} else {
None
}
}
}

84
nested/src/grid/offset.rs Normal file
View file

@ -0,0 +1,84 @@
use {
std::sync::Arc,
cgmath::{Point2, Vector2},
std::sync::RwLock,
crate::{
core::{
View,
Observer,
ObserverExt,
ObserverBroadcast,
InnerViewPort
},
index::{IndexView},
grid::{GridView}
}
};
pub struct GridOffset<V: GridView + ?Sized> {
src: Option<Arc<V>>,
offset: Vector2<i16>,
cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = V::Item>>>>
}
impl<V: 'static + GridView + ?Sized> GridOffset<V> {
pub fn new(port: InnerViewPort<dyn GridView<Item = V::Item>>) -> Arc<RwLock<Self>> {
let offset_view =
Arc::new(RwLock::new(
GridOffset::<V> {
src: None,
offset: Vector2::new(0, 0),
cast: port.get_broadcast()
}
));
port.set_view(Some(offset_view.clone()));
offset_view
}
pub fn set_offset(&mut self, new_offset: Vector2<i16>) {
let old_area = self.area();
self.offset = new_offset;
let new_area = self.area();
if let Some(area) = old_area { self.cast.notify_each(area); }
if let Some(area) = new_area { self.cast.notify_each(area); }
}
}
impl<V: GridView + ?Sized> View for GridOffset<V> {
type Msg = Point2<i16>;
}
impl<V: GridView + ?Sized> IndexView<Point2<i16>> for GridOffset<V> {
type Item = V::Item;
fn get(&self, pos: &Point2<i16>) -> Option<Self::Item> {
self.src.as_ref()?.get(&(pos - self.offset))
}
fn area(&self) -> Option<Vec<Point2<i16>>> {
Some(
self.src.as_ref()?
.area()?.into_iter()
.map(|pos| pos + self.offset)
.collect()
)
}
}
impl<V: GridView + ?Sized> Observer<V> for GridOffset<V> {
fn reset(&mut self, view: Option<Arc<V>>) {
let old_area = self.area();
self.src = view;
let new_area = self.area();
if let Some(area) = old_area { self.cast.notify_each(area); }
if let Some(area) = new_area { self.cast.notify_each(area); }
}
fn notify(&self, msg: &Point2<i16>) {
self.cast.notify(&(msg + self.offset));
}
}

View file

@ -0,0 +1,107 @@
pub use {
std::{
sync::Arc,
boxed::Box
},
std::sync::RwLock,
crate::{
core::{
View,
Observer,
ObserverExt,
ObserverBroadcast,
ViewPort,
InnerViewPort,
OuterViewPort
},
index::{IndexView}
}
};
impl<Key: 'static, Item: 'static> OuterViewPort<dyn IndexView<Key, Item = Item>> {
pub fn map_item<
DstItem: 'static,
F: Fn(&Key, &Item) -> DstItem + Send + Sync + 'static
>(
&self,
f: F
) -> OuterViewPort<dyn IndexView<Key, Item = DstItem>> {
let port = ViewPort::new();
let map = MapIndexItem::new(port.inner(), f);
self.add_observer(map.clone());
port.into_outer()
}
}
pub struct MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
{
src_view: Option<Arc<SrcView>>,
f: F,
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Key, Item = DstItem>>>>
}
impl<Key, DstItem, SrcView, F> MapIndexItem<Key, DstItem, SrcView, F>
where Key: 'static,
DstItem: 'static,
SrcView: IndexView<Key> + ?Sized + 'static,
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync + 'static
{
fn new(
port: InnerViewPort<dyn IndexView<Key, Item = DstItem>>,
f: F
) -> Arc<RwLock<Self>> {
let map = Arc::new(RwLock::new(
MapIndexItem {
src_view: None,
f,
cast: port.get_broadcast()
}
));
port.set_view(Some(map.clone()));
map
}
}
impl<Key, DstItem, SrcView, F> View for MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
{
type Msg = Key;
}
impl<Key, DstItem, SrcView, F> IndexView<Key> for MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
{
type Item = DstItem;
fn get(&self, key: &Key) -> Option<Self::Item> {
self.src_view.get(key).as_ref().map(|item| (self.f)(key, item))
}
fn area(&self) -> Option<Vec<Key>> {
self.src_view.area()
}
}
impl<Key, DstItem, SrcView, F> Observer<SrcView> for MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
{
fn reset(&mut self, view: Option<Arc<SrcView>>) {
let old_area = self.area();
self.src_view = view;
let new_area = self.area();
if let Some(area) = old_area { self.cast.notify_each(area); }
if let Some(area) = new_area { self.cast.notify_each(area); }
}
fn notify(&self, msg: &Key) {
self.cast.notify(msg);
}
}

118
nested/src/index/map_key.rs Normal file
View file

@ -0,0 +1,118 @@
pub use {
std::{
sync::Arc,
boxed::Box
},
std::sync::RwLock,
crate::{
core::{
View,
Observer,
ObserverExt,
ObserverBroadcast,
ViewPort,
InnerViewPort,
OuterViewPort
},
index::{IndexView}
}
};
impl<SrcKey: 'static, Item: 'static> OuterViewPort<dyn IndexView<SrcKey, Item = Item>> {
pub fn map_key<
DstKey: 'static,
F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
>(
&self,
f1: F1,
f2: F2
) -> OuterViewPort<dyn IndexView<DstKey, Item = Item>> {
let port = ViewPort::new();
let map = MapIndexKey::new(port.inner(), f1, f2);
self.add_observer(map.clone());
port.into_outer()
}
}
pub struct MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
where SrcView: IndexView<SrcKey> + ?Sized,
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
{
src_view: Option<Arc<SrcView>>,
f1: F1,
f2: F2,
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<DstKey, Item = SrcView::Item>>>>
}
impl<DstKey, SrcKey, SrcView, F1, F2> MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
where DstKey: 'static,
SrcKey: 'static,
SrcView: IndexView<SrcKey> + ?Sized + 'static,
SrcView::Item: 'static,
F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
{
fn new(
port: InnerViewPort<dyn IndexView<DstKey, Item = SrcView::Item>>,
f1: F1,
f2: F2
) -> Arc<RwLock<Self>> {
let map = Arc::new(RwLock::new(
MapIndexKey {
src_view: None,
f1,
f2,
cast: port.get_broadcast()
}
));
port.set_view(Some(map.clone()));
map
}
}
impl<DstKey, SrcKey, SrcView, F1, F2> View for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
where SrcView: IndexView<SrcKey> + ?Sized,
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
{
type Msg = DstKey;
}
impl<DstKey, SrcKey, SrcView, F1, F2> IndexView<DstKey> for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
where SrcView: IndexView<SrcKey> + ?Sized,
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
{
type Item = SrcView::Item;
fn get(&self, key: &DstKey) -> Option<Self::Item> {
self.src_view.get(&(self.f2)(key)?)
}
fn area(&self) -> Option<Vec<DstKey>> {
Some(self.src_view.area()?.iter().map(&self.f1).collect())
}
}
impl<DstKey, SrcKey, SrcView, F1, F2> Observer<SrcView> for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
where SrcView: IndexView<SrcKey> + ?Sized,
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
{
fn reset(&mut self, view: Option<Arc<SrcView>>) {
let old_area = self.area();
self.src_view = view;
let new_area = self.area();
if let Some(area) = old_area { self.cast.notify_each(area); }
if let Some(area) = new_area { self.cast.notify_each(area); }
}
fn notify(&self, msg: &SrcKey) {
self.cast.notify(&(self.f1)(msg));
}
}

96
nested/src/index/mod.rs Normal file
View file

@ -0,0 +1,96 @@
pub mod map_item;
pub mod map_key;
use {
std::{
sync::Arc,
ops::Deref,
},
std::sync::RwLock,
crate::core::View
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub trait IndexView<Key> : View<Msg = Key> {
type Item;
fn get(&self, key: &Key) -> Option<Self::Item>;
// todo: AreaIterator enum to switch between Allocated and Procedural area
fn area(&self) -> Option<Vec<Key>> {
None
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Key, V: IndexView<Key> + ?Sized> IndexView<Key> for RwLock<V> {
type Item = V::Item;
fn get(&self, key: &Key) -> Option<Self::Item> {
self.read().unwrap().get(key)
}
fn area(&self) -> Option<Vec<Key>> {
self.read().unwrap().area()
}
}
impl<Key, V: IndexView<Key> + ?Sized> IndexView<Key> for Arc<V> {
type Item = V::Item;
fn get(&self, key: &Key) -> Option<Self::Item> {
self.deref().get(key)
}
fn area(&self) -> Option<Vec<Key>> {
self.deref().area()
}
}
impl<Key, V: IndexView<Key>> IndexView<Key> for Option<V> {
type Item = V::Item;
fn get(&self, key: &Key) -> Option<Self::Item> {
self.as_ref()?.get(key)
}
fn area(&self) -> Option<Vec<Key>> {
if let Some(v) = self.as_ref() {
v.area()
} else {
Some(Vec::new())
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub trait ImplIndexView : Send + Sync {
type Key;
type Value;
fn get(&self, key: &Self::Key) -> Option<Self::Value>;
fn area(&self) -> Option<Vec<Self::Key>> {
None
}
}
impl<V: ImplIndexView> View for V {
type Msg = V::Key;
}
impl<V: ImplIndexView> IndexView<V::Key> for V {
type Item = V::Value;
fn get(&self, key: &V::Key) -> Option<Self::Item> {
(self as &V).get(key)
}
fn area(&self) -> Option<Vec<V::Key>> {
(self as &V).area()
}
}

View file

@ -0,0 +1,90 @@
use {
std::{
sync::{Arc},
collections::HashSet
},
std::sync::RwLock,
cgmath::Point2,
crate::{
core::{ViewPort, Observer, ObserverExt, ObserverBroadcast, InnerViewPort, OuterViewPort},
index::{ImplIndexView},
terminal::{TerminalAtom, TerminalView, TerminalStyle},
projection::{ProjectionHelper, ProjectionArg}
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct LeveledTermView {
src: Arc<RwLock<dyn TerminalView>>,
level: usize,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
proj_helper: ProjectionHelper<Self>
}
impl LeveledTermView {
pub fn new(
src: OuterViewPort<dyn TerminalView>
) -> (Arc<RwLock<Self>>, OuterViewPort<dyn TerminalView>) {
let port = ViewPort::new();
let v = Self::with_port(src, port.inner());
(v, port.into_outer())
}
pub fn with_port(
src_port: OuterViewPort<dyn TerminalView>,
dst_port: InnerViewPort<dyn TerminalView>
) -> Arc<RwLock<Self>> {
let mut proj_helper = ProjectionHelper::new();
let v = Arc::new(RwLock::new(
LeveledTermView {
src: proj_helper.new_index_arg(
src_port,
|p: &mut Self, pos: &Point2<i16>| {
p.cast.notify(pos);
}),
level: 0,
cast: dst_port.get_broadcast(),
proj_helper
}
));
v.write().unwrap().proj_helper.set_proj(&v);
dst_port.set_view(Some(v.clone()));
v
}
pub fn set_level(&mut self, l: usize) {
self.level = l;
// update complete area
if let Some(a) = self.src.area() {
self.cast.notify_each(a);
}
}
}
impl ImplIndexView for LeveledTermView {
type Key = Point2<i16>;
type Value = TerminalAtom;
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
self.src.get(pos).map(
|a| a.add_style_front(
if self.level > 0 {
TerminalStyle::bold(true)
.add(TerminalStyle::bg_color((0, 0, 0)))
} else {
TerminalStyle::bold(false)
})
)
}
fn area(&self) -> Option<Vec<Point2<i16>>> {
self.src.area()
}
}

5
nested/src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
pub fn magic_header() {
eprintln!("<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>");
}

176
nested/src/projection.rs Normal file
View file

@ -0,0 +1,176 @@
use {
std::{
cmp::{max},
any::Any,
sync::{Arc, Weak},
},
async_std::{
stream::StreamExt
},
std::sync::RwLock,
crate::{
core::{View, Observer, ObserverExt, OuterViewPort, channel::{channel, ChannelData, ChannelSender, ChannelReceiver}},
singleton::{SingletonView},
sequence::{SequenceView},
index::{IndexView}
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct ProjectionHelper<P: Send + Sync + 'static> {
keepalive: Vec<Arc<dyn Any + Send + Sync>>,
proj: Arc<RwLock<Weak<RwLock<P>>>>
}
impl<P: Send + Sync + 'static> ProjectionHelper<P> {
pub fn new() -> Self {
ProjectionHelper {
keepalive: Vec::new(),
proj: Arc::new(RwLock::new(Weak::new()))
}
}
pub fn set_proj(&mut self, proj: &Arc<RwLock<P>>) {
*self.proj.write().unwrap() = Arc::downgrade(proj);
}
// todo: make this functions generic over the View
// this does currently not work because Observer<V> is not implemented for ProjectionArg for *all* V.
pub fn new_singleton_arg<Item: 'static>(
&mut self,
port: OuterViewPort<dyn SingletonView<Item = Item>>,
notify: impl Fn(&mut P, &()) + Send + Sync + 'static
) -> Arc<RwLock<Option<Arc<dyn SingletonView<Item = Item>>>>> {
let (view, obs) = self.new_arg(notify);
port.add_observer(obs);
view
}
pub fn new_sequence_arg<Item: 'static>(
&mut self,
port: OuterViewPort<dyn SequenceView<Item = Item>>,
notify: impl Fn(&mut P, &usize) + Send + Sync + 'static
) -> Arc<RwLock<Option<Arc<dyn SequenceView<Item = Item>>>>> {
let (view, obs) = self.new_arg(notify);
port.add_observer(obs);
view
}
pub fn new_index_arg<Key: Clone + Send + Sync + 'static, Item: 'static>(
&mut self,
port: OuterViewPort<dyn IndexView<Key, Item = Item>>,
notify: impl Fn(&mut P, &Key) + Send + Sync + 'static
) -> Arc<RwLock<Option<Arc<dyn IndexView<Key, Item = Item>>>>> {
let (view, obs) = self.new_arg(notify);
port.add_observer(obs);
view
}
pub fn new_arg<
V: View + ?Sized + 'static
>(
&mut self,
notify: impl Fn(&mut P, &V::Msg) + Send + Sync + 'static
) -> (
Arc<RwLock<Option<Arc<V>>>>,
Arc<RwLock<ProjectionArg<V, Vec<V::Msg>>>>
) where V::Msg: Send + Sync {
let (tx, mut rx) = channel::<Vec<V::Msg>>();
let view = Arc::new(RwLock::new(None));
let arg = Arc::new(RwLock::new(
ProjectionArg {
src: view.clone(),
sender: tx
}));
let proj = self.proj.clone();
async_std::task::spawn(async move {
while let Some(msg) = rx.next().await {
if let Some(proj) = proj.read().unwrap().upgrade() {
loop {
if let Ok(p) = proj.try_write().as_mut() {
notify(&mut *p, &msg);
break;
}
async_std::task::yield_now();
}
}
}
});
self.keepalive.push(arg.clone());
(view, arg)
}
}
/// Special Observer which can access the state of the projection on notify
/// also handles the reset() and default behaviour of unitinitalized inputs
pub struct ProjectionArg<V, D>
where V: View + ?Sized,
D: ChannelData<Item = V::Msg>,
D::IntoIter: Send + Sync
{
src: Arc<RwLock<Option<Arc<V>>>>,
sender: ChannelSender<D>
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Item, D> Observer<dyn SingletonView<Item = Item>> for ProjectionArg<dyn SingletonView<Item = Item>, D>
where D: ChannelData<Item = ()>,
D::IntoIter: Send + Sync
{
fn reset(&mut self, new_src: Option<Arc<dyn SingletonView<Item = Item>>>) {
*self.src.write().unwrap() = new_src;
self.notify(&());
}
fn notify(&self, msg: &()) {
self.sender.send(*msg);
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Item, D> Observer<dyn SequenceView<Item = Item>> for ProjectionArg<dyn SequenceView<Item = Item>, D>
where D: ChannelData<Item = usize>,
D::IntoIter: Send + Sync
{
fn reset(&mut self, new_src: Option<Arc<dyn SequenceView<Item = Item>>>) {
let old_len = self.src.len().unwrap_or(0);
*self.src.write().unwrap() = new_src;
let new_len = self.src.len().unwrap_or(0);
self.notify_each(0 .. max(old_len, new_len));
}
fn notify(&self, msg: &usize) {
self.sender.send(*msg);
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<Key: Clone, Item, D> Observer<dyn IndexView<Key, Item = Item>> for ProjectionArg<dyn IndexView<Key, Item = Item>, D>
where D: ChannelData<Item = Key>,
D::IntoIter: Send + Sync
{
fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
let old_area = self.src.area();
*self.src.write().unwrap() = new_src;
let new_area = self.src.area();
if let Some(area) = old_area { self.notify_each(area); }
if let Some(area) = new_area { self.notify_each(area); }
}
fn notify(&self, msg: &Key) {
self.sender.send(msg.clone());
}
}

View file

@ -0,0 +1,256 @@
use {
async_std::stream::StreamExt,
std::{
sync::{Arc, Weak},
collections::{HashMap, HashSet}
},
std::sync::RwLock,
crate::{
core::{
View, Observer, ObserverExt, ObserverBroadcast,
ViewPort, InnerViewPort, OuterViewPort,
channel::{ChannelSender, ChannelReceiver}
},
sequence::SequenceView
}
};
impl<V1, V2> OuterViewPort<V1>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
pub fn flatten(&self) -> OuterViewPort<dyn SequenceView<Item = V2::Item>> {
let port = ViewPort::new();
Flatten::new(self.clone(), port.inner());
port.into_outer()
}
}
pub struct Flatten<V1, V2>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
length: usize,
top: Arc<RwLock<TopObserver<V1, V2>>>,
chunks: HashMap<usize, Arc<RwLock<BotObserver<V2>>>>,
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = V2::Item>>>>
}
struct TopObserver<V1, V2>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
view: Option<Arc<V1>>,
sender: ChannelSender<HashSet<usize>>
}
struct BotObserver<V2>
where V2: SequenceView + ?Sized + 'static
{
offset: usize,
view: Option<Arc<V2>>,
sender: ChannelSender<HashSet<usize>>
}
impl<V1, V2> Observer<V1> for TopObserver<V1, V2>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
fn reset(&mut self, view: Option<Arc<V1>>) {
let old_len = self.view.len().unwrap_or(0);
self.view = view;
let new_len = self.view.len().unwrap_or(0);
self.notify_each(0 .. std::cmp::max(old_len, new_len));
}
fn notify(&self, chunk_idx: &usize) {
self.sender.send(*chunk_idx);
}
}
impl<V2> Observer<V2> for BotObserver<V2>
where V2: SequenceView + ?Sized + 'static
{
fn reset(&mut self, src: Option<Arc<V2>>) {
let old_len = self.view.len().unwrap_or(0);
self.view = src;
let new_len = self.view.len().unwrap_or(0);
self.notify_each(0 .. std::cmp::max(old_len, new_len));
}
fn notify(&self, idx: &usize) {
self.sender.send(*idx);
}
}
impl<V1, V2> View for Flatten<V1, V2>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
type Msg = usize;
}
impl<V1, V2> SequenceView for Flatten<V1, V2>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
type Item = V2::Item;
fn get(&self, idx: &usize) -> Option<Self::Item> {
let chunk = self.chunks[&self.get_chunk_idx(*idx)?].read().unwrap();
chunk.view.get(&(*idx - chunk.offset))
}
fn len(&self) -> Option<usize> {
Some(self.length)
}
}
impl<V1, V2> Flatten<V1, V2>
where V1: SequenceView<Item = OuterViewPort<V2>> + ?Sized + 'static,
V2: SequenceView + ?Sized + 'static
{
pub fn new(
top_port: OuterViewPort<V1>,
out_port: InnerViewPort<dyn SequenceView<Item = V2::Item>>
) -> Arc<RwLock<Self>> {
let (sender, mut recv) = crate::core::channel::set_channel();
let top_obs = Arc::new(RwLock::new(
TopObserver {
view: None,
sender
}
));
let flat = Arc::new(RwLock::new(Flatten::<V1, V2> {
length: 0,
top: top_obs.clone(),
chunks: HashMap::new(),
cast: out_port.get_broadcast()
}));
let f = flat.clone();
let cast = out_port.get_broadcast();
async_std::task::spawn(async move {
while let Some(chunk_idx) = recv.next().await {
if let Some(mut chunk_rcv) = f.write().unwrap().update_chunk(chunk_idx) {
let f = f.clone();
let cast = cast.clone();
async_std::task::spawn(async move {
while let Some(idx) = chunk_rcv.next().await {
let mut flat = f.write().unwrap();
let chunk = flat.chunks[&chunk_idx].read().unwrap();
let chunk_offset = chunk.offset;
let chunk_len = chunk.view.len().unwrap_or(0);
drop(chunk);
let mut dirty_idx = Vec::new();
if idx+1 >= chunk_len {
dirty_idx = flat.update_offsets(chunk_idx);
}
drop(flat);
cast.notify(&(idx + chunk_offset));
cast.notify_each(dirty_idx);
}
});
}
}
});
top_port.add_observer(top_obs);
out_port.set_view(Some(flat.clone()));
flat
}
/// the top-sequence has changed the item at chunk_idx,
/// create a new observer for the contained sub sequence
fn update_chunk(&mut self, chunk_idx: usize) -> Option<ChannelReceiver<HashSet<usize>>> {
if let Some(chunk_port) = self.top.read().unwrap().view.get(&chunk_idx) {
let (sender, recv) = crate::core::channel::set_channel();
let chunk_obs = Arc::new(RwLock::new(
BotObserver {
offset:
if chunk_idx > 0 {
if let Some(prev_chunk) = self.chunks.get(&(chunk_idx-1)) {
let prev_chunk = prev_chunk.read().unwrap();
prev_chunk.offset + prev_chunk.view.len().unwrap_or(0)
} else {
0
}
} else {
0
},
view: None,
sender
}
));
self.chunks.insert(chunk_idx, chunk_obs.clone());
chunk_port.add_observer(chunk_obs);
Some(recv)
} else {
self.chunks.remove(&chunk_idx);
None
}
}
/// recalculate all chunk offsets beginning at start_idx
/// and update length of flattened sequence
fn update_offsets(&mut self, start_idx: usize) -> Vec<usize> {
let top_len = self.top.read().unwrap().view.len().unwrap_or(0);
let first_chunk = self.chunks.get(&start_idx).unwrap().read().unwrap();
let start_offset = first_chunk.offset + first_chunk.view.len().unwrap_or(0);
let mut cur_offset = start_offset;
let mut dirty_idx = Vec::new();
for chunk_idx in start_idx+1 .. top_len {
if let Some(cur_chunk) = self.chunks.get(&chunk_idx) {
let mut cur_chunk = cur_chunk.write().unwrap();
let chunk_len = cur_chunk.view.len().unwrap_or(0);
let old_offset = cur_chunk.offset;
cur_chunk.offset = cur_offset;
if old_offset != cur_offset {
dirty_idx.extend(
std::cmp::min(old_offset, cur_offset)
.. std::cmp::max(old_offset, cur_offset) + chunk_len
);
}
cur_offset += chunk_len;
}
}
let old_length = self.length;
self.length = cur_offset;
dirty_idx.extend(self.length .. old_length);
dirty_idx
}
/// given an index in the flattened sequence,
/// which sub-sequence does it belong to?
fn get_chunk_idx(&self, glob_idx: usize) -> Option<usize> {
for chunk_idx in 0 .. self.top.read().unwrap().view.len().unwrap_or(0) {
if let Some(cur_chunk) = self.chunks.get(&chunk_idx) {
let cur_chunk = cur_chunk.read().unwrap();
if glob_idx < cur_chunk.offset + cur_chunk.view.len().unwrap_or(0) {
return Some(chunk_idx)
}
}
}
None
}
}

View file

@ -0,0 +1,69 @@
pub mod seq2idx;
pub mod vec_buffer;
pub mod flatten;
pub use {
seq2idx::{Sequence2Index},
vec_buffer::{VecBuffer, VecSequence}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
use crate::core::View;
pub trait SequenceView : View<Msg = usize> {
type Item;
fn get(&self, idx: &usize) -> Option<Self::Item>;
fn len(&self) -> Option<usize>;
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
use std::{
sync::Arc,
ops::{Deref}
};
use std::sync::RwLock;
impl<V: SequenceView + ?Sized> SequenceView for RwLock<V> {
type Item = V::Item;
fn get(&self, idx: &usize) -> Option<Self::Item> {
self.read().unwrap().get(idx)
}
fn len(&self) -> Option<usize> {
self.read().unwrap().len()
}
}
impl<V: SequenceView + ?Sized> SequenceView for Arc<V> {
type Item = V::Item;
fn get(&self, idx: &usize) -> Option<Self::Item> {
self.deref().get(idx)
}
fn len(&self) -> Option<usize> {
self.deref().len()
}
}
impl<V: SequenceView> SequenceView for Option<V> {
type Item = V::Item;
fn get(&self, idx: &usize) -> Option<Self::Item> {
(self.as_ref()? as &V).get(idx)
}
fn len(&self) -> Option<usize> {
if let Some(v) = self.as_ref() {
v.len()
} else {
Some(0)
}
}
}

View file

@ -0,0 +1,80 @@
use {
std::{
sync::Arc
},
std::sync::RwLock,
crate::{
core::{
View, Observer, ObserverExt, ObserverBroadcast,
ViewPort, InnerViewPort, OuterViewPort
},
sequence::SequenceView,
index::IndexView
}
};
/// Transforms a SequenceView into IndexView<usize>
pub struct Sequence2Index<SrcView>
where SrcView: SequenceView + ?Sized + 'static {
src_view: Option<Arc<SrcView>>,
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<usize, Item = SrcView::Item>>>>
}
impl<SrcView> Sequence2Index<SrcView>
where SrcView: SequenceView + ?Sized + 'static {
pub fn new(
port: InnerViewPort<dyn IndexView<usize, Item = SrcView::Item>>
) -> Arc<RwLock<Self>> {
let s2i = Arc::new(RwLock::new(
Sequence2Index {
src_view: None,
cast: port.get_broadcast()
}
));
port.set_view(Some(s2i.clone()));
s2i
}
}
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
pub fn to_index(&self) -> OuterViewPort<dyn IndexView<usize, Item = Item>> {
let port = ViewPort::new();
self.add_observer(Sequence2Index::new(port.inner()));
port.into_outer()
}
}
impl<SrcView> View for Sequence2Index<SrcView>
where SrcView: SequenceView + ?Sized + 'static {
type Msg = usize;
}
impl<SrcView> IndexView<usize> for Sequence2Index<SrcView>
where SrcView: SequenceView + ?Sized + 'static {
type Item = SrcView::Item;
fn get(&self, key: &usize) -> Option<Self::Item> {
self.src_view.get(key)
}
fn area(&self) -> Option<Vec<usize>> {
Some((0 .. self.src_view.len()?).collect())
}
}
impl<SrcView> Observer<SrcView> for Sequence2Index<SrcView>
where SrcView: SequenceView + ?Sized + 'static {
fn reset(&mut self, view: Option<Arc<SrcView>>) {
let old_area = self.area();
self.src_view = view;
let new_area = self.area();
if let Some(area) = old_area { self.cast.notify_each(area); }
if let Some(area) = new_area { self.cast.notify_each(area); }
}
fn notify(&self, msg: &usize) {
self.cast.notify(msg);
}
}

View file

@ -0,0 +1,240 @@
use {
std::{
sync::Arc,
ops::{Deref, DerefMut}
},
std::sync::RwLock,
crate::{
core::{View, Observer, ObserverExt, ObserverBroadcast, ViewPort, InnerViewPort, OuterViewPort},
sequence::SequenceView,
}
};
pub enum VecDiff<T> {
Push(T),
Remove(usize),
Insert{ idx: usize, val: T },
Update{ idx: usize, val: T }
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<T> View for Vec<T>
where T: Clone + Send + Sync + 'static {
type Msg = VecDiff<T>;
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct VecSequence<T>
where T: Clone + Send + Sync + 'static {
cur_len: RwLock<usize>,
data: Option<Arc<RwLock<Vec<T>>>>,
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>
}
impl<T> OuterViewPort<RwLock<Vec<T>>>
where T: Clone + Send + Sync + 'static {
pub fn to_sequence(&self) -> OuterViewPort<dyn SequenceView<Item = T>> {
let port = ViewPort::new();
let vec_seq = VecSequence::new(port.inner());
self.add_observer(vec_seq.clone());
port.into_outer()
}
}
impl<T> VecSequence<T>
where T: Clone + Send + Sync + 'static {
pub fn new(
port: InnerViewPort<dyn SequenceView<Item = T>>
) -> Arc<RwLock<Self>> {
let seq = Arc::new(RwLock::new(
VecSequence {
cur_len: RwLock::new(0),
data: None,
cast: port.get_broadcast()
}
));
port.set_view(Some(seq.clone()));
seq
}
}
impl<T> Observer<RwLock<Vec<T>>> for VecSequence<T>
where T: Clone + Send + Sync + 'static {
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
let old_len = self.len().unwrap();
self.data = view;
*self.cur_len.write().unwrap() =
if let Some(data) = self.data.as_ref() {
data.read().unwrap().len()
} else {
0
};
let new_len = self.len().unwrap();
self.cast.notify_each(0 .. std::cmp::max(old_len, new_len));
}
fn notify(&self, diff: &VecDiff<T>) {
match diff {
VecDiff::Push(_) => {
let l = {
let mut l = self.cur_len.write().unwrap();
*l += 1;
*l
};
self.cast.notify(&(l - 1));
},
VecDiff::Remove(idx) => {
let l = {
let mut l = self.cur_len.write().unwrap();
*l -= 1;
*l + 1
};
self.cast.notify_each(*idx .. l);
},
VecDiff::Insert{ idx, val: _ } => {
let l = {
let mut l = self.cur_len.write().unwrap();
*l += 1;
*l
};
self.cast.notify_each(*idx .. l);
},
VecDiff::Update{ idx, val: _ } => {
self.cast.notify(&idx);
}
}
}
}
impl<T> View for VecSequence<T>
where T: Clone + Send + Sync + 'static {
type Msg = usize;
}
impl<T> SequenceView for VecSequence<T>
where T: Clone + Send + Sync + 'static {
type Item = T;
fn get(&self, idx: &usize) -> Option<T> {
self.data.as_ref()?
.read().unwrap()
.get(*idx).cloned()
}
fn len(&self) -> Option<usize> {
Some(*self.cur_len.read().unwrap())
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
#[derive(Clone)]
pub struct VecBuffer<T>
where T: Clone + Send + Sync + 'static {
data: Arc<RwLock<Vec<T>>>,
cast: Arc<RwLock<ObserverBroadcast<RwLock<Vec<T>>>>>
}
impl<T> VecBuffer<T>
where T: Clone + Send + Sync + 'static {
pub fn with_data(
data: Vec<T>,
port: InnerViewPort<RwLock<Vec<T>>>
) -> Self {
let data = Arc::new(RwLock::new(data));
port.set_view(Some(data.clone()));
VecBuffer { data, cast: port.get_broadcast() }
}
pub fn new(port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
VecBuffer::with_data(Vec::new(), port)
}
pub fn apply_diff(&mut self, diff: VecDiff<T>) {
let mut data = self.data.write().unwrap();
match &diff {
VecDiff::Push(val) => { data.push(val.clone()); },
VecDiff::Remove(idx) => { data.remove(*idx); },
VecDiff::Insert{ idx, val } => { data.insert(*idx, val.clone()); },
VecDiff::Update{ idx, val } => { data[*idx] = val.clone(); }
}
drop(data);
self.cast.notify(&diff);
}
pub fn clear(&mut self) {
for _ in 0 .. self.len() {
self.remove(0);
}
}
pub fn len(&self) -> usize {
self.data.read().unwrap().len()
}
pub fn get(&self, idx: usize) -> T {
self.data.read().unwrap()[idx].clone()
}
pub fn push(&mut self, val: T) {
self.apply_diff(VecDiff::Push(val));
}
pub fn remove(&mut self, idx: usize) {
self.apply_diff(VecDiff::Remove(idx));
}
pub fn insert(&mut self, idx: usize, val: T) {
self.apply_diff(VecDiff::Insert{ idx, val });
}
pub fn update(&mut self, idx: usize, val: T) {
self.apply_diff(VecDiff::Update{ idx, val });
}
pub fn get_mut(&mut self, idx: usize) -> MutableVecAccess<T> {
MutableVecAccess {
buf: self.clone(),
idx,
val: self.get(idx)
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct MutableVecAccess<T>
where T: Clone + Send + Sync + 'static {
buf: VecBuffer<T>,
idx: usize,
val: T,
}
impl<T> Deref for MutableVecAccess<T>
where T: Clone + Send + Sync + 'static {
type Target = T;
fn deref(&self) -> &T {
&self.val
}
}
impl<T> DerefMut for MutableVecAccess<T>
where T: Clone + Send + Sync + 'static {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.val
}
}
impl<T> Drop for MutableVecAccess<T>
where T: Clone + Send + Sync + 'static {
fn drop(&mut self) {
self.buf.update(self.idx, self.val.clone());
}
}

View file

@ -0,0 +1,70 @@
use {
std::{
sync::{Arc}
},
std::sync::RwLock,
crate::{
core::{
Observer,
ObserverBroadcast,
View,
InnerViewPort
},
singleton::{SingletonView}
}
};
pub struct SingletonBufferView<T: Clone + Eq + Send + Sync + 'static>(Arc<RwLock<T>>);
impl<T> View for SingletonBufferView<T>
where T: Clone + Eq + Send + Sync + 'static {
type Msg = ();
}
impl<T> SingletonView for SingletonBufferView<T>
where T: Clone + Eq + Send + Sync + 'static {
type Item = T;
fn get(&self) -> Self::Item {
self.0.read().unwrap().clone()
}
}
#[derive(Clone)]
pub struct SingletonBuffer<T>
where T: Clone + Eq + Send + Sync + 'static {
value: Arc<RwLock<T>>,
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = T>>>>
}
impl<T> SingletonBuffer<T>
where T: Clone + Eq + Send + Sync + 'static {
pub fn new(
value: T,
port: InnerViewPort<dyn SingletonView<Item = T>>
) -> Self {
let value = Arc::new(RwLock::new(value));
port.set_view(Some(Arc::new(SingletonBufferView(value.clone()))));
SingletonBuffer {
value,
cast: port.get_broadcast()
}
}
pub fn get(&self) -> T {
self.value.read().unwrap().clone()
}
pub fn set(&mut self, new_value: T) {
let mut v = self.value.write().unwrap();
if *v != new_value {
*v = new_value;
drop(v);
self.cast.notify(&());
}
}
}
// TODO: impl Deref & DerefMut

View file

@ -0,0 +1,72 @@
pub mod buffer;
use {
std::{
sync::Arc,
ops::Deref
},
std::sync::RwLock,
crate::core::{View}
};
pub use buffer::SingletonBuffer;
// TODO: #[ImplForArc, ImplForRwLock]
pub trait SingletonView : View<Msg = ()> {
type Item;
fn get(&self) -> Self::Item;
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl<V: SingletonView + ?Sized> SingletonView for RwLock<V> {
type Item = V::Item;
fn get(&self) -> Self::Item {
self.read().unwrap().get()
}
}
impl<V: SingletonView + ?Sized> SingletonView for Arc<V> {
type Item = V::Item;
fn get(&self) -> Self::Item {
self.deref().get()
}
}
impl<V: SingletonView> SingletonView for Option<V>
where V::Item: Default{
type Item = V::Item;
fn get(&self) -> Self::Item {
if let Some(s) = self.as_ref() {
s.get()
} else {
V::Item::default()
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
/*
pub trait ImplSingletonView : Send + Sync {
type Item;
fn get(&self) -> Self::Item;
}
impl<V: ImplSingletonView> View for V {
type Msg = ();
}
impl<V: ImplSingletonView> SingletonView for V {
type Item = V::Item;
fn get(&self) -> Self::Item {
(self as &V).get()
}
}
*/

212
nested/src/string_editor.rs Normal file
View file

@ -0,0 +1,212 @@
use {
std::sync::RwLock,
crate::{
core::{ViewPort, OuterViewPort},
singleton::{SingletonView, SingletonBuffer},
sequence::VecBuffer,
terminal::{TerminalView}
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
#[derive(Clone)]
pub struct StringEditor {
data: VecBuffer<char>,
cursor: SingletonBuffer<usize>,
data_port: ViewPort<RwLock<Vec<char>>>,
cursor_port: ViewPort<dyn SingletonView<Item = usize>>
}
impl StringEditor {
pub fn new() -> Self {
let data_port = ViewPort::new();
let cursor_port = ViewPort::new();
StringEditor {
data: VecBuffer::new(data_port.inner()),
cursor: SingletonBuffer::new(0, cursor_port.inner()),
data_port,
cursor_port
}
}
pub fn insert_view(&self) -> OuterViewPort<dyn TerminalView> {
let port = ViewPort::new();
insert_view::StringInsertView::new(
self.get_cursor_port(),
self.get_data_port().to_sequence(),
port.inner()
);
port.into_outer()
}
pub fn get_data_port(&self) -> OuterViewPort<RwLock<Vec<char>>> {
self.data_port.outer()
}
pub fn get_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = usize>> {
self.cursor_port.outer()
}
pub fn goto(&mut self, new_pos: usize) {
if new_pos <= self.data.len() {
self.cursor.set(new_pos);
}
}
pub fn goto_end(&mut self) {
self.cursor.set(self.data.len());
}
pub fn prev(&mut self) {
let cur = self.cursor.get();
if cur > 0 {
self.cursor.set(cur - 1);
}
}
pub fn next(&mut self) {
self.goto(self.cursor.get() + 1);
}
pub fn insert(&mut self, c: char) {
self.data.insert(self.cursor.get(), c);
self.next();
}
pub fn delete_prev(&mut self) {
let cur = self.cursor.get();
if cur <= self.data.len() && cur > 0 {
self.data.remove(cur-1);
}
self.prev();
}
pub fn delete(&mut self) {
let cur = self.cursor.get();
if cur < self.data.len() {
self.data.remove(cur);
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub mod insert_view {
use {
std::{
sync::Arc,
cmp::{min, max},
any::Any,
collections::HashSet
},
cgmath::Point2,
std::sync::RwLock,
crate::{
core::{View, Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort},
terminal::{TerminalAtom, TerminalStyle, TerminalView},
grid::{GridWindowIterator},
singleton::{SingletonView},
sequence::{SequenceView},
index::{IndexView},
projection::{ProjectionHelper, ProjectionArg},
}
};
pub struct StringInsertView {
cursor: Arc<dyn SingletonView<Item = usize>>,
data: Arc<RwLock<dyn SequenceView<Item = char>>>,
cur_pos: usize,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
proj_helper: ProjectionHelper<Self>
}
impl View for StringInsertView {
type Msg = Point2<i16>;
}
impl IndexView<Point2<i16>> for StringInsertView {
type Item = TerminalAtom;
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
if pos.y == 0 && pos.x >= 0 {
let i = pos.x as usize;
let data = self.data.read().unwrap();
let len = data.len().unwrap_or(0);
if i < len+1 {
return Some(
if i < self.cur_pos {
TerminalAtom::from(data.get(&i)?)
} else if i == self.cur_pos {
TerminalAtom::new('|', TerminalStyle::fg_color((200, 0, 0)))
} else {
TerminalAtom::from(data.get(&(i - 1))?)
}
);
}
}
None
}
fn area(&self) -> Option<Vec<Point2<i16>>> {
Some(GridWindowIterator::from(
Point2::new(0, 0) .. Point2::new(self.data.len()? as i16 + 1, 1)
).collect())
}
}
impl StringInsertView {
pub fn new(
cursor_port: OuterViewPort<dyn SingletonView<Item = usize>>,
data_port: OuterViewPort<dyn SequenceView<Item = char>>,
out_port: InnerViewPort<dyn TerminalView>
) -> Arc<RwLock<Self>> {
let mut proj_helper = ProjectionHelper::new();
let proj = Arc::new(RwLock::new(
StringInsertView {
cursor: proj_helper.new_singleton_arg(
cursor_port,
|s: &mut Self, _msg| {
let old_pos = s.cur_pos;
let new_pos = s.cursor.get();
s.cur_pos = new_pos;
s.cast.notify_each(GridWindowIterator::from(Point2::new(min(old_pos, new_pos) as i16,0) ..= Point2::new(max(old_pos, new_pos) as i16, 0)))
}),
data: proj_helper.new_sequence_arg(
data_port,
|s: &mut Self, idx| {
s.cast.notify(&Point2::new(
if *idx < s.cur_pos {
*idx as i16
} else {
*idx as i16 + 1
},
0
));
}),
cur_pos: 0,
cast: out_port.get_broadcast(),
proj_helper
}
));
proj.write().unwrap().proj_helper.set_proj(&proj);
out_port.set_view(Some(proj.clone()));
proj
}
}
}

View file

@ -0,0 +1,56 @@
use super::TerminalStyle;
#[derive(Clone, Copy)]
pub struct TerminalAtom {
pub c: Option<char>,
pub style: TerminalStyle
}
impl TerminalAtom {
pub fn new(c: char, style: TerminalStyle) -> Self {
TerminalAtom { c: Some(c), style }
}
pub fn new_bg(bg_color: (u8, u8, u8)) -> Self {
TerminalAtom { c: None, style: TerminalStyle::bg_color(bg_color) }
}
pub fn add_style_front(mut self, style: TerminalStyle) -> Self {
self.style = self.style.add(style);
self
}
pub fn add_style_back(mut self, style: TerminalStyle) -> Self {
self.style = style.add(self.style);
self
}
}
impl From<char> for TerminalAtom {
fn from(c: char) -> Self {
TerminalAtom {
c: Some(c),
style: TerminalStyle::default()
}
}
}
impl From<Option<char>> for TerminalAtom {
fn from(c: Option<char>) -> Self {
TerminalAtom {
c,
style: TerminalStyle::default()
}
}
}
impl From<&char> for TerminalAtom {
fn from(c: &char) -> Self {
TerminalAtom {
c: Some(*c),
style: TerminalStyle::default()
}
}
}

View file

@ -0,0 +1,96 @@
use {
std::{
sync::{Arc, Weak},
collections::HashMap
},
std::sync::RwLock,
cgmath::Point2,
crate::{
core::{InnerViewPort, OuterViewPort, Observer, ObserverExt, ObserverBroadcast},
index::{ImplIndexView},
terminal::{TerminalAtom, TerminalView},
projection::ProjectionHelper
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct TerminalCompositor {
layers: Vec<Arc<dyn TerminalView>>,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
proj_helper: ProjectionHelper<Self>
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl TerminalCompositor {
pub fn new(
port: InnerViewPort<dyn TerminalView>
) -> Arc<RwLock<Self>> {
let comp = Arc::new(RwLock::new(
TerminalCompositor {
layers: Vec::new(),
cast: port.get_broadcast(),
proj_helper: ProjectionHelper::new()
}
));
comp.write().unwrap().proj_helper.set_proj(&comp);
port.set_view(Some(comp.clone()));
comp
}
pub fn push(&mut self, v: OuterViewPort<dyn TerminalView>) {
self.layers.push(
self.proj_helper.new_index_arg(
v,
|s: &mut Self, pos| {
s.cast.notify(pos);
}
)
);
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl ImplIndexView for TerminalCompositor {
type Key = Point2<i16>;
type Value = TerminalAtom;
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
let mut atom = None;
for layer in self.layers.iter() {
match (atom, layer.get(pos)) {
(None, next) => atom = next,
(Some(last), Some(next)) => atom = Some(next.add_style_back(last.style)),
_ => {}
}
}
atom
}
fn area(&self) -> Option<Vec<Point2<i16>>> {
let mut area = Some(Vec::new());
for layer in self.layers.iter() {
if let (
Some(mut new_area),
Some(area)
) = (
layer.area(),
area.as_mut()
) {
area.append(&mut new_area);
} else {
area = None;
}
}
area
}
}

View file

@ -0,0 +1,24 @@
pub mod style;
pub mod atom;
pub mod terminal;
pub mod compositor;
pub use {
style::{TerminalStyle},
atom::{TerminalAtom},
terminal::{Terminal, TerminalEvent},
compositor::TerminalCompositor,
};
use {
crate::{
grid::GridView
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub trait TerminalView = GridView<Item = TerminalAtom>;
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

View file

@ -0,0 +1,87 @@
#[derive(Default, Copy, Clone, PartialEq)]
pub struct TerminalStyle {
pub fg_color: Option<(u8, u8, u8)>,
pub bg_color: Option<(u8, u8, u8)>,
pub bold: Option<bool>,
pub italic: Option<bool>,
pub underline: Option<bool>
}
impl TerminalStyle {
pub fn add(&self, mut dominant: TerminalStyle) -> Self {
if dominant.fg_color == None {
dominant.fg_color = self.fg_color;
}
if dominant.bg_color == None {
dominant.bg_color = self.bg_color;
}
if dominant.bold == None {
dominant.bold = self.bold;
}
if dominant.italic == None {
dominant.italic = self.italic;
}
if dominant.underline == None {
dominant.underline = self.underline;
}
dominant
}
pub fn fg_color(rgb: (u8, u8, u8)) -> Self {
let mut style = TerminalStyle::default();
style.fg_color = Some(rgb);
style
}
pub fn bg_color(rgb: (u8, u8, u8)) -> Self {
let mut style = TerminalStyle::default();
style.bg_color = Some(rgb);
style
}
pub fn bold(b: bool) -> Self {
let mut style = TerminalStyle::default();
style.bold = Some(b);
style
}
pub fn italic(i: bool) -> Self {
let mut style = TerminalStyle::default();
style.italic = Some(i);
style
}
pub fn underline(u: bool) -> Self {
let mut style = TerminalStyle::default();
style.underline = Some(u);
style
}
}
impl std::fmt::Display for TerminalStyle {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.fg_color {
Some((r, g, b)) => write!(fmt, "{}", termion::color::Fg(termion::color::Rgb(r, g, b)))?,
None => write!(fmt, "{}", termion::color::Fg(termion::color::Reset))?,
};
match self.bg_color {
Some((r, g, b)) => write!(fmt, "{}", termion::color::Bg(termion::color::Rgb(r, g, b)))?,
None => write!(fmt, "{}", termion::color::Bg(termion::color::Reset))?,
};
match self.bold {
Some(true) => write!(fmt, "{}", termion::style::Bold)?,
_ => write!(fmt, "{}", termion::style::NoBold)?,
};
match self.italic {
Some(true) => write!(fmt, "{}", termion::style::Italic)?,
_ => write!(fmt, "{}", termion::style::NoItalic)?,
};
match self.underline {
Some(true) => write!(fmt, "{}", termion::style::Underline)?,
_ => write!(fmt, "{}", termion::style::NoUnderline)?,
};
Ok(())
}
}

View file

@ -0,0 +1,203 @@
use {
std::{
sync::Arc,
io::{Write, stdout, stdin},
collections::HashSet
},
std::sync::RwLock,
async_std::{
stream::StreamExt,
task
},
signal_hook,
signal_hook_async_std::Signals,
cgmath::{Vector2, Point2},
termion::{
raw::IntoRawMode,
input::{TermRead, MouseTerminal}
},
crate::{
core::{
OuterViewPort,
Observer,
channel::{
ChannelReceiver,
ChannelSender,
queue_channel,
set_channel
}
},
grid::{GridWindowIterator}
},
super::{
TerminalStyle,
TerminalView
},
};
pub enum TerminalEvent {
Resize(Vector2<i16>),
Input(termion::event::Event)
}
pub struct Terminal {
writer: Arc<TermOutWriter>,
_observer: Arc<RwLock<TermOutObserver>>,
events: ChannelReceiver<Vec<TerminalEvent>>,
_signal_handle: signal_hook_async_std::Handle
}
impl Terminal {
pub fn new(
port: OuterViewPort<dyn TerminalView>
) -> Self {
let (dirty_pos_tx, dirty_pos_rx) = set_channel();
let writer = Arc::new(TermOutWriter {
out: RwLock::new(MouseTerminal::from(stdout().into_raw_mode().unwrap())),
dirty_pos_rx,
view: port.get_view_arc()
});
let observer = Arc::new(RwLock::new(TermOutObserver {
dirty_pos_tx,
writer: writer.clone()
}));
port.add_observer(observer.clone());
let (event_tx, event_rx) = queue_channel();
let input_tx = event_tx.clone();
std::thread::spawn(move || {
for event in stdin().events() {
input_tx.send(TerminalEvent::Input(event.unwrap()));
}
});
// send initial teriminal size
let (w, h) = termion::terminal_size().unwrap();
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
// and again on SIGWINCH
let signals = Signals::new(&[ signal_hook::consts::signal::SIGWINCH ]).unwrap();
let handle = signals.handle();
task::spawn(async move {
let mut signals = signals.fuse();
while let Some(signal) = signals.next().await {
match signal {
signal_hook::consts::signal::SIGWINCH => {
let (w,h) = termion::terminal_size().unwrap();
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
},
_ => unreachable!(),
}
}
});
Terminal {
writer,
_observer: observer,
events: event_rx,
_signal_handle: handle
}
}
pub fn get_writer(&self) -> Arc<TermOutWriter> {
self.writer.clone()
}
pub async fn next_event(&mut self) -> TerminalEvent {
self.events.next().await.unwrap()
}
}
struct TermOutObserver {
dirty_pos_tx: ChannelSender<HashSet<Point2<i16>>>,
writer: Arc<TermOutWriter>
}
impl Observer<dyn TerminalView> for TermOutObserver {
fn reset(&mut self, view: Option<Arc<dyn TerminalView>>) {
self.writer.reset();
let (w, h) = termion::terminal_size().unwrap();
if let Some(view) = view {
for pos in view.area().unwrap_or(
GridWindowIterator::from(
Point2::new(0, 0) .. Point2::new(w as i16, h as i16)).collect()
) {
self.dirty_pos_tx.send(pos);
}
}
}
fn notify(&self, pos: &Point2<i16>) {
self.dirty_pos_tx.send(*pos);
}
}
pub struct TermOutWriter {
out: RwLock<MouseTerminal<termion::raw::RawTerminal<std::io::Stdout>>>,
dirty_pos_rx: ChannelReceiver<HashSet<Point2<i16>>>,
view: Arc<RwLock<Option<Arc<dyn TerminalView>>>>
}
impl TermOutWriter {
fn reset(&self) {
let mut out = self.out.write().unwrap();
write!(out, "{}", termion::clear::All).ok();
}
}
impl TermOutWriter {
pub async fn show(&self) -> std::io::Result<()> {
// init
write!(self.out.write().unwrap(), "{}{}{}",
termion::cursor::Hide,
termion::cursor::Goto(1, 1),
termion::style::Reset)?;
let mut cur_pos = Point2::<i16>::new(0, 0);
let mut cur_style = TerminalStyle::default();
// draw atoms until view port is destroyed
while let Some(dirty_pos) = self.dirty_pos_rx.recv().await {
if let Some(view) = self.view.read().unwrap().as_ref() {
let mut out = self.out.write().unwrap();
for pos in dirty_pos.into_iter() {
if pos != cur_pos {
write!(out, "{}", termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1))?;
}
if let Some(atom) = view.get(&pos) {
if cur_style != atom.style {
cur_style = atom.style;
write!(out, "{}", atom.style)?;
}
write!(out, "{}", atom.c.unwrap_or(' '))?;
} else {
write!(out, "{} ", termion::style::Reset)?;
cur_style = TerminalStyle::default();
}
cur_pos = pos + Vector2::new(1, 0);
}
out.flush()?;
}
}
// restore conventional terminal settings
let mut out = self.out.write().unwrap();
write!(out, "{}", termion::cursor::Show)?;
out.flush()?;
std::io::Result::Ok(())
}
}