From c0596179bd8582e4f4b5289cdeee8de4fa3de464 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Thu, 3 Nov 2022 00:35:01 +0100 Subject: [PATCH 01/43] non uniform border radius for quads --- examples/custom_widget/src/main.rs | 2 +- glow/src/quad/compatibility.rs | 10 +++---- glow/src/quad/core.rs | 4 +-- glow/src/shader/core/quad.frag | 24 ++++++++++++--- glow/src/shader/core/quad.vert | 12 ++++---- graphics/src/layer/quad.rs | 2 +- graphics/src/primitive.rs | 2 +- graphics/src/renderer.rs | 2 +- native/src/element.rs | 2 +- native/src/overlay/menu.rs | 4 +-- native/src/renderer.rs | 26 ++++++++++++++++- native/src/widget/button.rs | 4 +-- native/src/widget/checkbox.rs | 2 +- native/src/widget/container.rs | 2 +- native/src/widget/pane_grid.rs | 2 +- native/src/widget/pick_list.rs | 2 +- native/src/widget/progress_bar.rs | 4 +-- native/src/widget/radio.rs | 4 +-- native/src/widget/rule.rs | 2 +- native/src/widget/scrollable.rs | 4 +-- native/src/widget/slider.rs | 6 ++-- native/src/widget/text_input.rs | 6 ++-- native/src/widget/toggler.rs | 4 +-- wgpu/src/quad.rs | 2 +- wgpu/src/shader/quad.wgsl | 47 +++++++++++++++++++++--------- 25 files changed, 121 insertions(+), 60 deletions(-) diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index c37a1a1212..f6bb3b1e6f 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -61,7 +61,7 @@ mod circle { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), - border_radius: self.radius, + border_radius: self.radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs index 28a5ea7ce4..5734c15926 100644 --- a/glow/src/quad/compatibility.rs +++ b/glow/src/quad/compatibility.rs @@ -254,7 +254,7 @@ unsafe fn create_buffers( gl.enable_vertex_attrib_array(4); gl.vertex_attrib_pointer_f32( 4, - 1, + 4, glow::FLOAT, false, stride, @@ -268,7 +268,7 @@ unsafe fn create_buffers( glow::FLOAT, false, stride, - 4 * (2 + 2 + 4 + 4 + 1), + 4 * (2 + 2 + 4 + 4 + 4), ); gl.enable_vertex_attrib_array(6); @@ -278,7 +278,7 @@ unsafe fn create_buffers( glow::FLOAT, false, stride, - 4 * (2 + 2 + 4 + 4 + 1 + 1), + 4 * (2 + 2 + 4 + 4 + 4 + 1), ); gl.bind_vertex_array(None); @@ -307,7 +307,7 @@ pub struct Vertex { pub border_color: [f32; 4], /// The border radius of the [`Vertex`]. - pub border_radius: f32, + pub border_radius: [f32; 4], /// The border width of the [`Vertex`]. pub border_width: f32, @@ -325,7 +325,7 @@ impl Vertex { size: quad.size, color: quad.color, border_color: quad.color, - border_radius: quad.border_radius, + border_radius: [quad.border_radius[0]; 4], border_width: quad.border_width, q_position: [0.0, 0.0], }; diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs index 16bec38539..890365301e 100644 --- a/glow/src/quad/core.rs +++ b/glow/src/quad/core.rs @@ -218,7 +218,7 @@ unsafe fn create_instance_buffer( gl.enable_vertex_attrib_array(4); gl.vertex_attrib_pointer_f32( 4, - 1, + 4, glow::FLOAT, false, stride, @@ -233,7 +233,7 @@ unsafe fn create_instance_buffer( glow::FLOAT, false, stride, - 4 * (2 + 2 + 4 + 4 + 1), + 4 * (2 + 2 + 4 + 4 + 4), ); gl.vertex_attrib_divisor(5, 1); diff --git a/glow/src/shader/core/quad.frag b/glow/src/shader/core/quad.frag index 57e2e8e737..d036ea1a45 100644 --- a/glow/src/shader/core/quad.frag +++ b/glow/src/shader/core/quad.frag @@ -17,7 +17,7 @@ in vec4 v_Color; in vec4 v_BorderColor; in vec2 v_Pos; in vec2 v_Scale; -in float v_BorderRadius; +in vec4 v_BorderRadius; in float v_BorderWidth; float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) @@ -38,14 +38,30 @@ float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) return sqrt(distance.x * distance.x + distance.y * distance.y); } +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + void main() { vec4 mixed_color; vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y); + float borderRadius = selectBorderRadius( + v_BorderRadius, + fragCoord, + (v_Pos + v_Scale * 0.5).xy + ); + // TODO: Remove branching (?) if(v_BorderWidth > 0.0) { - float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0); + float internal_border = max(borderRadius - v_BorderWidth, 0.0); float internal_distance = fDistance( fragCoord, @@ -69,11 +85,11 @@ void main() { fragCoord, v_Pos, v_Scale, - v_BorderRadius + borderRadius ); float radius_alpha = - 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d); + 1.0 - smoothstep(max(borderRadius - 0.5, 0.0), borderRadius + 0.5, d); gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } diff --git a/glow/src/shader/core/quad.vert b/glow/src/shader/core/quad.vert index b1fb2365d2..17c3e64191 100644 --- a/glow/src/shader/core/quad.vert +++ b/glow/src/shader/core/quad.vert @@ -5,14 +5,14 @@ in vec2 i_Pos; in vec2 i_Scale; in vec4 i_Color; in vec4 i_BorderColor; -in float i_BorderRadius; +in vec4 i_BorderRadius; in float i_BorderWidth; out vec4 v_Color; out vec4 v_BorderColor; out vec2 v_Pos; out vec2 v_Scale; -out float v_BorderRadius; +out vec4 v_BorderRadius; out float v_BorderWidth; vec2 positions[4] = vec2[]( @@ -27,9 +27,11 @@ void main() { vec2 p_Pos = i_Pos * u_Scale; vec2 p_Scale = i_Scale * u_Scale; - float i_BorderRadius = min( - i_BorderRadius, - min(i_Scale.x, i_Scale.y) / 2.0 + vec4 i_BorderRadius = vec4( + min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0) ); mat4 i_Transform = mat4( diff --git a/graphics/src/layer/quad.rs b/graphics/src/layer/quad.rs index 4def8427ea..0d8bde9dde 100644 --- a/graphics/src/layer/quad.rs +++ b/graphics/src/layer/quad.rs @@ -17,7 +17,7 @@ pub struct Quad { pub border_color: [f32; 4], /// The border radius of the [`Quad`]. - pub border_radius: f32, + pub border_radius: [f32; 4], /// The border width of the [`Quad`]. pub border_width: f32, diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index b481ac0baa..84f0462422 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -41,7 +41,7 @@ pub enum Primitive { /// The background of the quad background: Background, /// The border radius of the quad - border_radius: f32, + border_radius: [f32; 4], /// The border width of the quad border_width: f32, /// The border color of the quad diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index cdbc4f4062..46d37cd680 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -109,7 +109,7 @@ where self.primitives.push(Primitive::Quad { bounds: quad.bounds, background: background.into(), - border_radius: quad.border_radius, + border_radius: quad.border_radius.to_array(), border_width: quad.border_width, border_color: quad.border_color, }); diff --git a/native/src/element.rs b/native/src/element.rs index 074e422ef4..955e1beeed 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -503,7 +503,7 @@ where bounds: layout.bounds(), border_color: color, border_width: 1.0, - border_radius: 0.0, + border_radius: 0.0.into(), }, Color::TRANSPARENT, ); diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index 0813587288..dbcf23d453 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -299,7 +299,7 @@ where }, border_color: appearance.border_color, border_width: appearance.border_width, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), }, appearance.background, ); @@ -491,7 +491,7 @@ where bounds, border_color: Color::TRANSPARENT, border_width: 0.0, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), }, appearance.selected_background, ); diff --git a/native/src/renderer.rs b/native/src/renderer.rs index ef64ac36f4..39c1b4c7cb 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -50,7 +50,7 @@ pub struct Quad { pub bounds: Rectangle, /// The border radius of the [`Quad`]. - pub border_radius: f32, + pub border_radius: QuadBorderRadius, /// The border width of the [`Quad`]. pub border_width: f32, @@ -59,6 +59,30 @@ pub struct Quad { pub border_color: Color, } +/// The border radi for the corners of a [`Quad`] in the order: +/// top-left, top-right, bottom-right, bottom-left. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct QuadBorderRadius([f32; 4]); + +impl QuadBorderRadius { + /// Convert the corners of the Quad into an array [top_left, top_right, bottom_left, bottom_right]. + pub fn to_array(self) -> [f32; 4] { + self.0 + } +} + +impl From for QuadBorderRadius { + fn from(w: f32) -> Self { + Self([w; 4]) + } +} + +impl From<[f32; 4]> for QuadBorderRadius { + fn from(radi: [f32; 4]) -> Self { + Self(radi) + } +} + /// The styling attributes of a [`Renderer`]. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 6c0b8f6e79..14759cfd64 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -393,7 +393,7 @@ where y: bounds.y + styling.shadow_offset.y, ..bounds }, - border_radius: styling.border_radius, + border_radius: styling.border_radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -404,7 +404,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: styling.border_radius, + border_radius: styling.border_radius.into(), border_width: styling.border_width, border_color: styling.border_color, }, diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index dc3c0bd061..8f30037ae2 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -236,7 +236,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: custom_style.border_radius, + border_radius: custom_style.border_radius.into(), border_width: custom_style.border_width, border_color: custom_style.border_color, }, diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 2afad3f2d3..b0c4938adc 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -321,7 +321,7 @@ pub fn draw_background( renderer.fill_quad( renderer::Quad { bounds, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), border_width: appearance.border_width, border_color: appearance.border_color, }, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 96cf78efa2..45090ecf6c 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -828,7 +828,7 @@ pub fn draw( height: split_region.height, }, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 896f5b3582..a92ea6552c 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -514,7 +514,7 @@ pub fn draw( bounds, border_color: style.border_color, border_width: style.border_width, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), }, style.background, ); diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index 8a9454332b..5f8892fe45 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -129,7 +129,7 @@ where renderer.fill_quad( renderer::Quad { bounds: Rectangle { ..bounds }, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -143,7 +143,7 @@ where width: active_progress_width, ..bounds }, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index cb83f745df..10e8987093 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -245,7 +245,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: size / 2.0, + border_radius: (size / 2.0).into(), border_width: custom_style.border_width, border_color: custom_style.border_color, }, @@ -261,7 +261,7 @@ where width: bounds.width - dot_size, height: bounds.height - dot_size, }, - border_radius: dot_size / 2.0, + border_radius: (dot_size / 2.0).into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs index 56f8c80d36..1d1c04cfcd 100644 --- a/native/src/widget/rule.rs +++ b/native/src/widget/rule.rs @@ -123,7 +123,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: style.radius, + border_radius: style.radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 4ebb07a065..b445e505a8 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -698,7 +698,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: scrollbar.bounds, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), border_width: style.border_width, border_color: style.border_color, }, @@ -715,7 +715,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: scrollbar.scroller.bounds, - border_radius: style.scroller.border_radius, + border_radius: style.scroller.border_radius.into(), border_width: style.scroller.border_width, border_color: style.scroller.border_color, }, diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 585d9c35c9..011454de90 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -380,7 +380,7 @@ pub fn draw( width: bounds.width, height: 2.0, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -395,7 +395,7 @@ pub fn draw( width: bounds.width, height: 2.0, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -435,7 +435,7 @@ pub fn draw( width: handle_width, height: handle_height, }, - border_radius: handle_border_radius, + border_radius: handle_border_radius.into(), border_width: style.handle.border_width, border_color: style.handle.border_color, }, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 54a6aaf805..ae0305dc57 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -766,7 +766,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), border_width: appearance.border_width, border_color: appearance.border_color, }, @@ -798,7 +798,7 @@ pub fn draw( width: 1.0, height: text_bounds.height, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -842,7 +842,7 @@ pub fn draw( width, height: text_bounds.height, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 7893f78c79..c5c2d82afb 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -278,7 +278,7 @@ where renderer.fill_quad( renderer::Quad { bounds: toggler_background_bounds, - border_radius, + border_radius: border_radius.into(), border_width: 1.0, border_color: style .background_border @@ -302,7 +302,7 @@ where renderer.fill_quad( renderer::Quad { bounds: toggler_foreground_bounds, - border_radius, + border_radius: border_radius.into(), border_width: 1.0, border_color: style .foreground_border diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index a117df64c2..027a34bebe 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -91,7 +91,7 @@ impl Pipeline { 2 => Float32x2, 3 => Float32x4, 4 => Float32x4, - 5 => Float32, + 5 => Float32x4, 6 => Float32, ), }, diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 73edd97ce0..cf4f7e4d5b 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -11,7 +11,7 @@ struct VertexInput { @location(2) scale: vec2, @location(3) color: vec4, @location(4) border_color: vec4, - @location(5) border_radius: f32, + @location(5) border_radius: vec4, @location(6) border_width: f32, } @@ -21,7 +21,7 @@ struct VertexOutput { @location(1) border_color: vec4, @location(2) pos: vec2, @location(3) scale: vec2, - @location(4) border_radius: f32, + @location(4) border_radius: vec4, @location(5) border_width: f32, } @@ -32,9 +32,12 @@ fn vs_main(input: VertexInput) -> VertexOutput { var pos: vec2 = input.pos * globals.scale; var scale: vec2 = input.scale * globals.scale; - var border_radius: f32 = min( - input.border_radius, - min(input.scale.x, input.scale.y) / 2.0 + var min_border_radius = min(input.scale.x, input.scale.y) * 0.5; + var border_radius: vec4 = vec4( + min(input.border_radius.x, min_border_radius), + min(input.border_radius.y, min_border_radius), + min(input.border_radius.z, min_border_radius), + min(input.border_radius.w, min_border_radius) ); var transform: mat4x4 = mat4x4( @@ -76,6 +79,18 @@ fn distance_alg( return sqrt(dist.x * dist.x + dist.y * dist.y); } +// Based on the fragement position and the center of the quad, select one of the 4 radi. +// Order matches CSS border radius attribute: +// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left +fn select_border_radius(radi: vec4, position: vec2, center: vec2) -> f32 { + var rx = radi.x; + var ry = radi.y; + rx = select(radi.x, radi.y, position.x > center.x); + ry = select(radi.w, radi.z, position.x > center.x); + rx = select(rx, ry, position.y > center.y); + return rx; +} + @fragment fn fs_main( @@ -83,14 +98,17 @@ fn fs_main( ) -> @location(0) vec4 { var mixed_color: vec4 = input.color; + var border_radius = select_border_radius( + input.border_radius, + input.position.xy, + (input.pos + input.scale * 0.5).xy + ); + if (input.border_width > 0.0) { - var internal_border: f32 = max( - input.border_radius - input.border_width, - 0.0 - ); + var internal_border: f32 = max(border_radius - input.border_width, 0.0); var internal_distance: f32 = distance_alg( - vec2(input.position.x, input.position.y), + input.position.xy, input.pos + vec2(input.border_width, input.border_width), input.scale - vec2(input.border_width * 2.0, input.border_width * 2.0), internal_border @@ -109,13 +127,14 @@ fn fs_main( vec2(input.position.x, input.position.y), input.pos, input.scale, - input.border_radius + border_radius ); var radius_alpha: f32 = 1.0 - smoothstep( - max(input.border_radius - 0.5, 0.0), - input.border_radius + 0.5, - dist); + max(border_radius - 0.5, 0.0), + border_radius + 0.5, + dist + ); return vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); } From dcec3fd792cf422aef7a7217ec7f0311fbd12390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 8 Nov 2022 04:50:26 +0100 Subject: [PATCH 02/43] Fix `compatibility::quad` pipeline in `iced_glow` --- glow/src/quad/compatibility.rs | 2 +- glow/src/shader/compatibility/quad.frag | 24 ++++++++++++++++++++---- glow/src/shader/compatibility/quad.vert | 12 +++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs index 5734c15926..e909162c3b 100644 --- a/glow/src/quad/compatibility.rs +++ b/glow/src/quad/compatibility.rs @@ -325,7 +325,7 @@ impl Vertex { size: quad.size, color: quad.color, border_color: quad.color, - border_radius: [quad.border_radius[0]; 4], + border_radius: quad.border_radius, border_width: quad.border_width, q_position: [0.0, 0.0], }; diff --git a/glow/src/shader/compatibility/quad.frag b/glow/src/shader/compatibility/quad.frag index 8ea5693d1d..bb9d8122d1 100644 --- a/glow/src/shader/compatibility/quad.frag +++ b/glow/src/shader/compatibility/quad.frag @@ -12,7 +12,7 @@ varying vec4 v_Color; varying vec4 v_BorderColor; varying vec2 v_Pos; varying vec2 v_Scale; -varying float v_BorderRadius; +varying vec4 v_BorderRadius; varying float v_BorderWidth; float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius) @@ -33,10 +33,26 @@ float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius) return sqrt(distance.x * distance.x + distance.y * distance.y); } +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + void main() { vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y); - float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0); + float border_radius = selectBorderRadius( + v_BorderRadius, + fragCoord, + (v_Pos + v_Scale * 0.5).xy + ); + + float internal_border = max(border_radius - v_BorderWidth, 0.0); float internal_distance = _distance( fragCoord, @@ -57,11 +73,11 @@ void main() { fragCoord, v_Pos, v_Scale, - v_BorderRadius + border_radius ); float radius_alpha = - 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d); + 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } diff --git a/glow/src/shader/compatibility/quad.vert b/glow/src/shader/compatibility/quad.vert index abe70c0eed..89931f0671 100644 --- a/glow/src/shader/compatibility/quad.vert +++ b/glow/src/shader/compatibility/quad.vert @@ -5,7 +5,7 @@ attribute vec2 i_Pos; attribute vec2 i_Scale; attribute vec4 i_Color; attribute vec4 i_BorderColor; -attribute float i_BorderRadius; +attribute vec4 i_BorderRadius; attribute float i_BorderWidth; attribute vec2 q_Pos; @@ -13,7 +13,7 @@ varying vec4 v_Color; varying vec4 v_BorderColor; varying vec2 v_Pos; varying vec2 v_Scale; -varying float v_BorderRadius; +varying vec4 v_BorderRadius; varying float v_BorderWidth; @@ -21,9 +21,11 @@ void main() { vec2 p_Pos = i_Pos * u_Scale; vec2 p_Scale = i_Scale * u_Scale; - float i_BorderRadius = min( - i_BorderRadius, - min(i_Scale.x, i_Scale.y) / 2.0 + vec4 i_BorderRadius = vec4( + min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0) ); mat4 i_Transform = mat4( From 15f21641b7a27bbfb99f9f9f5d21b84445173900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 8 Nov 2022 04:51:45 +0100 Subject: [PATCH 03/43] Fix casing of `border_radius` in `quad` shader --- glow/src/shader/core/quad.frag | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/glow/src/shader/core/quad.frag b/glow/src/shader/core/quad.frag index d036ea1a45..71147aa51d 100644 --- a/glow/src/shader/core/quad.frag +++ b/glow/src/shader/core/quad.frag @@ -53,7 +53,7 @@ void main() { vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y); - float borderRadius = selectBorderRadius( + float border_radius = selectBorderRadius( v_BorderRadius, fragCoord, (v_Pos + v_Scale * 0.5).xy @@ -61,7 +61,7 @@ void main() { // TODO: Remove branching (?) if(v_BorderWidth > 0.0) { - float internal_border = max(borderRadius - v_BorderWidth, 0.0); + float internal_border = max(border_radius - v_BorderWidth, 0.0); float internal_distance = fDistance( fragCoord, @@ -85,11 +85,11 @@ void main() { fragCoord, v_Pos, v_Scale, - borderRadius + border_radius ); float radius_alpha = - 1.0 - smoothstep(max(borderRadius - 0.5, 0.0), borderRadius + 0.5, d); + 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } From 676d8efe03ebdbeeb95aef96b8097395b788b1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 8 Nov 2022 04:59:34 +0100 Subject: [PATCH 04/43] Rename `QuadBorderRadius` to `BorderRadius` --- graphics/src/renderer.rs | 2 +- native/src/renderer.rs | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 46d37cd680..ff0dad2b6a 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -109,7 +109,7 @@ where self.primitives.push(Primitive::Quad { bounds: quad.bounds, background: background.into(), - border_radius: quad.border_radius.to_array(), + border_radius: quad.border_radius.into(), border_width: quad.border_width, border_color: quad.border_color, }); diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 39c1b4c7cb..5e776be6cd 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -50,7 +50,7 @@ pub struct Quad { pub bounds: Rectangle, /// The border radius of the [`Quad`]. - pub border_radius: QuadBorderRadius, + pub border_radius: BorderRadius, /// The border width of the [`Quad`]. pub border_width: f32, @@ -59,30 +59,29 @@ pub struct Quad { pub border_color: Color, } -/// The border radi for the corners of a [`Quad`] in the order: +/// The border radi for the corners of a graphics primitive in the order: /// top-left, top-right, bottom-right, bottom-left. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct QuadBorderRadius([f32; 4]); - -impl QuadBorderRadius { - /// Convert the corners of the Quad into an array [top_left, top_right, bottom_left, bottom_right]. - pub fn to_array(self) -> [f32; 4] { - self.0 - } -} +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct BorderRadius([f32; 4]); -impl From for QuadBorderRadius { +impl From for BorderRadius { fn from(w: f32) -> Self { Self([w; 4]) } } -impl From<[f32; 4]> for QuadBorderRadius { +impl From<[f32; 4]> for BorderRadius { fn from(radi: [f32; 4]) -> Self { Self(radi) } } +impl From for [f32; 4] { + fn from(radi: BorderRadius) -> Self { + radi.0 + } +} + /// The styling attributes of a [`Renderer`]. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { From 365f37a3ae10e7aff407b84050f77da10820866e Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Thu, 10 Nov 2022 14:43:38 -0800 Subject: [PATCH 05/43] Added conditional configurations for WASM target for gradients & storage buffers, since storage buffers are not supported on wgpu WASM target at the moment. --- glow/src/triangle.rs | 1 + graphics/src/triangle.rs | 6 +++++- graphics/src/widget/canvas/frame.rs | 1 + wgpu/src/buffer/dynamic.rs | 7 +++++++ wgpu/src/triangle.rs | 15 +++++++++++++-- 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs index 0e27bcf2a7..cb7ab05579 100644 --- a/glow/src/triangle.rs +++ b/glow/src/triangle.rs @@ -141,6 +141,7 @@ impl Pipeline { triangle::Style::Solid(color) => { self.programs.solid.use_program(gl, color, &transform); } + #[cfg(not(target_arch = "wasm32"))] triangle::Style::Gradient(gradient) => { self.programs .gradient diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index 04ff6d21f5..8b41bfc4af 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -1,5 +1,7 @@ //! Draw geometry using meshes of triangles. -use crate::{Color, Gradient}; +use crate::Color; +#[cfg(not(target_arch = "wasm32"))] +use crate::Gradient; use bytemuck::{Pod, Zeroable}; @@ -27,6 +29,7 @@ pub struct Vertex2D { pub enum Style { /// Fill a primitive with a solid color. Solid(Color), + #[cfg(not(target_arch = "wasm32"))] /// Fill a primitive with an interpolated color. Gradient(Gradient), } @@ -37,6 +40,7 @@ impl From for Style { } } +#[cfg(not(target_arch = "wasm32"))] impl From for Style { fn from(gradient: Gradient) -> Self { Self::Gradient(gradient) diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index cf6c6928c9..a7b8850252 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -76,6 +76,7 @@ impl Transform { fn transform_style(&self, style: triangle::Style) -> triangle::Style { match style { triangle::Style::Solid(color) => triangle::Style::Solid(color), + #[cfg(not(target_arch = "wasm32"))] triangle::Style::Gradient(gradient) => { triangle::Style::Gradient(self.transform_gradient(gradient)) } diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs index c0c48c7439..2a675d8124 100644 --- a/wgpu/src/buffer/dynamic.rs +++ b/wgpu/src/buffer/dynamic.rs @@ -24,6 +24,7 @@ impl Buffer { ) } + #[cfg(not(target_arch = "wasm32"))] /// Creates a new dynamic storage buffer. pub fn storage(device: &wgpu::Device, label: &'static str) -> Self { Buffer::new( @@ -91,6 +92,7 @@ impl Buffer { Internal::Uniform(_) => { wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST } + #[cfg(not(target_arch = "wasm32"))] Internal::Storage(_) => { wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST } @@ -154,6 +156,8 @@ impl Buffer { // Currently supported dynamic buffers. enum Internal { Uniform(encase::DynamicUniformBuffer>), + #[cfg(not(target_arch = "wasm32"))] + //storage buffers are not supported on wgpu wasm target (yet) Storage(encase::DynamicStorageBuffer>), } @@ -168,6 +172,7 @@ impl Internal { .write(value) .expect("Error when writing to dynamic uniform buffer.") as u32, + #[cfg(not(target_arch = "wasm32"))] Internal::Storage(buf) => buf .write(value) .expect("Error when writing to dynamic storage buffer.") @@ -179,6 +184,7 @@ impl Internal { pub(super) fn get_ref(&self) -> &Vec { match self { Internal::Uniform(buf) => buf.as_ref(), + #[cfg(not(target_arch = "wasm32"))] Internal::Storage(buf) => buf.as_ref(), } } @@ -190,6 +196,7 @@ impl Internal { buf.as_mut().clear(); buf.set_offset(0); } + #[cfg(not(target_arch = "wasm32"))] Internal::Storage(buf) => { buf.as_mut().clear(); buf.set_offset(0); diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index f9abf2b510..c51b53396f 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,4 +1,5 @@ //! Draw meshes of triangles. +#[cfg(not(target_arch = "wasm32"))] mod gradient; mod msaa; mod solid; @@ -27,6 +28,8 @@ pub(crate) struct Pipeline { /// Supported triangle pipelines for different fills. pub(crate) struct PipelineList { solid: solid::Pipeline, + /// Gradients are currently not supported on WASM targets due to their need of storage buffers. + #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Pipeline, } @@ -40,8 +43,11 @@ impl PipelineList { /// Resets each pipeline's buffers. fn clear(&mut self) { self.solid.buffer.clear(); - self.gradient.uniform_buffer.clear(); - self.gradient.storage_buffer.clear(); + #[cfg(not(target_arch = "wasm32"))] + { + self.gradient.uniform_buffer.clear(); + self.gradient.storage_buffer.clear(); + } } /// Writes the contents of each pipeline's CPU buffer to the GPU, resizing the GPU buffer @@ -53,6 +59,7 @@ impl PipelineList { encoder: &mut wgpu::CommandEncoder, ) { self.solid.write(device, staging_belt, encoder); + #[cfg(not(target_arch = "wasm32"))] self.gradient.write(device, staging_belt, encoder); } } @@ -79,6 +86,7 @@ impl Pipeline { index_strides: Vec::new(), pipelines: PipelineList { solid: solid::Pipeline::new(device, format, antialiasing), + #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Pipeline::new(device, format, antialiasing), }, } @@ -145,6 +153,7 @@ impl Pipeline { triangle::Style::Solid(color) => { self.pipelines.solid.push(transform, color); } + #[cfg(not(target_arch = "wasm32"))] triangle::Style::Gradient(gradient) => { self.pipelines.gradient.push(transform, gradient); } @@ -186,6 +195,7 @@ impl Pipeline { }); let mut num_solids = 0; + #[cfg(not(target_arch = "wasm32"))] let mut num_gradients = 0; let mut last_is_solid = None; @@ -216,6 +226,7 @@ impl Pipeline { num_solids += 1; } + #[cfg(not(target_arch = "wasm32"))] triangle::Style::Gradient(_) => { if last_is_solid.unwrap_or(true) { self.pipelines From 44aba47b0e67ce3cf1d5e8a79768e8fe996f5580 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Fri, 11 Nov 2022 08:44:10 +0800 Subject: [PATCH 06/43] Allow converting from widget-specific IDs to generic ID --- native/src/widget/scrollable.rs | 6 ++++++ native/src/widget/text_input.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index b257cbe589..32ec6eb3f1 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -334,6 +334,12 @@ impl Id { } } +impl From for widget::Id { + fn from(id: Id) -> Self { + id.0 + } +} + /// Produces a [`Command`] that snaps the [`Scrollable`] with the given [`Id`] /// to the provided `percentage`. pub fn snap_to(id: Id, percentage: f32) -> Command { diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 2315b05ab1..e57f41ea8b 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -332,6 +332,12 @@ impl Id { } } +impl From for widget::Id { + fn from(id: Id) -> Self { + id.0 + } +} + /// Produces a [`Command`] that focuses the [`TextInput`] with the given [`Id`]. pub fn focus(id: Id) -> Command { Command::widget(operation::focusable::focus(id.0)) From e56c45470c9a00e31d92e4e7d4b1cb1894d4de7d Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Fri, 11 Nov 2022 09:15:35 +0800 Subject: [PATCH 07/43] Add widget operation to find currently focused widget --- native/src/widget/operation/focusable.rs | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/native/src/widget/operation/focusable.rs b/native/src/widget/operation/focusable.rs index f17bf17861..0067006bbd 100644 --- a/native/src/widget/operation/focusable.rs +++ b/native/src/widget/operation/focusable.rs @@ -167,3 +167,37 @@ pub fn focus_next() -> impl Operation { count(|count| FocusNext { count, current: 0 }) } + +/// Produces an [`Operation`] that searches for the current focused widget +/// and stores its ID. This ignores widgets that do not have an ID. +pub fn find_focused() -> impl Operation { + struct FindFocused { + focused: Option, + } + + impl Operation for FindFocused { + fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { + if state.is_focused() && id.is_some() { + self.focused = id.cloned(); + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + + fn finish(&self) -> Outcome { + if let Some(id) = &self.focused { + Outcome::Some(id.clone()) + } else { + Outcome::None + } + } + } + + FindFocused { focused: None } +} From 8a5a365be93fd72dcc1b49a902820ac459f99d84 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Fri, 11 Nov 2022 20:00:18 +0800 Subject: [PATCH 08/43] #1484, #1527: Fix overlay transition --- native/src/user_interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 341eeb3a11..bc74c83503 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -255,7 +255,7 @@ where cursor_position }; - self.overlay = Some(layout); + self.overlay = None; (base_cursor, event_statuses) } else { From c4bca3f2af7ae9b2c8dfc3af48ab73dad852ed34 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 11 Nov 2022 08:43:36 -0800 Subject: [PATCH 09/43] Add text input operations --- lazy/src/component.rs | 8 ++ native/src/element.rs | 8 ++ native/src/overlay/element.rs | 8 ++ native/src/widget/action.rs | 8 ++ native/src/widget/operation.rs | 5 + native/src/widget/operation/text_input.rs | 131 ++++++++++++++++++++++ native/src/widget/text_input.rs | 45 ++++++++ src/widget.rs | 3 +- 8 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 native/src/widget/operation/text_input.rs diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 8987b9930f..4f1df6504c 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -260,6 +260,14 @@ where ) { self.operation.focusable(state, id); } + + fn text_input( + &mut self, + state: &mut dyn widget::operation::TextInput, + id: Option<&widget::Id>, + ) { + self.operation.text_input(state, id); + } } self.with_element(|element| { diff --git a/native/src/element.rs b/native/src/element.rs index c82d8e965a..f941a4904b 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -324,6 +324,14 @@ where ) { self.operation.scrollable(state, id); } + + fn text_input( + &mut self, + state: &mut dyn widget::operation::TextInput, + id: Option<&widget::Id>, + ) { + self.operation.text_input(state, id); + } } self.widget diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index 2c558086b3..09eee86d63 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -178,6 +178,14 @@ where ) { self.operation.scrollable(state, id); } + + fn text_input( + &mut self, + state: &mut dyn widget::operation::TextInput, + id: Option<&widget::Id>, + ) { + self.operation.text_input(state, id) + } } self.content diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs index 0345fe2b41..593d6f6396 100644 --- a/native/src/widget/action.rs +++ b/native/src/widget/action.rs @@ -93,4 +93,12 @@ where ) { self.operation.scrollable(state, id); } + + fn text_input( + &mut self, + state: &mut dyn operation::TextInput, + id: Option<&Id>, + ) { + self.operation.text_input(state, id); + } } diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs index 2b1179f16d..a0aa411729 100644 --- a/native/src/widget/operation.rs +++ b/native/src/widget/operation.rs @@ -1,9 +1,11 @@ //! Query or update internal widget state. pub mod focusable; pub mod scrollable; +pub mod text_input; pub use focusable::Focusable; pub use scrollable::Scrollable; +pub use text_input::TextInput; use crate::widget::Id; @@ -28,6 +30,9 @@ pub trait Operation { /// Operates on a widget that can be scrolled. fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {} + /// Operates on a widget that has text input. + fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {} + /// Finishes the [`Operation`] and returns its [`Outcome`]. fn finish(&self) -> Outcome { Outcome::None diff --git a/native/src/widget/operation/text_input.rs b/native/src/widget/operation/text_input.rs new file mode 100644 index 0000000000..4c773e9984 --- /dev/null +++ b/native/src/widget/operation/text_input.rs @@ -0,0 +1,131 @@ +//! Operate on widgets that have text input. +use crate::widget::operation::Operation; +use crate::widget::Id; + +/// The internal state of a widget that has text input. +pub trait TextInput { + /// Moves the cursor of the text input to the front of the input text. + fn move_cursor_to_front(&mut self); + /// Moves the cursor of the text input to the end of the input text. + fn move_cursor_to_end(&mut self); + /// Moves the cursor of the text input to an arbitrary location. + fn move_cursor_to(&mut self, position: usize); + /// Selects all the content of the text input. + fn select_all(&mut self); +} + +/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the +/// front. +pub fn move_cursor_to_front(target: Id) -> impl Operation { + struct MoveCursor { + target: Id, + } + + impl Operation for MoveCursor { + fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { + match id { + Some(id) if id == &self.target => { + state.move_cursor_to_front(); + } + _ => {} + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + } + + MoveCursor { target } +} + +/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the +/// end. +pub fn move_cursor_to_end(target: Id) -> impl Operation { + struct MoveCursor { + target: Id, + } + + impl Operation for MoveCursor { + fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { + match id { + Some(id) if id == &self.target => { + state.move_cursor_to_end(); + } + _ => {} + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + } + + MoveCursor { target } +} + +/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the +/// provided position. +pub fn move_cursor_to(target: Id, position: usize) -> impl Operation { + struct MoveCursor { + target: Id, + position: usize, + } + + impl Operation for MoveCursor { + fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { + match id { + Some(id) if id == &self.target => { + state.move_cursor_to(self.position); + } + _ => {} + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + } + + MoveCursor { target, position } +} + +/// Produces an [`Operation`] that selects all the content of the widget with the given [`Id`]. +pub fn select_all(target: Id) -> impl Operation { + struct MoveCursor { + target: Id, + } + + impl Operation for MoveCursor { + fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { + match id { + Some(id) if id == &self.target => { + state.select_all(); + } + _ => {} + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + } + + MoveCursor { target } +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 2315b05ab1..e28861812e 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -233,6 +233,7 @@ where let state = tree.state.downcast_mut::(); operation.focusable(state, self.id.as_ref().map(|id| &id.0)); + operation.text_input(state, self.id.as_ref().map(|id| &id.0)); } fn on_event( @@ -337,6 +338,32 @@ pub fn focus(id: Id) -> Command { Command::widget(operation::focusable::focus(id.0)) } +/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the +/// end. +pub fn move_cursor_to_end(id: Id) -> Command { + Command::widget(operation::text_input::move_cursor_to_end(id.0)) +} + +/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the +/// front. +pub fn move_cursor_to_front(id: Id) -> Command { + Command::widget(operation::text_input::move_cursor_to_front(id.0)) +} + +/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the +/// provided position. +pub fn move_cursor_to( + id: Id, + position: usize, +) -> Command { + Command::widget(operation::text_input::move_cursor_to(id.0, position)) +} + +/// Produces a [`Command`] that selects all the content of the [`TextInput`] with the given [`Id`]. +pub fn select_all(id: Id) -> Command { + Command::widget(operation::text_input::select_all(id.0)) +} + /// Computes the layout of a [`TextInput`]. pub fn layout( renderer: &Renderer, @@ -1001,6 +1028,24 @@ impl operation::Focusable for State { } } +impl operation::TextInput for State { + fn move_cursor_to_front(&mut self) { + State::move_cursor_to_front(self) + } + + fn move_cursor_to_end(&mut self) { + State::move_cursor_to_end(self) + } + + fn move_cursor_to(&mut self, position: usize) { + State::move_cursor_to(self, position) + } + + fn select_all(&mut self) { + State::select_all(self) + } +} + mod platform { use crate::keyboard; diff --git a/src/widget.rs b/src/widget.rs index 8c8ea21652..7c67a59963 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -120,7 +120,8 @@ pub mod toggler { pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ - focus, Appearance, Id, StyleSheet, + focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, + select_all, Appearance, Id, StyleSheet, }; /// A field that can be filled with text. From a7a4a92466a4447f8d2e9b5ae2fc715ba6262de0 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 11 Nov 2022 09:08:52 -0800 Subject: [PATCH 10/43] Update todos example to select_all input on edit --- examples/todos/src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index be48ae8cc6..690d9c0979 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -131,7 +131,11 @@ impl Application for Todos { task.update(task_message); if should_focus { - text_input::focus(Task::text_input_id(i)) + let id = Task::text_input_id(i); + Command::batch(vec![ + text_input::focus(id.clone()), + text_input::select_all(id), + ]) } else { Command::none() } From b0678f4c75d9913b2a1f11392f94f69af7db2efd Mon Sep 17 00:00:00 2001 From: Ryan Scheidter Date: Sun, 13 Nov 2022 14:21:27 -0600 Subject: [PATCH 11/43] Implement `Widget::operate` for `PaneGrid` --- native/src/widget/pane_grid.rs | 18 +++++++++++ native/src/widget/pane_grid/content.rs | 29 ++++++++++++++++- native/src/widget/pane_grid/title_bar.rs | 40 +++++++++++++++++++++++- 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 321e4e75fb..8f9065b099 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -38,6 +38,7 @@ use crate::mouse; use crate::overlay; use crate::renderer; use crate::touch; +use crate::widget; use crate::widget::container; use crate::widget::tree::{self, Tree}; use crate::{ @@ -289,6 +290,23 @@ where ) } + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + operation: &mut dyn widget::Operation, + ) { + operation.container(None, &mut |operation| { + self.contents + .iter() + .zip(&mut tree.children) + .zip(layout.children()) + .for_each(|(((_pane, content), state), layout)| { + content.operate(state, layout, operation); + }) + }); + } + fn on_event( &mut self, tree: &mut Tree, diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 7e6c8148dc..5e843cff74 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -5,7 +5,7 @@ use crate::overlay; use crate::renderer; use crate::widget::container; use crate::widget::pane_grid::{Draggable, TitleBar}; -use crate::widget::Tree; +use crate::widget::{self, Tree}; use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; /// The content of a [`Pane`]. @@ -183,6 +183,33 @@ where } } + pub(crate) fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + operation: &mut dyn widget::Operation, + ) { + let body_layout = if let Some(title_bar) = &self.title_bar { + let mut children = layout.children(); + + title_bar.operate( + &mut tree.children[1], + children.next().unwrap(), + operation, + ); + + children.next().unwrap() + } else { + layout + }; + + self.body.as_widget().operate( + &mut tree.children[0], + body_layout, + operation, + ); + } + pub(crate) fn on_event( &mut self, tree: &mut Tree, diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 1b70e51b79..115f6270da 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -4,7 +4,7 @@ use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget::container; -use crate::widget::Tree; +use crate::widget::{self, Tree}; use crate::{ Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size, }; @@ -257,6 +257,44 @@ where layout::Node::with_children(node.size().pad(self.padding), vec![node]) } + pub(crate) fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + operation: &mut dyn widget::Operation, + ) { + let mut children = layout.children(); + let padded = children.next().unwrap(); + + let mut children = padded.children(); + let title_layout = children.next().unwrap(); + let mut show_title = true; + + if let Some(controls) = &self.controls { + let controls_layout = children.next().unwrap(); + + if title_layout.bounds().width + controls_layout.bounds().width + > padded.bounds().width + { + show_title = false; + } + + controls.as_widget().operate( + &mut tree.children[1], + controls_layout, + operation, + ) + }; + + if show_title { + self.content.as_widget().operate( + &mut tree.children[0], + title_layout, + operation, + ) + } + } + pub(crate) fn on_event( &mut self, tree: &mut Tree, From cfdfec97707c492568096684309f79dc20c94262 Mon Sep 17 00:00:00 2001 From: Ryan Scheidter Date: Sun, 13 Nov 2022 15:41:47 -0600 Subject: [PATCH 12/43] Partially Fixed Mapped Operations --- native/src/widget/action.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs index 593d6f6396..c78c577692 100644 --- a/native/src/widget/action.rs +++ b/native/src/widget/action.rs @@ -1,4 +1,4 @@ -use crate::widget::operation::{self, Operation}; +use crate::widget::operation::{self, Focusable, Operation, Scrollable}; use crate::widget::Id; use iced_futures::MaybeSend; @@ -67,6 +67,14 @@ where operate_on_children(&mut MapRef { operation, f }); }); } + + fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { + self.operation.scrollable(state, id); + } + + fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { + self.operation.focusable(state, id); + } } let Self { operation, f } = self; From cbb3475d4c34941f59e8e80ddd16c9d61f56c6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 14 Nov 2022 00:14:28 +0100 Subject: [PATCH 13/43] Implement `Operation::finish` for `action::widget::Map` --- native/src/widget/action.rs | 45 ++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs index c78c577692..9aa79dec47 100644 --- a/native/src/widget/action.rs +++ b/native/src/widget/action.rs @@ -3,6 +3,8 @@ use crate::widget::Id; use iced_futures::MaybeSend; +use std::rc::Rc; + /// An operation to be performed on the widget tree. #[allow(missing_debug_implementations)] pub struct Action(Box>); @@ -24,7 +26,7 @@ impl Action { { Action(Box::new(Map { operation: self.0, - f: Box::new(f), + f: Rc::new(f), })) } @@ -37,7 +39,7 @@ impl Action { #[allow(missing_debug_implementations)] struct Map { operation: Box>, - f: Box B>, + f: Rc B>, } impl Operation for Map @@ -50,38 +52,44 @@ where id: Option<&Id>, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - struct MapRef<'a, A, B> { + struct MapRef<'a, A> { operation: &'a mut dyn Operation, - f: &'a dyn Fn(A) -> B, } - impl<'a, A, B> Operation for MapRef<'a, A, B> { + impl<'a, A, B> Operation for MapRef<'a, A> { fn container( &mut self, id: Option<&Id>, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - let Self { operation, f } = self; + let Self { operation, .. } = self; operation.container(id, &mut |operation| { - operate_on_children(&mut MapRef { operation, f }); + operate_on_children(&mut MapRef { operation }); }); } - fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + ) { self.operation.scrollable(state, id); } - fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { + fn focusable( + &mut self, + state: &mut dyn Focusable, + id: Option<&Id>, + ) { self.operation.focusable(state, id); } } - let Self { operation, f } = self; + let Self { operation, .. } = self; MapRef { operation: operation.as_mut(), - f, } .container(id, operate_on_children); } @@ -109,4 +117,19 @@ where ) { self.operation.text_input(state, id); } + + fn finish(&self) -> operation::Outcome { + match self.operation.finish() { + operation::Outcome::None => operation::Outcome::None, + operation::Outcome::Some(output) => { + operation::Outcome::Some((self.f)(output)) + } + operation::Outcome::Chain(next) => { + operation::Outcome::Chain(Box::new(Map { + operation: next, + f: self.f.clone(), + })) + } + } + } } From bbd55ff3a9338775cf87f236265369d9eddae4bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 14 Nov 2022 00:50:18 +0100 Subject: [PATCH 14/43] Invalidate `overlay` layout when `base` layer captures an `Event` --- native/src/user_interface.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index bc74c83503..7c82878c01 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -255,7 +255,7 @@ where cursor_position }; - self.overlay = None; + self.overlay = Some(layout); (base_cursor, event_statuses) } else { @@ -285,6 +285,10 @@ where &mut shell, ); + if matches!(event_status, event::Status::Captured) { + self.overlay = None; + } + shell.revalidate_layout(|| { self.base = renderer.layout( &self.root, From 33c3c0c0aa774bb7462e3c42aa04c591a66376a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 14 Nov 2022 00:02:42 +0100 Subject: [PATCH 15/43] Group all solid triangles independently of color --- .../common/{triangle.vert => gradient.vert} | 0 .../common/{triangle.frag => solid.frag} | 4 +- glow/src/shader/common/solid.vert | 11 + glow/src/triangle.rs | 562 ++++++++++++--- glow/src/triangle/gradient.rs | 162 ----- glow/src/triangle/solid.rs | 91 --- graphics/src/layer.rs | 25 +- graphics/src/layer/mesh.rs | 94 ++- graphics/src/primitive.rs | 25 +- graphics/src/triangle.rs | 37 +- graphics/src/widget/canvas.rs | 2 + graphics/src/widget/canvas/fill.rs | 2 +- graphics/src/widget/canvas/frame.rs | 196 +++-- graphics/src/widget/canvas/stroke.rs | 2 +- graphics/src/widget/canvas/style.rs | 11 + wgpu/src/buffer/dynamic.rs | 15 +- wgpu/src/buffer/static.rs | 2 +- wgpu/src/lib.rs | 2 +- wgpu/src/shader/solid.wgsl | 29 +- wgpu/src/shader/triangle.wgsl | 30 - wgpu/src/triangle.rs | 671 ++++++++++++++---- wgpu/src/triangle/gradient.rs | 268 ------- wgpu/src/triangle/solid.rs | 170 ----- 23 files changed, 1335 insertions(+), 1076 deletions(-) rename glow/src/shader/common/{triangle.vert => gradient.vert} (100%) rename glow/src/shader/common/{triangle.frag => solid.frag} (82%) create mode 100644 glow/src/shader/common/solid.vert delete mode 100644 glow/src/triangle/gradient.rs delete mode 100644 glow/src/triangle/solid.rs create mode 100644 graphics/src/widget/canvas/style.rs delete mode 100644 wgpu/src/shader/triangle.wgsl delete mode 100644 wgpu/src/triangle/gradient.rs delete mode 100644 wgpu/src/triangle/solid.rs diff --git a/glow/src/shader/common/triangle.vert b/glow/src/shader/common/gradient.vert similarity index 100% rename from glow/src/shader/common/triangle.vert rename to glow/src/shader/common/gradient.vert diff --git a/glow/src/shader/common/triangle.frag b/glow/src/shader/common/solid.frag similarity index 82% rename from glow/src/shader/common/triangle.frag rename to glow/src/shader/common/solid.frag index 8260f6a6dc..174ffdd3e0 100644 --- a/glow/src/shader/common/triangle.frag +++ b/glow/src/shader/common/solid.frag @@ -11,8 +11,8 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif -uniform vec4 color; +in vec4 v_Color; void main() { - gl_FragColor = color; + gl_FragColor = v_Color; } diff --git a/glow/src/shader/common/solid.vert b/glow/src/shader/common/solid.vert new file mode 100644 index 0000000000..59ed88e596 --- /dev/null +++ b/glow/src/shader/common/solid.vert @@ -0,0 +1,11 @@ +uniform mat4 u_Transform; + +in vec2 i_Position; +in vec4 i_Color; + +out vec4 v_Color; + +void main() { + gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0); + v_Color = i_Color; +} diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs index cb7ab05579..849a72723d 100644 --- a/glow/src/triangle.rs +++ b/glow/src/triangle.rs @@ -1,74 +1,49 @@ //! Draw meshes of triangles. -mod gradient; -mod solid; - use crate::program; use crate::Transformation; +use iced_graphics::gradient::Gradient; use iced_graphics::layer::mesh::{self, Mesh}; -use iced_graphics::triangle::{self, Vertex2D}; +use iced_graphics::triangle::{ColoredVertex2D, Vertex2D}; use glow::HasContext; use std::marker::PhantomData; #[derive(Debug)] pub(crate) struct Pipeline { - vertex_array: ::VertexArray, - vertices: Buffer, indices: Buffer, - programs: ProgramList, -} - -#[derive(Debug)] -struct ProgramList { solid: solid::Program, gradient: gradient::Program, } impl Pipeline { pub fn new(gl: &glow::Context, shader_version: &program::Version) -> Self { - let vertex_array = - unsafe { gl.create_vertex_array().expect("Create vertex array") }; - - unsafe { - gl.bind_vertex_array(Some(vertex_array)); - } - - let vertices = unsafe { - Buffer::new( - gl, - glow::ARRAY_BUFFER, - glow::DYNAMIC_DRAW, - std::mem::size_of::() as usize, - ) - }; - - let indices = unsafe { + let mut indices = unsafe { Buffer::new( gl, glow::ELEMENT_ARRAY_BUFFER, glow::DYNAMIC_DRAW, - std::mem::size_of::() as usize, + 1000, ) }; + let solid = solid::Program::new(gl, shader_version); + let gradient = gradient::Program::new(gl, shader_version); + unsafe { - let stride = std::mem::size_of::() as i32; + gl.bind_vertex_array(Some(solid.vertex_array)); + indices.bind(gl, 0); - gl.enable_vertex_attrib_array(0); - gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0); + gl.bind_vertex_array(Some(gradient.vertex_array)); + indices.bind(gl, 0); gl.bind_vertex_array(None); - }; + } Self { - vertex_array, - vertices, indices, - programs: ProgramList { - solid: solid::Program::new(gl, shader_version), - gradient: gradient::Program::new(gl, shader_version), - }, + solid, + gradient, } } @@ -83,50 +58,83 @@ impl Pipeline { unsafe { gl.enable(glow::MULTISAMPLE); gl.enable(glow::SCISSOR_TEST); - gl.bind_vertex_array(Some(self.vertex_array)) } - //count the total amount of vertices & indices we need to handle - let (total_vertices, total_indices) = mesh::attribute_count_of(meshes); + // Count the total amount of vertices & indices we need to handle + let count = mesh::attribute_count_of(meshes); // Then we ensure the current attribute buffers are big enough, resizing if necessary unsafe { - self.vertices.bind(gl, total_vertices); - self.indices.bind(gl, total_indices); + self.indices.bind(gl, count.indices); } // We upload all the vertices and indices upfront - let mut vertex_offset = 0; + let mut solid_vertex_offset = 0; + let mut gradient_vertex_offset = 0; let mut index_offset = 0; for mesh in meshes { - unsafe { - gl.buffer_sub_data_u8_slice( - glow::ARRAY_BUFFER, - (vertex_offset * std::mem::size_of::()) as i32, - bytemuck::cast_slice(&mesh.buffers.vertices), - ); + let indices = mesh.indices(); + unsafe { gl.buffer_sub_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, (index_offset * std::mem::size_of::()) as i32, - bytemuck::cast_slice(&mesh.buffers.indices), + bytemuck::cast_slice(indices), ); - vertex_offset += mesh.buffers.vertices.len(); - index_offset += mesh.buffers.indices.len(); + index_offset += indices.len(); + } + + match mesh { + Mesh::Solid { buffers, .. } => { + unsafe { + self.solid.vertices.bind(gl, count.solid_vertices); + + gl.buffer_sub_data_u8_slice( + glow::ARRAY_BUFFER, + (solid_vertex_offset + * std::mem::size_of::()) + as i32, + bytemuck::cast_slice(&buffers.vertices), + ); + } + + solid_vertex_offset += buffers.vertices.len(); + } + Mesh::Gradient { buffers, .. } => { + unsafe { + self.gradient + .vertices + .bind(gl, count.gradient_vertices); + + gl.buffer_sub_data_u8_slice( + glow::ARRAY_BUFFER, + (gradient_vertex_offset + * std::mem::size_of::()) + as i32, + bytemuck::cast_slice(&buffers.vertices), + ); + } + + gradient_vertex_offset += buffers.vertices.len(); + } } } // Then we draw each mesh using offsets - let mut last_vertex = 0; + let mut last_solid_vertex = 0; + let mut last_gradient_vertex = 0; let mut last_index = 0; for mesh in meshes { - let transform = transformation - * Transformation::translate(mesh.origin.x, mesh.origin.y); + let indices = mesh.indices(); + let origin = mesh.origin(); - let clip_bounds = (mesh.clip_bounds * scale_factor).snap(); + let transform = + transformation * Transformation::translate(origin.x, origin.y); + + let clip_bounds = (mesh.clip_bounds() * scale_factor).snap(); unsafe { gl.scissor( @@ -136,30 +144,126 @@ impl Pipeline { clip_bounds.width as i32, clip_bounds.height as i32, ); + } + + match mesh { + Mesh::Solid { buffers, .. } => unsafe { + gl.use_program(Some(self.solid.program)); + gl.bind_vertex_array(Some(self.solid.vertex_array)); + + if transform != self.solid.uniforms.transform { + gl.uniform_matrix_4_f32_slice( + Some(&self.solid.uniforms.transform_location), + false, + transform.as_ref(), + ); - match mesh.style { - triangle::Style::Solid(color) => { - self.programs.solid.use_program(gl, color, &transform); + self.solid.uniforms.transform = transform; } - #[cfg(not(target_arch = "wasm32"))] - triangle::Style::Gradient(gradient) => { - self.programs - .gradient - .use_program(gl, gradient, &transform); + + gl.draw_elements_base_vertex( + glow::TRIANGLES, + indices.len() as i32, + glow::UNSIGNED_INT, + (last_index * std::mem::size_of::()) as i32, + last_solid_vertex as i32, + ); + + last_solid_vertex += buffers.vertices.len(); + }, + Mesh::Gradient { + buffers, gradient, .. + } => unsafe { + gl.use_program(Some(self.gradient.program)); + gl.bind_vertex_array(Some(self.gradient.vertex_array)); + + if transform != self.gradient.uniforms.transform { + gl.uniform_matrix_4_f32_slice( + Some(&self.gradient.uniforms.locations.transform), + false, + transform.as_ref(), + ); + + self.gradient.uniforms.transform = transform; } - } - gl.draw_elements_base_vertex( - glow::TRIANGLES, - mesh.buffers.indices.len() as i32, - glow::UNSIGNED_INT, - (last_index * std::mem::size_of::()) as i32, - last_vertex as i32, - ); + if &self.gradient.uniforms.gradient != *gradient { + match gradient { + Gradient::Linear(linear) => { + gl.uniform_4_f32( + Some( + &self + .gradient + .uniforms + .locations + .gradient_direction, + ), + linear.start.x, + linear.start.y, + linear.end.x, + linear.end.y, + ); + + gl.uniform_1_i32( + Some( + &self + .gradient + .uniforms + .locations + .color_stops_size, + ), + (linear.color_stops.len() * 2) as i32, + ); + + let mut stops = [0.0; 128]; + + for (index, stop) in linear + .color_stops + .iter() + .enumerate() + .take(16) + { + let [r, g, b, a] = stop.color.into_linear(); + + stops[index * 8] = r; + stops[(index * 8) + 1] = g; + stops[(index * 8) + 2] = b; + stops[(index * 8) + 3] = a; + stops[(index * 8) + 4] = stop.offset; + stops[(index * 8) + 5] = 0.; + stops[(index * 8) + 6] = 0.; + stops[(index * 8) + 7] = 0.; + } + + gl.uniform_4_f32_slice( + Some( + &self + .gradient + .uniforms + .locations + .color_stops, + ), + &stops, + ); + } + } + + self.gradient.uniforms.gradient = (*gradient).clone(); + } - last_vertex += mesh.buffers.vertices.len(); - last_index += mesh.buffers.indices.len(); + gl.draw_elements_base_vertex( + glow::TRIANGLES, + indices.len() as i32, + glow::UNSIGNED_INT, + (last_index * std::mem::size_of::()) as i32, + last_gradient_vertex as i32, + ); + + last_gradient_vertex += buffers.vertices.len(); + }, } + + last_index += indices.len(); } unsafe { @@ -170,47 +274,8 @@ impl Pipeline { } } -/// A simple shader program. Uses [`triangle.vert`] for its vertex shader and only binds position -/// attribute location. -pub(super) fn program( - gl: &glow::Context, - shader_version: &program::Version, - fragment_shader: &'static str, -) -> ::Program { - unsafe { - let vertex_shader = program::Shader::vertex( - gl, - shader_version, - include_str!("shader/common/triangle.vert"), - ); - - let fragment_shader = - program::Shader::fragment(gl, shader_version, fragment_shader); - - program::create( - gl, - &[vertex_shader, fragment_shader], - &[(0, "i_Position")], - ) - } -} - -pub fn set_transform( - gl: &glow::Context, - location: ::UniformLocation, - transform: Transformation, -) { - unsafe { - gl.uniform_matrix_4_f32_slice( - Some(&location), - false, - transform.as_ref(), - ); - } -} - #[derive(Debug)] -struct Buffer { +pub struct Buffer { raw: ::Buffer, target: u32, usage: u32, @@ -254,3 +319,268 @@ impl Buffer { } } } + +mod solid { + use crate::program; + use crate::triangle; + use glow::{Context, HasContext, NativeProgram}; + use iced_graphics::triangle::ColoredVertex2D; + use iced_graphics::Transformation; + + #[derive(Debug)] + pub struct Program { + pub program: ::Program, + pub vertex_array: ::VertexArray, + pub vertices: triangle::Buffer, + pub uniforms: Uniforms, + } + + impl Program { + pub fn new(gl: &Context, shader_version: &program::Version) -> Self { + let program = unsafe { + let vertex_shader = program::Shader::vertex( + gl, + shader_version, + include_str!("shader/common/solid.vert"), + ); + + let fragment_shader = program::Shader::fragment( + gl, + shader_version, + include_str!("shader/common/solid.frag"), + ); + + program::create( + gl, + &[vertex_shader, fragment_shader], + &[(0, "i_Position"), (1, "i_Color")], + ) + }; + + let vertex_array = unsafe { + gl.create_vertex_array().expect("Create vertex array") + }; + + let vertices = unsafe { + triangle::Buffer::new( + gl, + glow::ARRAY_BUFFER, + glow::DYNAMIC_DRAW, + 1000, + ) + }; + + unsafe { + gl.bind_vertex_array(Some(vertex_array)); + + let stride = std::mem::size_of::() as i32; + + gl.enable_vertex_attrib_array(0); + gl.vertex_attrib_pointer_f32( + 0, + 2, + glow::FLOAT, + false, + stride, + 0, + ); + + gl.enable_vertex_attrib_array(1); + gl.vertex_attrib_pointer_f32( + 1, + 4, + glow::FLOAT, + false, + stride, + 4 * 2, + ); + + gl.bind_vertex_array(None); + }; + + Self { + program, + vertex_array, + vertices, + uniforms: Uniforms::new(gl, program), + } + } + } + + #[derive(Debug)] + pub struct Uniforms { + pub transform: Transformation, + pub transform_location: ::UniformLocation, + } + + impl Uniforms { + fn new(gl: &Context, program: NativeProgram) -> Self { + let transform = Transformation::identity(); + let transform_location = + unsafe { gl.get_uniform_location(program, "u_Transform") } + .expect("Solid - Get u_Transform."); + + unsafe { + gl.use_program(Some(program)); + + gl.uniform_matrix_4_f32_slice( + Some(&transform_location), + false, + transform.as_ref(), + ); + + gl.use_program(None); + } + + Self { + transform, + transform_location, + } + } + } +} + +mod gradient { + use crate::program; + use crate::triangle; + use glow::{Context, HasContext, NativeProgram}; + use iced_graphics::gradient::{self, Gradient}; + use iced_graphics::triangle::Vertex2D; + use iced_graphics::Transformation; + + #[derive(Debug)] + pub struct Program { + pub program: ::Program, + pub vertex_array: ::VertexArray, + pub vertices: triangle::Buffer, + pub uniforms: Uniforms, + } + + impl Program { + pub fn new(gl: &Context, shader_version: &program::Version) -> Self { + let program = unsafe { + let vertex_shader = program::Shader::vertex( + gl, + shader_version, + include_str!("shader/common/gradient.vert"), + ); + + let fragment_shader = program::Shader::fragment( + gl, + shader_version, + include_str!("shader/common/gradient.frag"), + ); + + program::create( + gl, + &[vertex_shader, fragment_shader], + &[(0, "i_Position")], + ) + }; + + let vertex_array = unsafe { + gl.create_vertex_array().expect("Create vertex array") + }; + + let vertices = unsafe { + triangle::Buffer::new( + gl, + glow::ARRAY_BUFFER, + glow::DYNAMIC_DRAW, + 1000, + ) + }; + + unsafe { + gl.bind_vertex_array(Some(vertex_array)); + + let stride = std::mem::size_of::() as i32; + + gl.enable_vertex_attrib_array(0); + gl.vertex_attrib_pointer_f32( + 0, + 2, + glow::FLOAT, + false, + stride, + 0, + ); + + gl.bind_vertex_array(None); + }; + + Self { + program, + vertex_array, + vertices, + uniforms: Uniforms::new(gl, program), + } + } + } + + #[derive(Debug)] + pub struct Uniforms { + pub gradient: Gradient, + pub transform: Transformation, + pub locations: Locations, + } + + #[derive(Debug)] + pub struct Locations { + pub gradient_direction: ::UniformLocation, + pub color_stops_size: ::UniformLocation, + //currently the maximum number of stops is 16 due to lack of SSBO in GL2.1 + pub color_stops: ::UniformLocation, + pub transform: ::UniformLocation, + } + + impl Uniforms { + fn new(gl: &Context, program: NativeProgram) -> Self { + let gradient_direction = unsafe { + gl.get_uniform_location(program, "gradient_direction") + } + .expect("Gradient - Get gradient_direction."); + + let color_stops_size = + unsafe { gl.get_uniform_location(program, "color_stops_size") } + .expect("Gradient - Get color_stops_size."); + + let color_stops = unsafe { + gl.get_uniform_location(program, "color_stops") + .expect("Gradient - Get color_stops.") + }; + + let transform = Transformation::identity(); + let transform_location = + unsafe { gl.get_uniform_location(program, "u_Transform") } + .expect("Solid - Get u_Transform."); + + unsafe { + gl.use_program(Some(program)); + + gl.uniform_matrix_4_f32_slice( + Some(&transform_location), + false, + transform.as_ref(), + ); + + gl.use_program(None); + } + + Self { + gradient: Gradient::Linear(gradient::Linear { + start: Default::default(), + end: Default::default(), + color_stops: vec![], + }), + transform: Transformation::identity(), + locations: Locations { + gradient_direction, + color_stops_size, + color_stops, + transform: transform_location, + }, + } + } + } +} diff --git a/glow/src/triangle/gradient.rs b/glow/src/triangle/gradient.rs deleted file mode 100644 index d5f268776c..0000000000 --- a/glow/src/triangle/gradient.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate::program::Version; -use crate::triangle; -use glow::{Context, HasContext, NativeProgram}; -use iced_graphics::gradient::Gradient; -use iced_graphics::gradient::Linear; -use iced_graphics::Transformation; - -#[derive(Debug)] -pub struct Program { - pub program: ::Program, - pub uniform_data: UniformData, -} - -#[derive(Debug)] -pub struct UniformData { - gradient: Gradient, - transform: Transformation, - uniform_locations: UniformLocations, -} - -#[derive(Debug)] -struct UniformLocations { - gradient_direction_location: ::UniformLocation, - color_stops_size_location: ::UniformLocation, - //currently the maximum number of stops is 16 due to lack of SSBO in GL2.1 - color_stops_location: ::UniformLocation, - transform_location: ::UniformLocation, -} - -impl Program { - pub fn new(gl: &Context, shader_version: &Version) -> Self { - let program = triangle::program( - gl, - shader_version, - include_str!("../shader/common/gradient.frag"), - ); - - Self { - program, - uniform_data: UniformData::new(gl, program), - } - } - - pub fn write_uniforms( - &mut self, - gl: &Context, - gradient: &Gradient, - transform: &Transformation, - ) { - if transform != &self.uniform_data.transform { - triangle::set_transform( - gl, - self.uniform_data.uniform_locations.transform_location, - *transform, - ); - } - - if &self.uniform_data.gradient != gradient { - match gradient { - Gradient::Linear(linear) => unsafe { - gl.uniform_4_f32( - Some( - &self - .uniform_data - .uniform_locations - .gradient_direction_location, - ), - linear.start.x, - linear.start.y, - linear.end.x, - linear.end.y, - ); - - gl.uniform_1_i32( - Some( - &self - .uniform_data - .uniform_locations - .color_stops_size_location, - ), - (linear.color_stops.len() * 2) as i32, - ); - - let mut stops = [0.0; 128]; - - for (index, stop) in - linear.color_stops.iter().enumerate().take(16) - { - let [r, g, b, a] = stop.color.into_linear(); - - stops[index * 8] = r; - stops[(index * 8) + 1] = g; - stops[(index * 8) + 2] = b; - stops[(index * 8) + 3] = a; - stops[(index * 8) + 4] = stop.offset; - stops[(index * 8) + 5] = 0.; - stops[(index * 8) + 6] = 0.; - stops[(index * 8) + 7] = 0.; - } - - gl.uniform_4_f32_slice( - Some( - &self - .uniform_data - .uniform_locations - .color_stops_location, - ), - &stops, - ); - }, - } - - self.uniform_data.gradient = gradient.clone(); - } - } - - pub fn use_program( - &mut self, - gl: &Context, - gradient: &Gradient, - transform: &Transformation, - ) { - unsafe { gl.use_program(Some(self.program)) } - self.write_uniforms(gl, gradient, transform); - } -} - -impl UniformData { - fn new(gl: &Context, program: NativeProgram) -> Self { - let gradient_direction_location = - unsafe { gl.get_uniform_location(program, "gradient_direction") } - .expect("Gradient - Get gradient_direction."); - - let color_stops_size_location = - unsafe { gl.get_uniform_location(program, "color_stops_size") } - .expect("Gradient - Get color_stops_size."); - - let color_stops_location = unsafe { - gl.get_uniform_location(program, "color_stops") - .expect("Gradient - Get color_stops.") - }; - - let transform_location = - unsafe { gl.get_uniform_location(program, "u_Transform") } - .expect("Gradient - Get u_Transform."); - - Self { - gradient: Gradient::Linear(Linear { - start: Default::default(), - end: Default::default(), - color_stops: vec![], - }), - transform: Transformation::identity(), - uniform_locations: UniformLocations { - gradient_direction_location, - color_stops_size_location, - color_stops_location, - transform_location, - }, - } - } -} diff --git a/glow/src/triangle/solid.rs b/glow/src/triangle/solid.rs deleted file mode 100644 index fb3d40c35c..0000000000 --- a/glow/src/triangle/solid.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::program::Version; -use crate::{triangle, Color}; -use glow::{Context, HasContext, NativeProgram}; -use iced_graphics::Transformation; - -#[derive(Debug)] -pub struct Program { - program: ::Program, - uniform_data: UniformData, -} - -#[derive(Debug)] -struct UniformData { - pub color: Color, - pub color_location: ::UniformLocation, - pub transform: Transformation, - pub transform_location: ::UniformLocation, -} - -impl UniformData { - fn new(gl: &Context, program: NativeProgram) -> Self { - Self { - color: Color::TRANSPARENT, - color_location: unsafe { - gl.get_uniform_location(program, "color") - } - .expect("Solid - Get color."), - transform: Transformation::identity(), - transform_location: unsafe { - gl.get_uniform_location(program, "u_Transform") - } - .expect("Solid - Get u_Transform."), - } - } -} - -impl Program { - pub fn new(gl: &Context, shader_version: &Version) -> Self { - let program = triangle::program( - gl, - shader_version, - include_str!("../shader/common/triangle.frag"), - ); - - Self { - program, - uniform_data: UniformData::new(gl, program), - } - } - - pub fn write_uniforms( - &mut self, - gl: &Context, - color: &Color, - transform: &Transformation, - ) { - if transform != &self.uniform_data.transform { - triangle::set_transform( - gl, - self.uniform_data.transform_location, - *transform, - ) - } - - if color != &self.uniform_data.color { - let [r, g, b, a] = color.into_linear(); - - unsafe { - gl.uniform_4_f32( - Some(&self.uniform_data.color_location), - r, - g, - b, - a, - ); - } - - self.uniform_data.color = *color; - } - } - - pub fn use_program( - &mut self, - gl: &Context, - color: &Color, - transform: &Transformation, - ) { - unsafe { gl.use_program(Some(self.program)) } - self.write_uniforms(gl, color, transform) - } -} diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index e95934b032..fd670f4833 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -166,10 +166,27 @@ impl<'a> Layer<'a> { border_color: border_color.into_linear(), }); } - Primitive::Mesh2D { + Primitive::SolidMesh { buffers, size } => { + let layer = &mut layers[current_layer]; + + let bounds = Rectangle::new( + Point::new(translation.x, translation.y), + *size, + ); + + // Only draw visible content + if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { + layer.meshes.push(Mesh::Solid { + origin: Point::new(translation.x, translation.y), + buffers, + clip_bounds, + }); + } + } + Primitive::GradientMesh { buffers, size, - style, + gradient, } => { let layer = &mut layers[current_layer]; @@ -180,11 +197,11 @@ impl<'a> Layer<'a> { // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { - layer.meshes.push(Mesh { + layer.meshes.push(Mesh::Gradient { origin: Point::new(translation.x, translation.y), buffers, clip_bounds, - style, + gradient, }); } } diff --git a/graphics/src/layer/mesh.rs b/graphics/src/layer/mesh.rs index 979081f1b9..7661c5c9c3 100644 --- a/graphics/src/layer/mesh.rs +++ b/graphics/src/layer/mesh.rs @@ -1,31 +1,93 @@ //! A collection of triangle primitives. use crate::triangle; -use crate::{Point, Rectangle}; +use crate::{Gradient, Point, Rectangle}; /// A mesh of triangles. #[derive(Debug, Clone, Copy)] -pub struct Mesh<'a> { - /// The origin of the vertices of the [`Mesh`]. - pub origin: Point, +pub enum Mesh<'a> { + /// A mesh of triangles with a solid color. + Solid { + /// The origin of the vertices of the [`Mesh`]. + origin: Point, - /// The vertex and index buffers of the [`Mesh`]. - pub buffers: &'a triangle::Mesh2D, + /// The vertex and index buffers of the [`Mesh`]. + buffers: &'a triangle::Mesh2D, - /// The clipping bounds of the [`Mesh`]. - pub clip_bounds: Rectangle, + /// The clipping bounds of the [`Mesh`]. + clip_bounds: Rectangle, + }, + /// A mesh of triangles with a gradient color. + Gradient { + /// The origin of the vertices of the [`Mesh`]. + origin: Point, - /// The shader of the [`Mesh`]. - pub style: &'a triangle::Style, + /// The vertex and index buffers of the [`Mesh`]. + buffers: &'a triangle::Mesh2D, + + /// The clipping bounds of the [`Mesh`]. + clip_bounds: Rectangle, + + /// The gradient to apply to the [`Mesh`]. + gradient: &'a Gradient, + }, +} + +impl Mesh<'_> { + /// Returns the origin of the [`Mesh`]. + pub fn origin(&self) -> Point { + match self { + Self::Solid { origin, .. } | Self::Gradient { origin, .. } => { + *origin + } + } + } + + /// Returns the indices of the [`Mesh`]. + pub fn indices(&self) -> &[u32] { + match self { + Self::Solid { buffers, .. } => &buffers.indices, + Self::Gradient { buffers, .. } => &buffers.indices, + } + } + + /// Returns the clip bounds of the [`Mesh`]. + pub fn clip_bounds(&self) -> Rectangle { + match self { + Self::Solid { clip_bounds, .. } + | Self::Gradient { clip_bounds, .. } => *clip_bounds, + } + } +} + +/// The result of counting the attributes of a set of meshes. +#[derive(Debug, Clone, Copy, Default)] +pub struct AttributeCount { + /// The total amount of solid vertices. + pub solid_vertices: usize, + + /// The total amount of gradient vertices. + pub gradient_vertices: usize, + + /// The total amount of indices. + pub indices: usize, } /// Returns the number of total vertices & total indices of all [`Mesh`]es. -pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> (usize, usize) { +pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> AttributeCount { meshes .iter() - .map(|Mesh { buffers, .. }| { - (buffers.vertices.len(), buffers.indices.len()) - }) - .fold((0, 0), |(total_v, total_i), (v, i)| { - (total_v + v, total_i + i) + .fold(AttributeCount::default(), |mut count, mesh| { + match mesh { + Mesh::Solid { buffers, .. } => { + count.solid_vertices += buffers.vertices.len(); + count.indices += buffers.indices.len(); + } + Mesh::Gradient { buffers, .. } => { + count.gradient_vertices += buffers.vertices.len(); + count.indices += buffers.indices.len(); + } + } + + count }) } diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index b481ac0baa..9759d97ad4 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -3,6 +3,7 @@ use iced_native::svg; use iced_native::{Background, Color, Font, Rectangle, Size, Vector}; use crate::alignment; +use crate::gradient::Gradient; use crate::triangle; use std::sync::Arc; @@ -77,20 +78,32 @@ pub enum Primitive { /// The primitive to translate content: Box, }, - /// A low-level primitive to render a mesh of triangles. + /// A low-level primitive to render a mesh of triangles with a solid color. /// /// It can be used to render many kinds of geometry freely. - Mesh2D { - /// The vertex and index buffers of the mesh - buffers: triangle::Mesh2D, + SolidMesh { + /// The vertices and indices of the mesh. + buffers: triangle::Mesh2D, + + /// The size of the drawable region of the mesh. + /// + /// Any geometry that falls out of this region will be clipped. + size: Size, + }, + /// A low-level primitive to render a mesh of triangles with a gradient. + /// + /// It can be used to render many kinds of geometry freely. + GradientMesh { + /// The vertices and indices of the mesh. + buffers: triangle::Mesh2D, /// The size of the drawable region of the mesh. /// /// Any geometry that falls out of this region will be clipped. size: Size, - /// The shader of the mesh - style: triangle::Style, + /// The [`Gradient`] to apply to the mesh. + gradient: Gradient, }, /// A cached primitive. /// diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index 8b41bfc4af..f52b2339b1 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -1,15 +1,12 @@ //! Draw geometry using meshes of triangles. -use crate::Color; -#[cfg(not(target_arch = "wasm32"))] -use crate::Gradient; - use bytemuck::{Pod, Zeroable}; /// A set of [`Vertex2D`] and indices representing a list of triangles. #[derive(Clone, Debug)] -pub struct Mesh2D { +pub struct Mesh2D { /// The vertices of the mesh - pub vertices: Vec, + pub vertices: Vec, + /// The list of vertex indices that defines the triangles of the mesh. /// /// Therefore, this list should always have a length that is a multiple of 3. @@ -24,25 +21,13 @@ pub struct Vertex2D { pub position: [f32; 2], } -#[derive(Debug, Clone, PartialEq)] -/// Supported shaders for triangle primitives. -pub enum Style { - /// Fill a primitive with a solid color. - Solid(Color), - #[cfg(not(target_arch = "wasm32"))] - /// Fill a primitive with an interpolated color. - Gradient(Gradient), -} - -impl From for Style { - fn from(color: Color) -> Self { - Self::Solid(color) - } -} +/// A two-dimensional vertex with a color. +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +#[repr(C)] +pub struct ColoredVertex2D { + /// The vertex position in 2D space. + pub position: [f32; 2], -#[cfg(not(target_arch = "wasm32"))] -impl From for Style { - fn from(gradient: Gradient) -> Self { - Self::Gradient(gradient) - } + /// The color of the vertex in __linear__ RGBA. + pub color: [f32; 4], } diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index a14940d96b..b070d0a676 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -13,6 +13,7 @@ mod cursor; mod frame; mod geometry; mod program; +mod style; mod text; pub use crate::gradient::{self, Gradient}; @@ -25,6 +26,7 @@ pub use geometry::Geometry; pub use path::Path; pub use program::Program; pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; +pub use style::Style; pub use text::Text; use crate::{Backend, Primitive, Renderer}; diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index e2fc1cfe44..e954ebb52b 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,7 +1,7 @@ //! Fill [crate::widget::canvas::Geometry] with a certain style. use crate::{Color, Gradient}; -pub use crate::triangle::Style; +pub use crate::widget::canvas::Style; /// The style used to fill geometry. #[derive(Debug, Clone)] diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index a7b8850252..d68548ae8d 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,7 +1,6 @@ use crate::gradient::Gradient; use crate::triangle; -use crate::triangle::Vertex2D; -use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; +use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text}; use crate::Primitive; use iced_native::{Point, Rectangle, Size, Vector}; @@ -23,8 +22,16 @@ pub struct Frame { stroke_tessellator: tessellation::StrokeTessellator, } +enum Buffer { + Solid(tessellation::VertexBuffers), + Gradient( + tessellation::VertexBuffers, + Gradient, + ), +} + struct BufferStack { - stack: Vec<(tessellation::VertexBuffers, triangle::Style)>, + stack: Vec, } impl BufferStack { @@ -32,22 +39,64 @@ impl BufferStack { Self { stack: Vec::new() } } - fn get( - &mut self, - mesh_style: triangle::Style, - ) -> tessellation::BuffersBuilder<'_, Vertex2D, u32, Vertex2DBuilder> { - match self.stack.last_mut() { - Some((_, current_style)) if current_style == &mesh_style => {} - _ => { - self.stack - .push((tessellation::VertexBuffers::new(), mesh_style)); + fn get_mut(&mut self, style: &Style) -> &mut Buffer { + match style { + Style::Solid(_) => match self.stack.last() { + Some(Buffer::Solid(_)) => {} + _ => { + self.stack.push(Buffer::Solid( + tessellation::VertexBuffers::new(), + )); + } + }, + Style::Gradient(gradient) => match self.stack.last() { + Some(Buffer::Gradient(_, last)) if gradient == last => {} + _ => { + self.stack.push(Buffer::Gradient( + tessellation::VertexBuffers::new(), + gradient.clone(), + )); + } + }, + } + + self.stack.last_mut().unwrap() + } + + fn get_fill<'a>( + &'a mut self, + style: &Style, + ) -> Box { + match (style, self.get_mut(style)) { + (Style::Solid(color), Buffer::Solid(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + TriangleVertex2DBuilder(color.into_linear()), + )) } - }; + (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( + tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), + ), + _ => unreachable!(), + } + } - tessellation::BuffersBuilder::new( - &mut self.stack.last_mut().unwrap().0, - Vertex2DBuilder, - ) + fn get_stroke<'a>( + &'a mut self, + style: &Style, + ) -> Box { + match (style, self.get_mut(style)) { + (Style::Solid(color), Buffer::Solid(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + TriangleVertex2DBuilder(color.into_linear()), + )) + } + (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( + tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), + ), + _ => unreachable!(), + } } } @@ -73,12 +122,11 @@ impl Transform { point.y = transformed.y; } - fn transform_style(&self, style: triangle::Style) -> triangle::Style { + fn transform_style(&self, style: Style) -> Style { match style { - triangle::Style::Solid(color) => triangle::Style::Solid(color), - #[cfg(not(target_arch = "wasm32"))] - triangle::Style::Gradient(gradient) => { - triangle::Style::Gradient(self.transform_gradient(gradient)) + Style::Solid(color) => Style::Solid(color), + Style::Gradient(gradient) => { + Style::Gradient(self.transform_gradient(gradient)) } } } @@ -146,7 +194,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(style)); + .get_fill(&self.transforms.current.transform_style(style)); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); @@ -155,7 +203,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -163,7 +211,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } .expect("Tessellate path."); @@ -181,7 +229,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(style)); + .get_fill(&self.transforms.current.transform_style(style)); let top_left = self.transforms.current.raw.transform_point( @@ -200,7 +248,7 @@ impl Frame { .tessellate_rectangle( &lyon::math::Box2D::new(top_left, top_left + size), &options, - &mut buffer, + buffer.as_mut(), ) .expect("Fill rectangle"); } @@ -212,7 +260,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(stroke.style)); + .get_stroke(&self.transforms.current.transform_style(stroke.style)); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -230,7 +278,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -238,7 +286,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } .expect("Stroke path"); @@ -383,16 +431,31 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - for (buffer, style) in self.buffers.stack { - if !buffer.indices.is_empty() { - self.primitives.push(Primitive::Mesh2D { - buffers: triangle::Mesh2D { - vertices: buffer.vertices, - indices: buffer.indices, - }, - size: self.size, - style, - }) + for buffer in self.buffers.stack { + match buffer { + Buffer::Solid(buffer) => { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::SolidMesh { + buffers: triangle::Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }, + size: self.size, + }) + } + } + Buffer::Gradient(buffer, gradient) => { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::GradientMesh { + buffers: triangle::Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }, + size: self.size, + gradient, + }) + } + } } } @@ -402,25 +465,66 @@ impl Frame { struct Vertex2DBuilder; -impl tessellation::FillVertexConstructor for Vertex2DBuilder { - fn new_vertex(&mut self, vertex: tessellation::FillVertex<'_>) -> Vertex2D { +impl tessellation::FillVertexConstructor + for Vertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> triangle::Vertex2D { + let position = vertex.position(); + + triangle::Vertex2D { + position: [position.x, position.y], + } + } +} + +impl tessellation::StrokeVertexConstructor + for Vertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::StrokeVertex<'_, '_>, + ) -> triangle::Vertex2D { + let position = vertex.position(); + + triangle::Vertex2D { + position: [position.x, position.y], + } + } +} + +struct TriangleVertex2DBuilder([f32; 4]); + +impl tessellation::FillVertexConstructor + for TriangleVertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> triangle::ColoredVertex2D { let position = vertex.position(); - Vertex2D { + triangle::ColoredVertex2D { position: [position.x, position.y], + color: self.0, } } } -impl tessellation::StrokeVertexConstructor for Vertex2DBuilder { +impl tessellation::StrokeVertexConstructor + for TriangleVertex2DBuilder +{ fn new_vertex( &mut self, vertex: tessellation::StrokeVertex<'_, '_>, - ) -> Vertex2D { + ) -> triangle::ColoredVertex2D { let position = vertex.position(); - Vertex2D { + triangle::ColoredVertex2D { position: [position.x, position.y], + color: self.0, } } } diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index a882531a45..4c19251d79 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,5 +1,5 @@ //! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles. -pub use crate::triangle::Style; +pub use crate::widget::canvas::Style; use iced_native::Color; diff --git a/graphics/src/widget/canvas/style.rs b/graphics/src/widget/canvas/style.rs new file mode 100644 index 0000000000..794109bd8b --- /dev/null +++ b/graphics/src/widget/canvas/style.rs @@ -0,0 +1,11 @@ +use crate::{Color, Gradient}; + +/// The coloring style of some drawing. +#[derive(Debug, Clone, PartialEq)] +pub enum Style { + /// A solid [`Color`]. + Solid(Color), + + /// A [`Gradient`] color. + Gradient(Gradient), +} diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs index 2a675d8124..18be03ddeb 100644 --- a/wgpu/src/buffer/dynamic.rs +++ b/wgpu/src/buffer/dynamic.rs @@ -1,10 +1,13 @@ //! Utilities for uniform buffer operations. use encase::private::WriteInto; use encase::ShaderType; + +use std::fmt; use std::marker::PhantomData; /// A dynamic buffer is any type of buffer which does not have a static offset. -pub(crate) struct Buffer { +#[derive(Debug)] +pub struct Buffer { offsets: Vec, cpu: Internal, gpu: wgpu::Buffer, @@ -204,3 +207,13 @@ impl Internal { } } } + +impl fmt::Debug for Internal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Uniform(_) => write!(f, "Internal::Uniform(_)"), + #[cfg(not(target_arch = "wasm32"))] + Self::Storage(_) => write!(f, "Internal::Storage(_)"), + } + } +} diff --git a/wgpu/src/buffer/static.rs b/wgpu/src/buffer/static.rs index cf06790c3c..ef87422f41 100644 --- a/wgpu/src/buffer/static.rs +++ b/wgpu/src/buffer/static.rs @@ -8,7 +8,7 @@ const DEFAULT_STATIC_BUFFER_COUNT: wgpu::BufferAddress = 128; /// A generic buffer struct useful for items which have no alignment requirements /// (e.g. Vertex, Index buffers) & no dynamic offsets. #[derive(Debug)] -pub(crate) struct Buffer { +pub struct Buffer { //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer offsets: Vec, label: &'static str, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index f4436e9d38..74152945b1 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -39,13 +39,13 @@ #![cfg_attr(docsrs, feature(doc_cfg))] pub mod settings; -pub mod triangle; pub mod window; mod backend; mod buffer; mod quad; mod text; +mod triangle; pub use iced_graphics::{Antialiasing, Color, Error, Primitive, Viewport}; pub use iced_native::Theme; diff --git a/wgpu/src/shader/solid.wgsl b/wgpu/src/shader/solid.wgsl index 68a8fea3b8..b24402f832 100644 --- a/wgpu/src/shader/solid.wgsl +++ b/wgpu/src/shader/solid.wgsl @@ -1,17 +1,30 @@ -struct Uniforms { +struct Globals { transform: mat4x4, - color: vec4 } -@group(0) @binding(0) -var uniforms: Uniforms; +@group(0) @binding(0) var globals: Globals; + +struct VertexInput { + @location(0) position: vec2, + @location(1) color: vec4, +} + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) color: vec4, +} @vertex -fn vs_main(@location(0) input: vec2) -> @builtin(position) vec4 { - return uniforms.transform * vec4(input.xy, 0.0, 1.0); +fn vs_main(input: VertexInput) -> VertexOutput { + var out: VertexOutput; + + out.color = input.color; + out.position = globals.transform * vec4(input.position, 0.0, 1.0); + + return out; } @fragment -fn fs_main() -> @location(0) vec4 { - return uniforms.color; +fn fs_main(input: VertexOutput) -> @location(0) vec4 { + return input.color; } diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl deleted file mode 100644 index b24402f832..0000000000 --- a/wgpu/src/shader/triangle.wgsl +++ /dev/null @@ -1,30 +0,0 @@ -struct Globals { - transform: mat4x4, -} - -@group(0) @binding(0) var globals: Globals; - -struct VertexInput { - @location(0) position: vec2, - @location(1) color: vec4, -} - -struct VertexOutput { - @builtin(position) position: vec4, - @location(0) color: vec4, -} - -@vertex -fn vs_main(input: VertexInput) -> VertexOutput { - var out: VertexOutput; - - out.color = input.color; - out.position = globals.transform * vec4(input.position, 0.0, 1.0); - - return out; -} - -@fragment -fn fs_main(input: VertexOutput) -> @location(0) vec4 { - return input.color; -} diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index c51b53396f..b33b488a44 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,71 +1,27 @@ //! Draw meshes of triangles. -#[cfg(not(target_arch = "wasm32"))] -mod gradient; mod msaa; -mod solid; use crate::buffer::r#static::Buffer; use crate::settings; use crate::Transformation; use iced_graphics::layer::mesh::{self, Mesh}; -use iced_graphics::triangle::{self, Vertex2D}; +use iced_graphics::triangle::ColoredVertex2D; use iced_graphics::Size; -use core::fmt; -use std::fmt::Formatter; - -/// Triangle pipeline for all mesh layers in a [`iced_graphics::Canvas`] widget. #[derive(Debug)] -pub(crate) struct Pipeline { +pub struct Pipeline { blit: Option, - vertex_buffer: Buffer, index_buffer: Buffer, index_strides: Vec, - pipelines: PipelineList, -} - -/// Supported triangle pipelines for different fills. -pub(crate) struct PipelineList { solid: solid::Pipeline, + /// Gradients are currently not supported on WASM targets due to their need of storage buffers. #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Pipeline, } -impl fmt::Debug for PipelineList { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("TrianglePipelines").finish() - } -} - -impl PipelineList { - /// Resets each pipeline's buffers. - fn clear(&mut self) { - self.solid.buffer.clear(); - #[cfg(not(target_arch = "wasm32"))] - { - self.gradient.uniform_buffer.clear(); - self.gradient.storage_buffer.clear(); - } - } - - /// Writes the contents of each pipeline's CPU buffer to the GPU, resizing the GPU buffer - /// beforehand if necessary. - fn write( - &mut self, - device: &wgpu::Device, - staging_belt: &mut wgpu::util::StagingBelt, - encoder: &mut wgpu::CommandEncoder, - ) { - self.solid.write(device, staging_belt, encoder); - #[cfg(not(target_arch = "wasm32"))] - self.gradient.write(device, staging_belt, encoder); - } -} - impl Pipeline { - /// Creates supported pipelines, listed in [TrianglePipelines]. pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, @@ -73,26 +29,19 @@ impl Pipeline { ) -> Pipeline { Pipeline { blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), - vertex_buffer: Buffer::new( - device, - "iced_wgpu::triangle vertex buffer", - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - ), index_buffer: Buffer::new( device, "iced_wgpu::triangle vertex buffer", wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, ), index_strides: Vec::new(), - pipelines: PipelineList { - solid: solid::Pipeline::new(device, format, antialiasing), - #[cfg(not(target_arch = "wasm32"))] - gradient: gradient::Pipeline::new(device, format, antialiasing), - }, + solid: solid::Pipeline::new(device, format, antialiasing), + + #[cfg(not(target_arch = "wasm32"))] + gradient: gradient::Pipeline::new(device, format, antialiasing), } } - /// Draws the contents of the current layer's meshes to the [target]. pub fn draw( &mut self, device: &wgpu::Device, @@ -104,68 +53,185 @@ impl Pipeline { scale_factor: f32, meshes: &[Mesh<'_>], ) { - //count the total amount of vertices & indices we need to handle - let (total_vertices, total_indices) = mesh::attribute_count_of(meshes); + // Count the total amount of vertices & indices we need to handle + let count = mesh::attribute_count_of(meshes); // Then we ensure the current attribute buffers are big enough, resizing if necessary. + // We are not currently using the return value of these functions as we have no system in + // place to calculate mesh diff, or to know whether or not that would be more performant for + // the majority of use cases. Therefore we will write GPU data every frame (for now). + let _ = self.index_buffer.resize(device, count.indices); + let _ = self.solid.vertices.resize(device, count.solid_vertices); - //We are not currently using the return value of these functions as we have no system in - //place to calculate mesh diff, or to know whether or not that would be more performant for - //the majority of use cases. Therefore we will write GPU data every frame (for now). - let _ = self.vertex_buffer.resize(device, total_vertices); - let _ = self.index_buffer.resize(device, total_indices); + #[cfg(not(target_arch = "wasm32"))] + let _ = self + .gradient + .vertices + .resize(device, count.gradient_vertices); - //prepare dynamic buffers & data store for writing + // Prepare dynamic buffers & data store for writing self.index_strides.clear(); - self.pipelines.clear(); + self.solid.vertices.clear(); + self.solid.uniforms.clear(); + + #[cfg(not(target_arch = "wasm32"))] + { + self.gradient.uniforms.clear(); + self.gradient.vertices.clear(); + self.gradient.storage.clear(); + } - let mut vertex_offset = 0; + let mut solid_vertex_offset = 0; let mut index_offset = 0; + #[cfg(not(target_arch = "wasm32"))] + let mut gradient_vertex_offset = 0; + for mesh in meshes { - let transform = transformation - * Transformation::translate(mesh.origin.x, mesh.origin.y); + let origin = mesh.origin(); + let indices = mesh.indices(); - //write to both buffers - let new_vertex_offset = self.vertex_buffer.write( - device, - staging_belt, - encoder, - vertex_offset, - &mesh.buffers.vertices, - ); + let transform = + transformation * Transformation::translate(origin.x, origin.y); let new_index_offset = self.index_buffer.write( device, staging_belt, encoder, index_offset, - &mesh.buffers.indices, + indices, ); - vertex_offset += new_vertex_offset; index_offset += new_index_offset; - - self.index_strides.push(mesh.buffers.indices.len() as u32); + self.index_strides.push(indices.len() as u32); //push uniform data to CPU buffers - match mesh.style { - triangle::Style::Solid(color) => { - self.pipelines.solid.push(transform, color); + match mesh { + Mesh::Solid { buffers, .. } => { + self.solid.uniforms.push(&solid::Uniforms::new(transform)); + + let written_bytes = self.solid.vertices.write( + device, + staging_belt, + encoder, + solid_vertex_offset, + &buffers.vertices, + ); + + solid_vertex_offset += written_bytes; } #[cfg(not(target_arch = "wasm32"))] - triangle::Style::Gradient(gradient) => { - self.pipelines.gradient.push(transform, gradient); + Mesh::Gradient { + buffers, gradient, .. + } => { + let written_bytes = self.gradient.vertices.write( + device, + staging_belt, + encoder, + gradient_vertex_offset, + &buffers.vertices, + ); + + gradient_vertex_offset += written_bytes; + + match gradient { + iced_graphics::Gradient::Linear(linear) => { + use glam::{IVec4, Vec4}; + + let start_offset = self.gradient.color_stop_offset; + let end_offset = (linear.color_stops.len() as i32) + + start_offset + - 1; + + self.gradient.uniforms.push(&gradient::Uniforms { + transform: transform.into(), + direction: Vec4::new( + linear.start.x, + linear.start.y, + linear.end.x, + linear.end.y, + ), + stop_range: IVec4::new( + start_offset, + end_offset, + 0, + 0, + ), + }); + + self.gradient.color_stop_offset = end_offset + 1; + + let stops: Vec = linear + .color_stops + .iter() + .map(|stop| { + let [r, g, b, a] = stop.color.into_linear(); + + gradient::ColorStop { + offset: stop.offset, + color: Vec4::new(r, g, b, a), + } + }) + .collect(); + + self.gradient + .color_stops_pending_write + .color_stops + .extend(stops); + } + } } + #[cfg(target_arch = "wasm32")] + Mesh::Gradient { .. } => {} + } + } + + // Write uniform data to GPU + if count.solid_vertices > 0 { + let uniforms_resized = self.solid.uniforms.resize(device); + + if uniforms_resized { + self.solid.bind_group = solid::Pipeline::bind_group( + device, + self.solid.uniforms.raw(), + &self.solid.bind_group_layout, + ) } + + self.solid.uniforms.write(device, staging_belt, encoder); } - //write uniform data to GPU - self.pipelines.write(device, staging_belt, encoder); + #[cfg(not(target_arch = "wasm32"))] + if count.gradient_vertices > 0 { + // First write the pending color stops to the CPU buffer + self.gradient + .storage + .push(&self.gradient.color_stops_pending_write); + + // Resize buffers if needed + let uniforms_resized = self.gradient.uniforms.resize(device); + let storage_resized = self.gradient.storage.resize(device); + + if uniforms_resized || storage_resized { + self.gradient.bind_group = gradient::Pipeline::bind_group( + device, + self.gradient.uniforms.raw(), + self.gradient.storage.raw(), + &self.gradient.bind_group_layout, + ); + } + + // Write to GPU + self.gradient.uniforms.write(device, staging_belt, encoder); + self.gradient.storage.write(device, staging_belt, encoder); - //configure the render pass now that the data is uploaded to the GPU + // Cleanup + self.gradient.color_stop_offset = 0; + self.gradient.color_stops_pending_write.color_stops.clear(); + } + + // Configure render pass { - //configure antialiasing pass let (attachment, resolve_target, load) = if let Some(blit) = &mut self.blit { @@ -200,7 +266,7 @@ impl Pipeline { let mut last_is_solid = None; for (index, mesh) in meshes.iter().enumerate() { - let clip_bounds = (mesh.clip_bounds * scale_factor).snap(); + let clip_bounds = (mesh.clip_bounds() * scale_factor).snap(); render_pass.set_scissor_rect( clip_bounds.x, @@ -209,47 +275,57 @@ impl Pipeline { clip_bounds.height, ); - match mesh.style { - triangle::Style::Solid(_) => { + match mesh { + Mesh::Solid { .. } => { if !last_is_solid.unwrap_or(false) { - self.pipelines - .solid - .set_render_pass_pipeline(&mut render_pass); + render_pass.set_pipeline(&self.solid.pipeline); last_is_solid = Some(true); } - self.pipelines.solid.configure_render_pass( - &mut render_pass, - num_solids, + render_pass.set_bind_group( + 0, + &self.solid.bind_group, + &[self.solid.uniforms.offset_at_index(num_solids)], + ); + + render_pass.set_vertex_buffer( + 0, + self.solid.vertices.slice_from_index(num_solids), ); num_solids += 1; } #[cfg(not(target_arch = "wasm32"))] - triangle::Style::Gradient(_) => { + Mesh::Gradient { .. } => { if last_is_solid.unwrap_or(true) { - self.pipelines - .gradient - .set_render_pass_pipeline(&mut render_pass); + render_pass.set_pipeline(&self.gradient.pipeline); last_is_solid = Some(false); } - self.pipelines.gradient.configure_render_pass( - &mut render_pass, - num_gradients, + render_pass.set_bind_group( + 0, + &self.gradient.bind_group, + &[self + .gradient + .uniforms + .offset_at_index(num_gradients)], + ); + + render_pass.set_vertex_buffer( + 0, + self.gradient + .vertices + .slice_from_index(num_gradients), ); num_gradients += 1; } + #[cfg(target_arch = "wasm32")] + Mesh::Gradient { .. } => {} }; - render_pass.set_vertex_buffer( - 0, - self.vertex_buffer.slice_from_index(index), - ); - render_pass.set_index_buffer( self.index_buffer.slice_from_index(index), wgpu::IndexFormat::Uint32, @@ -263,7 +339,6 @@ impl Pipeline { } } - self.vertex_buffer.clear(); self.index_buffer.clear(); if let Some(blit) = &mut self.blit { @@ -272,19 +347,6 @@ impl Pipeline { } } -//utility functions for individual pipelines with shared functionality -fn vertex_buffer_layout<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }], - } -} - fn fragment_target( texture_format: wgpu::TextureFormat, ) -> Option { @@ -312,3 +374,360 @@ fn multisample_state( alpha_to_coverage_enabled: false, } } + +mod solid { + use crate::buffer::dynamic; + use crate::buffer::r#static::Buffer; + use crate::settings; + use crate::triangle; + use encase::ShaderType; + use iced_graphics::Transformation; + + #[derive(Debug)] + pub struct Pipeline { + pub pipeline: wgpu::RenderPipeline, + pub vertices: Buffer, + pub uniforms: dynamic::Buffer, + pub bind_group_layout: wgpu::BindGroupLayout, + pub bind_group: wgpu::BindGroup, + } + + #[derive(Debug, Clone, Copy, ShaderType)] + pub struct Uniforms { + transform: glam::Mat4, + } + + impl Uniforms { + pub fn new(transform: Transformation) -> Self { + Self { + transform: transform.into(), + } + } + } + + impl Pipeline { + /// Creates a new [SolidPipeline] using `solid.wgsl` shader. + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + antialiasing: Option, + ) -> Self { + let vertices = Buffer::new( + device, + "iced_wgpu::triangle::solid vertex buffer", + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + let uniforms = dynamic::Buffer::uniform( + device, + "iced_wgpu::triangle::solid uniforms", + ); + + let bind_group_layout = device.create_bind_group_layout( + &wgpu::BindGroupLayoutDescriptor { + label: Some("iced_wgpu::triangle::solid bind group layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(Uniforms::min_size()), + }, + count: None, + }], + }, + ); + + let bind_group = + Self::bind_group(device, uniforms.raw(), &bind_group_layout); + + let layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("iced_wgpu::triangle::solid pipeline layout"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }, + ); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some( + "iced_wgpu::triangle::solid create shader module", + ), + source: wgpu::ShaderSource::Wgsl( + std::borrow::Cow::Borrowed(include_str!( + "shader/solid.wgsl" + )), + ), + }); + + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::triangle::solid pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::< + triangle::ColoredVertex2D, + >() + as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array!( + // Position + 0 => Float32x2, + // Color + 1 => Float32x4, + ), + }], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[triangle::fragment_target(format)], + }), + primitive: triangle::primitive_state(), + depth_stencil: None, + multisample: triangle::multisample_state(antialiasing), + multiview: None, + }, + ); + + Self { + pipeline, + vertices, + uniforms, + bind_group_layout, + bind_group, + } + } + + pub fn bind_group( + device: &wgpu::Device, + buffer: &wgpu::Buffer, + layout: &wgpu::BindGroupLayout, + ) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("iced_wgpu::triangle::solid bind group"), + layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + wgpu::BufferBinding { + buffer, + offset: 0, + size: Some(Uniforms::min_size()), + }, + ), + }], + }) + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +mod gradient { + use crate::buffer::dynamic; + use crate::buffer::r#static::Buffer; + use crate::settings; + use crate::triangle; + + use encase::ShaderType; + use glam::{IVec4, Vec4}; + use iced_graphics::triangle::Vertex2D; + + #[derive(Debug)] + pub struct Pipeline { + pub pipeline: wgpu::RenderPipeline, + pub vertices: Buffer, + pub uniforms: dynamic::Buffer, + pub storage: dynamic::Buffer, + pub color_stop_offset: i32, + //Need to store these and then write them all at once + //or else they will be padded to 256 and cause gaps in the storage buffer + pub color_stops_pending_write: Storage, + pub bind_group_layout: wgpu::BindGroupLayout, + pub bind_group: wgpu::BindGroup, + } + + #[derive(Debug, ShaderType)] + pub struct Uniforms { + pub transform: glam::Mat4, + //xy = start, zw = end + pub direction: Vec4, + //x = start stop, y = end stop, zw = padding + pub stop_range: IVec4, + } + + #[derive(Debug, ShaderType)] + pub struct ColorStop { + pub color: Vec4, + pub offset: f32, + } + + #[derive(Debug, ShaderType)] + pub struct Storage { + #[size(runtime)] + pub color_stops: Vec, + } + + impl Pipeline { + /// Creates a new [GradientPipeline] using `gradient.wgsl` shader. + pub(super) fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + antialiasing: Option, + ) -> Self { + let vertices = Buffer::new( + device, + "iced_wgpu::triangle::gradient vertex buffer", + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + let uniforms = dynamic::Buffer::uniform( + device, + "iced_wgpu::triangle::gradient uniforms", + ); + + //Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static + // sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work + let storage = dynamic::Buffer::storage( + device, + "iced_wgpu::triangle::gradient storage", + ); + + let bind_group_layout = device.create_bind_group_layout( + &wgpu::BindGroupLayoutDescriptor { + label: Some( + "iced_wgpu::triangle::gradient bind group layout", + ), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(Uniforms::min_size()), + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { + read_only: true, + }, + has_dynamic_offset: false, + min_binding_size: Some(Storage::min_size()), + }, + count: None, + }, + ], + }, + ); + + let bind_group = Pipeline::bind_group( + device, + uniforms.raw(), + storage.raw(), + &bind_group_layout, + ); + + let layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some( + "iced_wgpu::triangle::gradient pipeline layout", + ), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }, + ); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some( + "iced_wgpu::triangle::gradient create shader module", + ), + source: wgpu::ShaderSource::Wgsl( + std::borrow::Cow::Borrowed(include_str!( + "shader/gradient.wgsl" + )), + ), + }); + + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::triangle::gradient pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() + as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array!( + // Position + 0 => Float32x2, + ), + }], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[triangle::fragment_target(format)], + }), + primitive: triangle::primitive_state(), + depth_stencil: None, + multisample: triangle::multisample_state(antialiasing), + multiview: None, + }, + ); + + Self { + pipeline, + vertices, + uniforms, + storage, + color_stop_offset: 0, + color_stops_pending_write: Storage { + color_stops: vec![], + }, + bind_group_layout, + bind_group, + } + } + + pub fn bind_group( + device: &wgpu::Device, + uniform_buffer: &wgpu::Buffer, + storage_buffer: &wgpu::Buffer, + layout: &wgpu::BindGroupLayout, + ) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("iced_wgpu::triangle::gradient bind group"), + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + wgpu::BufferBinding { + buffer: uniform_buffer, + offset: 0, + size: Some(Uniforms::min_size()), + }, + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: storage_buffer.as_entire_binding(), + }, + ], + }) + } + } +} diff --git a/wgpu/src/triangle/gradient.rs b/wgpu/src/triangle/gradient.rs deleted file mode 100644 index b06cbac6a6..0000000000 --- a/wgpu/src/triangle/gradient.rs +++ /dev/null @@ -1,268 +0,0 @@ -use crate::buffer::dynamic; -use crate::settings; -use crate::triangle; -use encase::ShaderType; -use glam::{IVec4, Vec4}; -use iced_graphics::gradient::Gradient; -use iced_graphics::Transformation; - -pub struct Pipeline { - pipeline: wgpu::RenderPipeline, - pub(super) uniform_buffer: dynamic::Buffer, - pub(super) storage_buffer: dynamic::Buffer, - color_stop_offset: i32, - //Need to store these and then write them all at once - //or else they will be padded to 256 and cause gaps in the storage buffer - color_stops_pending_write: Storage, - bind_group_layout: wgpu::BindGroupLayout, - bind_group: wgpu::BindGroup, -} - -#[derive(Debug, ShaderType)] -pub(super) struct Uniforms { - transform: glam::Mat4, - //xy = start, zw = end - direction: Vec4, - //x = start stop, y = end stop, zw = padding - stop_range: IVec4, -} - -#[derive(Debug, ShaderType)] -pub(super) struct ColorStop { - color: Vec4, - offset: f32, -} - -#[derive(ShaderType)] -pub(super) struct Storage { - #[size(runtime)] - pub color_stops: Vec, -} - -impl Pipeline { - /// Creates a new [GradientPipeline] using `gradient.wgsl` shader. - pub(super) fn new( - device: &wgpu::Device, - format: wgpu::TextureFormat, - antialiasing: Option, - ) -> Self { - let uniform_buffer = dynamic::Buffer::uniform( - device, - "iced_wgpu::triangle::gradient uniforms", - ); - - //Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static - // sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work - let storage_buffer = dynamic::Buffer::storage( - device, - "iced_wgpu::triangle::gradient storage", - ); - - let bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("iced_wgpu::triangle::gradient bind group layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { - read_only: true, - }, - has_dynamic_offset: false, - min_binding_size: Some(Storage::min_size()), - }, - count: None, - }, - ], - }); - - let bind_group = Pipeline::bind_group( - device, - uniform_buffer.raw(), - storage_buffer.raw(), - &bind_group_layout, - ); - - let layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("iced_wgpu::triangle::gradient pipeline layout"), - bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: &[], - }); - - let shader = - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some( - "iced_wgpu::triangle::gradient create shader module", - ), - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( - include_str!("../shader/gradient.wgsl"), - )), - }); - - let pipeline = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("iced_wgpu::triangle::gradient pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[triangle::vertex_buffer_layout()], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[triangle::fragment_target(format)], - }), - primitive: triangle::primitive_state(), - depth_stencil: None, - multisample: triangle::multisample_state(antialiasing), - multiview: None, - }); - - Self { - pipeline, - uniform_buffer, - storage_buffer, - color_stop_offset: 0, - color_stops_pending_write: Storage { - color_stops: vec![], - }, - bind_group_layout, - bind_group, - } - } - - /// Pushes a new gradient uniform to the CPU buffer. - pub fn push(&mut self, transform: Transformation, gradient: &Gradient) { - match gradient { - Gradient::Linear(linear) => { - let start_offset = self.color_stop_offset; - let end_offset = - (linear.color_stops.len() as i32) + start_offset - 1; - - self.uniform_buffer.push(&Uniforms { - transform: transform.into(), - direction: Vec4::new( - linear.start.x, - linear.start.y, - linear.end.x, - linear.end.y, - ), - stop_range: IVec4::new(start_offset, end_offset, 0, 0), - }); - - self.color_stop_offset = end_offset + 1; - - let stops: Vec = linear - .color_stops - .iter() - .map(|stop| { - let [r, g, b, a] = stop.color.into_linear(); - - ColorStop { - offset: stop.offset, - color: Vec4::new(r, g, b, a), - } - }) - .collect(); - - self.color_stops_pending_write.color_stops.extend(stops); - } - } - } - - fn bind_group( - device: &wgpu::Device, - uniform_buffer: &wgpu::Buffer, - storage_buffer: &wgpu::Buffer, - layout: &wgpu::BindGroupLayout, - ) -> wgpu::BindGroup { - device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("iced_wgpu::triangle::gradient bind group"), - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer( - wgpu::BufferBinding { - buffer: uniform_buffer, - offset: 0, - size: Some(Uniforms::min_size()), - }, - ), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: storage_buffer.as_entire_binding(), - }, - ], - }) - } - - /// Writes the contents of the gradient CPU buffer to the GPU buffer, resizing the GPU buffer - /// beforehand if necessary. - pub fn write( - &mut self, - device: &wgpu::Device, - staging_belt: &mut wgpu::util::StagingBelt, - encoder: &mut wgpu::CommandEncoder, - ) { - //first write the pending color stops to the CPU buffer - self.storage_buffer.push(&self.color_stops_pending_write); - - //resize buffers if needed - let uniforms_resized = self.uniform_buffer.resize(device); - let storage_resized = self.storage_buffer.resize(device); - - if uniforms_resized || storage_resized { - //recreate bind groups if any buffers were resized - self.bind_group = Pipeline::bind_group( - device, - self.uniform_buffer.raw(), - self.storage_buffer.raw(), - &self.bind_group_layout, - ); - } - - //write to GPU - self.uniform_buffer.write(device, staging_belt, encoder); - self.storage_buffer.write(device, staging_belt, encoder); - - //cleanup - self.color_stop_offset = 0; - self.color_stops_pending_write.color_stops.clear(); - } - - pub fn set_render_pass_pipeline<'a>( - &'a self, - render_pass: &mut wgpu::RenderPass<'a>, - ) { - render_pass.set_pipeline(&self.pipeline); - } - - /// Configures the current render pass to draw the gradient at its offset stored in the - /// [DynamicBuffer] at [index]. - pub fn configure_render_pass<'a>( - &'a self, - render_pass: &mut wgpu::RenderPass<'a>, - count: usize, - ) { - render_pass.set_bind_group( - 0, - &self.bind_group, - &[self.uniform_buffer.offset_at_index(count)], - ) - } -} diff --git a/wgpu/src/triangle/solid.rs b/wgpu/src/triangle/solid.rs deleted file mode 100644 index 2e1052f294..0000000000 --- a/wgpu/src/triangle/solid.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::buffer::dynamic; -use crate::triangle; -use crate::{settings, Color}; -use encase::ShaderType; -use glam::Vec4; -use iced_graphics::Transformation; - -pub struct Pipeline { - pipeline: wgpu::RenderPipeline, - pub(super) buffer: dynamic::Buffer, - bind_group_layout: wgpu::BindGroupLayout, - bind_group: wgpu::BindGroup, -} - -#[derive(Debug, Clone, Copy, ShaderType)] -pub(super) struct Uniforms { - transform: glam::Mat4, - color: Vec4, -} - -impl Uniforms { - pub fn new(transform: Transformation, color: Color) -> Self { - let [r, g, b, a] = color.into_linear(); - - Self { - transform: transform.into(), - color: Vec4::new(r, g, b, a), - } - } -} - -impl Pipeline { - /// Creates a new [SolidPipeline] using `solid.wgsl` shader. - pub fn new( - device: &wgpu::Device, - format: wgpu::TextureFormat, - antialiasing: Option, - ) -> Self { - let buffer = dynamic::Buffer::uniform( - device, - "iced_wgpu::triangle::solid uniforms", - ); - - let bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("iced_wgpu::triangle::solid bind group layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }], - }); - - let bind_group = - Pipeline::bind_group(device, buffer.raw(), &bind_group_layout); - - let layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("iced_wgpu::triangle::solid pipeline layout"), - bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: &[], - }); - - let shader = - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("iced_wgpu::triangle::solid create shader module"), - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( - include_str!("../shader/solid.wgsl"), - )), - }); - - let pipeline = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("iced_wgpu::triangle::solid pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[triangle::vertex_buffer_layout()], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[triangle::fragment_target(format)], - }), - primitive: triangle::primitive_state(), - depth_stencil: None, - multisample: triangle::multisample_state(antialiasing), - multiview: None, - }); - - Self { - pipeline, - buffer, - bind_group_layout, - bind_group, - } - } - - fn bind_group( - device: &wgpu::Device, - buffer: &wgpu::Buffer, - layout: &wgpu::BindGroupLayout, - ) -> wgpu::BindGroup { - device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("iced_wgpu::triangle::solid bind group"), - layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer, - offset: 0, - size: Some(Uniforms::min_size()), - }), - }], - }) - } - - /// Pushes a new solid uniform to the CPU buffer. - pub fn push(&mut self, transform: Transformation, color: &Color) { - self.buffer.push(&Uniforms::new(transform, *color)); - } - - /// Writes the contents of the solid CPU buffer to the GPU buffer, resizing the GPU buffer - /// beforehand if necessary. - pub fn write( - &mut self, - device: &wgpu::Device, - staging_belt: &mut wgpu::util::StagingBelt, - encoder: &mut wgpu::CommandEncoder, - ) { - let uniforms_resized = self.buffer.resize(device); - - if uniforms_resized { - self.bind_group = Pipeline::bind_group( - device, - self.buffer.raw(), - &self.bind_group_layout, - ) - } - - self.buffer.write(device, staging_belt, encoder); - } - - pub fn set_render_pass_pipeline<'a>( - &'a self, - render_pass: &mut wgpu::RenderPass<'a>, - ) { - render_pass.set_pipeline(&self.pipeline); - } - - /// Configures the current render pass to draw the solid at its offset stored in the - /// [DynamicBuffer] at [index]. - pub fn configure_render_pass<'a>( - &'a self, - render_pass: &mut wgpu::RenderPass<'a>, - count: usize, - ) { - render_pass.set_bind_group( - 0, - &self.bind_group, - &[self.buffer.offset_at_index(count)], - ) - } -} From c81eaf5f8d8db41e9bb96419bc39684a9978c232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 16 Nov 2022 09:31:29 +0100 Subject: [PATCH 16/43] Replace magic constants in `glow::triangle` --- glow/src/triangle.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs index 849a72723d..d0205e0818 100644 --- a/glow/src/triangle.rs +++ b/glow/src/triangle.rs @@ -9,6 +9,9 @@ use iced_graphics::triangle::{ColoredVertex2D, Vertex2D}; use glow::HasContext; use std::marker::PhantomData; +const DEFAULT_VERTICES: usize = 1_000; +const DEFAULT_INDICES: usize = 1_000; + #[derive(Debug)] pub(crate) struct Pipeline { indices: Buffer, @@ -23,7 +26,7 @@ impl Pipeline { gl, glow::ELEMENT_ARRAY_BUFFER, glow::DYNAMIC_DRAW, - 1000, + DEFAULT_INDICES, ) }; @@ -366,7 +369,7 @@ mod solid { gl, glow::ARRAY_BUFFER, glow::DYNAMIC_DRAW, - 1000, + super::DEFAULT_VERTICES, ) }; @@ -487,7 +490,7 @@ mod gradient { gl, glow::ARRAY_BUFFER, glow::DYNAMIC_DRAW, - 1000, + super::DEFAULT_VERTICES, ) }; From 3bd99221cc3b50ba9e038b1875fec91fdea8039b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 16 Nov 2022 10:01:40 +0100 Subject: [PATCH 17/43] Fix padding for `TextInput` with `Length::Units` width --- native/src/widget/text_input.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index ef2f9a176a..14e7e1b71f 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -383,19 +383,17 @@ where { let text_size = size.unwrap_or_else(|| renderer.default_size()); - let text_limits = limits + let padding = padding.fit(Size::ZERO, limits.max()); + + let limits = limits .pad(padding) .width(width) .height(Length::Units(text_size)); - let limits = limits.width(width).height(Length::Shrink); - - let mut text = layout::Node::new(text_limits.resolve(Size::ZERO)); - let padding = padding.fit(text.size(), limits.max()); - let size = limits.pad(padding).resolve(text.size()).pad(padding); + let mut text = layout::Node::new(limits.resolve(Size::ZERO)); text.move_to(Point::new(padding.left.into(), padding.top.into())); - layout::Node::with_children(size, vec![text]) + layout::Node::with_children(text.size().pad(padding), vec![text]) } /// Processes an [`Event`] and updates the [`State`] of a [`TextInput`] From 751ffb590053d713ea376893a1d9050514b8ffe1 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 23 Nov 2022 13:37:59 -0500 Subject: [PATCH 18/43] fix: scissor layout bounds for images --- glow/src/backend.rs | 2 +- glow/src/image.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 1a41d54024..c663869e1f 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -129,7 +129,7 @@ impl Backend { * Transformation::scale(scale_factor, scale_factor); self.image_pipeline - .draw(gl, scaled, scale_factor, &layer.images); + .draw(gl, scaled, scale_factor, &layer.images, bounds); } if !layer.text.is_empty() { diff --git a/glow/src/image.rs b/glow/src/image.rs index f906cd4c47..234583e982 100644 --- a/glow/src/image.rs +++ b/glow/src/image.rs @@ -14,6 +14,7 @@ use iced_graphics::image::raster; use iced_graphics::image::vector; use iced_graphics::layer; +use iced_graphics::Rectangle; use iced_graphics::Size; use glow::HasContext; @@ -144,11 +145,13 @@ impl Pipeline { transformation: Transformation, _scale_factor: f32, images: &[layer::Image], + layer_bounds: Rectangle, ) { unsafe { gl.use_program(Some(self.program)); gl.bind_vertex_array(Some(self.vertex_array)); gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer)); + gl.enable(glow::SCISSOR_TEST); } #[cfg(feature = "image")] @@ -187,6 +190,13 @@ impl Pipeline { }; unsafe { + gl.scissor( + layer_bounds.x as i32, + layer_bounds.y as i32, + layer_bounds.width as i32, + layer_bounds.height as i32, + ); + if let Some(storage::Entry { texture, .. }) = entry { gl.bind_texture(glow::TEXTURE_2D, Some(*texture)) } else { @@ -213,6 +223,7 @@ impl Pipeline { gl.bind_buffer(glow::ARRAY_BUFFER, None); gl.bind_vertex_array(None); gl.use_program(None); + gl.disable(glow::SCISSOR_TEST); } } From 84c5ee7fb2d716e1d3c35a53a2cd33fcc6fafc3a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 23 Nov 2022 13:40:06 -0500 Subject: [PATCH 19/43] cargo fmt --- glow/src/backend.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/glow/src/backend.rs b/glow/src/backend.rs index c663869e1f..645c5faf33 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -128,8 +128,13 @@ impl Backend { let scaled = transformation * Transformation::scale(scale_factor, scale_factor); - self.image_pipeline - .draw(gl, scaled, scale_factor, &layer.images, bounds); + self.image_pipeline.draw( + gl, + scaled, + scale_factor, + &layer.images, + bounds, + ); } if !layer.text.is_empty() { From efc00b2f412865b975088002cbe3c927ef662e10 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 23 Nov 2022 14:46:57 -0500 Subject: [PATCH 20/43] fix: adjust y position of scissor rectangle --- glow/src/backend.rs | 1 + glow/src/image.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 645c5faf33..416c3b94d4 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -130,6 +130,7 @@ impl Backend { self.image_pipeline.draw( gl, + target_height, scaled, scale_factor, &layer.images, diff --git a/glow/src/image.rs b/glow/src/image.rs index 234583e982..955fd1abfb 100644 --- a/glow/src/image.rs +++ b/glow/src/image.rs @@ -142,6 +142,7 @@ impl Pipeline { pub fn draw( &mut self, mut gl: &glow::Context, + target_height: u32, transformation: Transformation, _scale_factor: f32, images: &[layer::Image], @@ -192,7 +193,8 @@ impl Pipeline { unsafe { gl.scissor( layer_bounds.x as i32, - layer_bounds.y as i32, + (target_height - (layer_bounds.y + layer_bounds.height)) + as i32, layer_bounds.width as i32, layer_bounds.height as i32, ); From ee263cbb3c90a9adce7608686fbe5881c81f1c91 Mon Sep 17 00:00:00 2001 From: Leo Lee Date: Fri, 25 Nov 2022 17:36:42 +0800 Subject: [PATCH 21/43] docs: Remove invalid links. --- examples/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index bb15dc2ed2..8d9718a46b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -92,7 +92,6 @@ A bunch of simpler examples exist: - [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle. - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`events`](events), a log of native events displayed using a conditional `Subscription`. -- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). - [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application. - [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. From 94988bb8f25efb93ee1616e4c827b6f740da8fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 28 Nov 2022 19:31:26 +0100 Subject: [PATCH 22/43] Implement `From` traits for `Style` in `canvas` --- graphics/src/widget/canvas/style.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/graphics/src/widget/canvas/style.rs b/graphics/src/widget/canvas/style.rs index 794109bd8b..6794f2e753 100644 --- a/graphics/src/widget/canvas/style.rs +++ b/graphics/src/widget/canvas/style.rs @@ -9,3 +9,15 @@ pub enum Style { /// A [`Gradient`] color. Gradient(Gradient), } + +impl From for Style { + fn from(color: Color) -> Self { + Self::Solid(color) + } +} + +impl From for Style { + fn from(gradient: Gradient) -> Self { + Self::Gradient(gradient) + } +} From 324d60db6319017304bf0b514b76db98b3637929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 28 Nov 2022 19:42:53 +0100 Subject: [PATCH 23/43] Re-introduce the `geometry` example --- Cargo.toml | 1 + examples/README.md | 1 + examples/geometry/Cargo.toml | 11 ++ examples/geometry/README.md | 18 +++ examples/geometry/src/main.rs | 213 ++++++++++++++++++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 examples/geometry/Cargo.toml create mode 100644 examples/geometry/README.md create mode 100644 examples/geometry/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 2644f50829..d0243dfd8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ members = [ "examples/events", "examples/exit", "examples/game_of_life", + "examples/geometry", "examples/integration_opengl", "examples/integration_wgpu", "examples/lazy", diff --git a/examples/README.md b/examples/README.md index 8d9718a46b..bb15dc2ed2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -92,6 +92,7 @@ A bunch of simpler examples exist: - [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle. - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`events`](events), a log of native events displayed using a conditional `Subscription`. +- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). - [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application. - [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. diff --git a/examples/geometry/Cargo.toml b/examples/geometry/Cargo.toml new file mode 100644 index 0000000000..22ede0e00d --- /dev/null +++ b/examples/geometry/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "geometry" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../.." } +iced_native = { path = "../../native" } +iced_graphics = { path = "../../graphics" } diff --git a/examples/geometry/README.md b/examples/geometry/README.md new file mode 100644 index 0000000000..4d5c81cbef --- /dev/null +++ b/examples/geometry/README.md @@ -0,0 +1,18 @@ +## Geometry + +A custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../../wgpu). + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package geometry +``` + +[`main`]: src/main.rs diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs new file mode 100644 index 0000000000..4de281fe57 --- /dev/null +++ b/examples/geometry/src/main.rs @@ -0,0 +1,213 @@ +//! This example showcases a simple native custom widget that renders using +//! arbitrary low-level geometry. +mod rainbow { + // For now, to implement a custom native widget you will need to add + // `iced_native` and `iced_wgpu` to your dependencies. + // + // Then, you simply need to define your widget type and implement the + // `iced_native::Widget` trait with the `iced_wgpu::Renderer`. + // + // Of course, you can choose to make the implementation renderer-agnostic, + // if you wish to, by creating your own `Renderer` trait, which could be + // implemented by `iced_wgpu` and other renderers. + use iced_graphics::renderer::{self, Renderer}; + use iced_graphics::triangle::ColoredVertex2D; + use iced_graphics::{Backend, Primitive}; + + use iced_native::layout; + use iced_native::widget::{self, Widget}; + use iced_native::{ + Element, Layout, Length, Point, Rectangle, Size, Vector, + }; + + pub struct Rainbow; + + impl Rainbow { + pub fn new() -> Self { + Self + } + } + + impl Widget> for Rainbow + where + B: Backend, + { + fn width(&self) -> Length { + Length::Fill + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let size = limits.width(Length::Fill).resolve(Size::ZERO); + + layout::Node::new(Size::new(size.width, size.width)) + } + + fn draw( + &self, + _tree: &widget::Tree, + renderer: &mut Renderer, + _theme: &T, + _style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + _viewport: &Rectangle, + ) { + use iced_graphics::triangle::Mesh2D; + use iced_native::Renderer as _; + + let b = layout.bounds(); + + // R O Y G B I V + let color_r = [1.0, 0.0, 0.0, 1.0]; + let color_o = [1.0, 0.5, 0.0, 1.0]; + let color_y = [1.0, 1.0, 0.0, 1.0]; + let color_g = [0.0, 1.0, 0.0, 1.0]; + let color_gb = [0.0, 1.0, 0.5, 1.0]; + let color_b = [0.0, 0.2, 1.0, 1.0]; + let color_i = [0.5, 0.0, 1.0, 1.0]; + let color_v = [0.75, 0.0, 0.5, 1.0]; + + let posn_center = { + if b.contains(cursor_position) { + [cursor_position.x - b.x, cursor_position.y - b.y] + } else { + [b.width / 2.0, b.height / 2.0] + } + }; + + let posn_tl = [0.0, 0.0]; + let posn_t = [b.width / 2.0, 0.0]; + let posn_tr = [b.width, 0.0]; + let posn_r = [b.width, b.height / 2.0]; + let posn_br = [b.width, b.height]; + let posn_b = [(b.width / 2.0), b.height]; + let posn_bl = [0.0, b.height]; + let posn_l = [0.0, b.height / 2.0]; + + let mesh = Primitive::SolidMesh { + size: b.size(), + buffers: Mesh2D { + vertices: vec![ + ColoredVertex2D { + position: posn_center, + color: [1.0, 1.0, 1.0, 1.0], + }, + ColoredVertex2D { + position: posn_tl, + color: color_r, + }, + ColoredVertex2D { + position: posn_t, + color: color_o, + }, + ColoredVertex2D { + position: posn_tr, + color: color_y, + }, + ColoredVertex2D { + position: posn_r, + color: color_g, + }, + ColoredVertex2D { + position: posn_br, + color: color_gb, + }, + ColoredVertex2D { + position: posn_b, + color: color_b, + }, + ColoredVertex2D { + position: posn_bl, + color: color_i, + }, + ColoredVertex2D { + position: posn_l, + color: color_v, + }, + ], + indices: vec![ + 0, 1, 2, // TL + 0, 2, 3, // T + 0, 3, 4, // TR + 0, 4, 5, // R + 0, 5, 6, // BR + 0, 6, 7, // B + 0, 7, 8, // BL + 0, 8, 1, // L + ], + }, + }; + + renderer.with_translation(Vector::new(b.x, b.y), |renderer| { + renderer.draw_primitive(mesh); + }); + } + } + + impl<'a, Message, B, T> From for Element<'a, Message, Renderer> + where + B: Backend, + { + fn from(rainbow: Rainbow) -> Self { + Self::new(rainbow) + } + } +} + +use iced::widget::{column, container, scrollable}; +use iced::{Element, Length, Sandbox, Settings}; +use rainbow::Rainbow; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +struct Example; + +impl Sandbox for Example { + type Message = (); + + fn new() -> Self { + Self + } + + fn title(&self) -> String { + String::from("Custom 2D geometry - Iced") + } + + fn update(&mut self, _: ()) {} + + fn view(&self) -> Element<()> { + let content = column![ + Rainbow::new(), + "In this example we draw a custom widget Rainbow, using \ + the Mesh2D primitive. This primitive supplies a list of \ + triangles, expressed as vertices and indices.", + "Move your cursor over it, and see the center vertex \ + follow you!", + "Every Vertex2D defines its own color. You could use the \ + Mesh2D primitive to render virtually any two-dimensional \ + geometry for your widget.", + ] + .padding(20) + .spacing(20) + .max_width(500); + + let scrollable = + scrollable(container(content).width(Length::Fill).center_x()); + + container(scrollable) + .width(Length::Fill) + .height(Length::Fill) + .center_y() + .into() + } +} From bb2bf063b472396d44f9f3114a87ba79dfd5f62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 28 Nov 2022 19:49:23 +0100 Subject: [PATCH 24/43] Derive `Default` for `Rainbow` in `geometry` example --- examples/geometry/src/main.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 4de281fe57..9bacce7f6b 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -20,12 +20,11 @@ mod rainbow { Element, Layout, Length, Point, Rectangle, Size, Vector, }; + #[derive(Debug, Clone, Copy, Default)] pub struct Rainbow; - impl Rainbow { - pub fn new() -> Self { - Self - } + pub fn rainbow() -> Rainbow { + Rainbow } impl Widget> for Rainbow @@ -164,7 +163,7 @@ mod rainbow { use iced::widget::{column, container, scrollable}; use iced::{Element, Length, Sandbox, Settings}; -use rainbow::Rainbow; +use rainbow::rainbow; pub fn main() -> iced::Result { Example::run(Settings::default()) @@ -187,7 +186,7 @@ impl Sandbox for Example { fn view(&self) -> Element<()> { let content = column![ - Rainbow::new(), + rainbow(), "In this example we draw a custom widget Rainbow, using \ the Mesh2D primitive. This primitive supplies a list of \ triangles, expressed as vertices and indices.", From f1ada7a803998ac3fb2c1bedc6d6650264f3e603 Mon Sep 17 00:00:00 2001 From: tarkah Date: Sat, 19 Nov 2022 12:25:59 -0800 Subject: [PATCH 25/43] Allow &mut self in overlay --- lazy/src/component.rs | 31 ++++++++++-------------- lazy/src/lazy.rs | 27 +++++++-------------- lazy/src/responsive.rs | 22 ++++++++++------- native/src/element.rs | 4 +-- native/src/overlay.rs | 6 ++--- native/src/user_interface.rs | 18 +++++++------- native/src/widget.rs | 2 +- native/src/widget/button.rs | 4 +-- native/src/widget/column.rs | 4 +-- native/src/widget/container.rs | 4 +-- native/src/widget/pane_grid.rs | 4 +-- native/src/widget/pane_grid/content.rs | 8 +++--- native/src/widget/pane_grid/title_bar.rs | 8 +++--- native/src/widget/pick_list.rs | 2 +- native/src/widget/row.rs | 4 +-- native/src/widget/scrollable.rs | 4 +-- native/src/widget/tooltip.rs | 4 +-- 17 files changed, 73 insertions(+), 83 deletions(-) diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 4f1df6504c..3d7b8758bc 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -11,7 +11,7 @@ use iced_native::{ }; use ouroboros::self_referencing; -use std::cell::{Ref, RefCell}; +use std::cell::RefCell; use std::marker::PhantomData; /// A reusable, custom widget that uses The Elm Architecture. @@ -322,25 +322,25 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { let overlay = OverlayBuilder { instance: self, - instance_ref_builder: |instance| instance.state.borrow(), tree, types: PhantomData, overlay_builder: |instance, tree| { - instance - .as_ref() - .unwrap() - .borrow_element() - .as_ref() - .unwrap() - .as_widget() - .overlay(&mut tree.children[0], layout, renderer) + instance.state.get_mut().as_mut().unwrap().with_element_mut( + move |element| { + element.as_mut().unwrap().as_widget_mut().overlay( + &mut tree.children[0], + layout, + renderer, + ) + }, + ) }, } .build(); @@ -362,15 +362,11 @@ where #[self_referencing] struct Overlay<'a, 'b, Message, Renderer, Event, S> { - instance: &'a Instance<'b, Message, Renderer, Event, S>, + instance: &'a mut Instance<'b, Message, Renderer, Event, S>, tree: &'a mut Tree, types: PhantomData<(Message, Event, S)>, - #[borrows(instance)] - #[covariant] - instance_ref: Ref<'this, Option>>, - - #[borrows(instance_ref, mut tree)] + #[borrows(mut instance, mut tree)] #[covariant] overlay: Option>, } @@ -514,7 +510,6 @@ where self.overlay = Some( OverlayBuilder { instance: overlay.instance, - instance_ref_builder: |instance| instance.state.borrow(), tree: overlay.tree, types: PhantomData, overlay_builder: |_, _| None, diff --git a/lazy/src/lazy.rs b/lazy/src/lazy.rs index d61cc77e46..2611dd1092 100644 --- a/lazy/src/lazy.rs +++ b/lazy/src/lazy.rs @@ -207,7 +207,7 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -216,12 +216,12 @@ where cached: self, tree: &mut tree.children[0], types: PhantomData, - element_ref_builder: |cached| cached.element.borrow(), - element_builder: |element_ref| { - element_ref.as_ref().unwrap().borrow() - }, - overlay_builder: |element, tree| { - element.as_widget().overlay(tree, layout, renderer) + overlay_builder: |cached, tree| { + Rc::get_mut(cached.element.get_mut().as_mut().unwrap()) + .unwrap() + .get_mut() + .as_widget_mut() + .overlay(tree, layout, renderer) }, } .build(); @@ -237,20 +237,11 @@ where #[self_referencing] struct Overlay<'a, 'b, Message, Renderer, Dependency, View> { - cached: &'a Lazy<'b, Message, Renderer, Dependency, View>, + cached: &'a mut Lazy<'b, Message, Renderer, Dependency, View>, tree: &'a mut Tree, types: PhantomData<(Message, Dependency, View)>, - #[borrows(cached)] - #[covariant] - element_ref: - Ref<'this, Option>>>>, - - #[borrows(element_ref)] - #[covariant] - element: Ref<'this, Element<'static, Message, Renderer>>, - - #[borrows(element, mut tree)] + #[borrows(mut cached, mut tree)] #[covariant] overlay: Option>, } diff --git a/lazy/src/responsive.rs b/lazy/src/responsive.rs index 0b7ae6de10..5e1b5dff1c 100644 --- a/lazy/src/responsive.rs +++ b/lazy/src/responsive.rs @@ -235,18 +235,20 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { + use std::ops::DerefMut; + let state = tree.state.downcast_ref::(); let overlay = OverlayBuilder { content: self.content.borrow_mut(), tree: state.tree.borrow_mut(), types: PhantomData, - overlay_builder: |content, tree| { + overlay_builder: |content: &mut RefMut>, tree| { content.update( tree, renderer, @@ -254,16 +256,18 @@ where &self.view, ); + let Content { + element, layout, .. + } = content.deref_mut(); + let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - &content.layout, + layout.bounds().position() - Point::ORIGIN, + layout, ); - content.element.as_widget().overlay( - tree, - content_layout, - renderer, - ) + element + .as_widget_mut() + .overlay(tree, content_layout, renderer) }, } .build(); diff --git a/native/src/element.rs b/native/src/element.rs index f941a4904b..4cca387093 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -405,7 +405,7 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -560,7 +560,7 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, state: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 8c01581f95..bcb1a2ee13 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -93,7 +93,7 @@ where /// This method will generally only be used by advanced users that are /// implementing the [`Widget`](crate::Widget) trait. pub fn from_children<'a, Message, Renderer>( - children: &'a [crate::Element<'_, Message, Renderer>], + children: &'a mut [crate::Element<'_, Message, Renderer>], tree: &'a mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -102,11 +102,11 @@ where Renderer: crate::Renderer, { children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .filter_map(|((child, state), layout)| { - child.as_widget().overlay(state, layout, renderer) + child.as_widget_mut().overlay(state, layout, renderer) }) .next() } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 7c82878c01..55c86a1736 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -190,7 +190,7 @@ where let mut state = State::Updated; let mut manual_overlay = - ManuallyDrop::new(self.root.as_widget().overlay( + ManuallyDrop::new(self.root.as_widget_mut().overlay( &mut self.state, Layout::new(&self.base), renderer, @@ -226,7 +226,7 @@ where ); manual_overlay = - ManuallyDrop::new(self.root.as_widget().overlay( + ManuallyDrop::new(self.root.as_widget_mut().overlay( &mut self.state, Layout::new(&self.base), renderer, @@ -395,11 +395,11 @@ where let viewport = Rectangle::with_size(self.bounds); - let base_cursor = if let Some(overlay) = self.root.as_widget().overlay( - &mut self.state, - Layout::new(&self.base), - renderer, - ) { + let base_cursor = if let Some(overlay) = self + .root + .as_widget_mut() + .overlay(&mut self.state, Layout::new(&self.base), renderer) + { let overlay_layout = self .overlay .take() @@ -452,7 +452,7 @@ where overlay .as_ref() .and_then(|layout| { - root.as_widget() + root.as_widget_mut() .overlay(&mut self.state, Layout::new(base), renderer) .map(|overlay| { let overlay_interaction = overlay.mouse_interaction( @@ -497,7 +497,7 @@ where ); if let Some(layout) = self.overlay.as_ref() { - if let Some(overlay) = self.root.as_widget().overlay( + if let Some(overlay) = self.root.as_widget_mut().overlay( &mut self.state, Layout::new(&self.base), renderer, diff --git a/native/src/widget.rs b/native/src/widget.rs index 526c7d0087..36d679a499 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -208,7 +208,7 @@ where /// Returns the overlay of the [`Widget`], if there is any. fn overlay<'a>( - &'a self, + &'a mut self, _state: &'a mut Tree, _layout: Layout<'_>, _renderer: &Renderer, diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index fa5da24bb2..d68e01c709 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -260,12 +260,12 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - self.content.as_widget().overlay( + self.content.as_widget_mut().overlay( &mut tree.children[0], layout.children().next().unwrap(), renderer, diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index a8b0f18307..8030778bff 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -242,12 +242,12 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - overlay::from_children(&self.children, tree, layout, renderer) + overlay::from_children(&mut self.children, tree, layout, renderer) } } diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 9d3e4d9b3e..1c6810916f 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -248,12 +248,12 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - self.content.as_widget().overlay( + self.content.as_widget_mut().overlay( &mut tree.children[0], layout.children().next().unwrap(), renderer, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 8f9065b099..c3ac6de604 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -444,13 +444,13 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { self.contents - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .filter_map(|(((_, pane), tree), layout)| { diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 5e843cff74..5f269d1f60 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -305,12 +305,12 @@ where } pub(crate) fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - if let Some(title_bar) = self.title_bar.as_ref() { + if let Some(title_bar) = self.title_bar.as_mut() { let mut children = layout.children(); let title_bar_layout = children.next()?; @@ -321,14 +321,14 @@ where match title_bar.overlay(title_bar_state, title_bar_layout, renderer) { Some(overlay) => Some(overlay), - None => self.body.as_widget().overlay( + None => self.body.as_widget_mut().overlay( body_state, children.next()?, renderer, ), } } else { - self.body.as_widget().overlay( + self.body.as_widget_mut().overlay( &mut tree.children[0], layout, renderer, diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 115f6270da..28e4670f4f 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -395,7 +395,7 @@ where } pub(crate) fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -415,13 +415,13 @@ where let controls_state = states.next().unwrap(); content - .as_widget() + .as_widget_mut() .overlay(title_state, title_layout, renderer) .or_else(move || { - controls.as_ref().and_then(|controls| { + controls.as_mut().and_then(|controls| { let controls_layout = children.next()?; - controls.as_widget().overlay( + controls.as_widget_mut().overlay( controls_state, controls_layout, renderer, diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index a6459cd6a4..8f799a2a43 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -219,7 +219,7 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index eda7c2d355..c689ac13f2 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -229,12 +229,12 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - overlay::from_children(&self.children, tree, layout, renderer) + overlay::from_children(&mut self.children, tree, layout, renderer) } } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 32ec6eb3f1..495b7af19a 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -276,13 +276,13 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { self.content - .as_widget() + .as_widget_mut() .overlay( &mut tree.children[0], layout.children().next().unwrap(), diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 9347a8868e..084dc269ee 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -221,12 +221,12 @@ where } fn overlay<'b>( - &'b self, + &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - self.content.as_widget().overlay( + self.content.as_widget_mut().overlay( &mut tree.children[0], layout, renderer, From 9a601c095420d3eba23df470becc112b928f9f62 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 18 Nov 2022 12:09:18 -0800 Subject: [PATCH 26/43] Get widget operations working w/ overlay --- native/src/overlay.rs | 2 +- native/src/overlay/element.rs | 4 ++-- native/src/user_interface.rs | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/native/src/overlay.rs b/native/src/overlay.rs index bcb1a2ee13..0b05b058e1 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -44,7 +44,7 @@ where /// Applies a [`widget::Operation`] to the [`Overlay`]. fn operate( - &self, + &mut self, _layout: Layout<'_>, _operation: &mut dyn widget::Operation, ) { diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index 09eee86d63..4f5ef32ac7 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -106,7 +106,7 @@ where /// Applies a [`widget::Operation`] to the [`Element`]. pub fn operate( - &self, + &mut self, layout: Layout<'_>, operation: &mut dyn widget::Operation, ) { @@ -142,7 +142,7 @@ where } fn operate( - &self, + &mut self, layout: Layout<'_>, operation: &mut dyn widget::Operation, ) { diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 55c86a1736..6b853da826 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -496,14 +496,19 @@ where operation, ); - if let Some(layout) = self.overlay.as_ref() { - if let Some(overlay) = self.root.as_widget_mut().overlay( - &mut self.state, - Layout::new(&self.base), - renderer, - ) { - overlay.operate(Layout::new(layout), operation); + if let Some(mut overlay) = self.root.as_widget_mut().overlay( + &mut self.state, + Layout::new(&self.base), + renderer, + ) { + if self.overlay.is_none() { + self.overlay = Some(overlay.layout(renderer, self.bounds)); } + + overlay.operate( + Layout::new(self.overlay.as_ref().unwrap()), + operation, + ); } } From 3814fda9d22875bdbc9e11ccfc6dce1f0e7ca5d3 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 18 Nov 2022 14:16:02 -0800 Subject: [PATCH 27/43] Add modal example --- Cargo.toml | 35 +-- examples/modal/Cargo.toml | 10 + examples/modal/src/main.rs | 454 +++++++++++++++++++++++++++++++++++++ 3 files changed, 465 insertions(+), 34 deletions(-) create mode 100644 examples/modal/Cargo.toml create mode 100644 examples/modal/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index d0243dfd8c..5b76b22d96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,40 +55,7 @@ members = [ "style", "wgpu", "winit", - "examples/arc", - "examples/bezier_tool", - "examples/clock", - "examples/color_palette", - "examples/component", - "examples/counter", - "examples/custom_widget", - "examples/download_progress", - "examples/events", - "examples/exit", - "examples/game_of_life", - "examples/geometry", - "examples/integration_opengl", - "examples/integration_wgpu", - "examples/lazy", - "examples/modern_art", - "examples/multitouch", - "examples/pane_grid", - "examples/pick_list", - "examples/pokedex", - "examples/progress_bar", - "examples/qr_code", - "examples/scrollable", - "examples/sierpinski_triangle", - "examples/solar_system", - "examples/stopwatch", - "examples/styling", - "examples/svg", - "examples/system_information", - "examples/todos", - "examples/tooltip", - "examples/tour", - "examples/url_handler", - "examples/websocket", + "examples/*", ] [dependencies] diff --git a/examples/modal/Cargo.toml b/examples/modal/Cargo.toml new file mode 100644 index 0000000000..8770acac6c --- /dev/null +++ b/examples/modal/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "modal" +version = "0.1.0" +authors = ["tarkah "] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = [] } +iced_native = { path = "../../native" } diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs new file mode 100644 index 0000000000..37ac16fd2c --- /dev/null +++ b/examples/modal/src/main.rs @@ -0,0 +1,454 @@ +use iced::widget::{ + self, button, column, container, horizontal_space, row, text, text_input, +}; +use iced::{ + executor, keyboard, subscription, theme, Alignment, Application, Command, + Element, Event, Length, Settings, Subscription, +}; + +use self::modal::Modal; + +pub fn main() -> iced::Result { + App::run(Settings::default()) +} + +#[derive(Default)] +struct App { + show_modal: bool, + email: String, + password: String, +} + +#[derive(Debug, Clone)] +enum Message { + ShowModal, + HideModal, + Email(String), + Password(String), + Event(Event), +} + +impl Application for App { + type Executor = executor::Default; + type Message = Message; + type Theme = iced::Theme; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command) { + (App::default(), Command::none()) + } + + fn title(&self) -> String { + String::from("Modal - Iced") + } + + fn subscription(&self) -> Subscription { + subscription::events().map(Message::Event) + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::ShowModal => { + self.show_modal = true; + widget::focus_next() + } + Message::HideModal => { + self.show_modal = false; + self.email.clear(); + self.password.clear(); + Command::none() + } + Message::Email(email) => { + self.email = email; + Command::none() + } + Message::Password(password) => { + self.password = password; + Command::none() + } + Message::Event(event) => { + if let Event::Keyboard(keyboard::Event::KeyPressed { + key_code: keyboard::KeyCode::Tab, + modifiers, + }) = event + { + if modifiers.shift() { + widget::focus_previous() + } else { + widget::focus_next() + } + } else { + Command::none() + } + } + } + } + + fn view(&self) -> Element { + let content = container( + column![ + row![ + text("Top Left"), + horizontal_space(Length::Fill), + text("Top Right") + ] + .align_items(Alignment::Start) + .height(Length::Fill), + container( + button(text("Show Modal")).on_press(Message::ShowModal) + ) + .center_x() + .center_y() + .width(Length::Fill) + .height(Length::Fill), + row![ + text("Bottom Left"), + horizontal_space(Length::Fill), + text("Bottom Right") + ] + .align_items(Alignment::End) + .height(Length::Fill), + ] + .height(Length::Fill), + ) + .padding(10) + .width(Length::Fill) + .height(Length::Fill); + + if self.show_modal { + let modal = container( + column![ + text("Sign Up").size(24), + column![ + column![ + text("Email").size(12), + text_input( + "abc@123.com", + &self.email, + Message::Email + ) + .padding(5), + ] + .spacing(5), + column![ + text("Password").size(12), + text_input("", &self.password, Message::Password) + .password() + .padding(5), + ] + .spacing(5), + button(text("Submit")).on_press(Message::HideModal), + ] + .spacing(10) + ] + .spacing(20), + ) + .width(Length::Units(300)) + .padding(10) + .style(theme::Container::Box); + + Modal::new(content, modal) + .on_blur(Message::HideModal) + .into() + } else { + content.into() + } + } +} + +mod modal { + use iced_native::alignment::Alignment; + use iced_native::widget::{self, Tree}; + use iced_native::{ + event, layout, mouse, overlay, renderer, Clipboard, Color, Element, + Event, Layout, Length, Point, Rectangle, Shell, Size, Widget, + }; + + /// A widget that centers a modal element over some base element + pub struct Modal<'a, Message, Renderer> { + base: Element<'a, Message, Renderer>, + modal: Element<'a, Message, Renderer>, + on_blur: Option, + } + + impl<'a, Message, Renderer> Modal<'a, Message, Renderer> { + /// Returns a new [`Modal`] + pub fn new( + base: impl Into>, + modal: impl Into>, + ) -> Self { + Self { + base: base.into(), + modal: modal.into(), + on_blur: None, + } + } + + /// Sets the message that will be produces when the background + /// of the [`Modal`] is pressed + pub fn on_blur(self, on_blur: Message) -> Self { + Self { + on_blur: Some(on_blur), + ..self + } + } + } + + impl<'a, Message, Renderer> Widget + for Modal<'a, Message, Renderer> + where + Renderer: iced_native::Renderer, + Message: Clone, + { + fn children(&self) -> Vec { + vec![Tree::new(&self.base), Tree::new(&self.modal)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[&self.base, &self.modal]); + } + + fn width(&self) -> Length { + self.base.as_widget().width() + } + + fn height(&self) -> Length { + self.base.as_widget().height() + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.base.as_widget().layout(renderer, limits) + } + + fn on_event( + &mut self, + state: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + self.base.as_widget_mut().on_event( + &mut state.children[0], + event, + layout, + cursor_position, + renderer, + clipboard, + shell, + ) + } + + fn draw( + &self, + state: &Tree, + renderer: &mut Renderer, + theme: &::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + self.base.as_widget().draw( + &state.children[0], + renderer, + theme, + style, + layout, + cursor_position, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + state: &'b mut Tree, + layout: Layout<'_>, + _renderer: &Renderer, + ) -> Option> { + Some(overlay::Element::new( + layout.position(), + Box::new(Overlay { + content: &mut self.modal, + tree: &mut state.children[1], + size: layout.bounds().size(), + on_blur: self.on_blur.clone(), + }), + )) + } + + fn mouse_interaction( + &self, + state: &Tree, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.base.as_widget().mouse_interaction( + &state.children[0], + layout, + cursor_position, + viewport, + renderer, + ) + } + + fn operate( + &self, + state: &mut Tree, + layout: Layout<'_>, + operation: &mut dyn widget::Operation, + ) { + self.base.as_widget().operate( + &mut state.children[0], + layout, + operation, + ); + } + } + + struct Overlay<'a, 'b, Message, Renderer> { + content: &'b mut Element<'a, Message, Renderer>, + tree: &'b mut Tree, + size: Size, + on_blur: Option, + } + + impl<'a, 'b, Message, Renderer> overlay::Overlay + for Overlay<'a, 'b, Message, Renderer> + where + Renderer: iced_native::Renderer, + Message: Clone, + { + fn layout( + &self, + renderer: &Renderer, + _bounds: Size, + position: Point, + ) -> layout::Node { + let limits = layout::Limits::new(Size::ZERO, self.size) + .width(Length::Fill) + .height(Length::Fill); + + let mut child = self.content.as_widget().layout(renderer, &limits); + child.align(Alignment::Center, Alignment::Center, limits.max()); + + let mut node = layout::Node::with_children(self.size, vec![child]); + node.move_to(position); + + node + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + let content_bounds = layout.children().next().unwrap().bounds(); + + if let Some(message) = self.on_blur.as_ref() { + if let Event::Mouse(mouse::Event::ButtonPressed( + mouse::Button::Left, + )) = &event + { + if !content_bounds.contains(cursor_position) { + shell.publish(message.clone()); + return event::Status::Captured; + } + } + } + + self.content.as_widget_mut().on_event( + self.tree, + event, + layout.children().next().unwrap(), + cursor_position, + renderer, + clipboard, + shell, + ) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Renderer::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + ) { + renderer.fill_quad( + renderer::Quad { + bounds: layout.bounds(), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Color { + a: 0.80, + ..Color::BLACK + }, + ); + + self.content.as_widget().draw( + self.tree, + renderer, + theme, + style, + layout.children().next().unwrap(), + cursor_position, + &layout.bounds(), + ); + } + + fn operate( + &mut self, + layout: Layout<'_>, + operation: &mut dyn widget::Operation, + ) { + self.content.as_widget().operate( + self.tree, + layout.children().next().unwrap(), + operation, + ); + } + + fn mouse_interaction( + &self, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + self.tree, + layout.children().next().unwrap(), + cursor_position, + viewport, + renderer, + ) + } + } + + impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> + where + Renderer: 'a + iced_native::Renderer, + Message: 'a + Clone, + { + fn from(modal: Modal<'a, Message, Renderer>) -> Self { + Element::new(modal) + } + } +} From 899bd45700f0fe7a5dee5fb71400820981f154e0 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 18 Nov 2022 15:00:10 -0800 Subject: [PATCH 28/43] Add on_submit & esc for example --- examples/modal/src/main.rs | 39 +++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 37ac16fd2c..25483cf3bc 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -25,6 +25,7 @@ enum Message { HideModal, Email(String), Password(String), + Submit, Event(Event), } @@ -53,9 +54,7 @@ impl Application for App { widget::focus_next() } Message::HideModal => { - self.show_modal = false; - self.email.clear(); - self.password.clear(); + self.hide_modal(); Command::none() } Message::Email(email) => { @@ -66,21 +65,33 @@ impl Application for App { self.password = password; Command::none() } - Message::Event(event) => { - if let Event::Keyboard(keyboard::Event::KeyPressed { + Message::Submit => { + if !self.email.is_empty() && !self.password.is_empty() { + self.hide_modal(); + } + + Command::none() + } + Message::Event(event) => match event { + Event::Keyboard(keyboard::Event::KeyPressed { key_code: keyboard::KeyCode::Tab, modifiers, - }) = event - { + }) => { if modifiers.shift() { widget::focus_previous() } else { widget::focus_next() } - } else { + } + Event::Keyboard(keyboard::Event::KeyPressed { + key_code: keyboard::KeyCode::Escape, + .. + }) => { + self.hide_modal(); Command::none() } - } + _ => Command::none(), + }, } } @@ -127,12 +138,14 @@ impl Application for App { &self.email, Message::Email ) + .on_submit(Message::Submit) .padding(5), ] .spacing(5), column![ text("Password").size(12), text_input("", &self.password, Message::Password) + .on_submit(Message::Submit) .password() .padding(5), ] @@ -156,6 +169,14 @@ impl Application for App { } } +impl App { + fn hide_modal(&mut self) { + self.show_modal = false; + self.email.clear(); + self.password.clear(); + } +} + mod modal { use iced_native::alignment::Alignment; use iced_native::widget::{self, Tree}; From 74ebcca146d17e4436eaf9a6e1338683be993486 Mon Sep 17 00:00:00 2001 From: Poly Date: Fri, 2 Dec 2022 16:37:55 +0100 Subject: [PATCH 29/43] Fix custom style of PickList --- style/src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/src/theme.rs b/style/src/theme.rs index d7ebb827a2..dde0df5df4 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -559,7 +559,7 @@ impl pick_list::StyleSheet for Theme { icon_size: 0.7, } } - PickList::Custom(custom, _) => custom.active(self), + PickList::Custom(custom, _) => custom.hovered(self), } } } From 6d39aebec61fa50ab7a5f58056adce1b87ac25e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 2 Dec 2022 18:55:10 +0100 Subject: [PATCH 30/43] Fix `border_radius` in `modal` example --- examples/modal/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 25483cf3bc..2f20795c0a 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -412,7 +412,7 @@ mod modal { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), - border_radius: 0.0, + border_radius: renderer::BorderRadius::from(0.0), border_width: 0.0, border_color: Color::TRANSPARENT, }, From 18fd5300854522a091a2bcccb47b65f1d1d8f063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 2 Dec 2022 19:06:42 +0100 Subject: [PATCH 31/43] Add `custom_quad` example Thanks to @rksm :bow: --- examples/custom_quad/Cargo.toml | 10 ++ examples/custom_quad/src/main.rs | 159 +++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 examples/custom_quad/Cargo.toml create mode 100644 examples/custom_quad/src/main.rs diff --git a/examples/custom_quad/Cargo.toml b/examples/custom_quad/Cargo.toml new file mode 100644 index 0000000000..39154786e1 --- /dev/null +++ b/examples/custom_quad/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "custom_quad" +version = "0.1.0" +authors = ["Robert Krahn"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../.." } +iced_native = { path = "../../native" } diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs new file mode 100644 index 0000000000..2a5fe798d5 --- /dev/null +++ b/examples/custom_quad/src/main.rs @@ -0,0 +1,159 @@ +//! This example showcases a drawing a quad. +mod quad { + use iced_native::layout::{self, Layout}; + use iced_native::renderer; + use iced_native::widget::{self, Widget}; + use iced_native::{Color, Element, Length, Point, Rectangle, Size}; + + pub struct CustomQuad { + size: f32, + radius: [f32; 4], + border_width: f32, + } + + impl CustomQuad { + pub fn new(size: f32, radius: [f32; 4], border_width: f32) -> Self { + Self { + size, + radius, + border_width, + } + } + } + + impl Widget for CustomQuad + where + Renderer: renderer::Renderer, + { + fn width(&self) -> Length { + Length::Shrink + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + _renderer: &Renderer, + _limits: &layout::Limits, + ) -> layout::Node { + layout::Node::new(Size::new(self.size, self.size)) + } + + fn draw( + &self, + _state: &widget::Tree, + renderer: &mut Renderer, + _theme: &Renderer::Theme, + _style: &renderer::Style, + layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + renderer.fill_quad( + renderer::Quad { + bounds: layout.bounds(), + border_radius: self.radius.into(), + border_width: self.border_width, + border_color: Color::from_rgb(1.0, 0.0, 0.0), + }, + Color::BLACK, + ); + } + } + + impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> + where + Renderer: renderer::Renderer, + { + fn from(circle: CustomQuad) -> Self { + Self::new(circle) + } + } +} + +use iced::widget::{column, container, slider, text}; +use iced::{Alignment, Element, Length, Sandbox, Settings}; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +struct Example { + radius: [f32; 4], + border_width: f32, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + RadiusTopLeftChanged(f32), + RadiusTopRightChanged(f32), + RadiusBottomRightChanged(f32), + RadiusBottomLeftChanged(f32), + BorderWidthChanged(f32), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Self { + radius: [50.0; 4], + border_width: 0.0, + } + } + + fn title(&self) -> String { + String::from("Custom widget - Iced") + } + + fn update(&mut self, message: Message) { + let [tl, tr, br, bl] = self.radius; + match message { + Message::RadiusTopLeftChanged(radius) => { + self.radius = [radius, tr, br, bl]; + } + Message::RadiusTopRightChanged(radius) => { + self.radius = [tl, radius, br, bl]; + } + Message::RadiusBottomRightChanged(radius) => { + self.radius = [tl, tr, radius, bl]; + } + Message::RadiusBottomLeftChanged(radius) => { + self.radius = [tl, tr, br, radius]; + } + Message::BorderWidthChanged(width) => { + self.border_width = width; + } + } + } + + fn view(&self) -> Element { + let [tl, tr, br, bl] = self.radius; + + let content = column![ + quad::CustomQuad::new(200.0, self.radius, self.border_width), + text(format!("Radius: {tl:.2}/{tr:.2}/{br:.2}/{bl:.2}")), + slider(1.0..=100.0, tl, Message::RadiusTopLeftChanged).step(0.01), + slider(1.0..=100.0, tr, Message::RadiusTopRightChanged).step(0.01), + slider(1.0..=100.0, br, Message::RadiusBottomRightChanged) + .step(0.01), + slider(1.0..=100.0, bl, Message::RadiusBottomLeftChanged) + .step(0.01), + slider(1.0..=10.0, self.border_width, Message::BorderWidthChanged) + .step(0.01), + ] + .padding(20) + .spacing(20) + .max_width(500) + .align_items(Alignment::Center); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} From 60e41666d0e203d9777de981121c39f569bc3a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 2 Dec 2022 19:13:04 +0100 Subject: [PATCH 32/43] Ask `clippy` to behave --- examples/custom_quad/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index 2a5fe798d5..6509887c52 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -86,6 +86,7 @@ struct Example { } #[derive(Debug, Clone, Copy)] +#[allow(clippy::enum_variant_names)] enum Message { RadiusTopLeftChanged(f32), RadiusTopRightChanged(f32), From a2864df098c57fe47bca4c028afcb94e08e804dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 5 Dec 2022 14:44:26 +0100 Subject: [PATCH 33/43] Fix scroller in `Scrollable` always being drawn ... instead of only drawing it when the mouse is over the `Scrollable`. --- native/src/widget/scrollable.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index a5e97aa77e..a5e0e0e30c 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -714,9 +714,8 @@ pub fn draw( ); } - if is_mouse_over - || state.is_scroller_grabbed() - || is_scrollbar_visible + if (is_mouse_over || state.is_scroller_grabbed()) + && is_scrollbar_visible { renderer.fill_quad( renderer::Quad { From 0249640213120e039462f5fc12c677f15ecbc7cc Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 16 Nov 2022 16:35:56 +0100 Subject: [PATCH 34/43] feat: Add Color::into_rgb8 --- core/src/color.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/color.rs b/core/src/color.rs index 212c12148a..e578eda4db 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -89,6 +89,19 @@ impl Color { } } + /// Converts the [`Color`] into its RGBA8 equivalent. + #[must_use] + #[allow(clippy::cast_sign_loss)] + #[allow(clippy::cast_possible_truncation)] + pub fn into_rgba8(self) -> [u8; 4] { + [ + (self.r * 255.0).round() as u8, + (self.g * 255.0).round() as u8, + (self.b * 255.0).round() as u8, + (self.a * 255.0).round() as u8, + ] + } + /// Converts the [`Color`] into its linear values. pub fn into_linear(self) -> [f32; 4] { // As described in: From 75ae0de9bdd3376b6e537bf59030059c926114ee Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 16 Nov 2022 17:42:41 +0100 Subject: [PATCH 35/43] feat: SVG styling with icon fill color --- graphics/src/image/vector.rs | 38 +++++++++++++++++++------- native/src/svg.rs | 14 ++++++++++ native/src/widget/helpers.rs | 8 +++++- native/src/widget/svg.rs | 53 ++++++++++++++++++++++++++++-------- style/src/lib.rs | 1 + style/src/svg.rs | 21 ++++++++++++++ style/src/theme.rs | 24 ++++++++++++++++ 7 files changed, 137 insertions(+), 22 deletions(-) create mode 100644 style/src/svg.rs diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index 42f4b50005..5be5d3c7d1 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -7,6 +7,8 @@ use iced_native::Size; use std::collections::{HashMap, HashSet}; use std::fs; +type Fill = Option<[u8; 4]>; + /// Entry in cache corresponding to an svg handle pub enum Svg { /// Parsed svg @@ -33,9 +35,9 @@ impl Svg { #[derive(Debug)] pub struct Cache { svgs: HashMap, - rasterized: HashMap<(u64, u32, u32), T::Entry>, + rasterized: HashMap<(u64, u32, u32, Fill), T::Entry>, svg_hits: HashSet, - rasterized_hits: HashSet<(u64, u32, u32)>, + rasterized_hits: HashSet<(u64, u32, u32, Fill)>, } impl Cache { @@ -88,15 +90,18 @@ impl Cache { (scale * height).ceil() as u32, ); + let appearance = handle.appearance(); + let fill = appearance.fill.map(crate::Color::into_rgba8); + // TODO: Optimize! // We currently rerasterize the SVG when its size changes. This is slow // as heck. A GPU rasterizer like `pathfinder` may perform better. // It would be cool to be able to smooth resize the `svg` example. - if self.rasterized.contains_key(&(id, width, height)) { + if self.rasterized.contains_key(&(id, width, height, fill)) { let _ = self.svg_hits.insert(id); - let _ = self.rasterized_hits.insert((id, width, height)); + let _ = self.rasterized_hits.insert((id, width, height, fill)); - return self.rasterized.get(&(id, width, height)); + return self.rasterized.get(&(id, width, height, fill)); } match self.load(handle) { @@ -121,15 +126,28 @@ impl Cache { img.as_mut(), )?; - let allocation = - storage.upload(width, height, img.data(), state)?; + let mut rgba = img.take(); + + if let Some(color) = fill { + rgba.chunks_exact_mut(4).for_each(|rgba| { + if rgba[3] > 0 { + rgba[0] = color[0]; + rgba[1] = color[1]; + rgba[2] = color[2]; + } + }); + } + + let allocation = storage.upload(width, height, &rgba, state)?; log::debug!("allocating {} {}x{}", id, width, height); let _ = self.svg_hits.insert(id); - let _ = self.rasterized_hits.insert((id, width, height)); - let _ = self.rasterized.insert((id, width, height), allocation); + let _ = self.rasterized_hits.insert((id, width, height, fill)); + let _ = self + .rasterized + .insert((id, width, height, fill), allocation); - self.rasterized.get(&(id, width, height)) + self.rasterized.get(&(id, width, height, fill)) } Svg::NotFound => None, } diff --git a/native/src/svg.rs b/native/src/svg.rs index a8e481d2f7..08b0984aac 100644 --- a/native/src/svg.rs +++ b/native/src/svg.rs @@ -6,11 +6,14 @@ use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; use std::sync::Arc; +pub use iced_style::svg::{Appearance, StyleSheet}; + /// A handle of Svg data. #[derive(Debug, Clone)] pub struct Handle { id: u64, data: Arc, + appearance: Appearance, } impl Handle { @@ -36,6 +39,7 @@ impl Handle { Handle { id: hasher.finish(), data: Arc::new(data), + appearance: Appearance::default(), } } @@ -48,6 +52,16 @@ impl Handle { pub fn data(&self) -> &Data { &self.data } + + /// Returns the styling [`Appearance`] for the SVG. + pub fn appearance(&self) -> Appearance { + self.appearance + } + + /// Set the [`Appearance`] for the SVG. + pub fn set_appearance(&mut self, appearance: Appearance) { + self.appearance = appearance; + } } impl Hash for Handle { diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index 3bce9e604b..e802f629ab 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -285,6 +285,12 @@ where /// /// [`Svg`]: widget::Svg /// [`Handle`]: widget::svg::Handle -pub fn svg(handle: impl Into) -> widget::Svg { +pub fn svg( + handle: impl Into, +) -> widget::Svg +where + Renderer: crate::svg::Renderer, + Renderer::Theme: crate::svg::StyleSheet, +{ widget::Svg::new(handle) } diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 1015ed0ada..c7eb4f6d0a 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -9,7 +9,7 @@ use crate::{ use std::path::PathBuf; -pub use svg::Handle; +pub use svg::{Handle, StyleSheet}; /// A vector graphics image. /// @@ -17,15 +17,25 @@ pub use svg::Handle; /// /// [`Svg`] images can have a considerable rendering cost when resized, /// specially when they are complex. -#[derive(Debug, Clone)] -pub struct Svg { +#[derive(Clone)] +#[allow(missing_debug_implementations)] +pub struct Svg +where + Renderer: svg::Renderer, + Renderer::Theme: StyleSheet, +{ handle: Handle, width: Length, height: Length, content_fit: ContentFit, + style: ::Style, } -impl Svg { +impl Svg +where + Renderer: svg::Renderer, + Renderer::Theme: StyleSheet, +{ /// Creates a new [`Svg`] from the given [`Handle`]. pub fn new(handle: impl Into) -> Self { Svg { @@ -33,22 +43,26 @@ impl Svg { width: Length::Fill, height: Length::Shrink, content_fit: ContentFit::Contain, + style: Default::default(), } } /// Creates a new [`Svg`] that will display the contents of the file at the /// provided path. + #[must_use] pub fn from_path(path: impl Into) -> Self { Self::new(Handle::from_path(path)) } /// Sets the width of the [`Svg`]. + #[must_use] pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Svg`]. + #[must_use] pub fn height(mut self, height: Length) -> Self { self.height = height; self @@ -57,17 +71,29 @@ impl Svg { /// Sets the [`ContentFit`] of the [`Svg`]. /// /// Defaults to [`ContentFit::Contain`] + #[must_use] pub fn content_fit(self, content_fit: ContentFit) -> Self { Self { content_fit, ..self } } + + /// Sets the style variant of this [`Svg`]. + #[must_use] + pub fn style( + mut self, + style: ::Style, + ) -> Self { + self.style = style; + self + } } -impl Widget for Svg +impl Widget for Svg where Renderer: svg::Renderer, + Renderer::Theme: iced_style::svg::StyleSheet, { fn width(&self) -> Length { self.width @@ -114,12 +140,15 @@ where &self, _state: &Tree, renderer: &mut Renderer, - _theme: &Renderer::Theme, + theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, ) { + let mut handle = self.handle.clone(); + handle.set_appearance(theme.appearance(self.style)); + let Size { width, height } = renderer.dimensions(&self.handle); let image_size = Size::new(width as f32, height as f32); @@ -138,7 +167,7 @@ where ..bounds }; - renderer.draw(self.handle.clone(), drawing_bounds + offset) + renderer.draw(handle, drawing_bounds + offset); }; if adjusted_fit.width > bounds.width @@ -146,16 +175,18 @@ where { renderer.with_layer(bounds, render); } else { - render(renderer) + render(renderer); } } } -impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> where - Renderer: svg::Renderer, + Renderer: svg::Renderer + 'a, + Renderer::Theme: iced_style::svg::StyleSheet, { - fn from(icon: Svg) -> Element<'a, Message, Renderer> { + fn from(icon: Svg) -> Element<'a, Message, Renderer> { Element::new(icon) } } diff --git a/style/src/lib.rs b/style/src/lib.rs index 3242602cd8..59eb1eb8f9 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -32,6 +32,7 @@ pub mod radio; pub mod rule; pub mod scrollable; pub mod slider; +pub mod svg; pub mod text; pub mod text_input; pub mod theme; diff --git a/style/src/svg.rs b/style/src/svg.rs new file mode 100644 index 0000000000..66791d043c --- /dev/null +++ b/style/src/svg.rs @@ -0,0 +1,21 @@ +//! Change the appearance of a svg. + +use iced_core::Color; + +/// The appearance of a svg. +#[derive(Debug, Default, Clone, Copy)] +pub struct Appearance { + /// Changes the fill color + /// + /// Useful for coloring a symbolic icon. + pub fill: Option, +} + +/// The stylesheet of a svg. +pub trait StyleSheet { + /// The supported style of the [`StyleSheet`]. + type Style: Default + Copy; + + /// Produces the [`Appearance`] of the svg. + fn appearance(&self, style: Self::Style) -> Appearance; +} diff --git a/style/src/theme.rs b/style/src/theme.rs index dde0df5df4..d825b08698 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -16,6 +16,7 @@ use crate::radio; use crate::rule; use crate::scrollable; use crate::slider; +use crate::svg; use crate::text; use crate::text_input; use crate::toggler; @@ -797,6 +798,29 @@ impl From rule::Appearance> for Rule { } } +/** + * SVG + */ +#[derive(Default, Clone, Copy)] +pub enum Svg { + /// No filtering to the rendered SVG. + #[default] + Default, + /// Apply custom filtering to the SVG. + Custom(fn(&Theme) -> svg::Appearance), +} + +impl svg::StyleSheet for Theme { + type Style = Svg; + + fn appearance(&self, style: Self::Style) -> svg::Appearance { + match style { + Svg::Default => Default::default(), + Svg::Custom(appearance) => appearance(self), + } + } +} + impl rule::StyleSheet for Theme { type Style = Rule; From 314b0f7dc52c844669c224060a7b03e842762370 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 16 Nov 2022 17:43:16 +0100 Subject: [PATCH 36/43] chore(examples): Add svg-style example --- examples/svg_style/Cargo.toml | 10 +++ .../svg_style/resources/go-next-symbolic.svg | 1 + examples/svg_style/src/main.rs | 78 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 examples/svg_style/Cargo.toml create mode 100644 examples/svg_style/resources/go-next-symbolic.svg create mode 100644 examples/svg_style/src/main.rs diff --git a/examples/svg_style/Cargo.toml b/examples/svg_style/Cargo.toml new file mode 100644 index 0000000000..9ecda7c48d --- /dev/null +++ b/examples/svg_style/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "svg_style" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iced = { path = "../..", features = ["svg"] } +iced_style = { path = "../../style" } \ No newline at end of file diff --git a/examples/svg_style/resources/go-next-symbolic.svg b/examples/svg_style/resources/go-next-symbolic.svg new file mode 100644 index 0000000000..79c456b7e7 --- /dev/null +++ b/examples/svg_style/resources/go-next-symbolic.svg @@ -0,0 +1 @@ + diff --git a/examples/svg_style/src/main.rs b/examples/svg_style/src/main.rs new file mode 100644 index 0000000000..905e1d8680 --- /dev/null +++ b/examples/svg_style/src/main.rs @@ -0,0 +1,78 @@ +use iced::widget::{container, svg}; +use iced::{Color, Element, Length, Sandbox, Settings}; +use iced_style::svg::Appearance; +use iced_style::theme::{self, Theme}; + +pub fn main() -> iced::Result { + SvgStyleExample::run(Settings::default()) +} + +struct SvgStyleExample; + +impl Sandbox for SvgStyleExample { + type Message = (); + + fn new() -> Self { + SvgStyleExample + } + + fn theme(&self) -> Theme { + Theme::Light + } + + fn title(&self) -> String { + String::from("SVG - Iced") + } + + fn update(&mut self, _message: ()) {} + + fn view(&self) -> Element<()> { + let svg1: Element<_> = svg(svg::Handle::from_path(format!( + "{}/resources/go-next-symbolic.svg", + env!("CARGO_MANIFEST_DIR") + ))) + .width(Length::Fill) + .height(Length::Fill) + .into(); + + let svg2: Element<_> = svg(svg::Handle::from_path(format!( + "{}/resources/go-next-symbolic.svg", + env!("CARGO_MANIFEST_DIR") + ))) + .style(theme::Svg::Custom(|_theme| Appearance { + fill: Some(Color { + r: 0.0, + g: 0.28627452, + b: 0.42745098, + a: 1.0, + }), + })) + .width(Length::Fill) + .height(Length::Fill) + .into(); + + let svg3: Element<_> = svg(svg::Handle::from_path(format!( + "{}/resources/go-next-symbolic.svg", + env!("CARGO_MANIFEST_DIR") + ))) + .style(theme::Svg::Custom(|_theme| Appearance { + fill: Some(Color { + r: 0.5803922, + g: 0.92156863, + b: 0.92156863, + a: 1.0, + }), + })) + .width(Length::Fill) + .height(Length::Fill) + .into(); + + container(iced::widget::row!(svg1, svg2, svg3)) + .width(Length::Fill) + .height(Length::Fill) + .padding(20) + .center_x() + .center_y() + .into() + } +} From b205a663471a8170d7b30cc59894425c09bea563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 6 Dec 2022 04:34:00 +0100 Subject: [PATCH 37/43] Remove `appearance` from `Handle` ... and pass it directly to `Renderer::draw` instead. --- examples/svg_style/src/main.rs | 8 ++--- glow/src/image.rs | 7 +++- graphics/src/image/vector.rs | 22 ++++++------ graphics/src/layer.rs | 7 +++- graphics/src/layer/image.rs | 6 +++- graphics/src/primitive.rs | 3 ++ graphics/src/renderer.rs | 15 +++++++-- native/src/svg.rs | 20 ++--------- native/src/widget/helpers.rs | 2 +- native/src/widget/svg.rs | 15 +++++---- src/widget.rs | 2 +- style/src/svg.rs | 12 ++++--- style/src/theme.rs | 61 +++++++++++++++++++++------------- wgpu/src/image.rs | 7 +++- 14 files changed, 112 insertions(+), 75 deletions(-) diff --git a/examples/svg_style/src/main.rs b/examples/svg_style/src/main.rs index 905e1d8680..0a1fa039c5 100644 --- a/examples/svg_style/src/main.rs +++ b/examples/svg_style/src/main.rs @@ -39,8 +39,8 @@ impl Sandbox for SvgStyleExample { "{}/resources/go-next-symbolic.svg", env!("CARGO_MANIFEST_DIR") ))) - .style(theme::Svg::Custom(|_theme| Appearance { - fill: Some(Color { + .style(theme::Svg::custom_fn(|_theme| Appearance { + color: Some(Color { r: 0.0, g: 0.28627452, b: 0.42745098, @@ -55,8 +55,8 @@ impl Sandbox for SvgStyleExample { "{}/resources/go-next-symbolic.svg", env!("CARGO_MANIFEST_DIR") ))) - .style(theme::Svg::Custom(|_theme| Appearance { - fill: Some(Color { + .style(theme::Svg::custom_fn(|_theme| Appearance { + color: Some(Color { r: 0.5803922, g: 0.92156863, b: 0.92156863, diff --git a/glow/src/image.rs b/glow/src/image.rs index 955fd1abfb..c32b216291 100644 --- a/glow/src/image.rs +++ b/glow/src/image.rs @@ -172,11 +172,16 @@ impl Pipeline { layer::Image::Raster { handle: _, bounds } => (None, bounds), #[cfg(feature = "svg")] - layer::Image::Vector { handle, bounds } => { + layer::Image::Vector { + handle, + color, + bounds, + } => { let size = [bounds.width, bounds.height]; ( vector_cache.upload( handle, + *color, size, _scale_factor, &mut gl, diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index 5be5d3c7d1..0af5be01bb 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -1,5 +1,6 @@ //! Vector image loading and caching use crate::image::Storage; +use crate::Color; use iced_native::svg; use iced_native::Size; @@ -78,6 +79,7 @@ impl Cache { pub fn upload( &mut self, handle: &svg::Handle, + color: Option, [width, height]: [f32; 2], scale: f32, state: &mut T::State<'_>, @@ -90,18 +92,18 @@ impl Cache { (scale * height).ceil() as u32, ); - let appearance = handle.appearance(); - let fill = appearance.fill.map(crate::Color::into_rgba8); + let color = color.map(Color::into_rgba8); + let key = (id, width, height, color); // TODO: Optimize! // We currently rerasterize the SVG when its size changes. This is slow // as heck. A GPU rasterizer like `pathfinder` may perform better. // It would be cool to be able to smooth resize the `svg` example. - if self.rasterized.contains_key(&(id, width, height, fill)) { + if self.rasterized.contains_key(&key) { let _ = self.svg_hits.insert(id); - let _ = self.rasterized_hits.insert((id, width, height, fill)); + let _ = self.rasterized_hits.insert(key); - return self.rasterized.get(&(id, width, height, fill)); + return self.rasterized.get(&key); } match self.load(handle) { @@ -128,7 +130,7 @@ impl Cache { let mut rgba = img.take(); - if let Some(color) = fill { + if let Some(color) = color { rgba.chunks_exact_mut(4).for_each(|rgba| { if rgba[3] > 0 { rgba[0] = color[0]; @@ -142,12 +144,10 @@ impl Cache { log::debug!("allocating {} {}x{}", id, width, height); let _ = self.svg_hits.insert(id); - let _ = self.rasterized_hits.insert((id, width, height, fill)); - let _ = self - .rasterized - .insert((id, width, height, fill), allocation); + let _ = self.rasterized_hits.insert(key); + let _ = self.rasterized.insert(key, allocation); - self.rasterized.get(&(id, width, height, fill)) + self.rasterized.get(&key) } Svg::NotFound => None, } diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index fd670f4833..1d453caa39 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -251,11 +251,16 @@ impl<'a> Layer<'a> { bounds: *bounds + translation, }); } - Primitive::Svg { handle, bounds } => { + Primitive::Svg { + handle, + color, + bounds, + } => { let layer = &mut layers[current_layer]; layer.images.push(Image::Vector { handle: handle.clone(), + color: *color, bounds: *bounds + translation, }); } diff --git a/graphics/src/layer/image.rs b/graphics/src/layer/image.rs index 045ec665f4..3eff239779 100644 --- a/graphics/src/layer/image.rs +++ b/graphics/src/layer/image.rs @@ -1,4 +1,5 @@ -use crate::Rectangle; +use crate::{Color, Rectangle}; + use iced_native::{image, svg}; /// A raster or vector image. @@ -17,6 +18,9 @@ pub enum Image { /// The handle of a vector image. handle: svg::Handle, + /// The [`Color`] filter + color: Option, + /// The bounds of the image. bounds: Rectangle, }, diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 6f1b6f26fc..5a163a2f5f 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -60,6 +60,9 @@ pub enum Primitive { /// The path of the SVG file handle: svg::Handle, + /// The [`Color`] filter + color: Option, + /// The bounds of the viewport bounds: Rectangle, }, diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 65350037b6..aabdf7fc45 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -6,7 +6,7 @@ use iced_native::layout; use iced_native::renderer; use iced_native::svg; use iced_native::text::{self, Text}; -use iced_native::{Background, Element, Font, Point, Rectangle, Size}; +use iced_native::{Background, Color, Element, Font, Point, Rectangle, Size}; pub use iced_native::renderer::Style; @@ -200,7 +200,16 @@ where self.backend().viewport_dimensions(handle) } - fn draw(&mut self, handle: svg::Handle, bounds: Rectangle) { - self.draw_primitive(Primitive::Svg { handle, bounds }) + fn draw( + &mut self, + handle: svg::Handle, + color: Option, + bounds: Rectangle, + ) { + self.draw_primitive(Primitive::Svg { + handle, + color, + bounds, + }) } } diff --git a/native/src/svg.rs b/native/src/svg.rs index 08b0984aac..2168e40937 100644 --- a/native/src/svg.rs +++ b/native/src/svg.rs @@ -1,19 +1,16 @@ //! Load and draw vector graphics. -use crate::{Hasher, Rectangle, Size}; +use crate::{Color, Hasher, Rectangle, Size}; use std::borrow::Cow; use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; use std::sync::Arc; -pub use iced_style::svg::{Appearance, StyleSheet}; - /// A handle of Svg data. #[derive(Debug, Clone)] pub struct Handle { id: u64, data: Arc, - appearance: Appearance, } impl Handle { @@ -39,7 +36,6 @@ impl Handle { Handle { id: hasher.finish(), data: Arc::new(data), - appearance: Appearance::default(), } } @@ -52,16 +48,6 @@ impl Handle { pub fn data(&self) -> &Data { &self.data } - - /// Returns the styling [`Appearance`] for the SVG. - pub fn appearance(&self) -> Appearance { - self.appearance - } - - /// Set the [`Appearance`] for the SVG. - pub fn set_appearance(&mut self, appearance: Appearance) { - self.appearance = appearance; - } } impl Hash for Handle { @@ -98,6 +84,6 @@ pub trait Renderer: crate::Renderer { /// Returns the default dimensions of an SVG for the given [`Handle`]. fn dimensions(&self, handle: &Handle) -> Size; - /// Draws an SVG with the given [`Handle`] and inside the provided `bounds`. - fn draw(&mut self, handle: Handle, bounds: Rectangle); + /// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`. + fn draw(&mut self, handle: Handle, color: Option, bounds: Rectangle); } diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index e802f629ab..0bde288fca 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -290,7 +290,7 @@ pub fn svg( ) -> widget::Svg where Renderer: crate::svg::Renderer, - Renderer::Theme: crate::svg::StyleSheet, + Renderer::Theme: widget::svg::StyleSheet, { widget::Svg::new(handle) } diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index c7eb4f6d0a..f83f5acfe4 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -9,7 +9,8 @@ use crate::{ use std::path::PathBuf; -pub use svg::{Handle, StyleSheet}; +pub use iced_style::svg::{Appearance, StyleSheet}; +pub use svg::Handle; /// A vector graphics image. /// @@ -17,7 +18,6 @@ pub use svg::{Handle, StyleSheet}; /// /// [`Svg`] images can have a considerable rendering cost when resized, /// specially when they are complex. -#[derive(Clone)] #[allow(missing_debug_implementations)] pub struct Svg where @@ -146,9 +146,6 @@ where _cursor_position: Point, _viewport: &Rectangle, ) { - let mut handle = self.handle.clone(); - handle.set_appearance(theme.appearance(self.style)); - let Size { width, height } = renderer.dimensions(&self.handle); let image_size = Size::new(width as f32, height as f32); @@ -167,7 +164,13 @@ where ..bounds }; - renderer.draw(handle, drawing_bounds + offset); + let appearance = theme.appearance(&self.style); + + renderer.draw( + self.handle.clone(), + appearance.color, + drawing_bounds + offset, + ); }; if adjusted_fit.width > bounds.width diff --git a/src/widget.rs b/src/widget.rs index 7c67a59963..e7df6e4e1f 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -194,7 +194,7 @@ pub use iced_graphics::widget::qr_code; pub mod svg { //! Display vector graphics in your application. pub use iced_native::svg::Handle; - pub use iced_native::widget::Svg; + pub use iced_native::widget::svg::{Appearance, StyleSheet, Svg}; } #[cfg(feature = "canvas")] diff --git a/style/src/svg.rs b/style/src/svg.rs index 66791d043c..9378c1a728 100644 --- a/style/src/svg.rs +++ b/style/src/svg.rs @@ -2,20 +2,22 @@ use iced_core::Color; -/// The appearance of a svg. +/// The appearance of an SVG. #[derive(Debug, Default, Clone, Copy)] pub struct Appearance { - /// Changes the fill color + /// The [`Color`] filter of an SVG. /// /// Useful for coloring a symbolic icon. - pub fill: Option, + /// + /// `None` keeps the original color. + pub color: Option, } /// The stylesheet of a svg. pub trait StyleSheet { /// The supported style of the [`StyleSheet`]. - type Style: Default + Copy; + type Style: Default; /// Produces the [`Appearance`] of the svg. - fn appearance(&self, style: Self::Style) -> Appearance; + fn appearance(&self, style: &Self::Style) -> Appearance; } diff --git a/style/src/theme.rs b/style/src/theme.rs index d825b08698..d2b583ed53 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -798,29 +798,6 @@ impl From rule::Appearance> for Rule { } } -/** - * SVG - */ -#[derive(Default, Clone, Copy)] -pub enum Svg { - /// No filtering to the rendered SVG. - #[default] - Default, - /// Apply custom filtering to the SVG. - Custom(fn(&Theme) -> svg::Appearance), -} - -impl svg::StyleSheet for Theme { - type Style = Svg; - - fn appearance(&self, style: Self::Style) -> svg::Appearance { - match style { - Svg::Default => Default::default(), - Svg::Custom(appearance) => appearance(self), - } - } -} - impl rule::StyleSheet for Theme { type Style = Rule; @@ -847,6 +824,44 @@ impl rule::StyleSheet for fn(&Theme) -> rule::Appearance { } } +/** + * SVG + */ +#[derive(Default)] +pub enum Svg { + /// No filtering to the rendered SVG. + #[default] + Default, + /// A custom style. + Custom(Box>), +} + +impl Svg { + /// Creates a custom [`Svg`] style. + pub fn custom_fn(f: fn(&Theme) -> svg::Appearance) -> Self { + Self::Custom(Box::new(f)) + } +} + +impl svg::StyleSheet for Theme { + type Style = Svg; + + fn appearance(&self, style: &Self::Style) -> svg::Appearance { + match style { + Svg::Default => Default::default(), + Svg::Custom(custom) => custom.appearance(self), + } + } +} + +impl svg::StyleSheet for fn(&Theme) -> svg::Appearance { + type Style = Theme; + + fn appearance(&self, style: &Self::Style) -> svg::Appearance { + (self)(style) + } +} + /// The style of a scrollable. #[derive(Default)] pub enum Scrollable { diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index d06815bb9d..390bad90df 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -318,11 +318,16 @@ impl Pipeline { layer::Image::Raster { .. } => {} #[cfg(feature = "svg")] - layer::Image::Vector { handle, bounds } => { + layer::Image::Vector { + handle, + color, + bounds, + } => { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = vector_cache.upload( handle, + *color, size, _scale, &mut (device, encoder), From c0ca1807d42b0ec58887df9926ff53a587104723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 6 Dec 2022 04:34:42 +0100 Subject: [PATCH 38/43] Fix macro hygiene of `color!` --- core/src/color.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index e578eda4db..2a4d43f404 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -161,24 +161,26 @@ impl From<[f32; 4]> for Color { #[macro_export] macro_rules! color { ($r:expr, $g:expr, $b:expr) => { - Color::from_rgb8($r, $g, $b) + $crate::Color::from_rgb8($r, $g, $b) }; ($r:expr, $g:expr, $b:expr, $a:expr) => { - Color::from_rgba8($r, $g, $b, $a) + $crate::Color::from_rgba8($r, $g, $b, $a) }; ($hex:expr) => {{ let hex = $hex as u32; let r = (hex & 0xff0000) >> 16; let g = (hex & 0xff00) >> 8; let b = (hex & 0xff); - Color::from_rgb8(r as u8, g as u8, b as u8) + + $crate::Color::from_rgb8(r as u8, g as u8, b as u8) }}; ($hex:expr, $a:expr) => {{ let hex = $hex as u32; let r = (hex & 0xff0000) >> 16; let g = (hex & 0xff00) >> 8; let b = (hex & 0xff); - Color::from_rgba8(r as u8, g as u8, b as u8, $a) + + $crate::Color::from_rgba8(r as u8, g as u8, b as u8, $a) }}; } From 1220ce55bc882cc4fef891c8b2d0fdc22e18a015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 6 Dec 2022 04:35:08 +0100 Subject: [PATCH 39/43] Showcase color filtering in existing `svg` example ... and remove the `svg_style` example --- examples/svg/src/main.rs | 71 +++++++++++++---- examples/svg_style/Cargo.toml | 10 --- .../svg_style/resources/go-next-symbolic.svg | 1 - examples/svg_style/src/main.rs | 78 ------------------- 4 files changed, 54 insertions(+), 106 deletions(-) delete mode 100644 examples/svg_style/Cargo.toml delete mode 100644 examples/svg_style/resources/go-next-symbolic.svg delete mode 100644 examples/svg_style/src/main.rs diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index 27d175da7d..4dc92416f1 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -1,39 +1,76 @@ -use iced::widget::{container, svg}; -use iced::{Element, Length, Sandbox, Settings}; +use iced::theme; +use iced::widget::{checkbox, column, container, svg}; +use iced::{color, Element, Length, Sandbox, Settings}; pub fn main() -> iced::Result { Tiger::run(Settings::default()) } -struct Tiger; +#[derive(Debug, Default)] +struct Tiger { + apply_color_filter: bool, +} + +#[derive(Debug, Clone, Copy)] +pub enum Message { + ToggleColorFilter(bool), +} impl Sandbox for Tiger { - type Message = (); + type Message = Message; fn new() -> Self { - Tiger + Tiger::default() } fn title(&self) -> String { String::from("SVG - Iced") } - fn update(&mut self, _message: ()) {} + fn update(&mut self, message: Self::Message) { + match message { + Message::ToggleColorFilter(apply_color_filter) => { + self.apply_color_filter = apply_color_filter; + } + } + } - fn view(&self) -> Element<()> { - let svg = svg(svg::Handle::from_path(format!( + fn view(&self) -> Element { + let handle = svg::Handle::from_path(format!( "{}/resources/tiger.svg", env!("CARGO_MANIFEST_DIR") - ))) - .width(Length::Fill) - .height(Length::Fill); + )); + + let svg = svg(handle).width(Length::Fill).height(Length::Fill).style( + if self.apply_color_filter { + theme::Svg::custom_fn(|_theme| svg::Appearance { + color: Some(color!(0x0000ff)), + }) + } else { + theme::Svg::Default + }, + ); - container(svg) + let apply_color_filter = checkbox( + "Apply a color filter", + self.apply_color_filter, + Message::ToggleColorFilter, + ); + + container( + column![ + svg, + container(apply_color_filter).width(Length::Fill).center_x() + ] + .spacing(20) .width(Length::Fill) - .height(Length::Fill) - .padding(20) - .center_x() - .center_y() - .into() + .height(Length::Fill), + ) + .width(Length::Fill) + .height(Length::Fill) + .padding(20) + .center_x() + .center_y() + .into() } } diff --git a/examples/svg_style/Cargo.toml b/examples/svg_style/Cargo.toml deleted file mode 100644 index 9ecda7c48d..0000000000 --- a/examples/svg_style/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "svg_style" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -iced = { path = "../..", features = ["svg"] } -iced_style = { path = "../../style" } \ No newline at end of file diff --git a/examples/svg_style/resources/go-next-symbolic.svg b/examples/svg_style/resources/go-next-symbolic.svg deleted file mode 100644 index 79c456b7e7..0000000000 --- a/examples/svg_style/resources/go-next-symbolic.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/svg_style/src/main.rs b/examples/svg_style/src/main.rs deleted file mode 100644 index 0a1fa039c5..0000000000 --- a/examples/svg_style/src/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -use iced::widget::{container, svg}; -use iced::{Color, Element, Length, Sandbox, Settings}; -use iced_style::svg::Appearance; -use iced_style::theme::{self, Theme}; - -pub fn main() -> iced::Result { - SvgStyleExample::run(Settings::default()) -} - -struct SvgStyleExample; - -impl Sandbox for SvgStyleExample { - type Message = (); - - fn new() -> Self { - SvgStyleExample - } - - fn theme(&self) -> Theme { - Theme::Light - } - - fn title(&self) -> String { - String::from("SVG - Iced") - } - - fn update(&mut self, _message: ()) {} - - fn view(&self) -> Element<()> { - let svg1: Element<_> = svg(svg::Handle::from_path(format!( - "{}/resources/go-next-symbolic.svg", - env!("CARGO_MANIFEST_DIR") - ))) - .width(Length::Fill) - .height(Length::Fill) - .into(); - - let svg2: Element<_> = svg(svg::Handle::from_path(format!( - "{}/resources/go-next-symbolic.svg", - env!("CARGO_MANIFEST_DIR") - ))) - .style(theme::Svg::custom_fn(|_theme| Appearance { - color: Some(Color { - r: 0.0, - g: 0.28627452, - b: 0.42745098, - a: 1.0, - }), - })) - .width(Length::Fill) - .height(Length::Fill) - .into(); - - let svg3: Element<_> = svg(svg::Handle::from_path(format!( - "{}/resources/go-next-symbolic.svg", - env!("CARGO_MANIFEST_DIR") - ))) - .style(theme::Svg::custom_fn(|_theme| Appearance { - color: Some(Color { - r: 0.5803922, - g: 0.92156863, - b: 0.92156863, - a: 1.0, - }), - })) - .width(Length::Fill) - .height(Length::Fill) - .into(); - - container(iced::widget::row!(svg1, svg2, svg3)) - .width(Length::Fill) - .height(Length::Fill) - .padding(20) - .center_x() - .center_y() - .into() - } -} From 2d58a2c03325a77893461194bd601b312a0097ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 6 Dec 2022 04:40:32 +0100 Subject: [PATCH 40/43] Remove unnecessary `clippy` directives in `Color` --- core/src/color.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 2a4d43f404..fe0a18569b 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -91,8 +91,6 @@ impl Color { /// Converts the [`Color`] into its RGBA8 equivalent. #[must_use] - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_possible_truncation)] pub fn into_rgba8(self) -> [u8; 4] { [ (self.r * 255.0).round() as u8, From a2f71f42ba300cec5490050a471efeebf627d534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 6 Dec 2022 04:42:25 +0100 Subject: [PATCH 41/43] Rename `Fill` to `ColorFilter` in `graphics::image::vector` --- graphics/src/image/vector.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index 0af5be01bb..82d77aff64 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -8,8 +8,6 @@ use iced_native::Size; use std::collections::{HashMap, HashSet}; use std::fs; -type Fill = Option<[u8; 4]>; - /// Entry in cache corresponding to an svg handle pub enum Svg { /// Parsed svg @@ -36,11 +34,13 @@ impl Svg { #[derive(Debug)] pub struct Cache { svgs: HashMap, - rasterized: HashMap<(u64, u32, u32, Fill), T::Entry>, + rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>, svg_hits: HashSet, - rasterized_hits: HashSet<(u64, u32, u32, Fill)>, + rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>, } +type ColorFilter = Option<[u8; 4]>; + impl Cache { /// Load svg pub fn load(&mut self, handle: &svg::Handle) -> &Svg { From f99d24e0850b63194b7976ec66d547ea2ff6bfc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 6 Dec 2022 04:44:37 +0100 Subject: [PATCH 42/43] Fix casing in `theme` --- style/src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/src/theme.rs b/style/src/theme.rs index d2b583ed53..271d9a29f2 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -825,7 +825,7 @@ impl rule::StyleSheet for fn(&Theme) -> rule::Appearance { } /** - * SVG + * Svg */ #[derive(Default)] pub enum Svg { From 4c61f12768cdbe728b1dd4a074e36fb6a69534ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 7 Dec 2022 04:38:00 +0100 Subject: [PATCH 43/43] Bump versions :tada: --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ Cargo.toml | 16 ++++++++-------- README.md | 4 ++-- core/Cargo.toml | 2 +- core/src/lib.rs | 2 +- futures/src/subscription.rs | 6 +++--- glow/Cargo.toml | 6 +++--- glow/src/lib.rs | 2 +- glutin/Cargo.toml | 8 ++++---- graphics/Cargo.toml | 4 ++-- lazy/Cargo.toml | 4 ++-- native/Cargo.toml | 4 ++-- native/src/lib.rs | 4 ++-- native/src/subscription.rs | 2 +- native/src/user_interface.rs | 4 ++-- native/src/widget.rs | 10 +++++----- native/src/widget/pane_grid.rs | 2 +- src/application.rs | 18 +++++++++--------- src/lib.rs | 6 +++--- src/sandbox.rs | 22 +++++++++++----------- src/widget.rs | 2 +- style/Cargo.toml | 2 +- wgpu/Cargo.toml | 6 +++--- wgpu/src/lib.rs | 2 +- winit/Cargo.toml | 6 +++--- winit/src/conversion.rs | 12 ++++++------ winit/src/lib.rs | 2 +- 27 files changed, 112 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d098e8ecef..907fced4da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.6.0] - 2022-12-07 +### Added +- Support for non-uniform border radius for `Primitive::Quad`. [#1506](https://github.com/iced-rs/iced/pull/1506) +- Operation to query the current focused widget. [#1526](https://github.com/iced-rs/iced/pull/1526) +- Additional operations for `TextInput`. [#1529](https://github.com/iced-rs/iced/pull/1529) +- Styling support for `Svg`. [#1578](https://github.com/iced-rs/iced/pull/1578) + +### Changed +- Triangle geometry using a solid color is now drawn in a single draw call. [#1538](https://github.com/iced-rs/iced/pull/1538) + +### Fixed +- Gradients for WebAssembly target. [#1524](https://github.com/iced-rs/iced/pull/1524) +- `Overlay` layout cache not being invalidated. [#1528](https://github.com/iced-rs/iced/pull/1528) +- Operations not working for `PaneGrid`. [#1533](https://github.com/iced-rs/iced/pull/1533) +- Mapped `widget::Operation` always returning `Outcome::None`. [#1536](https://github.com/iced-rs/iced/pull/1536) +- Padding of `TextInput` with `Length::Units` width. [#1539](https://github.com/iced-rs/iced/pull/1539) +- Clipping of `Image` and `Svg` widgets in `iced_glow`. [#1557](https://github.com/iced-rs/iced/pull/1557) +- Invalid links in documentation. [#1560](https://github.com/iced-rs/iced/pull/1560) +- `Custom` style of `PickList` widget. [#1570](https://github.com/iced-rs/iced/pull/1570) +- Scroller in `Scrollable` always being drawn. [#1574](https://github.com/iced-rs/iced/pull/1574) + +Many thanks to... + +- @bungoboingo +- @l1Dan +- @mmstick +- @mtkennerly +- @PolyMeilex +- @rksm +- @rs017991 +- @tarkah +- @wash2 + ## [0.5.0] - 2022-11-10 ### Added - __[Stabilization of stateless widgets][stateless]__ (#1393) diff --git a/Cargo.toml b/Cargo.toml index 5b76b22d96..9c45b2f588 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced" -version = "0.5.2" +version = "0.6.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A cross-platform GUI library inspired by Elm" @@ -61,11 +61,11 @@ members = [ [dependencies] iced_core = { version = "0.6", path = "core" } iced_futures = { version = "0.5", path = "futures" } -iced_native = { version = "0.6", path = "native" } -iced_graphics = { version = "0.4", path = "graphics" } -iced_winit = { version = "0.5", path = "winit", features = ["application"] } -iced_glutin = { version = "0.4", path = "glutin", optional = true } -iced_glow = { version = "0.4", path = "glow", optional = true } +iced_native = { version = "0.7", path = "native" } +iced_graphics = { version = "0.5", path = "graphics" } +iced_winit = { version = "0.6", path = "winit", features = ["application"] } +iced_glutin = { version = "0.5", path = "glutin", optional = true } +iced_glow = { version = "0.5", path = "glow", optional = true } thiserror = "1.0" [dependencies.image_rs] @@ -74,10 +74,10 @@ package = "image" optional = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -iced_wgpu = { version = "0.6", path = "wgpu", optional = true } +iced_wgpu = { version = "0.7", path = "wgpu", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -iced_wgpu = { version = "0.6", path = "wgpu", features = ["webgl"], optional = true } +iced_wgpu = { version = "0.7", path = "wgpu", features = ["webgl"], optional = true } [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/README.md b/README.md index 1d2fb76f20..bc7f5440ea 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ __Iced is currently experimental software.__ [Take a look at the roadmap], Add `iced` as a dependency in your `Cargo.toml`: ```toml -iced = "0.5" +iced = "0.6" ``` If your project is using a Rust edition older than 2021, then you will need to @@ -215,7 +215,7 @@ cargo run --features iced/glow --package game_of_life and then use it in your project with ```toml -iced = { version = "0.5", default-features = false, features = ["glow"] } +iced = { version = "0.6", default-features = false, features = ["glow"] } ``` __NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0, diff --git a/core/Cargo.toml b/core/Cargo.toml index 6fd0a38cdd..c401f30a38 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_core" -version = "0.6.1" +version = "0.6.2" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "The essential concepts of Iced" diff --git a/core/src/lib.rs b/core/src/lib.rs index 383b4f7372..f95d61f642 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,7 +7,7 @@ //! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true) //! //! [Iced]: https://github.com/iced-rs/iced -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`iced_web`]: https://github.com/iced-rs/iced_web #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index 30cf758137..1d4b68a632 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -125,9 +125,9 @@ impl std::fmt::Debug for Subscription { /// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how /// to listen to time. /// -/// [examples]: https://github.com/iced-rs/iced/tree/0.5/examples -/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.5/examples/download_progress -/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.5/examples/stopwatch +/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples +/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.6/examples/download_progress +/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.6/examples/stopwatch pub trait Recipe { /// The events that will be produced by a [`Subscription`] with this /// [`Recipe`]. diff --git a/glow/Cargo.toml b/glow/Cargo.toml index 1977f4b61e..f69ce48a73 100644 --- a/glow/Cargo.toml +++ b/glow/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_glow" -version = "0.4.1" +version = "0.5.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A glow renderer for iced" @@ -34,11 +34,11 @@ bytemuck = "1.4" log = "0.4" [dependencies.iced_native] -version = "0.6" +version = "0.7" path = "../native" [dependencies.iced_graphics] -version = "0.4" +version = "0.5" path = "../graphics" features = ["font-fallback", "font-icons", "opengl"] diff --git a/glow/src/lib.rs b/glow/src/lib.rs index e6ca056202..710ac36d8d 100644 --- a/glow/src/lib.rs +++ b/glow/src/lib.rs @@ -3,7 +3,7 @@ //! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true) //! //! [`glow`]: https://github.com/grovesNL/glow -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml index 730ee6f748..022457b186 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_glutin" -version = "0.4.0" +version = "0.5.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A glutin runtime for Iced" @@ -23,15 +23,15 @@ git = "https://github.com/iced-rs/glutin" rev = "da8d291486b4c9bec12487a46c119c4b1d386abf" [dependencies.iced_native] -version = "0.6" +version = "0.7" path = "../native" [dependencies.iced_winit] -version = "0.5" +version = "0.6" path = "../winit" features = ["application"] [dependencies.iced_graphics] -version = "0.4" +version = "0.5" path = "../graphics" features = ["opengl"] diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index b601f37c1b..823a05f436 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_graphics" -version = "0.4.0" +version = "0.5.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced" @@ -44,7 +44,7 @@ version = "1.4" features = ["derive"] [dependencies.iced_native] -version = "0.6" +version = "0.7" path = "../native" [dependencies.iced_style] diff --git a/lazy/Cargo.toml b/lazy/Cargo.toml index 51459148d9..1b26e5c9c2 100644 --- a/lazy/Cargo.toml +++ b/lazy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_lazy" -version = "0.2.0" +version = "0.3.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "Lazy widgets for Iced" @@ -14,5 +14,5 @@ categories = ["gui"] ouroboros = "0.13" [dependencies.iced_native] -version = "0.6" +version = "0.7" path = "../native" diff --git a/native/Cargo.toml b/native/Cargo.toml index 558909bed6..bbf9295122 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_native" -version = "0.6.1" +version = "0.7.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A renderer-agnostic library for native GUIs" @@ -25,5 +25,5 @@ path = "../futures" features = ["thread-pool"] [dependencies.iced_style] -version = "0.5" +version = "0.5.1" path = "../style" diff --git a/native/src/lib.rs b/native/src/lib.rs index 62b8f372bb..ce7c010d4d 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -23,8 +23,8 @@ //! - Build a new renderer, see the [renderer] module. //! - Build a custom widget, start at the [`Widget`] trait. //! -//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.5/core -//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.5/winit +//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.6/core +//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.6/winit //! [`druid`]: https://github.com/xi-editor/druid //! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle //! [renderer]: crate::renderer diff --git a/native/src/subscription.rs b/native/src/subscription.rs index d24801d4ca..c60b12819a 100644 --- a/native/src/subscription.rs +++ b/native/src/subscription.rs @@ -155,7 +155,7 @@ where /// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket /// connection open. /// -/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.5/examples/websocket +/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.6/examples/websocket pub fn unfold( id: I, initial: T, diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 6b853da826..376ce568f8 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -18,8 +18,8 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; /// The [`integration_opengl`] & [`integration_wgpu`] examples use a /// [`UserInterface`] to integrate Iced in an existing graphical application. /// -/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_opengl -/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_wgpu +/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_opengl +/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_wgpu #[allow(missing_debug_implementations)] pub struct UserInterface<'a, Message, Renderer> { root: Element<'a, Message, Renderer>, diff --git a/native/src/widget.rs b/native/src/widget.rs index 36d679a499..a4b46ed431 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -107,12 +107,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell}; /// - [`geometry`], a custom widget showcasing how to draw geometry with the /// `Mesh2D` primitive in [`iced_wgpu`]. /// -/// [examples]: https://github.com/iced-rs/iced/tree/0.5/examples -/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.5/examples/bezier_tool -/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.5/examples/custom_widget -/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.5/examples/geometry +/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples +/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool +/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget +/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry /// [`lyon`]: https://github.com/nical/lyon -/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/wgpu +/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu pub trait Widget where Renderer: crate::Renderer, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index edad993ee1..5de95c651f 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -6,7 +6,7 @@ //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! drag and drop, and hotkey support. //! -//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid +//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid mod axis; mod configuration; mod content; diff --git a/src/application.rs b/src/application.rs index 0f514f66b0..9d6c47e281 100644 --- a/src/application.rs +++ b/src/application.rs @@ -39,15 +39,15 @@ pub use iced_native::application::{Appearance, StyleSheet}; /// to listen to time. /// - [`todos`], a todos tracker inspired by [TodoMVC]. /// -/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.5/examples -/// [`clock`]: https://github.com/iced-rs/iced/tree/0.5/examples/clock -/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.5/examples/download_progress -/// [`events`]: https://github.com/iced-rs/iced/tree/0.5/examples/events -/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.5/examples/game_of_life -/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.5/examples/pokedex -/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.5/examples/solar_system -/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.5/examples/stopwatch -/// [`todos`]: https://github.com/iced-rs/iced/tree/0.5/examples/todos +/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.6/examples +/// [`clock`]: https://github.com/iced-rs/iced/tree/0.6/examples/clock +/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.6/examples/download_progress +/// [`events`]: https://github.com/iced-rs/iced/tree/0.6/examples/events +/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.6/examples/game_of_life +/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.6/examples/pokedex +/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.6/examples/solar_system +/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.6/examples/stopwatch +/// [`todos`]: https://github.com/iced-rs/iced/tree/0.6/examples/todos /// [`Sandbox`]: crate::Sandbox /// [`Canvas`]: crate::widget::Canvas /// [PokéAPI]: https://pokeapi.co/ diff --git a/src/lib.rs b/src/lib.rs index 4d0484a78e..001768272b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,11 +26,11 @@ //! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md //! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/0.4/master/native //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs -//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.5/wgpu -//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.5/winit +//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.6/wgpu +//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.6/winit //! [`dodrio`]: https://github.com/fitzgen/dodrio //! [web runtime]: https://github.com/iced-rs/iced_web -//! [examples]: https://github.com/iced-rs/iced/tree/0.5/examples +//! [examples]: https://github.com/iced-rs/iced/tree/0.6/examples //! [repository]: https://github.com/iced-rs/iced //! //! # Overview diff --git a/src/sandbox.rs b/src/sandbox.rs index dc69b6bbf3..47bad8310c 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -34,19 +34,19 @@ use crate::{Application, Command, Element, Error, Settings, Subscription}; /// - [`tour`], a simple UI tour that can run both on native platforms and the /// web! /// -/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.5/examples -/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.5/examples/bezier_tool -/// [`counter`]: https://github.com/iced-rs/iced/tree/0.5/examples/counter -/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.5/examples/custom_widget -/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.5/examples/geometry -/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid -/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.5/examples/progress_bar -/// [`styling`]: https://github.com/iced-rs/iced/tree/0.5/examples/styling -/// [`svg`]: https://github.com/iced-rs/iced/tree/0.5/examples/svg -/// [`tour`]: https://github.com/iced-rs/iced/tree/0.5/examples/tour +/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.6/examples +/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool +/// [`counter`]: https://github.com/iced-rs/iced/tree/0.6/examples/counter +/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget +/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry +/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid +/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.6/examples/progress_bar +/// [`styling`]: https://github.com/iced-rs/iced/tree/0.6/examples/styling +/// [`svg`]: https://github.com/iced-rs/iced/tree/0.6/examples/svg +/// [`tour`]: https://github.com/iced-rs/iced/tree/0.6/examples/tour /// [`Canvas widget`]: crate::widget::Canvas /// [the overview]: index.html#overview -/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/wgpu +/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu /// [`Svg` widget]: crate::widget::Svg /// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg /// diff --git a/src/widget.rs b/src/widget.rs index e7df6e4e1f..526f2b535a 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -56,7 +56,7 @@ pub mod pane_grid { //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! drag and drop, and hotkey support. //! - //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid + //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid pub use iced_native::widget::pane_grid::{ Axis, Configuration, Direction, DragEvent, Line, Node, Pane, ResizeEvent, Split, State, StyleSheet, diff --git a/style/Cargo.toml b/style/Cargo.toml index 559b446279..9f7d904af5 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_style" -version = "0.5.0" +version = "0.5.1" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "The default set of styles of Iced" diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index b1fb0dd1af..a40d996714 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_wgpu" -version = "0.6.1" +version = "0.7.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A wgpu renderer for Iced" @@ -42,11 +42,11 @@ version = "1.9" features = ["derive"] [dependencies.iced_native] -version = "0.6" +version = "0.7" path = "../native" [dependencies.iced_graphics] -version = "0.4" +version = "0.5" path = "../graphics" features = ["font-fallback", "font-icons"] diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 74152945b1..e4a3800532 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -16,7 +16,7 @@ //! - Meshes of triangles, useful to draw geometry freely. //! //! [Iced]: https://github.com/iced-rs/iced -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [WebGPU API]: https://gpuweb.github.io/gpuweb/ //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph diff --git a/winit/Cargo.toml b/winit/Cargo.toml index a3ac3ddd46..ebbadb12dc 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_winit" -version = "0.5.1" +version = "0.6.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A winit runtime for Iced" @@ -26,11 +26,11 @@ git = "https://github.com/iced-rs/winit.git" rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c" [dependencies.iced_native] -version = "0.6" +version = "0.7" path = "../native" [dependencies.iced_graphics] -version = "0.4" +version = "0.5" path = "../graphics" [dependencies.iced_futures] diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 0707aed5e3..b1076afe7c 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -1,7 +1,7 @@ //! Convert [`winit`] types into [`iced_native`] types, and viceversa. //! //! [`winit`]: https://github.com/rust-windowing/winit -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native use crate::keyboard; use crate::mouse; use crate::touch; @@ -218,7 +218,7 @@ pub fn mode(mode: Option) -> window::Mode { /// Converts a `MouseCursor` from [`iced_native`] to a [`winit`] cursor icon. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native pub fn mouse_interaction( interaction: mouse::Interaction, ) -> winit::window::CursorIcon { @@ -242,7 +242,7 @@ pub fn mouse_interaction( /// Converts a `MouseButton` from [`winit`] to an [`iced_native`] mouse button. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { match mouse_button { winit::event::MouseButton::Left => mouse::Button::Left, @@ -258,7 +258,7 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { /// modifiers state. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native pub fn modifiers( modifiers: winit::event::ModifiersState, ) -> keyboard::Modifiers { @@ -285,7 +285,7 @@ pub fn cursor_position( /// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native pub fn touch_event( touch: winit::event::Touch, scale_factor: f64, @@ -316,7 +316,7 @@ pub fn touch_event( /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native pub fn key_code( virtual_keycode: winit::event::VirtualKeyCode, ) -> keyboard::KeyCode { diff --git a/winit/src/lib.rs b/winit/src/lib.rs index bb3a3d5b70..b8ed492d38 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -11,7 +11,7 @@ //! Additionally, a [`conversion`] module is available for users that decide to //! implement a custom event loop. //! -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native //! [`winit`]: https://github.com/rust-windowing/winit //! [`conversion`]: crate::conversion #![doc(