summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'graphics')
-rw-r--r--graphics/Cargo.toml29
-rw-r--r--graphics/src/antialiasing.rs2
-rw-r--r--graphics/src/backend.rs8
-rw-r--r--graphics/src/font.rs4
-rw-r--r--graphics/src/font/source.rs4
-rw-r--r--graphics/src/layer.rs92
-rw-r--r--graphics/src/lib.rs2
-rw-r--r--graphics/src/overlay/menu.rs31
-rw-r--r--graphics/src/primitive.rs4
-rw-r--r--graphics/src/renderer.rs16
-rw-r--r--graphics/src/triangle.rs11
-rw-r--r--graphics/src/viewport.rs18
-rw-r--r--graphics/src/widget.rs14
-rw-r--r--graphics/src/widget/button.rs12
-rw-r--r--graphics/src/widget/canvas.rs37
-rw-r--r--graphics/src/widget/canvas/cache.rs10
-rw-r--r--graphics/src/widget/canvas/cursor.rs8
-rw-r--r--graphics/src/widget/canvas/event.rs5
-rw-r--r--graphics/src/widget/canvas/frame.rs39
-rw-r--r--graphics/src/widget/canvas/geometry.rs8
-rw-r--r--graphics/src/widget/canvas/path.rs11
-rw-r--r--graphics/src/widget/canvas/path/arc.rs2
-rw-r--r--graphics/src/widget/canvas/path/builder.rs29
-rw-r--r--graphics/src/widget/canvas/program.rs25
-rw-r--r--graphics/src/widget/canvas/stroke.rs10
-rw-r--r--graphics/src/widget/column.rs12
-rw-r--r--graphics/src/widget/container.rs12
-rw-r--r--graphics/src/widget/image.rs5
-rw-r--r--graphics/src/widget/image/viewer.rs55
-rw-r--r--graphics/src/widget/pane_grid.rs134
-rw-r--r--graphics/src/widget/pick_list.rs21
-rw-r--r--graphics/src/widget/progress_bar.rs15
-rw-r--r--graphics/src/widget/qr_code.rs305
-rw-r--r--graphics/src/widget/radio.rs6
-rw-r--r--graphics/src/widget/row.rs12
-rw-r--r--graphics/src/widget/rule.rs4
-rw-r--r--graphics/src/widget/scrollable.rs50
-rw-r--r--graphics/src/widget/slider.rs25
-rw-r--r--graphics/src/widget/text_input.rs11
-rw-r--r--graphics/src/widget/toggler.rs99
-rw-r--r--graphics/src/widget/tooltip.rs168
-rw-r--r--graphics/src/window.rs2
-rw-r--r--graphics/src/window/compositor.rs44
-rw-r--r--graphics/src/window/gl_compositor.rs27
44 files changed, 1019 insertions, 419 deletions
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index dec24c59..ea9471c6 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -1,36 +1,51 @@
[package]
name = "iced_graphics"
-version = "0.1.0"
+version = "0.2.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
+description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced"
+license = "MIT"
+repository = "https://github.com/hecrj/iced"
+documentation = "https://docs.rs/iced_graphics"
+keywords = ["gui", "ui", "graphics", "interface", "widgets"]
+categories = ["gui"]
[features]
canvas = ["lyon"]
+qr_code = ["qrcode", "canvas"]
font-source = ["font-kit"]
font-fallback = []
font-icons = []
opengl = []
[dependencies]
-bytemuck = "1.2"
-glam = "0.9"
+glam = "0.10"
raw-window-handle = "0.3"
thiserror = "1.0"
+[dependencies.bytemuck]
+version = "1.4"
+features = ["derive"]
+
[dependencies.iced_native]
-version = "0.2"
+version = "0.4"
path = "../native"
[dependencies.iced_style]
-version = "0.1"
+version = "0.3"
path = "../style"
[dependencies.lyon]
-version = "0.15"
+version = "0.16"
+optional = true
+
+[dependencies.qrcode]
+version = "0.12"
optional = true
+default-features = false
[dependencies.font-kit]
-version = "0.6"
+version = "0.10"
optional = true
[package.metadata.docs.rs]
diff --git a/graphics/src/antialiasing.rs b/graphics/src/antialiasing.rs
index 34d94711..7631c97c 100644
--- a/graphics/src/antialiasing.rs
+++ b/graphics/src/antialiasing.rs
@@ -13,8 +13,6 @@ pub enum Antialiasing {
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,
diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs
index dd7dbbc2..ed1b9e08 100644
--- a/graphics/src/backend.rs
+++ b/graphics/src/backend.rs
@@ -5,7 +5,7 @@ use iced_native::{Font, Size};
/// The graphics backend of a [`Renderer`].
///
-/// [`Renderer`]: ../struct.Renderer.html
+/// [`Renderer`]: crate::Renderer
pub trait Backend {
/// Trims the measurements cache.
///
@@ -22,12 +22,12 @@ pub trait Text {
/// The `char` representing a ✔ icon in the [`ICON_FONT`].
///
- /// [`ICON_FONT`]: #associatedconst.ICON_FONT
+ /// [`ICON_FONT`]: Self::ICON_FONT
const CHECKMARK_ICON: char;
- /// The `char` representing a ▼ icon in the built-in [`ICONS`] font.
+ /// The `char` representing a ▼ icon in the built-in [`ICON_FONT`].
///
- /// [`ICON_FONT`]: #associatedconst.ICON_FONT
+ /// [`ICON_FONT`]: Self::ICON_FONT
const ARROW_DOWN_ICON: char;
/// Returns the default size of text.
diff --git a/graphics/src/font.rs b/graphics/src/font.rs
index 5c62681c..d55d0faf 100644
--- a/graphics/src/font.rs
+++ b/graphics/src/font.rs
@@ -26,14 +26,10 @@ pub const ICONS: iced_native::Font = iced_native::Font::External {
};
/// 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}';
/// The `char` representing a ▼ icon in the built-in [`ICONS`] font.
-///
-/// [`ICONS`]: const.ICONS.html
#[cfg(feature = "font-icons")]
pub const ARROW_DOWN_ICON: char = '\u{E800}';
diff --git a/graphics/src/font/source.rs b/graphics/src/font/source.rs
index 917291ff..a2d3f51d 100644
--- a/graphics/src/font/source.rs
+++ b/graphics/src/font/source.rs
@@ -8,8 +8,6 @@ pub struct Source {
impl Source {
/// Creates a new [`Source`].
- ///
- /// [`Source`]: struct.Source.html
pub fn new() -> Self {
Source {
raw: font_kit::source::SystemSource::new(),
@@ -17,8 +15,6 @@ impl Source {
}
/// 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
index 6aca738e..7dce1d4c 100644
--- a/graphics/src/layer.rs
+++ b/graphics/src/layer.rs
@@ -11,35 +11,23 @@ use crate::{
#[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,
@@ -53,8 +41,6 @@ impl<'a> Layer<'a> {
/// 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()));
@@ -87,8 +73,6 @@ impl<'a> Layer<'a> {
/// 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,
@@ -98,7 +82,12 @@ impl<'a> Layer<'a> {
let mut layers = vec![first_layer];
- Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive);
+ Self::process_primitive(
+ &mut layers,
+ Vector::new(0.0, 0.0),
+ primitive,
+ 0,
+ );
layers
}
@@ -107,13 +96,19 @@ impl<'a> Layer<'a> {
layers: &mut Vec<Self>,
translation: Vector,
primitive: &'a Primitive,
+ current_layer: usize,
) {
match primitive {
Primitive::None => {}
Primitive::Group { primitives } => {
// TODO: Inspect a bit and regroup (?)
for primitive in primitives {
- Self::process_primitive(layers, translation, primitive)
+ Self::process_primitive(
+ layers,
+ translation,
+ primitive,
+ current_layer,
+ )
}
}
Primitive::Text {
@@ -125,7 +120,7 @@ impl<'a> Layer<'a> {
horizontal_alignment,
vertical_alignment,
} => {
- let layer = layers.last_mut().unwrap();
+ let layer = &mut layers[current_layer];
layer.text.push(Text {
content,
@@ -144,7 +139,7 @@ impl<'a> Layer<'a> {
border_width,
border_color,
} => {
- let layer = layers.last_mut().unwrap();
+ let layer = &mut layers[current_layer];
// TODO: Move some of these computations to the GPU (?)
layer.quads.push(Quad {
@@ -156,13 +151,13 @@ impl<'a> Layer<'a> {
color: match background {
Background::Color(color) => color.into_linear(),
},
- border_radius: *border_radius as f32,
- border_width: *border_width as f32,
+ border_radius: *border_radius,
+ border_width: *border_width,
border_color: border_color.into_linear(),
});
}
Primitive::Mesh2D { buffers, size } => {
- let layer = layers.last_mut().unwrap();
+ let layer = &mut layers[current_layer];
let bounds = Rectangle::new(
Point::new(translation.x, translation.y),
@@ -183,7 +178,7 @@ impl<'a> Layer<'a> {
offset,
content,
} => {
- let layer = layers.last_mut().unwrap();
+ let layer = &mut layers[current_layer];
let translated_bounds = *bounds + translation;
// Only draw visible content
@@ -191,16 +186,15 @@ impl<'a> Layer<'a> {
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.len() - 1,
);
- layers.push(new_layer);
}
}
Primitive::Translate {
@@ -211,13 +205,19 @@ impl<'a> Layer<'a> {
layers,
translation + *new_translation,
&content,
+ current_layer,
);
}
Primitive::Cached { cache } => {
- Self::process_primitive(layers, translation, &cache);
+ Self::process_primitive(
+ layers,
+ translation,
+ &cache,
+ current_layer,
+ );
}
Primitive::Image { handle, bounds } => {
- let layer = layers.last_mut().unwrap();
+ let layer = &mut layers[current_layer];
layer.images.push(Image::Raster {
handle: handle.clone(),
@@ -225,7 +225,7 @@ impl<'a> Layer<'a> {
});
}
Primitive::Svg { handle, bounds } => {
- let layer = layers.last_mut().unwrap();
+ let layer = &mut layers[current_layer];
layer.images.push(Image::Vector {
handle: handle.clone(),
@@ -243,33 +243,21 @@ impl<'a> Layer<'a> {
#[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,
}
@@ -277,18 +265,12 @@ pub struct Quad {
#[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>,
}
@@ -296,38 +278,24 @@ pub struct Mesh<'a> {
#[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,
}
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index a3bd5364..14388653 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -1,6 +1,8 @@
//! A bunch of backend-agnostic types that can be leveraged to build a renderer
//! for [`iced`].
//!
+//! ![The native path of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
+//!
//! [`iced`]: https://github.com/hecrj/iced
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs
index a952f065..9e91a0ef 100644
--- a/graphics/src/overlay/menu.rs
+++ b/graphics/src/overlay/menu.rs
@@ -2,8 +2,8 @@
use crate::backend::{self, Backend};
use crate::{Primitive, Renderer};
use iced_native::{
- mouse, overlay, Color, Font, HorizontalAlignment, Point, Rectangle,
- VerticalAlignment,
+ mouse, overlay, Color, Font, HorizontalAlignment, Padding, Point,
+ Rectangle, VerticalAlignment,
};
pub use iced_style::menu::Style;
@@ -29,7 +29,7 @@ where
background: style.background,
border_color: style.border_color,
border_width: style.border_width,
- border_radius: 0,
+ border_radius: 0.0,
},
primitives,
],
@@ -42,9 +42,10 @@ where
&mut self,
bounds: Rectangle,
cursor_position: Point,
+ viewport: &Rectangle,
options: &[T],
hovered_option: Option<usize>,
- padding: u16,
+ padding: Padding,
text_size: u16,
font: Font,
style: &Style,
@@ -52,18 +53,26 @@ where
use std::f32;
let is_mouse_over = bounds.contains(cursor_position);
+ let option_height = (text_size + padding.vertical()) as usize;
let mut primitives = Vec::new();
- for (i, option) in options.iter().enumerate() {
+ let offset = viewport.y - bounds.y;
+ let start = (offset / option_height as f32) as usize;
+ let end =
+ ((offset + viewport.height) / option_height as f32).ceil() as usize;
+
+ let visible_options = &options[start..end.min(options.len())];
+
+ for (i, option) in visible_options.iter().enumerate() {
+ let i = start + i;
let is_selected = hovered_option == Some(i);
let bounds = Rectangle {
x: bounds.x,
- y: bounds.y
- + ((text_size as usize + padding as usize * 2) * i) as f32,
+ y: bounds.y + (option_height * i) as f32,
width: bounds.width,
- height: f32::from(text_size + padding * 2),
+ height: f32::from(text_size + padding.vertical()),
};
if is_selected {
@@ -71,15 +80,15 @@ where
bounds,
background: style.selected_background,
border_color: Color::TRANSPARENT,
- border_width: 0,
- border_radius: 0,
+ border_width: 0.0,
+ border_radius: 0.0,
});
}
primitives.push(Primitive::Text {
content: option.to_string(),
bounds: Rectangle {
- x: bounds.x + f32::from(padding),
+ x: bounds.x + padding.left as f32,
y: bounds.center_y(),
width: f32::INFINITY,
..bounds
diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs
index 95dbf7dd..30263bd4 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -40,9 +40,9 @@ pub enum Primitive {
/// The background of the quad
background: Background,
/// The border radius of the quad
- border_radius: u16,
+ border_radius: f32,
/// The border width of the quad
- border_width: u16,
+ border_width: f32,
/// The border color of the quad
border_color: Color,
},
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index 5d51e6d4..fa63991b 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -13,25 +13,16 @@ pub struct Renderer<B: Backend> {
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
}
@@ -96,10 +87,11 @@ where
widget: &dyn Widget<Message, Self>,
layout: Layout<'_>,
cursor_position: Point,
+ viewport: &Rectangle,
color: Color,
) -> Self::Output {
let (primitive, cursor) =
- widget.draw(self, defaults, layout, cursor_position);
+ widget.draw(self, defaults, layout, cursor_position, viewport);
let mut primitives = Vec::new();
@@ -118,8 +110,8 @@ fn explain_layout(
primitives.push(Primitive::Quad {
bounds: layout.bounds(),
background: Background::Color(Color::TRANSPARENT),
- border_radius: 0,
- border_width: 1,
+ border_radius: 0.0,
+ border_width: 1.0,
border_color: [0.6, 0.6, 0.6, 0.5].into(),
});
diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs
index ce879ffc..05028f51 100644
--- a/graphics/src/triangle.rs
+++ b/graphics/src/triangle.rs
@@ -1,8 +1,7 @@
//! Draw geometry using meshes of triangles.
+use bytemuck::{Pod, Zeroable};
/// 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
@@ -16,7 +15,7 @@ pub struct Mesh2D {
}
/// A two-dimensional vertex with some color in __linear__ RGBA.
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Zeroable, Pod)]
#[repr(C)]
pub struct Vertex2D {
/// The vertex position
@@ -24,9 +23,3 @@ pub struct Vertex2D {
/// 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
index 66122e6d..2c0b541a 100644
--- a/graphics/src/viewport.rs
+++ b/graphics/src/viewport.rs
@@ -1,7 +1,7 @@
use crate::{Size, Transformation};
/// A viewing region for displaying computer graphics.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct Viewport {
physical_size: Size<u32>,
logical_size: Size<f32>,
@@ -12,8 +12,6 @@ pub struct Viewport {
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,
@@ -27,43 +25,31 @@ impl Viewport {
}
/// 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
+ self.physical_size.width
}
/// 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
index f87b558a..e34d267f 100644
--- a/graphics/src/widget.rs
+++ b/graphics/src/widget.rs
@@ -20,6 +20,8 @@ pub mod scrollable;
pub mod slider;
pub mod svg;
pub mod text_input;
+pub mod toggler;
+pub mod tooltip;
mod column;
mod row;
@@ -48,6 +50,10 @@ pub use scrollable::Scrollable;
pub use slider::Slider;
#[doc(no_inline)]
pub use text_input::TextInput;
+#[doc(no_inline)]
+pub use toggler::Toggler;
+#[doc(no_inline)]
+pub use tooltip::Tooltip;
pub use column::Column;
pub use image::Image;
@@ -63,3 +69,11 @@ pub mod canvas;
#[cfg(feature = "canvas")]
#[doc(no_inline)]
pub use canvas::Canvas;
+
+#[cfg(feature = "qr_code")]
+#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
+pub mod qr_code;
+
+#[cfg(feature = "qr_code")]
+#[doc(no_inline)]
+pub use qr_code::QRCode;
diff --git a/graphics/src/widget/button.rs b/graphics/src/widget/button.rs
index ecabc868..60400ed8 100644
--- a/graphics/src/widget/button.rs
+++ b/graphics/src/widget/button.rs
@@ -1,14 +1,11 @@
//! 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::{
- Background, Color, Element, Layout, Point, Rectangle, Vector,
+ Background, Color, Element, Layout, Padding, Point, Rectangle, Vector,
};
pub use iced_native::button::State;
@@ -24,7 +21,7 @@ impl<B> iced_native::button::Renderer for Renderer<B>
where
B: Backend,
{
- const DEFAULT_PADDING: u16 = 5;
+ const DEFAULT_PADDING: Padding = Padding::new(5);
type Style = Box<dyn StyleSheet>;
@@ -62,10 +59,11 @@ where
},
content_layout,
cursor_position,
+ &bounds,
);
(
- if styling.background.is_some() || styling.border_width > 0 {
+ if styling.background.is_some() || styling.border_width > 0.0 {
let background = Primitive::Quad {
bounds,
background: styling
@@ -92,7 +90,7 @@ where
[0.0, 0.0, 0.0, 0.5].into(),
),
border_radius: styling.border_radius,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
};
diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs
index bc0802e5..7897c8ec 100644
--- a/graphics/src/widget/canvas.rs
+++ b/graphics/src/widget/canvas.rs
@@ -3,22 +3,21 @@
//! 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;
+use iced_native::mouse;
use iced_native::{
- layout, mouse, Clipboard, Element, Hasher, Layout, Length, Point, Size,
- Vector, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector,
+ Widget,
};
use std::hash::Hash;
use std::marker::PhantomData;
+pub mod event;
pub mod path;
mod cache;
mod cursor;
-mod event;
mod fill;
mod frame;
mod geometry;
@@ -39,8 +38,6 @@ 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`]:
@@ -106,8 +103,6 @@ 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),
@@ -118,16 +113,12 @@ impl<Message, P: Program<Message>> Canvas<Message, P> {
}
/// 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
@@ -163,10 +154,10 @@ where
event: iced_native::Event,
layout: Layout<'_>,
cursor_position: Point,
- messages: &mut Vec<Message>,
_renderer: &Renderer<B>,
- _clipboard: Option<&dyn Clipboard>,
- ) {
+ _clipboard: &mut dyn Clipboard,
+ messages: &mut Vec<Message>,
+ ) -> event::Status {
let bounds = layout.bounds();
let canvas_event = match event {
@@ -182,12 +173,17 @@ where
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)
- {
+ let (event_status, message) =
+ self.program.update(canvas_event, bounds, cursor);
+
+ if let Some(message) = message {
messages.push(message);
}
+
+ return event_status;
}
+
+ event::Status::Ignored
}
fn draw(
@@ -196,6 +192,7 @@ where
_defaults: &Defaults,
layout: Layout<'_>,
cursor_position: Point,
+ _viewport: &Rectangle,
) -> (Primitive, mouse::Interaction) {
let bounds = layout.bounds();
let translation = Vector::new(bounds.x, bounds.y);
diff --git a/graphics/src/widget/canvas/cache.rs b/graphics/src/widget/canvas/cache.rs
index 4b28d164..a469417d 100644
--- a/graphics/src/widget/canvas/cache.rs
+++ b/graphics/src/widget/canvas/cache.rs
@@ -23,10 +23,6 @@ impl Default for State {
///
/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer
/// change or it is explicitly cleared.
-///
-/// [`Layer`]: ../trait.Layer.html
-/// [`Cache`]: struct.Cache.html
-/// [`Geometry`]: struct.Geometry.html
#[derive(Debug, Default)]
pub struct Cache {
state: RefCell<State>,
@@ -34,8 +30,6 @@ pub struct Cache {
impl Cache {
/// Creates a new empty [`Cache`].
- ///
- /// [`Cache`]: struct.Cache.html
pub fn new() -> Self {
Cache {
state: Default::default(),
@@ -43,8 +37,6 @@ impl Cache {
}
/// Clears the [`Cache`], forcing a redraw the next time it is used.
- ///
- /// [`Cache`]: struct.Cache.html
pub fn clear(&mut self) {
*self.state.borrow_mut() = State::Empty;
}
@@ -59,8 +51,6 @@ impl Cache {
/// Otherwise, the previously stored [`Geometry`] will be returned. The
/// [`Cache`] is not cleared in this case. In other words, it will keep
/// returning the stored [`Geometry`] if needed.
- ///
- /// [`Cache`]: struct.Cache.html
pub fn draw(&self, bounds: Size, draw_fn: impl Fn(&mut Frame)) -> Geometry {
use std::ops::Deref;
diff --git a/graphics/src/widget/canvas/cursor.rs b/graphics/src/widget/canvas/cursor.rs
index 456760ea..9588d129 100644
--- a/graphics/src/widget/canvas/cursor.rs
+++ b/graphics/src/widget/canvas/cursor.rs
@@ -22,8 +22,6 @@ impl Cursor {
}
/// Returns the absolute position of the [`Cursor`], if available.
- ///
- /// [`Cursor`]: enum.Cursor.html
pub fn position(&self) -> Option<Point> {
match self {
Cursor::Available(position) => Some(*position),
@@ -36,8 +34,6 @@ impl Cursor {
///
/// If the [`Cursor`] is not over the provided bounds, this method will
/// return `None`.
- ///
- /// [`Cursor`]: enum.Cursor.html
pub fn position_in(&self, bounds: &Rectangle) -> Option<Point> {
if self.is_over(bounds) {
self.position_from(bounds.position())
@@ -48,8 +44,6 @@ impl Cursor {
/// Returns the relative position of the [`Cursor`] from the given origin,
/// if available.
- ///
- /// [`Cursor`]: enum.Cursor.html
pub fn position_from(&self, origin: Point) -> Option<Point> {
match self {
Cursor::Available(position) => {
@@ -61,8 +55,6 @@ impl Cursor {
/// Returns whether the [`Cursor`] is currently over the provided bounds
/// or not.
- ///
- /// [`Cursor`]: enum.Cursor.html
pub fn is_over(&self, bounds: &Rectangle) -> bool {
match self {
Cursor::Available(position) => bounds.contains(*position),
diff --git a/graphics/src/widget/canvas/event.rs b/graphics/src/widget/canvas/event.rs
index 0e66f0ff..5bf6f7a6 100644
--- a/graphics/src/widget/canvas/event.rs
+++ b/graphics/src/widget/canvas/event.rs
@@ -1,9 +1,12 @@
+//! Handle events of a canvas.
use iced_native::keyboard;
use iced_native::mouse;
+pub use iced_native::event::Status;
+
/// A [`Canvas`] event.
///
-/// [`Canvas`]: struct.Event.html
+/// [`Canvas`]: crate::widget::Canvas
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Event {
/// A mouse event.
diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs
index b5c6a2b1..5af9d11f 100644
--- a/graphics/src/widget/canvas/frame.rs
+++ b/graphics/src/widget/canvas/frame.rs
@@ -7,7 +7,7 @@ use crate::{
/// The frame of a [`Canvas`].
///
-/// [`Canvas`]: struct.Canvas.html
+/// [`Canvas`]: crate::widget::Canvas
#[derive(Debug)]
pub struct Frame {
size: Size,
@@ -33,8 +33,6 @@ impl Frame {
///
/// The default coordinate system of a [`Frame`] has its origin at the
/// top-left corner of its bounds.
- ///
- /// [`Frame`]: struct.Frame.html
pub fn new(size: Size) -> Frame {
Frame {
size,
@@ -51,32 +49,24 @@ impl Frame {
}
/// Returns the width of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn width(&self) -> f32 {
self.size.width
}
- /// Returns the width of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
+ /// Returns the height of the [`Frame`].
#[inline]
pub fn height(&self) -> f32 {
self.size.height
}
/// Returns the dimensions of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn size(&self) -> Size {
self.size
}
/// Returns the coordinate of the center of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn center(&self) -> Point {
Point::new(self.size.width / 2.0, self.size.height / 2.0)
@@ -84,9 +74,6 @@ impl Frame {
/// Draws the given [`Path`] on the [`Frame`] by filling it with the
/// provided style.
- ///
- /// [`Path`]: path/struct.Path.html
- /// [`Frame`]: struct.Frame.html
pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
use lyon::tessellation::{
BuffersBuilder, FillOptions, FillTessellator,
@@ -115,8 +102,6 @@ impl Frame {
/// Draws an axis-aligned rectangle given its top-left corner coordinate and
/// its `Size` on the [`Frame`] by filling it with the provided style.
- ///
- /// [`Frame`]: struct.Frame.html
pub fn fill_rectangle(
&mut self,
top_left: Point,
@@ -152,9 +137,6 @@ impl Frame {
/// Draws the stroke of the given [`Path`] on the [`Frame`] with the
/// provided style.
- ///
- /// [`Path`]: path/struct.Path.html
- /// [`Frame`]: struct.Frame.html
pub fn stroke(&mut self, path: &Path, stroke: impl Into<Stroke>) {
use lyon::tessellation::{
BuffersBuilder, StrokeOptions, StrokeTessellator,
@@ -200,9 +182,7 @@ impl Frame {
/// Support for vectorial text is planned, and should address all these
/// limitations.
///
- /// [`Text`]: struct.Text.html
- /// [`Frame`]: struct.Frame.html
- /// [`Canvas`]: struct.Canvas.html
+ /// [`Canvas`]: crate::widget::Canvas
pub fn fill_text(&mut self, text: impl Into<Text>) {
use std::f32;
@@ -240,8 +220,6 @@ impl Frame {
///
/// This method is useful to compose transforms and perform drawing
/// operations in different coordinate systems.
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) {
self.transforms.previous.push(self.transforms.current);
@@ -252,8 +230,6 @@ impl Frame {
}
/// Applies a translation to the current transform of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn translate(&mut self, translation: Vector) {
self.transforms.current.raw = self
@@ -268,21 +244,17 @@ impl Frame {
}
/// Applies a rotation to the current transform of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn rotate(&mut self, angle: f32) {
self.transforms.current.raw = self
.transforms
.current
.raw
- .pre_rotate(lyon::math::Angle::radians(-angle));
+ .pre_rotate(lyon::math::Angle::radians(angle));
self.transforms.current.is_identity = false;
}
/// Applies a scaling to the current transform of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
#[inline]
pub fn scale(&mut self, scale: f32) {
self.transforms.current.raw =
@@ -291,9 +263,6 @@ impl Frame {
}
/// Produces the [`Geometry`] representing everything drawn on the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- /// [`Geometry`]: struct.Geometry.html
pub fn into_geometry(mut self) -> Geometry {
if !self.buffers.indices.is_empty() {
self.primitives.push(Primitive::Mesh2D {
diff --git a/graphics/src/widget/canvas/geometry.rs b/graphics/src/widget/canvas/geometry.rs
index 4cadee39..8915cda1 100644
--- a/graphics/src/widget/canvas/geometry.rs
+++ b/graphics/src/widget/canvas/geometry.rs
@@ -5,9 +5,8 @@ use crate::Primitive;
/// [`Geometry`] can be easily generated with a [`Frame`] or stored in a
/// [`Cache`].
///
-/// [`Geometry`]: struct.Geometry.html
-/// [`Frame`]: struct.Frame.html
-/// [`Cache`]: struct.Cache.html
+/// [`Frame`]: crate::widget::canvas::Frame
+/// [`Cache`]: crate::widget::canvas::Cache
#[derive(Debug, Clone)]
pub struct Geometry(Primitive);
@@ -19,9 +18,6 @@ impl Geometry {
/// Turns the [`Geometry`] into a [`Primitive`].
///
/// This can be useful if you are building a custom widget.
- ///
- /// [`Geometry`]: struct.Geometry.html
- /// [`Primitive`]: ../enum.Primitive.html
pub fn into_primitive(self) -> Primitive {
self.0
}
diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs
index c26bf187..6de19321 100644
--- a/graphics/src/widget/canvas/path.rs
+++ b/graphics/src/widget/canvas/path.rs
@@ -12,8 +12,6 @@ use iced_native::{Point, Size};
/// An immutable set of points that may or may not be connected.
///
/// A single [`Path`] can represent different kinds of 2D shapes!
-///
-/// [`Path`]: struct.Path.html
#[derive(Debug, Clone)]
pub struct Path {
raw: lyon::path::Path,
@@ -23,9 +21,6 @@ impl Path {
/// Creates a new [`Path`] with the provided closure.
///
/// Use the [`Builder`] to configure your [`Path`].
- ///
- /// [`Path`]: struct.Path.html
- /// [`Builder`]: struct.Builder.html
pub fn new(f: impl FnOnce(&mut Builder)) -> Self {
let mut builder = Builder::new();
@@ -37,8 +32,6 @@ impl Path {
/// Creates a new [`Path`] representing a line segment given its starting
/// and end points.
- ///
- /// [`Path`]: struct.Path.html
pub fn line(from: Point, to: Point) -> Self {
Self::new(|p| {
p.move_to(from);
@@ -48,16 +41,12 @@ impl Path {
/// Creates a new [`Path`] representing a rectangle given its top-left
/// corner coordinate and its `Size`.
- ///
- /// [`Path`]: struct.Path.html
pub fn rectangle(top_left: Point, size: Size) -> Self {
Self::new(|p| p.rectangle(top_left, size))
}
/// Creates a new [`Path`] representing a circle given its center
/// coordinate and its radius.
- ///
- /// [`Path`]: struct.Path.html
pub fn circle(center: Point, radius: f32) -> Self {
Self::new(|p| p.circle(center, radius))
}
diff --git a/graphics/src/widget/canvas/path/arc.rs b/graphics/src/widget/canvas/path/arc.rs
index 343191f1..b8e72daf 100644
--- a/graphics/src/widget/canvas/path/arc.rs
+++ b/graphics/src/widget/canvas/path/arc.rs
@@ -15,8 +15,6 @@ pub struct Arc {
}
/// An elliptical [`Arc`].
-///
-/// [`Arc`]: struct.Arc.html
#[derive(Debug, Clone, Copy)]
pub struct Elliptical {
/// The center of the arc.
diff --git a/graphics/src/widget/canvas/path/builder.rs b/graphics/src/widget/canvas/path/builder.rs
index e0e52845..5ce0e02c 100644
--- a/graphics/src/widget/canvas/path/builder.rs
+++ b/graphics/src/widget/canvas/path/builder.rs
@@ -6,8 +6,6 @@ use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder};
/// A [`Path`] builder.
///
/// Once a [`Path`] is built, it can no longer be mutated.
-///
-/// [`Path`]: struct.Path.html
#[allow(missing_debug_implementations)]
pub struct Builder {
raw: lyon::path::builder::SvgPathBuilder<lyon::path::Builder>,
@@ -15,8 +13,6 @@ pub struct Builder {
impl Builder {
/// Creates a new [`Builder`].
- ///
- /// [`Builder`]: struct.Builder.html
pub fn new() -> Builder {
Builder {
raw: lyon::path::Path::builder().with_svg(),
@@ -31,8 +27,6 @@ impl Builder {
/// Connects the last point in the [`Path`] to the given `Point` with a
/// straight line.
- ///
- /// [`Path`]: struct.Path.html
#[inline]
pub fn line_to(&mut self, point: Point) {
let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y));
@@ -40,9 +34,6 @@ impl Builder {
/// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in
/// a clockwise direction.
- ///
- /// [`Arc`]: struct.Arc.html
- /// [`Path`]: struct.Path.html
#[inline]
pub fn arc(&mut self, arc: Arc) {
self.ellipse(arc.into());
@@ -53,8 +44,6 @@ impl Builder {
///
/// The arc is connected to the previous point by a straight line, if
/// necessary.
- ///
- /// [`Path`]: struct.Path.html
pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {
use lyon::{math, path};
@@ -72,10 +61,7 @@ impl Builder {
);
}
- /// Adds an [`Ellipse`] to the [`Path`] using a clockwise direction.
- ///
- /// [`Ellipse`]: struct.Arc.html
- /// [`Path`]: struct.Path.html
+ /// Adds an ellipse to the [`Path`] using a clockwise direction.
pub fn ellipse(&mut self, arc: arc::Elliptical) {
use lyon::{geom, math};
@@ -96,8 +82,6 @@ impl Builder {
/// Adds a cubic Bézier curve to the [`Path`] given its two control points
/// and its end point.
- ///
- /// [`Path`]: struct.Path.html
#[inline]
pub fn bezier_curve_to(
&mut self,
@@ -116,8 +100,6 @@ impl Builder {
/// Adds a quadratic Bézier curve to the [`Path`] given its control point
/// and its end point.
- ///
- /// [`Path`]: struct.Path.html
#[inline]
pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
use lyon::math;
@@ -130,8 +112,6 @@ impl Builder {
/// Adds a rectangle to the [`Path`] given its top-left corner coordinate
/// and its `Size`.
- ///
- /// [`Path`]: struct.Path.html
#[inline]
pub fn rectangle(&mut self, top_left: Point, size: Size) {
self.move_to(top_left);
@@ -146,8 +126,6 @@ impl Builder {
/// Adds a circle to the [`Path`] given its center coordinate and its
/// radius.
- ///
- /// [`Path`]: struct.Path.html
#[inline]
pub fn circle(&mut self, center: Point, radius: f32) {
self.arc(Arc {
@@ -160,17 +138,12 @@ impl Builder {
/// Closes the current sub-path in the [`Path`] with a straight line to
/// the starting point.
- ///
- /// [`Path`]: struct.Path.html
#[inline]
pub fn close(&mut self) {
self.raw.close()
}
/// Builds the [`Path`] of this [`Builder`].
- ///
- /// [`Path`]: struct.Path.html
- /// [`Builder`]: struct.Builder.html
#[inline]
pub fn build(self) -> Path {
Path {
diff --git a/graphics/src/widget/canvas/program.rs b/graphics/src/widget/canvas/program.rs
index 725d9d72..85a2f67b 100644
--- a/graphics/src/widget/canvas/program.rs
+++ b/graphics/src/widget/canvas/program.rs
@@ -1,4 +1,5 @@
-use crate::canvas::{Cursor, Event, Geometry};
+use crate::canvas::event::{self, Event};
+use crate::canvas::{Cursor, Geometry};
use iced_native::{mouse, Rectangle};
/// The state and logic of a [`Canvas`].
@@ -6,8 +7,7 @@ use iced_native::{mouse, Rectangle};
/// A [`Program`] can mutate internal state and produce messages for an
/// application.
///
-/// [`Canvas`]: struct.Canvas.html
-/// [`Program`]: trait.Program.html
+/// [`Canvas`]: crate::widget::Canvas
pub trait Program<Message> {
/// Updates the state of the [`Program`].
///
@@ -19,16 +19,14 @@ pub trait Program<Message> {
///
/// By default, this method does and returns nothing.
///
- /// [`Program`]: trait.Program.html
- /// [`Canvas`]: struct.Canvas.html
- /// [`Event`]: enum.Event.html
+ /// [`Canvas`]: crate::widget::Canvas
fn update(
&mut self,
_event: Event,
_bounds: Rectangle,
_cursor: Cursor,
- ) -> Option<Message> {
- None
+ ) -> (event::Status, Option<Message>) {
+ (event::Status::Ignored, None)
}
/// Draws the state of the [`Program`], producing a bunch of [`Geometry`].
@@ -36,10 +34,8 @@ pub trait Program<Message> {
/// [`Geometry`] can be easily generated with a [`Frame`] or stored in a
/// [`Cache`].
///
- /// [`Program`]: trait.Program.html
- /// [`Geometry`]: struct.Geometry.html
- /// [`Frame`]: struct.Frame.html
- /// [`Cache`]: struct.Cache.html
+ /// [`Frame`]: crate::widget::canvas::Frame
+ /// [`Cache`]: crate::widget::canvas::Cache
fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry>;
/// Returns the current mouse interaction of the [`Program`].
@@ -47,8 +43,7 @@ pub trait Program<Message> {
/// The interaction returned will be in effect even if the cursor position
/// is out of bounds of the program's [`Canvas`].
///
- /// [`Program`]: trait.Program.html
- /// [`Canvas`]: struct.Canvas.html
+ /// [`Canvas`]: crate::widget::Canvas
fn mouse_interaction(
&self,
_bounds: Rectangle,
@@ -67,7 +62,7 @@ where
event: Event,
bounds: Rectangle,
cursor: Cursor,
- ) -> Option<Message> {
+ ) -> (event::Status, Option<Message>) {
T::update(self, event, bounds, cursor)
}
diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs
index 5b6fc56a..9f0449d0 100644
--- a/graphics/src/widget/canvas/stroke.rs
+++ b/graphics/src/widget/canvas/stroke.rs
@@ -16,31 +16,21 @@ pub struct Stroke {
impl Stroke {
/// Sets the color of the [`Stroke`].
- ///
- /// [`Stroke`]: struct.Stroke.html
pub fn with_color(self, color: Color) -> Stroke {
Stroke { color, ..self }
}
/// Sets the width of the [`Stroke`].
- ///
- /// [`Stroke`]: struct.Stroke.html
pub fn with_width(self, width: f32) -> Stroke {
Stroke { width, ..self }
}
/// Sets the [`LineCap`] of the [`Stroke`].
- ///
- /// [`LineCap`]: enum.LineCap.html
- /// [`Stroke`]: struct.Stroke.html
pub fn with_line_cap(self, line_cap: LineCap) -> Stroke {
Stroke { line_cap, ..self }
}
/// Sets the [`LineJoin`] of the [`Stroke`].
- ///
- /// [`LineJoin`]: enum.LineJoin.html
- /// [`Stroke`]: struct.Stroke.html
pub fn with_line_join(self, line_join: LineJoin) -> Stroke {
Stroke { line_join, ..self }
}
diff --git a/graphics/src/widget/column.rs b/graphics/src/widget/column.rs
index 6c7235c7..0cf56842 100644
--- a/graphics/src/widget/column.rs
+++ b/graphics/src/widget/column.rs
@@ -1,7 +1,7 @@
use crate::{Backend, Primitive, Renderer};
use iced_native::column;
use iced_native::mouse;
-use iced_native::{Element, Layout, Point};
+use iced_native::{Element, Layout, Point, Rectangle};
/// A container that distributes its contents vertically.
pub type Column<'a, Message, Backend> =
@@ -17,6 +17,7 @@ where
content: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
+ viewport: &Rectangle,
) -> Self::Output {
let mut mouse_interaction = mouse::Interaction::default();
@@ -26,8 +27,13 @@ where
.iter()
.zip(layout.children())
.map(|(child, layout)| {
- let (primitive, new_mouse_interaction) =
- child.draw(self, defaults, layout, cursor_position);
+ let (primitive, new_mouse_interaction) = child.draw(
+ self,
+ defaults,
+ layout,
+ cursor_position,
+ viewport,
+ );
if new_mouse_interaction > mouse_interaction {
mouse_interaction = new_mouse_interaction;
diff --git a/graphics/src/widget/container.rs b/graphics/src/widget/container.rs
index 5b3a01d2..aae3e1d8 100644
--- a/graphics/src/widget/container.rs
+++ b/graphics/src/widget/container.rs
@@ -24,6 +24,7 @@ where
defaults: &Defaults,
bounds: Rectangle,
cursor_position: Point,
+ viewport: &Rectangle,
style_sheet: &Self::Style,
content: &Element<'_, Message, Self>,
content_layout: Layout<'_>,
@@ -36,8 +37,13 @@ where
},
};
- let (content, mouse_interaction) =
- content.draw(self, &defaults, content_layout, cursor_position);
+ let (content, mouse_interaction) = content.draw(
+ self,
+ &defaults,
+ content_layout,
+ cursor_position,
+ viewport,
+ );
if let Some(background) = background(bounds, &style) {
(
@@ -56,7 +62,7 @@ pub(crate) fn background(
bounds: Rectangle,
style: &container::Style,
) -> Option<Primitive> {
- if style.background.is_some() || style.border_width > 0 {
+ if style.background.is_some() || style.border_width > 0.0 {
Some(Primitive::Quad {
bounds,
background: style
diff --git a/graphics/src/widget/image.rs b/graphics/src/widget/image.rs
index 30f446e8..bdf03de3 100644
--- a/graphics/src/widget/image.rs
+++ b/graphics/src/widget/image.rs
@@ -1,11 +1,14 @@
//! Display images in your user interface.
+pub mod viewer;
+
use crate::backend::{self, Backend};
+
use crate::{Primitive, Renderer};
use iced_native::image;
use iced_native::mouse;
use iced_native::Layout;
-pub use iced_native::image::{Handle, Image};
+pub use iced_native::image::{Handle, Image, Viewer};
impl<B> image::Renderer for Renderer<B>
where
diff --git a/graphics/src/widget/image/viewer.rs b/graphics/src/widget/image/viewer.rs
new file mode 100644
index 00000000..28dffc4f
--- /dev/null
+++ b/graphics/src/widget/image/viewer.rs
@@ -0,0 +1,55 @@
+//! Zoom and pan on an image.
+use crate::backend::{self, Backend};
+use crate::{Primitive, Renderer};
+
+use iced_native::image;
+use iced_native::image::viewer;
+use iced_native::mouse;
+use iced_native::{Rectangle, Size, Vector};
+
+impl<B> viewer::Renderer for Renderer<B>
+where
+ B: Backend + backend::Image,
+{
+ fn draw(
+ &mut self,
+ state: &viewer::State,
+ bounds: Rectangle,
+ image_size: Size,
+ translation: Vector,
+ handle: image::Handle,
+ is_mouse_over: bool,
+ ) -> Self::Output {
+ (
+ {
+ Primitive::Clip {
+ bounds,
+ content: Box::new(Primitive::Translate {
+ translation,
+ content: Box::new(Primitive::Image {
+ handle,
+ bounds: Rectangle {
+ x: bounds.x,
+ y: bounds.y,
+ ..Rectangle::with_size(image_size)
+ },
+ }),
+ }),
+ offset: Vector::new(0, 0),
+ }
+ },
+ {
+ if state.is_cursor_grabbed() {
+ mouse::Interaction::Grabbing
+ } else if is_mouse_over
+ && (image_size.width > bounds.width
+ || image_size.height > bounds.height)
+ {
+ mouse::Interaction::Grab
+ } else {
+ mouse::Interaction::Idle
+ }
+ },
+ )
+ }
+}
diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs
index aa8a3f7c..92cdbb77 100644
--- a/graphics/src/widget/pane_grid.rs
+++ b/graphics/src/widget/pane_grid.rs
@@ -6,24 +6,21 @@
//! 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::{self, Backend};
+//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
use crate::defaults;
-use crate::{Primitive, Renderer};
+use crate::{Backend, Color, Primitive, Renderer};
+use iced_native::container;
use iced_native::mouse;
use iced_native::pane_grid;
-use iced_native::text;
-use iced_native::{
- Element, HorizontalAlignment, Layout, Point, Rectangle, Vector,
- VerticalAlignment,
-};
+use iced_native::{Element, Layout, Point, Rectangle, Vector};
pub use iced_native::pane_grid::{
- Axis, Configuration, Content, Direction, DragEvent, Focus, KeyPressEvent,
- Pane, ResizeEvent, Split, State, TitleBar,
+ Axis, Configuration, Content, Direction, DragEvent, Node, Pane,
+ ResizeEvent, Split, State, TitleBar,
};
+pub use iced_style::pane_grid::{Line, StyleSheet};
+
/// A collection of panes distributed using either vertical or horizontal splits
/// to completely fill the space available.
///
@@ -35,16 +32,20 @@ pub type PaneGrid<'a, Message, Backend> =
impl<B> pane_grid::Renderer for Renderer<B>
where
- B: Backend + backend::Text,
+ B: Backend,
{
+ type Style = Box<dyn StyleSheet>;
+
fn draw<Message>(
&mut self,
defaults: &Self::Defaults,
content: &[(Pane, Content<'_, Message, Self>)],
dragging: Option<(Pane, Point)>,
- resizing: Option<Axis>,
+ resizing: Option<(Axis, Rectangle, bool)>,
layout: Layout<'_>,
+ style_sheet: &<Self as pane_grid::Renderer>::Style,
cursor_position: Point,
+ viewport: &Rectangle,
) -> Self::Output {
let pane_cursor_position = if dragging.is_some() {
// TODO: Remove once cursor availability is encoded in the type
@@ -62,8 +63,13 @@ where
.zip(layout.children())
.enumerate()
.map(|(i, ((id, pane), layout))| {
- let (primitive, new_mouse_interaction) =
- pane.draw(self, defaults, layout, pane_cursor_position);
+ let (primitive, new_mouse_interaction) = pane.draw(
+ self,
+ defaults,
+ layout,
+ pane_cursor_position,
+ viewport,
+ );
if new_mouse_interaction > mouse_interaction {
mouse_interaction = new_mouse_interaction;
@@ -79,7 +85,8 @@ where
})
.collect();
- let primitives = if let Some((index, layout, origin)) = dragged_pane {
+ let mut primitives = if let Some((index, layout, origin)) = dragged_pane
+ {
let pane = panes.remove(index);
let bounds = layout.bounds();
@@ -109,15 +116,62 @@ where
panes
};
+ let (primitives, mouse_interaction) =
+ if let Some((axis, split_region, is_picked)) = resizing {
+ let highlight = if is_picked {
+ style_sheet.picked_split()
+ } else {
+ style_sheet.hovered_split()
+ };
+
+ if let Some(highlight) = highlight {
+ primitives.push(Primitive::Quad {
+ bounds: match axis {
+ Axis::Horizontal => Rectangle {
+ x: split_region.x,
+ y: (split_region.y
+ + (split_region.height - highlight.width)
+ / 2.0)
+ .round(),
+ width: split_region.width,
+ height: highlight.width,
+ },
+ Axis::Vertical => Rectangle {
+ x: (split_region.x
+ + (split_region.width - highlight.width)
+ / 2.0)
+ .round(),
+ y: split_region.y,
+ width: highlight.width,
+ height: split_region.height,
+ },
+ },
+ background: highlight.color.into(),
+ border_radius: 0.0,
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ });
+ }
+
+ (
+ primitives,
+ match axis {
+ Axis::Horizontal => {
+ mouse::Interaction::ResizingVertically
+ }
+ Axis::Vertical => {
+ mouse::Interaction::ResizingHorizontally
+ }
+ },
+ )
+ } else {
+ (primitives, mouse_interaction)
+ };
+
(
Primitive::Group { primitives },
if dragging.is_some() {
mouse::Interaction::Grabbing
- } else if let Some(axis) = resizing {
- match axis {
- Axis::Horizontal => mouse::Interaction::ResizingVertically,
- Axis::Vertical => mouse::Interaction::ResizingHorizontally,
- }
} else {
mouse_interaction
},
@@ -128,16 +182,17 @@ where
&mut self,
defaults: &Self::Defaults,
bounds: Rectangle,
- style_sheet: &Self::Style,
+ style_sheet: &<Self as container::Renderer>::Style,
title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>,
body: (&Element<'_, Message, Self>, Layout<'_>),
cursor_position: Point,
+ viewport: &Rectangle,
) -> Self::Output {
let style = style_sheet.style();
let (body, body_layout) = body;
let (body_primitive, body_interaction) =
- body.draw(self, defaults, body_layout, cursor_position);
+ body.draw(self, defaults, body_layout, cursor_position, viewport);
let background = crate::widget::container::background(bounds, &style);
@@ -151,6 +206,7 @@ where
defaults,
title_bar_layout,
cursor_position,
+ viewport,
show_controls,
);
@@ -162,10 +218,10 @@ where
body_primitive,
],
},
- if is_over_pick_area {
- mouse::Interaction::Grab
- } else if title_bar_interaction > body_interaction {
+ if title_bar_interaction > body_interaction {
title_bar_interaction
+ } else if is_over_pick_area {
+ mouse::Interaction::Grab
} else {
body_interaction
},
@@ -188,15 +244,14 @@ where
&mut self,
defaults: &Self::Defaults,
bounds: Rectangle,
- style_sheet: &Self::Style,
- title: &str,
- title_size: u16,
- title_font: Self::Font,
- title_bounds: Rectangle,
+ style_sheet: &<Self as container::Renderer>::Style,
+ content: (&Element<'_, Message, Self>, Layout<'_>),
controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>,
cursor_position: Point,
+ viewport: &Rectangle,
) -> Self::Output {
let style = style_sheet.style();
+ let (title_content, title_layout) = content;
let defaults = Self::Defaults {
text: defaults::Text {
@@ -206,16 +261,12 @@ where
let background = crate::widget::container::background(bounds, &style);
- let (title_primitive, _) = text::Renderer::draw(
+ let (title_primitive, title_interaction) = title_content.draw(
self,
&defaults,
- title_bounds,
- title,
- title_size,
- title_font,
- None,
- HorizontalAlignment::Left,
- VerticalAlignment::Top,
+ title_layout,
+ cursor_position,
+ viewport,
);
if let Some((controls, controls_layout)) = controls {
@@ -224,6 +275,7 @@ where
&defaults,
controls_layout,
cursor_position,
+ viewport,
);
(
@@ -234,7 +286,7 @@ where
controls_primitive,
],
},
- controls_interaction,
+ controls_interaction.max(title_interaction),
)
} else {
(
@@ -245,7 +297,7 @@ where
} else {
title_primitive
},
- mouse::Interaction::default(),
+ title_interaction,
)
}
}
diff --git a/graphics/src/widget/pick_list.rs b/graphics/src/widget/pick_list.rs
index f42a8707..88a590b5 100644
--- a/graphics/src/widget/pick_list.rs
+++ b/graphics/src/widget/pick_list.rs
@@ -2,7 +2,8 @@
use crate::backend::{self, Backend};
use crate::{Primitive, Renderer};
use iced_native::{
- mouse, Font, HorizontalAlignment, Point, Rectangle, VerticalAlignment,
+ mouse, Font, HorizontalAlignment, Padding, Point, Rectangle,
+ VerticalAlignment,
};
use iced_style::menu;
@@ -19,7 +20,7 @@ where
{
type Style = Box<dyn StyleSheet>;
- const DEFAULT_PADDING: u16 = 5;
+ const DEFAULT_PADDING: Padding = Padding::new(5);
fn menu_style(style: &Box<dyn StyleSheet>) -> menu::Style {
style.menu()
@@ -30,12 +31,14 @@ where
bounds: Rectangle,
cursor_position: Point,
selected: Option<String>,
- padding: u16,
+ placeholder: Option<&str>,
+ padding: Padding,
text_size: u16,
font: Font,
style: &Box<dyn StyleSheet>,
) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position);
+ let is_selected = selected.is_some();
let style = if is_mouse_over {
style.hovered()
@@ -56,7 +59,7 @@ where
font: B::ICON_FONT,
size: bounds.height * style.icon_size,
bounds: Rectangle {
- x: bounds.x + bounds.width - f32::from(padding) * 2.0,
+ x: bounds.x + bounds.width - f32::from(padding.horizontal()),
y: bounds.center_y(),
..bounds
},
@@ -67,14 +70,18 @@ where
(
Primitive::Group {
- primitives: if let Some(label) = selected {
+ primitives: if let Some(label) =
+ selected.or_else(|| placeholder.map(str::to_string))
+ {
let label = Primitive::Text {
content: label,
size: f32::from(text_size),
font,
- color: style.text_color,
+ color: is_selected
+ .then(|| style.text_color)
+ .unwrap_or(style.placeholder_color),
bounds: Rectangle {
- x: bounds.x + f32::from(padding),
+ x: bounds.x + f32::from(padding.left),
y: bounds.center_y(),
..bounds
},
diff --git a/graphics/src/widget/progress_bar.rs b/graphics/src/widget/progress_bar.rs
index 48acb3c1..32ee42c6 100644
--- a/graphics/src/widget/progress_bar.rs
+++ b/graphics/src/widget/progress_bar.rs
@@ -2,8 +2,6 @@
//!
//! 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;
@@ -33,17 +31,20 @@ where
style_sheet: &Self::Style,
) -> Self::Output {
let style = style_sheet.style();
-
let (range_start, range_end) = range.into_inner();
- let active_progress_width = bounds.width
- * ((value - range_start) / (range_end - range_start).max(1.0));
+
+ let active_progress_width = if range_start >= range_end {
+ 0.0
+ } else {
+ bounds.width * (value - range_start) / (range_end - range_start)
+ };
let background = Primitive::Group {
primitives: vec![Primitive::Quad {
bounds: Rectangle { ..bounds },
background: style.background,
border_radius: style.border_radius,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
}],
};
@@ -57,7 +58,7 @@ where
},
background: style.bar,
border_radius: style.border_radius,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
};
diff --git a/graphics/src/widget/qr_code.rs b/graphics/src/widget/qr_code.rs
new file mode 100644
index 00000000..b3a01dd7
--- /dev/null
+++ b/graphics/src/widget/qr_code.rs
@@ -0,0 +1,305 @@
+//! Encode and display information in a QR code.
+use crate::canvas;
+use crate::{Backend, Defaults, Primitive, Renderer, Vector};
+
+use iced_native::{
+ layout, mouse, Color, Element, Hasher, Layout, Length, Point, Rectangle,
+ Size, Widget,
+};
+use thiserror::Error;
+
+const DEFAULT_CELL_SIZE: u16 = 4;
+const QUIET_ZONE: usize = 2;
+
+/// A type of matrix barcode consisting of squares arranged in a grid which
+/// can be read by an imaging device, such as a camera.
+#[derive(Debug)]
+pub struct QRCode<'a> {
+ state: &'a State,
+ dark: Color,
+ light: Color,
+ cell_size: u16,
+}
+
+impl<'a> QRCode<'a> {
+ /// Creates a new [`QRCode`] with the provided [`State`].
+ pub fn new(state: &'a State) -> Self {
+ Self {
+ cell_size: DEFAULT_CELL_SIZE,
+ dark: Color::BLACK,
+ light: Color::WHITE,
+ state,
+ }
+ }
+
+ /// Sets both the dark and light [`Color`]s of the [`QRCode`].
+ pub fn color(mut self, dark: Color, light: Color) -> Self {
+ self.dark = dark;
+ self.light = light;
+ self
+ }
+
+ /// Sets the size of the squares of the grid cell of the [`QRCode`].
+ pub fn cell_size(mut self, cell_size: u16) -> Self {
+ self.cell_size = cell_size;
+ self
+ }
+}
+
+impl<'a, Message, B> Widget<Message, Renderer<B>> for QRCode<'a>
+where
+ B: Backend,
+{
+ fn width(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn height(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn layout(
+ &self,
+ _renderer: &Renderer<B>,
+ _limits: &layout::Limits,
+ ) -> layout::Node {
+ let side_length = (self.state.width + 2 * QUIET_ZONE) as f32
+ * f32::from(self.cell_size);
+
+ layout::Node::new(Size::new(
+ f32::from(side_length),
+ f32::from(side_length),
+ ))
+ }
+
+ fn hash_layout(&self, state: &mut Hasher) {
+ use std::hash::Hash;
+
+ self.state.contents.hash(state);
+ }
+
+ fn draw(
+ &self,
+ _renderer: &mut Renderer<B>,
+ _defaults: &Defaults,
+ layout: Layout<'_>,
+ _cursor_position: Point,
+ _viewport: &Rectangle,
+ ) -> (Primitive, mouse::Interaction) {
+ let bounds = layout.bounds();
+ let side_length = self.state.width + 2 * QUIET_ZONE;
+
+ // Reuse cache if possible
+ let geometry = self.state.cache.draw(bounds.size(), |frame| {
+ // Scale units to cell size
+ frame.scale(f32::from(self.cell_size));
+
+ // Draw background
+ frame.fill_rectangle(
+ Point::ORIGIN,
+ Size::new(side_length as f32, side_length as f32),
+ self.light,
+ );
+
+ // Avoid drawing on the quiet zone
+ frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32));
+
+ // Draw contents
+ self.state
+ .contents
+ .iter()
+ .enumerate()
+ .filter(|(_, value)| **value == qrcode::Color::Dark)
+ .for_each(|(index, _)| {
+ let row = index / self.state.width;
+ let column = index % self.state.width;
+
+ frame.fill_rectangle(
+ Point::new(column as f32, row as f32),
+ Size::UNIT,
+ self.dark,
+ );
+ });
+ });
+
+ (
+ Primitive::Translate {
+ translation: Vector::new(bounds.x, bounds.y),
+ content: Box::new(geometry.into_primitive()),
+ },
+ mouse::Interaction::default(),
+ )
+ }
+}
+
+impl<'a, Message, B> Into<Element<'a, Message, Renderer<B>>> for QRCode<'a>
+where
+ B: Backend,
+{
+ fn into(self) -> Element<'a, Message, Renderer<B>> {
+ Element::new(self)
+ }
+}
+
+/// The state of a [`QRCode`].
+///
+/// It stores the data that will be displayed.
+#[derive(Debug)]
+pub struct State {
+ contents: Vec<qrcode::Color>,
+ width: usize,
+ cache: canvas::Cache,
+}
+
+impl State {
+ /// Creates a new [`State`] with the provided data.
+ ///
+ /// This method uses an [`ErrorCorrection::Medium`] and chooses the smallest
+ /// size to display the data.
+ pub fn new(data: impl AsRef<[u8]>) -> Result<Self, Error> {
+ let encoded = qrcode::QrCode::new(data)?;
+
+ Ok(Self::build(encoded))
+ }
+
+ /// Creates a new [`State`] with the provided [`ErrorCorrection`].
+ pub fn with_error_correction(
+ data: impl AsRef<[u8]>,
+ error_correction: ErrorCorrection,
+ ) -> Result<Self, Error> {
+ let encoded = qrcode::QrCode::with_error_correction_level(
+ data,
+ error_correction.into(),
+ )?;
+
+ Ok(Self::build(encoded))
+ }
+
+ /// Creates a new [`State`] with the provided [`Version`] and
+ /// [`ErrorCorrection`].
+ pub fn with_version(
+ data: impl AsRef<[u8]>,
+ version: Version,
+ error_correction: ErrorCorrection,
+ ) -> Result<Self, Error> {
+ let encoded = qrcode::QrCode::with_version(
+ data,
+ version.into(),
+ error_correction.into(),
+ )?;
+
+ Ok(Self::build(encoded))
+ }
+
+ fn build(encoded: qrcode::QrCode) -> Self {
+ let width = encoded.width();
+ let contents = encoded.into_colors();
+
+ Self {
+ contents,
+ width,
+ cache: canvas::Cache::new(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// The size of a [`QRCode`].
+///
+/// The higher the version the larger the grid of cells, and therefore the more
+/// information the [`QRCode`] can carry.
+pub enum Version {
+ /// A normal QR code version. It should be between 1 and 40.
+ Normal(u8),
+
+ /// A micro QR code version. It should be between 1 and 4.
+ Micro(u8),
+}
+
+impl From<Version> for qrcode::Version {
+ fn from(version: Version) -> Self {
+ match version {
+ Version::Normal(v) => qrcode::Version::Normal(i16::from(v)),
+ Version::Micro(v) => qrcode::Version::Micro(i16::from(v)),
+ }
+ }
+}
+
+/// The error correction level.
+///
+/// It controls the amount of data that can be damaged while still being able
+/// to recover the original information.
+///
+/// A higher error correction level allows for more corrupted data.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ErrorCorrection {
+ /// Low error correction. 7% of the data can be restored.
+ Low,
+ /// Medium error correction. 15% of the data can be restored.
+ Medium,
+ /// Quartile error correction. 25% of the data can be restored.
+ Quartile,
+ /// High error correction. 30% of the data can be restored.
+ High,
+}
+
+impl From<ErrorCorrection> for qrcode::EcLevel {
+ fn from(ec_level: ErrorCorrection) -> Self {
+ match ec_level {
+ ErrorCorrection::Low => qrcode::EcLevel::L,
+ ErrorCorrection::Medium => qrcode::EcLevel::M,
+ ErrorCorrection::Quartile => qrcode::EcLevel::Q,
+ ErrorCorrection::High => qrcode::EcLevel::H,
+ }
+ }
+}
+
+/// An error that occurred when building a [`State`] for a [`QRCode`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
+pub enum Error {
+ /// The data is too long to encode in a QR code for the chosen [`Version`].
+ #[error(
+ "The data is too long to encode in a QR code for the chosen version"
+ )]
+ DataTooLong,
+
+ /// The chosen [`Version`] and [`ErrorCorrection`] combination is invalid.
+ #[error(
+ "The chosen version and error correction level combination is invalid."
+ )]
+ InvalidVersion,
+
+ /// One or more characters in the provided data are not supported by the
+ /// chosen [`Version`].
+ #[error(
+ "One or more characters in the provided data are not supported by the \
+ chosen version"
+ )]
+ UnsupportedCharacterSet,
+
+ /// The chosen ECI designator is invalid. A valid designator should be
+ /// between 0 and 999999.
+ #[error(
+ "The chosen ECI designator is invalid. A valid designator should be \
+ between 0 and 999999."
+ )]
+ InvalidEciDesignator,
+
+ /// A character that does not belong to the character set was found.
+ #[error("A character that does not belong to the character set was found")]
+ InvalidCharacter,
+}
+
+impl From<qrcode::types::QrError> for Error {
+ fn from(error: qrcode::types::QrError) -> Self {
+ use qrcode::types::QrError;
+
+ match error {
+ QrError::DataTooLong => Error::DataTooLong,
+ QrError::InvalidVersion => Error::InvalidVersion,
+ QrError::UnsupportedCharacterSet => Error::UnsupportedCharacterSet,
+ QrError::InvalidEciDesignator => Error::InvalidEciDesignator,
+ QrError::InvalidCharacter => Error::InvalidCharacter,
+ }
+ }
+}
diff --git a/graphics/src/widget/radio.rs b/graphics/src/widget/radio.rs
index da41ac47..fd3d8145 100644
--- a/graphics/src/widget/radio.rs
+++ b/graphics/src/widget/radio.rs
@@ -42,7 +42,7 @@ where
let radio = Primitive::Quad {
bounds,
background: style.background,
- border_radius: (size / 2.0) as u16,
+ border_radius: size / 2.0,
border_width: style.border_width,
border_color: style.border_color,
};
@@ -58,8 +58,8 @@ where
height: bounds.height - dot_size,
},
background: Background::Color(style.dot_color),
- border_radius: (dot_size / 2.0) as u16,
- border_width: 0,
+ border_radius: dot_size / 2.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
};
diff --git a/graphics/src/widget/row.rs b/graphics/src/widget/row.rs
index 4c1dbadc..397d80bf 100644
--- a/graphics/src/widget/row.rs
+++ b/graphics/src/widget/row.rs
@@ -1,7 +1,7 @@
use crate::{Backend, Primitive, Renderer};
use iced_native::mouse;
use iced_native::row;
-use iced_native::{Element, Layout, Point};
+use iced_native::{Element, Layout, Point, Rectangle};
/// A container that distributes its contents horizontally.
pub type Row<'a, Message, Backend> =
@@ -17,6 +17,7 @@ where
content: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
+ viewport: &Rectangle,
) -> Self::Output {
let mut mouse_interaction = mouse::Interaction::default();
@@ -26,8 +27,13 @@ where
.iter()
.zip(layout.children())
.map(|(child, layout)| {
- let (primitive, new_mouse_interaction) =
- child.draw(self, defaults, layout, cursor_position);
+ let (primitive, new_mouse_interaction) = child.draw(
+ self,
+ defaults,
+ layout,
+ cursor_position,
+ viewport,
+ );
if new_mouse_interaction > mouse_interaction {
mouse_interaction = new_mouse_interaction;
diff --git a/graphics/src/widget/rule.rs b/graphics/src/widget/rule.rs
index a7a5d0e7..835ebed8 100644
--- a/graphics/src/widget/rule.rs
+++ b/graphics/src/widget/rule.rs
@@ -43,7 +43,7 @@ where
},
background: Background::Color(style.color),
border_radius: style.radius,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
}
} else {
@@ -63,7 +63,7 @@ where
},
background: Background::Color(style.color),
border_radius: style.radius,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
}
};
diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs
index b149db0a..2220e4b8 100644
--- a/graphics/src/widget/scrollable.rs
+++ b/graphics/src/widget/scrollable.rs
@@ -15,9 +15,6 @@ pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
pub type Scrollable<'a, Message, Backend> =
iced_native::Scrollable<'a, Message, Renderer<Backend>>;
-const SCROLLBAR_WIDTH: u16 = 10;
-const SCROLLBAR_MARGIN: u16 = 2;
-
impl<B> scrollable::Renderer for Renderer<B>
where
B: Backend,
@@ -29,29 +26,45 @@ where
bounds: Rectangle,
content_bounds: Rectangle,
offset: u32,
+ scrollbar_width: u16,
+ scrollbar_margin: u16,
+ scroller_width: u16,
) -> Option<scrollable::Scrollbar> {
if content_bounds.height > bounds.height {
+ let outer_width =
+ scrollbar_width.max(scroller_width) + 2 * scrollbar_margin;
+
+ let outer_bounds = Rectangle {
+ x: bounds.x + bounds.width - outer_width as f32,
+ y: bounds.y,
+ width: outer_width as f32,
+ height: bounds.height,
+ };
+
let scrollbar_bounds = Rectangle {
x: bounds.x + bounds.width
- - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN),
+ - f32::from(outer_width / 2 + scrollbar_width / 2),
y: bounds.y,
- width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN),
+ width: scrollbar_width as f32,
height: bounds.height,
};
let ratio = bounds.height / content_bounds.height;
- let scrollbar_height = bounds.height * ratio;
+ let scroller_height = bounds.height * ratio;
let y_offset = offset as f32 * ratio;
let scroller_bounds = Rectangle {
- x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN),
+ x: bounds.x + bounds.width
+ - f32::from(outer_width / 2 + scroller_width / 2),
y: scrollbar_bounds.y + y_offset,
- width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN),
- height: scrollbar_height,
+ width: scroller_width as f32,
+ height: scroller_height,
};
Some(scrollable::Scrollbar {
+ outer_bounds,
bounds: scrollbar_bounds,
+ margin: scrollbar_margin,
scroller: scrollable::Scroller {
bounds: scroller_bounds,
},
@@ -90,7 +103,7 @@ where
};
let is_scrollbar_visible =
- style.background.is_some() || style.border_width > 0;
+ style.background.is_some() || style.border_width > 0.0;
let scroller = if is_mouse_over
|| state.is_scroller_grabbed()
@@ -109,12 +122,7 @@ where
let scrollbar = if is_scrollbar_visible {
Primitive::Quad {
- bounds: Rectangle {
- x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN),
- width: scrollbar.bounds.width
- - f32::from(2 * SCROLLBAR_MARGIN),
- ..scrollbar.bounds
- },
+ bounds: scrollbar.bounds,
background: style
.background
.unwrap_or(Background::Color(Color::TRANSPARENT)),
@@ -126,8 +134,16 @@ where
Primitive::None
};
+ let scroll = Primitive::Clip {
+ bounds,
+ offset: Vector::new(0, 0),
+ content: Box::new(Primitive::Group {
+ primitives: vec![scrollbar, scroller],
+ }),
+ };
+
Primitive::Group {
- primitives: vec![clip, scrollbar, scroller],
+ primitives: vec![clip, scroll],
}
} else {
content
diff --git a/graphics/src/widget/slider.rs b/graphics/src/widget/slider.rs
index 99f0a098..aeceec3f 100644
--- a/graphics/src/widget/slider.rs
+++ b/graphics/src/widget/slider.rs
@@ -1,9 +1,6 @@
//! 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;
@@ -57,8 +54,8 @@ where
height: 2.0,
},
background: Background::Color(style.rail_colors.0),
- border_radius: 0,
- border_width: 0,
+ border_radius: 0.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
Primitive::Quad {
@@ -69,20 +66,18 @@ where
height: 2.0,
},
background: Background::Color(style.rail_colors.1),
- border_radius: 0,
- border_width: 0,
+ border_radius: 0.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
);
- let (range_start, range_end) = range.into_inner();
-
let (handle_width, handle_height, handle_border_radius) = match style
.handle
.shape
{
HandleShape::Circle { radius } => {
- (f32::from(radius * 2), f32::from(radius * 2), radius)
+ (radius * 2.0, radius * 2.0, radius)
}
HandleShape::Rectangle {
width,
@@ -90,8 +85,14 @@ where
} => (f32::from(width), f32::from(bounds.height), border_radius),
};
- let handle_offset = (bounds.width - handle_width)
- * ((value - range_start) / (range_end - range_start).max(1.0));
+ let (range_start, range_end) = range.into_inner();
+
+ let handle_offset = if range_start >= range_end {
+ 0.0
+ } else {
+ (bounds.width - handle_width) * (value - range_start)
+ / (range_end - range_start)
+ };
let handle = Primitive::Quad {
bounds: Rectangle {
diff --git a/graphics/src/widget/text_input.rs b/graphics/src/widget/text_input.rs
index 575d67f5..c269022b 100644
--- a/graphics/src/widget/text_input.rs
+++ b/graphics/src/widget/text_input.rs
@@ -1,9 +1,6 @@
//! 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;
@@ -149,8 +146,8 @@ where
background: Background::Color(
style_sheet.value_color(),
),
- border_radius: 0,
- border_width: 0,
+ border_radius: 0.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
offset,
@@ -193,8 +190,8 @@ where
background: Background::Color(
style_sheet.selection_color(),
),
- border_radius: 0,
- border_width: 0,
+ border_radius: 0.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
if end == right {
diff --git a/graphics/src/widget/toggler.rs b/graphics/src/widget/toggler.rs
new file mode 100644
index 00000000..852d18ee
--- /dev/null
+++ b/graphics/src/widget/toggler.rs
@@ -0,0 +1,99 @@
+//! Show toggle controls using togglers.
+use crate::backend::{self, Backend};
+use crate::{Primitive, Renderer};
+use iced_native::mouse;
+use iced_native::toggler;
+use iced_native::Rectangle;
+
+pub use iced_style::toggler::{Style, StyleSheet};
+
+/// Makes sure that the border radius of the toggler looks good at every size.
+const BORDER_RADIUS_RATIO: f32 = 32.0 / 13.0;
+
+/// The space ratio between the background Quad and the Toggler bounds, and
+/// between the background Quad and foreground Quad.
+const SPACE_RATIO: f32 = 0.05;
+
+/// A toggler that can be toggled.
+///
+/// This is an alias of an `iced_native` toggler with an `iced_wgpu::Renderer`.
+pub type Toggler<Message, Backend> =
+ iced_native::Toggler<Message, Renderer<Backend>>;
+
+impl<B> toggler::Renderer for Renderer<B>
+where
+ B: Backend + backend::Text,
+{
+ type Style = Box<dyn StyleSheet>;
+
+ const DEFAULT_SIZE: u16 = 20;
+
+ fn draw(
+ &mut self,
+ bounds: Rectangle,
+ is_active: bool,
+ is_mouse_over: bool,
+ label: Option<Self::Output>,
+ style_sheet: &Self::Style,
+ ) -> Self::Output {
+ let style = if is_mouse_over {
+ style_sheet.hovered(is_active)
+ } else {
+ style_sheet.active(is_active)
+ };
+
+ let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO;
+ let space = SPACE_RATIO * bounds.height as f32;
+
+ let toggler_background_bounds = Rectangle {
+ x: bounds.x + space,
+ y: bounds.y + space,
+ width: bounds.width - (2.0 * space),
+ height: bounds.height - (2.0 * space),
+ };
+
+ let toggler_background = Primitive::Quad {
+ bounds: toggler_background_bounds,
+ background: style.background.into(),
+ border_radius,
+ border_width: 1.0,
+ border_color: style.background_border.unwrap_or(style.background),
+ };
+
+ let toggler_foreground_bounds = Rectangle {
+ x: bounds.x
+ + if is_active {
+ bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
+ } else {
+ 2.0 * space
+ },
+ y: bounds.y + (2.0 * space),
+ width: bounds.height - (4.0 * space),
+ height: bounds.height - (4.0 * space),
+ };
+
+ let toggler_foreground = Primitive::Quad {
+ bounds: toggler_foreground_bounds,
+ background: style.foreground.into(),
+ border_radius,
+ border_width: 1.0,
+ border_color: style.foreground_border.unwrap_or(style.foreground),
+ };
+
+ (
+ Primitive::Group {
+ primitives: match label {
+ Some((l, _)) => {
+ vec![l, toggler_background, toggler_foreground]
+ }
+ None => vec![toggler_background, toggler_foreground],
+ },
+ },
+ if is_mouse_over {
+ mouse::Interaction::Pointer
+ } else {
+ mouse::Interaction::default()
+ },
+ )
+ }
+}
diff --git a/graphics/src/widget/tooltip.rs b/graphics/src/widget/tooltip.rs
new file mode 100644
index 00000000..493a6389
--- /dev/null
+++ b/graphics/src/widget/tooltip.rs
@@ -0,0 +1,168 @@
+//! Decorate content and apply alignment.
+use crate::backend::{self, Backend};
+use crate::defaults::{self, Defaults};
+use crate::{Primitive, Renderer, Vector};
+
+use iced_native::container;
+use iced_native::layout::{self, Layout};
+use iced_native::{Element, Padding, Point, Rectangle, Size, Text};
+
+/// An element decorating some content.
+///
+/// This is an alias of an `iced_native` tooltip with a default
+/// `Renderer`.
+pub type Tooltip<'a, Message, Backend> =
+ iced_native::Tooltip<'a, Message, Renderer<Backend>>;
+
+pub use iced_native::tooltip::Position;
+
+impl<B> iced_native::tooltip::Renderer for Renderer<B>
+where
+ B: Backend + backend::Text,
+{
+ const DEFAULT_PADDING: u16 = 5;
+
+ fn draw<Message>(
+ &mut self,
+ defaults: &Defaults,
+ cursor_position: Point,
+ content_layout: Layout<'_>,
+ viewport: &Rectangle,
+ content: &Element<'_, Message, Self>,
+ tooltip: &Text<Self>,
+ position: Position,
+ style_sheet: &<Self as container::Renderer>::Style,
+ gap: u16,
+ padding: u16,
+ ) -> Self::Output {
+ let (content, mouse_interaction) = content.draw(
+ self,
+ &defaults,
+ content_layout,
+ cursor_position,
+ viewport,
+ );
+
+ let bounds = content_layout.bounds();
+
+ if bounds.contains(cursor_position) {
+ use iced_native::Widget;
+
+ let gap = f32::from(gap);
+ let style = style_sheet.style();
+
+ let defaults = Defaults {
+ text: defaults::Text {
+ color: style.text_color.unwrap_or(defaults.text.color),
+ },
+ };
+
+ let text_layout = Widget::<(), Self>::layout(
+ tooltip,
+ self,
+ &layout::Limits::new(Size::ZERO, viewport.size())
+ .pad(Padding::new(padding)),
+ );
+
+ let padding = f32::from(padding);
+ let text_bounds = text_layout.bounds();
+ let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0;
+ let y_center =
+ bounds.y + (bounds.height - text_bounds.height) / 2.0;
+
+ let mut tooltip_bounds = {
+ let offset = match position {
+ Position::Top => Vector::new(
+ x_center,
+ bounds.y - text_bounds.height - gap - padding,
+ ),
+ Position::Bottom => Vector::new(
+ x_center,
+ bounds.y + bounds.height + gap + padding,
+ ),
+ Position::Left => Vector::new(
+ bounds.x - text_bounds.width - gap - padding,
+ y_center,
+ ),
+ Position::Right => Vector::new(
+ bounds.x + bounds.width + gap + padding,
+ y_center,
+ ),
+ Position::FollowCursor => Vector::new(
+ cursor_position.x,
+ cursor_position.y - text_bounds.height,
+ ),
+ };
+
+ Rectangle {
+ x: offset.x - padding,
+ y: offset.y - padding,
+ width: text_bounds.width + padding * 2.0,
+ height: text_bounds.height + padding * 2.0,
+ }
+ };
+
+ if tooltip_bounds.x < viewport.x {
+ tooltip_bounds.x = viewport.x;
+ } else if viewport.x + viewport.width
+ < tooltip_bounds.x + tooltip_bounds.width
+ {
+ tooltip_bounds.x =
+ viewport.x + viewport.width - tooltip_bounds.width;
+ }
+
+ if tooltip_bounds.y < viewport.y {
+ tooltip_bounds.y = viewport.y;
+ } else if viewport.y + viewport.height
+ < tooltip_bounds.y + tooltip_bounds.height
+ {
+ tooltip_bounds.y =
+ viewport.y + viewport.height - tooltip_bounds.height;
+ }
+
+ let (tooltip, _) = Widget::<(), Self>::draw(
+ tooltip,
+ self,
+ &defaults,
+ Layout::with_offset(
+ Vector::new(
+ tooltip_bounds.x + padding,
+ tooltip_bounds.y + padding,
+ ),
+ &text_layout,
+ ),
+ cursor_position,
+ viewport,
+ );
+
+ (
+ Primitive::Group {
+ primitives: vec![
+ content,
+ Primitive::Clip {
+ bounds: *viewport,
+ offset: Vector::new(0, 0),
+ content: Box::new(
+ if let Some(background) =
+ crate::container::background(
+ tooltip_bounds,
+ &style,
+ )
+ {
+ Primitive::Group {
+ primitives: vec![background, tooltip],
+ }
+ } else {
+ tooltip
+ },
+ ),
+ },
+ ],
+ },
+ mouse_interaction,
+ )
+ } else {
+ (content, mouse_interaction)
+ }
+ }
+}
diff --git a/graphics/src/window.rs b/graphics/src/window.rs
index 3e74db5f..365ddfbc 100644
--- a/graphics/src/window.rs
+++ b/graphics/src/window.rs
@@ -4,7 +4,7 @@ mod compositor;
#[cfg(feature = "opengl")]
mod gl_compositor;
-pub use compositor::Compositor;
+pub use compositor::{Compositor, SwapChainError};
#[cfg(feature = "opengl")]
pub use gl_compositor::GLCompositor;
diff --git a/graphics/src/window/compositor.rs b/graphics/src/window/compositor.rs
index 7674f227..de2a6990 100644
--- a/graphics/src/window/compositor.rs
+++ b/graphics/src/window/compositor.rs
@@ -1,6 +1,9 @@
use crate::{Color, Error, Viewport};
+
use iced_native::mouse;
+
use raw_window_handle::HasRawWindowHandle;
+use thiserror::Error;
/// A graphics compositor that can draw to windows.
pub trait Compositor: Sized {
@@ -16,14 +19,15 @@ pub trait Compositor: Sized {
/// The swap chain of the backend.
type SwapChain;
- /// Creates a new [`Backend`].
- ///
- /// [`Backend`]: trait.Backend.html
- fn new(settings: Self::Settings) -> Result<(Self, Self::Renderer), Error>;
+ /// Creates a new [`Compositor`].
+ fn new<W: HasRawWindowHandle>(
+ settings: Self::Settings,
+ compatible_window: Option<&W>,
+ ) -> Result<(Self, Self::Renderer), Error>;
/// Crates a new [`Surface`] for the given window.
///
- /// [`Surface`]: #associatedtype.Surface
+ /// [`Surface`]: Self::Surface
fn create_surface<W: HasRawWindowHandle>(
&mut self,
window: &W,
@@ -31,8 +35,8 @@ pub trait Compositor: Sized {
/// Crates a new [`SwapChain`] for the given [`Surface`].
///
- /// [`SwapChain`]: #associatedtype.SwapChain
- /// [`Surface`]: #associatedtype.Surface
+ /// [`SwapChain`]: Self::SwapChain
+ /// [`Surface`]: Self::Surface
fn create_swap_chain(
&mut self,
surface: &Self::Surface,
@@ -42,8 +46,7 @@ pub trait Compositor: Sized {
/// Draws the output primitives to the next frame of the given [`SwapChain`].
///
- /// [`SwapChain`]: #associatedtype.SwapChain
- /// [`Surface`]: #associatedtype.Surface
+ /// [`SwapChain`]: Self::SwapChain
fn draw<T: AsRef<str>>(
&mut self,
renderer: &mut Self::Renderer,
@@ -52,5 +55,26 @@ pub trait Compositor: Sized {
background_color: Color,
output: &<Self::Renderer as iced_native::Renderer>::Output,
overlay: &[T],
- ) -> mouse::Interaction;
+ ) -> Result<mouse::Interaction, SwapChainError>;
+}
+
+/// Result of an unsuccessful call to [`Compositor::draw`].
+#[derive(Clone, PartialEq, Eq, Debug, Error)]
+pub enum SwapChainError {
+ /// A timeout was encountered while trying to acquire the next frame.
+ #[error(
+ "A timeout was encountered while trying to acquire the next frame"
+ )]
+ Timeout,
+ /// The underlying surface has changed, and therefore the swap chain must be updated.
+ #[error(
+ "The underlying surface has changed, and therefore the swap chain must be updated."
+ )]
+ Outdated,
+ /// The swap chain has been lost and needs to be recreated.
+ #[error("The swap chain has been lost and needs to be recreated")]
+ Lost,
+ /// There is no more memory left to allocate a new frame.
+ #[error("There is no more memory left to allocate a new frame")]
+ OutOfMemory,
}
diff --git a/graphics/src/window/gl_compositor.rs b/graphics/src/window/gl_compositor.rs
index 1f37642e..34d70be3 100644
--- a/graphics/src/window/gl_compositor.rs
+++ b/graphics/src/window/gl_compositor.rs
@@ -15,28 +15,27 @@ use core::ffi::c_void;
/// 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`].
+ /// The renderer of the [`GLCompositor`].
///
/// 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
+ /// [`Renderer`]: crate::Renderer
+ /// [`Backend`]: crate::Backend
type Renderer: iced_native::Renderer;
- /// The settings of the [`Compositor`].
+ /// The settings of the [`GLCompositor`].
///
/// 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
+ /// Creates a new [`GLCompositor`] and [`Renderer`] with the given
/// [`Settings`] and an OpenGL address loader function.
///
- /// [`Compositor`]: trait.Compositor.html
- /// [`Renderer`]: #associatedtype.Renderer
- /// [`Backend`]: ../backend/trait.Backend.html
+ /// [`Renderer`]: crate::Renderer
+ /// [`Backend`]: crate::Backend
+ /// [`Settings`]: Self::Settings
#[allow(unsafe_code)]
unsafe fn new(
settings: Self::Settings,
@@ -44,19 +43,15 @@ pub trait GLCompositor: Sized {
) -> Result<(Self, Self::Renderer), Error>;
/// Returns the amount of samples that should be used when configuring
- /// an OpenGL context for this [`Compositor`].
- ///
- /// [`Compositor`]: trait.Compositor.html
+ /// an OpenGL context for this [`GLCompositor`].
fn sample_count(settings: &Self::Settings) -> u32;
- /// Resizes the viewport of the [`Compositor`].
- ///
- /// [`Compositor`]: trait.Compositor.html
+ /// Resizes the viewport of the [`GLCompositor`].
fn resize_viewport(&mut self, physical_size: Size<u32>);
/// Draws the provided output with the given [`Renderer`].
///
- /// [`Compositor`]: trait.Compositor.html
+ /// [`Renderer`]: crate::Renderer
fn draw<T: AsRef<str>>(
&mut self,
renderer: &mut Self::Renderer,