Skip to main content

widgetkit_render/
canvas.rs

1use crate::model::{
2    ClearCommand, ClipCommand, ClipPrimitive, CommandList, Fill, FillCommand, FillShape,
3    ImageCommand, ImageSource, Paint, RenderCommand, RenderFrame, StateCommand, StrokeCommand,
4    StrokeShape, TextCommand, Transform, TransformCommand,
5};
6use crate::text::measure_text;
7use crate::{Stroke, TextMetrics, TextStyle};
8use widgetkit_core::{Color, Point, Rect, Size};
9
10#[derive(Debug)]
11pub struct Canvas {
12    size: Size,
13    commands: CommandList,
14}
15
16pub struct RawCanvas<'a> {
17    commands: &'a mut CommandList,
18}
19
20impl Canvas {
21    pub fn new(size: Size) -> Self {
22        Self {
23            size,
24            commands: CommandList::new(),
25        }
26    }
27
28    pub fn size(&self) -> Size {
29        self.size
30    }
31
32    pub fn measure_text(&self, text: impl AsRef<str>, style: &TextStyle) -> TextMetrics {
33        measure_text(text.as_ref(), style)
34    }
35
36    pub fn clear(&mut self, color: Color) {
37        self.raw().clear(color);
38    }
39
40    pub fn rect(&mut self, rect: Rect, color: Color) {
41        self.raw().fill_rect(rect, color);
42    }
43
44    pub fn round_rect(&mut self, rect: Rect, radius: f32, color: Color) {
45        self.raw().fill_round_rect(rect, radius, color);
46    }
47
48    pub fn line(&mut self, start: Point, end: Point, stroke: Stroke, color: Color) {
49        self.raw().stroke_line(start, end, stroke, color);
50    }
51
52    pub fn circle(&mut self, center: Point, radius: f32, color: Color) {
53        self.raw().fill_circle(center, radius, color);
54    }
55
56    pub fn ellipse(&mut self, center: Point, radius_x: f32, radius_y: f32, color: Color) {
57        self.raw().fill_ellipse(center, radius_x, radius_y, color);
58    }
59
60    pub fn text(
61        &mut self,
62        position: Point,
63        text: impl Into<String>,
64        style: TextStyle,
65        color: Color,
66    ) {
67        self.raw().draw_text(position, text, style, color);
68    }
69
70    pub fn image_placeholder(&mut self, rect: Rect, color: Color) {
71        self.raw().image_placeholder(rect, color);
72    }
73
74    pub fn clip_rect(&mut self, rect: Rect) {
75        self.raw().clip_rect(rect);
76    }
77
78    pub fn save(&mut self) {
79        self.raw().save();
80    }
81
82    pub fn restore(&mut self) {
83        self.raw().restore();
84    }
85
86    pub fn translate(&mut self, dx: f32, dy: f32) {
87        self.raw().translate(dx, dy);
88    }
89
90    pub fn experimental_raw(&mut self, f: impl FnOnce(&mut RawCanvas<'_>)) {
91        let mut raw = self.raw();
92        f(&mut raw);
93    }
94
95    fn raw(&mut self) -> RawCanvas<'_> {
96        RawCanvas {
97            commands: &mut self.commands,
98        }
99    }
100
101    pub fn into_frame(self) -> RenderFrame {
102        RenderFrame::from_list(self.size, self.commands)
103    }
104}
105
106impl RawCanvas<'_> {
107    pub fn push(&mut self, command: RenderCommand) {
108        self.commands.push(command);
109    }
110
111    pub fn clear(&mut self, color: Color) {
112        self.push(RenderCommand::Clear(ClearCommand {
113            paint: Paint::solid(color),
114        }));
115    }
116
117    pub fn fill_rect(&mut self, rect: Rect, color: Color) {
118        self.push(RenderCommand::Fill(FillCommand {
119            shape: FillShape::Rect(rect),
120            fill: Fill::solid(color),
121        }));
122    }
123
124    pub fn fill_round_rect(&mut self, rect: Rect, radius: f32, color: Color) {
125        self.push(RenderCommand::Fill(FillCommand {
126            shape: FillShape::RoundRect {
127                rect,
128                radius: radius.max(0.0),
129            },
130            fill: Fill::solid(color),
131        }));
132    }
133
134    pub fn fill_circle(&mut self, center: Point, radius: f32, color: Color) {
135        self.push(RenderCommand::Fill(FillCommand {
136            shape: FillShape::Circle {
137                center,
138                radius: radius.max(0.0),
139            },
140            fill: Fill::solid(color),
141        }));
142    }
143
144    pub fn fill_ellipse(&mut self, center: Point, radius_x: f32, radius_y: f32, color: Color) {
145        self.push(RenderCommand::Fill(FillCommand {
146            shape: FillShape::Ellipse {
147                center,
148                radius_x: radius_x.max(0.0),
149                radius_y: radius_y.max(0.0),
150            },
151            fill: Fill::solid(color),
152        }));
153    }
154
155    pub fn stroke_line(&mut self, start: Point, end: Point, stroke: Stroke, color: Color) {
156        self.push(RenderCommand::Stroke(StrokeCommand {
157            shape: StrokeShape::Line { start, end },
158            stroke,
159            paint: Paint::solid(color),
160        }));
161    }
162
163    pub fn draw_text(
164        &mut self,
165        position: Point,
166        text: impl Into<String>,
167        style: TextStyle,
168        color: Color,
169    ) {
170        self.push(RenderCommand::Text(TextCommand {
171            position,
172            text: text.into(),
173            style,
174            paint: Paint::solid(color),
175        }));
176    }
177
178    pub fn image_placeholder(&mut self, rect: Rect, color: Color) {
179        self.push(RenderCommand::Image(ImageCommand {
180            rect,
181            source: ImageSource::Placeholder,
182            paint: Paint::solid(color),
183        }));
184    }
185
186    pub fn clip_rect(&mut self, rect: Rect) {
187        self.push(RenderCommand::Clip(ClipCommand {
188            primitive: ClipPrimitive::Rect(rect),
189        }));
190    }
191
192    pub fn save(&mut self) {
193        self.push(RenderCommand::State(StateCommand::Save));
194    }
195
196    pub fn restore(&mut self) {
197        self.push(RenderCommand::State(StateCommand::Restore));
198    }
199
200    pub fn translate(&mut self, dx: f32, dy: f32) {
201        self.push(RenderCommand::Transform(TransformCommand {
202            transform: Transform::translation(dx, dy),
203        }));
204    }
205}
206
207// TODO(v0.3): image draw API stabilization
208// TODO(v0.3): transform stack stabilization
209// TODO(v0.3): richer clipping model
210// TODO(v0.5): map declarative nodes to Canvas without API gaps