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