use { crate::{ core::{InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort}, index::IndexArea, projection::ProjectionHelper, sequence::SequenceView, terminal::{make_label, TerminalStyle, TerminalView}, }, cgmath::Point2, std::sync::Arc, std::sync::RwLock, }; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> 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 + 1 }) } 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 } } pub trait ListDecoration { fn decorate( &self, opening: &str, closing: &str, delim: &str, level: usize, ) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>; } impl ListDecoration for OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> { fn decorate( &self, opening: &str, closing: &str, delim: &str, level: usize, ) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> { let port = ViewPort::new(); ListDecorator::new(opening, closing, delim, level, self.clone(), port.inner()); port.into_outer() } } //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> use crate::{grid::GridView, index::IndexView}; 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 = IndexArea<Point2<i16>>; } impl IndexView<Point2<i16>> for VerticalSexprDecorator { type Item = OuterViewPort<dyn TerminalView>; fn area(&self) -> IndexArea<Point2<i16>> { IndexArea::Range( Point2::new(0, 0) ..=Point2::new(2, std::cmp::max(self.items.len().unwrap() as i16 - 1, 0)), ) } fn get(&self, pt: &Point2<i16>) -> Option<Self::Item> { if pt.y < 0 { return None; } 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(&IndexArea::Range( Point2::new(0, *item_idx as i16)..=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() } }