use crate::context::RenderContext;
use crate::event::{Event, EventHandler};
use crate::layout::Rect;
use crate::render::Renderer;
use anyhow::Result;
pub trait Component: EventHandler {
fn render(&mut self, renderer: &mut Renderer, bounds: Rect, ctx: &RenderContext) -> Result<()>;
fn min_size(&self) -> (u16, u16) {
(0, 0)
}
fn on_mount(&mut self) {}
fn on_unmount(&mut self) {}
fn mark_dirty(&mut self) {}
fn is_dirty(&self) -> bool {
true }
fn name(&self) -> &str {
"Component"
}
}
pub trait Container: Component {
fn children_mut(&mut self) -> &mut [Box<dyn Component>];
fn children(&self) -> &[Box<dyn Component>];
fn add_child(&mut self, child: Box<dyn Component>);
fn remove_child(&mut self, index: usize) -> Option<Box<dyn Component>>;
}
pub fn propagate_event(children: &mut [Box<dyn Component>], event: &Event) -> bool {
for child in children.iter_mut() {
if child.handle_event(event) {
return true; }
}
false
}
#[cfg(test)]
mod tests {
use super::*;
struct TestComponent {
dirty: bool,
}
impl EventHandler for TestComponent {}
impl Component for TestComponent {
fn render(
&mut self,
_renderer: &mut Renderer,
_bounds: Rect,
_ctx: &RenderContext,
) -> Result<()> {
self.dirty = false;
Ok(())
}
fn mark_dirty(&mut self) {
self.dirty = true;
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn name(&self) -> &str {
"TestComponent"
}
}
#[test]
fn test_component_dirty_tracking() {
use crate::slots::Slots;
use crate::terminal::TerminalCapabilities;
use crate::theme::Theme;
let mut comp = TestComponent { dirty: true };
assert!(comp.is_dirty());
let mut renderer = Renderer::headless();
let caps = TerminalCapabilities::detect();
let theme = Theme::new(caps);
let slots = Slots::new();
let ctx = RenderContext::new(&theme, &slots);
comp.render(&mut renderer, Rect::new(0, 0, 10, 10), &ctx)
.unwrap();
assert!(!comp.is_dirty());
comp.mark_dirty();
assert!(comp.is_dirty());
}
}