1use floem_reactive::SignalTracker;
2use peniko::kurbo::Size;
3
4use crate::{
5 context::{LayoutChanged, LayoutChangedListener, PaintCx},
6 event::listener::EventListenerTrait,
7 view::{View, ViewId},
8};
9
10#[allow(clippy::type_complexity)]
12pub struct Canvas {
13 id: ViewId,
14 paint_fn: Box<dyn Fn(&mut PaintCx, Size)>,
15 size: Size,
16 tracker: Option<SignalTracker>,
17}
18
19pub fn canvas(paint: impl Fn(&mut PaintCx, Size) + 'static) -> Canvas {
43 let id = ViewId::new();
44 id.register_listener(LayoutChangedListener::listener_key());
45
46 Canvas {
47 id,
48 paint_fn: Box::new(paint),
49 size: Default::default(),
50 tracker: None,
51 }
52}
53
54impl Canvas {
55 fn post_layout(&mut self, new_layout: &LayoutChanged) {
56 self.size = new_layout.new_box.size();
57 }
58}
59
60impl View for Canvas {
61 fn id(&self) -> ViewId {
62 self.id
63 }
64
65 fn debug_name(&self) -> std::borrow::Cow<'static, str> {
66 "Canvas".into()
67 }
68
69 fn event(&mut self, cx: &mut crate::event::EventCx) -> crate::event::EventPropagation {
70 if let Some(new_layout) = LayoutChangedListener::extract(&cx.event) {
72 self.post_layout(new_layout);
73 }
74 crate::event::EventPropagation::Continue
75 }
76
77 fn paint(&mut self, cx: &mut PaintCx) {
78 let id = self.id;
79 let paint = &self.paint_fn;
80
81 if self.tracker.is_none() {
82 self.tracker = Some(SignalTracker::new(move || {
83 id.request_paint();
84 }));
85 }
86
87 let tracker = self.tracker.as_ref().unwrap();
88 tracker.track(|| {
89 paint(cx, self.size);
90 });
91 }
92}