From 719c073fc67c87d6b2da1bc01b74751d3f5e59f0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 25 Oct 2019 03:47:34 +0200 Subject: Draft `Scrollable` widget (no clipping yet!) --- wgpu/Cargo.toml | 1 + wgpu/src/primitive.rs | 5 +++ wgpu/src/renderer.rs | 97 +++++++++++++++++++++++++++++------------ wgpu/src/renderer/scrollable.rs | 70 +++++++++++++++++++++++++++++ wgpu/src/transformation.rs | 67 ++++++++++++++++++++-------- 5 files changed, 193 insertions(+), 47 deletions(-) create mode 100644 wgpu/src/renderer/scrollable.rs (limited to 'wgpu') diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index cac5e113..ffb15ea2 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -13,4 +13,5 @@ wgpu = { version = "0.3", git = "https://github.com/gfx-rs/wgpu-rs", rev = "cb25 wgpu_glyph = { version = "0.4", git = "https://github.com/hecrj/wgpu_glyph", rev = "48daa98f5f785963838b4345e86ac40eac095ba9" } raw-window-handle = "0.1" image = "0.22" +nalgebra = "0.18" log = "0.4" diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 0b9e2c41..cdd87894 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -23,4 +23,9 @@ pub enum Primitive { path: String, bounds: Rectangle, }, + Scrollable { + bounds: Rectangle, + offset: u32, + content: Box, + }, } diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index ab6f744f..bb0e7b27 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -20,6 +20,7 @@ mod column; mod image; mod radio; mod row; +mod scrollable; mod slider; mod text; @@ -31,8 +32,6 @@ pub struct Renderer { quad_pipeline: quad::Pipeline, image_pipeline: crate::image::Pipeline, - quads: Vec, - images: Vec, glyph_brush: Rc>>, } @@ -43,6 +42,24 @@ pub struct Target { swap_chain: SwapChain, } +pub struct Layer { + quads: Vec, + images: Vec, + layers: Vec, + y_offset: u32, +} + +impl Layer { + pub fn new(y_offset: u32) -> Self { + Self { + quads: Vec::new(), + images: Vec::new(), + layers: Vec::new(), + y_offset, + } + } +} + impl Renderer { fn new(window: &W) -> Self { let adapter = Adapter::request(&RequestAdapterOptions { @@ -79,8 +96,6 @@ impl Renderer { quad_pipeline, image_pipeline, - quads: Vec::new(), - images: Vec::new(), glyph_brush: Rc::new(RefCell::new(glyph_brush)), } } @@ -132,27 +147,10 @@ impl Renderer { depth_stencil_attachment: None, }); - self.draw_primitive(primitive); + let mut layer = Layer::new(0); - self.quad_pipeline.draw( - &mut self.device, - &mut encoder, - &self.quads, - target.transformation, - &frame.view, - ); - - self.quads.clear(); - - self.image_pipeline.draw( - &mut self.device, - &mut encoder, - &self.images, - target.transformation, - &frame.view, - ); - - self.images.clear(); + self.draw_primitive(primitive, &mut layer); + self.flush(target.transformation, &layer, &mut encoder, &frame.view); self.glyph_brush .borrow_mut() @@ -170,13 +168,13 @@ impl Renderer { *mouse_cursor } - fn draw_primitive(&mut self, primitive: &Primitive) { + fn draw_primitive(&mut self, primitive: &Primitive, layer: &mut Layer) { match primitive { Primitive::None => {} Primitive::Group { primitives } => { // TODO: Inspect a bit and regroup (?) for primitive in primitives { - self.draw_primitive(primitive) + self.draw_primitive(primitive, layer) } } Primitive::Text { @@ -244,7 +242,7 @@ impl Renderer { background, border_radius, } => { - self.quads.push(Quad { + layer.quads.push(Quad { position: [bounds.x, bounds.y], scale: [bounds.width, bounds.height], color: match background { @@ -254,12 +252,55 @@ impl Renderer { }); } Primitive::Image { path, bounds } => { - self.images.push(Image { + layer.images.push(Image { path: path.clone(), position: [bounds.x, bounds.y], scale: [bounds.width, bounds.height], }); } + Primitive::Scrollable { + bounds, + offset, + content, + } => { + let mut new_layer = Layer::new(layer.y_offset + offset); + + // TODO: Primitive culling + self.draw_primitive(content, &mut new_layer); + + layer.layers.push(new_layer); + } + } + } + + fn flush( + &mut self, + transformation: Transformation, + layer: &Layer, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + ) { + let translated = transformation + * Transformation::translate(0.0, -(layer.y_offset as f32)); + + self.quad_pipeline.draw( + &mut self.device, + encoder, + &layer.quads, + transformation, + target, + ); + + self.image_pipeline.draw( + &mut self.device, + encoder, + &layer.images, + translated, + target, + ); + + for layer in layer.layers.iter() { + self.flush(transformation, layer, encoder, target); } } } diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs new file mode 100644 index 00000000..14ff9ff4 --- /dev/null +++ b/wgpu/src/renderer/scrollable.rs @@ -0,0 +1,70 @@ +use crate::{Primitive, Renderer}; +use iced_native::{ + scrollable, Background, Color, Layout, Point, Rectangle, Scrollable, Widget, +}; + +impl scrollable::Renderer for Renderer { + fn draw( + &mut self, + scrollable: &Scrollable<'_, Message, Self>, + layout: Layout<'_>, + cursor_position: Point, + ) -> Self::Output { + let bounds = layout.bounds(); + let is_mouse_over = bounds.contains(cursor_position); + + let content = layout.children().next().unwrap(); + let content_bounds = content.bounds(); + + let cursor_position = if bounds.contains(cursor_position) { + Point::new( + cursor_position.x, + cursor_position.y + scrollable.state.offset as f32, + ) + } else { + Point::new(cursor_position.x, -1.0) + }; + + let (content, mouse_cursor) = + scrollable.content.draw(self, content, cursor_position); + + let primitive = Primitive::Scrollable { + bounds, + offset: scrollable.state.offset, + content: Box::new(content), + }; + + ( + Primitive::Group { + primitives: if is_mouse_over + && content_bounds.height > bounds.height + { + let ratio = bounds.height / content_bounds.height; + let scrollbar_height = bounds.height * ratio; + let y_offset = scrollable.state.offset as f32 * ratio; + + let scrollbar = Primitive::Quad { + bounds: Rectangle { + x: bounds.x + bounds.width - 12.0, + y: bounds.y + y_offset, + width: 10.0, + height: scrollbar_height, + }, + background: Background::Color(Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.5, + }), + border_radius: 5, + }; + + vec![primitive, scrollbar] + } else { + vec![primitive] + }, + }, + mouse_cursor, + ) + } +} diff --git a/wgpu/src/transformation.rs b/wgpu/src/transformation.rs index 1101e135..ed80b31a 100644 --- a/wgpu/src/transformation.rs +++ b/wgpu/src/transformation.rs @@ -1,30 +1,59 @@ -#[derive(Debug, Clone, Copy)] -pub struct Transformation([f32; 16]); +use nalgebra::Matrix3; +use std::ops::Mul; + +/// A 2D transformation matrix. +/// +/// It can be used to apply a transformation to a [`Target`]. +/// +/// [`Target`]: struct.Target.html +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Transformation(Matrix3); impl Transformation { - #[rustfmt::skip] - pub fn identity() -> Self { - Transformation([ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - ]) + /// Get the identity transformation. + pub fn identity() -> Transformation { + Transformation(Matrix3::identity()) } + /// Creates an orthographic projection. + /// + /// You should rarely need this. On creation, a [`Target`] is automatically + /// set up with the correct orthographic projection. + /// + /// [`Target`]: struct.Target.html #[rustfmt::skip] - pub fn orthographic(width: u16, height: u16) -> Self { - Transformation([ - 2.0 / width as f32, 0.0, 0.0, 0.0, - 0.0, 2.0 / height as f32, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - -1.0, -1.0, 0.0, 1.0, - ]) + pub fn orthographic(width: u16, height: u16) -> Transformation { + Transformation(nalgebra::Matrix3::new( + 2.0 / f32::from(width), 0.0, -1.0, + 0.0, 2.0 / f32::from(height), -1.0, + 0.0, 0.0, 1.0 + )) + } + + /// Creates a translate transformation. + /// + /// You can use this to pan your camera, for example. + pub fn translate(x: f32, y: f32) -> Transformation { + Transformation(Matrix3::new_translation(&nalgebra::Vector2::new(x, y))) + } +} + +impl Mul for Transformation { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Transformation(self.0 * rhs.0) } } impl From for [f32; 16] { - fn from(transformation: Transformation) -> [f32; 16] { - transformation.0 + #[rustfmt::skip] + fn from(t: Transformation) -> [f32; 16] { + [ + t.0[0], t.0[1], 0.0, t.0[2], + t.0[3], t.0[4], 0.0, t.0[5], + 0.0, 0.0, -1.0, 0.0, + t.0[6], t.0[7], 0.0, t.0[8] + ] } } -- cgit From 09bd2c46c06eba72da40852d82a52e7353cc9e9b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 27 Oct 2019 01:24:08 +0200 Subject: Expose scrollable offset properly --- wgpu/src/renderer/scrollable.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs index 14ff9ff4..5b93a765 100644 --- a/wgpu/src/renderer/scrollable.rs +++ b/wgpu/src/renderer/scrollable.rs @@ -16,11 +16,10 @@ impl scrollable::Renderer for Renderer { let content = layout.children().next().unwrap(); let content_bounds = content.bounds(); + let offset = scrollable.state.offset(bounds, content_bounds); + let cursor_position = if bounds.contains(cursor_position) { - Point::new( - cursor_position.x, - cursor_position.y + scrollable.state.offset as f32, - ) + Point::new(cursor_position.x, cursor_position.y + offset as f32) } else { Point::new(cursor_position.x, -1.0) }; @@ -30,7 +29,7 @@ impl scrollable::Renderer for Renderer { let primitive = Primitive::Scrollable { bounds, - offset: scrollable.state.offset, + offset, content: Box::new(content), }; @@ -41,7 +40,7 @@ impl scrollable::Renderer for Renderer { { let ratio = bounds.height / content_bounds.height; let scrollbar_height = bounds.height * ratio; - let y_offset = scrollable.state.offset as f32 * ratio; + let y_offset = offset as f32 * ratio; let scrollbar = Primitive::Quad { bounds: Rectangle { -- cgit From 0a0aa3edd9c5185551040c75a934f12d3bce7618 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 27 Oct 2019 02:29:23 +0100 Subject: Implement clipping for images --- wgpu/src/image.rs | 8 ++++++++ wgpu/src/renderer.rs | 31 +++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index c883eaa8..c42e1cd4 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -1,4 +1,5 @@ use crate::Transformation; +use iced_native::Rectangle; use std::cell::RefCell; use std::collections::HashMap; @@ -218,6 +219,7 @@ impl Pipeline { encoder: &mut wgpu::CommandEncoder, instances: &[Image], transformation: Transformation, + bounds: Rectangle, target: &wgpu::TextureView, ) { let matrix: [f32; 16] = transformation.into(); @@ -291,6 +293,12 @@ impl Pipeline { 0, &[(&self.vertices, 0), (&self.instances, 0)], ); + render_pass.set_scissor_rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + ); render_pass.draw_indexed( 0..QUAD_INDICES.len() as u32, diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index bb0e7b27..d838c6ee 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -1,7 +1,7 @@ use crate::{quad, Image, Primitive, Quad, Transformation}; use iced_native::{ renderer::Debugger, renderer::Windowed, Background, Color, Layout, - MouseCursor, Point, Widget, + MouseCursor, Point, Rectangle, Widget, }; use raw_window_handle::HasRawWindowHandle; @@ -43,19 +43,21 @@ pub struct Target { } pub struct Layer { + bounds: Rectangle, + y_offset: u32, quads: Vec, images: Vec, layers: Vec, - y_offset: u32, } impl Layer { - pub fn new(y_offset: u32) -> Self { + pub fn new(bounds: Rectangle, y_offset: u32) -> Self { Self { + bounds, + y_offset, quads: Vec::new(), images: Vec::new(), layers: Vec::new(), - y_offset, } } } @@ -147,7 +149,15 @@ impl Renderer { depth_stencil_attachment: None, }); - let mut layer = Layer::new(0); + let mut layer = Layer::new( + Rectangle { + x: 0, + y: 0, + width: u32::from(target.width), + height: u32::from(target.height), + }, + 0, + ); self.draw_primitive(primitive, &mut layer); self.flush(target.transformation, &layer, &mut encoder, &frame.view); @@ -263,7 +273,15 @@ impl Renderer { offset, content, } => { - let mut new_layer = Layer::new(layer.y_offset + offset); + let mut new_layer = Layer::new( + Rectangle { + x: bounds.x as u32, + y: bounds.y as u32 - layer.y_offset, + width: bounds.width as u32, + height: bounds.height as u32, + }, + layer.y_offset + offset, + ); // TODO: Primitive culling self.draw_primitive(content, &mut new_layer); @@ -296,6 +314,7 @@ impl Renderer { encoder, &layer.images, translated, + layer.bounds, target, ); -- cgit From e2668b882a8115fd0afcd32373edb180492908b1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 27 Oct 2019 02:30:19 +0100 Subject: Remove `adapter` from `iced_wgpu::Renderer` --- wgpu/src/renderer.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index d838c6ee..5bd7be8d 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -26,7 +26,6 @@ mod text; pub struct Renderer { surface: Surface, - adapter: Adapter, device: Device, queue: Queue, quad_pipeline: quad::Pipeline, @@ -92,7 +91,6 @@ impl Renderer { Self { surface, - adapter, device, queue, quad_pipeline, -- cgit From 21eb2f692c687a675c54ae5e951556e28e7435eb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 27 Oct 2019 03:10:49 +0100 Subject: Implement clipping for quads --- wgpu/src/quad.rs | 8 ++++++++ wgpu/src/renderer.rs | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'wgpu') diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index adb294f0..6365e117 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -1,4 +1,5 @@ use crate::Transformation; +use iced_native::Rectangle; use std::mem; @@ -165,6 +166,7 @@ impl Pipeline { encoder: &mut wgpu::CommandEncoder, instances: &[Quad], transformation: Transformation, + bounds: Rectangle, target: &wgpu::TextureView, ) { let matrix: [f32; 16] = transformation.into(); @@ -227,6 +229,12 @@ impl Pipeline { 0, &[(&self.vertices, 0), (&self.instances, 0)], ); + render_pass.set_scissor_rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + ); render_pass.draw_indexed( 0..QUAD_INDICES.len() as u32, diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 5bd7be8d..ba140a66 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -251,7 +251,7 @@ impl Renderer { border_radius, } => { layer.quads.push(Quad { - position: [bounds.x, bounds.y], + position: [bounds.x, bounds.y - layer.y_offset as f32], scale: [bounds.width, bounds.height], color: match background { Background::Color(color) => color.into_linear(), @@ -304,6 +304,7 @@ impl Renderer { encoder, &layer.quads, transformation, + layer.bounds, target, ); -- cgit From 35e94f5a324f5c28de855b725039733efb21b26a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 27 Oct 2019 03:11:54 +0100 Subject: Draft text scrolling (no clipping yet!) --- wgpu/src/renderer.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index ba140a66..cfdd7a45 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -41,21 +41,23 @@ pub struct Target { swap_chain: SwapChain, } -pub struct Layer { +pub struct Layer<'a> { bounds: Rectangle, y_offset: u32, quads: Vec, images: Vec, - layers: Vec, + text: Vec>, + layers: Vec>, } -impl Layer { +impl<'a> Layer<'a> { pub fn new(bounds: Rectangle, y_offset: u32) -> Self { Self { bounds, y_offset, quads: Vec::new(), images: Vec::new(), + text: Vec::new(), layers: Vec::new(), } } @@ -176,7 +178,11 @@ impl Renderer { *mouse_cursor } - fn draw_primitive(&mut self, primitive: &Primitive, layer: &mut Layer) { + fn draw_primitive<'a>( + &mut self, + primitive: &'a Primitive, + layer: &mut Layer<'a>, + ) { match primitive { Primitive::None => {} Primitive::Group { primitives } => { @@ -213,7 +219,7 @@ impl Renderer { } }; - self.glyph_brush.borrow_mut().queue(Section { + layer.text.push(Section { text: &content, screen_position: (x, y), bounds: (bounds.width, bounds.height), @@ -317,6 +323,23 @@ impl Renderer { target, ); + { + let mut glyph_brush = self.glyph_brush.borrow_mut(); + + for text in layer.text.iter() { + glyph_brush.queue(text); + } + + glyph_brush + .draw_queued_with_transform( + &mut self.device, + encoder, + target, + translated.into(), + ) + .expect("Draw text"); + } + for layer in layer.layers.iter() { self.flush(transformation, layer, encoder, target); } -- cgit From 2b23e0986c532dbacd89ccd73bb603db558cbdaf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 28 Oct 2019 04:28:21 +0100 Subject: Implement text clipping (caching still broken) --- wgpu/Cargo.toml | 6 +++--- wgpu/src/renderer.rs | 21 ++++++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) (limited to 'wgpu') diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index ffb15ea2..30f9224f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -9,9 +9,9 @@ repository = "https://github.com/hecrj/iced" [dependencies] iced_native = { version = "0.1.0-alpha", path = "../native" } -wgpu = { version = "0.3", git = "https://github.com/gfx-rs/wgpu-rs", rev = "cb25914b95b58fee0dc139b400867e7a731d98f4" } -wgpu_glyph = { version = "0.4", git = "https://github.com/hecrj/wgpu_glyph", rev = "48daa98f5f785963838b4345e86ac40eac095ba9" } -raw-window-handle = "0.1" +wgpu = { version = "0.3", git = "https://github.com/gfx-rs/wgpu-rs", rev = "ed2c67f762970d0099c1e6c6e078fb645afbf964" } +wgpu_glyph = { version = "0.4", git = "https://github.com/hecrj/wgpu_glyph", rev = "954ac865ca1b7f6b97bf403f8c6174a7120e667c" } +raw-window-handle = "0.3" image = "0.22" nalgebra = "0.18" log = "0.4" diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index cfdd7a45..0f91428e 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -75,7 +75,7 @@ impl Renderer { extensions: Extensions { anisotropic_filtering: false, }, - limits: Limits { max_bind_groups: 1 }, + limits: Limits { max_bind_groups: 2 }, }); let surface = Surface::create(window); @@ -162,17 +162,6 @@ impl Renderer { self.draw_primitive(primitive, &mut layer); self.flush(target.transformation, &layer, &mut encoder, &frame.view); - self.glyph_brush - .borrow_mut() - .draw_queued( - &mut self.device, - &mut encoder, - &frame.view, - u32::from(target.width), - u32::from(target.height), - ) - .expect("Draw text"); - self.queue.submit(&[encoder.finish()]); *mouse_cursor @@ -331,11 +320,17 @@ impl Renderer { } glyph_brush - .draw_queued_with_transform( + .draw_queued_with_transform_and_scissoring( &mut self.device, encoder, target, translated.into(), + wgpu_glyph::Region { + x: layer.bounds.x, + y: layer.bounds.y, + width: layer.bounds.width, + height: layer.bounds.height, + }, ) .expect("Draw text"); } -- cgit From 23ebfb707a52d03a7beceaa5e197b4491619ae1d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 29 Oct 2019 01:21:28 +0100 Subject: Issue draw calls only when necessary --- wgpu/src/renderer.rs | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 0f91428e..bb7cb858 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -294,25 +294,29 @@ impl Renderer { let translated = transformation * Transformation::translate(0.0, -(layer.y_offset as f32)); - self.quad_pipeline.draw( - &mut self.device, - encoder, - &layer.quads, - transformation, - layer.bounds, - target, - ); + if layer.quads.len() > 0 { + self.quad_pipeline.draw( + &mut self.device, + encoder, + &layer.quads, + transformation, + layer.bounds, + target, + ); + } - self.image_pipeline.draw( - &mut self.device, - encoder, - &layer.images, - translated, - layer.bounds, - target, - ); + if layer.images.len() > 0 { + self.image_pipeline.draw( + &mut self.device, + encoder, + &layer.images, + translated, + layer.bounds, + target, + ); + } - { + if layer.text.len() > 0 { let mut glyph_brush = self.glyph_brush.borrow_mut(); for text in layer.text.iter() { -- cgit From be488ac73837c9a741d900617840ee5c4ed74d61 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 29 Oct 2019 02:00:17 +0100 Subject: Draw scrollbar on top of scrollable content --- wgpu/src/renderer.rs | 29 +++++++++++++---------- wgpu/src/renderer/scrollable.rs | 51 +++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 39 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index bb7cb858..f0be0860 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -47,7 +47,6 @@ pub struct Layer<'a> { quads: Vec, images: Vec, text: Vec>, - layers: Vec>, } impl<'a> Layer<'a> { @@ -58,7 +57,6 @@ impl<'a> Layer<'a> { quads: Vec::new(), images: Vec::new(), text: Vec::new(), - layers: Vec::new(), } } } @@ -149,7 +147,8 @@ impl Renderer { depth_stencil_attachment: None, }); - let mut layer = Layer::new( + let mut layers = Vec::new(); + let mut current = Layer::new( Rectangle { x: 0, y: 0, @@ -159,8 +158,17 @@ impl Renderer { 0, ); - self.draw_primitive(primitive, &mut layer); - self.flush(target.transformation, &layer, &mut encoder, &frame.view); + self.draw_primitive(primitive, &mut current, &mut layers); + layers.push(current); + + for layer in layers { + self.flush( + target.transformation, + &layer, + &mut encoder, + &frame.view, + ); + } self.queue.submit(&[encoder.finish()]); @@ -171,13 +179,14 @@ impl Renderer { &mut self, primitive: &'a Primitive, layer: &mut Layer<'a>, + layers: &mut Vec>, ) { match primitive { Primitive::None => {} Primitive::Group { primitives } => { // TODO: Inspect a bit and regroup (?) for primitive in primitives { - self.draw_primitive(primitive, layer) + self.draw_primitive(primitive, layer, layers) } } Primitive::Text { @@ -277,9 +286,9 @@ impl Renderer { ); // TODO: Primitive culling - self.draw_primitive(content, &mut new_layer); + self.draw_primitive(content, &mut new_layer, layers); - layer.layers.push(new_layer); + layers.push(new_layer); } } } @@ -338,10 +347,6 @@ impl Renderer { ) .expect("Draw text"); } - - for layer in layer.layers.iter() { - self.flush(transformation, layer, encoder, target); - } } } diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs index 5b93a765..43dddeed 100644 --- a/wgpu/src/renderer/scrollable.rs +++ b/wgpu/src/renderer/scrollable.rs @@ -34,34 +34,31 @@ impl scrollable::Renderer for Renderer { }; ( - Primitive::Group { - primitives: if is_mouse_over - && content_bounds.height > bounds.height - { - let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; + if is_mouse_over && content_bounds.height > bounds.height { + let ratio = bounds.height / content_bounds.height; + let scrollbar_height = bounds.height * ratio; + let y_offset = offset as f32 * ratio; - let scrollbar = Primitive::Quad { - bounds: Rectangle { - x: bounds.x + bounds.width - 12.0, - y: bounds.y + y_offset, - width: 10.0, - height: scrollbar_height, - }, - background: Background::Color(Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 0.5, - }), - border_radius: 5, - }; - - vec![primitive, scrollbar] - } else { - vec![primitive] - }, + let scrollbar = Primitive::Quad { + bounds: Rectangle { + x: bounds.x + bounds.width - 12.0, + y: bounds.y + y_offset, + width: 10.0, + height: scrollbar_height, + }, + background: Background::Color(Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.7, + }), + border_radius: 5, + }; + Primitive::Group { + primitives: vec![primitive, scrollbar], + } + } else { + primitive }, mouse_cursor, ) -- cgit From a3c55f75174f9bc87f80d7fe6236a71138d2fd77 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 29 Oct 2019 02:13:22 +0100 Subject: Stop leaking impl details in scrollable `Renderer` --- wgpu/src/renderer/scrollable.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs index 43dddeed..1327e577 100644 --- a/wgpu/src/renderer/scrollable.rs +++ b/wgpu/src/renderer/scrollable.rs @@ -7,13 +7,11 @@ impl scrollable::Renderer for Renderer { fn draw( &mut self, scrollable: &Scrollable<'_, Message, Self>, - layout: Layout<'_>, + bounds: Rectangle, + content: Layout<'_>, cursor_position: Point, ) -> Self::Output { - let bounds = layout.bounds(); let is_mouse_over = bounds.contains(cursor_position); - - let content = layout.children().next().unwrap(); let content_bounds = content.bounds(); let offset = scrollable.state.offset(bounds, content_bounds); -- cgit From 29588f604af66fb4911f791c0c402fccd30ba64b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 29 Oct 2019 05:09:54 +0100 Subject: Implement scrollbar interactions! :tada: --- wgpu/src/renderer/scrollable.rs | 87 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 9 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs index 1327e577..7f8a7db6 100644 --- a/wgpu/src/renderer/scrollable.rs +++ b/wgpu/src/renderer/scrollable.rs @@ -1,9 +1,33 @@ use crate::{Primitive, Renderer}; use iced_native::{ - scrollable, Background, Color, Layout, Point, Rectangle, Scrollable, Widget, + scrollable, Background, Color, Layout, MouseCursor, Point, Rectangle, + Scrollable, Widget, }; +const SCROLLBAR_WIDTH: u16 = 10; +const SCROLLBAR_MARGIN: u16 = 10; + +fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { + Rectangle { + x: bounds.x + bounds.width + - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + y: bounds.y, + width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + height: bounds.height, + } +} + impl scrollable::Renderer for Renderer { + fn is_mouse_over_scrollbar( + &self, + bounds: Rectangle, + content_bounds: Rectangle, + cursor_position: Point, + ) -> bool { + content_bounds.height > bounds.height + && scrollbar_bounds(bounds).contains(cursor_position) + } + fn draw( &mut self, scrollable: &Scrollable<'_, Message, Self>, @@ -15,8 +39,15 @@ impl scrollable::Renderer for Renderer { let content_bounds = content.bounds(); let offset = scrollable.state.offset(bounds, content_bounds); + let is_content_overflowing = content_bounds.height > bounds.height; + let scrollbar_bounds = scrollbar_bounds(bounds); + let is_mouse_over_scrollbar = self.is_mouse_over_scrollbar( + bounds, + content_bounds, + cursor_position, + ); - let cursor_position = if bounds.contains(cursor_position) { + let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { Point::new(cursor_position.x, cursor_position.y + offset as f32) } else { Point::new(cursor_position.x, -1.0) @@ -32,16 +63,19 @@ impl scrollable::Renderer for Renderer { }; ( - if is_mouse_over && content_bounds.height > bounds.height { + if is_content_overflowing + && (is_mouse_over || scrollable.state.is_scrollbar_grabbed()) + { let ratio = bounds.height / content_bounds.height; let scrollbar_height = bounds.height * ratio; let y_offset = offset as f32 * ratio; let scrollbar = Primitive::Quad { bounds: Rectangle { - x: bounds.x + bounds.width - 12.0, - y: bounds.y + y_offset, - width: 10.0, + x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + y: scrollbar_bounds.y + y_offset, + width: scrollbar_bounds.width + - f32::from(2 * SCROLLBAR_MARGIN), height: scrollbar_height, }, background: Background::Color(Color { @@ -52,13 +86,48 @@ impl scrollable::Renderer for Renderer { }), border_radius: 5, }; - Primitive::Group { - primitives: vec![primitive, scrollbar], + + if is_mouse_over_scrollbar + || scrollable.state.is_scrollbar_grabbed() + { + let scrollbar_background = Primitive::Quad { + bounds: Rectangle { + x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + width: scrollbar_bounds.width + - f32::from(2 * SCROLLBAR_MARGIN), + ..scrollbar_bounds + }, + background: Background::Color(Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.3, + }), + border_radius: 5, + }; + + Primitive::Group { + primitives: vec![ + primitive, + scrollbar_background, + scrollbar, + ], + } + } else { + Primitive::Group { + primitives: vec![primitive, scrollbar], + } } } else { primitive }, - mouse_cursor, + if is_mouse_over_scrollbar + || scrollable.state.is_scrollbar_grabbed() + { + MouseCursor::Idle + } else { + mouse_cursor + }, ) } } -- cgit From ace4217b22131865623faac99cfdb5692a84d1ae Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 29 Oct 2019 19:45:47 +0100 Subject: Fix `Transformation` docs --- wgpu/src/transformation.rs | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/transformation.rs b/wgpu/src/transformation.rs index ed80b31a..53583e7e 100644 --- a/wgpu/src/transformation.rs +++ b/wgpu/src/transformation.rs @@ -2,10 +2,6 @@ use nalgebra::Matrix3; use std::ops::Mul; /// A 2D transformation matrix. -/// -/// It can be used to apply a transformation to a [`Target`]. -/// -/// [`Target`]: struct.Target.html #[derive(Debug, Clone, Copy, PartialEq)] pub struct Transformation(Matrix3); @@ -16,11 +12,6 @@ impl Transformation { } /// Creates an orthographic projection. - /// - /// You should rarely need this. On creation, a [`Target`] is automatically - /// set up with the correct orthographic projection. - /// - /// [`Target`]: struct.Target.html #[rustfmt::skip] pub fn orthographic(width: u16, height: u16) -> Transformation { Transformation(nalgebra::Matrix3::new( @@ -31,8 +22,6 @@ impl Transformation { } /// Creates a translate transformation. - /// - /// You can use this to pan your camera, for example. pub fn translate(x: f32, y: f32) -> Transformation { Transformation(Matrix3::new_translation(&nalgebra::Vector2::new(x, y))) } -- cgit From 85916c9e8710ee90cbf37d384acbb6d208ff1da3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 29 Oct 2019 19:50:34 +0100 Subject: Rename `Primitive::Scrollable` to `Clip` --- wgpu/src/primitive.rs | 2 +- wgpu/src/renderer.rs | 2 +- wgpu/src/renderer/scrollable.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index cdd87894..354b0851 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -23,7 +23,7 @@ pub enum Primitive { path: String, bounds: Rectangle, }, - Scrollable { + Clip { bounds: Rectangle, offset: u32, content: Box, diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index f0be0860..a70693af 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -270,7 +270,7 @@ impl Renderer { scale: [bounds.width, bounds.height], }); } - Primitive::Scrollable { + Primitive::Clip { bounds, offset, content, diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs index 7f8a7db6..7bce3a68 100644 --- a/wgpu/src/renderer/scrollable.rs +++ b/wgpu/src/renderer/scrollable.rs @@ -56,7 +56,7 @@ impl scrollable::Renderer for Renderer { let (content, mouse_cursor) = scrollable.content.draw(self, content, cursor_position); - let primitive = Primitive::Scrollable { + let primitive = Primitive::Clip { bounds, offset, content: Box::new(content), -- cgit From 298c42ac5f208745cd3b23b3cc8f10f7c8769797 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 31 Oct 2019 01:41:04 +0100 Subject: Replace `nalgebra` with `glam` `glam` compiles much faster and leverages SIMD nicely. --- wgpu/Cargo.toml | 2 +- wgpu/src/image.rs | 4 +--- wgpu/src/quad.rs | 8 ++------ wgpu/src/transformation.rs | 31 ++++++++++++++++--------------- 4 files changed, 20 insertions(+), 25 deletions(-) (limited to 'wgpu') diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 30f9224f..04fae248 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -13,5 +13,5 @@ wgpu = { version = "0.3", git = "https://github.com/gfx-rs/wgpu-rs", rev = "ed2c wgpu_glyph = { version = "0.4", git = "https://github.com/hecrj/wgpu_glyph", rev = "954ac865ca1b7f6b97bf403f8c6174a7120e667c" } raw-window-handle = "0.3" image = "0.22" -nalgebra = "0.18" +glam = "0.8" log = "0.4" diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index c42e1cd4..75cfa166 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -222,11 +222,9 @@ impl Pipeline { bounds: Rectangle, target: &wgpu::TextureView, ) { - let matrix: [f32; 16] = transformation.into(); - let transform_buffer = device .create_buffer_mapped(16, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&matrix[..]); + .fill_from_slice(transformation.as_ref()); encoder.copy_buffer_to_buffer( &transform_buffer, diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 6365e117..bfbd7e2d 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -23,14 +23,12 @@ impl Pipeline { }], }); - let matrix: [f32; 16] = Transformation::identity().into(); - let transform = device .create_buffer_mapped( 16, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, ) - .fill_from_slice(&matrix[..]); + .fill_from_slice(Transformation::identity().as_ref()); let constants = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &constant_layout, @@ -169,11 +167,9 @@ impl Pipeline { bounds: Rectangle, target: &wgpu::TextureView, ) { - let matrix: [f32; 16] = transformation.into(); - let transform_buffer = device .create_buffer_mapped(16, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&matrix[..]); + .fill_from_slice(transformation.as_ref()); encoder.copy_buffer_to_buffer( &transform_buffer, diff --git a/wgpu/src/transformation.rs b/wgpu/src/transformation.rs index 53583e7e..b0d14cc8 100644 --- a/wgpu/src/transformation.rs +++ b/wgpu/src/transformation.rs @@ -1,29 +1,30 @@ -use nalgebra::Matrix3; +use glam::{Mat4, Vec3, Vec4}; use std::ops::Mul; /// A 2D transformation matrix. #[derive(Debug, Clone, Copy, PartialEq)] -pub struct Transformation(Matrix3); +pub struct Transformation(Mat4); impl Transformation { /// Get the identity transformation. pub fn identity() -> Transformation { - Transformation(Matrix3::identity()) + Transformation(Mat4::identity()) } /// Creates an orthographic projection. #[rustfmt::skip] pub fn orthographic(width: u16, height: u16) -> Transformation { - Transformation(nalgebra::Matrix3::new( - 2.0 / f32::from(width), 0.0, -1.0, - 0.0, 2.0 / f32::from(height), -1.0, - 0.0, 0.0, 1.0 + Transformation(Mat4::from_cols( + Vec4::new(2.0 / f32::from(width), 0.0, 0.0, 0.0), + Vec4::new(0.0, 2.0 / f32::from(height), 0.0, 0.0), + Vec4::new(0.0, 0.0, -1.0, 0.0), + Vec4::new(-1.0, -1.0, 0.0, 1.0) )) } /// Creates a translate transformation. pub fn translate(x: f32, y: f32) -> Transformation { - Transformation(Matrix3::new_translation(&nalgebra::Vector2::new(x, y))) + Transformation(Mat4::from_translation(Vec3::new(x, y, 0.0))) } } @@ -35,14 +36,14 @@ impl Mul for Transformation { } } +impl AsRef<[f32; 16]> for Transformation { + fn as_ref(&self) -> &[f32; 16] { + self.0.as_ref() + } +} + impl From for [f32; 16] { - #[rustfmt::skip] fn from(t: Transformation) -> [f32; 16] { - [ - t.0[0], t.0[1], 0.0, t.0[2], - t.0[3], t.0[4], 0.0, t.0[5], - 0.0, 0.0, -1.0, 0.0, - t.0[6], t.0[7], 0.0, t.0[8] - ] + t.as_ref().clone() } } -- cgit From 58d04cadef020412375f2adc5fd96bfa10ec4692 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 2 Nov 2019 01:42:51 +0100 Subject: Add scrollable section to `tour` --- wgpu/src/renderer/scrollable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'wgpu') diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs index 7bce3a68..e812a7e1 100644 --- a/wgpu/src/renderer/scrollable.rs +++ b/wgpu/src/renderer/scrollable.rs @@ -5,7 +5,7 @@ use iced_native::{ }; const SCROLLBAR_WIDTH: u16 = 10; -const SCROLLBAR_MARGIN: u16 = 10; +const SCROLLBAR_MARGIN: u16 = 2; fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { Rectangle { -- cgit