diff options
Diffstat (limited to '')
-rw-r--r-- | graphics/Cargo.toml | 37 | ||||
-rw-r--r-- | graphics/fonts/Icons.ttf (renamed from wgpu/src/text/icons.ttf) | bin | 4912 -> 4912 bytes | |||
-rw-r--r-- | graphics/fonts/Lato-Regular.ttf (renamed from wgpu/fonts/Lato-Regular.ttf) | bin | 75136 -> 75136 bytes | |||
-rw-r--r-- | graphics/fonts/OFL.txt (renamed from wgpu/fonts/OFL.txt) | 0 | ||||
-rw-r--r-- | graphics/src/antialiasing.rs | 26 | ||||
-rw-r--r-- | graphics/src/backend.rs | 50 | ||||
-rw-r--r-- | graphics/src/defaults.rs (renamed from wgpu/src/defaults.rs) | 0 | ||||
-rw-r--r-- | graphics/src/font.rs | 33 | ||||
-rw-r--r-- | graphics/src/font/source.rs (renamed from wgpu/src/text/font.rs) | 12 | ||||
-rw-r--r-- | graphics/src/layer.rs | 359 | ||||
-rw-r--r-- | graphics/src/lib.rs | 40 | ||||
-rw-r--r-- | graphics/src/primitive.rs (renamed from wgpu/src/primitive.rs) | 6 | ||||
-rw-r--r-- | graphics/src/renderer.rs | 98 | ||||
-rw-r--r-- | graphics/src/transformation.rs (renamed from wgpu/src/transformation.rs) | 0 | ||||
-rw-r--r-- | graphics/src/triangle.rs | 32 | ||||
-rw-r--r-- | graphics/src/viewport.rs | 70 | ||||
-rw-r--r-- | graphics/src/widget.rs | 59 | ||||
-rw-r--r-- | graphics/src/widget/button.rs (renamed from wgpu/src/renderer/widget/button.rs) | 26 | ||||
-rw-r--r-- | graphics/src/widget/canvas.rs | 236 | ||||
-rw-r--r-- | graphics/src/widget/canvas/cache.rs (renamed from wgpu/src/widget/canvas/cache.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/cursor.rs (renamed from wgpu/src/widget/canvas/cursor.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/event.rs (renamed from wgpu/src/widget/canvas/event.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/fill.rs (renamed from wgpu/src/widget/canvas/fill.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/frame.rs (renamed from wgpu/src/widget/canvas/frame.rs) | 2 | ||||
-rw-r--r-- | graphics/src/widget/canvas/geometry.rs (renamed from wgpu/src/widget/canvas/geometry.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/path.rs (renamed from wgpu/src/widget/canvas/path.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/path/arc.rs (renamed from wgpu/src/widget/canvas/path/arc.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/path/builder.rs (renamed from wgpu/src/widget/canvas/path/builder.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/program.rs (renamed from wgpu/src/widget/canvas/program.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/stroke.rs (renamed from wgpu/src/widget/canvas/stroke.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/canvas/text.rs (renamed from wgpu/src/widget/canvas/text.rs) | 0 | ||||
-rw-r--r-- | graphics/src/widget/checkbox.rs (renamed from wgpu/src/renderer/widget/checkbox.rs) | 27 | ||||
-rw-r--r-- | graphics/src/widget/column.rs (renamed from wgpu/src/renderer/widget/row.rs) | 19 | ||||
-rw-r--r-- | graphics/src/widget/container.rs (renamed from wgpu/src/renderer/widget/container.rs) | 19 | ||||
-rw-r--r-- | graphics/src/widget/image.rs (renamed from wgpu/src/renderer/widget/image.rs) | 15 | ||||
-rw-r--r-- | graphics/src/widget/pane_grid.rs (renamed from wgpu/src/renderer/widget/pane_grid.rs) | 37 | ||||
-rw-r--r-- | graphics/src/widget/progress_bar.rs (renamed from wgpu/src/renderer/widget/progress_bar.rs) | 25 | ||||
-rw-r--r-- | graphics/src/widget/radio.rs (renamed from wgpu/src/renderer/widget/radio.rs) | 21 | ||||
-rw-r--r-- | graphics/src/widget/row.rs (renamed from wgpu/src/renderer/widget/column.rs) | 15 | ||||
-rw-r--r-- | graphics/src/widget/scrollable.rs (renamed from wgpu/src/renderer/widget/scrollable.rs) | 23 | ||||
-rw-r--r-- | graphics/src/widget/slider.rs (renamed from wgpu/src/renderer/widget/slider.rs) | 30 | ||||
-rw-r--r-- | graphics/src/widget/space.rs | 15 | ||||
-rw-r--r-- | graphics/src/widget/svg.rs (renamed from wgpu/src/renderer/widget/svg.rs) | 11 | ||||
-rw-r--r-- | graphics/src/widget/text.rs (renamed from wgpu/src/renderer/widget/text.rs) | 19 | ||||
-rw-r--r-- | graphics/src/widget/text_input.rs (renamed from wgpu/src/renderer/widget/text_input.rs) | 52 | ||||
-rw-r--r-- | graphics/src/window.rs | 10 | ||||
-rw-r--r-- | graphics/src/window/compositor.rs (renamed from native/src/window/backend.rs) | 16 | ||||
-rw-r--r-- | graphics/src/window/gl_compositor.rs | 67 |
48 files changed, 1422 insertions, 85 deletions
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml new file mode 100644 index 00000000..8e078d75 --- /dev/null +++ b/graphics/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "iced_graphics" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2018" + +[features] +canvas = ["lyon"] +font-source = ["font-kit"] +font-fallback = [] +font-icons = [] +opengl = [] + +[dependencies] +bytemuck = "1.2" +glam = "0.8" +raw-window-handle = "0.3" + +[dependencies.iced_native] +version = "0.2" +path = "../native" + +[dependencies.iced_style] +version = "0.1" +path = "../style" + +[dependencies.lyon] +version = "0.15" +optional = true + +[dependencies.font-kit] +version = "0.6" +optional = true + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] +all-features = true diff --git a/wgpu/src/text/icons.ttf b/graphics/fonts/Icons.ttf Binary files differindex 1c832f86..1c832f86 100644 --- a/wgpu/src/text/icons.ttf +++ b/graphics/fonts/Icons.ttf diff --git a/wgpu/fonts/Lato-Regular.ttf b/graphics/fonts/Lato-Regular.ttf Binary files differindex 33eba8b1..33eba8b1 100644 --- a/wgpu/fonts/Lato-Regular.ttf +++ b/graphics/fonts/Lato-Regular.ttf diff --git a/wgpu/fonts/OFL.txt b/graphics/fonts/OFL.txt index dfca0da4..dfca0da4 100644 --- a/wgpu/fonts/OFL.txt +++ b/graphics/fonts/OFL.txt diff --git a/graphics/src/antialiasing.rs b/graphics/src/antialiasing.rs new file mode 100644 index 00000000..34d94711 --- /dev/null +++ b/graphics/src/antialiasing.rs @@ -0,0 +1,26 @@ +/// An antialiasing strategy. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Antialiasing { + /// Multisample AA with 2 samples + MSAAx2, + /// Multisample AA with 4 samples + MSAAx4, + /// Multisample AA with 8 samples + MSAAx8, + /// Multisample AA with 16 samples + MSAAx16, +} + +impl Antialiasing { + /// Returns the amount of samples of the [`Antialiasing`]. + /// + /// [`Antialiasing`]: enum.Antialiasing.html + pub fn sample_count(self) -> u32 { + match self { + Antialiasing::MSAAx2 => 2, + Antialiasing::MSAAx4 => 4, + Antialiasing::MSAAx8 => 8, + Antialiasing::MSAAx16 => 16, + } + } +} diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs new file mode 100644 index 00000000..83510311 --- /dev/null +++ b/graphics/src/backend.rs @@ -0,0 +1,50 @@ +//! Write a graphics backend. +use iced_native::image; +use iced_native::svg; +use iced_native::{Font, Size}; + +/// The graphics backend of a [`Renderer`]. +/// +/// [`Renderer`]: ../struct.Renderer.html +pub trait Backend { + /// Trims the measurements cache. + /// + /// This method is currently necessary to properly trim the text cache in + /// `iced_wgpu` and `iced_glow` because of limitations in the text rendering + /// pipeline. It will be removed in the future. + fn trim_measurements(&mut self) {} +} + +/// A graphics backend that supports text rendering. +pub trait Text { + /// The icon font of the backend. + const ICON_FONT: Font; + + /// The `char` representing a ✔ icon in the [`ICON_FONT`]. + /// + /// [`ICON_FONT`]: #associatedconst.ICON_FONt + const CHECKMARK_ICON: char; + + /// Measures the text contents with the given size and font, + /// returning the size of a laid out paragraph that fits in the provided + /// bounds. + fn measure( + &self, + contents: &str, + size: f32, + font: Font, + bounds: Size, + ) -> (f32, f32); +} + +/// A graphics backend that supports image rendering. +pub trait Image { + /// Returns the dimensions of the provided image. + fn dimensions(&self, handle: &image::Handle) -> (u32, u32); +} + +/// A graphics backend that supports SVG rendering. +pub trait Svg { + /// Returns the viewport dimensions of the provided SVG. + fn viewport_dimensions(&self, handle: &svg::Handle) -> (u32, u32); +} diff --git a/wgpu/src/defaults.rs b/graphics/src/defaults.rs index 11718a87..11718a87 100644 --- a/wgpu/src/defaults.rs +++ b/graphics/src/defaults.rs diff --git a/graphics/src/font.rs b/graphics/src/font.rs new file mode 100644 index 00000000..bcc28857 --- /dev/null +++ b/graphics/src/font.rs @@ -0,0 +1,33 @@ +//! Find system fonts or use the built-in ones. +#[cfg(feature = "font-source")] +mod source; + +#[cfg(feature = "font-source")] +#[cfg_attr(docsrs, doc(cfg(feature = "font-source")))] +pub use source::Source; + +#[cfg(feature = "font-source")] +#[cfg_attr(docsrs, doc(cfg(feature = "font-source")))] +pub use font_kit::{ + error::SelectionError as LoadError, family_name::FamilyName as Family, +}; + +/// A built-in fallback font, for convenience. +#[cfg(feature = "font-fallback")] +#[cfg_attr(docsrs, doc(cfg(feature = "font-fallback")))] +pub const FALLBACK: &[u8] = include_bytes!("../fonts/Lato-Regular.ttf"); + +/// A built-in icon font, for convenience. +#[cfg(feature = "font-icons")] +#[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))] +pub const ICONS: iced_native::Font = iced_native::Font::External { + name: "iced_wgpu icons", + bytes: include_bytes!("../fonts/Icons.ttf"), +}; + +/// The `char` representing a ✔ icon in the built-in [`ICONS`] font. +/// +/// [`ICONS`]: const.ICONS.html +#[cfg(feature = "font-icons")] +#[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))] +pub const CHECKMARK_ICON: char = '\u{F00C}'; diff --git a/wgpu/src/text/font.rs b/graphics/src/font/source.rs index 7346ccdb..917291ff 100644 --- a/wgpu/src/text/font.rs +++ b/graphics/src/font/source.rs @@ -1,18 +1,24 @@ -pub use font_kit::{ - error::SelectionError as LoadError, family_name::FamilyName as Family, -}; +use crate::font::{Family, LoadError}; +/// A font source that can find and load system fonts. +#[allow(missing_debug_implementations)] pub struct Source { raw: font_kit::source::SystemSource, } impl Source { + /// Creates a new [`Source`]. + /// + /// [`Source`]: struct.Source.html pub fn new() -> Self { Source { raw: font_kit::source::SystemSource::new(), } } + /// Finds and loads a font matching the set of provided family priorities. + /// + /// [`Source`]: struct.Source.html pub fn load(&self, families: &[Family]) -> Result<Vec<u8>, LoadError> { let font = self.raw.select_best_match( families, diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs new file mode 100644 index 00000000..6aca738e --- /dev/null +++ b/graphics/src/layer.rs @@ -0,0 +1,359 @@ +//! Organize rendering primitives into a flattened list of layers. +use crate::image; +use crate::svg; +use crate::triangle; +use crate::{ + Background, Font, HorizontalAlignment, Point, Primitive, Rectangle, Size, + Vector, VerticalAlignment, Viewport, +}; + +/// A group of primitives that should be clipped together. +#[derive(Debug, Clone)] +pub struct Layer<'a> { + /// The clipping bounds of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html + pub bounds: Rectangle, + + /// The quads of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html + pub quads: Vec<Quad>, + + /// The triangle meshes of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html + pub meshes: Vec<Mesh<'a>>, + + /// The text of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html + pub text: Vec<Text<'a>>, + + /// The images of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html + pub images: Vec<Image>, +} + +impl<'a> Layer<'a> { + /// Creates a new [`Layer`] with the given clipping bounds. + /// + /// [`Layer`]: struct.Layer.html + pub fn new(bounds: Rectangle) -> Self { + Self { + bounds, + quads: Vec::new(), + meshes: Vec::new(), + text: Vec::new(), + images: Vec::new(), + } + } + + /// Creates a new [`Layer`] for the provided overlay text. + /// + /// This can be useful for displaying debug information. + /// + /// [`Layer`]: struct.Layer.html + pub fn overlay(lines: &'a [impl AsRef<str>], viewport: &Viewport) -> Self { + let mut overlay = + Layer::new(Rectangle::with_size(viewport.logical_size())); + + for (i, line) in lines.iter().enumerate() { + let text = Text { + content: line.as_ref(), + bounds: Rectangle::new( + Point::new(11.0, 11.0 + 25.0 * i as f32), + Size::INFINITY, + ), + color: [0.9, 0.9, 0.9, 1.0], + size: 20.0, + font: Font::Default, + horizontal_alignment: HorizontalAlignment::Left, + vertical_alignment: VerticalAlignment::Top, + }; + + overlay.text.push(text); + + overlay.text.push(Text { + bounds: text.bounds + Vector::new(-1.0, -1.0), + color: [0.0, 0.0, 0.0, 1.0], + ..text + }); + } + + overlay + } + + /// Distributes the given [`Primitive`] and generates a list of layers based + /// on its contents. + /// + /// [`Primitive`]: ../enum.Primitive.html + pub fn generate( + primitive: &'a Primitive, + viewport: &Viewport, + ) -> Vec<Self> { + let first_layer = + Layer::new(Rectangle::with_size(viewport.logical_size())); + + let mut layers = vec![first_layer]; + + Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive); + + layers + } + + fn process_primitive( + layers: &mut Vec<Self>, + translation: Vector, + primitive: &'a Primitive, + ) { + match primitive { + Primitive::None => {} + Primitive::Group { primitives } => { + // TODO: Inspect a bit and regroup (?) + for primitive in primitives { + Self::process_primitive(layers, translation, primitive) + } + } + Primitive::Text { + content, + bounds, + size, + color, + font, + horizontal_alignment, + vertical_alignment, + } => { + let layer = layers.last_mut().unwrap(); + + layer.text.push(Text { + content, + bounds: *bounds + translation, + size: *size, + color: color.into_linear(), + font: *font, + horizontal_alignment: *horizontal_alignment, + vertical_alignment: *vertical_alignment, + }); + } + Primitive::Quad { + bounds, + background, + border_radius, + border_width, + border_color, + } => { + let layer = layers.last_mut().unwrap(); + + // TODO: Move some of these computations to the GPU (?) + layer.quads.push(Quad { + position: [ + bounds.x + translation.x, + bounds.y + translation.y, + ], + size: [bounds.width, bounds.height], + color: match background { + Background::Color(color) => color.into_linear(), + }, + border_radius: *border_radius as f32, + border_width: *border_width as f32, + border_color: border_color.into_linear(), + }); + } + Primitive::Mesh2D { buffers, size } => { + let layer = layers.last_mut().unwrap(); + + let bounds = Rectangle::new( + Point::new(translation.x, translation.y), + *size, + ); + + // Only draw visible content + if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { + layer.meshes.push(Mesh { + origin: Point::new(translation.x, translation.y), + buffers, + clip_bounds, + }); + } + } + Primitive::Clip { + bounds, + offset, + content, + } => { + let layer = layers.last_mut().unwrap(); + let translated_bounds = *bounds + translation; + + // Only draw visible content + if let Some(clip_bounds) = + layer.bounds.intersection(&translated_bounds) + { + let clip_layer = Layer::new(clip_bounds); + let new_layer = Layer::new(layer.bounds); + + layers.push(clip_layer); + Self::process_primitive( + layers, + translation + - Vector::new(offset.x as f32, offset.y as f32), + content, + ); + layers.push(new_layer); + } + } + Primitive::Translate { + translation: new_translation, + content, + } => { + Self::process_primitive( + layers, + translation + *new_translation, + &content, + ); + } + Primitive::Cached { cache } => { + Self::process_primitive(layers, translation, &cache); + } + Primitive::Image { handle, bounds } => { + let layer = layers.last_mut().unwrap(); + + layer.images.push(Image::Raster { + handle: handle.clone(), + bounds: *bounds + translation, + }); + } + Primitive::Svg { handle, bounds } => { + let layer = layers.last_mut().unwrap(); + + layer.images.push(Image::Vector { + handle: handle.clone(), + bounds: *bounds + translation, + }); + } + } + } +} + +/// A colored rectangle with a border. +/// +/// This type can be directly uploaded to GPU memory. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Quad { + /// The position of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html + pub position: [f32; 2], + + /// The size of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html + pub size: [f32; 2], + + /// The color of the [`Quad`], in __linear RGB__. + /// + /// [`Quad`]: struct.Quad.html + pub color: [f32; 4], + + /// The border color of the [`Quad`], in __linear RGB__. + /// + /// [`Quad`]: struct.Quad.html + pub border_color: [f32; 4], + + /// The border radius of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html + pub border_radius: f32, + + /// The border width of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html + pub border_width: f32, +} + +/// A mesh of triangles. +#[derive(Debug, Clone, Copy)] +pub struct Mesh<'a> { + /// The origin of the vertices of the [`Mesh`]. + /// + /// [`Mesh`]: struct.Mesh.html + pub origin: Point, + + /// The vertex and index buffers of the [`Mesh`]. + /// + /// [`Mesh`]: struct.Mesh.html + pub buffers: &'a triangle::Mesh2D, + + /// The clipping bounds of the [`Mesh`]. + /// + /// [`Mesh`]: struct.Mesh.html + pub clip_bounds: Rectangle<f32>, +} + +/// A paragraph of text. +#[derive(Debug, Clone, Copy)] +pub struct Text<'a> { + /// The content of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub content: &'a str, + + /// The layout bounds of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub bounds: Rectangle, + + /// The color of the [`Text`], in __linear RGB_. + /// + /// [`Text`]: struct.Text.html + pub color: [f32; 4], + + /// The size of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub size: f32, + + /// The font of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub font: Font, + + /// The horizontal alignment of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub horizontal_alignment: HorizontalAlignment, + + /// The vertical alignment of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub vertical_alignment: VerticalAlignment, +} + +/// A raster or vector image. +#[derive(Debug, Clone)] +pub enum Image { + /// A raster image. + Raster { + /// The handle of a raster image. + handle: image::Handle, + + /// The bounds of the image. + bounds: Rectangle, + }, + /// A vector image. + Vector { + /// The handle of a vector image. + handle: svg::Handle, + + /// The bounds of the image. + bounds: Rectangle, + }, +} + +#[allow(unsafe_code)] +unsafe impl bytemuck::Zeroable for Quad {} + +#[allow(unsafe_code)] +unsafe impl bytemuck::Pod for Quad {} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs new file mode 100644 index 00000000..b6dda132 --- /dev/null +++ b/graphics/src/lib.rs @@ -0,0 +1,40 @@ +//! A bunch of backend-agnostic types that can be leveraged to build a renderer +//! for [`iced`]. +//! +//! [`iced`]: https://github.com/hecrj/iced +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![deny(unused_results)] +#![deny(unsafe_code)] +#![forbid(rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg))] +mod antialiasing; +mod defaults; +mod primitive; +mod renderer; +mod transformation; +mod viewport; +mod widget; + +pub mod backend; +pub mod font; +pub mod layer; +pub mod triangle; +pub mod window; + +#[doc(no_inline)] +pub use widget::*; + +pub use antialiasing::Antialiasing; +pub use backend::Backend; +pub use defaults::Defaults; +pub use layer::Layer; +pub use primitive::Primitive; +pub use renderer::Renderer; +pub use transformation::Transformation; +pub use viewport::Viewport; + +pub use iced_native::{ + Background, Font, HorizontalAlignment, Point, Rectangle, Size, Vector, + VerticalAlignment, +}; diff --git a/wgpu/src/primitive.rs b/graphics/src/primitive.rs index e73227ef..95dbf7dd 100644 --- a/wgpu/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -82,13 +82,13 @@ pub enum Primitive { /// /// It can be used to render many kinds of geometry freely. Mesh2D { + /// The vertex and index buffers of the mesh + buffers: triangle::Mesh2D, + /// The size of the drawable region of the mesh. /// /// Any geometry that falls out of this region will be clipped. size: Size, - - /// The vertex and index buffers of the mesh - buffers: triangle::Mesh2D, }, /// A cached primitive. /// diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs new file mode 100644 index 00000000..c9360f3a --- /dev/null +++ b/graphics/src/renderer.rs @@ -0,0 +1,98 @@ +use crate::{Backend, Defaults, Primitive}; +use iced_native::layout::{self, Layout}; +use iced_native::mouse; +use iced_native::{Background, Color, Element, Point, Widget}; + +/// A backend-agnostic renderer that supports all the built-in widgets. +#[derive(Debug)] +pub struct Renderer<B: Backend> { + backend: B, +} + +impl<B: Backend> Renderer<B> { + /// Creates a new [`Renderer`] from the given [`Backend`]. + /// + /// [`Renderer`]: struct.Renderer.html + /// [`Backend`]: backend/trait.Backend.html + pub fn new(backend: B) -> Self { + Self { backend } + } + + /// Returns a reference to the [`Backend`] of the [`Renderer`]. + /// + /// [`Renderer`]: struct.Renderer.html + /// [`Backend`]: backend/trait.Backend.html + pub fn backend(&self) -> &B { + &self.backend + } + + /// Returns a mutable reference to the [`Backend`] of the [`Renderer`]. + /// + /// [`Renderer`]: struct.Renderer.html + /// [`Backend`]: backend/trait.Backend.html + pub fn backend_mut(&mut self) -> &mut B { + &mut self.backend + } +} + +impl<B> iced_native::Renderer for Renderer<B> +where + B: Backend, +{ + type Output = (Primitive, mouse::Interaction); + type Defaults = Defaults; + + fn layout<'a, Message>( + &mut self, + element: &Element<'a, Message, Self>, + limits: &layout::Limits, + ) -> layout::Node { + let layout = element.layout(self, limits); + + self.backend.trim_measurements(); + + layout + } +} + +impl<B> layout::Debugger for Renderer<B> +where + B: Backend, +{ + fn explain<Message>( + &mut self, + defaults: &Defaults, + widget: &dyn Widget<Message, Self>, + layout: Layout<'_>, + cursor_position: Point, + color: Color, + ) -> Self::Output { + let (primitive, cursor) = + widget.draw(self, defaults, layout, cursor_position); + + let mut primitives = Vec::new(); + + explain_layout(layout, color, &mut primitives); + primitives.push(primitive); + + (Primitive::Group { primitives }, cursor) + } +} + +fn explain_layout( + layout: Layout<'_>, + color: Color, + primitives: &mut Vec<Primitive>, +) { + primitives.push(Primitive::Quad { + bounds: layout.bounds(), + background: Background::Color(Color::TRANSPARENT), + border_radius: 0, + border_width: 1, + border_color: [0.6, 0.6, 0.6, 0.5].into(), + }); + + for child in layout.children() { + explain_layout(child, color, primitives); + } +} diff --git a/wgpu/src/transformation.rs b/graphics/src/transformation.rs index ff3b1d00..ff3b1d00 100644 --- a/wgpu/src/transformation.rs +++ b/graphics/src/transformation.rs diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs new file mode 100644 index 00000000..ce879ffc --- /dev/null +++ b/graphics/src/triangle.rs @@ -0,0 +1,32 @@ +//! Draw geometry using meshes of triangles. + +/// A set of [`Vertex2D`] and indices representing a list of triangles. +/// +/// [`Vertex2D`]: struct.Vertex2D.html +#[derive(Clone, Debug)] +pub struct Mesh2D { + /// The vertices of the mesh + pub vertices: Vec<Vertex2D>, + + /// The list of vertex indices that defines the triangles of the mesh. + /// + /// Therefore, this list should always have a length that is a multiple of + /// 3. + pub indices: Vec<u32>, +} + +/// A two-dimensional vertex with some color in __linear__ RGBA. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct Vertex2D { + /// The vertex position + pub position: [f32; 2], + /// The vertex color in __linear__ RGBA. + pub color: [f32; 4], +} + +#[allow(unsafe_code)] +unsafe impl bytemuck::Zeroable for Vertex2D {} + +#[allow(unsafe_code)] +unsafe impl bytemuck::Pod for Vertex2D {} diff --git a/graphics/src/viewport.rs b/graphics/src/viewport.rs new file mode 100644 index 00000000..66122e6d --- /dev/null +++ b/graphics/src/viewport.rs @@ -0,0 +1,70 @@ +use crate::{Size, Transformation}; + +/// A viewing region for displaying computer graphics. +#[derive(Debug)] +pub struct Viewport { + physical_size: Size<u32>, + logical_size: Size<f32>, + scale_factor: f64, + projection: Transformation, +} + +impl Viewport { + /// Creates a new [`Viewport`] with the given physical dimensions and scale + /// factor. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn with_physical_size(size: Size<u32>, scale_factor: f64) -> Viewport { + Viewport { + physical_size: size, + logical_size: Size::new( + (size.width as f64 / scale_factor) as f32, + (size.height as f64 / scale_factor) as f32, + ), + scale_factor, + projection: Transformation::orthographic(size.width, size.height), + } + } + + /// Returns the physical size of the [`Viewport`]. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn physical_size(&self) -> Size<u32> { + self.physical_size + } + + /// Returns the physical width of the [`Viewport`]. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn physical_width(&self) -> u32 { + self.physical_size.height + } + + /// Returns the physical height of the [`Viewport`]. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn physical_height(&self) -> u32 { + self.physical_size.height + } + + /// Returns the logical size of the [`Viewport`]. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn logical_size(&self) -> Size<f32> { + self.logical_size + } + + /// Returns the scale factor of the [`Viewport`]. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn scale_factor(&self) -> f64 { + self.scale_factor + } + + /// Returns the projection transformation of the [`Viewport`]. + /// + /// [`Viewport`]: struct.Viewport.html + pub fn projection(&self) -> Transformation { + self.projection + } +} diff --git a/graphics/src/widget.rs b/graphics/src/widget.rs new file mode 100644 index 00000000..1f6d6559 --- /dev/null +++ b/graphics/src/widget.rs @@ -0,0 +1,59 @@ +//! Use the widgets supported out-of-the-box. +//! +//! # Re-exports +//! For convenience, the contents of this module are available at the root +//! module. Therefore, you can directly type: +//! +//! ``` +//! use iced_graphics::{button, Button}; +//! ``` +pub mod button; +pub mod checkbox; +pub mod container; +pub mod image; +pub mod pane_grid; +pub mod progress_bar; +pub mod radio; +pub mod scrollable; +pub mod slider; +pub mod svg; +pub mod text_input; + +mod column; +mod row; +mod space; +mod text; + +#[doc(no_inline)] +pub use button::Button; +#[doc(no_inline)] +pub use checkbox::Checkbox; +#[doc(no_inline)] +pub use container::Container; +#[doc(no_inline)] +pub use pane_grid::PaneGrid; +#[doc(no_inline)] +pub use progress_bar::ProgressBar; +#[doc(no_inline)] +pub use radio::Radio; +#[doc(no_inline)] +pub use scrollable::Scrollable; +#[doc(no_inline)] +pub use slider::Slider; +#[doc(no_inline)] +pub use text_input::TextInput; + +pub use column::Column; +pub use image::Image; +pub use row::Row; +pub use space::Space; +pub use svg::Svg; +pub use text::Text; + +#[cfg(feature = "canvas")] +#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))] +pub mod canvas; + +#[cfg(feature = "canvas")] +#[doc(no_inline)] +pub use canvas::Canvas; diff --git a/wgpu/src/renderer/widget/button.rs b/graphics/src/widget/button.rs index eb225038..aeb862d5 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/graphics/src/widget/button.rs @@ -1,9 +1,29 @@ -use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer}; +//! Allow your users to perform actions by pressing a button. +//! +//! A [`Button`] has some local [`State`]. +//! +//! [`Button`]: type.Button.html +//! [`State`]: struct.State.html +use crate::defaults::{self, Defaults}; +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; use iced_native::{ - mouse, Background, Color, Element, Layout, Point, Rectangle, Vector, + Background, Color, Element, Layout, Point, Rectangle, Vector, }; -impl iced_native::button::Renderer for Renderer { +pub use iced_native::button::State; +pub use iced_style::button::{Style, StyleSheet}; + +/// A widget that produces a message when clicked. +/// +/// This is an alias of an `iced_native` button with an `iced_wgpu::Renderer`. +pub type Button<'a, Message, Backend> = + iced_native::Button<'a, Message, Renderer<Backend>>; + +impl<B> iced_native::button::Renderer for Renderer<B> +where + B: Backend, +{ const DEFAULT_PADDING: u16 = 5; type Style = Box<dyn StyleSheet>; diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs new file mode 100644 index 00000000..d393a5c5 --- /dev/null +++ b/graphics/src/widget/canvas.rs @@ -0,0 +1,236 @@ +//! Draw 2D graphics for your users. +//! +//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a +//! [`Frame`]. It can be used for animation, data visualization, game graphics, +//! and more! +//! +//! [`Canvas`]: struct.Canvas.html +//! [`Frame`]: struct.Frame.html +use crate::{Backend, Defaults, Primitive, Renderer}; +use iced_native::{ + layout, mouse, Clipboard, Element, Hasher, Layout, Length, Point, Size, + Vector, Widget, +}; +use std::hash::Hash; +use std::marker::PhantomData; + +pub mod path; + +mod cache; +mod cursor; +mod event; +mod fill; +mod frame; +mod geometry; +mod program; +mod stroke; +mod text; + +pub use cache::Cache; +pub use cursor::Cursor; +pub use event::Event; +pub use fill::Fill; +pub use frame::Frame; +pub use geometry::Geometry; +pub use path::Path; +pub use program::Program; +pub use stroke::{LineCap, LineJoin, Stroke}; +pub use text::Text; + +/// A widget capable of drawing 2D graphics. +/// +/// [`Canvas`]: struct.Canvas.html +/// +/// # Examples +/// The repository has a couple of [examples] showcasing how to use a +/// [`Canvas`]: +/// +/// - [`clock`], an application that uses the [`Canvas`] widget to draw a clock +/// and its hands to display the current time. +/// - [`game_of_life`], an interactive version of the Game of Life, invented by +/// John Conway. +/// - [`solar_system`], an animated solar system drawn using the [`Canvas`] widget +/// and showcasing how to compose different transforms. +/// +/// [examples]: https://github.com/hecrj/iced/tree/master/examples +/// [`clock`]: https://github.com/hecrj/iced/tree/master/examples/clock +/// [`game_of_life`]: https://github.com/hecrj/iced/tree/master/examples/game_of_life +/// [`solar_system`]: https://github.com/hecrj/iced/tree/master/examples/solar_system +/// +/// ## Drawing a simple circle +/// If you want to get a quick overview, here's how we can draw a simple circle: +/// +/// ```no_run +/// # mod iced { +/// # pub use iced_graphics::canvas; +/// # pub use iced_native::{Color, Rectangle}; +/// # } +/// use iced::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program}; +/// use iced::{Color, Rectangle}; +/// +/// // First, we define the data we need for drawing +/// #[derive(Debug)] +/// struct Circle { +/// radius: f32, +/// } +/// +/// // Then, we implement the `Program` trait +/// impl Program<()> for Circle { +/// fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{ +/// // We prepare a new `Frame` +/// let mut frame = Frame::new(bounds.size()); +/// +/// // We create a `Path` representing a simple circle +/// let circle = Path::circle(frame.center(), self.radius); +/// +/// // And fill it with some color +/// frame.fill(&circle, Fill::Color(Color::BLACK)); +/// +/// // Finally, we produce the geometry +/// vec![frame.into_geometry()] +/// } +/// } +/// +/// // Finally, we simply use our `Circle` to create the `Canvas`! +/// let canvas = Canvas::new(Circle { radius: 50.0 }); +/// ``` +#[derive(Debug)] +pub struct Canvas<Message, P: Program<Message>> { + width: Length, + height: Length, + program: P, + phantom: PhantomData<Message>, +} + +impl<Message, P: Program<Message>> Canvas<Message, P> { + const DEFAULT_SIZE: u16 = 100; + + /// Creates a new [`Canvas`]. + /// + /// [`Canvas`]: struct.Canvas.html + pub fn new(program: P) -> Self { + Canvas { + width: Length::Units(Self::DEFAULT_SIZE), + height: Length::Units(Self::DEFAULT_SIZE), + program, + phantom: PhantomData, + } + } + + /// Sets the width of the [`Canvas`]. + /// + /// [`Canvas`]: struct.Canvas.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Canvas`]. + /// + /// [`Canvas`]: struct.Canvas.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } +} + +impl<Message, P, B> Widget<Message, Renderer<B>> for Canvas<Message, P> +where + P: Program<Message>, + B: Backend, +{ + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _renderer: &Renderer<B>, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + event: iced_native::Event, + layout: Layout<'_>, + cursor_position: Point, + messages: &mut Vec<Message>, + _renderer: &Renderer<B>, + _clipboard: Option<&dyn Clipboard>, + ) { + let bounds = layout.bounds(); + + let canvas_event = match event { + iced_native::Event::Mouse(mouse_event) => { + Some(Event::Mouse(mouse_event)) + } + _ => None, + }; + + let cursor = Cursor::from_window_position(cursor_position); + + if let Some(canvas_event) = canvas_event { + if let Some(message) = + self.program.update(canvas_event, bounds, cursor) + { + messages.push(message); + } + } + } + + fn draw( + &self, + _renderer: &mut Renderer<B>, + _defaults: &Defaults, + layout: Layout<'_>, + cursor_position: Point, + ) -> (Primitive, mouse::Interaction) { + let bounds = layout.bounds(); + let translation = Vector::new(bounds.x, bounds.y); + let cursor = Cursor::from_window_position(cursor_position); + + ( + Primitive::Translate { + translation, + content: Box::new(Primitive::Group { + primitives: self + .program + .draw(bounds, cursor) + .into_iter() + .map(Geometry::into_primitive) + .collect(), + }), + }, + self.program.mouse_interaction(bounds, cursor), + ) + } + + fn hash_layout(&self, state: &mut Hasher) { + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); + + self.width.hash(state); + self.height.hash(state); + } +} + +impl<'a, Message, P, B> From<Canvas<Message, P>> + for Element<'a, Message, Renderer<B>> +where + Message: 'static, + P: Program<Message> + 'a, + B: Backend, +{ + fn from(canvas: Canvas<Message, P>) -> Element<'a, Message, Renderer<B>> { + Element::new(canvas) + } +} diff --git a/wgpu/src/widget/canvas/cache.rs b/graphics/src/widget/canvas/cache.rs index 4b28d164..4b28d164 100644 --- a/wgpu/src/widget/canvas/cache.rs +++ b/graphics/src/widget/canvas/cache.rs diff --git a/wgpu/src/widget/canvas/cursor.rs b/graphics/src/widget/canvas/cursor.rs index 456760ea..456760ea 100644 --- a/wgpu/src/widget/canvas/cursor.rs +++ b/graphics/src/widget/canvas/cursor.rs diff --git a/wgpu/src/widget/canvas/event.rs b/graphics/src/widget/canvas/event.rs index ad11f51e..ad11f51e 100644 --- a/wgpu/src/widget/canvas/event.rs +++ b/graphics/src/widget/canvas/event.rs diff --git a/wgpu/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index a2010e45..a2010e45 100644 --- a/wgpu/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs diff --git a/wgpu/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 5262ab4e..48d28d95 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -304,11 +304,11 @@ impl Frame { pub fn into_geometry(mut self) -> Geometry { if !self.buffers.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { - size: self.size, buffers: triangle::Mesh2D { vertices: self.buffers.vertices, indices: self.buffers.indices, }, + size: self.size, }); } diff --git a/wgpu/src/widget/canvas/geometry.rs b/graphics/src/widget/canvas/geometry.rs index 4cadee39..4cadee39 100644 --- a/wgpu/src/widget/canvas/geometry.rs +++ b/graphics/src/widget/canvas/geometry.rs diff --git a/wgpu/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index c26bf187..c26bf187 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs diff --git a/wgpu/src/widget/canvas/path/arc.rs b/graphics/src/widget/canvas/path/arc.rs index 343191f1..343191f1 100644 --- a/wgpu/src/widget/canvas/path/arc.rs +++ b/graphics/src/widget/canvas/path/arc.rs diff --git a/wgpu/src/widget/canvas/path/builder.rs b/graphics/src/widget/canvas/path/builder.rs index 6511fa52..6511fa52 100644 --- a/wgpu/src/widget/canvas/path/builder.rs +++ b/graphics/src/widget/canvas/path/builder.rs diff --git a/wgpu/src/widget/canvas/program.rs b/graphics/src/widget/canvas/program.rs index 725d9d72..725d9d72 100644 --- a/wgpu/src/widget/canvas/program.rs +++ b/graphics/src/widget/canvas/program.rs diff --git a/wgpu/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 5b6fc56a..5b6fc56a 100644 --- a/wgpu/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs diff --git a/wgpu/src/widget/canvas/text.rs b/graphics/src/widget/canvas/text.rs index c4cae30e..c4cae30e 100644 --- a/wgpu/src/widget/canvas/text.rs +++ b/graphics/src/widget/canvas/text.rs diff --git a/wgpu/src/renderer/widget/checkbox.rs b/graphics/src/widget/checkbox.rs index 0340bf62..cb7fd2cf 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/graphics/src/widget/checkbox.rs @@ -1,9 +1,22 @@ -use crate::{checkbox::StyleSheet, Primitive, Renderer}; -use iced_native::{ - checkbox, mouse, HorizontalAlignment, Rectangle, VerticalAlignment, -}; +//! Show toggle controls using checkboxes. +use crate::backend::{self, Backend}; +use crate::{Primitive, Renderer}; +use iced_native::checkbox; +use iced_native::mouse; +use iced_native::{HorizontalAlignment, Rectangle, VerticalAlignment}; -impl checkbox::Renderer for Renderer { +pub use iced_style::checkbox::{Style, StyleSheet}; + +/// A box that can be checked. +/// +/// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`. +pub type Checkbox<Message, Backend> = + iced_native::Checkbox<Message, Renderer<Backend>>; + +impl<B> checkbox::Renderer for Renderer<B> +where + B: Backend + backend::Text, +{ type Style = Box<dyn StyleSheet>; const DEFAULT_SIZE: u16 = 20; @@ -35,8 +48,8 @@ impl checkbox::Renderer for Renderer { Primitive::Group { primitives: if is_checked { let check = Primitive::Text { - content: crate::text::CHECKMARK_ICON.to_string(), - font: crate::text::BUILTIN_ICONS, + content: B::CHECKMARK_ICON.to_string(), + font: B::ICON_FONT, size: bounds.height * 0.7, bounds: Rectangle { x: bounds.center_x(), diff --git a/wgpu/src/renderer/widget/row.rs b/graphics/src/widget/column.rs index d0b7ef09..6c7235c7 100644 --- a/wgpu/src/renderer/widget/row.rs +++ b/graphics/src/widget/column.rs @@ -1,11 +1,20 @@ -use crate::{Primitive, Renderer}; -use iced_native::{mouse, row, Element, Layout, Point}; +use crate::{Backend, Primitive, Renderer}; +use iced_native::column; +use iced_native::mouse; +use iced_native::{Element, Layout, Point}; -impl row::Renderer for Renderer { +/// A container that distributes its contents vertically. +pub type Column<'a, Message, Backend> = + iced_native::Column<'a, Message, Renderer<Backend>>; + +impl<B> column::Renderer for Renderer<B> +where + B: Backend, +{ fn draw<Message>( &mut self, defaults: &Self::Defaults, - children: &[Element<'_, Message, Self>], + content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, ) -> Self::Output { @@ -13,7 +22,7 @@ impl row::Renderer for Renderer { ( Primitive::Group { - primitives: children + primitives: content .iter() .zip(layout.children()) .map(|(child, layout)| { diff --git a/wgpu/src/renderer/widget/container.rs b/graphics/src/widget/container.rs index 30cc3f07..070cb48b 100644 --- a/wgpu/src/renderer/widget/container.rs +++ b/graphics/src/widget/container.rs @@ -1,7 +1,22 @@ -use crate::{container, defaults, Defaults, Primitive, Renderer}; +//! Decorate content and apply alignment. +use crate::container; +use crate::defaults::{self, Defaults}; +use crate::{Backend, Primitive, Renderer}; use iced_native::{Background, Color, Element, Layout, Point, Rectangle}; -impl iced_native::container::Renderer for Renderer { +pub use iced_style::container::{Style, StyleSheet}; + +/// An element decorating some content. +/// +/// This is an alias of an `iced_native` container with a default +/// `Renderer`. +pub type Container<'a, Message, Backend> = + iced_native::Container<'a, Message, Renderer<Backend>>; + +impl<B> iced_native::container::Renderer for Renderer<B> +where + B: Backend, +{ type Style = Box<dyn container::StyleSheet>; fn draw<Message>( diff --git a/wgpu/src/renderer/widget/image.rs b/graphics/src/widget/image.rs index c4c04984..30f446e8 100644 --- a/wgpu/src/renderer/widget/image.rs +++ b/graphics/src/widget/image.rs @@ -1,9 +1,18 @@ +//! Display images in your user interface. +use crate::backend::{self, Backend}; use crate::{Primitive, Renderer}; -use iced_native::{image, mouse, Layout}; +use iced_native::image; +use iced_native::mouse; +use iced_native::Layout; -impl image::Renderer for Renderer { +pub use iced_native::image::{Handle, Image}; + +impl<B> image::Renderer for Renderer<B> +where + B: Backend + backend::Image, +{ fn dimensions(&self, handle: &image::Handle) -> (u32, u32) { - self.image_pipeline.dimensions(handle) + self.backend().dimensions(handle) } fn draw( diff --git a/wgpu/src/renderer/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index 2253e4af..56af683d 100644 --- a/wgpu/src/renderer/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -1,11 +1,36 @@ -use crate::{Primitive, Renderer}; -use iced_native::{ - mouse, - pane_grid::{self, Axis, Pane}, - Element, Layout, Point, Rectangle, Vector, +//! Let your users split regions of your application and organize layout dynamically. +//! +//! [](https://gfycat.com/mixedflatjellyfish) +//! +//! # Example +//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, +//! drag and drop, and hotkey support. +//! +//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.1/examples/pane_grid +//! [`PaneGrid`]: type.PaneGrid.html +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::pane_grid; +use iced_native::{Element, Layout, Point, Rectangle, Vector}; + +pub use iced_native::pane_grid::{ + Axis, Direction, DragEvent, Focus, KeyPressEvent, Pane, ResizeEvent, Split, + State, }; -impl pane_grid::Renderer for Renderer { +/// A collection of panes distributed using either vertical or horizontal splits +/// to completely fill the space available. +/// +/// [](https://gfycat.com/mixedflatjellyfish) +/// +/// This is an alias of an `iced_native` pane grid with an `iced_wgpu::Renderer`. +pub type PaneGrid<'a, Message, Backend> = + iced_native::PaneGrid<'a, Message, Renderer<Backend>>; + +impl<B> pane_grid::Renderer for Renderer<B> +where + B: Backend, +{ fn draw<Message>( &mut self, defaults: &Self::Defaults, diff --git a/wgpu/src/renderer/widget/progress_bar.rs b/graphics/src/widget/progress_bar.rs index 2baeeb14..48acb3c1 100644 --- a/wgpu/src/renderer/widget/progress_bar.rs +++ b/graphics/src/widget/progress_bar.rs @@ -1,7 +1,26 @@ -use crate::{progress_bar::StyleSheet, Primitive, Renderer}; -use iced_native::{mouse, progress_bar, Color, Rectangle}; +//! Allow your users to visually track the progress of a computation. +//! +//! A [`ProgressBar`] has a range of possible values and a current value, +//! as well as a length, height and style. +//! +//! [`ProgressBar`]: type.ProgressBar.html +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::progress_bar; +use iced_native::{Color, Rectangle}; -impl progress_bar::Renderer for Renderer { +pub use iced_style::progress_bar::{Style, StyleSheet}; + +/// A bar that displays progress. +/// +/// This is an alias of an `iced_native` progress bar with an +/// `iced_wgpu::Renderer`. +pub type ProgressBar<Backend> = iced_native::ProgressBar<Renderer<Backend>>; + +impl<B> progress_bar::Renderer for Renderer<B> +where + B: Backend, +{ type Style = Box<dyn StyleSheet>; const DEFAULT_HEIGHT: u16 = 30; diff --git a/wgpu/src/renderer/widget/radio.rs b/graphics/src/widget/radio.rs index cee0deb6..dd8b5f17 100644 --- a/wgpu/src/renderer/widget/radio.rs +++ b/graphics/src/widget/radio.rs @@ -1,10 +1,25 @@ -use crate::{radio::StyleSheet, Primitive, Renderer}; -use iced_native::{mouse, radio, Background, Color, Rectangle}; +//! Create choices using radio buttons. +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::radio; +use iced_native::{Background, Color, Rectangle}; + +pub use iced_style::radio::{Style, StyleSheet}; + +/// A circular button representing a choice. +/// +/// This is an alias of an `iced_native` radio button with an +/// `iced_wgpu::Renderer`. +pub type Radio<Message, Backend> = + iced_native::Radio<Message, Renderer<Backend>>; const SIZE: f32 = 28.0; const DOT_SIZE: f32 = SIZE / 2.0; -impl radio::Renderer for Renderer { +impl<B> radio::Renderer for Renderer<B> +where + B: Backend, +{ type Style = Box<dyn StyleSheet>; const DEFAULT_SIZE: u16 = SIZE as u16; diff --git a/wgpu/src/renderer/widget/column.rs b/graphics/src/widget/row.rs index b853276d..4c1dbadc 100644 --- a/wgpu/src/renderer/widget/column.rs +++ b/graphics/src/widget/row.rs @@ -1,7 +1,16 @@ -use crate::{Primitive, Renderer}; -use iced_native::{column, mouse, Element, Layout, Point}; +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::row; +use iced_native::{Element, Layout, Point}; -impl column::Renderer for Renderer { +/// A container that distributes its contents horizontally. +pub type Row<'a, Message, Backend> = + iced_native::Row<'a, Message, Renderer<Backend>>; + +impl<B> row::Renderer for Renderer<B> +where + B: Backend, +{ fn draw<Message>( &mut self, defaults: &Self::Defaults, diff --git a/wgpu/src/renderer/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index 8a400b82..b149db0a 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -1,10 +1,27 @@ -use crate::{Primitive, Renderer}; -use iced_native::{mouse, scrollable, Background, Color, Rectangle, Vector}; +//! Navigate an endless amount of content with a scrollbar. +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::scrollable; +use iced_native::{Background, Color, Rectangle, Vector}; + +pub use iced_native::scrollable::State; +pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; + +/// A widget that can vertically display an infinite amount of content +/// with a scrollbar. +/// +/// This is an alias of an `iced_native` scrollable with a default +/// `Renderer`. +pub type Scrollable<'a, Message, Backend> = + iced_native::Scrollable<'a, Message, Renderer<Backend>>; const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; -impl scrollable::Renderer for Renderer { +impl<B> scrollable::Renderer for Renderer<B> +where + B: Backend, +{ type Style = Box<dyn iced_style::scrollable::StyleSheet>; fn scrollbar( diff --git a/wgpu/src/renderer/widget/slider.rs b/graphics/src/widget/slider.rs index 220feace..b00cde9a 100644 --- a/wgpu/src/renderer/widget/slider.rs +++ b/graphics/src/widget/slider.rs @@ -1,12 +1,30 @@ -use crate::{ - slider::{HandleShape, StyleSheet}, - Primitive, Renderer, -}; -use iced_native::{mouse, slider, Background, Color, Point, Rectangle}; +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +//! +//! [`Slider`]: struct.Slider.html +//! [`State`]: struct.State.html +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::slider; +use iced_native::{Background, Color, Point, Rectangle}; + +pub use iced_native::slider::State; +pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; + +/// An horizontal bar and a handle that selects a single value from a range of +/// values. +/// +/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`. +pub type Slider<'a, Message, Backend> = + iced_native::Slider<'a, Message, Renderer<Backend>>; const HANDLE_HEIGHT: f32 = 22.0; -impl slider::Renderer for Renderer { +impl<B> slider::Renderer for Renderer<B> +where + B: Backend, +{ type Style = Box<dyn StyleSheet>; fn height(&self) -> u32 { diff --git a/graphics/src/widget/space.rs b/graphics/src/widget/space.rs new file mode 100644 index 00000000..1f31eabe --- /dev/null +++ b/graphics/src/widget/space.rs @@ -0,0 +1,15 @@ +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::space; +use iced_native::Rectangle; + +pub use iced_native::Space; + +impl<B> space::Renderer for Renderer<B> +where + B: Backend, +{ + fn draw(&mut self, _bounds: Rectangle) -> Self::Output { + (Primitive::None, mouse::Interaction::default()) + } +} diff --git a/wgpu/src/renderer/widget/svg.rs b/graphics/src/widget/svg.rs index f6d6d0ba..8b5ed66a 100644 --- a/wgpu/src/renderer/widget/svg.rs +++ b/graphics/src/widget/svg.rs @@ -1,9 +1,16 @@ +//! Display vector graphics in your application. +use crate::backend::{self, Backend}; use crate::{Primitive, Renderer}; use iced_native::{mouse, svg, Layout}; -impl svg::Renderer for Renderer { +pub use iced_native::svg::{Handle, Svg}; + +impl<B> svg::Renderer for Renderer<B> +where + B: Backend + backend::Svg, +{ fn dimensions(&self, handle: &svg::Handle) -> (u32, u32) { - self.image_pipeline.viewport_dimensions(handle) + self.backend().viewport_dimensions(handle) } fn draw( diff --git a/wgpu/src/renderer/widget/text.rs b/graphics/src/widget/text.rs index 4605ed06..327f8e29 100644 --- a/wgpu/src/renderer/widget/text.rs +++ b/graphics/src/widget/text.rs @@ -1,12 +1,23 @@ +//! Write some text for your users to read. +use crate::backend::{self, Backend}; use crate::{Primitive, Renderer}; +use iced_native::mouse; +use iced_native::text; use iced_native::{ - mouse, text, Color, Font, HorizontalAlignment, Rectangle, Size, - VerticalAlignment, + Color, Font, HorizontalAlignment, Rectangle, Size, VerticalAlignment, }; +/// A paragraph of text. +/// +/// This is an alias of an `iced_native` text with an `iced_wgpu::Renderer`. +pub type Text<Backend> = iced_native::Text<Renderer<Backend>>; + use std::f32; -impl text::Renderer for Renderer { +impl<B> text::Renderer for Renderer<B> +where + B: Backend + backend::Text, +{ type Font = Font; const DEFAULT_SIZE: u16 = 20; @@ -18,7 +29,7 @@ impl text::Renderer for Renderer { font: Font, bounds: Size, ) -> (f32, f32) { - self.text_pipeline + self.backend() .measure(content, f32::from(size), font, bounds) } diff --git a/wgpu/src/renderer/widget/text_input.rs b/graphics/src/widget/text_input.rs index 57be6692..893197d1 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/graphics/src/widget/text_input.rs @@ -1,14 +1,32 @@ -use crate::{text_input::StyleSheet, Primitive, Renderer}; - +//! Display fields that can be filled with text. +//! +//! A [`TextInput`] has some local [`State`]. +//! +//! [`TextInput`]: struct.TextInput.html +//! [`State`]: struct.State.html +use crate::backend::{self, Backend}; +use crate::{Primitive, Renderer}; +use iced_native::mouse; +use iced_native::text_input::{self, cursor}; use iced_native::{ - mouse, - text_input::{self, cursor}, Background, Color, Font, HorizontalAlignment, Point, Rectangle, Size, Vector, VerticalAlignment, }; use std::f32; -impl text_input::Renderer for Renderer { +pub use iced_native::text_input::State; +pub use iced_style::text_input::{Style, StyleSheet}; + +/// A field that can be filled with text. +/// +/// This is an alias of an `iced_native` text input with an `iced_wgpu::Renderer`. +pub type TextInput<'a, Message, Backend> = + iced_native::TextInput<'a, Message, Renderer<Backend>>; + +impl<B> text_input::Renderer for Renderer<B> +where + B: Backend + backend::Text, +{ type Style = Box<dyn StyleSheet>; fn default_size(&self) -> u16 { @@ -17,19 +35,10 @@ impl text_input::Renderer for Renderer { } fn measure_value(&self, value: &str, size: u16, font: Font) -> f32 { - let (mut width, _) = self.text_pipeline.measure( - value, - f32::from(size), - font, - Size::INFINITY, - ); - - let spaces_around = value.len() - value.trim().len(); + let backend = self.backend(); - if spaces_around > 0 { - let space_width = self.text_pipeline.space_width(size as f32); - width += spaces_around as f32 * space_width; - } + let (width, _) = + backend.measure(value, f32::from(size), font, Size::INFINITY); width } @@ -241,14 +250,17 @@ impl text_input::Renderer for Renderer { } } -fn measure_cursor_and_scroll_offset( - renderer: &Renderer, +fn measure_cursor_and_scroll_offset<B>( + renderer: &Renderer<B>, text_bounds: Rectangle, value: &text_input::Value, size: u16, cursor_index: usize, font: Font, -) -> (f32, f32) { +) -> (f32, f32) +where + B: Backend + backend::Text, +{ use iced_native::text_input::Renderer; let text_before_cursor = value.until(cursor_index).to_string(); diff --git a/graphics/src/window.rs b/graphics/src/window.rs new file mode 100644 index 00000000..3e74db5f --- /dev/null +++ b/graphics/src/window.rs @@ -0,0 +1,10 @@ +//! Draw graphics to window surfaces. +mod compositor; + +#[cfg(feature = "opengl")] +mod gl_compositor; + +pub use compositor::Compositor; + +#[cfg(feature = "opengl")] +pub use gl_compositor::GLCompositor; diff --git a/native/src/window/backend.rs b/graphics/src/window/compositor.rs index 892d4bb9..d5920c95 100644 --- a/native/src/window/backend.rs +++ b/graphics/src/window/compositor.rs @@ -1,14 +1,14 @@ -use crate::mouse; - +use crate::Viewport; +use iced_native::mouse; use raw_window_handle::HasRawWindowHandle; -/// A graphics backend that can render to windows. -pub trait Backend: Sized { +/// A graphics compositor that can draw to windows. +pub trait Compositor: Sized { /// The settings of the backend. type Settings: Default; /// The iced renderer of the backend. - type Renderer: crate::Renderer; + type Renderer: iced_native::Renderer; /// The surface of the backend. type Surface; @@ -16,7 +16,7 @@ pub trait Backend: Sized { /// The swap chain of the backend. type SwapChain; - /// Creates a new [`Backend`] and an associated iced renderer. + /// Creates a new [`Backend`]. /// /// [`Backend`]: trait.Backend.html fn new(settings: Self::Settings) -> (Self, Self::Renderer); @@ -48,8 +48,8 @@ pub trait Backend: Sized { &mut self, renderer: &mut Self::Renderer, swap_chain: &mut Self::SwapChain, - output: &<Self::Renderer as crate::Renderer>::Output, - scale_factor: f64, + viewport: &Viewport, + output: &<Self::Renderer as iced_native::Renderer>::Output, overlay: &[T], ) -> mouse::Interaction; } diff --git a/graphics/src/window/gl_compositor.rs b/graphics/src/window/gl_compositor.rs new file mode 100644 index 00000000..542213b5 --- /dev/null +++ b/graphics/src/window/gl_compositor.rs @@ -0,0 +1,67 @@ +use crate::{Size, Viewport}; +use iced_native::mouse; + +use core::ffi::c_void; + +/// A basic OpenGL compositor. +/// +/// A compositor is responsible for initializing a renderer and managing window +/// surfaces. +/// +/// For now, this compositor only deals with a single global surface +/// for drawing. However, the trait will most likely change in the near future +/// to handle multiple surfaces at once. +/// +/// If you implement an OpenGL renderer, you can implement this trait to ease +/// integration with existing windowing shells, like `iced_glutin`. +pub trait GLCompositor: Sized { + /// The renderer of the [`Compositor`]. + /// + /// This should point to your renderer type, which could be a type alias + /// of the [`Renderer`] provided in this crate with with a specific + /// [`Backend`]. + /// + /// [`Compositor`]: trait.Compositor.html + /// [`Renderer`]: ../struct.Renderer.html + /// [`Backend`]: ../backend/trait.Backend.html + type Renderer: iced_native::Renderer; + + /// The settings of the [`Compositor`]. + /// + /// It's up to you to decide the configuration supported by your renderer! + type Settings: Default; + + /// Creates a new [`Compositor`] and [`Renderer`] with the given + /// [`Settings`] and an OpenGL address loader function. + /// + /// [`Compositor`]: trait.Compositor.html + /// [`Renderer`]: #associatedtype.Renderer + /// [`Backend`]: ../backend/trait.Backend.html + #[allow(unsafe_code)] + unsafe fn new( + settings: Self::Settings, + loader_function: impl FnMut(&str) -> *const c_void, + ) -> (Self, Self::Renderer); + + /// Returns the amount of samples that should be used when configuring + /// an OpenGL context for this [`Compositor`]. + /// + /// [`Compositor`]: trait.Compositor.html + fn sample_count(settings: &Self::Settings) -> u32; + + /// Resizes the viewport of the [`Compositor`]. + /// + /// [`Compositor`]: trait.Compositor.html + fn resize_viewport(&mut self, physical_size: Size<u32>); + + /// Draws the provided output with the given [`Renderer`]. + /// + /// [`Compositor`]: trait.Compositor.html + fn draw<T: AsRef<str>>( + &mut self, + renderer: &mut Self::Renderer, + viewport: &Viewport, + output: &<Self::Renderer as iced_native::Renderer>::Output, + overlay: &[T], + ) -> mouse::Interaction; +} |