From a2d5ea770763d3efff9e7a280db3523193bc8e91 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Fri, 5 Feb 2021 01:18:46 +0100
Subject: [PATCH] improve ProjectionArg, ProjectionHelper

---
 src/leveled_term_view.rs | 33 ++++++++------
 src/projection.rs        | 98 ++++++++++++++++++++++++++++------------
 src/string_editor.rs     | 68 ++++++++++++++++------------
 3 files changed, 127 insertions(+), 72 deletions(-)

diff --git a/src/leveled_term_view.rs b/src/leveled_term_view.rs
index e33cba4..6cb04de 100644
--- a/src/leveled_term_view.rs
+++ b/src/leveled_term_view.rs
@@ -1,20 +1,25 @@
 use {
-    std::sync::{Arc, RwLock},
+    std::{
+        sync::{Arc, RwLock},
+        collections::HashSet
+    },
     cgmath::Point2,
     crate::{
         core::{ViewPort, Observer, ObserverExt, ObserverBroadcast, InnerViewPort, OuterViewPort},
         index::{ImplIndexView},
         terminal::{TerminalAtom, TerminalView, TerminalStyle},
-        projection::ProjectionArg
+        projection::{ProjectionHelper, ProjectionArg}
     }
 };
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
 pub struct LeveledTermView {
-    src: Arc<RwLock<Option<Arc<dyn TerminalView>>>>,
-    _src_obs: Arc<RwLock<ProjectionArg<dyn TerminalView, Self>>>,
+    proj_helper: Option<ProjectionHelper<Self>>,
+
+    src: Arc<RwLock<dyn TerminalView>>,
     level: usize,
+
     cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
 }
 
@@ -31,24 +36,24 @@ impl LeveledTermView {
         src_port: OuterViewPort<dyn TerminalView>,
         dst_port: InnerViewPort<dyn TerminalView>
     ) -> Arc<RwLock<Self>> {
-        let src_obs = ProjectionArg::new(
-            // we simply forward all messages
-            |s: Arc<RwLock<Self>>, msg: &Point2<i16>| {
-                s.read().unwrap().cast.notify(msg);
-            }
-        );
-
         let v = Arc::new(RwLock::new(
             LeveledTermView {
-                src: src_obs.read().unwrap().src.clone(),
-                _src_obs: src_obs.clone(),
+                proj_helper: None,
+                src: Arc::new(RwLock::new(Option::<Arc<dyn TerminalView>>::None)),
                 level: 0,
                 cast: dst_port.get_broadcast()
             }
         ));
 
-        src_obs.write().unwrap().proj = Arc::downgrade(&v);
+        let mut projection_helper = ProjectionHelper::new(Arc::downgrade(&v));
 
+        let (src, src_obs) = projection_helper.new_arg(
+            |p: Arc<RwLock<Self>>, pos: &Point2<i16>| {
+                p.read().unwrap().cast.notify(pos);
+            });
+
+        v.write().unwrap().proj_helper = Some(projection_helper);
+        v.write().unwrap().src = src;
         src_port.add_observer(src_obs);
         dst_port.set_view(Some(v.clone()));
 
diff --git a/src/projection.rs b/src/projection.rs
index 00b7346..2e7831a 100644
--- a/src/projection.rs
+++ b/src/projection.rs
@@ -1,10 +1,12 @@
 use {
     std::{
         sync::{Arc, RwLock, Weak},
-        cmp::{max}
+        cmp::{max},
+        any::Any
     },
+    async_std::stream::StreamExt,
     crate::{
-        core::{View, Observer, ObserverExt},
+        core::{View, Observer, ObserverExt, channel::{channel, ChannelData, ChannelSender, ChannelReceiver}},
         singleton::{SingletonView},
         sequence::{SequenceView},
         index::{IndexView}
@@ -13,46 +15,84 @@ use {
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
-/// Special Observer which can access the state of the projection on notify
-/// also handles the reset() and default behaviour of unitinitalized inputs
-pub struct ProjectionArg<V, P>
-where V: View + ?Sized,
-      P: Send + Sync {
-    pub src: Arc<RwLock<Option<Arc<V>>>>,
-    pub proj: Weak<RwLock<P>>,
-    notify_fn: Box<dyn Fn(Arc<RwLock<P>>, &V::Msg) + Send + Sync>
+pub struct ProjectionHelper<P: Send + Sync + 'static> {
+    keepalive: Vec<Arc<dyn Any + Send + Sync>>,
+    proj: Weak<RwLock<P>>
 }
 
-impl<V, P> ProjectionArg<V, P>
-where V: View + ?Sized,
-      P: Send + Sync {
-    pub fn new(f: impl Fn(Arc<RwLock<P>>, &V::Msg) + Send + Sync + 'static) -> Arc<RwLock<Self>> {
-        Arc::new(RwLock::new(ProjectionArg {
-            src: Arc::new(RwLock::new(None)),
-            proj: Weak::new(),
-            notify_fn: Box::new(f)
-        }))
+impl<P: Send + Sync + 'static> ProjectionHelper<P> {
+    pub fn new(proj: Weak<RwLock<P>>) -> Self {
+        ProjectionHelper {
+            keepalive: Vec::new(),
+            proj
+        }
     }
+
+    pub fn new_arg<
+        V: View + ?Sized + 'static
+    >(
+        &mut self,
+        notify: impl Fn(Arc<RwLock<P>>, &V::Msg) + Send + Sync + 'static
+    ) -> (
+        Arc<RwLock<Option<Arc<V>>>>,
+        Arc<RwLock<ProjectionArg<V, Vec<V::Msg>>>>
+    ) where V::Msg: Send + Sync {
+        let (tx, mut rx) = channel::<Vec<V::Msg>>();
+
+        let view = Arc::new(RwLock::new(None));
+        let arg = Arc::new(RwLock::new(
+            ProjectionArg {
+                src: view.clone(),
+                sender: tx
+            }));
+
+        let proj = self.proj.clone();
+        async_std::task::spawn(async move {
+            while let Some(msg) = rx.next().await {
+                let proj = proj.upgrade().unwrap();
+                notify(proj, &msg);
+            }
+        });
+
+        self.keepalive.push(arg.clone());
+        
+        (view, arg)
+    }
+}
+
+/// Special Observer which can access the state of the projection on notify
+/// also handles the reset() and default behaviour of unitinitalized inputs
+pub struct ProjectionArg<V, D>
+where V: View + ?Sized,
+      D: ChannelData<Item = V::Msg>,
+      D::IntoIter: Send + Sync
+{
+    src: Arc<RwLock<Option<Arc<V>>>>,
+    sender: ChannelSender<D>
 }
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
-impl<Item, P> Observer<dyn SingletonView<Item = Item>> for ProjectionArg<dyn SingletonView<Item = Item>, P>
-where P: Send + Sync {
+impl<Item, D> Observer<dyn SingletonView<Item = Item>> for ProjectionArg<dyn SingletonView<Item = Item>, D>
+where D: ChannelData<Item = ()>,
+      D::IntoIter: Send + Sync
+{
     fn reset(&mut self, new_src: Option<Arc<dyn SingletonView<Item = Item>>>) {
         *self.src.write().unwrap() = new_src;
         self.notify(&());
     }
 
     fn notify(&self, msg: &()) {
-        (self.notify_fn)(self.proj.upgrade().unwrap(), msg);
+        self.sender.send(*msg);
     }
 }
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
-impl<Item, P> Observer<dyn SequenceView<Item = Item>> for ProjectionArg<dyn SequenceView<Item = Item>, P>
-where P: Send + Sync {
+impl<Item, D> Observer<dyn SequenceView<Item = Item>> for ProjectionArg<dyn SequenceView<Item = Item>, D>
+where D: ChannelData<Item = usize>,
+      D::IntoIter: Send + Sync
+{
     fn reset(&mut self, new_src: Option<Arc<dyn SequenceView<Item = Item>>>) {
         let old_len = self.src.len().unwrap_or(0);
         *self.src.write().unwrap() = new_src;
@@ -62,14 +102,16 @@ where P: Send + Sync {
     }
 
     fn notify(&self, msg: &usize) {
-        (self.notify_fn)(self.proj.upgrade().unwrap(), msg);
+        self.sender.send(*msg);
     }
 }
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
-impl<Key, Item, P> Observer<dyn IndexView<Key, Item = Item>> for ProjectionArg<dyn IndexView<Key, Item = Item>, P>
-where P: Send + Sync {
+impl<Key: Clone, Item, D> Observer<dyn IndexView<Key, Item = Item>> for ProjectionArg<dyn IndexView<Key, Item = Item>, D>
+where D: ChannelData<Item = Key>,
+      D::IntoIter: Send + Sync
+{
     fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
         let old_area = self.src.area();
         *self.src.write().unwrap() = new_src;
@@ -80,7 +122,7 @@ where P: Send + Sync {
     }
 
     fn notify(&self, msg: &Key) {
-        (self.notify_fn)(self.proj.upgrade().unwrap(), msg);
+        self.sender.send(msg.clone());
     }
 }
 
diff --git a/src/string_editor.rs b/src/string_editor.rs
index 7458ae3..5c1b887 100644
--- a/src/string_editor.rs
+++ b/src/string_editor.rs
@@ -1,5 +1,7 @@
 use {
-    std::sync::{RwLock},
+    std::{
+        sync::{RwLock}
+    },
     crate::{
         core::{ViewPort, OuterViewPort},
         singleton::{SingletonView, SingletonBuffer},
@@ -97,22 +99,27 @@ impl StringEditor {
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
 pub mod insert_view {
-    use cgmath::Point2;
-    use std::sync::{Arc, RwLock};
-    use std::cmp::{min, max};
-    use crate::{
-        core::{View, Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort},
-        terminal::{TerminalAtom, TerminalStyle, TerminalView},
-        grid::{GridWindowIterator},
-        singleton::{SingletonView},
-        sequence::{SequenceView},
-        index::{IndexView},
-        projection::ProjectionArg,
+    use {
+        std::{
+            sync::{Arc, RwLock},
+            cmp::{min, max},
+            any::Any,
+            collections::HashSet
+        },
+        cgmath::Point2,
+        crate::{
+            core::{View, Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort},
+            terminal::{TerminalAtom, TerminalStyle, TerminalView},
+            grid::{GridWindowIterator},
+            singleton::{SingletonView},
+            sequence::{SequenceView},
+            index::{IndexView},
+            projection::{ProjectionHelper, ProjectionArg},
+        }
     };
 
     pub struct StringInsertView {
-        _cursor_obs: Arc<RwLock<ProjectionArg<dyn SingletonView<Item = usize>, Self>>>,
-        _data_obs: Arc<RwLock<ProjectionArg<dyn SequenceView<Item = char>, Self>>>,
+        proj_helper: Option<ProjectionHelper<Self>>,
 
         cursor: Arc<dyn SingletonView<Item = usize>>,
         data: Arc<RwLock<dyn SequenceView<Item = char>>>,
@@ -162,7 +169,19 @@ pub mod insert_view {
             data_port: OuterViewPort<dyn SequenceView<Item = char>>,
             out_port: InnerViewPort<dyn TerminalView>
         ) -> Arc<RwLock<Self>> {
-            let cursor_obs = ProjectionArg::new(
+            let proj = Arc::new(RwLock::new(
+                StringInsertView {
+                    proj_helper: None,
+                    cursor: Arc::new(Option::<Arc<dyn SingletonView<Item = usize>>>::None),
+                    data: Arc::new(RwLock::new(Option::<Arc<dyn SequenceView<Item = char>>>::None)),
+                    cur_pos: 0,
+                    cast: out_port.get_broadcast()
+                }
+            ));
+
+            let mut projection_helper = ProjectionHelper::new(Arc::downgrade(&proj));
+
+            let (cursor, cursor_obs) = projection_helper.new_arg(
                 |s: Arc<RwLock<Self>>, _msg| {
                     let old_pos = s.read().unwrap().cur_pos;
                     let new_pos = s.read().unwrap().cursor.get();
@@ -170,7 +189,7 @@ pub mod insert_view {
                     s.read().unwrap().cast.notify_each(GridWindowIterator::from(Point2::new(min(old_pos, new_pos) as i16,0) ..= Point2::new(max(old_pos, new_pos) as i16, 0)))
                 });
 
-            let data_obs = ProjectionArg::new(
+            let (data, data_obs) = projection_helper.new_arg(
                 |s: Arc<RwLock<Self>>, idx| {
                     s.read().unwrap().cast.notify(&Point2::new(
                         if *idx < s.read().unwrap().cur_pos {
@@ -182,20 +201,9 @@ pub mod insert_view {
                     ));
                 });
 
-            let proj = Arc::new(RwLock::new(
-                StringInsertView {
-                    _cursor_obs: cursor_obs.clone(),
-                    _data_obs: data_obs.clone(),
-
-                    cursor: cursor_obs.read().unwrap().src.clone(),
-                    data: data_obs.read().unwrap().src.clone(),
-                    cur_pos: 0,
-                    cast: out_port.get_broadcast()
-                }
-            ));
-
-            cursor_obs.write().unwrap().proj = Arc::downgrade(&proj);
-            data_obs.write().unwrap().proj = Arc::downgrade(&proj);
+            proj.write().unwrap().proj_helper = Some(projection_helper);
+            proj.write().unwrap().cursor = cursor;
+            proj.write().unwrap().data = data;
 
             cursor_port.add_observer(cursor_obs);
             data_port.add_observer(data_obs);