move nested into a lib crate
This commit is contained in:
parent
c63fd6d1f2
commit
e21b888b6c
30 changed files with 30 additions and 377 deletions
nested
Cargo.toml
src
17
nested/Cargo.toml
Normal file
17
nested/Cargo.toml
Normal 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
239
nested/src/cell_layout.rs
Normal 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
212
nested/src/core/channel.rs
Normal 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
30
nested/src/core/mod.rs
Normal 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
147
nested/src/core/observer.rs
Normal 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
160
nested/src/core/port.rs
Normal 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
28
nested/src/core/view.rs
Normal 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
81
nested/src/grid/mod.rs
Normal 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
84
nested/src/grid/offset.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
107
nested/src/index/map_item.rs
Normal file
107
nested/src/index/map_item.rs
Normal 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
118
nested/src/index/map_key.rs
Normal 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
96
nested/src/index/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
90
nested/src/leveled_term_view.rs
Normal file
90
nested/src/leveled_term_view.rs
Normal 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
5
nested/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
pub fn magic_header() {
|
||||
eprintln!("<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>");
|
||||
}
|
||||
|
176
nested/src/projection.rs
Normal file
176
nested/src/projection.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
|
256
nested/src/sequence/flatten.rs
Normal file
256
nested/src/sequence/flatten.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
69
nested/src/sequence/mod.rs
Normal file
69
nested/src/sequence/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
80
nested/src/sequence/seq2idx.rs
Normal file
80
nested/src/sequence/seq2idx.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
240
nested/src/sequence/vec_buffer.rs
Normal file
240
nested/src/sequence/vec_buffer.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
|
70
nested/src/singleton/buffer.rs
Normal file
70
nested/src/singleton/buffer.rs
Normal 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
|
||||
|
72
nested/src/singleton/mod.rs
Normal file
72
nested/src/singleton/mod.rs
Normal 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
212
nested/src/string_editor.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
56
nested/src/terminal/atom.rs
Normal file
56
nested/src/terminal/atom.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
96
nested/src/terminal/compositor.rs
Normal file
96
nested/src/terminal/compositor.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
24
nested/src/terminal/mod.rs
Normal file
24
nested/src/terminal/mod.rs
Normal 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>;
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
87
nested/src/terminal/style.rs
Normal file
87
nested/src/terminal/style.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
203
nested/src/terminal/terminal.rs
Normal file
203
nested/src/terminal/terminal.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue