refactoring
This commit is contained in:
parent
cdd06eb9b8
commit
86562614cc
18 changed files with 1071 additions and 558 deletions
|
@ -6,10 +6,10 @@ name = "NeStEd"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cgmath = "*"
|
cgmath = "0.17.0"
|
||||||
termion = "*"
|
termion = "1.5.5"
|
||||||
signal-hook = "*"
|
signal-hook = "0.3.1"
|
||||||
signal-hook-async-std = "*"
|
signal-hook-async-std = "0.2.0"
|
||||||
|
|
||||||
[dependencies.async-std]
|
[dependencies.async-std]
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
|
|
@ -13,7 +13,7 @@ use {
|
||||||
},
|
},
|
||||||
|
|
||||||
crate::{
|
crate::{
|
||||||
view::{Observer}
|
core::{Observer}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,11 +83,9 @@ pub struct ChannelReceiver<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
impl<Data: ChannelData> Observer for ChannelSender<Data>
|
impl<Data: ChannelData> ChannelSender<Data>
|
||||||
where Data::IntoIter: Send + Sync {
|
where Data::IntoIter: Send + Sync {
|
||||||
type Msg = Data::Item;
|
pub fn send(&self, msg: Data::Item) {
|
||||||
|
|
||||||
fn notify(&self, msg: Data::Item) {
|
|
||||||
let mut state = self.0.lock().unwrap();
|
let mut state = self.0.lock().unwrap();
|
||||||
|
|
||||||
if state.send_buf.is_none() {
|
if state.send_buf.is_none() {
|
||||||
|
@ -102,6 +100,14 @@ where Data::IntoIter: Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
impl<Data: ChannelData> Clone for ChannelSender<Data> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
self.0.lock().unwrap().num_senders += 1;
|
self.0.lock().unwrap().num_senders += 1;
|
30
src/core/mod.rs
Normal file
30
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
149
src/core/observer.rs
Normal file
149
src/core/observer.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
use {
|
||||||
|
crate::core::View,
|
||||||
|
std::{
|
||||||
|
ops::Deref,
|
||||||
|
sync::{Arc, Weak, RwLock}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Observer
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub trait Observer<V: View + ?Sized> : Send + Sync {
|
||||||
|
fn reset(&self, view: Option<Arc<V>>) {}
|
||||||
|
fn notify(&self, msg: &V::Msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized, O: Observer<V>> Observer<V> for RwLock<O> {
|
||||||
|
fn notify(&self, msg: &V::Msg) {
|
||||||
|
self.read().unwrap().notify(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized, O: Observer<V>> Observer<V> for Arc<O> {
|
||||||
|
fn notify(&self, msg: &V::Msg) {
|
||||||
|
self.deref().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<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<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<dyn Observer<V>>> + '_ {
|
||||||
|
self.observers.iter().filter_map(|o| o.upgrade())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Observer<V> for ObserverBroadcast<V> {
|
||||||
|
fn reset(&self, view: Option<Arc<V>>) {
|
||||||
|
for o in self.iter() {
|
||||||
|
o.reset(view.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&self, msg: &V::Msg) {
|
||||||
|
for o in self.iter() {
|
||||||
|
o.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(&self, view: Option<Arc<V>>) {
|
||||||
|
(self.f)(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
144
src/core/port.rs
Normal file
144
src/core/port.rs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
use {
|
||||||
|
std::sync::{Arc, RwLock},
|
||||||
|
crate::core::{
|
||||||
|
View,
|
||||||
|
Observer,
|
||||||
|
ObserverBroadcast,
|
||||||
|
NotifyFnObserver,
|
||||||
|
ResetFnObserver,
|
||||||
|
channel::{
|
||||||
|
ChannelData,
|
||||||
|
ChannelSender,
|
||||||
|
ChannelReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
View Port
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
#[derive(Clone)]
|
||||||
|
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.read().unwrap().reset(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(&self, observer: Arc<dyn Observer<V>>) {
|
||||||
|
self.observers.write().unwrap().add_observer(Arc::downgrade(&observer));
|
||||||
|
observer.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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InnerViewPort<V: View + ?Sized>(ViewPort<V>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
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 + '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<dyn Observer<V>>) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.0.add_observer(observer);
|
||||||
|
self.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn add_reset_fn(&self, reset: impl Fn(Option<Arc<V>>) + Send + Sync + 'static) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.add_observer(Arc::new(ResetFnObserver::new(reset)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_notify_fn(&self, notify: impl Fn(&V::Msg) + Send + Sync + 'static) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.add_observer(Arc::new(NotifyFnObserver::new(notify)))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
24
src/core/view.rs
Normal file
24
src/core/view.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
View
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub trait View : Send + Sync {
|
||||||
|
/// Notification message for the observers
|
||||||
|
type Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
impl<V: View> View for RwLock<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View> View for Arc<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
125
src/main.rs
125
src/main.rs
|
@ -1,51 +1,104 @@
|
||||||
|
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
#![feature(assoc_char_funcs)]
|
#![feature(assoc_char_funcs)]
|
||||||
|
|
||||||
|
pub mod core;
|
||||||
pub mod view;
|
pub mod view;
|
||||||
pub mod port;
|
|
||||||
pub mod channel;
|
|
||||||
pub mod singleton_buffer;
|
|
||||||
pub mod vec_buffer;
|
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
|
|
||||||
pub mod string_editor;
|
pub mod string_editor;
|
||||||
|
|
||||||
|
//pub mod singleton_buffer;
|
||||||
|
//pub mod vec_buffer;
|
||||||
|
//pub mod sequence_element_projection;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
async_std::{task},
|
async_std::{task},
|
||||||
std::{
|
std::{
|
||||||
sync::{Arc, RwLock}
|
sync::{Arc, RwLock},
|
||||||
|
ops::Range
|
||||||
},
|
},
|
||||||
cgmath::{Vector2},
|
cgmath::{Vector2, Point2},
|
||||||
|
termion::event::{Event, Key},
|
||||||
crate::{
|
crate::{
|
||||||
view::{View, Observer},
|
core::{View, Observer, ObserverExt, ViewPort},
|
||||||
port::{ViewPort, InnerViewPort, OuterViewPort},
|
view::{*},
|
||||||
singleton_buffer::SingletonBuffer,
|
|
||||||
vec_buffer::VecBuffer,
|
|
||||||
terminal::{
|
terminal::{
|
||||||
Terminal,
|
TerminalView,
|
||||||
TerminalAtom,
|
TerminalAtom,
|
||||||
TerminalStyle,
|
TerminalStyle,
|
||||||
TerminalCompositor,
|
TerminalEvent,
|
||||||
TerminalEvent
|
Terminal,
|
||||||
}
|
TerminalCompositor
|
||||||
},
|
},
|
||||||
termion::event::{Event, Key}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Fill(TerminalAtom);
|
struct VecSequenceView<T: Send + Sync + Clone>(Arc<RwLock<Vec<T>>>);
|
||||||
impl View for Fill {
|
impl<T: Send + Sync + Clone> ImplIndexView for VecSequenceView<T> {
|
||||||
type Key = Vector2<i16>;
|
type Key = usize;
|
||||||
type Value = TerminalAtom;
|
type Value = T;
|
||||||
|
|
||||||
fn view(&self, _: Vector2<i16>) -> Option<TerminalAtom> {
|
fn get(&self, idx: &usize) -> T {
|
||||||
Some(self.0.clone())
|
self.0.read().unwrap()[*idx].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<usize>> {
|
||||||
|
Some(0 .. self.0.read().unwrap().len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Checkerboard;
|
||||||
|
impl ImplIndexView for Checkerboard {
|
||||||
|
type Key = Point2<i16>;
|
||||||
|
type Value = Option<TerminalAtom>;
|
||||||
|
|
||||||
|
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||||
|
if pos.x == 0 || pos.x == 1 || pos.x > 17 || pos.y == 0 || pos.y > 8 {
|
||||||
|
// border
|
||||||
|
Some(TerminalAtom::new_bg((20, 10, 10)))
|
||||||
|
} else {
|
||||||
|
// field
|
||||||
|
if ((pos.x/2) % 2 == 0) ^ ( pos.y % 2 == 0 ) {
|
||||||
|
Some(TerminalAtom::new_bg((0, 0, 0)))
|
||||||
|
} else {
|
||||||
|
Some(TerminalAtom::new_bg((200, 200, 200)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||||
|
Some(Point2::new(0,0) .. Point2::new(20,10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScrambleBackground;
|
||||||
|
impl ImplIndexView for ScrambleBackground {
|
||||||
|
type Key = Point2<i16>;
|
||||||
|
type Value = Option<TerminalAtom>;
|
||||||
|
|
||||||
|
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||||
|
if ((pos.x/2) % 2 == 0) ^ ( pos.y % 2 == 0 ) {
|
||||||
|
Some(TerminalAtom::new(char::from((35+(5*pos.y+pos.x)%40) as u8), TerminalStyle::fg_color((40, 40, 40))))
|
||||||
|
} else {
|
||||||
|
Some(TerminalAtom::new(char::from((35+(pos.y+9*pos.x)%40) as u8), TerminalStyle::fg_color((90, 90, 90))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||||
|
None
|
||||||
|
//Some(Point2::new(0,0) .. Point2::new(50,30))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let composite_view = port::ViewPort::new();
|
let term_port = ViewPort::<dyn TerminalView>::new();
|
||||||
let mut compositor = TerminalCompositor::new(composite_view.inner());
|
|
||||||
|
let mut compositor = TerminalCompositor::new(term_port.inner());
|
||||||
|
compositor.push(ViewPort::<dyn TerminalView>::with_view(Arc::new(ScrambleBackground)).into_outer());
|
||||||
|
|
||||||
|
let mut term = Terminal::new(term_port.outer());
|
||||||
|
let term_writer = term.get_writer();
|
||||||
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
/*\
|
/*\
|
||||||
|
@ -53,36 +106,30 @@ async fn main() {
|
||||||
Setup Views
|
Setup Views
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
\*/
|
\*/
|
||||||
let fp = port::ViewPort::with_view(Arc::new(Fill(TerminalAtom::new('.', TerminalStyle::fg_color((50,50,50))))));
|
|
||||||
compositor.push(fp.outer());
|
|
||||||
|
|
||||||
let ep = port::ViewPort::new();
|
compositor.push(ViewPort::<dyn TerminalView>::with_view(Arc::new(Checkerboard)).into_outer());
|
||||||
let mut editor = string_editor::StringEditor::new(ep.inner());
|
let edit_port = ViewPort::<dyn TerminalView>::new();
|
||||||
compositor.push(ep.outer());
|
let mut editor = string_editor::StringEditor::new(edit_port.inner());
|
||||||
|
compositor.push(edit_port.into_outer());
|
||||||
|
|
||||||
/*\
|
/*\
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
Event Loop
|
Event Loop
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
\*/
|
\*/
|
||||||
let mut term = Terminal::new();
|
|
||||||
loop {
|
loop {
|
||||||
match term.next_event().await {
|
match term.next_event().await {
|
||||||
TerminalEvent::Resize(size) => {
|
|
||||||
for x in 0 .. size.x {
|
|
||||||
for y in 0 .. size.y {
|
|
||||||
fp.inner().notify(Vector2::new(x,y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(),
|
TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(),
|
||||||
TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(),
|
TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(),
|
||||||
TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
|
TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
|
||||||
TerminalEvent::Input(Event::Key(Key::End)) => editor.goto_end(),
|
TerminalEvent::Input(Event::Key(Key::End)) => editor.goto_end(),
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {},
|
||||||
TerminalEvent::Input(Event::Key(Key::Char(c))) => editor.insert(c),
|
TerminalEvent::Input(Event::Key(Key::Char(c))) => editor.insert(c),
|
||||||
TerminalEvent::Input(Event::Key(Key::Delete)) => editor.delete(),
|
TerminalEvent::Input(Event::Key(Key::Delete)) => editor.delete(),
|
||||||
TerminalEvent::Input(Event::Key(Key::Backspace)) => { editor.prev(); editor.delete(); },
|
TerminalEvent::Input(Event::Key(Key::Backspace)) => { editor.prev(); editor.delete(); },
|
||||||
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => break,
|
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => {
|
||||||
|
break
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +140,6 @@ async fn main() {
|
||||||
Terminal Rendering
|
Terminal Rendering
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
\*/
|
\*/
|
||||||
Terminal::show(composite_view.into_outer()).await.ok();
|
term_writer.show().await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
186
src/port.rs
186
src/port.rs
|
@ -1,186 +0,0 @@
|
||||||
use {
|
|
||||||
std::{
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
collections::HashSet,
|
|
||||||
hash::Hash,
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
view::{View, Observer, FnView, FnObserver},
|
|
||||||
channel::{ChannelReceiver}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*\
|
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
View Port
|
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
\*/
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ViewPort<K: Send + Sync + 'static, V: Send + Sync + 'static> {
|
|
||||||
view: Arc<RwLock<Option<Arc<dyn View<Key = K, Value = V>>>>>,
|
|
||||||
observers: Arc<RwLock<Vec<Arc<dyn Observer<Msg = K>>>>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> ViewPort<K, V>
|
|
||||||
where K: Send + Sync + 'static,
|
|
||||||
V: Send + Sync + 'static {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
ViewPort {
|
|
||||||
view: Arc::new(RwLock::new(None)),
|
|
||||||
observers: Arc::new(RwLock::new(Vec::new()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_view(view: Arc<dyn View<Key = K, Value = V>>) -> Self {
|
|
||||||
ViewPort {
|
|
||||||
view: Arc::new(RwLock::new(Some(view))),
|
|
||||||
observers: Arc::new(RwLock::new(Vec::new()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_view(&self, view: Arc<dyn View<Key = K, Value = V>>) {
|
|
||||||
*self.view.write().unwrap() = Some(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_observer(&self, observer: Arc<dyn Observer<Msg = K>>) {
|
|
||||||
self.observers.write().unwrap().push(observer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inner(&self) -> InnerViewPort<K, V> {
|
|
||||||
InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn outer(&self) -> OuterViewPort<K, V> {
|
|
||||||
OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_inner(self) -> InnerViewPort<K, V> {
|
|
||||||
InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_outer(self) -> OuterViewPort<K, V> {
|
|
||||||
OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct InnerViewPort<K: Send + Sync + 'static, V: Send + Sync + 'static>(ViewPort<K, V>);
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct OuterViewPort<K: Send + Sync + 'static, V: Send + Sync + 'static>(ViewPort<K, V>);
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
impl<K: Send + Sync + 'static, V: Send + Sync + 'static> OuterViewPort<K, V> {
|
|
||||||
pub fn get_view(&self) -> Arc<RwLock<Option<Arc<dyn View<Key = K, Value = V>>>>> {
|
|
||||||
self.0.view.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_observer(self, observer: Arc<dyn Observer<Msg = K>>) -> Arc<RwLock<Option<Arc<dyn View<Key = K, Value = V>>>>> {
|
|
||||||
self.0.add_observer(observer);
|
|
||||||
self.0.view
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_observer_fn(self, obs_fn: impl Fn(K) + Send + Sync + 'static) -> Arc<RwLock<Option<Arc<dyn View<Key = K, Value = V>>>>> {
|
|
||||||
self.add_observer(Arc::new(FnObserver::new(obs_fn)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Eq + Hash + Send + Sync + 'static, V: Send + Sync + 'static> OuterViewPort<K, V> {
|
|
||||||
pub fn stream(self) -> ChannelReceiver<HashSet<K>> {
|
|
||||||
let (s, r) = crate::channel::set_channel();
|
|
||||||
self.0.add_observer(Arc::new(s));
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Clone + Eq + Hash + Send + Sync + 'static, V: Send + Sync + 'static> OuterViewPort<K, V> {
|
|
||||||
pub fn map_value<
|
|
||||||
V2: Clone + Send + Sync + 'static,
|
|
||||||
F: Fn(Option<V>) -> Option<V2> + Send + Sync + 'static
|
|
||||||
>(
|
|
||||||
self,
|
|
||||||
f: F
|
|
||||||
) -> OuterViewPort<K, V2> {
|
|
||||||
let port = ViewPort::new();
|
|
||||||
let view = self.add_observer_fn({
|
|
||||||
let dst = port.inner();
|
|
||||||
move |key| dst.notify(key)
|
|
||||||
});
|
|
||||||
port.inner().set_view_fn(move |key| f(view.view(key)));
|
|
||||||
port.outer()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_key<
|
|
||||||
K2: Clone + Send + Sync + 'static,
|
|
||||||
F1: Fn(K) -> K2 + Send + Sync + 'static,
|
|
||||||
F2: Fn(K2) -> K + Send + Sync + 'static
|
|
||||||
>(
|
|
||||||
self,
|
|
||||||
f1: F1,
|
|
||||||
f2: F2
|
|
||||||
) -> OuterViewPort<K2, V> {
|
|
||||||
let port = ViewPort::new();
|
|
||||||
let view = self.add_observer_fn({
|
|
||||||
let dst = port.inner();
|
|
||||||
move |key| dst.notify(f1(key))
|
|
||||||
});
|
|
||||||
port.inner().set_view_fn(move |key| view.view(f2(key)));
|
|
||||||
port.outer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
impl<K: Send + Sync + 'static, V: Send + Sync + 'static> InnerViewPort<K, V> {
|
|
||||||
pub fn set_view(&self, view: Arc<dyn View<Key = K, Value = V> + Send + Sync>) {
|
|
||||||
*self.0.view.write().unwrap() = Some(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_view_fn(&self, view_fn: impl Fn(K) -> Option<V> + Send + Sync + 'static) {
|
|
||||||
self.set_view(Arc::new(FnView::new(view_fn)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> Observer for InnerViewPort<K, V>
|
|
||||||
where K: Clone + Send + Sync + 'static,
|
|
||||||
V: Send + Sync + 'static {
|
|
||||||
type Msg = K;
|
|
||||||
|
|
||||||
fn notify(&self, msg: K) {
|
|
||||||
for observer in self.0.observers.read().unwrap().iter() {
|
|
||||||
observer.notify(msg.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*\
|
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
Stream Port
|
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
\*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub struct StreamPort<T> {
|
|
||||||
actions: Vec<Arc<Mutex<dyn FnMut(T)>>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StreamPort<T> {
|
|
||||||
async fn set_stream(&self, stream: impl Stream<T>) -> impl Future<()> {
|
|
||||||
for msg in stream.next().await.unwrap() {
|
|
||||||
for act in self.actions.iter() {
|
|
||||||
(*act.lock().unwrap())(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_action(&self, action: impl FnMut(T)) {
|
|
||||||
self.actions.push(Arc::new(Mutex::new(action)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,54 @@
|
||||||
use {
|
use {
|
||||||
std::sync::{Arc, RwLock},
|
std::{
|
||||||
cgmath::Vector2,
|
ops::Range,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
},
|
||||||
|
cgmath::Point2,
|
||||||
crate::{
|
crate::{
|
||||||
view::{View, Observer, ObserverExt},
|
core::{
|
||||||
port::{ViewPort, InnerViewPort, OuterViewPort},
|
View,
|
||||||
terminal::{TerminalAtom, TerminalStyle},
|
Observer,
|
||||||
vec_buffer::VecBuffer
|
ObserverExt,
|
||||||
|
ObserverBroadcast,
|
||||||
|
ViewPort,
|
||||||
|
InnerViewPort,
|
||||||
|
OuterViewPort
|
||||||
|
},
|
||||||
|
sequence::SequenceView,
|
||||||
|
index::{ImplIndexView},
|
||||||
|
grid::{GridView},
|
||||||
|
terminal::{TerminalAtom, TerminalStyle, TerminalView},
|
||||||
|
//vec_buffer::VecBuffer
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
pub struct StringEditorState {
|
pub struct StringEditorState {
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
data: Arc<RwLock<Vec<char>>>
|
data: Arc<RwLock<Vec<char>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for StringEditorState {
|
impl ImplIndexView for StringEditorState {
|
||||||
type Key = Vector2<i16>;
|
type Key = Point2<i16>;
|
||||||
type Value = TerminalAtom;
|
type Value = Option<TerminalAtom>;
|
||||||
|
|
||||||
fn view(&self, pos: Vector2<i16>) -> Option<TerminalAtom> {
|
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||||
if pos.y == 0 {
|
|
||||||
let cur = self.cursor;
|
|
||||||
let data = self.data.read().unwrap();
|
let data = self.data.read().unwrap();
|
||||||
|
|
||||||
|
if pos.y == 0 {
|
||||||
if pos.x < data.len() as i16 + 3 {
|
if pos.x < data.len() as i16 + 3 {
|
||||||
let i = pos.x as usize;
|
let i = pos.x as usize;
|
||||||
return Some(
|
return Some(
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
||||||
} else if i-1 == cur {
|
} else if i-1 == self.cursor {
|
||||||
TerminalAtom::new('|', TerminalStyle::fg_color((180,200,130)).add(TerminalStyle::bold(true)))
|
TerminalAtom::new('|', TerminalStyle::fg_color((180,200,130)).add(TerminalStyle::bold(true)))
|
||||||
} else if i-1 == data.len()+1 {
|
} else if i-1 == data.len()+1 {
|
||||||
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
||||||
} else {
|
} else {
|
||||||
TerminalAtom::new(
|
TerminalAtom::new(
|
||||||
data.get(i as usize - if i <= cur { 1 } else { 2 }).cloned().unwrap(),
|
data.get(i as usize - if i <= self.cursor { 1 } else { 2 }).unwrap().clone(),
|
||||||
TerminalStyle::fg_color((80,150,80))
|
TerminalStyle::fg_color((80,150,80))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -44,41 +58,34 @@ impl View for StringEditorState {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||||
|
Some(
|
||||||
|
Point2::new(0, 0)
|
||||||
|
.. Point2::new(self.data.read().unwrap().len() as i16 + 3, 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StringEditor {
|
pub struct StringEditor {
|
||||||
state: Arc<RwLock<StringEditorState>>,
|
state: Arc<RwLock<StringEditorState>>,
|
||||||
port: InnerViewPort<Vector2<i16>, TerminalAtom>
|
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringEditor {
|
impl StringEditor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
port: InnerViewPort<Vector2<i16>, TerminalAtom>
|
port: InnerViewPort<dyn TerminalView>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let state = Arc::new(RwLock::new(StringEditorState{
|
let state = Arc::new(RwLock::new(StringEditorState{
|
||||||
cursor: 0,
|
cursor: 7,
|
||||||
data: Arc::new(RwLock::new(Vec::new()))
|
data: Arc::new(RwLock::new("edit me".chars().collect()))
|
||||||
}));
|
}));
|
||||||
/*
|
|
||||||
let buf_port = ViewPort::new();
|
|
||||||
let buf = VecBuffer::with_data(data.clone(), buf_port.inner());
|
|
||||||
|
|
||||||
buf_port.outer().add_observer_fn({
|
let cast = port.set_view(Some(state.clone()));
|
||||||
let port = port.clone();
|
|
||||||
let cursor = cursor.clone();
|
|
||||||
|
|
||||||
move |idx|
|
|
||||||
if idx < *cursor.read().unwrap() {
|
|
||||||
port.notify(Vector2::new(1 + idx as i16, 0));
|
|
||||||
} else {
|
|
||||||
port.notify(Vector2::new(2 + idx as i16, 0));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
port.set_view(state.clone());
|
|
||||||
StringEditor {
|
StringEditor {
|
||||||
state,
|
state,
|
||||||
port
|
cast
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,26 +116,26 @@ impl StringEditor {
|
||||||
old_idx
|
old_idx
|
||||||
};
|
};
|
||||||
|
|
||||||
self.port.notify_each(
|
self.cast.notify_each(
|
||||||
(std::cmp::min(old_idx, new_idx) ..= std::cmp::max(old_idx, new_idx))
|
(std::cmp::min(old_idx, new_idx) ..= std::cmp::max(old_idx, new_idx))
|
||||||
.map(|idx| Vector2::new(1+idx as i16, 0))
|
.map(|idx| Point2::new(1+idx as i16, 0))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, c: char) {
|
pub fn insert(&mut self, c: char) {
|
||||||
self.port.notify_each({
|
self.cast.notify_each({
|
||||||
let mut state = self.state.write().unwrap();
|
let mut state = self.state.write().unwrap();
|
||||||
let mut data = state.data.write().unwrap();
|
let mut data = state.data.write().unwrap();
|
||||||
|
|
||||||
data.insert(state.cursor, c);
|
data.insert(state.cursor, c);
|
||||||
(state.cursor .. data.len()+2)
|
(state.cursor .. data.len()+2)
|
||||||
}.map(|idx| Vector2::new(1+idx as i16, 0)));
|
}.map(|idx| Point2::new(1+idx as i16, 0)));
|
||||||
|
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self) {
|
pub fn delete(&mut self) {
|
||||||
self.port.notify_each({
|
self.cast.notify_each({
|
||||||
let mut state = self.state.write().unwrap();
|
let mut state = self.state.write().unwrap();
|
||||||
let mut data = state.data.write().unwrap();
|
let mut data = state.data.write().unwrap();
|
||||||
|
|
||||||
|
@ -136,7 +143,7 @@ impl StringEditor {
|
||||||
data.remove(state.cursor);
|
data.remove(state.cursor);
|
||||||
}
|
}
|
||||||
(state.cursor .. data.len()+3)
|
(state.cursor .. data.len()+3)
|
||||||
}.map(|idx| Vector2::new(1+idx as i16, 0)));
|
}.map(|idx| Point2::new(1+idx as i16, 0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,53 +1,175 @@
|
||||||
use {
|
use {
|
||||||
std::sync::{Arc, RwLock},
|
std::{
|
||||||
cgmath::Vector2,
|
sync::{Arc, Weak, RwLock},
|
||||||
|
collections::HashMap,
|
||||||
|
ops::Range,
|
||||||
|
cmp::{min, max}
|
||||||
|
},
|
||||||
|
cgmath::Point2,
|
||||||
crate::{
|
crate::{
|
||||||
view::{View},
|
core::{View, ViewPort, InnerViewPort, OuterViewPort, Observer, ObserverExt, ObserverBroadcast},
|
||||||
port::{ViewPort, InnerViewPort, OuterViewPort},
|
view::{ImplIndexView, grid::GridWindowIterator},
|
||||||
terminal::{TerminalAtom}
|
terminal::{TerminalAtom, TerminalView}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TerminalCompositor {
|
struct CompositeLayer {
|
||||||
layers: Arc<RwLock<Vec<Arc<dyn View<Key = Vector2<i16>, Value = TerminalAtom>>>>>,
|
comp: Weak<RwLock<TerminalCompositeView>>,
|
||||||
port: Arc<InnerViewPort<Vector2<i16>, TerminalAtom>>
|
idx: usize,
|
||||||
|
view: RwLock<Option<Arc<dyn TerminalView>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalCompositor {
|
impl Observer<dyn TerminalView> for CompositeLayer {
|
||||||
pub fn new(port: InnerViewPort<Vector2<i16>, TerminalAtom>) -> Self {
|
fn reset(&self, view: Option<Arc<dyn TerminalView>>) {
|
||||||
let layers = Arc::new(RwLock::new(Vec::<Arc<dyn View<Key = Vector2<i16>, Value = TerminalAtom>>>::new()));
|
let comp = self.comp.upgrade().unwrap();
|
||||||
|
let mut c = comp.write().unwrap();
|
||||||
|
|
||||||
port.set_view_fn({
|
{
|
||||||
let layers = layers.clone();
|
let mut v = self.view.write().unwrap();
|
||||||
move |pos| {
|
let old_view = v.clone();
|
||||||
|
*v = view.clone();
|
||||||
|
|
||||||
|
if let Some(old_view) = old_view {
|
||||||
|
if let Some(range) = old_view.range() {
|
||||||
|
c.cast.notify_each(GridWindowIterator::from(range));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(view) = v.as_ref() {
|
||||||
|
if let Some(range) = view.range() {
|
||||||
|
c.cast.notify_each(GridWindowIterator::from(range));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.update_range();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&self, pos: &Point2<i16>) {
|
||||||
|
self.comp
|
||||||
|
.upgrade().unwrap()
|
||||||
|
.read().unwrap()
|
||||||
|
.cast.notify(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TerminalCompositeView {
|
||||||
|
idx_count: usize,
|
||||||
|
layers: HashMap<usize, Arc<CompositeLayer>>,
|
||||||
|
range: Option<Range<Point2<i16>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminalCompositeView {
|
||||||
|
fn update_range(&mut self) {
|
||||||
|
if self.layers.len() == 0 {
|
||||||
|
self.range = Some(Point2::new(0, 0) .. Point2::new(0, 0))
|
||||||
|
} else {
|
||||||
|
self.range = None;
|
||||||
|
|
||||||
|
for (idx, layer) in self.layers.iter() {
|
||||||
|
self.range =
|
||||||
|
if let (
|
||||||
|
Some(new_range),
|
||||||
|
Some(old_range)
|
||||||
|
) = (
|
||||||
|
if let Some(view) = layer.view.read().unwrap().clone() {
|
||||||
|
view.range().clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
self.range.as_ref()
|
||||||
|
) {
|
||||||
|
Some(
|
||||||
|
Point2::new(
|
||||||
|
min(old_range.start.x, new_range.start.x),
|
||||||
|
min(old_range.start.y, new_range.start.y)
|
||||||
|
) .. Point2::new(
|
||||||
|
max(old_range.end.x, new_range.end.x),
|
||||||
|
max(old_range.end.y, new_range.end.y)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplIndexView for TerminalCompositeView {
|
||||||
|
type Key = Point2<i16>;
|
||||||
|
type Value = Option<TerminalAtom>;
|
||||||
|
|
||||||
|
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||||
let mut atom = None;
|
let mut atom = None;
|
||||||
|
|
||||||
for l in layers.read().unwrap().iter() {
|
for idx in 0 .. self.idx_count {
|
||||||
match (atom, l.view(pos)) {
|
if let Some(l) = self.layers.get(&idx) {
|
||||||
|
if let Some(view) = l.view.read().unwrap().as_ref() {
|
||||||
|
if let Some(range) = view.range() {
|
||||||
|
if pos.x < range.start.x ||
|
||||||
|
pos.x >= range.end.x ||
|
||||||
|
pos.y < range.start.y ||
|
||||||
|
pos.y >= range.end.y {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match (atom, view.get(pos)) {
|
||||||
(None, next) => atom = next,
|
(None, next) => atom = next,
|
||||||
(Some(last), Some(next)) => atom = Some(next.add_style_back(last.style)),
|
(Some(last), Some(next)) => atom = Some(next.add_style_back(last.style)),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
atom
|
atom
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
TerminalCompositor {
|
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||||
layers,
|
self.range.clone()
|
||||||
port: Arc::new(port)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
pub struct TerminalCompositor {
|
||||||
pub fn push(&mut self, v: OuterViewPort<Vector2<i16>, TerminalAtom>) {
|
view: Arc<RwLock<TerminalCompositeView>>,
|
||||||
self.layers.write().unwrap().push(v.add_observer(self.port.clone()));
|
port: InnerViewPort<dyn TerminalView>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_port(&mut self) -> InnerViewPort<Vector2<i16>, TerminalAtom> {
|
impl TerminalCompositor {
|
||||||
let port = ViewPort::new();
|
pub fn new(
|
||||||
self.push(port.outer());
|
port: InnerViewPort<dyn TerminalView>
|
||||||
port.inner()
|
) -> Self {
|
||||||
|
let view = Arc::new(RwLock::new(
|
||||||
|
TerminalCompositeView {
|
||||||
|
idx_count: 0,
|
||||||
|
layers: HashMap::new(),
|
||||||
|
range: Some(Point2::new(0, 0) .. Point2::new(0, 0)),
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
port.set_view(Some(view.clone()));
|
||||||
|
TerminalCompositor{ view, port }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, v: OuterViewPort<dyn TerminalView>) {
|
||||||
|
let mut comp = self.view.write().unwrap();
|
||||||
|
let idx = comp.idx_count;
|
||||||
|
comp.idx_count += 1;
|
||||||
|
|
||||||
|
let layer = Arc::new(CompositeLayer {
|
||||||
|
comp: Arc::downgrade(&self.view),
|
||||||
|
idx: idx,
|
||||||
|
view: RwLock::new(None)
|
||||||
|
});
|
||||||
|
|
||||||
|
comp.layers.insert(idx, layer.clone());
|
||||||
|
drop(comp);
|
||||||
|
|
||||||
|
v.add_observer(layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,24 @@ pub use {
|
||||||
style::{TerminalStyle},
|
style::{TerminalStyle},
|
||||||
atom::{TerminalAtom},
|
atom::{TerminalAtom},
|
||||||
terminal::{Terminal, TerminalEvent},
|
terminal::{Terminal, TerminalEvent},
|
||||||
compositor::TerminalCompositor
|
compositor::TerminalCompositor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
core::View,
|
||||||
|
view::{
|
||||||
|
IndexView,
|
||||||
|
GridView
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cgmath::Point2,
|
||||||
|
std::ops::Range
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait TerminalView = GridView<Item = Option<TerminalAtom>>;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,40 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
std::io::{Write, stdout, stdin},
|
std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
io::{Write, stdout, stdin},
|
||||||
|
collections::HashSet
|
||||||
|
},
|
||||||
async_std::{
|
async_std::{
|
||||||
stream::StreamExt,
|
stream::StreamExt,
|
||||||
task
|
task
|
||||||
},
|
},
|
||||||
signal_hook,
|
signal_hook,
|
||||||
signal_hook_async_std::Signals,
|
signal_hook_async_std::Signals,
|
||||||
cgmath::Vector2,
|
cgmath::{Vector2, Point2},
|
||||||
termion::{
|
termion::{
|
||||||
raw::IntoRawMode,
|
raw::IntoRawMode,
|
||||||
input::{TermRead, MouseTerminal}
|
input::{TermRead, MouseTerminal}
|
||||||
},
|
},
|
||||||
super::{TerminalAtom, TerminalStyle},
|
|
||||||
crate::{
|
crate::{
|
||||||
view::{View, Observer},
|
core::{
|
||||||
port::{OuterViewPort},
|
OuterViewPort,
|
||||||
channel::ChannelReceiver
|
Observer,
|
||||||
|
channel::{
|
||||||
|
ChannelReceiver,
|
||||||
|
ChannelSender,
|
||||||
|
queue_channel,
|
||||||
|
set_channel
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
view::{
|
||||||
|
IndexView,
|
||||||
|
grid::GridWindowIterator
|
||||||
|
}
|
||||||
|
},
|
||||||
|
super::{
|
||||||
|
TerminalStyle,
|
||||||
|
TerminalView
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum TerminalEvent {
|
pub enum TerminalEvent {
|
||||||
|
@ -26,36 +43,56 @@ pub enum TerminalEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Terminal {
|
pub struct Terminal {
|
||||||
|
writer: Arc<TermOutWriter>,
|
||||||
|
observer: Arc<TermOutObserver>,
|
||||||
|
|
||||||
events: ChannelReceiver<Vec<TerminalEvent>>,
|
events: ChannelReceiver<Vec<TerminalEvent>>,
|
||||||
signal_handle: signal_hook_async_std::Handle
|
_signal_handle: signal_hook_async_std::Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
pub fn new() -> Self {
|
pub fn new(
|
||||||
let (event_tx, event_rx) = crate::channel::queue_channel();
|
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(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();
|
let input_tx = event_tx.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
for event in stdin().events() {
|
for event in stdin().events() {
|
||||||
input_tx.notify(TerminalEvent::Input(event.unwrap()));
|
input_tx.send(TerminalEvent::Input(event.unwrap()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// send initial teriminal size
|
// send initial teriminal size
|
||||||
let (w,h) = termion::terminal_size().unwrap();
|
let (w, h) = termion::terminal_size().unwrap();
|
||||||
event_tx.notify(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
||||||
|
|
||||||
// and again on SIGWINCH
|
// and again on SIGWINCH
|
||||||
let signals = Signals::new(&[ signal_hook::SIGWINCH ]).unwrap();
|
let signals = Signals::new(&[ signal_hook::consts::signal::SIGWINCH ]).unwrap();
|
||||||
let handle = signals.handle();
|
let handle = signals.handle();
|
||||||
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let mut signals = signals.fuse();
|
let mut signals = signals.fuse();
|
||||||
while let Some(signal) = signals.next().await {
|
while let Some(signal) = signals.next().await {
|
||||||
match signal {
|
match signal {
|
||||||
signal_hook::SIGWINCH => {
|
signal_hook::consts::signal::SIGWINCH => {
|
||||||
let (w,h) = termion::terminal_size().unwrap();
|
let (w,h) = termion::terminal_size().unwrap();
|
||||||
event_tx.notify(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -63,42 +100,83 @@ impl Terminal {
|
||||||
});
|
});
|
||||||
|
|
||||||
Terminal {
|
Terminal {
|
||||||
|
writer,
|
||||||
|
observer,
|
||||||
events: event_rx,
|
events: event_rx,
|
||||||
signal_handle: handle
|
_signal_handle: handle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_writer(&self) -> Arc<TermOutWriter> {
|
||||||
|
self.writer.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn next_event(&mut self) -> TerminalEvent {
|
pub async fn next_event(&mut self) -> TerminalEvent {
|
||||||
self.events.next().await.unwrap()
|
self.events.next().await.unwrap()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn show(view_port: OuterViewPort<Vector2<i16>, TerminalAtom>) -> std::io::Result<()> {
|
struct TermOutObserver {
|
||||||
let (atom_tx, atom_rx) = crate::channel::queue_channel();
|
dirty_pos_tx: ChannelSender<HashSet<Point2<i16>>>,
|
||||||
|
writer: Arc<TermOutWriter>
|
||||||
|
}
|
||||||
|
|
||||||
let view = view_port.get_view();
|
impl Observer<dyn TerminalView> for TermOutObserver {
|
||||||
view_port.add_observer_fn(move |pos| atom_tx.notify((pos, view.view(pos))));
|
fn reset(&self, view: Option<Arc<dyn TerminalView>>) {
|
||||||
|
self.writer.reset();
|
||||||
|
|
||||||
Self::show_stream(atom_rx).await
|
let (w, h) = termion::terminal_size().unwrap();
|
||||||
|
if let Some(view) = view {
|
||||||
|
for pos in GridWindowIterator::from(
|
||||||
|
view.range().unwrap_or(
|
||||||
|
Point2::new(0, 0) .. Point2::new(w as i16, h as i16)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
self.dirty_pos_tx.send(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn show_stream(recv: ChannelReceiver<Vec<(Vector2<i16>, Option<TerminalAtom>)>>) -> std::io::Result<()> {
|
fn notify(&self, pos: &Point2<i16>) {
|
||||||
let mut out = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
self.dirty_pos_tx.send(*pos);
|
||||||
let mut cur_pos = Vector2::<i16>::new(0, 0);
|
}
|
||||||
let mut cur_style = TerminalStyle::default();
|
}
|
||||||
write!(out, "{}{}{}{}",
|
|
||||||
termion::clear::All,
|
pub struct TermOutWriter {
|
||||||
termion::cursor::Goto(1, 1),
|
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::Hide,
|
||||||
|
termion::cursor::Goto(1, 1),
|
||||||
termion::style::Reset)?;
|
termion::style::Reset)?;
|
||||||
|
|
||||||
while let Some(atoms) = recv.recv().await {
|
let mut cur_pos = Point2::<i16>::new(0, 0);
|
||||||
for (pos, atom) in atoms.into_iter() {
|
let mut cur_style = TerminalStyle::default();
|
||||||
if pos != cur_pos+Vector2::new(1,0) {
|
|
||||||
|
// 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))?;
|
write!(out, "{}", termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1))?;
|
||||||
}
|
}
|
||||||
cur_pos = pos;
|
|
||||||
|
|
||||||
if let Some(atom) = atom {
|
if let Some(atom) = view.get(&pos) {
|
||||||
if cur_style != atom.style {
|
if cur_style != atom.style {
|
||||||
cur_style = atom.style;
|
cur_style = atom.style;
|
||||||
write!(out, "{}", atom.style)?;
|
write!(out, "{}", atom.style)?;
|
||||||
|
@ -109,11 +187,16 @@ impl Terminal {
|
||||||
write!(out, "{} ", termion::style::Reset)?;
|
write!(out, "{} ", termion::style::Reset)?;
|
||||||
cur_style = TerminalStyle::default();
|
cur_style = TerminalStyle::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cur_pos = pos + Vector2::new(1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
out.flush()?;
|
out.flush()?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore conventional terminal settings
|
||||||
|
let mut out = self.out.write().unwrap();
|
||||||
write!(out, "{}", termion::cursor::Show)?;
|
write!(out, "{}", termion::cursor::Show)?;
|
||||||
out.flush()?;
|
out.flush()?;
|
||||||
|
|
||||||
|
|
189
src/view.rs
189
src/view.rs
|
@ -1,189 +0,0 @@
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub trait View : Send + Sync {
|
|
||||||
type Key;
|
|
||||||
type Value;
|
|
||||||
|
|
||||||
fn view(&self, key: Self::Key) -> Option<Self::Value>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Observer : Send + Sync {
|
|
||||||
type Msg;
|
|
||||||
|
|
||||||
fn notify(&self, key: Self::Msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ObserverExt : Observer {
|
|
||||||
fn notify_each(&self, it: impl IntoIterator<Item = Self::Msg>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Observer> ObserverExt for T {
|
|
||||||
fn notify_each(&self, it: impl IntoIterator<Item = Self::Msg>) {
|
|
||||||
for msg in it {
|
|
||||||
self.notify(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
use cgmath::Vector2;
|
|
||||||
|
|
||||||
pub trait SingletonView = View<Key = ()>;
|
|
||||||
pub trait SingletonObserver = Observer<Msg = ()>;
|
|
||||||
|
|
||||||
pub trait SequenceView = View<Key = usize>;
|
|
||||||
pub trait SequenceObserver = Observer<Msg = usize>;
|
|
||||||
|
|
||||||
pub trait GridView = View<Key = Vector2<i16>>;
|
|
||||||
pub trait GridObserver = Observer<Msg = Vector2<i16>>;
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub struct FnView<K, V, F>
|
|
||||||
where K: Send + Sync,
|
|
||||||
V: Send + Sync,
|
|
||||||
F: Fn(K) -> Option<V> + Send + Sync {
|
|
||||||
f: F,
|
|
||||||
_phantom0: std::marker::PhantomData<K>,
|
|
||||||
_phantom1: std::marker::PhantomData<V>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V, F> FnView<K, V, F>
|
|
||||||
where K: Send + Sync,
|
|
||||||
V: Send + Sync,
|
|
||||||
F: Fn(K) -> Option<V> + Send + Sync {
|
|
||||||
pub fn new(f: F) -> Self {
|
|
||||||
FnView {
|
|
||||||
f,
|
|
||||||
_phantom0: std::marker::PhantomData,
|
|
||||||
_phantom1: std::marker::PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V, F> View for FnView<K, V, F>
|
|
||||||
where K: Send + Sync,
|
|
||||||
V: Send + Sync,
|
|
||||||
F: Fn(K) -> Option<V> + Send + Sync {
|
|
||||||
type Key = K;
|
|
||||||
type Value = V;
|
|
||||||
|
|
||||||
fn view(&self, key: K) -> Option<V> {
|
|
||||||
(self.f)(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub struct FnObserver<T, F>
|
|
||||||
where T: Send + Sync,
|
|
||||||
F: Fn(T) + Send + Sync {
|
|
||||||
f: F,
|
|
||||||
_phantom: std::marker::PhantomData<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, F> FnObserver<T, F>
|
|
||||||
where T: Send + Sync,
|
|
||||||
F: Fn(T) + Send + Sync {
|
|
||||||
pub fn new(f: F) -> Self {
|
|
||||||
FnObserver {
|
|
||||||
f,
|
|
||||||
_phantom: std::marker::PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, F> Observer for FnObserver<T, F>
|
|
||||||
where T: Send + Sync,
|
|
||||||
F: Fn(T) + Send + Sync {
|
|
||||||
type Msg = T;
|
|
||||||
|
|
||||||
fn notify(&self, msg: T) {
|
|
||||||
(self.f)(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
impl<T: View> View for RwLock<T> {
|
|
||||||
type Key = T::Key;
|
|
||||||
type Value = T::Value;
|
|
||||||
|
|
||||||
fn view(&self, key: T::Key) -> Option<T::Value> {
|
|
||||||
self.read().unwrap().view(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Observer> Observer for RwLock<T> {
|
|
||||||
type Msg = T::Msg;
|
|
||||||
|
|
||||||
fn notify(&self, msg: T::Msg) {
|
|
||||||
self.read().unwrap().notify(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: View> View for Arc<T> {
|
|
||||||
type Key = T::Key;
|
|
||||||
type Value = T::Value;
|
|
||||||
|
|
||||||
fn view(&self, key: T::Key) -> Option<T::Value> {
|
|
||||||
self.deref().view(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Observer> Observer for Arc<T> {
|
|
||||||
type Msg = T::Msg;
|
|
||||||
|
|
||||||
fn notify(&self, msg: T::Msg) {
|
|
||||||
self.deref().notify(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> View for Arc<dyn View<Key = K, Value = V>>
|
|
||||||
where K: Send + Sync,
|
|
||||||
V: Send + Sync {
|
|
||||||
type Key = K;
|
|
||||||
type Value = V;
|
|
||||||
|
|
||||||
fn view(&self, key: K) -> Option<V> {
|
|
||||||
self.deref().view(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Observer for Arc<dyn Observer<Msg = T>>
|
|
||||||
where T: Send + Sync {
|
|
||||||
type Msg = T;
|
|
||||||
|
|
||||||
fn notify(&self, msg: T) {
|
|
||||||
self.deref().notify(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: View> View for Option<T> {
|
|
||||||
type Key = T::Key;
|
|
||||||
type Value = T::Value;
|
|
||||||
|
|
||||||
fn view(&self, key: T::Key) -> Option<T::Value> {
|
|
||||||
if let Some(view) = self.as_ref() {
|
|
||||||
view.view(key)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Observer> Observer for Option<T> {
|
|
||||||
type Msg = T::Msg;
|
|
||||||
|
|
||||||
fn notify(&self, msg: T::Msg) {
|
|
||||||
if let Some(obs) = self.as_ref() {
|
|
||||||
obs.notify(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
78
src/view/grid.rs
Normal file
78
src/view/grid.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
ops::{Deref, Range}
|
||||||
|
},
|
||||||
|
cgmath::{Point2, Vector2},
|
||||||
|
crate::{
|
||||||
|
core::View,
|
||||||
|
view::{IndexView, ImplIndexView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait GridView = IndexView<Point2<i16>>;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
/*
|
||||||
|
pub trait ImplGridView : Send + Sync {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, pos: &Point2<i16>) -> Self::Item;
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ImplGridView> ImplIndexView for V {
|
||||||
|
type Key = Point2<i16>;
|
||||||
|
type Value = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, pos: &Point2<i16>) -> V::Item {
|
||||||
|
(self as &V).get(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||||
|
(self as &V).range()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
73
src/view/index.rs
Normal file
73
src/view/index.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
ops::{Deref, Range}
|
||||||
|
},
|
||||||
|
crate::core::View
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait IndexView<Key> : View<Msg = Key> {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Self::Item;
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Key>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Key, V: IndexView<Key>> IndexView<Key> for RwLock<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Self::Item {
|
||||||
|
self.read().unwrap().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Key>> {
|
||||||
|
self.read().unwrap().range()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, V: IndexView<Key>> IndexView<Key> for Arc<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Self::Item {
|
||||||
|
self.deref().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<Key>> {
|
||||||
|
self.deref().range()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait ImplIndexView : Send + Sync {
|
||||||
|
type Key;
|
||||||
|
type Value;
|
||||||
|
|
||||||
|
fn get(&self, key: &Self::Key) -> Self::Value;
|
||||||
|
fn range(&self) -> Option<Range<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) -> Self::Item {
|
||||||
|
(self as &V).get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<V::Key>> {
|
||||||
|
(self as &V).range()
|
||||||
|
}
|
||||||
|
}
|
13
src/view/mod.rs
Normal file
13
src/view/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
pub mod singleton;
|
||||||
|
pub mod index;
|
||||||
|
pub mod sequence;
|
||||||
|
pub mod grid;
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
singleton::SingletonView,
|
||||||
|
index::{IndexView, ImplIndexView},
|
||||||
|
sequence::SequenceView,
|
||||||
|
grid::GridView,
|
||||||
|
crate::core::View
|
||||||
|
};
|
||||||
|
|
41
src/view/sequence.rs
Normal file
41
src/view/sequence.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
ops::{Range, Deref}
|
||||||
|
},
|
||||||
|
super::{IndexView, ImplIndexView},
|
||||||
|
crate::core::View
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait SequenceView = IndexView<usize>;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
/*
|
||||||
|
pub trait ImplSequenceView : Send + Sync {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: usize) -> Self::Item;
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ImplSequenceView> ImplIndexView for V {
|
||||||
|
type Key = usize;
|
||||||
|
type Value = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> V::Item {
|
||||||
|
(self as V).get(*idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Option<Range<usize>> {
|
||||||
|
if let Some(len) = (self as V).len() {
|
||||||
|
Some(0 .. len)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
53
src/view/singleton.rs
Normal file
53
src/view/singleton.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
ops::Deref
|
||||||
|
},
|
||||||
|
crate::core::{View}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: #[ImplForArc, ImplForRwLock]
|
||||||
|
pub trait SingletonView : View<Msg = ()> {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: SingletonView> SingletonView for RwLock<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.read().unwrap().get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SingletonView> SingletonView for Arc<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.deref().get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
/*
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in a new issue