Skip to main content

repose_canvas/
lib.rs

1#![allow(non_snake_case)]
2use std::sync::Arc;
3
4use repose_core::*;
5use repose_ui::*;
6
7pub struct DrawScope {
8    pub commands: Vec<DrawCommand>,
9    pub size: Size,
10}
11
12#[derive(Clone)]
13pub enum DrawCommand {
14    Rect {
15        rect: Rect,
16        color: Color,
17        radius: f32,
18        stroke: Option<(f32, Color)>,
19    },
20    Ellipse {
21        center: Vec2,
22        rx: f32,
23        ry: f32,
24        color: Color,
25        stroke: Option<(f32, Color)>,
26    },
27    Text {
28        text: String,
29        pos: Vec2,
30        color: Color,
31        size: f32,
32    },
33}
34
35impl DrawScope {
36    pub fn draw_rect(&mut self, rect: Rect, color: Color, radius: f32) {
37        self.commands.push(DrawCommand::Rect {
38            rect,
39            color,
40            radius,
41            stroke: None,
42        });
43    }
44    pub fn draw_rect_stroke(&mut self, rect: Rect, color: Color, radius: f32, width: f32) {
45        self.commands.push(DrawCommand::Rect {
46            rect,
47            color,
48            radius,
49            stroke: Some((width, color)),
50        });
51    }
52    pub fn draw_ellipse(&mut self, center: Vec2, rx: f32, ry: f32, color: Color) {
53        self.commands.push(DrawCommand::Ellipse {
54            center,
55            rx: rx.max(0.0),
56            ry: ry.max(0.0),
57            color,
58            stroke: None,
59        });
60    }
61    pub fn draw_ellipse_stroke(
62        &mut self,
63        center: Vec2,
64        rx: f32,
65        ry: f32,
66        color: Color,
67        width: f32,
68    ) {
69        self.commands.push(DrawCommand::Ellipse {
70            center,
71            rx: rx.max(0.0),
72            ry: ry.max(0.0),
73            color,
74            stroke: Some((width.max(0.0), color)),
75        });
76    }
77    pub fn draw_circle(&mut self, center: Vec2, radius: f32, color: Color) {
78        self.draw_ellipse(center, radius, radius, color);
79    }
80    pub fn draw_circle_stroke(&mut self, center: Vec2, radius: f32, color: Color, width: f32) {
81        self.draw_ellipse_stroke(center, radius, radius, color, width);
82    }
83    pub fn draw_text(&mut self, text: impl Into<String>, pos: Vec2, color: Color, size: f32) {
84        self.commands.push(DrawCommand::Text {
85            text: text.into(),
86            pos,
87            color,
88            size,
89        });
90    }
91}
92
93pub fn Canvas(modifier: Modifier, on_draw: impl Fn(&mut DrawScope) + 'static) -> View {
94    // Painter replays drawing each frame, so Canvas can react to signals/animation.
95    let painter = move |scene: &mut Scene, rect: Rect| {
96        let mut scope = DrawScope {
97            commands: Vec::new(),
98            size: Size {
99                width: rect.w.max(0.0),
100                height: rect.h.max(0.0),
101            },
102        };
103        on_draw(&mut scope);
104
105        // local->global helper
106        let to_global = |r: Rect| Rect {
107            x: rect.x + r.x,
108            y: rect.y + r.y,
109            w: r.w,
110            h: r.h,
111        };
112
113        for cmd in &scope.commands {
114            match cmd {
115                DrawCommand::Rect {
116                    rect: r,
117                    color,
118                    radius,
119                    stroke,
120                } => {
121                    scene.nodes.push(SceneNode::Rect {
122                        rect: to_global(*r),
123                        brush: Brush::Solid(*color),
124                        radius: *radius,
125                    });
126                    if let Some((w, c)) = stroke {
127                        scene.nodes.push(SceneNode::Border {
128                            rect: to_global(*r),
129                            color: *c,
130                            width: *w,
131                            radius: *radius,
132                        });
133                    }
134                }
135                DrawCommand::Ellipse {
136                    center,
137                    rx,
138                    ry,
139                    color,
140                    stroke,
141                } => {
142                    let r = Rect {
143                        x: center.x - *rx,
144                        y: center.y - *ry,
145                        w: 2.0 * *rx,
146                        h: 2.0 * *ry,
147                    };
148                    scene.nodes.push(SceneNode::Ellipse {
149                        rect: to_global(r),
150                        brush: Brush::Solid(*color),
151                    });
152                    if let Some((w, c)) = stroke {
153                        scene.nodes.push(SceneNode::EllipseBorder {
154                            rect: to_global(r),
155                            color: *c,
156                            width: *w,
157                        });
158                    }
159                }
160                DrawCommand::Text {
161                    text,
162                    pos,
163                    color,
164                    size,
165                } => {
166                    scene.nodes.push(SceneNode::Text {
167                        rect: Rect {
168                            x: rect.x + pos.x,
169                            y: rect.y + pos.y,
170                            w: 0.0,
171                            h: *size,
172                        },
173                        text: Arc::<str>::from(text.clone()),
174                        color: *color,
175                        size: *size,
176                    });
177                }
178            }
179        }
180    };
181
182    // Respect caller sizing. Only apply a default if they didn't specify any size behavior.
183    let mut m = modifier.painter(painter);
184    let has_size = m.size.is_some()
185        || m.width.is_some()
186        || m.height.is_some()
187        || m.fill_max
188        || m.fill_max_w
189        || m.fill_max_h;
190    if !has_size {
191        m = m.size(100.0, 100.0);
192    }
193
194    Box(m)
195}