//! Draw 2D graphics for your users. pub mod event; mod program; pub use event::Event; pub use program::Program; pub use crate::graphics::geometry::*; pub use crate::renderer::geometry::*; use crate::core; use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::{Clipboard, Element, Shell, Widget}; use crate::core::{Length, Rectangle, Size, Vector}; use crate::graphics::geometry; use std::marker::PhantomData; /// A widget capable of drawing 2D graphics. /// /// ## Drawing a simple circle /// If you want to get a quick overview, here's how we can draw a simple circle: /// /// ```no_run /// # use iced_widget::canvas::{self, Canvas, Fill, Frame, Geometry, Path, Program}; /// # use iced_widget::core::{Color, Rectangle}; /// # use iced_widget::core::mouse; /// # use iced_widget::style::Theme; /// # /// # pub type Renderer = iced_widget::renderer::Renderer; /// // First, we define the data we need for drawing /// #[derive(Debug)] /// struct Circle { /// radius: f32, /// } /// /// // Then, we implement the `Program` trait /// impl Program<()> for Circle { /// type State = (); /// /// fn draw(&self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: mouse::Cursor) -> Vec{ /// // We prepare a new `Frame` /// let mut frame = Frame::new(renderer, bounds.size()); /// /// // We create a `Path` representing a simple circle /// let circle = Path::circle(frame.center(), self.radius); /// /// // And fill it with some color /// frame.fill(&circle, Color::BLACK); /// /// // Finally, we produce the geometry /// vec![frame.into_geometry()] /// } /// } /// /// // Finally, we simply use our `Circle` to create the `Canvas`! /// let canvas = Canvas::new(Circle { radius: 50.0 }); /// ``` #[derive(Debug)] pub struct Canvas where Renderer: geometry::Renderer, P: Program, { width: Length, height: Length, program: P, message_: PhantomData, theme_: PhantomData, } impl Canvas where Renderer: geometry::Renderer, P: Program, { const DEFAULT_SIZE: f32 = 100.0; /// Creates a new [`Canvas`]. pub fn new(program: P) -> Self { Canvas { width: Length::Fixed(Self::DEFAULT_SIZE), height: Length::Fixed(Self::DEFAULT_SIZE), program, message_: PhantomData, theme_: PhantomData, } } /// Sets the width of the [`Canvas`]. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the height of the [`Canvas`]. pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); self } } impl Widget for Canvas where Renderer: geometry::Renderer, P: Program, { fn tag(&self) -> tree::Tag { struct Tag(T); tree::Tag::of::>() } fn state(&self) -> tree::State { tree::State::new(P::State::default()) } fn width(&self) -> Length { self.width } fn height(&self) -> Length { self.height } fn layout( &self, _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let limits = limits.width(self.width).height(self.height); let size = limits.resolve(Size::ZERO); layout::Node::new(size) } fn on_event( &mut self, tree: &mut Tree, event: core::Event, layout: Layout<'_>, cursor: mouse::Cursor, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { let bounds = layout.bounds(); let canvas_event = match event { core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)), core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)), core::Event::Keyboard(keyboard_event) => { Some(Event::Keyboard(keyboard_event)) } _ => None, }; if let Some(canvas_event) = canvas_event { let state = tree.state.downcast_mut::(); let (event_status, message) = self.program.update(state, canvas_event, bounds, cursor); if let Some(message) = message { shell.publish(message); } return event_status; } event::Status::Ignored } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, cursor: mouse::Cursor, _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { let bounds = layout.bounds(); let state = tree.state.downcast_ref::(); self.program.mouse_interaction(state, bounds, cursor) } fn draw( &self, tree: &Tree, renderer: &mut Renderer, theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, _viewport: &Rectangle, ) { let bounds = layout.bounds(); if bounds.width < 1.0 || bounds.height < 1.0 { return; } let state = tree.state.downcast_ref::(); renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { renderer.draw( self.program.draw(state, renderer, theme, bounds, cursor), ); }, ); } } impl<'a, P, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a, Renderer: 'a + geometry::Renderer, P: Program + 'a, { fn from( canvas: Canvas, ) -> Element<'a, Message, Renderer> { Element::new(canvas) } }