use { std::{ sync::{Arc, Weak, RwLock}, collections::HashMap, ops::Range, cmp::{min, max} }, cgmath::Point2, crate::{ core::{View, ViewPort, InnerViewPort, OuterViewPort, Observer, ObserverExt, ObserverBroadcast}, view::{ImplIndexView, grid::GridWindowIterator}, terminal::{TerminalAtom, TerminalView} } }; struct CompositeLayer { comp: Weak>, idx: usize, view: RwLock>> } impl Observer for CompositeLayer { fn reset(&self, view: Option>) { let comp = self.comp.upgrade().unwrap(); let mut c = comp.write().unwrap(); { let mut v = self.view.write().unwrap(); let old_view = v.clone(); *v = view.clone(); if let Some(old_view) = old_view { if let Some(range) = old_view.range() { c.cast.notify_each(GridWindowIterator::from(range)); } } if let Some(view) = v.as_ref() { if let Some(range) = view.range() { c.cast.notify_each(GridWindowIterator::from(range)); } } } c.update_range(); } fn notify(&self, pos: &Point2) { self.comp .upgrade().unwrap() .read().unwrap() .cast.notify(pos); } } pub struct TerminalCompositeView { idx_count: usize, layers: HashMap>, range: Option>>, cast: Arc>> } impl TerminalCompositeView { fn update_range(&mut self) { if self.layers.len() == 0 { self.range = Some(Point2::new(0, 0) .. Point2::new(0, 0)) } else { self.range = None; for (idx, layer) in self.layers.iter() { self.range = if let ( Some(new_range), Some(old_range) ) = ( if let Some(view) = layer.view.read().unwrap().clone() { view.range().clone() } else { None }, self.range.as_ref() ) { Some( Point2::new( min(old_range.start.x, new_range.start.x), min(old_range.start.y, new_range.start.y) ) .. Point2::new( max(old_range.end.x, new_range.end.x), max(old_range.end.y, new_range.end.y) ) ) } else { None }; } } } } impl ImplIndexView for TerminalCompositeView { type Key = Point2; type Value = Option; fn get(&self, pos: &Point2) -> Option { let mut atom = None; for idx in 0 .. self.idx_count { if let Some(l) = self.layers.get(&idx) { if let Some(view) = l.view.read().unwrap().as_ref() { if let Some(range) = view.range() { if pos.x < range.start.x || pos.x >= range.end.x || pos.y < range.start.y || pos.y >= range.end.y { continue; } } match (atom, view.get(pos)) { (None, next) => atom = next, (Some(last), Some(next)) => atom = Some(next.add_style_back(last.style)), _ => {} } } } } atom } fn range(&self) -> Option>> { self.range.clone() } } pub struct TerminalCompositor { view: Arc>, port: InnerViewPort } impl TerminalCompositor { pub fn new( port: InnerViewPort ) -> Self { let view = Arc::new(RwLock::new( TerminalCompositeView { idx_count: 0, layers: HashMap::new(), range: Some(Point2::new(0, 0) .. Point2::new(0, 0)), cast: port.get_broadcast() } )); port.set_view(Some(view.clone())); TerminalCompositor{ view, port } } pub fn push(&mut self, v: OuterViewPort) { let mut comp = self.view.write().unwrap(); let idx = comp.idx_count; comp.idx_count += 1; let layer = Arc::new(CompositeLayer { comp: Arc::downgrade(&self.view), idx: idx, view: RwLock::new(None) }); comp.layers.insert(idx, layer.clone()); drop(comp); v.add_observer(layer); } }