diff options
Diffstat (limited to '')
| -rw-r--r-- | graphics/src/widget/canvas.rs | 236 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/cache.rs (renamed from wgpu/src/widget/canvas/cache.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/cursor.rs (renamed from wgpu/src/widget/canvas/cursor.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/event.rs (renamed from wgpu/src/widget/canvas/event.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/fill.rs (renamed from wgpu/src/widget/canvas/fill.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/frame.rs (renamed from wgpu/src/widget/canvas/frame.rs) | 2 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/geometry.rs (renamed from wgpu/src/widget/canvas/geometry.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/path.rs (renamed from wgpu/src/widget/canvas/path.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/path/arc.rs (renamed from wgpu/src/widget/canvas/path/arc.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/path/builder.rs (renamed from wgpu/src/widget/canvas/path/builder.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/program.rs (renamed from wgpu/src/widget/canvas/program.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/stroke.rs (renamed from wgpu/src/widget/canvas/stroke.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/text.rs (renamed from wgpu/src/widget/canvas/text.rs) | 0 | 
13 files changed, 237 insertions, 1 deletions
| diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs new file mode 100644 index 00000000..d393a5c5 --- /dev/null +++ b/graphics/src/widget/canvas.rs @@ -0,0 +1,236 @@ +//! Draw 2D graphics for your users. +//! +//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a +//! [`Frame`]. It can be used for animation, data visualization, game graphics, +//! and more! +//! +//! [`Canvas`]: struct.Canvas.html +//! [`Frame`]: struct.Frame.html +use crate::{Backend, Defaults, Primitive, Renderer}; +use iced_native::{ +    layout, mouse, Clipboard, Element, Hasher, Layout, Length, Point, Size, +    Vector, Widget, +}; +use std::hash::Hash; +use std::marker::PhantomData; + +pub mod path; + +mod cache; +mod cursor; +mod event; +mod fill; +mod frame; +mod geometry; +mod program; +mod stroke; +mod text; + +pub use cache::Cache; +pub use cursor::Cursor; +pub use event::Event; +pub use fill::Fill; +pub use frame::Frame; +pub use geometry::Geometry; +pub use path::Path; +pub use program::Program; +pub use stroke::{LineCap, LineJoin, Stroke}; +pub use text::Text; + +/// A widget capable of drawing 2D graphics. +/// +/// [`Canvas`]: struct.Canvas.html +/// +/// # Examples +/// The repository has a couple of [examples] showcasing how to use a +/// [`Canvas`]: +/// +/// - [`clock`], an application that uses the [`Canvas`] widget to draw a clock +/// and its hands to display the current time. +/// - [`game_of_life`], an interactive version of the Game of Life, invented by +/// John Conway. +/// - [`solar_system`], an animated solar system drawn using the [`Canvas`] widget +/// and showcasing how to compose different transforms. +/// +/// [examples]: https://github.com/hecrj/iced/tree/master/examples +/// [`clock`]: https://github.com/hecrj/iced/tree/master/examples/clock +/// [`game_of_life`]: https://github.com/hecrj/iced/tree/master/examples/game_of_life +/// [`solar_system`]: https://github.com/hecrj/iced/tree/master/examples/solar_system +/// +/// ## Drawing a simple circle +/// If you want to get a quick overview, here's how we can draw a simple circle: +/// +/// ```no_run +/// # mod iced { +/// #     pub use iced_graphics::canvas; +/// #     pub use iced_native::{Color, Rectangle}; +/// # } +/// use iced::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program}; +/// use iced::{Color, Rectangle}; +/// +/// // 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 { +///     fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{ +///         // We prepare a new `Frame` +///         let mut frame = Frame::new(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, Fill::Color(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<Message, P: Program<Message>> { +    width: Length, +    height: Length, +    program: P, +    phantom: PhantomData<Message>, +} + +impl<Message, P: Program<Message>> Canvas<Message, P> { +    const DEFAULT_SIZE: u16 = 100; + +    /// Creates a new [`Canvas`]. +    /// +    /// [`Canvas`]: struct.Canvas.html +    pub fn new(program: P) -> Self { +        Canvas { +            width: Length::Units(Self::DEFAULT_SIZE), +            height: Length::Units(Self::DEFAULT_SIZE), +            program, +            phantom: PhantomData, +        } +    } + +    /// Sets the width of the [`Canvas`]. +    /// +    /// [`Canvas`]: struct.Canvas.html +    pub fn width(mut self, width: Length) -> Self { +        self.width = width; +        self +    } + +    /// Sets the height of the [`Canvas`]. +    /// +    /// [`Canvas`]: struct.Canvas.html +    pub fn height(mut self, height: Length) -> Self { +        self.height = height; +        self +    } +} + +impl<Message, P, B> Widget<Message, Renderer<B>> for Canvas<Message, P> +where +    P: Program<Message>, +    B: Backend, +{ +    fn width(&self) -> Length { +        self.width +    } + +    fn height(&self) -> Length { +        self.height +    } + +    fn layout( +        &self, +        _renderer: &Renderer<B>, +        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, +        event: iced_native::Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +        _renderer: &Renderer<B>, +        _clipboard: Option<&dyn Clipboard>, +    ) { +        let bounds = layout.bounds(); + +        let canvas_event = match event { +            iced_native::Event::Mouse(mouse_event) => { +                Some(Event::Mouse(mouse_event)) +            } +            _ => None, +        }; + +        let cursor = Cursor::from_window_position(cursor_position); + +        if let Some(canvas_event) = canvas_event { +            if let Some(message) = +                self.program.update(canvas_event, bounds, cursor) +            { +                messages.push(message); +            } +        } +    } + +    fn draw( +        &self, +        _renderer: &mut Renderer<B>, +        _defaults: &Defaults, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> (Primitive, mouse::Interaction) { +        let bounds = layout.bounds(); +        let translation = Vector::new(bounds.x, bounds.y); +        let cursor = Cursor::from_window_position(cursor_position); + +        ( +            Primitive::Translate { +                translation, +                content: Box::new(Primitive::Group { +                    primitives: self +                        .program +                        .draw(bounds, cursor) +                        .into_iter() +                        .map(Geometry::into_primitive) +                        .collect(), +                }), +            }, +            self.program.mouse_interaction(bounds, cursor), +        ) +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        struct Marker; +        std::any::TypeId::of::<Marker>().hash(state); + +        self.width.hash(state); +        self.height.hash(state); +    } +} + +impl<'a, Message, P, B> From<Canvas<Message, P>> +    for Element<'a, Message, Renderer<B>> +where +    Message: 'static, +    P: Program<Message> + 'a, +    B: Backend, +{ +    fn from(canvas: Canvas<Message, P>) -> Element<'a, Message, Renderer<B>> { +        Element::new(canvas) +    } +} diff --git a/wgpu/src/widget/canvas/cache.rs b/graphics/src/widget/canvas/cache.rs index 4b28d164..4b28d164 100644 --- a/wgpu/src/widget/canvas/cache.rs +++ b/graphics/src/widget/canvas/cache.rs diff --git a/wgpu/src/widget/canvas/cursor.rs b/graphics/src/widget/canvas/cursor.rs index 456760ea..456760ea 100644 --- a/wgpu/src/widget/canvas/cursor.rs +++ b/graphics/src/widget/canvas/cursor.rs diff --git a/wgpu/src/widget/canvas/event.rs b/graphics/src/widget/canvas/event.rs index ad11f51e..ad11f51e 100644 --- a/wgpu/src/widget/canvas/event.rs +++ b/graphics/src/widget/canvas/event.rs diff --git a/wgpu/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index a2010e45..a2010e45 100644 --- a/wgpu/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs diff --git a/wgpu/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 5262ab4e..48d28d95 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -304,11 +304,11 @@ impl Frame {      pub fn into_geometry(mut self) -> Geometry {          if !self.buffers.indices.is_empty() {              self.primitives.push(Primitive::Mesh2D { -                size: self.size,                  buffers: triangle::Mesh2D {                      vertices: self.buffers.vertices,                      indices: self.buffers.indices,                  }, +                size: self.size,              });          } diff --git a/wgpu/src/widget/canvas/geometry.rs b/graphics/src/widget/canvas/geometry.rs index 4cadee39..4cadee39 100644 --- a/wgpu/src/widget/canvas/geometry.rs +++ b/graphics/src/widget/canvas/geometry.rs diff --git a/wgpu/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index c26bf187..c26bf187 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs diff --git a/wgpu/src/widget/canvas/path/arc.rs b/graphics/src/widget/canvas/path/arc.rs index 343191f1..343191f1 100644 --- a/wgpu/src/widget/canvas/path/arc.rs +++ b/graphics/src/widget/canvas/path/arc.rs diff --git a/wgpu/src/widget/canvas/path/builder.rs b/graphics/src/widget/canvas/path/builder.rs index 6511fa52..6511fa52 100644 --- a/wgpu/src/widget/canvas/path/builder.rs +++ b/graphics/src/widget/canvas/path/builder.rs diff --git a/wgpu/src/widget/canvas/program.rs b/graphics/src/widget/canvas/program.rs index 725d9d72..725d9d72 100644 --- a/wgpu/src/widget/canvas/program.rs +++ b/graphics/src/widget/canvas/program.rs diff --git a/wgpu/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 5b6fc56a..5b6fc56a 100644 --- a/wgpu/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs diff --git a/wgpu/src/widget/canvas/text.rs b/graphics/src/widget/canvas/text.rs index c4cae30e..c4cae30e 100644 --- a/wgpu/src/widget/canvas/text.rs +++ b/graphics/src/widget/canvas/text.rs | 
