diff options
| author | 2020-02-12 08:49:42 +0100 | |
|---|---|---|
| committer | 2020-02-12 08:49:42 +0100 | |
| commit | 578ea4abb8a2dd0d53d7087322796bf9ad541b56 (patch) | |
| tree | be684d9262fbf1ae0c5fc2db469d74ab0a5a98c4 | |
| parent | f34407bfdaf06c4bf204dc31b152be9451c243b8 (diff) | |
| download | iced-578ea4abb8a2dd0d53d7087322796bf9ad541b56.tar.gz iced-578ea4abb8a2dd0d53d7087322796bf9ad541b56.tar.bz2 iced-578ea4abb8a2dd0d53d7087322796bf9ad541b56.zip | |
Finish `clock` example
| -rw-r--r-- | examples/clock/Cargo.toml | 4 | ||||
| -rw-r--r-- | examples/clock/src/main.rs | 65 | ||||
| -rw-r--r-- | wgpu/src/primitive.rs | 10 | ||||
| -rw-r--r-- | wgpu/src/renderer.rs | 12 | ||||
| -rw-r--r-- | wgpu/src/shader/triangle.vert | 4 | ||||
| -rw-r--r-- | wgpu/src/shader/triangle.vert.spv | bin | 1468 -> 1256 bytes | |||
| -rw-r--r-- | wgpu/src/triangle.rs | 80 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas.rs | 21 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas/frame.rs | 19 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas/layer.rs | 63 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas/path.rs | 2 | 
11 files changed, 202 insertions, 78 deletions
| diff --git a/examples/clock/Cargo.toml b/examples/clock/Cargo.toml index 941e2bd0..308cbfbb 100644 --- a/examples/clock/Cargo.toml +++ b/examples/clock/Cargo.toml @@ -9,5 +9,7 @@ publish = false  canvas = []  [dependencies] -iced = { path = "../..", features = ["canvas", "async-std"] } +iced = { path = "../..", features = ["canvas", "async-std", "debug"] } +iced_native = { path = "../../native" }  chrono = "0.4" +async-std = { version = "1.0", features = ["unstable"] } diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 76752d20..1b8d1ee6 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -1,6 +1,6 @@  use iced::{ -    canvas, executor, Application, Canvas, Color, Command, Element, Length, -    Point, Settings, +    canvas, executor, Application, Canvas, Color, Command, Container, Element, +    Length, Point, Settings, Subscription, Vector,  };  pub fn main() { @@ -53,11 +53,21 @@ impl Application for Clock {          Command::none()      } +    fn subscription(&self) -> Subscription<Message> { +        time::every(std::time::Duration::from_millis(500)).map(Message::Tick) +    } +      fn view(&mut self) -> Element<Message> { -        Canvas::new() +        let canvas = Canvas::new() +            .width(Length::Units(400)) +            .height(Length::Units(400)) +            .push(self.clock.with(&self.now)); + +        Container::new(canvas)              .width(Length::Fill)              .height(Length::Fill) -            .push(self.clock.with(&self.now)) +            .center_x() +            .center_y()              .into()      }  } @@ -85,6 +95,7 @@ impl canvas::layer::Drawable for LocalTime {      fn draw(&self, frame: &mut canvas::Frame) {          let center = frame.center();          let radius = frame.width().min(frame.height()) as f32 / 2.0; +        let offset = Vector::new(center.x, center.y);          let path = canvas::Path::new(|path| {              path.arc(canvas::path::Arc { @@ -104,6 +115,7 @@ impl canvas::layer::Drawable for LocalTime {              n: u32,              total: u32,              length: f32, +            offset: Vector,              path: &mut canvas::path::Builder,          ) {              let turns = n as f32 / total as f32; @@ -112,15 +124,15 @@ impl canvas::layer::Drawable for LocalTime {              let x = length * t.cos();              let y = length * t.sin(); -            path.line_to(Point::new(x, y)); +            path.line_to(Point::new(x, y) + offset);          }          let path = canvas::Path::new(|path| {              path.move_to(center); -            draw_handle(self.hour, 12, 0.6 * radius, path); +            draw_handle(self.hour, 12, 0.5 * radius, offset, path);              path.move_to(center); -            draw_handle(self.minute, 60, 0.9 * radius, path) +            draw_handle(self.minute, 60, 0.8 * radius, offset, path)          });          frame.stroke( @@ -135,7 +147,7 @@ impl canvas::layer::Drawable for LocalTime {          let path = canvas::Path::new(|path| {              path.move_to(center); -            draw_handle(self.second, 60, 0.9 * radius, path) +            draw_handle(self.second, 60, 0.8 * radius, offset, path)          });          frame.stroke( @@ -149,3 +161,40 @@ impl canvas::layer::Drawable for LocalTime {          );      }  } + +mod time { +    use iced::futures; + +    pub fn every( +        duration: std::time::Duration, +    ) -> iced::Subscription<chrono::DateTime<chrono::Local>> { +        iced::Subscription::from_recipe(Every(duration)) +    } + +    struct Every(std::time::Duration); + +    impl<H, I> iced_native::subscription::Recipe<H, I> for Every +    where +        H: std::hash::Hasher, +    { +        type Output = chrono::DateTime<chrono::Local>; + +        fn hash(&self, state: &mut H) { +            use std::hash::Hash; + +            std::any::TypeId::of::<Self>().hash(state); +            self.0.hash(state); +        } + +        fn stream( +            self: Box<Self>, +            _input: futures::stream::BoxStream<'static, I>, +        ) -> futures::stream::BoxStream<'static, Self::Output> { +            use futures::stream::StreamExt; + +            async_std::stream::interval(self.0) +                .map(|_| chrono::Local::now()) +                .boxed() +        } +    } +} diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 481252ef..823b4b72 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -1,5 +1,5 @@  use iced_native::{ -    image, svg, Background, Color, Font, HorizontalAlignment, Rectangle, +    image, svg, Background, Color, Font, HorizontalAlignment, Point, Rectangle,      Vector, VerticalAlignment,  }; @@ -73,7 +73,13 @@ pub enum Primitive {      /// A low-level primitive to render a mesh of triangles.      ///      /// It can be used to render many kinds of geometry freely. -    Mesh2D(Arc<triangle::Mesh2D>), +    Mesh2D { +        /// The top-left coordinate of the mesh +        origin: Point, + +        /// The vertex and index buffers of the mesh +        buffers: Arc<triangle::Mesh2D>, +    },  }  impl Default for Primitive { diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index e93090b8..25b2e99a 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -26,7 +26,7 @@ struct Layer<'a> {      offset: Vector<u32>,      quads: Vec<Quad>,      images: Vec<Image>, -    meshes: Vec<Arc<triangle::Mesh2D>>, +    meshes: Vec<(Point, Arc<triangle::Mesh2D>)>,      text: Vec<wgpu_glyph::Section<'a>>,  } @@ -229,8 +229,8 @@ impl Renderer {                      scale: [bounds.width, bounds.height],                  });              } -            Primitive::Mesh2D(mesh) => { -                layer.meshes.push(mesh.clone()); +            Primitive::Mesh2D { origin, buffers } => { +                layer.meshes.push((*origin, buffers.clone()));              }              Primitive::Clip {                  bounds, @@ -313,9 +313,10 @@ impl Renderer {          if layer.meshes.len() > 0 {              let translated = transformation +                * Transformation::scale(scale_factor, scale_factor)                  * Transformation::translate( -                    -(layer.offset.x as f32) * scale_factor, -                    -(layer.offset.y as f32) * scale_factor, +                    -(layer.offset.x as f32), +                    -(layer.offset.y as f32),                  );              self.triangle_pipeline.draw( @@ -323,7 +324,6 @@ impl Renderer {                  encoder,                  target,                  translated, -                scale_factor,                  &layer.meshes,                  bounds,              ); diff --git a/wgpu/src/shader/triangle.vert b/wgpu/src/shader/triangle.vert index fd86ecd6..1f2c009b 100644 --- a/wgpu/src/shader/triangle.vert +++ b/wgpu/src/shader/triangle.vert @@ -7,11 +7,9 @@ layout(location = 0) out vec4 o_Color;  layout (set = 0, binding = 0) uniform Globals {      mat4 u_Transform; -    float u_Scale;  };  void main() { -    vec2 p_Position = i_Position * u_Scale; -    gl_Position = u_Transform * vec4(p_Position, 0.0, 1.0); +    gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);      o_Color = i_Color;  } diff --git a/wgpu/src/shader/triangle.vert.spv b/wgpu/src/shader/triangle.vert.spvBinary files differ index bc39c451..871f4f55 100644 --- a/wgpu/src/shader/triangle.vert.spv +++ b/wgpu/src/shader/triangle.vert.spv diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 38157d00..6f3adbe4 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,6 +1,6 @@  //! Draw meshes of triangles.  use crate::Transformation; -use iced_native::Rectangle; +use iced_native::{Point, Rectangle};  use std::{mem, sync::Arc};  #[derive(Debug)] @@ -128,47 +128,28 @@ impl Pipeline {          encoder: &mut wgpu::CommandEncoder,          target: &wgpu::TextureView,          transformation: Transformation, -        scale: f32, -        meshes: &Vec<Arc<Mesh2D>>, +        meshes: &Vec<(Point, Arc<Mesh2D>)>,          bounds: Rectangle<u32>,      ) { -        let uniforms = Uniforms { -            transform: transformation.into(), -            scale, -        }; - -        let constants_buffer = device -            .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) -            .fill_from_slice(&[uniforms]); - -        encoder.copy_buffer_to_buffer( -            &constants_buffer, -            0, -            &self.constants_buffer, -            0, -            std::mem::size_of::<Uniforms>() as u64, -        ); - -        let mut render_pass = -            encoder.begin_render_pass(&wgpu::RenderPassDescriptor { -                color_attachments: &[ -                    wgpu::RenderPassColorAttachmentDescriptor { -                        attachment: target, -                        resolve_target: None, -                        load_op: wgpu::LoadOp::Load, -                        store_op: wgpu::StoreOp::Store, -                        clear_color: wgpu::Color { -                            r: 0.0, -                            g: 0.0, -                            b: 0.0, -                            a: 0.0, -                        }, -                    }, -                ], -                depth_stencil_attachment: None, -            }); +        for (origin, mesh) in meshes { +            let uniforms = Uniforms { +                transform: (transformation +                    * Transformation::translate(origin.x, origin.y)) +                .into(), +            }; + +            let constants_buffer = device +                .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) +                .fill_from_slice(&[uniforms]); + +            encoder.copy_buffer_to_buffer( +                &constants_buffer, +                0, +                &self.constants_buffer, +                0, +                std::mem::size_of::<Uniforms>() as u64, +            ); -        for mesh in meshes {              let vertices_buffer = device                  .create_buffer_mapped(                      mesh.vertices.len(), @@ -183,6 +164,25 @@ impl Pipeline {                  )                  .fill_from_slice(&mesh.indices); +            let mut render_pass = +                encoder.begin_render_pass(&wgpu::RenderPassDescriptor { +                    color_attachments: &[ +                        wgpu::RenderPassColorAttachmentDescriptor { +                            attachment: target, +                            resolve_target: None, +                            load_op: wgpu::LoadOp::Load, +                            store_op: wgpu::StoreOp::Store, +                            clear_color: wgpu::Color { +                                r: 0.0, +                                g: 0.0, +                                b: 0.0, +                                a: 0.0, +                            }, +                        }, +                    ], +                    depth_stencil_attachment: None, +                }); +              render_pass.set_pipeline(&self.pipeline);              render_pass.set_bind_group(0, &self.constants, &[]);              render_pass.set_index_buffer(&indices_buffer, 0); @@ -203,14 +203,12 @@ impl Pipeline {  #[derive(Debug, Clone, Copy)]  struct Uniforms {      transform: [f32; 16], -    scale: f32,  }  impl Default for Uniforms {      fn default() -> Self {          Self {              transform: *Transformation::identity().as_ref(), -            scale: 1.0,          }      }  } diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index 6bfeed9a..c984fee9 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -68,7 +68,6 @@ impl<'a, Message> Widget<Message, Renderer> for Canvas<'a> {          limits: &layout::Limits,      ) -> layout::Node {          let limits = limits.width(self.width).height(self.height); -          let size = limits.resolve(Size::ZERO);          layout::Node::new(size) @@ -78,10 +77,26 @@ impl<'a, Message> Widget<Message, Renderer> for Canvas<'a> {          &self,          _renderer: &mut Renderer,          _defaults: &Defaults, -        _layout: Layout<'_>, +        layout: Layout<'_>,          _cursor_position: Point,      ) -> (Primitive, MouseCursor) { -        (Primitive::None, MouseCursor::Idle) +        let bounds = layout.bounds(); +        let origin = Point::new(bounds.x, bounds.y); +        let size = Size::new(bounds.width, bounds.height); + +        ( +            Primitive::Group { +                primitives: self +                    .layers +                    .iter() +                    .map(|layer| Primitive::Mesh2D { +                        origin, +                        buffers: layer.draw(size), +                    }) +                    .collect(), +            }, +            MouseCursor::Idle, +        )      }      fn hash_layout(&self, state: &mut Hasher) { diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 82ff526b..3c667426 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -7,13 +7,13 @@ use crate::{  #[derive(Debug)]  pub struct Frame { -    width: u32, -    height: u32, +    width: f32, +    height: f32,      buffers: lyon::tessellation::VertexBuffers<triangle::Vertex2D, u16>,  }  impl Frame { -    pub(crate) fn new(width: u32, height: u32) -> Frame { +    pub fn new(width: f32, height: f32) -> Frame {          Frame {              width,              height, @@ -21,16 +21,16 @@ impl Frame {          }      } -    pub fn width(&self) -> u32 { +    pub fn width(&self) -> f32 {          self.width      } -    pub fn height(&self) -> u32 { +    pub fn height(&self) -> f32 {          self.height      }      pub fn center(&self) -> Point { -        Point::new(self.width as f32 / 2.0, self.height as f32 / 2.0) +        Point::new(self.width / 2.0, self.height / 2.0)      }      pub fn fill(&mut self, path: &Path, fill: Fill) { @@ -74,6 +74,13 @@ impl Frame {              .tessellate_path(path.raw(), &options, &mut buffers)              .expect("Stroke path");      } + +    pub fn into_mesh(self) -> triangle::Mesh2D { +        triangle::Mesh2D { +            vertices: self.buffers.vertices, +            indices: self.buffers.indices, +        } +    }  }  struct FillVertex([f32; 4]); diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs index f97634e4..c239a254 100644 --- a/wgpu/src/widget/canvas/layer.rs +++ b/wgpu/src/widget/canvas/layer.rs @@ -1,13 +1,28 @@ -use crate::canvas::Frame; +use crate::{canvas::Frame, triangle}; -pub trait Layer: std::fmt::Debug {} +use iced_native::Size; +use std::cell::RefCell; +use std::sync::Arc; + +pub trait Layer: std::fmt::Debug { +    fn draw(&self, bounds: Size) -> Arc<triangle::Mesh2D>; +}  use std::marker::PhantomData; -use std::sync::{Arc, Weak};  #[derive(Debug)]  pub struct Cached<T: Drawable> {      input: PhantomData<T>, +    cache: RefCell<Cache>, +} + +#[derive(Debug)] +enum Cache { +    Empty, +    Filled { +        mesh: Arc<triangle::Mesh2D>, +        bounds: Size, +    },  }  impl<T> Cached<T> @@ -15,14 +30,19 @@ where      T: Drawable + std::fmt::Debug,  {      pub fn new() -> Self { -        Cached { input: PhantomData } +        Cached { +            input: PhantomData, +            cache: RefCell::new(Cache::Empty), +        }      } -    pub fn clear(&mut self) {} +    pub fn clear(&mut self) { +        *self.cache.borrow_mut() = Cache::Empty; +    }      pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a {          Bind { -            cache: self, +            layer: self,              input: input,          }      } @@ -30,11 +50,38 @@ where  #[derive(Debug)]  struct Bind<'a, T: Drawable> { -    cache: &'a Cached<T>, +    layer: &'a Cached<T>,      input: &'a T,  } -impl<'a, T> Layer for Bind<'a, T> where T: Drawable + std::fmt::Debug {} +impl<'a, T> Layer for Bind<'a, T> +where +    T: Drawable + std::fmt::Debug, +{ +    fn draw(&self, current_bounds: Size) -> Arc<triangle::Mesh2D> { +        use std::ops::Deref; + +        if let Cache::Filled { mesh, bounds } = +            self.layer.cache.borrow().deref() +        { +            if *bounds == current_bounds { +                return mesh.clone(); +            } +        } + +        let mut frame = Frame::new(current_bounds.width, current_bounds.height); +        self.input.draw(&mut frame); + +        let mesh = Arc::new(frame.into_mesh()); + +        *self.layer.cache.borrow_mut() = Cache::Filled { +            mesh: mesh.clone(), +            bounds: current_bounds, +        }; + +        mesh +    } +}  pub trait Drawable {      fn draw(&self, frame: &mut Frame); diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index 96206256..c8ba10e1 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -58,6 +58,8 @@ impl Builder {              sweep_angle: lyon::math::Angle::radians(ellipse.end_angle),          }; +        let _ = self.raw.move_to(arc.sample(0.0)); +          arc.for_each_quadratic_bezier(&mut |curve| {              let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);          }); | 
