diff --git a/src/main.rs b/src/main.rs index 6e2f903..dbe96f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ pub mod grid; pub mod sequence; pub mod singleton; pub mod terminal; - pub mod string_editor; use { @@ -29,24 +28,162 @@ use { Terminal, TerminalCompositor }, - grid::GridOffset + grid::{GridOffset, GridWindowIterator}, + singleton::{SingletonView, SingletonBuffer} } }; -struct VecSequenceView(Arc>>); -impl ImplIndexView for VecSequenceView { - type Key = usize; - type Value = T; +struct TermLabel(String); +impl ImplIndexView for TermLabel { + type Key = Point2; + type Value = Option; - fn get(&self, idx: &usize) -> T { - self.0.read().unwrap()[*idx].clone() + fn get(&self, pos: &Point2) -> Option { + if pos.y == 5 { + Some(TerminalAtom::from(self.0.chars().nth(pos.x as usize)?)) + } else { + None + } } - fn range(&self) -> Option> { - Some(0 .. self.0.read().unwrap().len()) + fn area(&self) -> Option>> { + Some( + GridWindowIterator::from( + Point2::new(0, 5) .. Point2::new(self.0.chars().count() as i16, 6) + ).collect() + ) } } +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[async_std::main] +async fn main() { + 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 { + /*\ + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Setup Views + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ + + let window_size_port = ViewPort::new(); + let window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner()); + + + //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + // string editor + let edit_port = ViewPort::::new(); + let mut editor = string_editor::StringEditor::new(edit_port.inner()); + + compositor.push(edit_port.outer().map_key( + |pt| pt + Vector2::new(4, 2), + |pt| Some(pt - Vector2::new(4, 2)) + )); + + let edit_offset_port = ViewPort::::new(); + let edit_o = GridOffset::new(edit_offset_port.inner()); + + edit_port.add_observer(edit_o.clone()); + + compositor.push( + edit_offset_port + .into_outer() + // add a nice black background + .map_item(|atom| atom.map( + |a| a.add_style_back(TerminalStyle::bg_color((0,0,0))))) + ); + + edit_o.write().unwrap().set_offset(Vector2::new(40, 4)); + + + //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + // stupid label animation + let label_port = ViewPort::::new(); + compositor.push( + label_port.outer() + .map_item( + |atom| atom.map(|atom| + atom.add_style_back(TerminalStyle::fg_color((255, 255, 255))) + .add_style_back(TerminalStyle::bg_color((0, 0, 0)))) + ) + ); + task::spawn(async move { + loop { + label_port.set_view(Some(Arc::new(TermLabel(String::from("Hello"))))); + task::sleep(std::time::Duration::from_secs(1)).await; + label_port.set_view(Some(Arc::new(TermLabel(String::from("I'm a dynamic label"))))); + task::sleep(std::time::Duration::from_secs(1)).await; + } + }); + + //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + // Vec-Buffer + let vec_port = ViewPort::new(); + let mut vec_buf = sequence::VecBuffer::::new(vec_port.inner()); + + // project Vec-Buffer to SequenceView + let vec_seq_port = ViewPort::new(); + let vec_seq = sequence::VecSequence::new(vec_seq_port.inner()); + vec_port.add_observer(vec_seq.clone()); + + let vec_term_view = vec_seq_port.outer() + .to_index() + .map_key( + |idx: &usize| Point2::::new(*idx as i16, 0), + |pt: &Point2| if pt.y == 0 { Some(pt.x as usize) } else { None } + ) + .map_item( + |c| Some(TerminalAtom::new(c.clone()?, TerminalStyle::fg_color((200, 10, 10)))) + ); + + compositor.push(vec_term_view); + + vec_buf.push('a'); + vec_buf.push('b'); + vec_buf.push('c'); + + /*\ + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Event Loop + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ + loop { + match term.next_event().await { + TerminalEvent::Resize(size) => window_size.write().unwrap().set(size), + 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); vec_buf.push(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 + } + _ => {} + } + } + }); + + /*\ + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Terminal Rendering + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ + term_writer.show().await.ok(); +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + struct Checkerboard; impl ImplIndexView for Checkerboard { type Key = Point2; @@ -66,11 +203,13 @@ impl ImplIndexView for Checkerboard { } } - fn range(&self) -> Option>> { - Some(Point2::new(0,0) .. Point2::new(20,10)) + fn area(&self) -> Option>> { + Some(GridWindowIterator::from(Point2::new(0,0) .. Point2::new(20,10)).collect()) } } +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + struct ScrambleBackground; impl ImplIndexView for ScrambleBackground { type Key = Point2; @@ -84,88 +223,10 @@ impl ImplIndexView for ScrambleBackground { } } - fn range(&self) -> Option>> { + fn area(&self) -> Option>> { None //Some(Point2::new(0,0) .. Point2::new(50,30)) } } -#[async_std::main] -async fn main() { - 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 { - /*\ - <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - Setup Views - <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - \*/ - - let offset_port = ViewPort::::new(); - let o = GridOffset::new(offset_port.inner()); - - let checkerboard_port = ViewPort::::with_view(Arc::new(Checkerboard)); - checkerboard_port.add_observer(o.clone()); - - compositor.push(offset_port.into_outer()); - - let edit_port = ViewPort::::new(); - let mut editor = string_editor::StringEditor::new(edit_port.inner()); - - let edit_offset_port = ViewPort::::new(); - let edit_o = GridOffset::new(edit_offset_port.inner()); - edit_port.add_observer(edit_o.clone()); - - compositor.push( - edit_offset_port - .into_outer() - // add a nice black background - .map_item(|atom| atom.map( - |a| a.add_style_back(TerminalStyle::bg_color((0,0,0)))))); - - edit_o.write().unwrap().set_offset(Vector2::new(40, 4)); - - task::spawn(async move { - for x in 0 .. 20 { - async_std::task::sleep(std::time::Duration::from_millis(15)).await; - o.write().unwrap().set_offset(Vector2::new(x as i16, x as i16)); - } - }); - - /*\ - <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - Event Loop - <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - \*/ - loop { - match term.next_event().await { - 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 - } - _ => {} - } - } - }); - - /*\ - <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - Terminal Rendering - <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - \*/ - term_writer.show().await.ok(); -} diff --git a/src/sequence/vec_buffer.rs b/src/sequence/vec_buffer.rs new file mode 100644 index 0000000..91f003f --- /dev/null +++ b/src/sequence/vec_buffer.rs @@ -0,0 +1,162 @@ +use { + std::{ + sync::{Arc, RwLock} + }, + crate::{ + core::{View, Observer, ObserverExt, ObserverBroadcast, InnerViewPort}, + sequence::SequenceView, + } +}; + +pub enum VecDiff { + Push(T), + Remove(usize), + Insert{ idx: usize, val: T }, + Update{ idx: usize, val: T } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for Vec +where T: Clone + Send + Sync + 'static { + type Msg = VecDiff; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct VecSequence +where T: Clone + Send + Sync + 'static { + cur_len: RwLock, + data: Option>>>, + cast: Arc>>> +} + +impl VecSequence +where T: Clone + Send + Sync + 'static { + pub fn new( + port: InnerViewPort> + ) -> Arc> { + let seq = Arc::new(RwLock::new( + VecSequence { + cur_len: RwLock::new(0), + data: None, + cast: port.get_broadcast() + } + )); + port.set_view(Some(seq.clone())); + seq + } +} + +impl Observer>> for VecSequence +where T: Clone + Send + Sync + 'static { + fn reset(&mut self, view: Option>>>) { + let old_len = self.len().unwrap(); + self.data = view; + let new_len = self.len().unwrap(); + self.cast.notify_each(0 .. std::cmp::max(old_len, new_len)); + } + + fn notify(&self, diff: &VecDiff) { + match diff { + VecDiff::Push(_) => { + let mut l = self.cur_len.write().unwrap(); + self.cast.notify(&l); + *l += 1; + }, + VecDiff::Remove(idx) => { + self.cast.notify(&idx); + *self.cur_len.write().unwrap() -= 1; + }, + VecDiff::Insert{ idx, val } => { + let mut l = self.cur_len.write().unwrap(); + *l += 1; + //self.cast.notify_each(idx .. &*l); + }, + VecDiff::Update{ idx, val } => { + self.cast.notify(&idx); + } + } + } +} + +impl View for VecSequence +where T: Clone + Send + Sync + 'static { + type Msg = usize; +} + +impl SequenceView for VecSequence +where T: Clone + Send + Sync + 'static { + type Item = T; + + fn get(&self, idx: usize) -> T { + self.data.as_ref().unwrap() + .read().unwrap()[idx].clone() + } + + fn len(&self) -> Option { + Some(*self.cur_len.read().unwrap()) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct VecBuffer +where T: Clone + Send + Sync + 'static { + data: Arc>>, + cast: Arc>>>> +} + +impl VecBuffer +where T: Clone + Send + Sync + 'static { + pub fn with_data( + data: Vec, + port: InnerViewPort>> + ) -> Self { + let data = Arc::new(RwLock::new(data)); + port.set_view(Some(data.clone())); + VecBuffer { data, cast: port.get_broadcast() } + } + + pub fn new(port: InnerViewPort>>) -> Self { + VecBuffer::with_data(Vec::new(), port) + } + + pub fn apply_diff(&mut self, diff: VecDiff) { + match diff { + VecDiff::Push(val) => self.push(val), + VecDiff::Remove(idx) => self.remove(idx), + VecDiff::Insert{ idx, val } => self.insert(idx, val), + VecDiff::Update{ idx, val } => self.update(idx, val) + } + } + + pub fn len(&self) -> usize { + self.data.read().unwrap().len() + } + + pub fn get(&self, idx: usize) -> T { + self.data.read().unwrap()[idx].clone() + } + + pub fn push(&mut self, val: T) { + self.data.write().unwrap().push(val.clone()); + self.cast.notify(&VecDiff::Push(val)); + } + + pub fn remove(&mut self, idx: usize) { + self.data.write().unwrap().remove(idx); + self.cast.notify(&VecDiff::Remove(idx)); + } + + pub fn insert(&mut self, idx: usize, val: T) { + self.data.write().unwrap().insert(idx, val.clone()); + self.cast.notify(&VecDiff::Insert{ idx, val }); + } + + pub fn update(&mut self, idx: usize, val: T) { + self.data.write().unwrap()[idx] = val.clone(); + self.cast.notify(&VecDiff::Update{ idx, val }); + } +} + diff --git a/src/singleton/mod.rs b/src/singleton/mod.rs index f02ea29..812154b 100644 --- a/src/singleton/mod.rs +++ b/src/singleton/mod.rs @@ -9,6 +9,8 @@ use { crate::core::{View} }; +pub use buffer::SingletonBuffer; + // TODO: #[ImplForArc, ImplForRwLock] pub trait SingletonView : View { type Item; diff --git a/src/vec_buffer.rs b/src/vec_buffer.rs deleted file mode 100644 index 4b5445f..0000000 --- a/src/vec_buffer.rs +++ /dev/null @@ -1,67 +0,0 @@ -use { - std::{ - sync::{Arc, RwLock} - }, - crate::{ - view::{View, Observer}, - port::{InnerViewPort} - } -}; - -impl View for Vec { - type Key = usize; - type Value = T; - - fn view(&self, key: usize) -> Option { - self.get(key).cloned() - } -} - -pub struct VecBuffer { - data: Arc>>, - port: InnerViewPort -} - -impl VecBuffer { - pub fn new(port: InnerViewPort) -> Self { - let data = Arc::new(RwLock::new(Vec::new())); - port.set_view(data.clone()); - VecBuffer { data, port } - } - - pub fn push(&mut self, val: T) { - self.port.notify({ - let mut d = self.data.write().unwrap(); - let idx = d.len(); - d.push(val); - idx - }); - } - - pub fn remove(&mut self, idx: usize) { - let len = { - let mut d = self.data.write().unwrap(); - let len = d.len(); - d.remove(idx); - len - }; - - for i in idx .. len { - self.port.notify(i); - } - } - - pub fn insert(&mut self, idx: usize, val: T) { - let len = { - let mut d = self.data.write().unwrap(); - d.insert(idx, val); - let len = d.len(); - len - }; - - for i in idx .. len { - self.port.notify(i); - } - } -} -