Skip to main content

tui_lipan/widgets/canvas/
mod.rs

1//! Absolute-positioned child container (`Canvas`).
2
3mod layout;
4mod node;
5mod reconcile;
6
7pub(crate) use self::layout::measure_canvas;
8pub(crate) use self::node::CanvasNode;
9pub(crate) use self::reconcile::{CanvasReconcile, reconcile_canvas};
10
11use crate::core::element::{Element, ElementKind};
12use crate::layout::hash::LayoutHash;
13use crate::style::{Length, Rect, Style};
14
15/// A single child placed at a Canvas-local rectangle.
16#[derive(Clone)]
17pub struct CanvasItem {
18    /// Placement rectangle in the containing [`Canvas`]'s local coordinates.
19    pub rect: Rect,
20    /// Child element to reconcile and render inside the placement rectangle.
21    pub element: Element,
22}
23
24impl CanvasItem {
25    /// Creates a Canvas item from a local rectangle and child element.
26    pub fn new(rect: Rect, element: impl Into<Element>) -> Self {
27        Self {
28            rect,
29            element: element.into(),
30        }
31    }
32}
33
34impl std::borrow::Borrow<Element> for CanvasItem {
35    fn borrow(&self) -> &Element {
36        &self.element
37    }
38}
39
40/// Absolute-positioned child container.
41///
42/// Child rectangles are local to the Canvas allocation. During reconciliation they are
43/// translated by the Canvas origin; during rendering descendants are clipped to the Canvas rect.
44/// Children are painted in declaration order, so later children appear visually on top.
45#[derive(Clone)]
46pub struct Canvas {
47    pub(crate) items: Vec<CanvasItem>,
48    pub(crate) style: Style,
49    pub(crate) passthrough: bool,
50    pub(crate) width: Length,
51    pub(crate) height: Length,
52}
53
54impl Default for Canvas {
55    fn default() -> Self {
56        Self {
57            items: Vec::new(),
58            style: Style::default(),
59            passthrough: false,
60            width: Length::Flex(1),
61            height: Length::Flex(1),
62        }
63    }
64}
65
66impl Canvas {
67    /// Create an empty Canvas that fills available width and height by default.
68    pub fn new() -> Self {
69        Self::default()
70    }
71
72    /// Add a child at a Canvas-local rectangle.
73    pub fn child_at(mut self, rect: Rect, child: impl Into<Element>) -> Self {
74        self.items.push(CanvasItem::new(rect, child));
75        self
76    }
77
78    /// Replace all positioned children.
79    pub fn items(mut self, items: impl IntoIterator<Item = CanvasItem>) -> Self {
80        self.items = items.into_iter().collect();
81        self
82    }
83
84    /// Set base style for the Canvas background/effects.
85    pub fn style(mut self, style: Style) -> Self {
86        self.style = style;
87        self
88    }
89
90    /// Allow pointer events to pass through non-interactive top layers.
91    pub fn passthrough(mut self, passthrough: bool) -> Self {
92        self.passthrough = passthrough;
93        self
94    }
95
96    /// Set requested Canvas width.
97    pub fn width(mut self, width: Length) -> Self {
98        self.width = width;
99        self
100    }
101
102    /// Set requested Canvas height.
103    pub fn height(mut self, height: Length) -> Self {
104        self.height = height;
105        self
106    }
107}
108
109impl From<Canvas> for Element {
110    fn from(value: Canvas) -> Self {
111        Element::new(ElementKind::Canvas(value))
112    }
113}
114
115impl LayoutHash for Canvas {
116    fn layout_hash(
117        &self,
118        hasher: &mut impl std::hash::Hasher,
119        recurse: &dyn Fn(&Element) -> Option<u64>,
120    ) -> Option<()> {
121        use std::hash::Hash;
122
123        self.passthrough.hash(hasher);
124        self.width.hash(hasher);
125        self.height.hash(hasher);
126        self.items.len().hash(hasher);
127        for item in &self.items {
128            item.rect.hash(hasher);
129            recurse(&item.element)?.hash(hasher);
130        }
131        Some(())
132    }
133}