From cdd06eb9b87eb65940b95e3591a536489bbc3d6e Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Mon, 14 Dec 2020 19:36:21 +0100 Subject: [PATCH] add first StringEditor --- src/main.rs | 95 ++++++++++++++--------------- src/string_editor.rs | 142 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 48 deletions(-) create mode 100644 src/string_editor.rs diff --git a/src/main.rs b/src/main.rs index 6531224..dbfb4ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ pub mod channel; pub mod singleton_buffer; pub mod vec_buffer; pub mod terminal; +pub mod string_editor; use { async_std::{task}, @@ -20,8 +21,15 @@ use { port::{ViewPort, InnerViewPort, OuterViewPort}, singleton_buffer::SingletonBuffer, vec_buffer::VecBuffer, - terminal::{Terminal, TerminalAtom, TerminalStyle, TerminalCompositor} - } + terminal::{ + Terminal, + TerminalAtom, + TerminalStyle, + TerminalCompositor, + TerminalEvent + } + }, + termion::event::{Event, Key} }; struct Fill(TerminalAtom); @@ -40,60 +48,51 @@ async fn main() { let mut compositor = TerminalCompositor::new(composite_view.inner()); task::spawn(async move { - // background + /*\ + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Setup Views + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ let fp = port::ViewPort::with_view(Arc::new(Fill(TerminalAtom::new('.', TerminalStyle::fg_color((50,50,50)))))); compositor.push(fp.outer()); - // view of Vec - let digits = port::ViewPort::new(); - let mut buf = VecBuffer::new(digits.inner()); - compositor.push( - digits.outer() - .map_value( // digit encoding - |digit| - if let Some(digit) = digit { - Some(TerminalAtom::new( - char::from_digit(digit, 16).unwrap(), - TerminalStyle::bg_color((100,30,30)).add( - TerminalStyle::fg_color((255,255,255))))) - } else { - None + let ep = port::ViewPort::new(); + let mut editor = string_editor::StringEditor::new(ep.inner()); + compositor.push(ep.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)); + } } - ) - .map_key( // a lightly tilted layout - // mapping from index to position in 2D-grid - |idx| Vector2::::new(idx as i16, idx as i16 / 2), - // reverse mapping from position to idx - |pos| pos.x as usize - )); - - // TODO: use the real terminal size... - for x in 0 .. 10 { - for y in 0 .. 10 { - 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(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, + _ => {} } } - - // now some modifications on our VecBuffer, which will automatically update the View - buf.push(0); - buf.push(10); - task::sleep(std::time::Duration::from_millis(400)).await; - buf.push(2); - buf.push(3); - task::sleep(std::time::Duration::from_millis(400)).await; - buf.push(4); - task::sleep(std::time::Duration::from_millis(400)).await; - buf.insert(0, 15); - task::sleep(std::time::Duration::from_millis(400)).await; - buf.remove(2); - task::sleep(std::time::Duration::from_millis(400)).await; - - for _ in 0 .. 4 { - buf.remove(0); - task::sleep(std::time::Duration::from_millis(400)).await; - } }); + /*\ + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Terminal Rendering + <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ Terminal::show(composite_view.into_outer()).await.ok(); } diff --git a/src/string_editor.rs b/src/string_editor.rs new file mode 100644 index 0000000..aeb2215 --- /dev/null +++ b/src/string_editor.rs @@ -0,0 +1,142 @@ +use { + std::sync::{Arc, RwLock}, + cgmath::Vector2, + crate::{ + view::{View, Observer, ObserverExt}, + port::{ViewPort, InnerViewPort, OuterViewPort}, + terminal::{TerminalAtom, TerminalStyle}, + vec_buffer::VecBuffer + } +}; + +pub struct StringEditorState { + cursor: usize, + data: Arc>> +} + +impl View for StringEditorState { + type Key = Vector2; + type Value = TerminalAtom; + + 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 { + 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(), + TerminalStyle::fg_color((80,150,80)) + ) + } + ) + } + } + + None + } +} + +pub struct StringEditor { + state: Arc>, + port: InnerViewPort, TerminalAtom> +} + +impl StringEditor { + pub fn new( + port: InnerViewPort, TerminalAtom> + ) -> Self { + let state = Arc::new(RwLock::new(StringEditorState{ + cursor: 0, + data: Arc::new(RwLock::new(Vec::new())) + })); +/* + 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(); + + 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 + } + } + + pub fn next(&mut self) { + let cur = self.state.read().unwrap().cursor; + self.goto(cur + 1); + } + + pub fn prev(&mut self) { + let cur = self.state.read().unwrap().cursor; + if cur > 0 { + self.goto(cur - 1); + } + } + + pub fn goto_end(&mut self) { + let l = self.state.read().unwrap().data.read().unwrap().len(); + self.goto(l); + } + + pub fn goto(&mut self, mut new_idx: usize) { + let old_idx = { + let mut state = self.state.write().unwrap(); + let old_idx = state.cursor.clone(); + let len = state.data.read().unwrap().len(); + new_idx = std::cmp::min(new_idx, len); + state.cursor = new_idx; + old_idx + }; + + self.port.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)) + ); + } + + pub fn insert(&mut self, c: char) { + self.port.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))); + + self.next(); + } + + pub fn delete(&mut self) { + self.port.notify_each({ + let mut state = self.state.write().unwrap(); + let mut data = state.data.write().unwrap(); + + if state.cursor < data.len() { + data.remove(state.cursor); + } + (state.cursor .. data.len()+3) + }.map(|idx| Vector2::new(1+idx as i16, 0))); + } +} +