use { std::sync::RwLock, termion::event::{Key, Event}, crate::{ core::{ViewPort, OuterViewPort}, singleton::{SingletonView, SingletonBuffer}, vec::VecBuffer, terminal::{TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult} } }; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> #[derive(Clone)] pub struct StringEditor { data: VecBuffer, cursor: SingletonBuffer>, data_port: ViewPort>>, cursor_port: ViewPort>> } impl StringEditor { pub fn new() -> Self { let data_port = ViewPort::new(); let cursor_port = ViewPort::new(); StringEditor { data: VecBuffer::new(data_port.inner()), cursor: SingletonBuffer::new(None, cursor_port.inner()), data_port, cursor_port } } pub fn insert_view(&self) -> OuterViewPort { let port = ViewPort::new(); insert_view::StringInsertView::new( self.get_cursor_port(), self.get_data_port().to_sequence(), port.inner() ); port.into_outer() } pub fn get_data_port(&self) -> OuterViewPort>> { self.data_port.outer() } pub fn get_cursor_port(&self) -> OuterViewPort>> { self.cursor_port.outer() } pub fn goto(&mut self, new_pos: usize) -> TerminalEditorResult { if new_pos <= self.data.len() { self.cursor.set(Some(new_pos)); TerminalEditorResult::Continue } else { self.cursor.set(None); TerminalEditorResult::Exit } } pub fn goto_end(&mut self) -> TerminalEditorResult { self.goto(self.data.len()) } pub fn prev(&mut self) -> TerminalEditorResult { let cur = self.cursor.get().unwrap_or(usize::MAX); if cur > 0 { self.cursor.set(Some(cur - 1)); TerminalEditorResult::Continue } else { self.cursor.set(None); TerminalEditorResult::Exit } } pub fn next(&mut self) -> TerminalEditorResult { self.goto(self.cursor.get().unwrap_or(0) + 1) } pub fn insert(&mut self, c: char) -> TerminalEditorResult { self.data.insert(self.cursor.get().unwrap_or(0), c); self.next() } pub fn delete_prev(&mut self) -> TerminalEditorResult { let cur = self.cursor.get().unwrap_or(0); if cur <= self.data.len() && cur > 0 { self.data.remove(cur-1); } self.prev() } pub fn delete(&mut self) -> TerminalEditorResult { let cur = self.cursor.get().unwrap_or(0); if cur < self.data.len() { self.data.remove(cur); TerminalEditorResult::Continue } else { self.cursor.set(None); TerminalEditorResult::Exit } } } impl TerminalEditor for StringEditor { fn get_term_view(&self) -> OuterViewPort { self.insert_view() } fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult { match event { TerminalEvent::Input(Event::Key(Key::Left)) => self.prev(), TerminalEvent::Input(Event::Key(Key::Right)) => self.next(), TerminalEvent::Input(Event::Key(Key::Up)) => { self.cursor.set(None); TerminalEditorResult::Exit } TerminalEvent::Input(Event::Key(Key::Down)) | TerminalEvent::Input(Event::Key(Key::Home)) => if self.cursor.get() == Some(0) { self.cursor.set(None); TerminalEditorResult::Exit } else { self.goto(0) }, TerminalEvent::Input(Event::Key(Key::End)) => if self.cursor.get() == Some(self.data.len()) { self.cursor.set(None); TerminalEditorResult::Exit } else { self.goto_end() }, TerminalEvent::Input(Event::Key(Key::Char('\n'))) => TerminalEditorResult::Continue, TerminalEvent::Input(Event::Key(Key::Char(c))) => self.insert(*c), TerminalEvent::Input(Event::Key(Key::Delete)) => self.delete(), TerminalEvent::Input(Event::Key(Key::Backspace)) => self.delete_prev(), _ => TerminalEditorResult::Continue } } } //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> pub mod insert_view { use { std::{ sync::Arc, cmp::{min, max} }, cgmath::Point2, std::sync::RwLock, crate::{ core::{View, Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort}, terminal::{TerminalAtom, TerminalStyle, TerminalView}, grid::{GridWindowIterator}, singleton::{SingletonView}, sequence::{SequenceView}, index::{IndexView}, projection::{ProjectionHelper}, } }; pub struct StringInsertView { cursor: Arc>>, data: Arc>>, cur_pos: Option, cast: Arc>>, proj_helper: ProjectionHelper } impl View for StringInsertView { type Msg = Point2; } impl IndexView> for StringInsertView { type Item = TerminalAtom; fn get(&self, pos: &Point2) -> Option { if pos.y == 0 && pos.x >= 0 { let i = pos.x as usize; let data = self.data.read().unwrap(); let len = data.len().unwrap_or(0); if i < len+1 { return Some( if i < self.cur_pos.unwrap_or(usize::MAX) { TerminalAtom::from(data.get(&i)?) } else if i == self.cur_pos.unwrap_or(usize::MAX) { TerminalAtom::new('|', TerminalStyle::fg_color((90, 60, 200))) } else { TerminalAtom::from(data.get(&(i - 1))?) } ); } } None } fn area(&self) -> Option>> { Some(GridWindowIterator::from( Point2::new(0, 0) .. Point2::new(self.data.len()? as i16 + if self.cursor.get().is_some() { 1 } else { 0 }, 1) ).collect()) } } impl StringInsertView { pub fn new( cursor_port: OuterViewPort>>, data_port: OuterViewPort>, out_port: InnerViewPort ) -> Arc> { let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone()); let proj = Arc::new(RwLock::new( StringInsertView { cursor: proj_helper.new_singleton_arg( cursor_port, |s: &mut Self, _msg| { let old_pos = s.cur_pos.unwrap_or(0); s.cur_pos = s.cursor.get(); let new_pos = s.cur_pos.unwrap_or(0); s.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))) }), data: proj_helper.new_sequence_arg( data_port, |s: &mut Self, idx| { s.cast.notify(&Point2::new( if *idx < s.cur_pos.unwrap_or(0) { *idx as i16 } else { *idx as i16 + 1 }, 0 )); }), cur_pos: None, cast: out_port.get_broadcast(), proj_helper } )); proj.write().unwrap().proj_helper.set_proj(&proj); out_port.set_view(Some(proj.clone())); proj } } }