use { crate::{ core::{InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort}, projection::ProjectionHelper, sequence::SequenceView, terminal::{make_label, TerminalStyle, TerminalView}, }, cgmath::Point2, std::sync::Arc, std::sync::RwLock, }; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> pub struct ListDecorator { opening_port: OuterViewPort, closing_port: OuterViewPort, delim_port: OuterViewPort, items: Arc>>, list_style: TerminalStyle, item_style: TerminalStyle, cast: Arc>>>>, proj_helper: ProjectionHelper, } impl View for ListDecorator { type Msg = usize; } impl SequenceView for ListDecorator { type Item = OuterViewPort; fn len(&self) -> Option { let l = self.items.len()?; Some(if l == 0 { 2 } else { 2 * l + 2 }) } fn get(&self, idx: &usize) -> Option { 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>>, out_port: InnerViewPort>>, ) -> Arc> { 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>>; } impl ListDecoration for OuterViewPort>> { fn decorate( &self, opening: &str, closing: &str, delim: &str, level: usize, ) -> OuterViewPort>> { 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, closing_port: OuterViewPort, items: Arc>>, list_style: TerminalStyle, item_style: TerminalStyle, cast: Arc>>>>, proj_helper: ProjectionHelper, } impl View for VerticalSexprDecorator { type Msg = Point2; } impl IndexView> for VerticalSexprDecorator { type Item = OuterViewPort; fn area(&self) -> Option>> { let mut area = (0..self.items.len()?) .map(|i| Point2::new(1 as i16, i as i16)) .collect::>(); area.push(Point2::new(0, 0)); area.push(Point2::new(2, self.items.len()? as i16 - 1)); Some(area) } fn get(&self, pt: &Point2) -> Option { 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>>, out_port: InnerViewPort>>, ) -> Arc> { 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; fn vertical_bar_view(&self, level: usize) -> OuterViewPort; fn vertical_sexpr_view(&self, level: usize) -> OuterViewPort; } impl SExprView for OuterViewPort>> { fn horizontal_sexpr_view(&self, level: usize) -> OuterViewPort { 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 { 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 { let port = ViewPort::new(); VerticalSexprDecorator::new("(", ")", level, self.clone(), port.inner()); port.into_outer().flatten() } }