Skip to main content

oxframe_2d/
node_api.rs

1//! Nodes of your own: a struct that draws itself with a [`DrawCtx`].
2//!
3//! Most code reaches this through `#[derive(Node)]` in `oxframe`, which
4//! writes the builder, the handle and the signals. The functions here are
5//! what that derive calls; they are public so the expansion compiles in
6//! your crate.
7
8use std::any::Any;
9use std::rc::Rc;
10
11use crate::draw::DrawCtx;
12use crate::graph::{NodeId, NodeKind, SceneGraph, with_graph};
13use crate::handles::Node;
14
15/// Size of a node, computed from its current values.
16pub type SizeFn = Rc<dyn Fn() -> (f64, f64)>;
17
18/// What a node of your own draws, in its own coordinates (origin at the
19/// center of its box, y down).
20///
21/// `draw` must be a function of the node's values and `ctx.time()`: the
22/// frame is painted again on seek, and `check` reports a node whose two
23/// paints of one frame differ. Use `ctx.rng(seed)` for randomness.
24pub trait Draw: 'static {
25    /// Paints the node with `ctx.line`, `ctx.circle`, `ctx.rect`,
26    /// `ctx.arc` and `ctx.path`.
27    fn draw(&self, ctx: &mut DrawCtx);
28
29    /// The node's box: what layout, `Camera::fit` and `check` use. The
30    /// default is 100 by 100.
31    fn size(&self) -> (f64, f64) {
32        (100.0, 100.0)
33    }
34}
35
36/// Adds a node that draws `snapshot()` every frame.
37///
38/// `snapshot` rebuilds the value from the node's signals, so `draw` sees
39/// the values of the frame being painted. `state` holds those signals; the
40/// handle gets it back with [`state`].
41#[doc(hidden)]
42pub fn add<T: Draw>(
43    graph: &SceneGraph,
44    parent: NodeId,
45    base: crate::BaseProps,
46    type_name: &'static str,
47    snapshot: Rc<dyn Fn() -> T>,
48    state: Rc<dyn Any>,
49) -> NodeId {
50    let paint = snapshot.clone();
51    let measure = snapshot;
52    let kind = NodeKind::Custom {
53        draw: Rc::new(move |ctx: &mut DrawCtx, _| paint().draw(ctx)),
54        width: 0.0,
55        height: 0.0,
56        measure: Some(Rc::new(move || measure().size())),
57        state: Some(state),
58        type_name,
59    };
60    crate::builder::insert(graph, parent, kind, base)
61}
62
63/// The signals stored by [`add`] for `node`.
64///
65/// # Panics
66///
67/// If `node` was not added with state of type `S`.
68#[doc(hidden)]
69pub fn state<S: 'static>(node: Node) -> Rc<S> {
70    with_graph(|g| match &g.node(node.id()).kind {
71        NodeKind::Custom {
72            state: Some(s),
73            type_name,
74            ..
75        } => s
76            .clone()
77            .downcast::<S>()
78            .unwrap_or_else(|_| panic!("{type_name} node has state of another type")),
79        _ => panic!("this handle does not point to a derived node"),
80    })
81}