From 86562614cc238fd52275e8c1a9dd54b78811921f Mon Sep 17 00:00:00 2001 From: Michael Sippel <micha@fragmental.art> Date: Wed, 6 Jan 2021 21:35:46 +0100 Subject: [PATCH] refactoring --- Cargo.toml | 8 +- src/{ => core}/channel.rs | 16 ++- src/core/mod.rs | 30 ++++++ src/core/observer.rs | 149 +++++++++++++++++++++++++++ src/core/port.rs | 144 ++++++++++++++++++++++++++ src/core/view.rs | 24 +++++ src/main.rs | 127 +++++++++++++++-------- src/port.rs | 186 --------------------------------- src/string_editor.rs | 89 ++++++++-------- src/terminal/compositor.rs | 186 +++++++++++++++++++++++++++------ src/terminal/mod.rs | 20 +++- src/terminal/terminal.rs | 203 ++++++++++++++++++++++++++----------- src/view.rs | 189 ---------------------------------- src/view/grid.rs | 78 ++++++++++++++ src/view/index.rs | 73 +++++++++++++ src/view/mod.rs | 13 +++ src/view/sequence.rs | 41 ++++++++ src/view/singleton.rs | 53 ++++++++++ 18 files changed, 1071 insertions(+), 558 deletions(-) rename src/{ => core}/channel.rs (93%) create mode 100644 src/core/mod.rs create mode 100644 src/core/observer.rs create mode 100644 src/core/port.rs create mode 100644 src/core/view.rs delete mode 100644 src/port.rs delete mode 100644 src/view.rs create mode 100644 src/view/grid.rs create mode 100644 src/view/index.rs create mode 100644 src/view/mod.rs create mode 100644 src/view/sequence.rs create mode 100644 src/view/singleton.rs diff --git a/Cargo.toml b/Cargo.toml index 828e078..c9d0fc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/channel.rs b/src/core/channel.rs similarity index 93% rename from src/channel.rs rename to src/core/channel.rs index 6fe6a46..453f17c 100644 --- a/src/channel.rs +++ b/src/core/channel.rs @@ -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; diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..30c8d6c --- /dev/null +++ b/src/core/mod.rs @@ -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 + } +}; + + diff --git a/src/core/observer.rs b/src/core/observer.rs new file mode 100644 index 0000000..b02f2fe --- /dev/null +++ b/src/core/observer.rs @@ -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); + } +} + + diff --git a/src/core/port.rs b/src/core/port.rs new file mode 100644 index 0000000..66078cb --- /dev/null +++ b/src/core/port.rs @@ -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 + } +} +*/ + diff --git a/src/core/view.rs b/src/core/view.rs new file mode 100644 index 0000000..1f76892 --- /dev/null +++ b/src/core/view.rs @@ -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; +} + + diff --git a/src/main.rs b/src/main.rs index dbfb4ba..071ee4f 100644 --- a/src/main.rs +++ b/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(); } diff --git a/src/port.rs b/src/port.rs deleted file mode 100644 index 2980068..0000000 --- a/src/port.rs +++ /dev/null @@ -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))) - } -} - */ - - diff --git a/src/string_editor.rs b/src/string_editor.rs index aeb2215..85db477 100644 --- a/src/string_editor.rs +++ b/src/string_editor.rs @@ -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))); } } diff --git a/src/terminal/compositor.rs b/src/terminal/compositor.rs index a174823..c66e97d 100644 --- a/src/terminal/compositor.rs +++ b/src/terminal/compositor.rs @@ -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); } } diff --git a/src/terminal/mod.rs b/src/terminal/mod.rs index 3296bc0..f07e7eb 100644 --- a/src/terminal/mod.rs +++ b/src/terminal/mod.rs @@ -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>>; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + diff --git a/src/terminal/terminal.rs b/src/terminal/terminal.rs index 1a5c5b2..3123f38 100644 --- a/src/terminal/terminal.rs +++ b/src/terminal/terminal.rs @@ -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(()) } } diff --git a/src/view.rs b/src/view.rs deleted file mode 100644 index 8c0c915..0000000 --- a/src/view.rs +++ /dev/null @@ -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); - } - } -} - diff --git a/src/view/grid.rs b/src/view/grid.rs new file mode 100644 index 0000000..81bddf8 --- /dev/null +++ b/src/view/grid.rs @@ -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 + } + } +} + diff --git a/src/view/index.rs b/src/view/index.rs new file mode 100644 index 0000000..bed5dac --- /dev/null +++ b/src/view/index.rs @@ -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() + } +} diff --git a/src/view/mod.rs b/src/view/mod.rs new file mode 100644 index 0000000..de3db23 --- /dev/null +++ b/src/view/mod.rs @@ -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 +}; + diff --git a/src/view/sequence.rs b/src/view/sequence.rs new file mode 100644 index 0000000..0c00374 --- /dev/null +++ b/src/view/sequence.rs @@ -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 + } + } +} +*/ diff --git a/src/view/singleton.rs b/src/view/singleton.rs new file mode 100644 index 0000000..ca0b6ca --- /dev/null +++ b/src/view/singleton.rs @@ -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() + } +} +*/