Re-organize custom module as pipeline module

... and move `Shader` widget to `iced_widget` crate
This commit is contained in:
Héctor Ramón Jiménez 2023-11-14 12:49:49 +01:00
parent 2dda9132cd
commit 9489e29e66
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
14 changed files with 246 additions and 197 deletions

View file

@ -3,15 +3,20 @@ mod cubes;
mod pipeline; mod pipeline;
mod primitive; mod primitive;
use crate::camera::Camera;
use crate::cubes::Cubes; use crate::cubes::Cubes;
use crate::pipeline::Pipeline;
use iced::executor;
use iced::time::Instant;
use iced::widget::{ use iced::widget::{
checkbox, column, container, row, slider, text, vertical_space, Shader, checkbox, column, container, row, slider, text, vertical_space, Shader,
}; };
use iced::window;
use iced::{ use iced::{
executor, window, Alignment, Application, Color, Command, Element, Length, Alignment, Application, Color, Command, Element, Length, Renderer,
Renderer, Subscription, Theme, Subscription, Theme,
}; };
use std::time::Instant;
fn main() -> iced::Result { fn main() -> iced::Result {
IcedCubes::run(iced::Settings::default()) IcedCubes::run(iced::Settings::default())

View file

@ -4,17 +4,18 @@ pub mod vertex;
mod buffer; mod buffer;
mod uniforms; mod uniforms;
use crate::camera::Camera; pub use buffer::Buffer;
use crate::pipeline::Pipeline; pub use cube::Cube;
use crate::primitive::cube::Cube; pub use uniforms::Uniforms;
pub use vertex::Vertex;
use crate::Camera;
use crate::Pipeline;
use iced::advanced::graphics::Transformation; use iced::advanced::graphics::Transformation;
use iced::widget::shader; use iced::widget::shader;
use iced::{Color, Rectangle, Size}; use iced::{Color, Rectangle, Size};
pub use crate::primitive::vertex::Vertex;
pub use buffer::Buffer;
pub use uniforms::Uniforms;
/// A collection of `Cube`s that can be rendered. /// A collection of `Cube`s that can be rendered.
#[derive(Debug)] #[derive(Debug)]
pub struct Primitive { pub struct Primitive {

View file

@ -1,13 +1,15 @@
#![forbid(rust_2018_idioms)] #![forbid(rust_2018_idioms)]
#![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)] #![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(feature = "wgpu")]
pub use iced_wgpu as wgpu;
pub mod compositor; pub mod compositor;
#[cfg(feature = "geometry")] #[cfg(feature = "geometry")]
pub mod geometry; pub mod geometry;
mod settings; mod settings;
pub mod widget;
pub use iced_graphics as graphics; pub use iced_graphics as graphics;
pub use iced_graphics::core; pub use iced_graphics::core;
@ -60,26 +62,6 @@ impl<T> Renderer<T> {
} }
} }
} }
pub fn draw_custom<P: widget::shader::Primitive>(
&mut self,
bounds: Rectangle,
primitive: P,
) {
match self {
Renderer::TinySkia(_) => {
log::warn!(
"Custom shader primitive is unavailable with tiny-skia."
);
}
#[cfg(feature = "wgpu")]
Renderer::Wgpu(renderer) => {
renderer.draw_primitive(iced_wgpu::Primitive::Custom(
iced_wgpu::primitive::Custom::shader(bounds, primitive),
));
}
}
}
} }
impl<T> core::Renderer for Renderer<T> { impl<T> core::Renderer for Renderer<T> {
@ -292,3 +274,23 @@ impl<T> crate::graphics::geometry::Renderer for Renderer<T> {
} }
} }
} }
#[cfg(feature = "wgpu")]
impl<T> iced_wgpu::primitive::pipeline::Renderer for Renderer<T> {
fn draw_pipeline_primitive(
&mut self,
bounds: Rectangle,
primitive: impl wgpu::primitive::pipeline::Primitive,
) {
match self {
Self::TinySkia(_renderer) => {
log::warn!(
"Custom shader primitive is unavailable with tiny-skia."
);
}
Self::Wgpu(renderer) => {
renderer.draw_pipeline_primitive(bounds, primitive);
}
}
}
}

View file

@ -1,2 +0,0 @@
#[cfg(feature = "wgpu")]
pub mod shader;

View file

@ -2,8 +2,11 @@ use crate::core::{Color, Size};
use crate::graphics::backend; use crate::graphics::backend;
use crate::graphics::color; use crate::graphics::color;
use crate::graphics::{Transformation, Viewport}; use crate::graphics::{Transformation, Viewport};
use crate::primitive::pipeline;
use crate::primitive::{self, Primitive}; use crate::primitive::{self, Primitive};
use crate::{custom, quad, text, triangle}; use crate::quad;
use crate::text;
use crate::triangle;
use crate::{Layer, Settings}; use crate::{Layer, Settings};
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@ -23,7 +26,7 @@ pub struct Backend {
quad_pipeline: quad::Pipeline, quad_pipeline: quad::Pipeline,
text_pipeline: text::Pipeline, text_pipeline: text::Pipeline,
triangle_pipeline: triangle::Pipeline, triangle_pipeline: triangle::Pipeline,
pipeline_storage: custom::Storage, pipeline_storage: pipeline::Storage,
#[cfg(any(feature = "image", feature = "svg"))] #[cfg(any(feature = "image", feature = "svg"))]
image_pipeline: image::Pipeline, image_pipeline: image::Pipeline,
@ -49,7 +52,7 @@ impl Backend {
quad_pipeline, quad_pipeline,
text_pipeline, text_pipeline,
triangle_pipeline, triangle_pipeline,
pipeline_storage: custom::Storage::default(), pipeline_storage: pipeline::Storage::default(),
#[cfg(any(feature = "image", feature = "svg"))] #[cfg(any(feature = "image", feature = "svg"))]
image_pipeline, image_pipeline,
@ -183,9 +186,9 @@ impl Backend {
); );
} }
if !layer.shaders.is_empty() { if !layer.pipelines.is_empty() {
for shader in &layer.shaders { for pipeline in &layer.pipelines {
shader.primitive.prepare( pipeline.primitive.prepare(
format, format,
device, device,
queue, queue,
@ -323,19 +326,17 @@ impl Backend {
// kill render pass to let custom shaders get mut access to encoder // kill render pass to let custom shaders get mut access to encoder
let _ = ManuallyDrop::into_inner(render_pass); let _ = ManuallyDrop::into_inner(render_pass);
if !layer.shaders.is_empty() { if !layer.pipelines.is_empty() {
for shader in &layer.shaders { for pipeline in &layer.pipelines {
//This extra check is needed since each custom pipeline must set it's own let bounds = (pipeline.bounds * scale_factor).snap();
//scissor rect, which will panic if bounds.w/h < 1
let bounds = shader.bounds * scale_factor;
if bounds.width < 1.0 || bounds.height < 1.0 { if bounds.width < 1 || bounds.height < 1 {
continue; continue;
} }
shader.primitive.render( pipeline.primitive.render(
&self.pipeline_storage, &self.pipeline_storage,
bounds.snap(), bounds,
target, target,
target_size, target_size,
encoder, encoder,

View file

@ -1,67 +1,8 @@
//! Draw custom primitives. //! Draw custom primitives.
use crate::core::{Rectangle, Size}; use crate::core::{Rectangle, Size};
use crate::graphics::Transformation; use crate::graphics::Transformation;
use crate::primitive;
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
/// Stores custom, user-provided pipelines.
#[derive(Default, Debug)]
pub struct Storage {
pipelines: HashMap<TypeId, Box<dyn Any>>,
}
impl Storage {
/// Returns `true` if `Storage` contains a pipeline with type `T`.
pub fn has<T: 'static>(&self) -> bool {
self.pipelines.get(&TypeId::of::<T>()).is_some()
}
/// Inserts the pipeline `T` in to [`Storage`].
pub fn store<T: 'static>(&mut self, pipeline: T) {
let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
}
/// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
pub fn get<T: 'static>(&self) -> Option<&T> {
self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
pipeline
.downcast_ref::<T>()
.expect("Pipeline with this type does not exist in Storage.")
})
}
/// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
pipeline
.downcast_mut::<T>()
.expect("Pipeline with this type does not exist in Storage.")
})
}
}
/// A set of methods which allows a [`Primitive`] to be rendered.
pub trait Primitive: Debug + Send + Sync + 'static {
/// Processes the [`Primitive`], allowing for GPU buffer allocation.
fn prepare(
&self,
format: wgpu::TextureFormat,
device: &wgpu::Device,
queue: &wgpu::Queue,
target_size: Size<u32>,
scale_factor: f32,
transform: Transformation,
storage: &mut Storage,
);
/// Renders the [`Primitive`].
fn render(
&self,
storage: &Storage,
bounds: Rectangle<u32>,
target: &wgpu::TextureView,
target_size: Size<u32>,
encoder: &mut wgpu::CommandEncoder,
);
}

View file

@ -35,8 +35,8 @@ pub struct Layer<'a> {
/// The images of the [`Layer`]. /// The images of the [`Layer`].
pub images: Vec<Image>, pub images: Vec<Image>,
/// The custom shader primitives of this [`Layer`]. /// The custom pipelines of this [`Layer`].
pub shaders: Vec<primitive::Shader>, pub pipelines: Vec<primitive::Pipeline>,
} }
impl<'a> Layer<'a> { impl<'a> Layer<'a> {
@ -48,7 +48,7 @@ impl<'a> Layer<'a> {
meshes: Vec::new(), meshes: Vec::new(),
text: Vec::new(), text: Vec::new(),
images: Vec::new(), images: Vec::new(),
shaders: Vec::new(), pipelines: Vec::new(),
} }
} }
@ -312,16 +312,21 @@ impl<'a> Layer<'a> {
} }
} }
}, },
primitive::Custom::Shader(shader) => { primitive::Custom::Pipeline(pipeline) => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
let bounds = Rectangle::new( let bounds = Rectangle::new(
Point::new(translation.x, translation.y), Point::new(translation.x, translation.y),
shader.bounds.size(), pipeline.bounds.size(),
); );
if layer.bounds.intersection(&bounds).is_some() { if let Some(clip_bounds) =
layer.shaders.push(shader.clone()); layer.bounds.intersection(&bounds)
{
layer.pipelines.push(primitive::Pipeline {
bounds: clip_bounds,
primitive: pipeline.primitive.clone(),
});
} }
} }
}, },

View file

@ -29,7 +29,6 @@
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links
)] )]
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod custom;
pub mod layer; pub mod layer;
pub mod primitive; pub mod primitive;
pub mod settings; pub mod settings;

View file

@ -1,10 +1,12 @@
//! Draw using different graphical primitives. //! Draw using different graphical primitives.
pub mod pipeline;
pub use pipeline::Pipeline;
use crate::core::Rectangle; use crate::core::Rectangle;
use crate::custom;
use crate::graphics::{Damage, Mesh}; use crate::graphics::{Damage, Mesh};
use std::any::Any;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc;
/// The graphical primitives supported by `iced_wgpu`. /// The graphical primitives supported by `iced_wgpu`.
pub type Primitive = crate::graphics::Primitive<Custom>; pub type Primitive = crate::graphics::Primitive<Custom>;
@ -14,44 +16,15 @@ pub type Primitive = crate::graphics::Primitive<Custom>;
pub enum Custom { pub enum Custom {
/// A mesh primitive. /// A mesh primitive.
Mesh(Mesh), Mesh(Mesh),
/// A custom shader primitive /// A custom pipeline primitive.
Shader(Shader), Pipeline(Pipeline),
}
impl Custom {
/// Create a custom [`Shader`] primitive.
pub fn shader<P: custom::Primitive>(
bounds: Rectangle,
primitive: P,
) -> Self {
Self::Shader(Shader {
bounds,
primitive: Arc::new(primitive),
})
}
} }
impl Damage for Custom { impl Damage for Custom {
fn bounds(&self) -> Rectangle { fn bounds(&self) -> Rectangle {
match self { match self {
Self::Mesh(mesh) => mesh.bounds(), Self::Mesh(mesh) => mesh.bounds(),
Self::Shader(shader) => shader.bounds, Self::Pipeline(pipeline) => pipeline.bounds,
} }
} }
} }
#[derive(Clone, Debug)]
/// A custom primitive which can be used to render primitives associated with a custom pipeline.
pub struct Shader {
/// The bounds of the [`Shader`].
pub bounds: Rectangle,
/// The [`custom::Primitive`] to render.
pub primitive: Arc<dyn custom::Primitive>,
}
impl PartialEq for Shader {
fn eq(&self, other: &Self) -> bool {
self.primitive.type_id() == other.primitive.type_id()
}
}

View file

@ -0,0 +1,117 @@
//! Draw primitives using custom pipelines.
use crate::core::{Rectangle, Size};
use crate::graphics::Transformation;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
#[derive(Clone, Debug)]
/// A custom primitive which can be used to render primitives associated with a custom pipeline.
pub struct Pipeline {
/// The bounds of the [`Shader`].
pub bounds: Rectangle,
/// The [`custom::Primitive`] to render.
pub primitive: Arc<dyn Primitive>,
}
impl Pipeline {
/// Creates a new [`Pipeline`] with the given [`Primitive`].
pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self {
Pipeline {
bounds,
primitive: Arc::new(primitive),
}
}
}
impl PartialEq for Pipeline {
fn eq(&self, other: &Self) -> bool {
self.primitive.type_id() == other.primitive.type_id()
}
}
/// A set of methods which allows a [`Primitive`] to be rendered.
pub trait Primitive: Debug + Send + Sync + 'static {
/// Processes the [`Primitive`], allowing for GPU buffer allocation.
fn prepare(
&self,
format: wgpu::TextureFormat,
device: &wgpu::Device,
queue: &wgpu::Queue,
target_size: Size<u32>,
scale_factor: f32,
transform: Transformation,
storage: &mut Storage,
);
/// Renders the [`Primitive`].
fn render(
&self,
storage: &Storage,
bounds: Rectangle<u32>,
target: &wgpu::TextureView,
target_size: Size<u32>,
encoder: &mut wgpu::CommandEncoder,
);
}
/// A renderer than can draw custom pipeline primitives.
pub trait Renderer: crate::core::Renderer {
/// Draws a custom pipeline primitive.
fn draw_pipeline_primitive(
&mut self,
bounds: Rectangle,
primitive: impl Primitive,
);
}
impl<Theme> Renderer for crate::Renderer<Theme> {
fn draw_pipeline_primitive(
&mut self,
bounds: Rectangle,
primitive: impl Primitive,
) {
self.draw_primitive(super::Primitive::Custom(super::Custom::Pipeline(
Pipeline::new(bounds, primitive),
)));
}
}
/// Stores custom, user-provided pipelines.
#[derive(Default, Debug)]
pub struct Storage {
pipelines: HashMap<TypeId, Box<dyn Any>>,
}
impl Storage {
/// Returns `true` if `Storage` contains a pipeline with type `T`.
pub fn has<T: 'static>(&self) -> bool {
self.pipelines.get(&TypeId::of::<T>()).is_some()
}
/// Inserts the pipeline `T` in to [`Storage`].
pub fn store<T: 'static>(&mut self, pipeline: T) {
let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
}
/// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
pub fn get<T: 'static>(&self) -> Option<&T> {
self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
pipeline
.downcast_ref::<T>()
.expect("Pipeline with this type does not exist in Storage.")
})
}
/// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
pipeline
.downcast_mut::<T>()
.expect("Pipeline with this type does not exist in Storage.")
})
}
}

View file

@ -98,7 +98,11 @@ pub use tooltip::Tooltip;
pub use vertical_slider::VerticalSlider; pub use vertical_slider::VerticalSlider;
#[cfg(feature = "wgpu")] #[cfg(feature = "wgpu")]
pub use renderer::widget::shader::{self, Shader, Transformation}; pub mod shader;
#[cfg(feature = "wgpu")]
#[doc(no_inline)]
pub use shader::Shader;
#[cfg(feature = "svg")] #[cfg(feature = "svg")]
pub mod svg; pub mod svg;

View file

@ -1,25 +1,24 @@
//! A custom shader widget for wgpu applications. //! A custom shader widget for wgpu applications.
use crate::core::event::Status;
use crate::core::layout::{Limits, Node};
use crate::core::mouse::{Cursor, Interaction};
use crate::core::renderer::Style;
use crate::core::widget::tree::{State, Tag};
use crate::core::widget::{tree, Tree};
use crate::core::{
self, layout, mouse, widget, window, Clipboard, Element, Layout, Length,
Rectangle, Shell, Size, Widget,
};
use std::marker::PhantomData;
mod event; mod event;
mod program; mod program;
pub use event::Event; pub use event::Event;
pub use iced_graphics::Transformation;
pub use iced_wgpu::custom::Primitive;
pub use iced_wgpu::custom::Storage;
pub use program::Program; 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 pipeline::{Primitive, Storage};
/// A widget which can render custom shaders with Iced's `wgpu` backend. /// 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 /// Must be initialized with a [`Program`], which describes the internal widget state & how
@ -56,17 +55,17 @@ impl<Message, P: Program<Message>> Shader<Message, P> {
} }
} }
impl<P, Message, Theme> Widget<Message, crate::Renderer<Theme>> impl<P, Message, Renderer> Widget<Message, Renderer> for Shader<Message, P>
for Shader<Message, P>
where where
P: Program<Message>, P: Program<Message>,
Renderer: pipeline::Renderer,
{ {
fn tag(&self) -> Tag { fn tag(&self) -> tree::Tag {
struct Tag<T>(T); struct Tag<T>(T);
tree::Tag::of::<Tag<P::State>>() tree::Tag::of::<Tag<P::State>>()
} }
fn state(&self) -> State { fn state(&self) -> tree::State {
tree::State::new(P::State::default()) tree::State::new(P::State::default())
} }
@ -81,9 +80,9 @@ where
fn layout( fn layout(
&self, &self,
_tree: &mut Tree, _tree: &mut Tree,
_renderer: &crate::Renderer<Theme>, _renderer: &Renderer,
limits: &Limits, limits: &layout::Limits,
) -> Node { ) -> layout::Node {
let limits = limits.width(self.width).height(self.height); let limits = limits.width(self.width).height(self.height);
let size = limits.resolve(Size::ZERO); let size = limits.resolve(Size::ZERO);
@ -95,12 +94,12 @@ where
tree: &mut Tree, tree: &mut Tree,
event: crate::core::Event, event: crate::core::Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: Cursor, cursor: mouse::Cursor,
_renderer: &crate::Renderer<Theme>, _renderer: &Renderer,
_clipboard: &mut dyn Clipboard, _clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) -> Status { ) -> event::Status {
let bounds = layout.bounds(); let bounds = layout.bounds();
let custom_shader_event = match event { let custom_shader_event = match event {
@ -140,9 +139,9 @@ where
&self, &self,
tree: &Tree, tree: &Tree,
layout: Layout<'_>, layout: Layout<'_>,
cursor: Cursor, cursor: mouse::Cursor,
_viewport: &Rectangle, _viewport: &Rectangle,
_renderer: &crate::Renderer<Theme>, _renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
let bounds = layout.bounds(); let bounds = layout.bounds();
let state = tree.state.downcast_ref::<P::State>(); let state = tree.state.downcast_ref::<P::State>();
@ -153,9 +152,9 @@ where
fn draw( fn draw(
&self, &self,
tree: &widget::Tree, tree: &widget::Tree,
renderer: &mut crate::Renderer<Theme>, renderer: &mut Renderer,
_theme: &Theme, _theme: &Renderer::Theme,
_style: &Style, _style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: mouse::Cursor, cursor_position: mouse::Cursor,
_viewport: &Rectangle, _viewport: &Rectangle,
@ -163,20 +162,21 @@ where
let bounds = layout.bounds(); let bounds = layout.bounds();
let state = tree.state.downcast_ref::<P::State>(); let state = tree.state.downcast_ref::<P::State>();
renderer.draw_custom( renderer.draw_pipeline_primitive(
bounds, bounds,
self.program.draw(state, cursor_position, bounds), self.program.draw(state, cursor_position, bounds),
); );
} }
} }
impl<'a, M, P, Theme> From<Shader<M, P>> impl<'a, Message, Renderer, P> From<Shader<Message, P>>
for Element<'a, M, crate::Renderer<Theme>> for Element<'a, Message, Renderer>
where where
M: 'a, Message: 'a,
P: Program<M> + 'a, Renderer: pipeline::Renderer,
P: Program<Message> + 'a,
{ {
fn from(custom: Shader<M, P>) -> Element<'a, M, crate::Renderer<Theme>> { fn from(custom: Shader<Message, P>) -> Element<'a, Message, Renderer> {
Element::new(custom) Element::new(custom)
} }
} }
@ -193,16 +193,16 @@ where
state: &mut Self::State, state: &mut Self::State,
event: Event, event: Event,
bounds: Rectangle, bounds: Rectangle,
cursor: Cursor, cursor: mouse::Cursor,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> (Status, Option<Message>) { ) -> (event::Status, Option<Message>) {
T::update(self, state, event, bounds, cursor, shell) T::update(self, state, event, bounds, cursor, shell)
} }
fn draw( fn draw(
&self, &self,
state: &Self::State, state: &Self::State,
cursor: Cursor, cursor: mouse::Cursor,
bounds: Rectangle, bounds: Rectangle,
) -> Self::Primitive { ) -> Self::Primitive {
T::draw(self, state, cursor, bounds) T::draw(self, state, cursor, bounds)
@ -212,8 +212,8 @@ where
&self, &self,
state: &Self::State, state: &Self::State,
bounds: Rectangle, bounds: Rectangle,
cursor: Cursor, cursor: mouse::Cursor,
) -> Interaction { ) -> mouse::Interaction {
T::mouse_interaction(self, state, bounds, cursor) T::mouse_interaction(self, state, bounds, cursor)
} }
} }

View file

@ -2,6 +2,7 @@
use crate::core::keyboard; use crate::core::keyboard;
use crate::core::mouse; use crate::core::mouse;
use crate::core::touch; use crate::core::touch;
use std::time::Instant; use std::time::Instant;
pub use crate::core::event::Status; pub use crate::core::event::Status;

View file

@ -1,6 +1,8 @@
use crate::core::{event, mouse, Rectangle, Shell}; use crate::core::event;
use crate::widget; use crate::core::mouse;
use widget::shader; use crate::core::{Rectangle, Shell};
use crate::renderer::wgpu::primitive::pipeline;
use crate::shader;
/// The state and logic of a [`Shader`] widget. /// The state and logic of a [`Shader`] widget.
/// ///
@ -13,7 +15,7 @@ pub trait Program<Message> {
type State: Default + 'static; type State: Default + 'static;
/// The type of primitive this [`Program`] can draw. /// The type of primitive this [`Program`] can draw.
type Primitive: shader::Primitive + 'static; type Primitive: pipeline::Primitive + 'static;
/// Update the internal [`State`] of the [`Program`]. This can be used to reflect state changes /// Update the internal [`State`] of the [`Program`]. This can be used to reflect state changes
/// based on mouse & other events. You can use the [`Shell`] to publish messages, request a /// based on mouse & other events. You can use the [`Shell`] to publish messages, request a