refactor string editor
This commit is contained in:
parent
d1c523335b
commit
afaa9d220b
4 changed files with 319 additions and 179 deletions
61
src/main.rs
61
src/main.rs
|
@ -28,7 +28,8 @@ use {
|
||||||
TerminalCompositor
|
TerminalCompositor
|
||||||
},
|
},
|
||||||
grid::{GridOffset, GridWindowIterator},
|
grid::{GridOffset, GridWindowIterator},
|
||||||
singleton::{SingletonView, SingletonBuffer}
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
|
string_editor::{StringEditor}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,52 +53,44 @@ async fn main() {
|
||||||
\*/
|
\*/
|
||||||
|
|
||||||
let window_size_port = ViewPort::new();
|
let window_size_port = ViewPort::new();
|
||||||
let window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner());
|
let mut window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner());
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
// string editor
|
// string editor
|
||||||
let edit_port = ViewPort::<dyn TerminalView>::new();
|
let mut editor = StringEditor::new();
|
||||||
let mut editor = string_editor::StringEditor::new(edit_port.inner());
|
|
||||||
|
|
||||||
let edit_offset_port = ViewPort::<dyn TerminalView>::new();
|
let edit_view_port = ViewPort::new();
|
||||||
let edit_o = GridOffset::new(edit_offset_port.inner());
|
let edit_view =
|
||||||
|
string_editor::insert_view::StringEditView::new(
|
||||||
edit_port.add_observer(edit_o.clone());
|
editor.get_cursor_port(),
|
||||||
|
editor.get_data_port().to_sequence(),
|
||||||
compositor.push(
|
edit_view_port.inner()
|
||||||
edit_offset_port
|
|
||||||
.into_outer()
|
|
||||||
// add a nice black background
|
|
||||||
.map_item(|a| a.add_style_back(TerminalStyle::bg_color((0,0,0))))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
edit_o.write().unwrap().set_offset(Vector2::new(40, 4));
|
compositor.push(
|
||||||
|
edit_view_port.outer()
|
||||||
|
.map_item(
|
||||||
|
|_pos, atom| atom.add_style_back(
|
||||||
|
TerminalStyle::fg_color((200,200,200))
|
||||||
|
.add(TerminalStyle::bg_color((0,0,0)))
|
||||||
|
.add(TerminalStyle::bold(true)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
// Vec-Buffer
|
// another view of the string, without editor
|
||||||
let vec_port = ViewPort::new();
|
compositor.push(
|
||||||
let mut vec_buf = sequence::VecBuffer::<char>::new(vec_port.inner());
|
editor.get_data_port()
|
||||||
|
|
||||||
// project Vec-Buffer
|
|
||||||
let vec_term_view = vec_port.outer()
|
|
||||||
.to_sequence()
|
.to_sequence()
|
||||||
.to_index()
|
.to_index()
|
||||||
.map_key(
|
.map_key(
|
||||||
|idx: &usize| Point2::<i16>::new(*idx as i16, 0),
|
|idx| Point2::new(*idx as i16, 2),
|
||||||
|pt: &Point2<i16>| if pt.y == 0 { Some(pt.x as usize) } else { None }
|
|pt| if pt.y == 2 { Some(pt.x as usize) } else { None }
|
||||||
|
).map_item(
|
||||||
|
|_key, c| TerminalAtom::new(*c, TerminalStyle::fg_color((0, 200, 0)))
|
||||||
)
|
)
|
||||||
.map_item(
|
|
||||||
|c| TerminalAtom::new(*c, TerminalStyle::fg_color((200, 10, 10)))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
compositor.push(vec_term_view);
|
|
||||||
|
|
||||||
vec_buf.push('a');
|
|
||||||
vec_buf.push('b');
|
|
||||||
vec_buf.push('c');
|
|
||||||
vec_buf.insert(1, 'x');
|
|
||||||
vec_buf.remove(2);
|
|
||||||
|
|
||||||
/*\
|
/*\
|
||||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
Event Loop
|
Event Loop
|
||||||
|
@ -105,7 +98,7 @@ async fn main() {
|
||||||
\*/
|
\*/
|
||||||
loop {
|
loop {
|
||||||
match term.next_event().await {
|
match term.next_event().await {
|
||||||
TerminalEvent::Resize(size) => window_size.write().unwrap().set(size),
|
TerminalEvent::Resize(size) => window_size.set(size),
|
||||||
TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(),
|
TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(),
|
||||||
TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(),
|
TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(),
|
||||||
TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
|
TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
|
||||||
|
|
|
@ -70,19 +70,28 @@ where T: Clone + Send + Sync + 'static {
|
||||||
fn notify(&self, diff: &VecDiff<T>) {
|
fn notify(&self, diff: &VecDiff<T>) {
|
||||||
match diff {
|
match diff {
|
||||||
VecDiff::Push(_) => {
|
VecDiff::Push(_) => {
|
||||||
|
let l = {
|
||||||
let mut l = self.cur_len.write().unwrap();
|
let mut l = self.cur_len.write().unwrap();
|
||||||
self.cast.notify(&l);
|
|
||||||
*l += 1;
|
*l += 1;
|
||||||
|
*l
|
||||||
|
};
|
||||||
|
self.cast.notify(&(l - 1));
|
||||||
},
|
},
|
||||||
VecDiff::Remove(idx) => {
|
VecDiff::Remove(idx) => {
|
||||||
|
let l = {
|
||||||
let mut l = self.cur_len.write().unwrap();
|
let mut l = self.cur_len.write().unwrap();
|
||||||
*l -= 1;
|
*l -= 1;
|
||||||
self.cast.notify_each(*idx .. *l+1);
|
*l + 1
|
||||||
|
};
|
||||||
|
self.cast.notify_each(*idx .. l);
|
||||||
},
|
},
|
||||||
VecDiff::Insert{ idx, val: _ } => {
|
VecDiff::Insert{ idx, val: _ } => {
|
||||||
|
let l = {
|
||||||
let mut l = self.cur_len.write().unwrap();
|
let mut l = self.cur_len.write().unwrap();
|
||||||
*l += 1;
|
*l += 1;
|
||||||
self.cast.notify_each(*idx .. *l);
|
*l
|
||||||
|
};
|
||||||
|
self.cast.notify_each(*idx .. l);
|
||||||
},
|
},
|
||||||
VecDiff::Update{ idx, val: _ } => {
|
VecDiff::Update{ idx, val: _ } => {
|
||||||
self.cast.notify(&idx);
|
self.cast.notify(&idx);
|
||||||
|
|
|
@ -13,45 +13,52 @@ use {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SingletonBuffer<T>
|
pub struct SingletonBufferView<T: Clone + Eq + Send + Sync + 'static>(Arc<RwLock<T>>);
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
|
||||||
value: T,
|
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = T>>>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> View for SingletonBuffer<T>
|
impl<T> View for SingletonBufferView<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
type Msg = ();
|
type Msg = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SingletonView for SingletonBuffer<T>
|
impl<T> SingletonView for SingletonBufferView<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
|
||||||
fn get(&self) -> Self::Item {
|
fn get(&self) -> Self::Item {
|
||||||
self.value.clone()
|
self.0.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SingletonBuffer<T>
|
||||||
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
|
value: Arc<RwLock<T>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = T>>>>
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> SingletonBuffer<T>
|
impl<T> SingletonBuffer<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
value: T,
|
value: T,
|
||||||
port: InnerViewPort<dyn SingletonView<Item = T>>
|
port: InnerViewPort<dyn SingletonView<Item = T>>
|
||||||
) -> Arc<RwLock<Self>> {
|
) -> Self {
|
||||||
let buf = Arc::new(RwLock::new(
|
let value = Arc::new(RwLock::new(value));
|
||||||
|
port.set_view(Some(Arc::new(SingletonBufferView(value.clone()))));
|
||||||
|
|
||||||
SingletonBuffer {
|
SingletonBuffer {
|
||||||
value,
|
value,
|
||||||
cast: port.get_broadcast()
|
cast: port.get_broadcast()
|
||||||
}
|
}
|
||||||
));
|
}
|
||||||
port.set_view(Some(buf.clone()));
|
|
||||||
buf
|
pub fn get(&self) -> T {
|
||||||
|
self.value.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, new_value: T) {
|
pub fn set(&mut self, new_value: T) {
|
||||||
if self.value != new_value {
|
let mut v = self.value.write().unwrap();
|
||||||
self.value = new_value;
|
if *v != new_value {
|
||||||
|
*v = new_value;
|
||||||
|
drop(v);
|
||||||
self.cast.notify(&());
|
self.cast.notify(&());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,255 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
std::{
|
std::sync::{Arc, RwLock},
|
||||||
sync::{Arc, RwLock},
|
|
||||||
},
|
|
||||||
cgmath::Point2,
|
|
||||||
crate::{
|
crate::{
|
||||||
core::{
|
core::{ViewPort, OuterViewPort, InnerViewPort},
|
||||||
ObserverExt,
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
ObserverBroadcast,
|
sequence::VecBuffer
|
||||||
InnerViewPort
|
|
||||||
},
|
|
||||||
index::{ImplIndexView},
|
|
||||||
grid::{GridWindowIterator},
|
|
||||||
terminal::{TerminalAtom, TerminalStyle, TerminalView},
|
|
||||||
//vec_buffer::VecBuffer
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
pub struct StringEditorState {
|
pub struct StringEditor {
|
||||||
cursor: usize,
|
data: VecBuffer<char>,
|
||||||
data: Arc<RwLock<Vec<char>>>
|
cursor: SingletonBuffer<usize>,
|
||||||
|
|
||||||
|
data_port: ViewPort<RwLock<Vec<char>>>,
|
||||||
|
cursor_port: ViewPort<dyn SingletonView<Item = usize>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImplIndexView for StringEditorState {
|
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 get_data_port(&self) -> OuterViewPort<RwLock<Vec<char>>> {
|
||||||
|
self.data_port.outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = usize>> {
|
||||||
|
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(&mut self) {
|
||||||
|
let cur = self.cursor.get();
|
||||||
|
if cur < self.data.len() {
|
||||||
|
self.data.remove(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub mod insert_view {
|
||||||
|
use {
|
||||||
|
std::sync::{Arc, RwLock, Weak},
|
||||||
|
cgmath::Point2,
|
||||||
|
crate::{
|
||||||
|
core::{Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort},
|
||||||
|
singleton::SingletonView,
|
||||||
|
sequence::SequenceView,
|
||||||
|
index::ImplIndexView,
|
||||||
|
grid::GridWindowIterator,
|
||||||
|
terminal::{TerminalAtom, TerminalStyle, TerminalView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CursorObserver {
|
||||||
|
cursor: Option<Arc<dyn SingletonView<Item = usize>>>,
|
||||||
|
edit: Weak<RwLock<StringEditView>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Observer<dyn SingletonView<Item = usize>> for CursorObserver {
|
||||||
|
fn reset(&mut self, new_cursor: Option<Arc<dyn SingletonView<Item = usize>>>) {
|
||||||
|
self.cursor = new_cursor;
|
||||||
|
|
||||||
|
if let Some(cursor) = self.cursor.as_ref() {
|
||||||
|
self.edit
|
||||||
|
.upgrade().unwrap()
|
||||||
|
.write().unwrap()
|
||||||
|
.update_cursor( cursor.get() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&self, _msg: &()) {
|
||||||
|
if let Some(cursor) = self.cursor.as_ref() {
|
||||||
|
self.edit
|
||||||
|
.upgrade().unwrap()
|
||||||
|
.write().unwrap()
|
||||||
|
.update_cursor( cursor.get() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DataObserver {
|
||||||
|
data: Option<Arc<dyn SequenceView<Item = char>>>,
|
||||||
|
edit: Weak<RwLock<StringEditView>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Observer<dyn SequenceView<Item = char>> for DataObserver {
|
||||||
|
fn reset(&mut self, new_data: Option<Arc<dyn SequenceView<Item = char>>>) {
|
||||||
|
let old_len =
|
||||||
|
if let Some(data) = self.data.as_ref() {
|
||||||
|
data.len().unwrap_or(0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.data = new_data;
|
||||||
|
|
||||||
|
let new_len =
|
||||||
|
if let Some(data) = self.data.as_ref() {
|
||||||
|
data.len().unwrap_or(0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.edit
|
||||||
|
.upgrade().unwrap()
|
||||||
|
.write().unwrap()
|
||||||
|
.reset_data( std::cmp::max(old_len, new_len) );
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&self, pos: &usize) {
|
||||||
|
self.edit
|
||||||
|
.upgrade().unwrap()
|
||||||
|
.write().unwrap()
|
||||||
|
.update_data( *pos );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StringEditView {
|
||||||
|
data_obs: Option<Arc<RwLock<DataObserver>>>,
|
||||||
|
cursor_obs: Option<Arc<RwLock<CursorObserver>>>,
|
||||||
|
cur_pos: usize,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringEditView {
|
||||||
|
pub fn new(
|
||||||
|
cursor_port: OuterViewPort<dyn SingletonView<Item = usize>>,
|
||||||
|
data_port: OuterViewPort<dyn SequenceView<Item = char>>,
|
||||||
|
out_port: InnerViewPort<dyn TerminalView>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let edit_view = Arc::new(RwLock::new(
|
||||||
|
StringEditView {
|
||||||
|
data_obs: None,
|
||||||
|
cursor_obs: None,
|
||||||
|
cur_pos: 0,
|
||||||
|
cast: out_port.get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let data_obs = Arc::new(RwLock::new(
|
||||||
|
DataObserver {
|
||||||
|
data: None,
|
||||||
|
edit: Arc::downgrade(&edit_view)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
edit_view.write().unwrap().data_obs = Some(data_obs.clone());
|
||||||
|
data_port.add_observer(data_obs);
|
||||||
|
|
||||||
|
let cursor_obs = Arc::new(RwLock::new(
|
||||||
|
CursorObserver {
|
||||||
|
cursor: None,
|
||||||
|
edit: Arc::downgrade(&edit_view)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
edit_view.write().unwrap().cursor_obs = Some(cursor_obs.clone());
|
||||||
|
cursor_port.add_observer(cursor_obs);
|
||||||
|
|
||||||
|
out_port.set_view(Some(edit_view.clone()));
|
||||||
|
edit_view
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_data(&mut self, max_len: usize) {
|
||||||
|
self.cast.notify_each(GridWindowIterator::from(
|
||||||
|
Point2::new(0, 0) .. Point2::new(max_len as i16 + 1, 1)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_data(&mut self, pos: usize) {
|
||||||
|
self.cast.notify(
|
||||||
|
&Point2::new(
|
||||||
|
if pos < self.cur_pos {
|
||||||
|
pos
|
||||||
|
} else {
|
||||||
|
pos + 1
|
||||||
|
} as i16,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_cursor(&mut self, new_pos: usize) {
|
||||||
|
let old_pos = self.cur_pos;
|
||||||
|
self.cur_pos = new_pos;
|
||||||
|
|
||||||
|
self.cast.notify_each(GridWindowIterator::from(
|
||||||
|
Point2::new(std::cmp::min(old_pos,new_pos) as i16, 0) .. Point2::new(std::cmp::max(old_pos,new_pos) as i16 + 1, 1)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplIndexView for StringEditView {
|
||||||
type Key = Point2<i16>;
|
type Key = Point2<i16>;
|
||||||
type Value = TerminalAtom;
|
type Value = TerminalAtom;
|
||||||
|
|
||||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||||
let data = self.data.read().unwrap();
|
if pos.y == 0 && pos.x >= 0 {
|
||||||
|
|
||||||
if pos.y == 0 {
|
|
||||||
let i = pos.x as usize;
|
let i = pos.x as usize;
|
||||||
if i < data.len() + 3 {
|
let data =
|
||||||
|
self.data_obs.as_ref().unwrap()
|
||||||
|
.read().unwrap()
|
||||||
|
.data.clone()
|
||||||
|
.unwrap();
|
||||||
|
let len = data.len().unwrap();
|
||||||
|
|
||||||
|
if i < len+1 {
|
||||||
return Some(
|
return Some(
|
||||||
if i == 0 {
|
if i < self.cur_pos && i < len {
|
||||||
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
TerminalAtom::from(data.get(&i).unwrap())
|
||||||
} else if i-1 == self.cursor {
|
} else if i == self.cur_pos {
|
||||||
TerminalAtom::new('|', TerminalStyle::fg_color((180,200,130)).add(TerminalStyle::bold(false)))
|
TerminalAtom::new('|', TerminalStyle::fg_color((200, 0, 0)))
|
||||||
} else if i-1 == data.len()+1 {
|
|
||||||
TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130)))
|
|
||||||
} else {
|
} else {
|
||||||
TerminalAtom::new(
|
TerminalAtom::from(data.get(&(i-1)).unwrap())
|
||||||
data.get(i as usize - if i <= self.cursor { 1 } else { 2 }).unwrap().clone(),
|
|
||||||
TerminalStyle::fg_color((80,150,80)).add(TerminalStyle::bold(true))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,91 +257,20 @@ impl ImplIndexView for StringEditorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||||
Some(GridWindowIterator::from(
|
let data =
|
||||||
Point2::new(0, 0)
|
self.data_obs.as_ref().unwrap()
|
||||||
.. Point2::new(self.data.read().unwrap().len() as i16 + 3, 1)).collect())
|
.read().unwrap()
|
||||||
|
.data.clone()
|
||||||
|
.unwrap();
|
||||||
|
let len = data.len()?;
|
||||||
|
|
||||||
|
Some(
|
||||||
|
GridWindowIterator::from(
|
||||||
|
Point2::new(0, 0) .. Point2::new(len as i16 + 1, 1)
|
||||||
|
).collect()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StringEditor {
|
|
||||||
state: Arc<RwLock<StringEditorState>>,
|
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StringEditor {
|
|
||||||
pub fn new(
|
|
||||||
port: InnerViewPort<dyn TerminalView>
|
|
||||||
) -> Self {
|
|
||||||
let state = Arc::new(RwLock::new(StringEditorState{
|
|
||||||
cursor: 7,
|
|
||||||
data: Arc::new(RwLock::new("edit me".chars().collect()))
|
|
||||||
}));
|
|
||||||
|
|
||||||
let cast = port.set_view(Some(state.clone()));
|
|
||||||
|
|
||||||
StringEditor {
|
|
||||||
state,
|
|
||||||
cast
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.cast.notify_each(
|
|
||||||
(std::cmp::min(old_idx, new_idx) ..= std::cmp::max(old_idx, new_idx))
|
|
||||||
.map(|idx| Point2::new(1+idx as i16, 0))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, c: char) {
|
|
||||||
self.cast.notify_each({
|
|
||||||
let state = self.state.write().unwrap();
|
|
||||||
let mut data = state.data.write().unwrap();
|
|
||||||
|
|
||||||
data.insert(state.cursor, c);
|
|
||||||
|
|
||||||
state.cursor .. data.len()+2
|
|
||||||
}.map(|idx| Point2::new(1+idx as i16, 0)));
|
|
||||||
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&mut self) {
|
|
||||||
self.cast.notify_each({
|
|
||||||
let 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| Point2::new(1+idx as i16, 0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue