move list editor module to nested crate
This commit is contained in:
parent
2d17254b2c
commit
07ee04bba4
6 changed files with 963 additions and 347 deletions
|
@ -10,6 +10,7 @@ pub mod sequence;
|
|||
pub mod vec;
|
||||
pub mod terminal;
|
||||
pub mod integer;
|
||||
pub mod list;
|
||||
|
||||
pub mod string_editor;
|
||||
pub mod leveled_term_view;
|
||||
|
|
427
nested/src/list/editor.rs
Normal file
427
nested/src/list/editor.rs
Normal file
|
@ -0,0 +1,427 @@
|
|||
use {
|
||||
std::sync::{Arc, RwLock},
|
||||
termion::event::{Event, Key},
|
||||
crate::{
|
||||
core::{
|
||||
View,
|
||||
ViewPort,
|
||||
OuterViewPort,
|
||||
InnerViewPort,
|
||||
ObserverBroadcast,
|
||||
Observer,
|
||||
ObserverExt,
|
||||
context::{
|
||||
ReprTree,
|
||||
Object,
|
||||
MorphismType,
|
||||
MorphismMode,
|
||||
Context
|
||||
}
|
||||
},
|
||||
projection::ProjectionHelper,
|
||||
singleton::{SingletonView, SingletonBuffer},
|
||||
sequence::{SequenceView},
|
||||
vec::{VecBuffer},
|
||||
terminal::{
|
||||
TerminalView,
|
||||
TerminalStyle,
|
||||
TerminalEvent,
|
||||
TerminalEditor,
|
||||
TerminalEditorResult,
|
||||
make_label
|
||||
},
|
||||
string_editor::StringEditor,
|
||||
leveled_term_view::LeveledTermView,
|
||||
list::SExprView
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum ListEditorCursor {
|
||||
None,
|
||||
Insert(usize),
|
||||
Select(usize),
|
||||
Edit(usize)
|
||||
}
|
||||
|
||||
impl Default for ListEditorCursor {
|
||||
fn default() -> Self {
|
||||
ListEditorCursor::None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListEditor<SubEditor, FnMakeItemEditor>
|
||||
where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
||||
FnMakeItemEditor: Fn() -> Arc<RwLock<SubEditor>>
|
||||
{
|
||||
cursor: SingletonBuffer<ListEditorCursor>,
|
||||
data: VecBuffer<Arc<RwLock<SubEditor>>>,
|
||||
data_sequence_port: OuterViewPort<dyn SequenceView<Item = Arc<RwLock<SubEditor>>>>,
|
||||
make_item_editor: FnMakeItemEditor,
|
||||
level: usize,
|
||||
pub segment_seq: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
}
|
||||
|
||||
impl<SubEditor, FnMakeItemEditor> TerminalEditor for ListEditor<SubEditor, FnMakeItemEditor>
|
||||
where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
||||
FnMakeItemEditor: Fn() -> Arc<RwLock<SubEditor>>
|
||||
{
|
||||
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||
self.segment_seq.horizontal_sexpr_view(0)
|
||||
}
|
||||
|
||||
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||
match self.cursor.get() {
|
||||
ListEditorCursor::None => {
|
||||
|
||||
}
|
||||
ListEditorCursor::Insert(idx) => {
|
||||
match event {
|
||||
TerminalEvent::Input(Event::Key(Key::Insert)) => {
|
||||
self.cursor.set(ListEditorCursor::Select(idx));
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
||||
if idx > 0 {
|
||||
self.cursor.set(ListEditorCursor::Insert(idx-1));
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
||||
if idx < self.data.len() {
|
||||
self.cursor.set(ListEditorCursor::Insert(idx+1));
|
||||
}
|
||||
}
|
||||
event => {
|
||||
self.data.insert(idx, (self.make_item_editor)());
|
||||
self.cursor.set(ListEditorCursor::Edit(idx));
|
||||
self.data.get_mut(idx).write().unwrap().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);
|
||||
if self.data.len() == 0 {
|
||||
self.cursor.set(ListEditorCursor::Insert(0));
|
||||
} else if idx == self.data.len() && idx > 0 {
|
||||
self.cursor.set(ListEditorCursor::Select(idx-1))
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Home)) => {
|
||||
if self.data.len() > 0 {
|
||||
self.cursor.set(ListEditorCursor::Select(0))
|
||||
} else {
|
||||
self.cursor.set(ListEditorCursor::Insert(0))
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::End)) => {
|
||||
if self.data.len() > 0 {
|
||||
self.cursor.set(ListEditorCursor::Select(self.data.len() - 1))
|
||||
} else {
|
||||
self.cursor.set(ListEditorCursor::Insert(0))
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
||||
if idx > 0 {
|
||||
self.cursor.set(ListEditorCursor::Select(idx - 1));
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
||||
if idx+1 < self.data.len() {
|
||||
self.cursor.set(ListEditorCursor::Select(idx + 1));
|
||||
}
|
||||
}
|
||||
event => {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx));
|
||||
self.data.get_mut(idx).write().unwrap().handle_terminal_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
ListEditorCursor::Edit(idx) => {
|
||||
match event {
|
||||
TerminalEvent::Input(Event::Key(Key::Char('\t'))) => {
|
||||
if idx > 0 {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx-1));
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||
if idx+1 < self.data.len() {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx+1));
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Char(' '))) => {
|
||||
// split..
|
||||
self.data.insert(idx+1, (self.make_item_editor)());
|
||||
self.data.get_mut(idx).write().unwrap().handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::End)));
|
||||
self.data.get_mut(idx+1).write().unwrap().handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Home)));
|
||||
self.cursor.set(ListEditorCursor::Edit(idx+1));
|
||||
}
|
||||
event => {
|
||||
let ce = self.data.get_mut(idx);
|
||||
let mut cur_edit = ce.write().unwrap();
|
||||
match cur_edit.handle_terminal_event(event) {
|
||||
TerminalEditorResult::Exit => {
|
||||
match event {
|
||||
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
||||
self.cursor.set(ListEditorCursor::Select(idx));
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Home)) => {
|
||||
if idx > 0 {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx-1));
|
||||
self.data.get_mut(idx-1).write().unwrap().handle_terminal_event(event);
|
||||
} else {
|
||||
cur_edit.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Home)));
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Backspace)) | // -> join
|
||||
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
||||
if idx > 0 {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx-1));
|
||||
self.data.get_mut(idx-1).write().unwrap().handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::End)));
|
||||
} else {
|
||||
cur_edit.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Home)));
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::End)) => {
|
||||
if idx+1 < self.data.len() {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx+1));
|
||||
self.data.get_mut(idx+1).write().unwrap().handle_terminal_event(event);
|
||||
} else if idx+1 == self.data.len() {
|
||||
cur_edit.handle_terminal_event(event);
|
||||
}
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Delete)) |
|
||||
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
||||
if idx+1 < self.data.len() {
|
||||
self.cursor.set(ListEditorCursor::Edit(idx+1));
|
||||
self.data.get_mut(idx+1).write().unwrap().handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Home)));
|
||||
} else if idx+1 == self.data.len() {
|
||||
cur_edit.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::End)));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
TerminalEditorResult::Continue => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TerminalEditorResult::Continue
|
||||
}
|
||||
}
|
||||
|
||||
enum ListEditorViewSegment {
|
||||
InsertCursor,
|
||||
View(OuterViewPort<dyn TerminalView>),
|
||||
Select(OuterViewPort<dyn TerminalView>),
|
||||
Edit(OuterViewPort<dyn TerminalView>)
|
||||
}
|
||||
|
||||
struct ListEditorView {
|
||||
cursor: Arc<dyn SingletonView<Item = ListEditorCursor>>,
|
||||
data: Arc<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
cur_cursor: ListEditorCursor,
|
||||
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = ListEditorViewSegment>>>>,
|
||||
proj_helper: ProjectionHelper<Self>
|
||||
}
|
||||
|
||||
impl View for ListEditorView {
|
||||
type Msg = usize;
|
||||
}
|
||||
|
||||
impl SequenceView for ListEditorView {
|
||||
type Item = ListEditorViewSegment;
|
||||
|
||||
fn len(&self) -> Option<usize> {
|
||||
match self.cur_cursor {
|
||||
ListEditorCursor::None => self.data.len(),
|
||||
ListEditorCursor::Select(_) => self.data.len(),
|
||||
ListEditorCursor::Edit(_) => self.data.len(),
|
||||
ListEditorCursor::Insert(_) => Some(self.data.len()? + 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||
Some(
|
||||
match self.cur_cursor {
|
||||
ListEditorCursor::None =>
|
||||
ListEditorViewSegment::View(self.data.get(idx)?),
|
||||
ListEditorCursor::Select(c) =>
|
||||
if *idx == c {
|
||||
ListEditorViewSegment::Select(self.data.get(idx)?)
|
||||
} else {
|
||||
ListEditorViewSegment::View(self.data.get(idx)?)
|
||||
},
|
||||
ListEditorCursor::Edit(c) =>
|
||||
if *idx == c {
|
||||
ListEditorViewSegment::Edit(self.data.get(idx)?)
|
||||
} else {
|
||||
ListEditorViewSegment::View(self.data.get(idx)?)
|
||||
},
|
||||
ListEditorCursor::Insert(c) =>
|
||||
if *idx < c {
|
||||
ListEditorViewSegment::View(self.data.get(idx)?)
|
||||
} else if *idx == c {
|
||||
ListEditorViewSegment::InsertCursor
|
||||
} else {
|
||||
ListEditorViewSegment::View(self.data.get(&(idx-1))?)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ListEditorView {
|
||||
fn new(
|
||||
cursor_port: OuterViewPort<dyn SingletonView<Item = ListEditorCursor>>,
|
||||
data_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
out_port: InnerViewPort<dyn SequenceView<Item = ListEditorViewSegment>>
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||
let proj = Arc::new(RwLock::new(
|
||||
ListEditorView {
|
||||
cur_cursor: ListEditorCursor::None,
|
||||
cursor: proj_helper.new_singleton_arg(
|
||||
cursor_port,
|
||||
|s: &mut Self, _msg| {
|
||||
let old_cursor = s.cur_cursor;
|
||||
let new_cursor = s.cursor.get();
|
||||
s.cur_cursor = new_cursor;
|
||||
|
||||
let mut begin = std::cmp::min(
|
||||
match old_cursor {
|
||||
ListEditorCursor::None => usize::MAX,
|
||||
ListEditorCursor::Select(c) => c,
|
||||
ListEditorCursor::Insert(c) => c,
|
||||
ListEditorCursor::Edit(c) => c
|
||||
},
|
||||
match new_cursor {
|
||||
ListEditorCursor::None => usize::MAX,
|
||||
ListEditorCursor::Select(c) => c,
|
||||
ListEditorCursor::Insert(c) => c,
|
||||
ListEditorCursor::Edit(c) => c
|
||||
}
|
||||
);
|
||||
let mut end =
|
||||
/*
|
||||
match (old_cursor, new_cursor) {
|
||||
(ListEditorCursor::None, ListEditorCursor::None) => usize::MAX,
|
||||
(ListEditorCursor::Select(old_pos), ListEditorCursor::Select(new_pos)) => max(old_pos, new_pos),
|
||||
(ListEditorCursor::Edit(old_pos), ListEditorCursor::Edit(new_pos)) => max(old_pos, new_pos),
|
||||
(ListEditorCursor::Insert(old_pos), ListEditorCursor::Insert(new_pos)) => max(old_pos, new_pos),
|
||||
(ListEditorCursor::)
|
||||
};
|
||||
*/
|
||||
std::cmp::max(
|
||||
match old_cursor {
|
||||
ListEditorCursor::None => 0,
|
||||
ListEditorCursor::Select(c) => c,
|
||||
ListEditorCursor::Insert(c) => c+1,
|
||||
ListEditorCursor::Edit(c) => c
|
||||
},
|
||||
match new_cursor {
|
||||
ListEditorCursor::None => 0,
|
||||
ListEditorCursor::Select(c) => c,
|
||||
ListEditorCursor::Insert(c) => c+1,
|
||||
ListEditorCursor::Edit(c) => c
|
||||
}
|
||||
);
|
||||
|
||||
s.cast.notify_each(
|
||||
begin ..= end
|
||||
);
|
||||
}),
|
||||
data: proj_helper.new_sequence_arg(
|
||||
data_port,
|
||||
|s: &mut Self, idx| {
|
||||
match s.cur_cursor {
|
||||
ListEditorCursor::None => s.cast.notify(idx),
|
||||
ListEditorCursor::Select(c) => s.cast.notify(idx),
|
||||
ListEditorCursor::Edit(c) => s.cast.notify(idx),
|
||||
ListEditorCursor::Insert(c) =>
|
||||
if *idx < c {
|
||||
s.cast.notify(idx)
|
||||
} else {
|
||||
s.cast.notify(&(*idx + 1))
|
||||
}
|
||||
}
|
||||
}),
|
||||
cast: out_port.get_broadcast(),
|
||||
proj_helper
|
||||
}
|
||||
));
|
||||
|
||||
proj.write().unwrap().proj_helper.set_proj(&proj);
|
||||
out_port.set_view(Some(proj.clone()));
|
||||
|
||||
proj
|
||||
}
|
||||
}
|
||||
|
||||
impl<SubEditor, FnMakeItemEditor> ListEditor<SubEditor, FnMakeItemEditor>
|
||||
where SubEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
||||
FnMakeItemEditor: Fn() -> Arc<RwLock<SubEditor>>
|
||||
{
|
||||
pub fn new(make_item_editor: FnMakeItemEditor) -> Self {
|
||||
let cursor_port = ViewPort::new();
|
||||
let data_port = ViewPort::new();
|
||||
|
||||
let mut cursor = SingletonBuffer::new(ListEditorCursor::Insert(0), cursor_port.inner());
|
||||
let mut data = VecBuffer::<Arc<RwLock<SubEditor>>>::new(data_port.inner());
|
||||
|
||||
let data_sequence_port = data_port.into_outer().to_sequence();
|
||||
|
||||
let segment_view_port = ViewPort::<dyn SequenceView<Item = ListEditorViewSegment>>::new();
|
||||
let segment_view = ListEditorView::new(
|
||||
cursor_port.outer(),
|
||||
data_sequence_port.map(|ed| ed.read().unwrap().get_term_view()),
|
||||
segment_view_port.inner()
|
||||
);
|
||||
|
||||
ListEditor {
|
||||
data,
|
||||
data_sequence_port,
|
||||
cursor,
|
||||
make_item_editor,
|
||||
level: 0,
|
||||
segment_seq: segment_view_port
|
||||
.outer()
|
||||
.map(
|
||||
|segment| match segment {
|
||||
ListEditorViewSegment::InsertCursor =>
|
||||
make_label("|")
|
||||
.map_item(
|
||||
|_pt, atom|
|
||||
atom.add_style_back(TerminalStyle::fg_color((90,60,200)))
|
||||
.add_style_back(TerminalStyle::bold(true))
|
||||
),
|
||||
ListEditorViewSegment::Select(sub_view) =>
|
||||
sub_view.map_item(
|
||||
|_pt, atom|
|
||||
atom.add_style_back(TerminalStyle::bg_color((90,60,200)))
|
||||
),
|
||||
ListEditorViewSegment::Edit(sub_view) =>
|
||||
sub_view.map_item(
|
||||
|_pt, atom|
|
||||
atom.add_style_back(TerminalStyle::bg_color((0,0,0)))
|
||||
.add_style_back(TerminalStyle::bold(true))
|
||||
),
|
||||
ListEditorViewSegment::View(sub_view) =>
|
||||
sub_view.clone()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = Arc<RwLock<SubEditor>>>> {
|
||||
self.data_sequence_port.clone()
|
||||
}
|
||||
}
|
||||
|
7
nested/src/list/mod.rs
Normal file
7
nested/src/list/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
pub mod editor;
|
||||
pub mod sexpr;
|
||||
|
||||
pub use editor::ListEditor;
|
||||
pub use sexpr::SExprView;
|
||||
|
283
nested/src/list/sexpr.rs
Normal file
283
nested/src/list/sexpr.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
use {
|
||||
std::sync::Arc,
|
||||
std::sync::RwLock,
|
||||
cgmath::{Point2},
|
||||
crate::{
|
||||
core::{
|
||||
View,
|
||||
ViewPort,
|
||||
OuterViewPort,
|
||||
InnerViewPort,
|
||||
Observer,
|
||||
ObserverBroadcast
|
||||
},
|
||||
sequence::{SequenceView},
|
||||
terminal::{TerminalStyle, TerminalView, make_label},
|
||||
projection::ProjectionHelper
|
||||
}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct ListDecorator {
|
||||
opening_port: OuterViewPort<dyn TerminalView>,
|
||||
closing_port: OuterViewPort<dyn TerminalView>,
|
||||
delim_port: OuterViewPort<dyn TerminalView>,
|
||||
items: Arc<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
|
||||
list_style: TerminalStyle,
|
||||
item_style: TerminalStyle,
|
||||
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>>>,
|
||||
proj_helper: ProjectionHelper<Self>
|
||||
}
|
||||
|
||||
impl View for ListDecorator {
|
||||
type Msg = usize;
|
||||
}
|
||||
|
||||
impl SequenceView for ListDecorator {
|
||||
type Item = OuterViewPort<dyn TerminalView>;
|
||||
|
||||
fn len(&self) -> Option<usize> {
|
||||
let l = self.items.len()?;
|
||||
Some(if l == 0 { 2 } else { 2*l + 2 })
|
||||
}
|
||||
|
||||
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||
let item_idx = idx / 2;
|
||||
let list_style = self.list_style.clone();
|
||||
let item_style = self.item_style.clone();
|
||||
let l = self.items.len().unwrap_or(0);
|
||||
Some(
|
||||
if *idx == 0 {
|
||||
self.opening_port.clone()
|
||||
.map_item(move |_, atom| atom.add_style_back(list_style))
|
||||
} else if (l == 0 && *idx == 1) || *idx == 2*l {
|
||||
self.closing_port.clone()
|
||||
.map_item(move |_, atom| atom.add_style_back(list_style))
|
||||
} else if idx % 2 == 0 {
|
||||
self.delim_port.clone()
|
||||
.map_item(move |_, atom| atom.add_style_back(list_style))
|
||||
} else {
|
||||
self.items
|
||||
.get(&item_idx)?
|
||||
.map_item(move |_, atom| atom.add_style_back(item_style))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ListDecorator {
|
||||
pub fn new(
|
||||
opening: &str,
|
||||
closing: &str,
|
||||
delim: &str,
|
||||
level: usize,
|
||||
items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
out_port: InnerViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||
|
||||
let li = Arc::new(RwLock::new(ListDecorator {
|
||||
opening_port: make_label(opening),
|
||||
closing_port: make_label(closing),
|
||||
delim_port: make_label(delim),
|
||||
items: proj_helper.new_sequence_arg(
|
||||
items_port,
|
||||
|s: &mut Self, item_idx| {
|
||||
s.cast.notify(
|
||||
&(*item_idx * 2 + 1)
|
||||
);
|
||||
s.cast.notify(
|
||||
&(*item_idx * 2 + 2)
|
||||
);
|
||||
}
|
||||
),
|
||||
list_style: TerminalStyle::fg_color(
|
||||
match level {
|
||||
0 => (200, 120, 10),
|
||||
1 => (120, 200, 10),
|
||||
_ => (255, 255, 255)
|
||||
}
|
||||
),
|
||||
item_style: TerminalStyle::fg_color(
|
||||
match level {
|
||||
_ => (255, 255, 255)
|
||||
}
|
||||
),
|
||||
cast: out_port.get_broadcast(),
|
||||
proj_helper
|
||||
}));
|
||||
|
||||
li.write().unwrap().proj_helper.set_proj(&li);
|
||||
|
||||
out_port.set_view(Some(li.clone()));
|
||||
li
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
use crate::{index::IndexView, grid::GridView};
|
||||
|
||||
pub struct VerticalSexprDecorator {
|
||||
opening_port: OuterViewPort<dyn TerminalView>,
|
||||
closing_port: OuterViewPort<dyn TerminalView>,
|
||||
items: Arc<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
|
||||
list_style: TerminalStyle,
|
||||
item_style: TerminalStyle,
|
||||
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = OuterViewPort<dyn TerminalView>>>>>,
|
||||
proj_helper: ProjectionHelper<Self>
|
||||
}
|
||||
|
||||
impl View for VerticalSexprDecorator {
|
||||
type Msg = Point2<i16>;
|
||||
}
|
||||
|
||||
impl IndexView<Point2<i16>> for VerticalSexprDecorator {
|
||||
type Item = OuterViewPort<dyn TerminalView>;
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
let mut area = (0 .. self.items.len()?).map(|i| Point2::new(1 as i16, i as i16)).collect::<Vec<_>>();
|
||||
area.push(Point2::new(0, 0));
|
||||
area.push(Point2::new(2, self.items.len()? as i16 - 1));
|
||||
Some(area)
|
||||
}
|
||||
|
||||
fn get(&self, pt: &Point2<i16>) -> Option<Self::Item> {
|
||||
let item_idx = pt.y as usize;
|
||||
let list_style = self.list_style.clone();
|
||||
let item_style = self.item_style.clone();
|
||||
let l = self.items.len().unwrap_or(0);
|
||||
|
||||
match pt.x {
|
||||
0 => {
|
||||
if pt.y == 0 {
|
||||
Some(self.opening_port.clone()
|
||||
.map_item(move |_, atom| atom.add_style_back(list_style)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
if item_idx < l {
|
||||
Some(self.items.get(&item_idx)?.map_item(move |_, atom| atom.add_style_back(item_style)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if (l == 0 && pt.y == 0) || (item_idx+1 == l) {
|
||||
Some(self.closing_port.clone()
|
||||
.map_item(move |_, atom| atom.add_style_back(list_style)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VerticalSexprDecorator {
|
||||
pub fn new(
|
||||
opening: &str,
|
||||
closing: &str,
|
||||
level: usize,
|
||||
items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||
out_port: InnerViewPort<dyn GridView<Item = OuterViewPort<dyn TerminalView>>>
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||
|
||||
let li = Arc::new(RwLock::new(VerticalSexprDecorator {
|
||||
opening_port: make_label(opening),
|
||||
closing_port: make_label(closing),
|
||||
items: proj_helper.new_sequence_arg(
|
||||
items_port,
|
||||
|s: &mut Self, item_idx| {
|
||||
s.cast.notify(
|
||||
&Point2::new(1, *item_idx as i16)
|
||||
);
|
||||
s.cast.notify(
|
||||
&Point2::new(2, *item_idx as i16 - 1)
|
||||
);
|
||||
s.cast.notify(
|
||||
&Point2::new(2, *item_idx as i16)
|
||||
);
|
||||
}
|
||||
),
|
||||
list_style: TerminalStyle::fg_color(
|
||||
match level {
|
||||
0 => (200, 120, 10),
|
||||
1 => (120, 200, 10),
|
||||
_ => (255, 255, 255)
|
||||
}
|
||||
),
|
||||
item_style: TerminalStyle::fg_color(
|
||||
match level {
|
||||
_ => (255, 255, 255)
|
||||
}
|
||||
),
|
||||
cast: out_port.get_broadcast(),
|
||||
proj_helper
|
||||
}));
|
||||
|
||||
li.write().unwrap().proj_helper.set_proj(&li);
|
||||
|
||||
out_port.set_view(Some(li.clone()));
|
||||
li
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait SExprView {
|
||||
fn horizontal_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView>;
|
||||
fn vertical_bar_view(&self, level: usize) -> OuterViewPort<dyn TerminalView>;
|
||||
fn vertical_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView>;
|
||||
}
|
||||
|
||||
impl SExprView for OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
||||
fn horizontal_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView> {
|
||||
let port = ViewPort::new();
|
||||
ListDecorator::new(
|
||||
"(", ")", " ",
|
||||
level,
|
||||
self.clone(),
|
||||
port.inner()
|
||||
);
|
||||
|
||||
port.into_outer()
|
||||
.to_grid_horizontal()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn vertical_bar_view(&self, level: usize) -> OuterViewPort<dyn TerminalView> {
|
||||
let port = ViewPort::new();
|
||||
ListDecorator::new(
|
||||
"Λ", "V", "|",
|
||||
level,
|
||||
self.clone(),
|
||||
port.inner()
|
||||
);
|
||||
|
||||
port.into_outer()
|
||||
.to_grid_vertical()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn vertical_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView> {
|
||||
let port = ViewPort::new();
|
||||
VerticalSexprDecorator::new(
|
||||
"(", ")",
|
||||
level,
|
||||
self.clone(),
|
||||
port.inner()
|
||||
);
|
||||
port.into_outer()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue