add ProjectionArg helper to simplify projection views with multiple inputs
This commit is contained in:
parent
3514e41432
commit
b896dd897a
7 changed files with 364 additions and 188 deletions
|
@ -17,7 +17,7 @@ pub trait GridView = IndexView<Point2<i16>>;
|
|||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Item> GridView<Item = Item> {
|
||||
impl<Item> dyn GridView<Item = Item> {
|
||||
pub fn range(&self) -> RangeInclusive<Point2<i16>> {
|
||||
let area = self.area().unwrap_or(Vec::new());
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ impl<Key, V: IndexView<Key>> IndexView<Key> for Option<V> {
|
|||
type Item = V::Item;
|
||||
|
||||
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||
(self.as_ref()? as &V).get(key)
|
||||
self.as_ref()?.get(key)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
|
|
88
src/leveled_term_view.rs
Normal file
88
src/leveled_term_view.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use {
|
||||
std::sync::{Arc, RwLock},
|
||||
cgmath::Point2,
|
||||
crate::{
|
||||
core::{ViewPort, Observer, ObserverExt, ObserverBroadcast, InnerViewPort, OuterViewPort},
|
||||
index::{ImplIndexView},
|
||||
terminal::{TerminalAtom, TerminalView, TerminalStyle},
|
||||
projection::ProjectionArg
|
||||
}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct LeveledTermView {
|
||||
src: Arc<RwLock<Option<Arc<dyn TerminalView>>>>,
|
||||
_src_obs: Arc<RwLock<ProjectionArg<dyn TerminalView, Self>>>,
|
||||
level: usize,
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
||||
}
|
||||
|
||||
impl LeveledTermView {
|
||||
pub fn new(
|
||||
src: OuterViewPort<dyn TerminalView>
|
||||
) -> (Arc<RwLock<Self>>, OuterViewPort<dyn TerminalView>) {
|
||||
let port = ViewPort::new();
|
||||
let v = Self::with_port(src, port.inner());
|
||||
(v, port.into_outer())
|
||||
}
|
||||
|
||||
pub fn with_port(
|
||||
src_port: OuterViewPort<dyn TerminalView>,
|
||||
dst_port: InnerViewPort<dyn TerminalView>
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let src_obs = ProjectionArg::new(
|
||||
// we simply forward all messages
|
||||
|s: Arc<RwLock<Self>>, msg: &Point2<i16>| {
|
||||
s.read().unwrap().cast.notify(msg);
|
||||
}
|
||||
);
|
||||
|
||||
let v = Arc::new(RwLock::new(
|
||||
LeveledTermView {
|
||||
src: src_obs.read().unwrap().src.clone(),
|
||||
_src_obs: src_obs.clone(),
|
||||
level: 0,
|
||||
cast: dst_port.get_broadcast()
|
||||
}
|
||||
));
|
||||
|
||||
src_obs.write().unwrap().proj = Arc::downgrade(&v);
|
||||
|
||||
src_port.add_observer(src_obs);
|
||||
dst_port.set_view(Some(v.clone()));
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn set_level(&mut self, l: usize) {
|
||||
self.level = l;
|
||||
|
||||
// update complete area
|
||||
if let Some(a) = self.src.area() {
|
||||
self.cast.notify_each(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplIndexView for LeveledTermView {
|
||||
type Key = Point2<i16>;
|
||||
type Value = TerminalAtom;
|
||||
|
||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
self.src.get(pos).map(
|
||||
|a| a.add_style_front(
|
||||
if self.level > 0 {
|
||||
TerminalStyle::bold(true)
|
||||
.add(TerminalStyle::bg_color((0, 0, 0)))
|
||||
} else {
|
||||
TerminalStyle::bold(false)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
self.src.area()
|
||||
}
|
||||
}
|
||||
|
103
src/main.rs
103
src/main.rs
|
@ -7,7 +7,9 @@ pub mod grid;
|
|||
pub mod sequence;
|
||||
pub mod singleton;
|
||||
pub mod terminal;
|
||||
pub mod projection;
|
||||
pub mod string_editor;
|
||||
pub mod leveled_term_view;
|
||||
|
||||
use {
|
||||
async_std::{task},
|
||||
|
@ -17,7 +19,7 @@ use {
|
|||
cgmath::{Vector2, Point2},
|
||||
termion::event::{Event, Key},
|
||||
crate::{
|
||||
core::{View, Observer, ObserverExt, ViewPort},
|
||||
core::{View, Observer, ObserverExt, ObserverBroadcast, ViewPort},
|
||||
index::{ImplIndexView},
|
||||
terminal::{
|
||||
TerminalView,
|
||||
|
@ -29,7 +31,8 @@ use {
|
|||
},
|
||||
grid::{GridOffset, GridWindowIterator},
|
||||
singleton::{SingletonView, SingletonBuffer},
|
||||
string_editor::{StringEditor}
|
||||
string_editor::{StringEditor, insert_view::StringInsertView},
|
||||
leveled_term_view::LeveledTermView
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -56,24 +59,28 @@ async fn main() {
|
|||
let mut window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner());
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
// string editor
|
||||
// string editor 1
|
||||
let mut editor = StringEditor::new();
|
||||
|
||||
let edit_view_port = ViewPort::new();
|
||||
let edit_view =
|
||||
string_editor::insert_view::StringEditView::new(
|
||||
editor.get_cursor_port(),
|
||||
editor.get_data_port().to_sequence(),
|
||||
edit_view_port.inner()
|
||||
);
|
||||
|
||||
let (leveled_edit_view, leveled_edit_view_port) = LeveledTermView::new(editor.insert_view());
|
||||
compositor.push(
|
||||
edit_view_port.outer()
|
||||
leveled_edit_view_port
|
||||
.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)))
|
||||
move |_pos, atom| atom.add_style_back(
|
||||
TerminalStyle::fg_color((200,200,200))))
|
||||
);
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
// string editor 2
|
||||
let mut editor2 = StringEditor::new();
|
||||
let (leveled_edit2_view, leveled_edit2_view_port) = LeveledTermView::new(editor2.insert_view());
|
||||
compositor.push(
|
||||
leveled_edit2_view_port
|
||||
.map_item(
|
||||
move |_pos, atom| atom.add_style_back(
|
||||
TerminalStyle::fg_color((200,200,200))))
|
||||
.map_key(
|
||||
|p| p + Vector2::new(0, 1),
|
||||
|p| Some(p - Vector2::new(0, 1))
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -84,29 +91,68 @@ async fn main() {
|
|||
.to_sequence()
|
||||
.to_index()
|
||||
.map_key(
|
||||
|idx| Point2::new(*idx as i16, 2),
|
||||
|pt| if pt.y == 2 { Some(pt.x as usize) } else { None }
|
||||
|idx| Point2::new(*idx as i16, 2 + *idx as i16),
|
||||
|pt| if pt.x == pt.y-2 { Some(pt.x as usize) } else { None }
|
||||
).map_item(
|
||||
|_key, c| TerminalAtom::new(*c, TerminalStyle::fg_color((0, 200, 0)))
|
||||
|_key, c| TerminalAtom::new(*c, TerminalStyle::fg_color((80, 20, 180)).add(TerminalStyle::bg_color((40,10,90))))
|
||||
)
|
||||
);
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
// welcome message
|
||||
for c in "Welcome!".chars() {
|
||||
editor.insert(c);
|
||||
task::sleep(std::time::Duration::from_millis(80)).await;
|
||||
}
|
||||
|
||||
task::sleep(std::time::Duration::from_millis(500)).await;
|
||||
|
||||
for c in "Use arrow keys to navigate.".chars() {
|
||||
editor2.insert(c);
|
||||
task::sleep(std::time::Duration::from_millis(80)).await;
|
||||
}
|
||||
|
||||
|
||||
/*\
|
||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
Event Loop
|
||||
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
\*/
|
||||
|
||||
let mut sel = 0;
|
||||
|
||||
leveled_edit_view.write().unwrap().set_level(if sel == 0 {1} else {0});
|
||||
leveled_edit2_view.write().unwrap().set_level(if sel == 1 {1} else {0});
|
||||
|
||||
loop {
|
||||
let ed = match sel {
|
||||
0 => &mut editor,
|
||||
1 => &mut editor2,
|
||||
_ => &mut editor2
|
||||
};
|
||||
|
||||
match term.next_event().await {
|
||||
TerminalEvent::Resize(size) => window_size.set(size),
|
||||
TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(),
|
||||
TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(),
|
||||
TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0),
|
||||
TerminalEvent::Input(Event::Key(Key::End)) => editor.goto_end(),
|
||||
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
||||
sel = 0;
|
||||
|
||||
leveled_edit_view.write().unwrap().set_level(if sel == 0 {1} else {0});
|
||||
leveled_edit2_view.write().unwrap().set_level(if sel == 1 {1} else {0});
|
||||
},
|
||||
TerminalEvent::Input(Event::Key(Key::Down)) => {
|
||||
sel = 1;
|
||||
|
||||
leveled_edit_view.write().unwrap().set_level(if sel == 0 {1} else {0});
|
||||
leveled_edit2_view.write().unwrap().set_level(if sel == 1 {1} else {0});
|
||||
},
|
||||
TerminalEvent::Input(Event::Key(Key::Left)) => ed.prev(),
|
||||
TerminalEvent::Input(Event::Key(Key::Right)) => ed.next(),
|
||||
TerminalEvent::Input(Event::Key(Key::Home)) => ed.goto(0),
|
||||
TerminalEvent::Input(Event::Key(Key::End)) => ed.goto_end(),
|
||||
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {},
|
||||
TerminalEvent::Input(Event::Key(Key::Char(c))) => editor.insert(c),
|
||||
TerminalEvent::Input(Event::Key(Key::Delete)) => editor.delete(),
|
||||
TerminalEvent::Input(Event::Key(Key::Backspace)) => { editor.prev(); editor.delete(); },
|
||||
TerminalEvent::Input(Event::Key(Key::Char(c))) => ed.insert(c),
|
||||
TerminalEvent::Input(Event::Key(Key::Delete)) => ed.delete(),
|
||||
TerminalEvent::Input(Event::Key(Key::Backspace)) => ed.delete_prev(),
|
||||
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => break,
|
||||
_ => {}
|
||||
}
|
||||
|
@ -155,7 +201,7 @@ impl ImplIndexView for TermLabel {
|
|||
|
||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
if pos.y == 5 {
|
||||
Some(TerminalAtom::from(self.0.chars().nth(pos.x as usize)?))
|
||||
Some(TerminalAtom::new(self.0.chars().nth(pos.x as usize)?, TerminalStyle::fg_color((255, 255, 255))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -189,4 +235,3 @@ impl ImplIndexView for ScrambleBackground {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
86
src/projection.rs
Normal file
86
src/projection.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use {
|
||||
std::{
|
||||
sync::{Arc, RwLock, Weak},
|
||||
cmp::{max}
|
||||
},
|
||||
crate::{
|
||||
core::{View, Observer, ObserverExt},
|
||||
singleton::{SingletonView},
|
||||
sequence::{SequenceView},
|
||||
index::{IndexView}
|
||||
}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
/// Special Observer which can access the state of the projection on notify
|
||||
/// also handles the reset() and default behaviour of unitinitalized inputs
|
||||
pub struct ProjectionArg<V, P>
|
||||
where V: View + ?Sized,
|
||||
P: Send + Sync {
|
||||
pub src: Arc<RwLock<Option<Arc<V>>>>,
|
||||
pub proj: Weak<RwLock<P>>,
|
||||
notify_fn: Box<dyn Fn(Arc<RwLock<P>>, &V::Msg) + Send + Sync>
|
||||
}
|
||||
|
||||
impl<V, P> ProjectionArg<V, P>
|
||||
where V: View + ?Sized,
|
||||
P: Send + Sync {
|
||||
pub fn new(f: impl Fn(Arc<RwLock<P>>, &V::Msg) + Send + Sync + 'static) -> Arc<RwLock<Self>> {
|
||||
Arc::new(RwLock::new(ProjectionArg {
|
||||
src: Arc::new(RwLock::new(None)),
|
||||
proj: Weak::new(),
|
||||
notify_fn: Box::new(f)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Item, P> Observer<dyn SingletonView<Item = Item>> for ProjectionArg<dyn SingletonView<Item = Item>, P>
|
||||
where P: Send + Sync {
|
||||
fn reset(&mut self, new_src: Option<Arc<dyn SingletonView<Item = Item>>>) {
|
||||
*self.src.write().unwrap() = new_src;
|
||||
self.notify(&());
|
||||
}
|
||||
|
||||
fn notify(&self, msg: &()) {
|
||||
(self.notify_fn)(self.proj.upgrade().unwrap(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Item, P> Observer<dyn SequenceView<Item = Item>> for ProjectionArg<dyn SequenceView<Item = Item>, P>
|
||||
where P: Send + Sync {
|
||||
fn reset(&mut self, new_src: Option<Arc<dyn SequenceView<Item = Item>>>) {
|
||||
let old_len = self.src.len().unwrap_or(0);
|
||||
*self.src.write().unwrap() = new_src;
|
||||
let new_len = self.src.len().unwrap_or(0);
|
||||
|
||||
self.notify_each(0 .. max(old_len, new_len));
|
||||
}
|
||||
|
||||
fn notify(&self, msg: &usize) {
|
||||
(self.notify_fn)(self.proj.upgrade().unwrap(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Key, Item, P> Observer<dyn IndexView<Key, Item = Item>> for ProjectionArg<dyn IndexView<Key, Item = Item>, P>
|
||||
where P: Send + Sync {
|
||||
fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
|
||||
let old_area = self.src.area();
|
||||
*self.src.write().unwrap() = new_src;
|
||||
let new_area = self.src.area();
|
||||
|
||||
if let Some(area) = old_area { self.notify_each(area); }
|
||||
if let Some(area) = new_area { self.notify_each(area); }
|
||||
}
|
||||
|
||||
fn notify(&self, msg: &Key) {
|
||||
(self.notify_fn)(self.proj.upgrade().unwrap(), msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ pub trait SingletonView : View<Msg = ()> {
|
|||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<V: SingletonView> SingletonView for RwLock<V> {
|
||||
impl<V: SingletonView + ?Sized> SingletonView for RwLock<V> {
|
||||
type Item = V::Item;
|
||||
|
||||
fn get(&self) -> Self::Item {
|
||||
|
@ -28,7 +28,7 @@ impl<V: SingletonView> SingletonView for RwLock<V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: SingletonView> SingletonView for Arc<V> {
|
||||
impl<V: SingletonView + ?Sized> SingletonView for Arc<V> {
|
||||
type Item = V::Item;
|
||||
|
||||
fn get(&self) -> Self::Item {
|
||||
|
@ -36,6 +36,19 @@ impl<V: SingletonView> SingletonView for Arc<V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: SingletonView> SingletonView for Option<V>
|
||||
where V::Item: Default{
|
||||
type Item = V::Item;
|
||||
|
||||
fn get(&self) -> Self::Item {
|
||||
if let Some(s) = self.as_ref() {
|
||||
s.get()
|
||||
} else {
|
||||
V::Item::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
/*
|
||||
pub trait ImplSingletonView : Send + Sync {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use {
|
||||
std::sync::{Arc, RwLock},
|
||||
std::sync::{RwLock},
|
||||
crate::{
|
||||
core::{ViewPort, OuterViewPort, InnerViewPort},
|
||||
core::{ViewPort, OuterViewPort},
|
||||
singleton::{SingletonView, SingletonBuffer},
|
||||
sequence::VecBuffer
|
||||
sequence::VecBuffer,
|
||||
terminal::{TerminalView}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -32,6 +33,17 @@ impl StringEditor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||
let port = ViewPort::new();
|
||||
insert_view::StringInsertView::new(
|
||||
self.get_cursor_port(),
|
||||
self.get_data_port().to_sequence(),
|
||||
port.inner()
|
||||
);
|
||||
|
||||
port.into_outer()
|
||||
}
|
||||
|
||||
pub fn get_data_port(&self) -> OuterViewPort<RwLock<Vec<char>>> {
|
||||
self.data_port.outer()
|
||||
}
|
||||
|
@ -66,6 +78,14 @@ impl StringEditor {
|
|||
self.next();
|
||||
}
|
||||
|
||||
pub fn delete_prev(&mut self) {
|
||||
let cur = self.cursor.get();
|
||||
if cur <= self.data.len() && cur > 0 {
|
||||
self.data.remove(cur-1);
|
||||
}
|
||||
self.prev();
|
||||
}
|
||||
|
||||
pub fn delete(&mut self) {
|
||||
let cur = self.cursor.get();
|
||||
if cur < self.data.len() {
|
||||
|
@ -77,166 +97,49 @@ impl StringEditor {
|
|||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
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}
|
||||
}
|
||||
use cgmath::Point2;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::cmp::{min, max};
|
||||
use crate::{
|
||||
core::{View, Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort},
|
||||
terminal::{TerminalAtom, TerminalStyle, TerminalView},
|
||||
grid::{GridWindowIterator},
|
||||
singleton::{SingletonView},
|
||||
sequence::{SequenceView},
|
||||
index::{IndexView},
|
||||
projection::ProjectionArg,
|
||||
};
|
||||
|
||||
struct CursorObserver {
|
||||
cursor: Option<Arc<dyn SingletonView<Item = usize>>>,
|
||||
edit: Weak<RwLock<StringEditView>>
|
||||
}
|
||||
pub struct StringInsertView {
|
||||
_cursor_obs: Arc<RwLock<ProjectionArg<dyn SingletonView<Item = usize>, Self>>>,
|
||||
_data_obs: Arc<RwLock<ProjectionArg<dyn SequenceView<Item = char>, Self>>>,
|
||||
|
||||
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 = self.data.len().unwrap_or(0);
|
||||
self.data = new_data;
|
||||
let new_len = self.data.len().unwrap_or(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>>>,
|
||||
cursor: Arc<dyn SingletonView<Item = usize>>,
|
||||
data: Arc<dyn SequenceView<Item = char>>,
|
||||
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 View for StringInsertView {
|
||||
type Msg = Point2<i16>;
|
||||
}
|
||||
|
||||
impl ImplIndexView for StringEditView {
|
||||
type Key = Point2<i16>;
|
||||
type Value = TerminalAtom;
|
||||
|
||||
impl IndexView<Point2<i16>> for StringInsertView {
|
||||
type Item = TerminalAtom;
|
||||
|
||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
if pos.y == 0 && pos.x >= 0 {
|
||||
let i = pos.x as usize;
|
||||
let data =
|
||||
self.data_obs.as_ref().unwrap()
|
||||
.read().unwrap()
|
||||
.data.clone()
|
||||
.unwrap();
|
||||
let len = data.len().unwrap();
|
||||
let len = self.data.len().unwrap_or(0);
|
||||
|
||||
if i < len+1 {
|
||||
return Some(
|
||||
if i < self.cur_pos && i < len {
|
||||
TerminalAtom::from(data.get(&i).unwrap())
|
||||
TerminalAtom::from(self.data.get(&i).unwrap())
|
||||
} else if i == self.cur_pos {
|
||||
TerminalAtom::new('|', TerminalStyle::fg_color((200, 0, 0)))
|
||||
} else {
|
||||
TerminalAtom::from(data.get(&(i-1)).unwrap())
|
||||
TerminalAtom::from(self.data.get(&(i-1)).unwrap())
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -246,18 +149,59 @@ pub mod insert_view {
|
|||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
let data =
|
||||
self.data_obs.as_ref().unwrap()
|
||||
.read().unwrap()
|
||||
.data.clone()
|
||||
.unwrap();
|
||||
let len = data.len()?;
|
||||
Some(GridWindowIterator::from(
|
||||
Point2::new(0, 0) .. Point2::new(self.data.len()? as i16 + 1, 1)
|
||||
).collect())
|
||||
}
|
||||
}
|
||||
|
||||
Some(
|
||||
GridWindowIterator::from(
|
||||
Point2::new(0, 0) .. Point2::new(len as i16 + 1, 1)
|
||||
).collect()
|
||||
)
|
||||
impl StringInsertView {
|
||||
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 cursor_obs = ProjectionArg::new(
|
||||
|s: Arc<RwLock<Self>>, _msg| {
|
||||
let old_pos = s.read().unwrap().cur_pos;
|
||||
let new_pos = s.read().unwrap().cursor.get();
|
||||
s.write().unwrap().cur_pos = new_pos;
|
||||
s.read().unwrap().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)))
|
||||
});
|
||||
|
||||
let data_obs = ProjectionArg::new(
|
||||
|s: Arc<RwLock<Self>>, idx| {
|
||||
s.read().unwrap().cast.notify(&Point2::new(
|
||||
if *idx < s.read().unwrap().cur_pos {
|
||||
*idx as i16
|
||||
} else {
|
||||
*idx as i16 + 1
|
||||
},
|
||||
0
|
||||
));
|
||||
});
|
||||
|
||||
let proj = Arc::new(RwLock::new(
|
||||
StringInsertView {
|
||||
_cursor_obs: cursor_obs.clone(),
|
||||
_data_obs: data_obs.clone(),
|
||||
|
||||
cursor: cursor_obs.read().unwrap().src.clone(),
|
||||
data: data_obs.read().unwrap().src.clone(),
|
||||
cur_pos: 0,
|
||||
cast: out_port.get_broadcast()
|
||||
}
|
||||
));
|
||||
|
||||
cursor_obs.write().unwrap().proj = Arc::downgrade(&proj);
|
||||
data_obs.write().unwrap().proj = Arc::downgrade(&proj);
|
||||
|
||||
cursor_port.add_observer(cursor_obs);
|
||||
data_port.add_observer(data_obs);
|
||||
|
||||
out_port.set_view(Some(proj.clone()));
|
||||
|
||||
proj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue