build StringEditor and PosIntEditor ontop of ListEditor

add DigitEditor and CharEditor
This commit is contained in:
Michael Sippel 2021-08-16 01:27:35 +02:00
parent fef4b930ae
commit 9905a2376f
Signed by: senvas
GPG key ID: F96CF119C34B64A6
7 changed files with 301 additions and 483 deletions

View file

@ -0,0 +1,76 @@
use {
std::sync::RwLock,
termion::event::{Key, Event},
crate::{
core::{ViewPort, OuterViewPort},
singleton::{SingletonView, SingletonBuffer},
vec::VecBuffer,
terminal::{TerminalAtom, TerminalStyle, TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult},
tree_nav::{TreeNav, TreeNavResult}
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct DigitEditor {
radix: u32,
data: SingletonBuffer<Option<char>>,
data_port: ViewPort<dyn SingletonView<Item = Option<char>>>
}
impl DigitEditor {
pub fn new(radix: u32) -> Self {
let mut data_port = ViewPort::new();
DigitEditor {
radix,
data: SingletonBuffer::new(None, data_port.inner()),
data_port
}
}
pub fn get_data_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<u32>>> {
let radix = self.radix;
self.data_port.outer().map(
move |c| c.unwrap_or('?').to_digit(radix)
)
}
}
impl TreeNav for DigitEditor {}
impl TerminalEditor for DigitEditor {
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
let radix = self.radix;
self.data_port.outer().map(
move |c| TerminalAtom::new(
c.unwrap_or('?'),
if c.unwrap_or('?').to_digit(radix).is_some() {
TerminalStyle::fg_color((100, 140, 100))
} else {
TerminalStyle::fg_color((200, 0, 0))
}
)
).to_grid()
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
match event {
TerminalEvent::Input(Event::Key(Key::Char('\n'))) =>
TerminalEditorResult::Continue,
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
self.data.set(Some(*c));
TerminalEditorResult::Exit
}
TerminalEvent::Input(Event::Key(Key::Backspace)) |
TerminalEvent::Input(Event::Key(Key::Delete)) => {
self.data.set(None);
TerminalEditorResult::Exit
}
_ => TerminalEditorResult::Continue
}
}
}
struct PosIntEditor {
}

View file

@ -1,9 +1,11 @@
pub mod radix;
pub mod add;
pub mod digit;
pub use {
radix::RadixProjection,
add::Add
add::Add,
digit::DigitEditor
};

View file

@ -31,7 +31,6 @@ use {
TerminalEditorResult,
make_label
},
string_editor::StringEditor,
leveled_term_view::LeveledTermView,
list::{SExprView, ListDecoration}
}
@ -145,10 +144,29 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
fn goto_end(&mut self) -> TreeNavResult {
match self.cursor.get() {
ListEditorCursor::None |
ListEditorCursor::Insert(_) |
ListEditorCursor::Select(_) =>
self.goto(vec![ self.data.len()-1 ]),
ListEditorCursor::None => { self.goto(vec![ self.data.len() - 1 ])}
ListEditorCursor::Insert(idx) => {
if idx < self.data.len() {
self.goto(vec![ self.data.len() ])
} else {
self.up();
self.nexd();
self.dn();
self.goto_end();
TreeNavResult::Continue
}
}
ListEditorCursor::Select(idx) => {
if idx < self.data.len()-1 {
self.goto(vec![ self.data.len()-1 ])
} else {
self.up();
self.nexd();
self.dn();
self.goto_end();
TreeNavResult::Continue
}
}
ListEditorCursor::Edit(idx) => {
let ce = self.data.get_mut(idx);
let mut cur_edit = ce.write().unwrap();
@ -157,15 +175,18 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
TreeNavResult::Continue => TreeNavResult::Continue,
TreeNavResult::Exit => {
if idx+1 < self.data.len() {
self.cursor.set(ListEditorCursor::Edit(idx+1));
self.data.get_mut(idx+1).write().unwrap().goto_end();
drop(cur_edit);
self.up();
self.nexd();
self.dn();
self.goto_end();
TreeNavResult::Continue
} else {
self.cursor.set(ListEditorCursor::None);
TreeNavResult::Exit
}
}
}
}
}
}
}
@ -183,8 +204,11 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
TreeNavResult::Continue => TreeNavResult::Continue,
TreeNavResult::Exit => {
if idx > 0 {
self.cursor.set(ListEditorCursor::Edit(idx-1));
self.data.get_mut(idx-1).write().unwrap().goto_home();
drop(cur_edit);
self.up();
self.pxev();
self.dn();
self.goto_home();
TreeNavResult::Continue
} else {
self.cursor.set(ListEditorCursor::None);
@ -206,6 +230,12 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
self.data.get_mut(idx).write().unwrap().goto_home();
self.cursor.set(ListEditorCursor::Edit(idx));
}
ListEditorCursor::Edit(idx) => {
let ce = self.data.get_mut(idx);
let mut cur_edit = ce.write().unwrap();
cur_edit.dn();
}
_ => {}
}
TreeNavResult::Continue
@ -239,8 +269,11 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
match cur_edit.pxev() {
TreeNavResult::Exit => {
if idx > 0 {
self.cursor.set(ListEditorCursor::Edit(idx-1));
self.data.get_mut(idx-1).write().unwrap().goto_end();
drop(cur_edit);
self.up();
self.pxev();
self.dn();
self.goto_end();
TreeNavResult::Continue
} else {
TreeNavResult::Exit
@ -268,6 +301,7 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
self.cursor.set(ListEditorCursor::Select(idx + 1));
TreeNavResult::Continue
} else {
self.cursor.set(ListEditorCursor::None);
TreeNavResult::Exit
}
}
@ -278,8 +312,10 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
match cur_edit.nexd() {
TreeNavResult::Exit => {
if idx+1 < self.data.len() {
self.cursor.set(ListEditorCursor::Edit(idx+1));
self.data.get_mut(idx+1).write().unwrap().goto_home();
drop(cur_edit);
self.up();
self.nexd();
self.dn();
TreeNavResult::Continue
} else {//if idx+1 == self.data.len() {
TreeNavResult::Exit
@ -297,42 +333,74 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
FnMakeItemEditor: Fn() -> Arc<RwLock<SubEditor>>
{
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
self.horizontal_sexpr_view()
self.terminal_view.clone()
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
match self.cursor.get() {
ListEditorCursor::Insert(idx) => {
self.data.insert(idx, (self.make_item_editor)());
match event {
TerminalEvent::Input(Event::Key(Key::Backspace)) => {
if idx > 0 {
self.data.remove(idx-1);
self.cursor.set(ListEditorCursor::Insert(idx-1));
TerminalEditorResult::Continue
} else {
TerminalEditorResult::Exit
}
}
TerminalEvent::Input(Event::Key(Key::Delete)) => {
if idx < self.data.len() {
self.data.remove(idx);
self.cursor.set(ListEditorCursor::Insert(idx));
TerminalEditorResult::Continue
} else {
TerminalEditorResult::Exit
}
}
TerminalEvent::Input(Event::Key(Key::Insert)) => {
let l = self.data.len();
if idx < l {
self.cursor.set(ListEditorCursor::Select(idx));
} else {
self.cursor.set(ListEditorCursor::Select(l-1))
}
TerminalEditorResult::Continue
}
_ => {
let new_edit = (self.make_item_editor)();
self.data.insert(idx, new_edit.clone());
let mut ce = self.data.get_mut(idx);
let mut cur_edit = ce.write().unwrap();
cur_edit.goto_home();
self.cursor.set(ListEditorCursor::Edit(idx));
cur_edit.handle_terminal_event(event);
new_edit.write().unwrap().goto_home();
self.cursor.set(ListEditorCursor::Edit(idx));
match new_edit.write().unwrap().handle_terminal_event(event) {
TerminalEditorResult::Exit => {
self.cursor.set(ListEditorCursor::Insert(idx+1));
}
_ => {}
}
TerminalEditorResult::Continue
}
}
}
ListEditorCursor::Edit(idx) => {
let mut ce = self.data.get_mut(idx);
let mut cur_edit = ce.write().unwrap();
match event {
TerminalEvent::Input(Event::Key(Key::Char(' '))) => {
// split..
self.data.insert(idx+1, (self.make_item_editor)());
self.data.get_mut(idx).write().unwrap().goto_end();
self.data.get_mut(idx+1).write().unwrap().goto_home();
self.cursor.set(ListEditorCursor::Edit(idx+1));
}
event => {
let mut ce = self.data.get_mut(idx);
let mut cur_edit = ce.write().unwrap();
cur_edit.handle_terminal_event(event);
cur_edit.up();
self.cursor.set(ListEditorCursor::Insert(idx+1));
TerminalEditorResult::Continue
}
_ => cur_edit.handle_terminal_event(event)
}
}
ListEditorCursor::Select(idx) => {
match event {
TerminalEvent::Input(Event::Key(Key::Insert)) => {
self.cursor.set(ListEditorCursor::Insert(idx));
}
TerminalEvent::Input(Event::Key(Key::Delete)) => {
self.data.remove(idx);
@ -345,11 +413,10 @@ where SubEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
}
_=>{}
}
TerminalEditorResult::Continue
}
_ => {}
_ => TerminalEditorResult::Continue
}
TerminalEditorResult::Continue
}
}
@ -472,7 +539,7 @@ impl ListEditorView {
);
s.cast.notify_each(
begin ..= end
0 ..= s.data.len().unwrap_or(0)+1
);
}),
data: proj_helper.new_sequence_arg(
@ -502,11 +569,19 @@ impl ListEditorView {
}
}
pub enum ListEditorStyle {
HorizontalSexpr,
VerticalSexpr,
Path,
String,
Clist,
Hex
}
impl<SubEditor, FnMakeItemEditor> ListEditor<SubEditor, FnMakeItemEditor>
where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
FnMakeItemEditor: Fn() -> Arc<RwLock<SubEditor>>
{
pub fn get_seg_seq_view(&self) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
self.segment_seq
.map(
@ -538,9 +613,11 @@ where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
pub fn horizontal_sexpr_view(&self) -> OuterViewPort<dyn TerminalView> {
self.get_seg_seq_view().horizontal_sexpr_view(0)
}
pub fn vertical_sexpr_view(&self) -> OuterViewPort<dyn TerminalView> {
self.get_seg_seq_view().vertical_sexpr_view(0)
}
pub fn path_view(&self) -> OuterViewPort<dyn TerminalView> {
self.get_seg_seq_view()
.decorate("<", ">", "/", 0)
@ -553,8 +630,21 @@ where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
.to_grid_horizontal()
.flatten()
}
pub fn clist_view(&self) -> OuterViewPort<dyn TerminalView> {
self.get_seg_seq_view()
.decorate("{", "}", ", ", 1)
.to_grid_horizontal()
.flatten()
}
pub fn new(make_item_editor: FnMakeItemEditor) -> Self {
pub fn hex_view(&self) -> OuterViewPort<dyn TerminalView> {
self.get_seg_seq_view()
.decorate("0x", "", "", 0)
.to_grid_horizontal()
.flatten()
}
pub fn new(make_item_editor: FnMakeItemEditor, style: ListEditorStyle) -> Self {
let cursor_port = ViewPort::new();
let data_port = ViewPort::new();
@ -570,16 +660,30 @@ where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
segment_view_port.inner()
);
ListEditor {
let mut le = ListEditor {
data,
data_sequence_port,
cursor,
make_item_editor,
level: 0,
segment_seq: segment_view_port.outer()
}
segment_seq: segment_view_port.outer(),
terminal_view: make_label("lol"),
};
le.set_style(style);
le
}
pub fn set_style(&mut self, style: ListEditorStyle) {
self.terminal_view = match style {
ListEditorStyle::HorizontalSexpr => self.horizontal_sexpr_view(),
ListEditorStyle::VerticalSexpr => self.vertical_sexpr_view(),
ListEditorStyle::Path => self.path_view(),
ListEditorStyle::String => self.string_view(),
ListEditorStyle::Clist => self.clist_view(),
ListEditorStyle::Hex => self.hex_view()
}
}
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = Arc<RwLock<SubEditor>>>> {
self.data_sequence_port.clone()
}

View file

@ -2,6 +2,6 @@
pub mod editor;
pub mod sexpr;
pub use editor::ListEditor;
pub use editor::{ListEditor, ListEditorStyle};
pub use sexpr::{SExprView, ListDecoration};

View file

@ -34,7 +34,7 @@ impl SequenceView for ListDecorator {
fn len(&self) -> Option<usize> {
let l = self.items.len()?;
Some(if l == 0 { 2 } else { 2 * l + 2 })
Some(if l == 0 { 2 } else { 2 * l + 1 })
}
fn get(&self, idx: &usize) -> Option<Self::Item> {

View file

@ -12,271 +12,52 @@ use {
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
#[derive(Clone)]
pub struct StringEditor {
data: VecBuffer<char>,
cursor: SingletonBuffer<Option<usize>>,
data_port: ViewPort<RwLock<Vec<char>>>,
cursor_port: ViewPort<dyn SingletonView<Item = Option<usize>>>
pub struct CharEditor {
data: SingletonBuffer<Option<char>>,
data_port: ViewPort<dyn SingletonView<Item = Option<char>>>
}
impl StringEditor {
impl CharEditor {
pub fn new() -> Self {
let data_port = ViewPort::new();
let cursor_port = ViewPort::new();
StringEditor {
data: VecBuffer::new(data_port.inner()),
cursor: SingletonBuffer::new(None, cursor_port.inner()),
data_port,
cursor_port
let mut data_port = ViewPort::new();
CharEditor {
data: SingletonBuffer::new(None, data_port.inner()),
data_port
}
}
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>>> {
pub fn get_data_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<char>>> {
self.data_port.outer()
}
pub fn get_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<usize>>> {
self.cursor_port.outer()
}
pub fn insert(&mut self, c: char) -> TreeNavResult {
self.data.insert(self.cursor.get().unwrap_or(0), c);
self.nexd()
}
pub fn delete_prev(&mut self) -> TreeNavResult {
let cur = self.cursor.get().unwrap_or(0);
if cur <= self.data.len() && cur > 0 {
self.data.remove(cur-1);
}
self.pxev()
}
pub fn delete(&mut self) -> TreeNavResult {
let cur = self.cursor.get().unwrap_or(0);
if cur < self.data.len() {
self.data.remove(cur);
TreeNavResult::Continue
} else {
self.cursor.set(None);
TreeNavResult::Exit
}
}
}
impl TreeNav for StringEditor {
fn goto(&mut self, tree_pos: Vec<usize>) -> TreeNavResult {
if tree_pos.len() == 1 {
let new_pos = tree_pos[0];
if new_pos <= self.data.len() {
self.cursor.set(Some(new_pos));
TreeNavResult::Continue
} else {
self.cursor.set(None);
TreeNavResult::Exit
}
} else {
self.cursor.set(None);
TreeNavResult::Exit
}
}
fn pxev(&mut self) -> TreeNavResult {
let cur = self.cursor.get().unwrap_or(usize::MAX);
if cur > 0 {
self.cursor.set(Some(cur - 1));
TreeNavResult::Continue
} else {
self.cursor.set(None);
TreeNavResult::Exit
}
}
fn nexd(&mut self) -> TreeNavResult {
self.goto(vec![ self.cursor.get().unwrap_or(0) + 1 ])
}
fn goto_end(&mut self) -> TreeNavResult {
if self.cursor.get() == Some(self.data.len()) {
self.up()
} else {
self.goto(vec![ self.data.len() ])
}
}
fn goto_home(&mut self) -> TreeNavResult {
if self.cursor.get() == Some(0) {
self.up()
} else {
self.goto(vec![ 0 ])
}
}
fn up(&mut self) -> TreeNavResult {
self.cursor.set(None);
TreeNavResult::Exit
}
fn dn(&mut self) -> TreeNavResult {
if self.cursor.get() == Some(0) {
self.up()
} else {
self.goto(vec![0])
}
}
}
impl TerminalEditor for StringEditor {
impl TreeNav for CharEditor {}
impl TerminalEditor for CharEditor {
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
self.insert_view()
crate::terminal::make_label(
&if let Some(c) = self.data.get() {
c.to_string()
} else {
"".to_string()
})
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
match event {
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => TerminalEditorResult::Continue,
TerminalEvent::Input(Event::Key(Key::Char(c))) => match self.insert(*c) {
TreeNavResult::Exit => TerminalEditorResult::Exit,
TreeNavResult::Continue => TerminalEditorResult::Continue
TerminalEvent::Input(Event::Key(Key::Char('\n'))) =>
TerminalEditorResult::Continue,
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
self.data.set(Some(*c));
TerminalEditorResult::Exit
}
TerminalEvent::Input(Event::Key(Key::Delete)) => match self.delete() {
TreeNavResult::Exit => TerminalEditorResult::Exit,
TreeNavResult::Continue => TerminalEditorResult::Continue
}
TerminalEvent::Input(Event::Key(Key::Backspace)) => match self.delete_prev() {
TreeNavResult::Exit => TerminalEditorResult::Exit,
TreeNavResult::Continue => TerminalEditorResult::Continue
TerminalEvent::Input(Event::Key(Key::Backspace)) |
TerminalEvent::Input(Event::Key(Key::Delete)) => {
self.data.set(None);
TerminalEditorResult::Exit
}
_ => TerminalEditorResult::Continue
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub mod insert_view {
use {
std::{
sync::Arc,
cmp::{min, max}
},
cgmath::Point2,
std::sync::RwLock,
crate::{
core::{View, Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort},
terminal::{TerminalAtom, TerminalStyle, TerminalView},
grid::{GridWindowIterator},
singleton::{SingletonView},
sequence::{SequenceView},
index::{IndexView},
projection::{ProjectionHelper},
}
};
pub struct StringInsertView {
cursor: Arc<dyn SingletonView<Item = Option<usize>>>,
data: Arc<RwLock<dyn SequenceView<Item = char>>>,
cur_pos: Option<usize>,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
proj_helper: ProjectionHelper<Self>
}
impl View for StringInsertView {
type Msg = Point2<i16>;
}
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.read().unwrap();
let len = data.len().unwrap_or(0);
if i < len+1 {
return Some(
if i < self.cur_pos.unwrap_or(usize::MAX) {
TerminalAtom::from(data.get(&i)?)
} else if i == self.cur_pos.unwrap_or(usize::MAX) {
TerminalAtom::new('|', TerminalStyle::fg_color((90, 60, 200)))
} else {
TerminalAtom::from(data.get(&(i - 1))?)
}
);
}
}
None
}
fn area(&self) -> Option<Vec<Point2<i16>>> {
Some(GridWindowIterator::from(
Point2::new(0, 0) .. Point2::new(self.data.len()? as i16 + if self.cursor.get().is_some() { 1 } else { 0 }, 1)
).collect())
}
}
impl StringInsertView {
pub fn new(
cursor_port: OuterViewPort<dyn SingletonView<Item = Option<usize>>>,
data_port: OuterViewPort<dyn SequenceView<Item = char>>,
out_port: InnerViewPort<dyn TerminalView>
) -> Arc<RwLock<Self>> {
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
let proj = Arc::new(RwLock::new(
StringInsertView {
cursor: proj_helper.new_singleton_arg(
cursor_port,
|s: &mut Self, _msg| {
let old_pos = s.cur_pos.unwrap_or(0);
s.cur_pos = s.cursor.get();
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)))
}),
data: proj_helper.new_sequence_arg(
data_port,
|s: &mut Self, idx| {
s.cast.notify(&Point2::new(
if *idx < s.cur_pos.unwrap_or(0) {
*idx as i16
} else {
*idx as i16 + 1
},
0
));
}),
cur_pos: None,
cast: out_port.get_broadcast(),
proj_helper
}
));
proj.write().unwrap().proj_helper.set_proj(&proj);
out_port.set_view(Some(proj.clone()));
proj
}
}
}