lib-nested/lib-nested-tty/src/editors/list.rs

304 lines
10 KiB
Rust

use {
r3vi::{
view::{ViewPort, OuterViewPort, sequence::*},
projection::decorate_sequence::*,
},
nested::{
repr_tree::{Context, ReprTree},
editors::list::*,
edit_tree::{TreeCursor, TreeNav, TreeNavResult, EditTree},
},
crate::{
DisplaySegment,
TerminalStyle,
TerminalEvent, TerminalView, make_label,
edit_tree::color::{bg_style_from_depth, fg_style_from_depth}
},
std::sync::{Arc, RwLock},
termion::event::{Event, Key}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl DisplaySegment for ListSegment {
fn display_view(&self) -> OuterViewPort<dyn TerminalView> {
match self {
ListSegment::InsertCursor => {
make_label("|")
.map_item(move |_pt, atom| {
atom.add_style_front(TerminalStyle::fg_color((150,80,230)))
.add_style_front(TerminalStyle::bold(true))
})
}
ListSegment::Item{ editor, cur_dist } => {
let e = editor.clone();
let cur_dist = *cur_dist;
editor.display_view().map_item(move |_pt, atom| {
let c = e.get_cursor();
let cur_depth = c.tree_addr.len();
let select =
if cur_dist == 0 {
cur_depth
} else {
usize::MAX
};
atom
.add_style_back(bg_style_from_depth(select))
.add_style_back(TerminalStyle::bold(select==1))
.add_style_back(fg_style_from_depth(e.disp.depth.get_view().get()))
})
}
}
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct PTYListStyle {
style: (String, String, String)
}
impl PTYListStyle {
pub fn new(style: (&str, &str, &str)) -> PTYListStyle {
PTYListStyle {
style: (style.0.into(), style.1.into(), style.2.into())
}
}
pub fn get_seg_seq_view(&self, editor: &ListEditor) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
let seg_seq = ListSegmentSequence::new(
editor.get_cursor_port(),
editor.get_data_port()
);
let se = seg_seq.read().unwrap();
se.get_view().map(move |segment| segment.display_view())
}
pub fn pty_view(&self, editor: &ListEditor) -> OuterViewPort<dyn TerminalView> {
let seg_seq = ListSegmentSequence::new(
editor.get_cursor_port(),
editor.get_data_port()
);
let seg_seq = seg_seq.read().unwrap();
seg_seq
.get_view()
.map(move |segment| segment.display_view())
.separate(make_label(&self.style.1))
.wrap(make_label(&self.style.0), make_label(&self.style.2))
.to_grid_horizontal()
.flatten()
}
pub fn for_node(node: &mut EditTree, style: (&str, &str, &str)) {
node.disp.view
.write().unwrap()
.insert_branch(ReprTree::from_view(
Context::parse(&node.ctx, "TerminalView"),
Self::new(style)
.pty_view(
&node.get_edit::<ListEditor>().unwrap().read().unwrap()
)
));
}
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
// todo: rename to CharController
pub struct PTYListController {
pub editor: Arc<RwLock<ListEditor>>,
split_char: Option<char>,
close_char: Option<char>,
depth: OuterViewPort<dyn SingletonView<Item = usize>>
}
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
impl PTYListController {
pub fn from_editor(
editor: Arc<RwLock<ListEditor>>,
split_char: Option<char>,
close_char: Option<char>,
depth: OuterViewPort<dyn SingletonView<Item = usize>>
) -> Self {
PTYListController {
editor,
split_char,
close_char,
depth
}
}
pub fn for_node(
node: &mut EditTree,
split_char: Option<char>,
close_char: Option<char>
) {
/*
{
let ctx = node.ctx.as_ref();
let mut ctx = ctx.write().unwrap();
if let Some(c) = split_char.as_ref() {
ctx.meta_chars.push(*c);
}
if let Some(c) = close_char.as_ref() {
ctx.meta_chars.push(*c);
}
}
*/
let editor = node.get_edit::<ListEditor>().unwrap();
let controller = Arc::new(RwLock::new(PTYListController::from_editor( editor, split_char, close_char, node.disp.depth.clone() )));
node.ctrl.cmd.set(Some(controller.clone()));
node.ctrl.close_char.set(close_char);
}
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = EditTree>> {
self.editor.read().unwrap().get_data_port()
}
pub fn clear(&mut self) {
self.editor.write().unwrap().clear();
}
pub fn get_item(&self) -> Option<EditTree> {
self.editor.read().unwrap().get_item()
}
pub fn handle_term_event(&mut self, event: &TerminalEvent, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
let mut e = self.editor.write().unwrap();
match event {
TerminalEvent::Input(Event::Key(Key::Insert)) => {
e.toggle_leaf_mode();
TreeNavResult::Continue
}
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
let ctx = e.ctx.clone();
drop(e);
self.handle_any_event(
ReprTree::from_char(&ctx, *c)
)
}
_ => TreeNavResult::Continue
}
}
pub fn handle_meta_char(&mut self, c: char, child_close_char: Option<char>) -> TreeNavResult {
// eprintln!("handle meta char: got '{}', child_close={:?}, self.close={:?}, split={:?}", c, child_close_char, self.close_char, self.split_char);
let mut e = self.editor.write().unwrap();
let cur = e.cursor.get();
if Some(c) == self.split_char
// || Some(c) == child_close_char
{
e.listlist_split();
// eprintln!("done listlist split");
TreeNavResult::Continue
} else if Some(c) == child_close_char {
e.goto(TreeCursor::none());
e.cursor.set(ListCursor {
mode: ListCursorMode::Insert,
idx: Some(cur.idx.unwrap_or(0)+1)
});
TreeNavResult::Continue
} else {
TreeNavResult::Exit
}
}
pub fn handle_any_event(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
let mut e = self.editor.write().unwrap();
let cur = e.cursor.get();
let ctx = e.ctx.clone();
let ctx = ctx.read().unwrap();
match cur.mode {
ListCursorMode::Insert => {
let rt = ReprTree::new_arc(e.typ.clone());
let new_edittree = ctx.setup_edittree(
rt,
self.depth.map(|d| d+1)
);
let mut ne = new_edittree.get();
match ne.send_cmd_obj(cmd_obj.clone()) {
TreeNavResult::Continue => {
drop(ne);
e.insert(new_edittree.value.clone());
TreeNavResult::Continue
}
TreeNavResult::Exit => {
TreeNavResult::Exit
}
}
},
ListCursorMode::Select => {
if let Some(item) = e.get_item_mut() {
let res = item.write().unwrap().send_cmd_obj(cmd_obj.clone());
let child_close_char = item.read().unwrap().ctrl.close_char.get();
match res {
TreeNavResult::Continue => TreeNavResult::Continue,
TreeNavResult::Exit => {
// child editor returned control, probably for meta-char handling..
if cmd_obj.read().unwrap().get_type().clone() == ctx.type_term_from_str("Char").unwrap() {
let co = cmd_obj.read().unwrap();
if let Some(cmd_view) = co.get_view::<dyn SingletonView<Item = char>>() {
drop(co);
drop(e);
self.handle_meta_char(cmd_view.get(), child_close_char)
} else {
TreeNavResult::Exit
}
} else {
TreeNavResult::Exit
}
}
}
} else {
// cursor selects non existent item
TreeNavResult::Exit
}
}
}
}
}
use r3vi::view::singleton::SingletonView;
use nested::editors::ObjCommander;
impl ObjCommander for PTYListController {
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
let mut e = self.editor.write().unwrap();
let cmd_type = cmd_obj.read().unwrap().get_type().clone();
if cmd_type == Context::parse(&e.ctx, "ListCmd").into()
|| cmd_type == Context::parse(&e.ctx, "NestedNode").into()
{
e.send_cmd_obj( cmd_obj )
}
else if cmd_type == Context::parse(&e.ctx, "TerminalEvent").into() {
let co = cmd_obj.read().unwrap();
if let Some(view) = co.get_view::<dyn SingletonView<Item = TerminalEvent>>() {
drop( co );
drop( e );
self.handle_term_event( &view.get(), cmd_obj )
} else {
TreeNavResult::Exit
}
}
else {
drop( e );
self.handle_any_event( cmd_obj )
}
}
}