From 781ef1f94c4859aeeb852f801b72be095b8ff82b Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 14 Sep 2023 13:58:36 -0700 Subject: Added support for custom shader widget for iced_wgpu backend. --- widget/Cargo.toml | 1 + widget/src/lib.rs | 3 +++ 2 files changed, 4 insertions(+) (limited to 'widget') diff --git a/widget/Cargo.toml b/widget/Cargo.toml index 6d62c181..032f801c 100644 --- a/widget/Cargo.toml +++ b/widget/Cargo.toml @@ -20,6 +20,7 @@ image = ["iced_renderer/image"] svg = ["iced_renderer/svg"] canvas = ["iced_renderer/geometry"] qr_code = ["canvas", "qrcode"] +wgpu = [] [dependencies] iced_renderer.workspace = true diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 2f959370..e052f398 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -97,6 +97,9 @@ pub use tooltip::Tooltip; #[doc(no_inline)] pub use vertical_slider::VerticalSlider; +#[cfg(feature = "wgpu")] +pub use renderer::widget::shader::{self, Shader}; + #[cfg(feature = "svg")] pub mod svg; -- cgit From 91fca024b629b7ddf4de533a75f01593954362f6 Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 21 Sep 2023 13:24:54 -0700 Subject: Reexport Transformation from widget::shader --- widget/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index e052f398..5220e83a 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -98,7 +98,7 @@ pub use tooltip::Tooltip; pub use vertical_slider::VerticalSlider; #[cfg(feature = "wgpu")] -pub use renderer::widget::shader::{self, Shader}; +pub use renderer::widget::shader::{self, Shader, Transformation}; #[cfg(feature = "svg")] pub mod svg; -- cgit From 9489e29e6619b14ed9f41a8887c4b34158266f71 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 12:49:49 +0100 Subject: Re-organize `custom` module as `pipeline` module ... and move `Shader` widget to `iced_widget` crate --- widget/src/lib.rs | 6 +- widget/src/shader.rs | 219 +++++++++++++++++++++++++++++++++++++++++++ widget/src/shader/event.rs | 26 +++++ widget/src/shader/program.rs | 62 ++++++++++++ 4 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 widget/src/shader.rs create mode 100644 widget/src/shader/event.rs create mode 100644 widget/src/shader/program.rs (limited to 'widget') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 5220e83a..07378d83 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -98,7 +98,11 @@ pub use tooltip::Tooltip; pub use vertical_slider::VerticalSlider; #[cfg(feature = "wgpu")] -pub use renderer::widget::shader::{self, Shader, Transformation}; +pub mod shader; + +#[cfg(feature = "wgpu")] +#[doc(no_inline)] +pub use shader::Shader; #[cfg(feature = "svg")] pub mod svg; diff --git a/widget/src/shader.rs b/widget/src/shader.rs new file mode 100644 index 00000000..9d482537 --- /dev/null +++ b/widget/src/shader.rs @@ -0,0 +1,219 @@ +//! A custom shader widget for wgpu applications. +mod event; +mod program; + +pub use event::Event; +pub use program::Program; + +use crate::core; +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::renderer; +use crate::core::widget::tree::{self, Tree}; +use crate::core::widget::{self, Widget}; +use crate::core::window; +use crate::core::{Clipboard, Element, Length, Rectangle, Shell, Size}; +use crate::renderer::wgpu::primitive::pipeline; + +use std::marker::PhantomData; + +pub use pipeline::{Primitive, Storage}; + +/// A widget which can render custom shaders with Iced's `wgpu` backend. +/// +/// Must be initialized with a [`Program`], which describes the internal widget state & how +/// its [`Program::Primitive`]s are drawn. +#[allow(missing_debug_implementations)] +pub struct Shader> { + width: Length, + height: Length, + program: P, + _message: PhantomData, +} + +impl> Shader { + /// Create a new custom [`Shader`]. + pub fn new(program: P) -> Self { + Self { + width: Length::Fixed(100.0), + height: Length::Fixed(100.0), + program, + _message: PhantomData, + } + } + + /// Set the `width` of the custom [`Shader`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Set the `height` of the custom [`Shader`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } +} + +impl Widget for Shader +where + P: Program, + Renderer: pipeline::Renderer, +{ + fn tag(&self) -> tree::Tag { + struct Tag(T); + tree::Tag::of::>() + } + + fn state(&self) -> tree::State { + tree::State::new(P::State::default()) + } + + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _tree: &mut Tree, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: crate::core::Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, + ) -> event::Status { + let bounds = layout.bounds(); + + let custom_shader_event = match event { + core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)), + core::Event::Keyboard(keyboard_event) => { + Some(Event::Keyboard(keyboard_event)) + } + core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)), + core::Event::Window(window::Event::RedrawRequested(instant)) => { + Some(Event::RedrawRequested(instant)) + } + _ => None, + }; + + if let Some(custom_shader_event) = custom_shader_event { + let state = tree.state.downcast_mut::(); + + let (event_status, message) = self.program.update( + state, + custom_shader_event, + bounds, + cursor, + shell, + ); + + if let Some(message) = message { + shell.publish(message); + } + + return event_status; + } + + event::Status::Ignored + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + let bounds = layout.bounds(); + let state = tree.state.downcast_ref::(); + + self.program.mouse_interaction(state, bounds, cursor) + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + _theme: &Renderer::Theme, + _style: &renderer::Style, + layout: Layout<'_>, + cursor_position: mouse::Cursor, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let state = tree.state.downcast_ref::(); + + renderer.draw_pipeline_primitive( + bounds, + self.program.draw(state, cursor_position, bounds), + ); + } +} + +impl<'a, Message, Renderer, P> From> + for Element<'a, Message, Renderer> +where + Message: 'a, + Renderer: pipeline::Renderer, + P: Program + 'a, +{ + fn from(custom: Shader) -> Element<'a, Message, Renderer> { + Element::new(custom) + } +} + +impl Program for &T +where + T: Program, +{ + type State = T::State; + type Primitive = T::Primitive; + + fn update( + &self, + state: &mut Self::State, + event: Event, + bounds: Rectangle, + cursor: mouse::Cursor, + shell: &mut Shell<'_, Message>, + ) -> (event::Status, Option) { + T::update(self, state, event, bounds, cursor, shell) + } + + fn draw( + &self, + state: &Self::State, + cursor: mouse::Cursor, + bounds: Rectangle, + ) -> Self::Primitive { + T::draw(self, state, cursor, bounds) + } + + fn mouse_interaction( + &self, + state: &Self::State, + bounds: Rectangle, + cursor: mouse::Cursor, + ) -> mouse::Interaction { + T::mouse_interaction(self, state, bounds, cursor) + } +} diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs new file mode 100644 index 00000000..e4d2b03d --- /dev/null +++ b/widget/src/shader/event.rs @@ -0,0 +1,26 @@ +//! Handle events of a custom shader widget. +use crate::core::keyboard; +use crate::core::mouse; +use crate::core::touch; + +use std::time::Instant; + +pub use crate::core::event::Status; + +/// A [`Shader`] event. +/// +/// [`Shader`]: crate::widget::shader::Shader; +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Event { + /// A mouse event. + Mouse(mouse::Event), + + /// A touch event. + Touch(touch::Event), + + /// A keyboard event. + Keyboard(keyboard::Event), + + /// A window requested a redraw. + RedrawRequested(Instant), +} diff --git a/widget/src/shader/program.rs b/widget/src/shader/program.rs new file mode 100644 index 00000000..0319844d --- /dev/null +++ b/widget/src/shader/program.rs @@ -0,0 +1,62 @@ +use crate::core::event; +use crate::core::mouse; +use crate::core::{Rectangle, Shell}; +use crate::renderer::wgpu::primitive::pipeline; +use crate::shader; + +/// The state and logic of a [`Shader`] widget. +/// +/// A [`Program`] can mutate the internal state of a [`Shader`] widget +/// and produce messages for an application. +/// +/// [`Shader`]: crate::widget::shader::Shader +pub trait Program { + /// The internal state of the [`Program`]. + type State: Default + 'static; + + /// The type of primitive this [`Program`] can draw. + type Primitive: pipeline::Primitive + 'static; + + /// Update the internal [`State`] of the [`Program`]. This can be used to reflect state changes + /// based on mouse & other events. You can use the [`Shell`] to publish messages, request a + /// redraw for the window, etc. + /// + /// By default, this method does and returns nothing. + /// + /// [`State`]: Self::State + fn update( + &self, + _state: &mut Self::State, + _event: shader::Event, + _bounds: Rectangle, + _cursor: mouse::Cursor, + _shell: &mut Shell<'_, Message>, + ) -> (event::Status, Option) { + (event::Status::Ignored, None) + } + + /// Draws the [`Primitive`]. + /// + /// [`Primitive`]: Self::Primitive + fn draw( + &self, + state: &Self::State, + cursor: mouse::Cursor, + bounds: Rectangle, + ) -> Self::Primitive; + + /// Returns the current mouse interaction of the [`Program`]. + /// + /// The interaction returned will be in effect even if the cursor position is out of + /// bounds of the [`Shader`]'s program. + /// + /// [`Shader`]: crate::widget::shader::Shader + fn mouse_interaction( + &self, + _state: &Self::State, + _bounds: Rectangle, + _cursor: mouse::Cursor, + ) -> mouse::Interaction { + mouse::Interaction::default() + } +} -- cgit From 882ae304ac8da899d7d83aed433b7dd3311a0496 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 12:51:08 +0100 Subject: Enable `iced_renderer/wgpu` feature in `iced_widget` --- widget/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget') diff --git a/widget/Cargo.toml b/widget/Cargo.toml index 032f801c..e8e363c4 100644 --- a/widget/Cargo.toml +++ b/widget/Cargo.toml @@ -20,7 +20,7 @@ image = ["iced_renderer/image"] svg = ["iced_renderer/svg"] canvas = ["iced_renderer/geometry"] qr_code = ["canvas", "qrcode"] -wgpu = [] +wgpu = ["iced_renderer/wgpu"] [dependencies] iced_renderer.workspace = true -- cgit From c2baf18cbff721e25c3103b6235292530b419c54 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 12:52:03 +0100 Subject: Use `Instant` from `iced_core` instead of `std` This is needed for Wasm compatibility. --- widget/src/shader/event.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'widget') diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs index e4d2b03d..a5a7acaa 100644 --- a/widget/src/shader/event.rs +++ b/widget/src/shader/event.rs @@ -1,10 +1,9 @@ //! Handle events of a custom shader widget. use crate::core::keyboard; use crate::core::mouse; +use crate::core::time::Instant; use crate::core::touch; -use std::time::Instant; - pub use crate::core::event::Status; /// A [`Shader`] event. -- cgit From 280d3736d59b62c4087fe980c187953cc2be83d2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 13:23:28 +0100 Subject: Fix broken intra-doc links --- widget/src/shader/event.rs | 2 +- widget/src/shader/program.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'widget') diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs index a5a7acaa..1cc484fb 100644 --- a/widget/src/shader/event.rs +++ b/widget/src/shader/event.rs @@ -8,7 +8,7 @@ pub use crate::core::event::Status; /// A [`Shader`] event. /// -/// [`Shader`]: crate::widget::shader::Shader; +/// [`Shader`]: crate::Shader #[derive(Debug, Clone, Copy, PartialEq)] pub enum Event { /// A mouse event. diff --git a/widget/src/shader/program.rs b/widget/src/shader/program.rs index 0319844d..6dd50404 100644 --- a/widget/src/shader/program.rs +++ b/widget/src/shader/program.rs @@ -9,7 +9,7 @@ use crate::shader; /// A [`Program`] can mutate the internal state of a [`Shader`] widget /// and produce messages for an application. /// -/// [`Shader`]: crate::widget::shader::Shader +/// [`Shader`]: crate::Shader pub trait Program { /// The internal state of the [`Program`]. type State: Default + 'static; @@ -50,7 +50,7 @@ pub trait Program { /// The interaction returned will be in effect even if the cursor position is out of /// bounds of the [`Shader`]'s program. /// - /// [`Shader`]: crate::widget::shader::Shader + /// [`Shader`]: crate::Shader fn mouse_interaction( &self, _state: &Self::State, -- cgit From 91d7df52cdedd1b5c431fdb51a6356e827765b60 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 13:25:49 +0100 Subject: Create `shader` function helper in `iced_widget` --- widget/src/helpers.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'widget') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index e0b58722..115198fb 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -385,6 +385,17 @@ where crate::Canvas::new(program) } +/// Creates a new [`Shader`]. +/// +/// [`Shader`]: crate::Shader +#[cfg(feature = "wgpu")] +pub fn shader(program: P) -> crate::Shader +where + P: crate::shader::Program, +{ + crate::Shader::new(program) +} + /// Focuses the previous focusable widget. pub fn focus_previous() -> Command where -- cgit From 63f36b04638f14af3455ead8b82d581a438a28a3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 14:04:54 +0100 Subject: Export `wgpu` crate in `shader` module in `iced_widget` --- widget/src/shader.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'widget') diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 9d482537..fe6214db 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -17,6 +17,7 @@ use crate::renderer::wgpu::primitive::pipeline; use std::marker::PhantomData; +pub use crate::renderer::wgpu::wgpu; pub use pipeline::{Primitive, Storage}; /// A widget which can render custom shaders with Iced's `wgpu` backend. -- cgit From 811aa673e9e832ebe38bf56a087f32fdc7aba59c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 15:48:01 +0100 Subject: Improve module hierarchy of `custom_shader` example --- widget/src/shader.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'widget') diff --git a/widget/src/shader.rs b/widget/src/shader.rs index fe6214db..ca140627 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -17,6 +17,7 @@ use crate::renderer::wgpu::primitive::pipeline; use std::marker::PhantomData; +pub use crate::graphics::Transformation; pub use crate::renderer::wgpu::wgpu; pub use pipeline::{Primitive, Storage}; -- cgit