iced_widget/
shader.rs

1//! A custom shader widget for wgpu applications.
2mod program;
3
4pub use program::Program;
5
6use crate::core::event;
7use crate::core::layout::{self, Layout};
8use crate::core::mouse;
9use crate::core::renderer;
10use crate::core::widget::tree::{self, Tree};
11use crate::core::widget::{self, Widget};
12use crate::core::{Clipboard, Element, Event, Length, Rectangle, Shell, Size};
13use crate::renderer::wgpu::primitive;
14
15use std::marker::PhantomData;
16
17pub use crate::Action;
18pub use crate::graphics::Viewport;
19pub use primitive::{Pipeline, Primitive, Storage};
20
21/// A widget which can render custom shaders with Iced's `wgpu` backend.
22///
23/// Must be initialized with a [`Program`], which describes the internal widget state & how
24/// its [`Program::Primitive`]s are drawn.
25pub struct Shader<Message, P: Program<Message>> {
26    width: Length,
27    height: Length,
28    program: P,
29    _message: PhantomData<Message>,
30}
31
32impl<Message, P: Program<Message>> Shader<Message, P> {
33    /// Create a new custom [`Shader`].
34    pub fn new(program: P) -> Self {
35        Self {
36            width: Length::Fixed(100.0),
37            height: Length::Fixed(100.0),
38            program,
39            _message: PhantomData,
40        }
41    }
42
43    /// Set the `width` of the custom [`Shader`].
44    pub fn width(mut self, width: impl Into<Length>) -> Self {
45        self.width = width.into();
46        self
47    }
48
49    /// Set the `height` of the custom [`Shader`].
50    pub fn height(mut self, height: impl Into<Length>) -> Self {
51        self.height = height.into();
52        self
53    }
54}
55
56impl<P, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Shader<Message, P>
57where
58    P: Program<Message>,
59    Renderer: primitive::Renderer,
60{
61    fn tag(&self) -> tree::Tag {
62        struct Tag<T>(T);
63        tree::Tag::of::<Tag<P::State>>()
64    }
65
66    fn state(&self) -> tree::State {
67        tree::State::new(P::State::default())
68    }
69
70    fn size(&self) -> Size<Length> {
71        Size {
72            width: self.width,
73            height: self.height,
74        }
75    }
76
77    fn layout(
78        &mut self,
79        _tree: &mut Tree,
80        _renderer: &Renderer,
81        limits: &layout::Limits,
82    ) -> layout::Node {
83        layout::atomic(limits, self.width, self.height)
84    }
85
86    fn update(
87        &mut self,
88        tree: &mut Tree,
89        event: &Event,
90        layout: Layout<'_>,
91        cursor: mouse::Cursor,
92        _renderer: &Renderer,
93        _clipboard: &mut dyn Clipboard,
94        shell: &mut Shell<'_, Message>,
95        _viewport: &Rectangle,
96    ) {
97        let bounds = layout.bounds();
98
99        let state = tree.state.downcast_mut::<P::State>();
100
101        if let Some(action) = self.program.update(state, event, bounds, cursor) {
102            let (message, redraw_request, event_status) = action.into_inner();
103
104            shell.request_redraw_at(redraw_request);
105
106            if let Some(message) = message {
107                shell.publish(message);
108            }
109
110            if event_status == event::Status::Captured {
111                shell.capture_event();
112            }
113        }
114    }
115
116    fn mouse_interaction(
117        &self,
118        tree: &Tree,
119        layout: Layout<'_>,
120        cursor: mouse::Cursor,
121        _viewport: &Rectangle,
122        _renderer: &Renderer,
123    ) -> mouse::Interaction {
124        let bounds = layout.bounds();
125        let state = tree.state.downcast_ref::<P::State>();
126
127        self.program.mouse_interaction(state, bounds, cursor)
128    }
129
130    fn draw(
131        &self,
132        tree: &widget::Tree,
133        renderer: &mut Renderer,
134        _theme: &Theme,
135        _style: &renderer::Style,
136        layout: Layout<'_>,
137        cursor_position: mouse::Cursor,
138        _viewport: &Rectangle,
139    ) {
140        let bounds = layout.bounds();
141        let state = tree.state.downcast_ref::<P::State>();
142
143        renderer.draw_primitive(bounds, self.program.draw(state, cursor_position, bounds));
144    }
145}
146
147impl<'a, Message, Theme, Renderer, P> From<Shader<Message, P>>
148    for Element<'a, Message, Theme, Renderer>
149where
150    Message: 'a,
151    Renderer: primitive::Renderer,
152    P: Program<Message> + 'a,
153{
154    fn from(custom: Shader<Message, P>) -> Element<'a, Message, Theme, Renderer> {
155        Element::new(custom)
156    }
157}
158
159impl<Message, T> Program<Message> for &T
160where
161    T: Program<Message>,
162{
163    type State = T::State;
164    type Primitive = T::Primitive;
165
166    fn update(
167        &self,
168        state: &mut Self::State,
169        event: &Event,
170        bounds: Rectangle,
171        cursor: mouse::Cursor,
172    ) -> Option<Action<Message>> {
173        T::update(self, state, event, bounds, cursor)
174    }
175
176    fn draw(
177        &self,
178        state: &Self::State,
179        cursor: mouse::Cursor,
180        bounds: Rectangle,
181    ) -> Self::Primitive {
182        T::draw(self, state, cursor, bounds)
183    }
184
185    fn mouse_interaction(
186        &self,
187        state: &Self::State,
188        bounds: Rectangle,
189        cursor: mouse::Cursor,
190    ) -> mouse::Interaction {
191        T::mouse_interaction(self, state, bounds, cursor)
192    }
193}