use std::{cell::RefCell, rc::Rc}; use crate::{ container::{Callable, FromContainer}, stack::Stack, }; use super::{ container::Container, geometry::{Pos, Rect, Size}, runes::Rune, view::View, }; /// ViewContext represents the display context for a given area. /// it maintains the drawing state for the region internally and is used /// to generate a final view that is eventually rendered. pub struct ViewContext { pub view: View, pub container: Rc>, pub(crate) rerender: bool, } impl std::ops::DerefMut for ViewContext { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.view } } impl std::ops::Deref for ViewContext { type Target = View; fn deref(&self) -> &Self::Target { &self.view } } impl ViewContext { /// Constructs a new ViewConext for a given area. A container reference /// must also be passedo, so that component functions called /// from the context are injectable. pub fn new(container: Rc>, size: Size) -> Self { let view = View::new(size); Self { view, container, rerender: false, } } /// Notify the application to rerender the view. This is useful after a /// state change that might affect other views. pub fn render(&mut self) { self.rerender = true; } pub fn vertical_stack(&self, size: S) -> Stack where S: Into, { Stack { direction: crate::stack::StackDirection::Vertical, container: self.container.clone(), view: View::new(size.into()), position: Pos::from(0), alignment: crate::stack::StackAlignment::Top, } } pub fn horizontal_stack(&self, size: S) -> Stack where S: Into, { Stack { direction: crate::stack::StackDirection::Horizontal, container: self.container.clone(), view: View::new(size.into()), position: Pos::from(0), alignment: crate::stack::StackAlignment::Left, } } /// Execute a component function. The passed function will receive a new /// ViewContext for its size and can be injected with arguments. /// The context given to the component function will then be applied to /// the parent ViewContext at a given position. pub fn component(&mut self, rect: R, f: F) where F: Callable, Args: FromContainer, R: Into, { let rect = rect.into(); let mut context = ViewContext::new(self.container.clone(), rect.size); let args = Args::from_container(&self.container.borrow()); f.call(&mut context, args); self.view.apply(rect.pos, &context.view); self.rerender = context.rerender; } /// Set a specific rune to a specific position. This function can be used /// to set a signle character. To set multiple runes at a time see the /// View::insert function. pub fn set_rune

(&mut self, pos: P, rune: Rune) where P: Into, { let Pos { x, y } = pos.into(); if let Some(r) = self.view.get_mut(y).and_then(|row| row.get_mut(x)) { *r = rune; } } } #[cfg(test)] pub mod tests { use std::{cell::RefCell, rc::Rc}; use crate::container::Container; use super::ViewContext; pub fn context_fixture() -> ViewContext { ViewContext::new(Rc::new(RefCell::new(Container::default())), (20, 20).into()) } }