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"
|
||||
|
||||
[dependencies]
|
||||
cgmath = "*"
|
||||
termion = "*"
|
||||
signal-hook = "*"
|
||||
signal-hook-async-std = "*"
|
||||
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"
|
||||
|
|
|
@ -13,7 +13,7 @@ use {
|
|||
},
|
||||
|
||||
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 {
|
||||
type Msg = Data::Item;
|
||||
|
||||
fn notify(&self, msg: Data::Item) {
|
||||
pub fn send(&self, msg: Data::Item) {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
|
||||
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> {
|
||||
fn clone(&self) -> Self {
|
||||
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;
|
||||
}
|
||||
|
||||
|
127
src/main.rs
127
src/main.rs
|
@ -1,51 +1,104 @@
|
|||
|
||||
#![feature(trait_alias)]
|
||||
#![feature(assoc_char_funcs)]
|
||||
|
||||
pub mod core;
|
||||
pub mod view;
|
||||
pub mod port;
|
||||
pub mod channel;
|
||||
pub mod singleton_buffer;
|
||||
pub mod vec_buffer;
|
||||
pub mod terminal;
|
||||
|
||||
pub mod string_editor;
|
||||
|
||||
//pub mod singleton_buffer;
|
||||
//pub mod vec_buffer;
|
||||
//pub mod sequence_element_projection;
|
||||
|
||||
use {
|
||||
async_std::{task},
|
||||
std::{
|
||||
sync::{Arc, RwLock}
|
||||
sync::{Arc, RwLock},
|
||||
ops::Range
|
||||
},
|
||||
cgmath::{Vector2},
|
||||
cgmath::{Vector2, Point2},
|
||||
termion::event::{Event, Key},
|
||||
crate::{
|
||||
view::{View, Observer},
|
||||
port::{ViewPort, InnerViewPort, OuterViewPort},
|
||||
singleton_buffer::SingletonBuffer,
|
||||
vec_buffer::VecBuffer,
|
||||
core::{View, Observer, ObserverExt, ViewPort},
|
||||
view::{*},
|
||||
terminal::{
|
||||
Terminal,
|
||||
TerminalView,
|
||||
TerminalAtom,
|
||||
TerminalStyle,
|
||||
TerminalCompositor,
|
||||
TerminalEvent
|
||||
}
|
||||
},
|
||||
termion::event::{Event, Key}
|
||||
TerminalEvent,
|
||||
Terminal,
|
||||
TerminalCompositor
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
struct Fill(TerminalAtom);
|
||||
impl View for Fill {
|
||||
type Key = Vector2<i16>;
|
||||
type Value = TerminalAtom;
|
||||
struct VecSequenceView<T: Send + Sync + Clone>(Arc<RwLock<Vec<T>>>);
|
||||
impl<T: Send + Sync + Clone> ImplIndexView for VecSequenceView<T> {
|
||||
type Key = usize;
|
||||
type Value = T;
|
||||
|
||||
fn view(&self, _: Vector2<i16>) -> Option<TerminalAtom> {
|
||||
Some(self.0.clone())
|
||||
fn get(&self, idx: &usize) -> T {
|
||||
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 fn main() {
|
||||
let composite_view = port::ViewPort::new();
|
||||
let mut compositor = TerminalCompositor::new(composite_view.inner());
|
||||
let term_port = ViewPort::<dyn TerminalView>::new();
|
||||
|
||||
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 {
|
||||
/*\
|
||||
|
@ -53,36 +106,30 @@ async fn main() {
|
|||
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();
|
||||
let mut editor = string_editor::StringEditor::new(ep.inner());
|
||||
compositor.push(ep.outer());
|
||||
compositor.push(ViewPort::<dyn TerminalView>::with_view(Arc::new(Checkerboard)).into_outer());
|
||||
let edit_port = ViewPort::<dyn TerminalView>::new();
|
||||
let mut editor = string_editor::StringEditor::new(edit_port.inner());
|
||||
compositor.push(edit_port.into_outer());
|
||||
|
||||
/*\
|
||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
Event Loop
|
||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
\*/
|
||||
let mut term = Terminal::new();
|
||||
loop {
|
||||
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::Right)) => editor.next(),
|
||||
TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
|
||||
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::Delete)) => 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::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 {
|
||||
std::sync::{Arc, RwLock},
|
||||
cgmath::Vector2,
|
||||
std::{
|
||||
ops::Range,
|
||||
sync::{Arc, RwLock},
|
||||
},
|
||||
cgmath::Point2,
|
||||
crate::{
|
||||
view::{View, Observer, ObserverExt},
|
||||
port::{ViewPort, InnerViewPort, OuterViewPort},
|
||||
terminal::{TerminalAtom, TerminalStyle},
|
||||
vec_buffer::VecBuffer
|
||||
core::{
|
||||
View,
|
||||
Observer,
|
||||
ObserverExt,
|
||||
ObserverBroadcast,
|
||||
ViewPort,
|
||||
InnerViewPort,
|
||||
OuterViewPort
|
||||
},
|
||||
sequence::SequenceView,
|
||||
index::{ImplIndexView},
|
||||
grid::{GridView},
|
||||
terminal::{TerminalAtom, TerminalStyle, TerminalView},
|
||||
//vec_buffer::VecBuffer
|
||||
}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct StringEditorState {
|
||||
cursor: usize,
|
||||
data: Arc<RwLock<Vec<char>>>
|
||||
}
|
||||
|
||||
impl View for StringEditorState {
|
||||
type Key = Vector2<i16>;
|
||||
type Value = TerminalAtom;
|
||||
impl ImplIndexView for StringEditorState {
|
||||
type Key = Point2<i16>;
|
||||
type Value = Option<TerminalAtom>;
|
||||
|
||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
let data = self.data.read().unwrap();
|
||||
|
||||
fn view(&self, pos: Vector2<i16>) -> Option<TerminalAtom> {
|
||||
if pos.y == 0 {
|
||||
let cur = self.cursor;
|
||||
let data = self.data.read().unwrap();
|
||||
|
||||
if pos.x < data.len() as i16 + 3 {
|
||||
let i = pos.x as usize;
|
||||
return Some(
|
||||
if i == 0 {
|
||||
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)))
|
||||
} else if i-1 == data.len()+1 {
|
||||
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
||||
} else {
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
@ -43,42 +57,35 @@ impl View for StringEditorState {
|
|||
}
|
||||
|
||||
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 {
|
||||
state: Arc<RwLock<StringEditorState>>,
|
||||
port: InnerViewPort<Vector2<i16>, TerminalAtom>
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
||||
}
|
||||
|
||||
impl StringEditor {
|
||||
pub fn new(
|
||||
port: InnerViewPort<Vector2<i16>, TerminalAtom>
|
||||
port: InnerViewPort<dyn TerminalView>
|
||||
) -> Self {
|
||||
let state = Arc::new(RwLock::new(StringEditorState{
|
||||
cursor: 0,
|
||||
data: Arc::new(RwLock::new(Vec::new()))
|
||||
cursor: 7,
|
||||
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 port = port.clone();
|
||||
let cursor = cursor.clone();
|
||||
let cast = port.set_view(Some(state.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 {
|
||||
state,
|
||||
port
|
||||
cast
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,26 +116,26 @@ impl StringEditor {
|
|||
old_idx
|
||||
};
|
||||
|
||||
self.port.notify_each(
|
||||
self.cast.notify_each(
|
||||
(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) {
|
||||
self.port.notify_each({
|
||||
self.cast.notify_each({
|
||||
let mut state = self.state.write().unwrap();
|
||||
let mut data = state.data.write().unwrap();
|
||||
|
||||
data.insert(state.cursor, c);
|
||||
(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();
|
||||
}
|
||||
|
||||
pub fn delete(&mut self) {
|
||||
self.port.notify_each({
|
||||
self.cast.notify_each({
|
||||
let mut state = self.state.write().unwrap();
|
||||
let mut data = state.data.write().unwrap();
|
||||
|
||||
|
@ -136,7 +143,7 @@ impl StringEditor {
|
|||
data.remove(state.cursor);
|
||||
}
|
||||
(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 {
|
||||
std::sync::{Arc, RwLock},
|
||||
cgmath::Vector2,
|
||||
std::{
|
||||
sync::{Arc, Weak, RwLock},
|
||||
collections::HashMap,
|
||||
ops::Range,
|
||||
cmp::{min, max}
|
||||
},
|
||||
cgmath::Point2,
|
||||
crate::{
|
||||
view::{View},
|
||||
port::{ViewPort, InnerViewPort, OuterViewPort},
|
||||
terminal::{TerminalAtom}
|
||||
core::{View, ViewPort, InnerViewPort, OuterViewPort, Observer, ObserverExt, ObserverBroadcast},
|
||||
view::{ImplIndexView, grid::GridWindowIterator},
|
||||
terminal::{TerminalAtom, TerminalView}
|
||||
}
|
||||
};
|
||||
|
||||
pub struct TerminalCompositor {
|
||||
layers: Arc<RwLock<Vec<Arc<dyn View<Key = Vector2<i16>, Value = TerminalAtom>>>>>,
|
||||
port: Arc<InnerViewPort<Vector2<i16>, TerminalAtom>>
|
||||
struct CompositeLayer {
|
||||
comp: Weak<RwLock<TerminalCompositeView>>,
|
||||
idx: usize,
|
||||
view: RwLock<Option<Arc<dyn TerminalView>>>
|
||||
}
|
||||
|
||||
impl TerminalCompositor {
|
||||
pub fn new(port: InnerViewPort<Vector2<i16>, TerminalAtom>) -> Self {
|
||||
let layers = Arc::new(RwLock::new(Vec::<Arc<dyn View<Key = Vector2<i16>, Value = TerminalAtom>>>::new()));
|
||||
impl Observer<dyn TerminalView> for CompositeLayer {
|
||||
fn reset(&self, view: Option<Arc<dyn TerminalView>>) {
|
||||
let comp = self.comp.upgrade().unwrap();
|
||||
let mut c = comp.write().unwrap();
|
||||
|
||||
port.set_view_fn({
|
||||
let layers = layers.clone();
|
||||
move |pos| {
|
||||
let mut atom = None;
|
||||
{
|
||||
let mut v = self.view.write().unwrap();
|
||||
let old_view = v.clone();
|
||||
*v = view.clone();
|
||||
|
||||
for l in layers.read().unwrap().iter() {
|
||||
match (atom, l.view(pos)) {
|
||||
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;
|
||||
|
||||
for idx in 0 .. self.idx_count {
|
||||
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,
|
||||
(Some(last), Some(next)) => atom = Some(next.add_style_back(last.style)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
atom
|
||||
}
|
||||
});
|
||||
|
||||
TerminalCompositor {
|
||||
layers,
|
||||
port: Arc::new(port)
|
||||
}
|
||||
|
||||
atom
|
||||
}
|
||||
|
||||
pub fn push(&mut self, v: OuterViewPort<Vector2<i16>, TerminalAtom>) {
|
||||
self.layers.write().unwrap().push(v.add_observer(self.port.clone()));
|
||||
}
|
||||
|
||||
pub fn make_port(&mut self) -> InnerViewPort<Vector2<i16>, TerminalAtom> {
|
||||
let port = ViewPort::new();
|
||||
self.push(port.outer());
|
||||
port.inner()
|
||||
fn range(&self) -> Option<Range<Point2<i16>>> {
|
||||
self.range.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TerminalCompositor {
|
||||
view: Arc<RwLock<TerminalCompositeView>>,
|
||||
port: InnerViewPort<dyn TerminalView>
|
||||
}
|
||||
|
||||
impl TerminalCompositor {
|
||||
pub fn new(
|
||||
port: InnerViewPort<dyn TerminalView>
|
||||
) -> 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},
|
||||
atom::{TerminalAtom},
|
||||
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 {
|
||||
std::io::{Write, stdout, stdin},
|
||||
std::{
|
||||
sync::{Arc, RwLock},
|
||||
io::{Write, stdout, stdin},
|
||||
collections::HashSet
|
||||
},
|
||||
async_std::{
|
||||
stream::StreamExt,
|
||||
task
|
||||
},
|
||||
signal_hook,
|
||||
signal_hook_async_std::Signals,
|
||||
cgmath::Vector2,
|
||||
cgmath::{Vector2, Point2},
|
||||
termion::{
|
||||
raw::IntoRawMode,
|
||||
input::{TermRead, MouseTerminal}
|
||||
},
|
||||
super::{TerminalAtom, TerminalStyle},
|
||||
crate::{
|
||||
view::{View, Observer},
|
||||
port::{OuterViewPort},
|
||||
channel::ChannelReceiver
|
||||
}
|
||||
core::{
|
||||
OuterViewPort,
|
||||
Observer,
|
||||
channel::{
|
||||
ChannelReceiver,
|
||||
ChannelSender,
|
||||
queue_channel,
|
||||
set_channel
|
||||
}
|
||||
},
|
||||
view::{
|
||||
IndexView,
|
||||
grid::GridWindowIterator
|
||||
}
|
||||
},
|
||||
super::{
|
||||
TerminalStyle,
|
||||
TerminalView
|
||||
},
|
||||
};
|
||||
|
||||
pub enum TerminalEvent {
|
||||
|
@ -26,36 +43,56 @@ pub enum TerminalEvent {
|
|||
}
|
||||
|
||||
pub struct Terminal {
|
||||
writer: Arc<TermOutWriter>,
|
||||
observer: Arc<TermOutObserver>,
|
||||
|
||||
events: ChannelReceiver<Vec<TerminalEvent>>,
|
||||
signal_handle: signal_hook_async_std::Handle
|
||||
_signal_handle: signal_hook_async_std::Handle
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
pub fn new() -> Self {
|
||||
let (event_tx, event_rx) = crate::channel::queue_channel();
|
||||
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(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.notify(TerminalEvent::Input(event.unwrap()));
|
||||
input_tx.send(TerminalEvent::Input(event.unwrap()));
|
||||
}
|
||||
});
|
||||
|
||||
// send initial teriminal size
|
||||
let (w,h) = termion::terminal_size().unwrap();
|
||||
event_tx.notify(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
||||
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::SIGWINCH ]).unwrap();
|
||||
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::SIGWINCH => {
|
||||
signal_hook::consts::signal::SIGWINCH => {
|
||||
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!(),
|
||||
}
|
||||
|
@ -63,61 +100,107 @@ impl Terminal {
|
|||
});
|
||||
|
||||
Terminal {
|
||||
writer,
|
||||
observer,
|
||||
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 {
|
||||
self.events.next().await.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn show(view_port: OuterViewPort<Vector2<i16>, TerminalAtom>) -> std::io::Result<()> {
|
||||
let (atom_tx, atom_rx) = crate::channel::queue_channel();
|
||||
struct TermOutObserver {
|
||||
dirty_pos_tx: ChannelSender<HashSet<Point2<i16>>>,
|
||||
writer: Arc<TermOutWriter>
|
||||
}
|
||||
|
||||
let view = view_port.get_view();
|
||||
view_port.add_observer_fn(move |pos| atom_tx.notify((pos, view.view(pos))));
|
||||
impl Observer<dyn TerminalView> for TermOutObserver {
|
||||
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<()> {
|
||||
let mut out = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
||||
let mut cur_pos = Vector2::<i16>::new(0, 0);
|
||||
let mut cur_style = TerminalStyle::default();
|
||||
write!(out, "{}{}{}{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Hide,
|
||||
termion::style::Reset)?;
|
||||
|
||||
while let Some(atoms) = recv.recv().await {
|
||||
for (pos, atom) in atoms.into_iter() {
|
||||
if pos != cur_pos+Vector2::new(1,0) {
|
||||
write!(out, "{}", termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1))?;
|
||||
}
|
||||
cur_pos = pos;
|
||||
|
||||
if let Some(atom) = atom {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
out.flush()?;
|
||||
}
|
||||
|
||||
write!(out, "{}", termion::cursor::Show)?;
|
||||
out.flush()?;
|
||||
|
||||
std::io::Result::Ok(())
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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