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