use ratatui_core::layout::Rect;
use super::geometry::Size;
use super::style::{StyleSheet, Theme};
use super::surface::Surface;
pub struct RenderCtx<'a> {
pub theme: &'a Theme,
pub sheet: StyleSheet,
pub focused: bool,
}
impl<'a> RenderCtx<'a> {
pub fn new(theme: &'a Theme) -> Self {
Self {
theme,
sheet: StyleSheet::from_theme(theme),
focused: false,
}
}
pub fn with_sheet(mut self, sheet: StyleSheet) -> Self {
self.sheet = sheet;
self
}
pub fn with_focus(&self, focused: bool) -> RenderCtx<'a> {
RenderCtx {
theme: self.theme,
sheet: self.sheet,
focused,
}
}
}
pub trait View {
fn measure(&self, available: Size) -> Size;
fn render(&self, area: Rect, surface: &mut Surface, ctx: &RenderCtx);
}
pub type Element = Box<dyn View>;
impl View for Element {
fn measure(&self, available: Size) -> Size {
(**self).measure(available)
}
fn render(&self, area: Rect, surface: &mut Surface, ctx: &RenderCtx) {
(**self).render(area, surface, ctx)
}
}
pub fn element<V: View + 'static>(view: V) -> Element {
Box::new(view)
}
pub struct DrawView<F> {
intrinsic: Size,
draw: F,
}
impl<F> DrawView<F> {
pub fn new(draw: F) -> Self {
Self {
intrinsic: Size::new(u16::MAX, u16::MAX),
draw,
}
}
pub fn intrinsic_size(mut self, size: Size) -> Self {
self.intrinsic = size;
self
}
}
impl<F> View for DrawView<F>
where
F: Fn(Rect, &mut Surface<'_>, &RenderCtx<'_>),
{
fn measure(&self, available: Size) -> Size {
Size::new(
self.intrinsic.width.min(available.width),
self.intrinsic.height.min(available.height),
)
}
fn render(&self, area: Rect, surface: &mut Surface, ctx: &RenderCtx) {
(self.draw)(area, surface, ctx);
}
}
pub type CanvasView<F> = DrawView<F>;
#[cfg(test)]
mod draw_view_tests {
use super::*;
use crate::Theme;
use crate::testing::{grid, render};
#[test]
fn draw_view_is_clipped_and_reports_intrinsic_size() {
let view = DrawView::new(
|area: Rect, surface: &mut Surface<'_>, ctx: &RenderCtx<'_>| {
surface.set_string(area.x, area.y, "canvas", ctx.theme.text_style());
surface.set(area.right(), area.bottom(), 'x', ctx.theme.text_style());
},
)
.intrinsic_size(Size::new(20, 3));
assert_eq!(view.measure(Size::new(4, 1)), Size::new(4, 1));
assert_eq!(grid(&render(&view, 4, 1, &Theme::default())), "canv");
}
}