From 86562614cc238fd52275e8c1a9dd54b78811921f Mon Sep 17 00:00:00 2001 From: Michael Sippel 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(Arc>>); //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> -impl Observer for ChannelSender +impl ChannelSender 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> Observer for ChannelSender +where V::Msg: Clone, Data::IntoIter: Send + Sync { + fn notify(&self, msg: &V::Msg) { + self.send(msg.clone()); + } +} + impl Clone for ChannelSender { 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 : Send + Sync { + fn reset(&self, view: Option>) {} + fn notify(&self, msg: &V::Msg); +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl> Observer for RwLock { + fn notify(&self, msg: &V::Msg) { + self.read().unwrap().notify(&msg); + } +} + +impl> Observer for Arc { + fn notify(&self, msg: &V::Msg) { + self.deref().notify(&msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub trait ObserverExt : Observer { + fn notify_each(&self, it: impl IntoIterator); +} + +impl> ObserverExt for T { + fn notify_each(&self, it: impl IntoIterator) { + for msg in it { + self.notify(&msg); + } + } +} + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Broadcast +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ + +pub struct ObserverBroadcast { + observers: Vec>> +} + +impl ObserverBroadcast { + pub fn new() -> Self { + ObserverBroadcast { + observers: Vec::new() + } + } + + pub fn add_observer(&mut self, obs: Weak>) { + self.cleanup(); + self.observers.push(obs); + } + + fn cleanup(&mut self) { + self.observers.retain(|o| o.strong_count() > 0); + } + + fn iter(&self) -> impl Iterator>> + '_ { + self.observers.iter().filter_map(|o| o.upgrade()) + } +} + +impl Observer for ObserverBroadcast { + fn reset(&self, view: Option>) { + 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 +where V: View + ?Sized, + F: Fn(&V::Msg) + Send + Sync { + f: F, + _phantom: std::marker::PhantomData +} + +impl NotifyFnObserver +where V: View + ?Sized, + F: Fn(&V::Msg) + Send + Sync { + pub fn new(f: F) -> Self { + NotifyFnObserver { + f, + _phantom: std::marker::PhantomData + } + } +} + +impl Observer for NotifyFnObserver +where V: View + ?Sized, + F: Fn(&V::Msg) + Send + Sync { + fn notify(&self, msg: &V::Msg) { + (self.f)(msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct ResetFnObserver +where V: View + ?Sized, + F: Fn(Option>) + Send + Sync { + f: F, + _phantom: std::marker::PhantomData +} + +impl ResetFnObserver +where V: View + ?Sized, + F: Fn(Option>) + Send + Sync { + pub fn new(f: F) -> Self { + ResetFnObserver { + f, + _phantom: std::marker::PhantomData + } + } +} + +impl Observer for ResetFnObserver +where V: View + ?Sized, + F: Fn(Option>) + Send + Sync { + fn notify(&self, msg: &V::Msg) {} + fn reset(&self, view: Option>) { + (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 { + view: Arc>>>, + observers: Arc>> +} + +impl ViewPort { + pub fn new() -> Self { + ViewPort { + view: Arc::new(RwLock::new(None)), + observers: Arc::new(RwLock::new(ObserverBroadcast::new())) + } + } + + pub fn with_view(view: Arc) -> Self { + let port = ViewPort::new(); + port.set_view(Some(view)); + port + } + + pub fn set_view(&self, view: Option>) { + *self.view.write().unwrap() = view.clone(); + self.observers.read().unwrap().reset(view); + } + + pub fn add_observer(&self, observer: Arc>) { + self.observers.write().unwrap().add_observer(Arc::downgrade(&observer)); + observer.reset(self.view.read().unwrap().clone()); + } + + pub fn inner(&self) -> InnerViewPort { + InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) + } + + pub fn outer(&self) -> OuterViewPort { + OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) + } + + pub fn into_inner(self) -> InnerViewPort { + InnerViewPort(ViewPort{ view: self.view, observers: self.observers }) + } + + pub fn into_outer(self) -> OuterViewPort { + OuterViewPort(ViewPort{ view: self.view, observers: self.observers }) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub struct InnerViewPort(ViewPort); + +#[derive(Clone)] +pub struct OuterViewPort(ViewPort); + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl InnerViewPort { + pub fn get_broadcast(&self) -> Arc>> { + self.0.observers.clone() + } + + pub fn set_view(&self, view: Option>) -> Arc>> { + self.0.set_view(view); + self.get_broadcast() + } + + pub fn get_view(&self) -> Option> { + self.0.view.read().unwrap().clone() + } + + pub fn notify(&self, msg: &V::Msg) { + self.0.observers.read().unwrap().notify(msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort { + pub fn get_view(&self) -> Option> { + self.0.view.read().unwrap().clone() + } + + pub fn get_view_arc(&self) -> Arc>>> { + self.0.view.clone() + } + + pub fn add_observer(&self, observer: Arc>) -> Arc>>> { + self.0.add_observer(observer); + self.get_view_arc() + } + +/* + pub fn add_reset_fn(&self, reset: impl Fn(Option>) + Send + Sync + 'static) -> Arc>>> { + self.add_observer(Arc::new(ResetFnObserver::new(reset))) + } + + pub fn add_notify_fn(&self, notify: impl Fn(&V::Msg) + Send + Sync + 'static) -> Arc>>> { + self.add_observer(Arc::new(NotifyFnObserver::new(notify))) + } +*/ +} + +/* +impl OuterViewPort +where V::Msg: Clone { + pub fn into_stream( + self, + reset: impl Fn(Option>, ChannelSender) + Send + Sync + 'static + ) -> ChannelReceiver + where Data: ChannelData + 'static, + Data::IntoIter: Send + Sync + 'static + { + let (s, r) = crate::core::channel::channel::(); + 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 View for RwLock { + type Msg = V::Msg; +} + +impl View for Arc { + 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; - type Value = TerminalAtom; +struct VecSequenceView(Arc>>); +impl ImplIndexView for VecSequenceView { + type Key = usize; + type Value = T; - fn view(&self, _: Vector2) -> Option { - Some(self.0.clone()) + fn get(&self, idx: &usize) -> T { + self.0.read().unwrap()[*idx].clone() + } + + fn range(&self) -> Option> { + Some(0 .. self.0.read().unwrap().len()) + } +} + +struct Checkerboard; +impl ImplIndexView for Checkerboard { + type Key = Point2; + type Value = Option; + + fn get(&self, pos: &Point2) -> Option { + 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>> { + Some(Point2::new(0,0) .. Point2::new(20,10)) + } +} + +struct ScrambleBackground; +impl ImplIndexView for ScrambleBackground { + type Key = Point2; + type Value = Option; + + fn get(&self, pos: &Point2) -> Option { + 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>> { + 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::::new(); + + let mut compositor = TerminalCompositor::new(term_port.inner()); + compositor.push(ViewPort::::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::::with_view(Arc::new(Checkerboard)).into_outer()); + let edit_port = ViewPort::::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 { - view: Arc>>>>, - observers: Arc>>>> -} - -impl ViewPort -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>) -> Self { - ViewPort { - view: Arc::new(RwLock::new(Some(view))), - observers: Arc::new(RwLock::new(Vec::new())) - } - } - - pub fn set_view(&self, view: Arc>) { - *self.view.write().unwrap() = Some(view); - } - - pub fn add_observer(&self, observer: Arc>) { - self.observers.write().unwrap().push(observer); - } - - pub fn inner(&self) -> InnerViewPort { - InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) - } - - pub fn outer(&self) -> OuterViewPort { - OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) - } - - pub fn into_inner(self) -> InnerViewPort { - InnerViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) - } - - pub fn into_outer(self) -> OuterViewPort { - OuterViewPort(ViewPort{ view: self.view.clone(), observers: self.observers.clone() }) - } -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - -#[derive(Clone)] -pub struct InnerViewPort(ViewPort); - -#[derive(Clone)] -pub struct OuterViewPort(ViewPort); - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - -impl OuterViewPort { - pub fn get_view(&self) -> Arc>>>> { - self.0.view.clone() - } - - pub fn add_observer(self, observer: Arc>) -> Arc>>>> { - self.0.add_observer(observer); - self.0.view - } - - pub fn add_observer_fn(self, obs_fn: impl Fn(K) + Send + Sync + 'static) -> Arc>>>> { - self.add_observer(Arc::new(FnObserver::new(obs_fn))) - } -} - -impl OuterViewPort { - pub fn stream(self) -> ChannelReceiver> { - let (s, r) = crate::channel::set_channel(); - self.0.add_observer(Arc::new(s)); - r - } -} - -impl OuterViewPort { - pub fn map_value< - V2: Clone + Send + Sync + 'static, - F: Fn(Option) -> Option + Send + Sync + 'static - >( - self, - f: F - ) -> OuterViewPort { - 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 { - 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 InnerViewPort { - pub fn set_view(&self, view: Arc + Send + Sync>) { - *self.0.view.write().unwrap() = Some(view); - } - - pub fn set_view_fn(&self, view_fn: impl Fn(K) -> Option + Send + Sync + 'static) { - self.set_view(Arc::new(FnView::new(view_fn))) - } -} - -impl Observer for InnerViewPort -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 { - actions: Vec>> -} - -impl StreamPort { - async fn set_stream(&self, stream: impl Stream) -> 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>> } -impl View for StringEditorState { - type Key = Vector2; - type Value = TerminalAtom; +impl ImplIndexView for StringEditorState { + type Key = Point2; + type Value = Option; + + fn get(&self, pos: &Point2) -> Option { + let data = self.data.read().unwrap(); - fn view(&self, pos: Vector2) -> Option { 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>> { + Some( + Point2::new(0, 0) + .. Point2::new(self.data.read().unwrap().len() as i16 + 3, 1) + ) } } pub struct StringEditor { state: Arc>, - port: InnerViewPort, TerminalAtom> + cast: Arc>> } impl StringEditor { pub fn new( - port: InnerViewPort, TerminalAtom> + port: InnerViewPort ) -> 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, Value = TerminalAtom>>>>>, - port: Arc, TerminalAtom>> +struct CompositeLayer { + comp: Weak>, + idx: usize, + view: RwLock>> } -impl TerminalCompositor { - pub fn new(port: InnerViewPort, TerminalAtom>) -> Self { - let layers = Arc::new(RwLock::new(Vec::, Value = TerminalAtom>>>::new())); +impl Observer for CompositeLayer { + fn reset(&self, view: Option>) { + 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) { + self.comp + .upgrade().unwrap() + .read().unwrap() + .cast.notify(pos); + } +} + +pub struct TerminalCompositeView { + idx_count: usize, + layers: HashMap>, + range: Option>>, + cast: Arc>> +} + +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; + type Value = Option; + + fn get(&self, pos: &Point2) -> Option { + 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, TerminalAtom>) { - self.layers.write().unwrap().push(v.add_observer(self.port.clone())); - } - - pub fn make_port(&mut self) -> InnerViewPort, TerminalAtom> { - let port = ViewPort::new(); - self.push(port.outer()); - port.inner() + fn range(&self) -> Option>> { + self.range.clone() + } +} + +pub struct TerminalCompositor { + view: Arc>, + port: InnerViewPort +} + +impl TerminalCompositor { + pub fn new( + port: InnerViewPort + ) -> 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) { + 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>; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + 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, + observer: Arc, + events: ChannelReceiver>, - 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 + ) -> 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 { + self.writer.clone() + } + pub async fn next_event(&mut self) -> TerminalEvent { self.events.next().await.unwrap() } +} - pub async fn show(view_port: OuterViewPort, TerminalAtom>) -> std::io::Result<()> { - let (atom_tx, atom_rx) = crate::channel::queue_channel(); +struct TermOutObserver { + dirty_pos_tx: ChannelSender>>, + writer: Arc +} - let view = view_port.get_view(); - view_port.add_observer_fn(move |pos| atom_tx.notify((pos, view.view(pos)))); +impl Observer for TermOutObserver { + fn reset(&self, view: Option>) { + 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, Option)>>) -> std::io::Result<()> { - let mut out = MouseTerminal::from(stdout().into_raw_mode().unwrap()); - let mut cur_pos = Vector2::::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) { + self.dirty_pos_tx.send(*pos); + } +} + +pub struct TermOutWriter { + out: RwLock>>, + dirty_pos_rx: ChannelReceiver>>, + view: Arc>>> +} + +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::::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; -} - -pub trait Observer : Send + Sync { - type Msg; - - fn notify(&self, key: Self::Msg); -} - -pub trait ObserverExt : Observer { - fn notify_each(&self, it: impl IntoIterator); -} - -impl ObserverExt for T { - fn notify_each(&self, it: impl IntoIterator) { - for msg in it { - self.notify(msg); - } - } -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - -use cgmath::Vector2; - -pub trait SingletonView = View; -pub trait SingletonObserver = Observer; - -pub trait SequenceView = View; -pub trait SequenceObserver = Observer; - -pub trait GridView = View>; -pub trait GridObserver = Observer>; - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - -pub struct FnView -where K: Send + Sync, - V: Send + Sync, - F: Fn(K) -> Option + Send + Sync { - f: F, - _phantom0: std::marker::PhantomData, - _phantom1: std::marker::PhantomData -} - -impl FnView -where K: Send + Sync, - V: Send + Sync, - F: Fn(K) -> Option + Send + Sync { - pub fn new(f: F) -> Self { - FnView { - f, - _phantom0: std::marker::PhantomData, - _phantom1: std::marker::PhantomData - } - } -} - -impl View for FnView -where K: Send + Sync, - V: Send + Sync, - F: Fn(K) -> Option + Send + Sync { - type Key = K; - type Value = V; - - fn view(&self, key: K) -> Option { - (self.f)(key) - } -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - -pub struct FnObserver -where T: Send + Sync, - F: Fn(T) + Send + Sync { - f: F, - _phantom: std::marker::PhantomData -} - -impl FnObserver -where T: Send + Sync, - F: Fn(T) + Send + Sync { - pub fn new(f: F) -> Self { - FnObserver { - f, - _phantom: std::marker::PhantomData - } - } -} - -impl Observer for FnObserver -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 View for RwLock { - type Key = T::Key; - type Value = T::Value; - - fn view(&self, key: T::Key) -> Option { - self.read().unwrap().view(key) - } -} - -impl Observer for RwLock { - type Msg = T::Msg; - - fn notify(&self, msg: T::Msg) { - self.read().unwrap().notify(msg) - } -} - -impl View for Arc { - type Key = T::Key; - type Value = T::Value; - - fn view(&self, key: T::Key) -> Option { - self.deref().view(key) - } -} - -impl Observer for Arc { - type Msg = T::Msg; - - fn notify(&self, msg: T::Msg) { - self.deref().notify(msg) - } -} - -impl View for Arc> -where K: Send + Sync, - V: Send + Sync { - type Key = K; - type Value = V; - - fn view(&self, key: K) -> Option { - self.deref().view(key) - } -} - -impl Observer for Arc> -where T: Send + Sync { - type Msg = T; - - fn notify(&self, msg: T) { - self.deref().notify(msg) - } -} - -impl View for Option { - type Key = T::Key; - type Value = T::Value; - - fn view(&self, key: T::Key) -> Option { - if let Some(view) = self.as_ref() { - view.view(key) - } else { - None - } - } -} - -impl Observer for Option { - 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>; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> +/* +pub trait ImplGridView : Send + Sync { + type Item; + + fn get(&self, pos: &Point2) -> Self::Item; + + fn range(&self) -> Option>> { + None + } +} + +impl ImplIndexView for V { + type Key = Point2; + type Value = V::Item; + + fn get(&self, pos: &Point2) -> V::Item { + (self as &V).get(pos) + } + + fn range(&self) -> Option>> { + (self as &V).range() + } +} +*/ +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct GridWindowIterator { + next: Point2, + range: Range> +} + +impl From>> for GridWindowIterator { + fn from(range: Range>) -> Self { + GridWindowIterator { + next: range.start, + range + } + } +} + +impl Iterator for GridWindowIterator { + type Item = Point2; + + fn next(&mut self) -> Option> { + 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 : View { + type Item; + + fn get(&self, key: &Key) -> Self::Item; + + fn range(&self) -> Option> { + None + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl> IndexView for RwLock { + type Item = V::Item; + + fn get(&self, key: &Key) -> Self::Item { + self.read().unwrap().get(key) + } + + fn range(&self) -> Option> { + self.read().unwrap().range() + } +} + +impl> IndexView for Arc { + type Item = V::Item; + + fn get(&self, key: &Key) -> Self::Item { + self.deref().get(key) + } + + fn range(&self) -> Option> { + self.deref().range() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub trait ImplIndexView : Send + Sync { + type Key; + type Value; + + fn get(&self, key: &Self::Key) -> Self::Value; + fn range(&self) -> Option> { + None + } +} + +impl View for V { + type Msg = V::Key; +} + +impl IndexView for V { + type Item = V::Value; + + fn get(&self, key: &V::Key) -> Self::Item { + (self as &V).get(key) + } + + fn range(&self) -> Option> { + (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; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> +/* +pub trait ImplSequenceView : Send + Sync { + type Item; + + fn get(&self, idx: usize) -> Self::Item; + fn len(&self) -> Option { + None + } +} + +impl 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> { + 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 { + type Item; + + fn get(&self) -> Self::Item; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl SingletonView for RwLock { + type Item = V::Item; + + fn get(&self) -> Self::Item { + self.read().unwrap().get() + } +} + +impl SingletonView for Arc { + 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 View for V { + type Msg = (); +} + +impl SingletonView for V { + type Item = V::Item; + + fn get(&self) -> Self::Item { + (self as &V).get() + } +} +*/