add first StringEditor

This commit is contained in:
Michael Sippel 2020-12-14 19:36:21 +01:00
parent c776a1a08e
commit cdd06eb9b8
Signed by: senvas
GPG key ID: F96CF119C34B64A6
2 changed files with 189 additions and 48 deletions

View file

@ -8,6 +8,7 @@ pub mod channel;
pub mod singleton_buffer; pub mod singleton_buffer;
pub mod vec_buffer; pub mod vec_buffer;
pub mod terminal; pub mod terminal;
pub mod string_editor;
use { use {
async_std::{task}, async_std::{task},
@ -20,8 +21,15 @@ use {
port::{ViewPort, InnerViewPort, OuterViewPort}, port::{ViewPort, InnerViewPort, OuterViewPort},
singleton_buffer::SingletonBuffer, singleton_buffer::SingletonBuffer,
vec_buffer::VecBuffer, vec_buffer::VecBuffer,
terminal::{Terminal, TerminalAtom, TerminalStyle, TerminalCompositor} terminal::{
Terminal,
TerminalAtom,
TerminalStyle,
TerminalCompositor,
TerminalEvent
} }
},
termion::event::{Event, Key}
}; };
struct Fill(TerminalAtom); struct Fill(TerminalAtom);
@ -40,60 +48,51 @@ async fn main() {
let mut compositor = TerminalCompositor::new(composite_view.inner()); let mut compositor = TerminalCompositor::new(composite_view.inner());
task::spawn(async move { task::spawn(async move {
// background /*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Setup Views
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
let fp = port::ViewPort::with_view(Arc::new(Fill(TerminalAtom::new('.', TerminalStyle::fg_color((50,50,50)))))); let fp = port::ViewPort::with_view(Arc::new(Fill(TerminalAtom::new('.', TerminalStyle::fg_color((50,50,50))))));
compositor.push(fp.outer()); compositor.push(fp.outer());
// view of Vec<u32> let ep = port::ViewPort::new();
let digits = port::ViewPort::new(); let mut editor = string_editor::StringEditor::new(ep.inner());
let mut buf = VecBuffer::new(digits.inner()); compositor.push(ep.outer());
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
}
)
.map_key( // a lightly tilted layout
// mapping from index to position in 2D-grid
|idx| Vector2::<i16>::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 { 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)); fp.inner().notify(Vector2::new(x,y));
} }
} }
},
// now some modifications on our VecBuffer, which will automatically update the View TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(),
buf.push(0); TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(),
buf.push(10); TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
task::sleep(std::time::Duration::from_millis(400)).await; TerminalEvent::Input(Event::Key(Key::End)) => editor.goto_end(),
buf.push(2); TerminalEvent::Input(Event::Key(Key::Char(c))) => editor.insert(c),
buf.push(3); TerminalEvent::Input(Event::Key(Key::Delete)) => editor.delete(),
task::sleep(std::time::Duration::from_millis(400)).await; TerminalEvent::Input(Event::Key(Key::Backspace)) => { editor.prev(); editor.delete(); },
buf.push(4); TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => break,
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(); Terminal::show(composite_view.into_outer()).await.ok();
} }

142
src/string_editor.rs Normal file
View file

@ -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<RwLock<Vec<char>>>
}
impl View for StringEditorState {
type Key = Vector2<i16>;
type Value = TerminalAtom;
fn view(&self, pos: Vector2<i16>) -> Option<TerminalAtom> {
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<RwLock<StringEditorState>>,
port: InnerViewPort<Vector2<i16>, TerminalAtom>
}
impl StringEditor {
pub fn new(
port: InnerViewPort<Vector2<i16>, 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)));
}
}