add first StringEditor
This commit is contained in:
parent
c776a1a08e
commit
cdd06eb9b8
2 changed files with 189 additions and 48 deletions
89
src/main.rs
89
src/main.rs
|
@ -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
142
src/string_editor.rs
Normal 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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue