//! 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 crate::renderer::wgpu::wgpu; 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) } }