add terminal editor trait & string editor improvements
string editor: make cursor Option<usize>
This commit is contained in:
parent
261bf15b70
commit
45df06adac
2 changed files with 105 additions and 38 deletions
|
@ -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,13 +27,13 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_view(&self) -> OuterViewPort<dyn TerminalView> {
|
pub fn insert_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
let port = ViewPort::new();
|
let port = ViewPort::new();
|
||||||
insert_view::StringInsertView::new(
|
insert_view::StringInsertView::new(
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue