Skip to main content

cranpose_ui/
draw.rs

1use std::rc::Rc;
2
3use cranpose_ui_graphics::{CommandRecording, DrawPrimitive, DrawScope, DrawScopeDefault};
4
5use crate::{modifier::Size, text::AppContextTextMeasurer};
6
7/// A draw command records into a scope the CONSUMER provides, instead of
8/// returning a freshly allocated primitive vector. This is the boundary that
9/// lets recording storage live with the command's stable identity and be
10/// reused frame over frame (the retained draw-command recorder), rather than
11/// being reallocated, moved, and dropped on every execution.
12pub type DrawCommandFn = Rc<dyn Fn(&mut DrawScopeDefault)>;
13
14/// A draw command and the phase of the node's drawing in which it runs.
15#[derive(Clone)]
16pub enum DrawCommand {
17    /// Records drawing that appears behind the node's content.
18    Behind(DrawCommandFn),
19    /// Records drawing around the node's content. A `draw_content` call in it marks where
20    /// the content draws, as in Compose's `drawWithContent`.
21    WithContent(DrawCommandFn),
22    /// Records drawing that appears in front of the node's content.
23    Overlay(DrawCommandFn),
24}
25
26/// The scope every framework-run draw command records into: sized by the
27/// consumer, measuring text with the app's fonts.
28pub fn command_draw_scope(size: Size) -> DrawScopeDefault {
29    DrawScopeDefault::with_text_measurer(size, AppContextTextMeasurer::shared())
30}
31
32/// [`command_draw_scope`] recording into storage the consumer already owns —
33/// the retained-recorder path where a command's buffer keeps its capacity
34/// across frames.
35pub fn command_draw_scope_reusing(size: Size, storage: CommandRecording) -> DrawScopeDefault {
36    DrawScopeDefault::with_text_measurer_reusing(size, AppContextTextMeasurer::shared(), storage)
37}
38
39/// Collects the draw commands that a `Modifier::draw_with_cache` block records.
40#[derive(Default, Clone)]
41pub struct DrawCacheBuilder {
42    behind: Vec<DrawCommandFn>,
43    with_content: Vec<DrawCommandFn>,
44    overlay: Vec<DrawCommandFn>,
45}
46
47impl DrawCacheBuilder {
48    /// Records drawing that appears behind the node's content.
49    pub fn on_draw_behind(&mut self, f: impl Fn(&mut dyn DrawScope) + 'static) {
50        self.behind
51            .push(Rc::new(move |scope: &mut DrawScopeDefault| f(scope)));
52    }
53
54    /// Returns the recorded commands in the order behind, with-content, overlay.
55    pub fn finish(self) -> Vec<DrawCommand> {
56        let mut commands = Vec::new();
57        commands.extend(self.behind.into_iter().map(DrawCommand::Behind));
58        commands.extend(self.with_content.into_iter().map(DrawCommand::WithContent));
59        commands.extend(self.overlay.into_iter().map(DrawCommand::Overlay));
60        commands
61    }
62}
63
64/// Runs every command at `size` and returns the primitives they record, in command order.
65pub fn execute_draw_commands(commands: &[DrawCommand], size: Size) -> Vec<DrawPrimitive> {
66    let mut primitives = Vec::new();
67    for command in commands {
68        match command {
69            DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
70                let mut scope = command_draw_scope(size);
71                f(&mut scope);
72                primitives.extend(scope.into_primitives());
73            }
74        }
75    }
76    primitives
77}