Skip to main content

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::{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        shell: &mut Shell<'_, Message>,
94        _viewport: &Rectangle,
95    ) {
96        let bounds = layout.bounds();
97
98        let state = tree.state.downcast_mut::<P::State>();
99
100        if let Some(action) = self.program.update(state, event, bounds, cursor) {
101            let (message, redraw_request, event_status) = action.into_inner();
102
103            shell.request_redraw_at(redraw_request);
104
105            if let Some(message) = message {
106                shell.publish(message);
107            }
108
109            if event_status == event::Status::Captured {
110                shell.capture_event();
111            }
112        }
113    }
114
115    fn mouse_interaction(
116        &self,
117        tree: &Tree,
118        layout: Layout<'_>,
119        cursor: mouse::Cursor,
120        _viewport: &Rectangle,
121        _renderer: &Renderer,
122    ) -> mouse::Interaction {
123        let bounds = layout.bounds();
124        let state = tree.state.downcast_ref::<P::State>();
125
126        self.program.mouse_interaction(state, bounds, cursor)
127    }
128
129    fn draw(
130        &self,
131        tree: &widget::Tree,
132        renderer: &mut Renderer,
133        _theme: &Theme,
134        _style: &renderer::Style,
135        layout: Layout<'_>,
136        cursor_position: mouse::Cursor,
137        _viewport: &Rectangle,
138    ) {
139        let bounds = layout.bounds();
140        let state = tree.state.downcast_ref::<P::State>();
141
142        renderer.draw_primitive(bounds, self.program.draw(state, cursor_position, bounds));
143    }
144}
145
146impl<'a, Message, Theme, Renderer, P> From<Shader<Message, P>>
147    for Element<'a, Message, Theme, Renderer>
148where
149    Message: 'a,
150    Renderer: primitive::Renderer,
151    P: Program<Message> + 'a,
152{
153    fn from(custom: Shader<Message, P>) -> Element<'a, Message, Theme, Renderer> {
154        Element::new(custom)
155    }
156}
157
158impl<Message, T> Program<Message> for &T
159where
160    T: Program<Message>,
161{
162    type State = T::State;
163    type Primitive = T::Primitive;
164
165    fn update(
166        &self,
167        state: &mut Self::State,
168        event: &Event,
169        bounds: Rectangle,
170        cursor: mouse::Cursor,
171    ) -> Option<Action<Message>> {
172        T::update(self, state, event, bounds, cursor)
173    }
174
175    fn draw(
176        &self,
177        state: &Self::State,
178        cursor: mouse::Cursor,
179        bounds: Rectangle,
180    ) -> Self::Primitive {
181        T::draw(self, state, cursor, bounds)
182    }
183
184    fn mouse_interaction(
185        &self,
186        state: &Self::State,
187        bounds: Rectangle,
188        cursor: mouse::Cursor,
189    ) -> mouse::Interaction {
190        T::mouse_interaction(self, state, bounds, cursor)
191    }
192}