Skip to main content

sequoia_view/
dev_overlay.rs

1use sequoia_layout::LayoutStyle;
2
3use crate::any::AnyView;
4use crate::runtime::Runtime;
5use crate::view::View;
6use crate::views::{Column, Row, Text, Unit};
7
8#[derive(Clone)]
9pub struct DevOverlay {
10    rt: Runtime,
11    tick: sequoia_reactive::Signal<u64>,
12    show_errors: sequoia_reactive::Signal<bool>,
13}
14
15impl DevOverlay {
16    pub fn new(rt: Runtime) -> Self {
17        Self {
18            rt,
19            tick: sequoia_reactive::Signal::new(0),
20            show_errors: sequoia_reactive::Signal::new(true),
21        }
22    }
23
24    pub fn summary_untracked(&self) -> String {
25        let s = self.rt.stats();
26        format!(
27            "el={} mounts={} rebuilds={} unmounts={} errors={}",
28            self.rt.element_count(),
29            s.mounts,
30            s.rebuilds,
31            s.unmounts,
32            s.errors
33        )
34    }
35}
36
37impl View for DevOverlay {
38    fn body(&self) -> impl View + 'static {
39        Unit
40    }
41
42    fn children_override(&self) -> Option<Vec<AnyView>> {
43        let _ = self.tick.get();
44        let show_errors = self.show_errors.get();
45        let s = self.rt.stats();
46        let header = Text::new(format!(
47            "el={} mounts={} rebuilds={} unmounts={} errors={}",
48            self.rt.element_count(),
49            s.mounts,
50            s.rebuilds,
51            s.unmounts,
52            s.errors
53        ))
54        .size(11.0);
55        let tick = self.tick.clone();
56        let refresh = RefreshBtn {
57            on_press: std::rc::Rc::new(move || tick.update(|n| *n += 1)),
58        };
59        let show = self.show_errors.clone();
60        let toggle = RefreshBtn {
61            on_press: std::rc::Rc::new(move || show.update(|v| *v = !*v)),
62        };
63        let mut col = Column::new()
64            .gap(6.0)
65            .child(header)
66            .child(Row::new().gap(8.0).child(refresh).child(toggle));
67        if show_errors {
68            for e in self.rt.errors().into_iter().rev().take(8) {
69                col = col.child(Text::new(format!("⚠ {}: {}", e.type_name, e.message)).size(11.0));
70            }
71        }
72        Some(vec![AnyView::new(col)])
73    }
74
75    fn layout_style(&self) -> LayoutStyle {
76        LayoutStyle::default()
77    }
78}
79
80#[derive(Clone)]
81struct RefreshBtn {
82    on_press: std::rc::Rc<dyn Fn()>,
83}
84
85impl View for RefreshBtn {
86    fn body(&self) -> impl View + 'static {
87        Unit
88    }
89
90    fn children_override(&self) -> Option<Vec<AnyView>> {
91        Some(vec![AnyView::new(Text::new("↻ refresh").size(11.0).bold())])
92    }
93
94    fn layout_style(&self) -> LayoutStyle {
95        LayoutStyle::default()
96    }
97
98    fn on_event(&self, event: &sequoia_input::Event, ctx: &mut crate::view::EventCtx) {
99        match event {
100            sequoia_input::Event::Pointer(sequoia_input::PointerEvent::Up {
101                button: sequoia_input::PointerButton::Left,
102                pos,
103                ..
104            }) if ctx.rect.contains(*pos) => {
105                (self.on_press)();
106                ctx.handled();
107            }
108            _ => {}
109        }
110    }
111}