1mod program;
52
53pub use program::Program;
54
55pub use crate::Action;
56pub use crate::core::event::Event;
57pub use crate::graphics::cache::Group;
58pub use crate::graphics::geometry::{
59 Fill, Gradient, Image, LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, fill, gradient,
60 path, stroke,
61};
62
63use crate::core::event;
64use crate::core::layout::{self, Layout};
65use crate::core::mouse;
66use crate::core::renderer;
67use crate::core::widget::Meta;
68use crate::core::widget::tree::{self, Tree};
69use crate::core::window;
70use crate::core::{Length, Rectangle, Shell, Size, Vector, Widget};
71use crate::graphics::geometry;
72
73use std::marker::PhantomData;
74
75pub type Cache<Renderer = crate::Renderer> = geometry::Cache<Renderer>;
80
81pub type Geometry<Renderer = crate::Renderer> = <Renderer as geometry::Renderer>::Geometry;
83
84pub type Frame<Renderer = crate::Renderer> = geometry::Frame<Renderer>;
86
87#[derive(Debug)]
138pub struct Canvas<P, Message, Theme = crate::Theme, Renderer = crate::Renderer>
139where
140 Renderer: geometry::Renderer,
141 P: Program<Message, Theme, Renderer>,
142{
143 width: Length,
144 height: Length,
145 program: P,
146 message_: PhantomData<Message>,
147 theme_: PhantomData<Theme>,
148 renderer_: PhantomData<Renderer>,
149 last_mouse_interaction: Option<mouse::Interaction>,
150}
151
152impl<P, Message, Theme, Renderer> Canvas<P, Message, Theme, Renderer>
153where
154 P: Program<Message, Theme, Renderer>,
155 Renderer: geometry::Renderer,
156{
157 const DEFAULT_SIZE: f32 = 100.0;
158
159 pub fn new(program: P) -> Self {
161 Canvas {
162 width: Length::Fixed(Self::DEFAULT_SIZE),
163 height: Length::Fixed(Self::DEFAULT_SIZE),
164 program,
165 message_: PhantomData,
166 theme_: PhantomData,
167 renderer_: PhantomData,
168 last_mouse_interaction: None,
169 }
170 }
171
172 pub fn width(mut self, width: impl Into<Length>) -> Self {
174 self.width = width.into();
175 self
176 }
177
178 pub fn height(mut self, height: impl Into<Length>) -> Self {
180 self.height = height.into();
181 self
182 }
183}
184
185impl<P, Message, Theme, Renderer> Meta for Canvas<P, Message, Theme, Renderer>
186where
187 Renderer: geometry::Renderer,
188 P: Program<Message, Theme, Renderer>,
189{
190}
191
192impl<P, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
193 for Canvas<P, Message, Theme, Renderer>
194where
195 Renderer: geometry::Renderer,
196 P: Program<Message, Theme, Renderer>,
197{
198 fn tag(&self) -> tree::Tag {
199 struct Tag<T>(T);
200 tree::Tag::of::<Tag<P::State>>()
201 }
202
203 fn state(&self) -> tree::State {
204 tree::State::new(P::State::default())
205 }
206
207 fn size(&self) -> Size<Length> {
208 Size {
209 width: self.width,
210 height: self.height,
211 }
212 }
213
214 fn layout(&mut self, tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits) {
215 tree.size = layout::atomic(limits, self.width, self.height);
216 }
217
218 fn update(
219 &mut self,
220 tree: &mut Tree,
221 event: &Event,
222 layout: Layout,
223 cursor: mouse::Cursor,
224 renderer: &Renderer,
225 shell: &mut Shell<'_, Message>,
226 viewport: &Rectangle,
227 ) {
228 let bounds = layout.bounds();
229
230 let state = tree.state.downcast_mut::<P::State>();
231 let is_redraw_request =
232 matches!(event, Event::Window(window::Event::RedrawRequested(_now)),);
233
234 if let Some(action) = self.program.update(state, event, bounds, cursor) {
235 let (message, redraw_request, event_status) = action.into_inner();
236
237 shell.request_redraw_at(redraw_request);
238
239 if let Some(message) = message {
240 shell.publish(message);
241 }
242
243 if event_status == event::Status::Captured {
244 shell.capture_event();
245 }
246 }
247
248 if shell.redraw_request() != window::RedrawRequest::NextFrame {
249 let mouse_interaction =
250 self.mouse_interaction(tree, layout, cursor, viewport, renderer);
251
252 if is_redraw_request {
253 self.last_mouse_interaction = Some(mouse_interaction);
254 } else if self
255 .last_mouse_interaction
256 .is_some_and(|last_mouse_interaction| last_mouse_interaction != mouse_interaction)
257 {
258 shell.request_redraw();
259 }
260 }
261 }
262
263 fn mouse_interaction(
264 &self,
265 tree: &Tree,
266 layout: Layout,
267 cursor: mouse::Cursor,
268 _viewport: &Rectangle,
269 _renderer: &Renderer,
270 ) -> mouse::Interaction {
271 let bounds = layout.bounds();
272 let state = tree.state.downcast_ref::<P::State>();
273
274 self.program.mouse_interaction(state, bounds, cursor)
275 }
276
277 fn draw(
278 &self,
279 tree: &Tree,
280 renderer: &mut Renderer,
281 theme: &Theme,
282 _style: &renderer::Style,
283 layout: Layout,
284 cursor: mouse::Cursor,
285 _viewport: &Rectangle,
286 ) {
287 let bounds = layout.bounds();
288
289 if bounds.width < 1.0 || bounds.height < 1.0 {
290 return;
291 }
292
293 let state = tree.state.downcast_ref::<P::State>();
294
295 renderer.with_translation(Vector::new(bounds.x, bounds.y), |renderer| {
296 let layers = self.program.draw(state, renderer, theme, bounds, cursor);
297
298 for layer in layers {
299 renderer.draw_geometry(layer);
300 }
301 });
302 }
303}