1pub mod event;
52
53mod program;
54
55pub use event::Event;
56pub use program::Program;
57
58pub use crate::graphics::cache::Group;
59pub use crate::graphics::geometry::{
60 fill, gradient, path, stroke, Fill, Gradient, Image, LineCap, LineDash,
61 LineJoin, Path, Stroke, Style, Text,
62};
63
64use crate::core;
65use crate::core::layout::{self, Layout};
66use crate::core::mouse;
67use crate::core::renderer;
68use crate::core::widget::tree::{self, Tree};
69use crate::core::{
70 Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
71};
72use crate::graphics::geometry;
73
74use std::marker::PhantomData;
75
76pub type Cache<Renderer = crate::Renderer> = geometry::Cache<Renderer>;
81
82pub type Geometry<Renderer = crate::Renderer> =
84 <Renderer as geometry::Renderer>::Geometry;
85
86pub type Frame<Renderer = crate::Renderer> = geometry::Frame<Renderer>;
88
89#[derive(Debug)]
140pub struct Canvas<P, Message, Theme = crate::Theme, Renderer = crate::Renderer>
141where
142 Renderer: geometry::Renderer,
143 P: Program<Message, Theme, Renderer>,
144{
145 width: Length,
146 height: Length,
147 program: P,
148 message_: PhantomData<Message>,
149 theme_: PhantomData<Theme>,
150 renderer_: PhantomData<Renderer>,
151}
152
153impl<P, Message, Theme, Renderer> Canvas<P, Message, Theme, Renderer>
154where
155 P: Program<Message, Theme, Renderer>,
156 Renderer: geometry::Renderer,
157{
158 const DEFAULT_SIZE: f32 = 100.0;
159
160 pub fn new(program: P) -> Self {
162 Canvas {
163 width: Length::Fixed(Self::DEFAULT_SIZE),
164 height: Length::Fixed(Self::DEFAULT_SIZE),
165 program,
166 message_: PhantomData,
167 theme_: PhantomData,
168 renderer_: PhantomData,
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> Widget<Message, Theme, Renderer>
186 for Canvas<P, Message, Theme, Renderer>
187where
188 Renderer: geometry::Renderer,
189 P: Program<Message, Theme, Renderer>,
190{
191 fn tag(&self) -> tree::Tag {
192 struct Tag<T>(T);
193 tree::Tag::of::<Tag<P::State>>()
194 }
195
196 fn state(&self) -> tree::State {
197 tree::State::new(P::State::default())
198 }
199
200 fn size(&self) -> Size<Length> {
201 Size {
202 width: self.width,
203 height: self.height,
204 }
205 }
206
207 fn layout(
208 &self,
209 _tree: &mut Tree,
210 _renderer: &Renderer,
211 limits: &layout::Limits,
212 ) -> layout::Node {
213 layout::atomic(limits, self.width, self.height)
214 }
215
216 fn on_event(
217 &mut self,
218 tree: &mut Tree,
219 event: core::Event,
220 layout: Layout<'_>,
221 cursor: mouse::Cursor,
222 _renderer: &Renderer,
223 _clipboard: &mut dyn Clipboard,
224 shell: &mut Shell<'_, Message>,
225 _viewport: &Rectangle,
226 ) -> event::Status {
227 let bounds = layout.bounds();
228
229 let canvas_event = match event {
230 core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)),
231 core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)),
232 core::Event::Keyboard(keyboard_event) => {
233 Some(Event::Keyboard(keyboard_event))
234 }
235 core::Event::Window(_) => None,
236 _ => None,
237 };
238
239 if let Some(canvas_event) = canvas_event {
240 let state = tree.state.downcast_mut::<P::State>();
241
242 let (event_status, message) =
243 self.program.update(state, canvas_event, bounds, cursor);
244
245 if let Some(message) = message {
246 shell.publish(message);
247 }
248
249 return event_status;
250 }
251
252 event::Status::Ignored
253 }
254
255 fn mouse_interaction(
256 &self,
257 tree: &Tree,
258 layout: Layout<'_>,
259 cursor: mouse::Cursor,
260 _viewport: &Rectangle,
261 _renderer: &Renderer,
262 ) -> mouse::Interaction {
263 let bounds = layout.bounds();
264 let state = tree.state.downcast_ref::<P::State>();
265
266 self.program.mouse_interaction(state, bounds, cursor)
267 }
268
269 fn draw(
270 &self,
271 tree: &Tree,
272 renderer: &mut Renderer,
273 theme: &Theme,
274 _style: &renderer::Style,
275 layout: Layout<'_>,
276 cursor: mouse::Cursor,
277 _viewport: &Rectangle,
278 ) {
279 let bounds = layout.bounds();
280
281 if bounds.width < 1.0 || bounds.height < 1.0 {
282 return;
283 }
284
285 let state = tree.state.downcast_ref::<P::State>();
286
287 renderer.with_translation(
288 Vector::new(bounds.x, bounds.y),
289 |renderer| {
290 let layers =
291 self.program.draw(state, renderer, theme, bounds, cursor);
292
293 for layer in layers {
294 renderer.draw_geometry(layer);
295 }
296 },
297 );
298 }
299}
300
301impl<'a, P, Message, Theme, Renderer> From<Canvas<P, Message, Theme, Renderer>>
302 for Element<'a, Message, Theme, Renderer>
303where
304 Message: 'a,
305 Theme: 'a,
306 Renderer: 'a + geometry::Renderer,
307 P: 'a + Program<Message, Theme, Renderer>,
308{
309 fn from(
310 canvas: Canvas<P, Message, Theme, Renderer>,
311 ) -> Element<'a, Message, Theme, Renderer> {
312 Element::new(canvas)
313 }
314}