add terminal editor trait & string editor improvements

string editor: make cursor Option<usize>
This commit is contained in:
Michael Sippel 2021-08-11 17:39:07 +02:00
parent 261bf15b70
commit 45df06adac
Signed by: senvas
GPG key ID: F96CF119C34B64A6
2 changed files with 105 additions and 38 deletions

View file

@ -1,10 +1,11 @@
use { use {
std::sync::RwLock, std::sync::RwLock,
termion::event::{Key, Event},
crate::{ crate::{
core::{ViewPort, OuterViewPort}, core::{ViewPort, OuterViewPort},
singleton::{SingletonView, SingletonBuffer}, singleton::{SingletonView, SingletonBuffer},
vec::VecBuffer, vec::VecBuffer,
terminal::{TerminalView} terminal::{TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult}
} }
}; };
@ -13,10 +14,10 @@ use {
#[derive(Clone)] #[derive(Clone)]
pub struct StringEditor { pub struct StringEditor {
data: VecBuffer<char>, data: VecBuffer<char>,
cursor: SingletonBuffer<usize>, cursor: SingletonBuffer<Option<usize>>,
data_port: ViewPort<RwLock<Vec<char>>>, data_port: ViewPort<RwLock<Vec<char>>>,
cursor_port: ViewPort<dyn SingletonView<Item = usize>> cursor_port: ViewPort<dyn SingletonView<Item = Option<usize>>>
} }
impl StringEditor { impl StringEditor {
@ -26,7 +27,7 @@ impl StringEditor {
StringEditor { StringEditor {
data: VecBuffer::new(data_port.inner()), data: VecBuffer::new(data_port.inner()),
cursor: SingletonBuffer::new(0, cursor_port.inner()), cursor: SingletonBuffer::new(None, cursor_port.inner()),
data_port, data_port,
cursor_port cursor_port
@ -48,48 +49,99 @@ impl StringEditor {
self.data_port.outer() self.data_port.outer()
} }
pub fn get_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = usize>> { pub fn get_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<usize>>> {
self.cursor_port.outer() self.cursor_port.outer()
} }
pub fn goto(&mut self, new_pos: usize) { pub fn goto(&mut self, new_pos: usize) -> TerminalEditorResult {
if new_pos <= self.data.len() { if new_pos <= self.data.len() {
self.cursor.set(new_pos); self.cursor.set(Some(new_pos));
TerminalEditorResult::Continue
} else {
self.cursor.set(None);
TerminalEditorResult::Exit
} }
} }
pub fn goto_end(&mut self) { pub fn goto_end(&mut self) -> TerminalEditorResult {
self.cursor.set(self.data.len()); self.goto(self.data.len())
} }
pub fn prev(&mut self) { pub fn prev(&mut self) -> TerminalEditorResult {
let cur = self.cursor.get(); let cur = self.cursor.get().unwrap_or(usize::MAX);
if cur > 0 { if cur > 0 {
self.cursor.set(cur - 1); self.cursor.set(Some(cur - 1));
TerminalEditorResult::Continue
} else {
self.cursor.set(None);
TerminalEditorResult::Exit
} }
} }
pub fn next(&mut self) { pub fn next(&mut self) -> TerminalEditorResult {
self.goto(self.cursor.get() + 1); self.goto(self.cursor.get().unwrap_or(0) + 1)
} }
pub fn insert(&mut self, c: char) { pub fn insert(&mut self, c: char) -> TerminalEditorResult {
self.data.insert(self.cursor.get(), c); self.data.insert(self.cursor.get().unwrap_or(0), c);
self.next(); self.next()
} }
pub fn delete_prev(&mut self) { pub fn delete_prev(&mut self) -> TerminalEditorResult {
let cur = self.cursor.get(); let cur = self.cursor.get().unwrap_or(0);
if cur <= self.data.len() && cur > 0 { if cur <= self.data.len() && cur > 0 {
self.data.remove(cur-1); self.data.remove(cur-1);
} }
self.prev(); self.prev()
} }
pub fn delete(&mut self) { pub fn delete(&mut self) -> TerminalEditorResult {
let cur = self.cursor.get(); let cur = self.cursor.get().unwrap_or(0);
if cur < self.data.len() { if cur < self.data.len() {
self.data.remove(cur); self.data.remove(cur);
TerminalEditorResult::Continue
} else {
self.cursor.set(None);
TerminalEditorResult::Exit
}
}
}
impl TerminalEditor for StringEditor {
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
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
} }
} }
} }
@ -116,9 +168,9 @@ pub mod insert_view {
}; };
pub struct StringInsertView { pub struct StringInsertView {
cursor: Arc<dyn SingletonView<Item = usize>>, cursor: Arc<dyn SingletonView<Item = Option<usize>>>,
data: Arc<RwLock<dyn SequenceView<Item = char>>>, data: Arc<RwLock<dyn SequenceView<Item = char>>>,
cur_pos: usize, cur_pos: Option<usize>,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>, cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
proj_helper: ProjectionHelper<Self> proj_helper: ProjectionHelper<Self>
@ -139,10 +191,10 @@ pub mod insert_view {
if i < len+1 { if i < len+1 {
return Some( return Some(
if i < self.cur_pos { if i < self.cur_pos.unwrap_or(usize::MAX) {
TerminalAtom::from(data.get(&i)?) TerminalAtom::from(data.get(&i)?)
} else if i == self.cur_pos { } else if i == self.cur_pos.unwrap_or(usize::MAX) {
TerminalAtom::new('|', TerminalStyle::fg_color((0, 200, 0))) TerminalAtom::new('|', TerminalStyle::fg_color((90, 60, 200)))
} else { } else {
TerminalAtom::from(data.get(&(i - 1))?) TerminalAtom::from(data.get(&(i - 1))?)
} }
@ -155,14 +207,14 @@ pub mod insert_view {
fn area(&self) -> Option<Vec<Point2<i16>>> { fn area(&self) -> Option<Vec<Point2<i16>>> {
Some(GridWindowIterator::from( Some(GridWindowIterator::from(
Point2::new(0, 0) .. Point2::new(self.data.len()? as i16 + 1, 1) Point2::new(0, 0) .. Point2::new(self.data.len()? as i16 + if self.cursor.get().is_some() { 1 } else { 0 }, 1)
).collect()) ).collect())
} }
} }
impl StringInsertView { impl StringInsertView {
pub fn new( pub fn new(
cursor_port: OuterViewPort<dyn SingletonView<Item = usize>>, cursor_port: OuterViewPort<dyn SingletonView<Item = Option<usize>>>,
data_port: OuterViewPort<dyn SequenceView<Item = char>>, data_port: OuterViewPort<dyn SequenceView<Item = char>>,
out_port: InnerViewPort<dyn TerminalView> out_port: InnerViewPort<dyn TerminalView>
) -> Arc<RwLock<Self>> { ) -> Arc<RwLock<Self>> {
@ -173,9 +225,9 @@ pub mod insert_view {
cursor: proj_helper.new_singleton_arg( cursor: proj_helper.new_singleton_arg(
cursor_port, cursor_port,
|s: &mut Self, _msg| { |s: &mut Self, _msg| {
let old_pos = s.cur_pos; let old_pos = s.cur_pos.unwrap_or(0);
let new_pos = s.cursor.get(); s.cur_pos = s.cursor.get();
s.cur_pos = new_pos; 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))) 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)))
}), }),
@ -183,7 +235,7 @@ pub mod insert_view {
data_port, data_port,
|s: &mut Self, idx| { |s: &mut Self, idx| {
s.cast.notify(&Point2::new( s.cast.notify(&Point2::new(
if *idx < s.cur_pos { if *idx < s.cur_pos.unwrap_or(0) {
*idx as i16 *idx as i16
} else { } else {
*idx as i16 + 1 *idx as i16 + 1
@ -192,7 +244,7 @@ pub mod insert_view {
)); ));
}), }),
cur_pos: 0, cur_pos: None,
cast: out_port.get_broadcast(), cast: out_port.get_broadcast(),
proj_helper proj_helper

View file

@ -22,6 +22,18 @@ pub trait TerminalView = GridView<Item = TerminalAtom>;
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub enum TerminalEditorResult {
Continue,
Exit
}
pub trait TerminalEditor {
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView>;
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult;
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
use { use {
crate::{ crate::{
vec::VecBuffer, vec::VecBuffer,
@ -32,14 +44,17 @@ use {
pub fn make_label(s: &str) -> OuterViewPort<dyn TerminalView> { pub fn make_label(s: &str) -> OuterViewPort<dyn TerminalView> {
let label_port = ViewPort::new(); let label_port = ViewPort::new();
let _label = VecBuffer::with_data(s.chars().collect(), label_port.inner()); let mut label = VecBuffer::with_data(s.chars().collect(), label_port.inner());
label_port.outer()
let v = label_port.outer()
.to_sequence() .to_sequence()
.map(|c| TerminalAtom::from(c)) .map(|c| TerminalAtom::from(c))
.to_index() .to_index()
.map_key( .map_key(
|idx| Point2::new(*idx as i16, 0), |idx| Point2::new(*idx as i16, 0),
|pt| if pt.y == 0 { Some(pt.x as usize) } else { None } |pt| if pt.y == 0 { Some(pt.x as usize) } else { None }
) );
v
} }