use { std::sync::{RwLock}, crate::{ core::{ViewPort, OuterViewPort}, singleton::{SingletonView, SingletonBuffer}, sequence::VecBuffer, terminal::{TerminalView} } }; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> #[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(0, 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) { if new_pos <= self.data.len() { self.cursor.set(new_pos); } } pub fn goto_end(&mut self) { self.cursor.set(self.data.len()); } pub fn prev(&mut self) { let cur = self.cursor.get(); if cur > 0 { self.cursor.set(cur - 1); } } pub fn next(&mut self) { self.goto(self.cursor.get() + 1); } pub fn insert(&mut self, c: char) { self.data.insert(self.cursor.get(), c); self.next(); } pub fn delete_prev(&mut self) { let cur = self.cursor.get(); if cur <= self.data.len() && cur > 0 { self.data.remove(cur-1); } self.prev(); } pub fn delete(&mut self) { let cur = self.cursor.get(); if cur < self.data.len() { self.data.remove(cur); } } } //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> 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, }; pub struct StringInsertView { _cursor_obs: Arc, Self>>>, _data_obs: Arc, Self>>>, cursor: Arc>, data: Arc>>, cur_pos: usize, cast: Arc>> } 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 { TerminalAtom::from(data.get(&i)?) } else if i == self.cur_pos { TerminalAtom::new('|', TerminalStyle::fg_color((200, 0, 0))) } 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 + 1, 1) ).collect()) } } impl StringInsertView { pub fn new( cursor_port: OuterViewPort>, data_port: OuterViewPort>, out_port: InnerViewPort ) -> Arc> { let cursor_obs = ProjectionArg::new( |s: Arc>, _msg| { let old_pos = s.read().unwrap().cur_pos; let new_pos = s.read().unwrap().cursor.get(); s.write().unwrap().cur_pos = new_pos; 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( |s: Arc>, idx| { s.read().unwrap().cast.notify(&Point2::new( if *idx < s.read().unwrap().cur_pos { *idx as i16 } else { *idx as i16 + 1 }, 0 )); }); 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); cursor_port.add_observer(cursor_obs); data_port.add_observer(data_obs); out_port.set_view(Some(proj.clone())); proj } } }