summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/BUG-REPORT.yml6
-rw-r--r--CHANGELOG.md36
-rw-r--r--Cargo.toml51
-rw-r--r--README.md4
-rw-r--r--core/Cargo.toml2
-rw-r--r--core/src/color.rs21
-rw-r--r--core/src/lib.rs2
-rw-r--r--core/src/rectangle.rs4
-rw-r--r--examples/custom_quad/Cargo.toml10
-rw-r--r--examples/custom_quad/src/main.rs160
-rw-r--r--examples/custom_widget/src/main.rs2
-rw-r--r--examples/game_of_life/src/main.rs6
-rw-r--r--examples/modal/Cargo.toml10
-rw-r--r--examples/modal/src/main.rs479
-rw-r--r--examples/slider/Cargo.toml9
-rw-r--r--examples/slider/README.md14
-rw-r--r--examples/slider/sliders.gifbin0 -> 87275 bytes
-rw-r--r--examples/slider/src/main.rs63
-rw-r--r--examples/svg/src/main.rs71
-rw-r--r--futures/src/subscription.rs6
-rw-r--r--glow/Cargo.toml6
-rw-r--r--glow/src/backend.rs10
-rw-r--r--glow/src/image.rs22
-rw-r--r--glow/src/lib.rs2
-rw-r--r--glow/src/quad/compatibility.rs8
-rw-r--r--glow/src/quad/core.rs4
-rw-r--r--glow/src/shader/compatibility/quad.frag24
-rw-r--r--glow/src/shader/compatibility/quad.vert12
-rw-r--r--glow/src/shader/core/quad.frag24
-rw-r--r--glow/src/shader/core/quad.vert12
-rw-r--r--glutin/Cargo.toml8
-rw-r--r--glutin/src/application.rs5
-rw-r--r--graphics/Cargo.toml4
-rw-r--r--graphics/src/image/storage.rs2
-rw-r--r--graphics/src/image/vector.rs38
-rw-r--r--graphics/src/layer.rs7
-rw-r--r--graphics/src/layer/image.rs6
-rw-r--r--graphics/src/layer/quad.rs2
-rw-r--r--graphics/src/primitive.rs5
-rw-r--r--graphics/src/renderer.rs17
-rw-r--r--lazy/Cargo.toml4
-rw-r--r--lazy/src/component.rs68
-rw-r--r--lazy/src/lazy.rs29
-rw-r--r--lazy/src/responsive.rs47
-rw-r--r--native/Cargo.toml4
-rw-r--r--native/src/element.rs20
-rw-r--r--native/src/layout/limits.rs22
-rw-r--r--native/src/lib.rs4
-rw-r--r--native/src/overlay.rs9
-rw-r--r--native/src/overlay/element.rs10
-rw-r--r--native/src/overlay/menu.rs4
-rw-r--r--native/src/renderer.rs25
-rw-r--r--native/src/subscription.rs2
-rw-r--r--native/src/svg.rs6
-rw-r--r--native/src/user_interface.rs41
-rw-r--r--native/src/widget.rs16
-rw-r--r--native/src/widget/button.rs10
-rw-r--r--native/src/widget/checkbox.rs2
-rw-r--r--native/src/widget/column.rs9
-rw-r--r--native/src/widget/container.rs8
-rw-r--r--native/src/widget/helpers.rs27
-rw-r--r--native/src/widget/image/viewer.rs13
-rw-r--r--native/src/widget/pane_grid.rs15
-rw-r--r--native/src/widget/pane_grid/content.rs11
-rw-r--r--native/src/widget/pane_grid/title_bar.rs11
-rw-r--r--native/src/widget/pick_list.rs4
-rw-r--r--native/src/widget/progress_bar.rs6
-rw-r--r--native/src/widget/radio.rs4
-rw-r--r--native/src/widget/row.rs9
-rw-r--r--native/src/widget/rule.rs2
-rw-r--r--native/src/widget/scrollable.rs20
-rw-r--r--native/src/widget/slider.rs6
-rw-r--r--native/src/widget/svg.rs54
-rw-r--r--native/src/widget/text_input.rs19
-rw-r--r--native/src/widget/toggler.rs26
-rw-r--r--native/src/widget/tooltip.rs4
-rw-r--r--native/src/widget/vertical_slider.rs470
-rw-r--r--native/src/window.rs2
-rw-r--r--native/src/window/action.rs41
-rw-r--r--native/src/window/user_attention.rs21
-rw-r--r--src/application.rs22
-rw-r--r--src/lib.rs6
-rw-r--r--src/sandbox.rs22
-rw-r--r--src/widget.rs6
-rw-r--r--style/Cargo.toml2
-rw-r--r--style/src/lib.rs1
-rw-r--r--style/src/svg.rs23
-rw-r--r--style/src/theme.rs41
-rw-r--r--wgpu/Cargo.toml6
-rw-r--r--wgpu/src/buffer/dynamic.rs2
-rw-r--r--wgpu/src/image.rs7
-rw-r--r--wgpu/src/lib.rs2
-rw-r--r--wgpu/src/quad.rs2
-rw-r--r--wgpu/src/shader/quad.wgsl47
-rw-r--r--wgpu/src/triangle.rs6
-rw-r--r--winit/Cargo.toml6
-rw-r--r--winit/src/application.rs31
-rw-r--r--winit/src/conversion.rs28
-rw-r--r--winit/src/lib.rs2
-rw-r--r--winit/src/window.rs7
100 files changed, 2125 insertions, 423 deletions
diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml
index 96dcc3b6..d58a4feb 100644
--- a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml
+++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml
@@ -58,12 +58,12 @@ body:
attributes:
label: Version
description: |
- We only offer support for the `0.4` release on crates.io and the `master` branch on this repository. Which version are you using? Please make sure you are using the latest patch available (e.g. run `cargo update`).
+ We only offer support for the latest release on crates.io and the `master` branch on this repository. Which version are you using? Please make sure you are using the latest patch available (e.g. run `cargo update`).
- If you are using an older release, please upgrade to `0.4` before filing an issue.
+ If you are using an older release, please upgrade to the latest one before filing an issue.
options:
- master
- - 0.4
+ - 0.6
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d098e8ec..29cdef58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.6.0] - 2022-12-07
+### Added
+- Support for non-uniform border radius for `Primitive::Quad`. [#1506](https://github.com/iced-rs/iced/pull/1506)
+- Operation to query the current focused widget. [#1526](https://github.com/iced-rs/iced/pull/1526)
+- Additional operations for `TextInput`. [#1529](https://github.com/iced-rs/iced/pull/1529)
+- Styling support for `Svg`. [#1578](https://github.com/iced-rs/iced/pull/1578)
+
+### Changed
+- Triangle geometry using a solid color is now drawn in a single draw call. [#1538](https://github.com/iced-rs/iced/pull/1538)
+
+### Fixed
+- Gradients for WebAssembly target. [#1524](https://github.com/iced-rs/iced/pull/1524)
+- `Overlay` layout cache not being invalidated. [#1528](https://github.com/iced-rs/iced/pull/1528)
+- Operations not working for `PaneGrid`. [#1533](https://github.com/iced-rs/iced/pull/1533)
+- Mapped `widget::Operation` always returning `Outcome::None`. [#1536](https://github.com/iced-rs/iced/pull/1536)
+- Padding of `TextInput` with `Length::Units` width. [#1539](https://github.com/iced-rs/iced/pull/1539)
+- Clipping of `Image` and `Svg` widgets in `iced_glow`. [#1557](https://github.com/iced-rs/iced/pull/1557)
+- Invalid links in documentation. [#1560](https://github.com/iced-rs/iced/pull/1560)
+- `Custom` style of `PickList` widget. [#1570](https://github.com/iced-rs/iced/pull/1570)
+- Scroller in `Scrollable` always being drawn. [#1574](https://github.com/iced-rs/iced/pull/1574)
+
+Many thanks to...
+
+- @bungoboingo
+- @l1Dan
+- @mmstick
+- @mtkennerly
+- @PolyMeilex
+- @rksm
+- @rs017991
+- @tarkah
+- @wash2
+
## [0.5.0] - 2022-11-10
### Added
- __[Stabilization of stateless widgets][stateless]__ (#1393)
@@ -288,7 +321,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- First release! :tada:
-[Unreleased]: https://github.com/iced-rs/iced/compare/0.5.0...HEAD
+[Unreleased]: https://github.com/iced-rs/iced/compare/0.6.0...HEAD
+[0.6.0]: https://github.com/iced-rs/iced/compare/0.5.0...0.6.0
[0.5.0]: https://github.com/iced-rs/iced/compare/0.4.2...0.5.0
[0.4.2]: https://github.com/iced-rs/iced/compare/0.4.1...0.4.2
[0.4.1]: https://github.com/iced-rs/iced/compare/0.4.0...0.4.1
diff --git a/Cargo.toml b/Cargo.toml
index d0243dfd..9c45b2f5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced"
-version = "0.5.2"
+version = "0.6.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A cross-platform GUI library inspired by Elm"
@@ -55,50 +55,17 @@ members = [
"style",
"wgpu",
"winit",
- "examples/arc",
- "examples/bezier_tool",
- "examples/clock",
- "examples/color_palette",
- "examples/component",
- "examples/counter",
- "examples/custom_widget",
- "examples/download_progress",
- "examples/events",
- "examples/exit",
- "examples/game_of_life",
- "examples/geometry",
- "examples/integration_opengl",
- "examples/integration_wgpu",
- "examples/lazy",
- "examples/modern_art",
- "examples/multitouch",
- "examples/pane_grid",
- "examples/pick_list",
- "examples/pokedex",
- "examples/progress_bar",
- "examples/qr_code",
- "examples/scrollable",
- "examples/sierpinski_triangle",
- "examples/solar_system",
- "examples/stopwatch",
- "examples/styling",
- "examples/svg",
- "examples/system_information",
- "examples/todos",
- "examples/tooltip",
- "examples/tour",
- "examples/url_handler",
- "examples/websocket",
+ "examples/*",
]
[dependencies]
iced_core = { version = "0.6", path = "core" }
iced_futures = { version = "0.5", path = "futures" }
-iced_native = { version = "0.6", path = "native" }
-iced_graphics = { version = "0.4", path = "graphics" }
-iced_winit = { version = "0.5", path = "winit", features = ["application"] }
-iced_glutin = { version = "0.4", path = "glutin", optional = true }
-iced_glow = { version = "0.4", path = "glow", optional = true }
+iced_native = { version = "0.7", path = "native" }
+iced_graphics = { version = "0.5", path = "graphics" }
+iced_winit = { version = "0.6", path = "winit", features = ["application"] }
+iced_glutin = { version = "0.5", path = "glutin", optional = true }
+iced_glow = { version = "0.5", path = "glow", optional = true }
thiserror = "1.0"
[dependencies.image_rs]
@@ -107,10 +74,10 @@ package = "image"
optional = true
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-iced_wgpu = { version = "0.6", path = "wgpu", optional = true }
+iced_wgpu = { version = "0.7", path = "wgpu", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
-iced_wgpu = { version = "0.6", path = "wgpu", features = ["webgl"], optional = true }
+iced_wgpu = { version = "0.7", path = "wgpu", features = ["webgl"], optional = true }
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
diff --git a/README.md b/README.md
index 1d2fb76f..bc7f5440 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ __Iced is currently experimental software.__ [Take a look at the roadmap],
Add `iced` as a dependency in your `Cargo.toml`:
```toml
-iced = "0.5"
+iced = "0.6"
```
If your project is using a Rust edition older than 2021, then you will need to
@@ -215,7 +215,7 @@ cargo run --features iced/glow --package game_of_life
and then use it in your project with
```toml
-iced = { version = "0.5", default-features = false, features = ["glow"] }
+iced = { version = "0.6", default-features = false, features = ["glow"] }
```
__NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0,
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 6fd0a38c..c401f30a 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_core"
-version = "0.6.1"
+version = "0.6.2"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "The essential concepts of Iced"
diff --git a/core/src/color.rs b/core/src/color.rs
index 212c1214..fe0a1856 100644
--- a/core/src/color.rs
+++ b/core/src/color.rs
@@ -89,6 +89,17 @@ impl Color {
}
}
+ /// Converts the [`Color`] into its RGBA8 equivalent.
+ #[must_use]
+ pub fn into_rgba8(self) -> [u8; 4] {
+ [
+ (self.r * 255.0).round() as u8,
+ (self.g * 255.0).round() as u8,
+ (self.b * 255.0).round() as u8,
+ (self.a * 255.0).round() as u8,
+ ]
+ }
+
/// Converts the [`Color`] into its linear values.
pub fn into_linear(self) -> [f32; 4] {
// As described in:
@@ -148,24 +159,26 @@ impl From<[f32; 4]> for Color {
#[macro_export]
macro_rules! color {
($r:expr, $g:expr, $b:expr) => {
- Color::from_rgb8($r, $g, $b)
+ $crate::Color::from_rgb8($r, $g, $b)
};
($r:expr, $g:expr, $b:expr, $a:expr) => {
- Color::from_rgba8($r, $g, $b, $a)
+ $crate::Color::from_rgba8($r, $g, $b, $a)
};
($hex:expr) => {{
let hex = $hex as u32;
let r = (hex & 0xff0000) >> 16;
let g = (hex & 0xff00) >> 8;
let b = (hex & 0xff);
- Color::from_rgb8(r as u8, g as u8, b as u8)
+
+ $crate::Color::from_rgb8(r as u8, g as u8, b as u8)
}};
($hex:expr, $a:expr) => {{
let hex = $hex as u32;
let r = (hex & 0xff0000) >> 16;
let g = (hex & 0xff00) >> 8;
let b = (hex & 0xff);
- Color::from_rgba8(r as u8, g as u8, b as u8, $a)
+
+ $crate::Color::from_rgba8(r as u8, g as u8, b as u8, $a)
}};
}
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 383b4f73..f95d61f6 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -7,7 +7,7 @@
//! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true)
//!
//! [Iced]: https://github.com/iced-rs/iced
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
//! [`iced_web`]: https://github.com/iced-rs/iced_web
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs
index 4e082051..4fe91519 100644
--- a/core/src/rectangle.rs
+++ b/core/src/rectangle.rs
@@ -116,8 +116,8 @@ impl std::ops::Mul<f32> for Rectangle<f32> {
fn mul(self, scale: f32) -> Self {
Self {
- x: self.x as f32 * scale,
- y: self.y as f32 * scale,
+ x: self.x * scale,
+ y: self.y * scale,
width: self.width * scale,
height: self.height * scale,
}
diff --git a/examples/custom_quad/Cargo.toml b/examples/custom_quad/Cargo.toml
new file mode 100644
index 00000000..39154786
--- /dev/null
+++ b/examples/custom_quad/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "custom_quad"
+version = "0.1.0"
+authors = ["Robert Krahn"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../.." }
+iced_native = { path = "../../native" }
diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs
new file mode 100644
index 00000000..6509887c
--- /dev/null
+++ b/examples/custom_quad/src/main.rs
@@ -0,0 +1,160 @@
+//! This example showcases a drawing a quad.
+mod quad {
+ use iced_native::layout::{self, Layout};
+ use iced_native::renderer;
+ use iced_native::widget::{self, Widget};
+ use iced_native::{Color, Element, Length, Point, Rectangle, Size};
+
+ pub struct CustomQuad {
+ size: f32,
+ radius: [f32; 4],
+ border_width: f32,
+ }
+
+ impl CustomQuad {
+ pub fn new(size: f32, radius: [f32; 4], border_width: f32) -> Self {
+ Self {
+ size,
+ radius,
+ border_width,
+ }
+ }
+ }
+
+ impl<Message, Renderer> Widget<Message, Renderer> for CustomQuad
+ where
+ Renderer: renderer::Renderer,
+ {
+ fn width(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn height(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn layout(
+ &self,
+ _renderer: &Renderer,
+ _limits: &layout::Limits,
+ ) -> layout::Node {
+ layout::Node::new(Size::new(self.size, self.size))
+ }
+
+ fn draw(
+ &self,
+ _state: &widget::Tree,
+ renderer: &mut Renderer,
+ _theme: &Renderer::Theme,
+ _style: &renderer::Style,
+ layout: Layout<'_>,
+ _cursor_position: Point,
+ _viewport: &Rectangle,
+ ) {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: layout.bounds(),
+ border_radius: self.radius.into(),
+ border_width: self.border_width,
+ border_color: Color::from_rgb(1.0, 0.0, 0.0),
+ },
+ Color::BLACK,
+ );
+ }
+ }
+
+ impl<'a, Message, Renderer> From<CustomQuad> for Element<'a, Message, Renderer>
+ where
+ Renderer: renderer::Renderer,
+ {
+ fn from(circle: CustomQuad) -> Self {
+ Self::new(circle)
+ }
+ }
+}
+
+use iced::widget::{column, container, slider, text};
+use iced::{Alignment, Element, Length, Sandbox, Settings};
+
+pub fn main() -> iced::Result {
+ Example::run(Settings::default())
+}
+
+struct Example {
+ radius: [f32; 4],
+ border_width: f32,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[allow(clippy::enum_variant_names)]
+enum Message {
+ RadiusTopLeftChanged(f32),
+ RadiusTopRightChanged(f32),
+ RadiusBottomRightChanged(f32),
+ RadiusBottomLeftChanged(f32),
+ BorderWidthChanged(f32),
+}
+
+impl Sandbox for Example {
+ type Message = Message;
+
+ fn new() -> Self {
+ Self {
+ radius: [50.0; 4],
+ border_width: 0.0,
+ }
+ }
+
+ fn title(&self) -> String {
+ String::from("Custom widget - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ let [tl, tr, br, bl] = self.radius;
+ match message {
+ Message::RadiusTopLeftChanged(radius) => {
+ self.radius = [radius, tr, br, bl];
+ }
+ Message::RadiusTopRightChanged(radius) => {
+ self.radius = [tl, radius, br, bl];
+ }
+ Message::RadiusBottomRightChanged(radius) => {
+ self.radius = [tl, tr, radius, bl];
+ }
+ Message::RadiusBottomLeftChanged(radius) => {
+ self.radius = [tl, tr, br, radius];
+ }
+ Message::BorderWidthChanged(width) => {
+ self.border_width = width;
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let [tl, tr, br, bl] = self.radius;
+
+ let content = column![
+ quad::CustomQuad::new(200.0, self.radius, self.border_width),
+ text(format!("Radius: {tl:.2}/{tr:.2}/{br:.2}/{bl:.2}")),
+ slider(1.0..=100.0, tl, Message::RadiusTopLeftChanged).step(0.01),
+ slider(1.0..=100.0, tr, Message::RadiusTopRightChanged).step(0.01),
+ slider(1.0..=100.0, br, Message::RadiusBottomRightChanged)
+ .step(0.01),
+ slider(1.0..=100.0, bl, Message::RadiusBottomLeftChanged)
+ .step(0.01),
+ slider(1.0..=10.0, self.border_width, Message::BorderWidthChanged)
+ .step(0.01),
+ ]
+ .padding(20)
+ .spacing(20)
+ .max_width(500)
+ .align_items(Alignment::Center);
+
+ container(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .into()
+ }
+}
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index c37a1a12..f6bb3b1e 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -61,7 +61,7 @@ mod circle {
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds(),
- border_radius: self.radius,
+ border_radius: self.radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 2a8b3721..b0f1c96d 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -492,8 +492,10 @@ mod grid {
let old_scaling = self.scaling;
let scaling = (self.scaling * (1.0 + y / 30.0))
- .max(Self::MIN_SCALING)
- .min(Self::MAX_SCALING);
+ .clamp(
+ Self::MIN_SCALING,
+ Self::MAX_SCALING,
+ );
let translation =
if let Some(cursor_to_center) =
diff --git a/examples/modal/Cargo.toml b/examples/modal/Cargo.toml
new file mode 100644
index 00000000..8770acac
--- /dev/null
+++ b/examples/modal/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "modal"
+version = "0.1.0"
+authors = ["tarkah <admin@tarkah.dev>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = [] }
+iced_native = { path = "../../native" }
diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs
new file mode 100644
index 00000000..5afafd0d
--- /dev/null
+++ b/examples/modal/src/main.rs
@@ -0,0 +1,479 @@
+use iced::widget::{
+ self, button, column, container, horizontal_space, row, text, text_input,
+};
+use iced::{
+ executor, keyboard, subscription, theme, Alignment, Application, Command,
+ Element, Event, Length, Settings, Subscription,
+};
+
+use self::modal::Modal;
+
+pub fn main() -> iced::Result {
+ App::run(Settings::default())
+}
+
+#[derive(Default)]
+struct App {
+ show_modal: bool,
+ email: String,
+ password: String,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ ShowModal,
+ HideModal,
+ Email(String),
+ Password(String),
+ Submit,
+ Event(Event),
+}
+
+impl Application for App {
+ type Executor = executor::Default;
+ type Message = Message;
+ type Theme = iced::Theme;
+ type Flags = ();
+
+ fn new(_flags: ()) -> (Self, Command<Message>) {
+ (App::default(), Command::none())
+ }
+
+ fn title(&self) -> String {
+ String::from("Modal - Iced")
+ }
+
+ fn subscription(&self) -> Subscription<Self::Message> {
+ subscription::events().map(Message::Event)
+ }
+
+ fn update(&mut self, message: Message) -> Command<Message> {
+ match message {
+ Message::ShowModal => {
+ self.show_modal = true;
+ widget::focus_next()
+ }
+ Message::HideModal => {
+ self.hide_modal();
+ Command::none()
+ }
+ Message::Email(email) => {
+ self.email = email;
+ Command::none()
+ }
+ Message::Password(password) => {
+ self.password = password;
+ Command::none()
+ }
+ Message::Submit => {
+ if !self.email.is_empty() && !self.password.is_empty() {
+ self.hide_modal();
+ }
+
+ Command::none()
+ }
+ Message::Event(event) => match event {
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ key_code: keyboard::KeyCode::Tab,
+ modifiers,
+ }) => {
+ if modifiers.shift() {
+ widget::focus_previous()
+ } else {
+ widget::focus_next()
+ }
+ }
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ key_code: keyboard::KeyCode::Escape,
+ ..
+ }) => {
+ self.hide_modal();
+ Command::none()
+ }
+ _ => Command::none(),
+ },
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let content = container(
+ column![
+ row![
+ text("Top Left"),
+ horizontal_space(Length::Fill),
+ text("Top Right")
+ ]
+ .align_items(Alignment::Start)
+ .height(Length::Fill),
+ container(
+ button(text("Show Modal")).on_press(Message::ShowModal)
+ )
+ .center_x()
+ .center_y()
+ .width(Length::Fill)
+ .height(Length::Fill),
+ row![
+ text("Bottom Left"),
+ horizontal_space(Length::Fill),
+ text("Bottom Right")
+ ]
+ .align_items(Alignment::End)
+ .height(Length::Fill),
+ ]
+ .height(Length::Fill),
+ )
+ .padding(10)
+ .width(Length::Fill)
+ .height(Length::Fill);
+
+ if self.show_modal {
+ let modal = container(
+ column![
+ text("Sign Up").size(24),
+ column![
+ column![
+ text("Email").size(12),
+ text_input(
+ "abc@123.com",
+ &self.email,
+ Message::Email
+ )
+ .on_submit(Message::Submit)
+ .padding(5),
+ ]
+ .spacing(5),
+ column![
+ text("Password").size(12),
+ text_input("", &self.password, Message::Password)
+ .on_submit(Message::Submit)
+ .password()
+ .padding(5),
+ ]
+ .spacing(5),
+ button(text("Submit")).on_press(Message::HideModal),
+ ]
+ .spacing(10)
+ ]
+ .spacing(20),
+ )
+ .width(Length::Units(300))
+ .padding(10)
+ .style(theme::Container::Box);
+
+ Modal::new(content, modal)
+ .on_blur(Message::HideModal)
+ .into()
+ } else {
+ content.into()
+ }
+ }
+}
+
+impl App {
+ fn hide_modal(&mut self) {
+ self.show_modal = false;
+ self.email.clear();
+ self.password.clear();
+ }
+}
+
+mod modal {
+ use iced_native::alignment::Alignment;
+ use iced_native::widget::{self, Tree};
+ use iced_native::{
+ event, layout, mouse, overlay, renderer, Clipboard, Color, Element,
+ Event, Layout, Length, Point, Rectangle, Shell, Size, Widget,
+ };
+
+ /// A widget that centers a modal element over some base element
+ pub struct Modal<'a, Message, Renderer> {
+ base: Element<'a, Message, Renderer>,
+ modal: Element<'a, Message, Renderer>,
+ on_blur: Option<Message>,
+ }
+
+ impl<'a, Message, Renderer> Modal<'a, Message, Renderer> {
+ /// Returns a new [`Modal`]
+ pub fn new(
+ base: impl Into<Element<'a, Message, Renderer>>,
+ modal: impl Into<Element<'a, Message, Renderer>>,
+ ) -> Self {
+ Self {
+ base: base.into(),
+ modal: modal.into(),
+ on_blur: None,
+ }
+ }
+
+ /// Sets the message that will be produces when the background
+ /// of the [`Modal`] is pressed
+ pub fn on_blur(self, on_blur: Message) -> Self {
+ Self {
+ on_blur: Some(on_blur),
+ ..self
+ }
+ }
+ }
+
+ impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for Modal<'a, Message, Renderer>
+ where
+ Renderer: iced_native::Renderer,
+ Message: Clone,
+ {
+ fn children(&self) -> Vec<Tree> {
+ vec![Tree::new(&self.base), Tree::new(&self.modal)]
+ }
+
+ fn diff(&self, tree: &mut Tree) {
+ tree.diff_children(&[&self.base, &self.modal]);
+ }
+
+ fn width(&self) -> Length {
+ self.base.as_widget().width()
+ }
+
+ fn height(&self) -> Length {
+ self.base.as_widget().height()
+ }
+
+ fn layout(
+ &self,
+ renderer: &Renderer,
+ limits: &layout::Limits,
+ ) -> layout::Node {
+ self.base.as_widget().layout(renderer, limits)
+ }
+
+ fn on_event(
+ &mut self,
+ state: &mut Tree,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ renderer: &Renderer,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ self.base.as_widget_mut().on_event(
+ &mut state.children[0],
+ event,
+ layout,
+ cursor_position,
+ renderer,
+ clipboard,
+ shell,
+ )
+ }
+
+ fn draw(
+ &self,
+ state: &Tree,
+ renderer: &mut Renderer,
+ theme: &<Renderer as iced_native::Renderer>::Theme,
+ style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ viewport: &Rectangle,
+ ) {
+ self.base.as_widget().draw(
+ &state.children[0],
+ renderer,
+ theme,
+ style,
+ layout,
+ cursor_position,
+ viewport,
+ );
+ }
+
+ fn overlay<'b>(
+ &'b mut self,
+ state: &'b mut Tree,
+ layout: Layout<'_>,
+ _renderer: &Renderer,
+ ) -> Option<overlay::Element<'b, Message, Renderer>> {
+ Some(overlay::Element::new(
+ layout.position(),
+ Box::new(Overlay {
+ content: &mut self.modal,
+ tree: &mut state.children[1],
+ size: layout.bounds().size(),
+ on_blur: self.on_blur.clone(),
+ }),
+ ))
+ }
+
+ fn mouse_interaction(
+ &self,
+ state: &Tree,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ viewport: &Rectangle,
+ renderer: &Renderer,
+ ) -> mouse::Interaction {
+ self.base.as_widget().mouse_interaction(
+ &state.children[0],
+ layout,
+ cursor_position,
+ viewport,
+ renderer,
+ )
+ }
+
+ fn operate(
+ &self,
+ state: &mut Tree,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ self.base.as_widget().operate(
+ &mut state.children[0],
+ layout,
+ renderer,
+ operation,
+ );
+ }
+ }
+
+ struct Overlay<'a, 'b, Message, Renderer> {
+ content: &'b mut Element<'a, Message, Renderer>,
+ tree: &'b mut Tree,
+ size: Size,
+ on_blur: Option<Message>,
+ }
+
+ impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
+ for Overlay<'a, 'b, Message, Renderer>
+ where
+ Renderer: iced_native::Renderer,
+ Message: Clone,
+ {
+ fn layout(
+ &self,
+ renderer: &Renderer,
+ _bounds: Size,
+ position: Point,
+ ) -> layout::Node {
+ let limits = layout::Limits::new(Size::ZERO, self.size)
+ .width(Length::Fill)
+ .height(Length::Fill);
+
+ let mut child = self.content.as_widget().layout(renderer, &limits);
+ child.align(Alignment::Center, Alignment::Center, limits.max());
+
+ let mut node = layout::Node::with_children(self.size, vec![child]);
+ node.move_to(position);
+
+ node
+ }
+
+ fn on_event(
+ &mut self,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ renderer: &Renderer,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ let content_bounds = layout.children().next().unwrap().bounds();
+
+ if let Some(message) = self.on_blur.as_ref() {
+ if let Event::Mouse(mouse::Event::ButtonPressed(
+ mouse::Button::Left,
+ )) = &event
+ {
+ if !content_bounds.contains(cursor_position) {
+ shell.publish(message.clone());
+ return event::Status::Captured;
+ }
+ }
+ }
+
+ self.content.as_widget_mut().on_event(
+ self.tree,
+ event,
+ layout.children().next().unwrap(),
+ cursor_position,
+ renderer,
+ clipboard,
+ shell,
+ )
+ }
+
+ fn draw(
+ &self,
+ renderer: &mut Renderer,
+ theme: &Renderer::Theme,
+ style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: layout.bounds(),
+ border_radius: renderer::BorderRadius::from(0.0),
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ Color {
+ a: 0.80,
+ ..Color::BLACK
+ },
+ );
+
+ self.content.as_widget().draw(
+ self.tree,
+ renderer,
+ theme,
+ style,
+ layout.children().next().unwrap(),
+ cursor_position,
+ &layout.bounds(),
+ );
+ }
+
+ fn operate(
+ &mut self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ self.content.as_widget().operate(
+ self.tree,
+ layout.children().next().unwrap(),
+ renderer,
+ operation,
+ );
+ }
+
+ fn mouse_interaction(
+ &self,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ viewport: &Rectangle,
+ renderer: &Renderer,
+ ) -> mouse::Interaction {
+ self.content.as_widget().mouse_interaction(
+ self.tree,
+ layout.children().next().unwrap(),
+ cursor_position,
+ viewport,
+ renderer,
+ )
+ }
+ }
+
+ impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
+ for Element<'a, Message, Renderer>
+ where
+ Renderer: 'a + iced_native::Renderer,
+ Message: 'a + Clone,
+ {
+ fn from(modal: Modal<'a, Message, Renderer>) -> Self {
+ Element::new(modal)
+ }
+ }
+}
diff --git a/examples/slider/Cargo.toml b/examples/slider/Cargo.toml
new file mode 100644
index 00000000..112d7cff
--- /dev/null
+++ b/examples/slider/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "slider"
+version = "0.1.0"
+authors = ["Casper Rogild Storm<casper@rogildstorm.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../.." }
diff --git a/examples/slider/README.md b/examples/slider/README.md
new file mode 100644
index 00000000..829d8285
--- /dev/null
+++ b/examples/slider/README.md
@@ -0,0 +1,14 @@
+## Slider
+
+A `Slider` is a bar and a handle that selects a single value from a range of values.
+There exists both `Slider` and `VerticalSlider` depending on which orientation you need.
+
+<div align="center">
+ <img src="sliders.gif">
+</div>
+
+You can run it with `cargo run`:
+
+```
+cargo run --package slider
+```
diff --git a/examples/slider/sliders.gif b/examples/slider/sliders.gif
new file mode 100644
index 00000000..f906d05a
--- /dev/null
+++ b/examples/slider/sliders.gif
Binary files differ
diff --git a/examples/slider/src/main.rs b/examples/slider/src/main.rs
new file mode 100644
index 00000000..6286d625
--- /dev/null
+++ b/examples/slider/src/main.rs
@@ -0,0 +1,63 @@
+use iced::widget::{column, container, slider, text, vertical_slider};
+use iced::{Element, Length, Sandbox, Settings};
+
+pub fn main() -> iced::Result {
+ Slider::run(Settings::default())
+}
+
+#[derive(Debug, Clone)]
+pub enum Message {
+ SliderChanged(u8),
+}
+
+pub struct Slider {
+ slider_value: u8,
+}
+
+impl Sandbox for Slider {
+ type Message = Message;
+
+ fn new() -> Slider {
+ Slider { slider_value: 50 }
+ }
+
+ fn title(&self) -> String {
+ String::from("Slider - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::SliderChanged(value) => {
+ self.slider_value = value;
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let value = self.slider_value;
+
+ let h_slider =
+ container(slider(0..=100, value, Message::SliderChanged))
+ .width(Length::Units(250));
+
+ let v_slider =
+ container(vertical_slider(0..=100, value, Message::SliderChanged))
+ .height(Length::Units(200));
+
+ let text = text(format!("{value}"));
+
+ container(
+ column![
+ container(v_slider).width(Length::Fill).center_x(),
+ container(h_slider).width(Length::Fill).center_x(),
+ container(text).width(Length::Fill).center_x(),
+ ]
+ .spacing(25),
+ )
+ .height(Length::Fill)
+ .width(Length::Fill)
+ .center_x()
+ .center_y()
+ .into()
+ }
+}
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index 27d175da..4dc92416 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -1,39 +1,76 @@
-use iced::widget::{container, svg};
-use iced::{Element, Length, Sandbox, Settings};
+use iced::theme;
+use iced::widget::{checkbox, column, container, svg};
+use iced::{color, Element, Length, Sandbox, Settings};
pub fn main() -> iced::Result {
Tiger::run(Settings::default())
}
-struct Tiger;
+#[derive(Debug, Default)]
+struct Tiger {
+ apply_color_filter: bool,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Message {
+ ToggleColorFilter(bool),
+}
impl Sandbox for Tiger {
- type Message = ();
+ type Message = Message;
fn new() -> Self {
- Tiger
+ Tiger::default()
}
fn title(&self) -> String {
String::from("SVG - Iced")
}
- fn update(&mut self, _message: ()) {}
+ fn update(&mut self, message: Self::Message) {
+ match message {
+ Message::ToggleColorFilter(apply_color_filter) => {
+ self.apply_color_filter = apply_color_filter;
+ }
+ }
+ }
- fn view(&self) -> Element<()> {
- let svg = svg(svg::Handle::from_path(format!(
+ fn view(&self) -> Element<Self::Message> {
+ let handle = svg::Handle::from_path(format!(
"{}/resources/tiger.svg",
env!("CARGO_MANIFEST_DIR")
- )))
- .width(Length::Fill)
- .height(Length::Fill);
+ ));
+
+ let svg = svg(handle).width(Length::Fill).height(Length::Fill).style(
+ if self.apply_color_filter {
+ theme::Svg::custom_fn(|_theme| svg::Appearance {
+ color: Some(color!(0x0000ff)),
+ })
+ } else {
+ theme::Svg::Default
+ },
+ );
- container(svg)
+ let apply_color_filter = checkbox(
+ "Apply a color filter",
+ self.apply_color_filter,
+ Message::ToggleColorFilter,
+ );
+
+ container(
+ column![
+ svg,
+ container(apply_color_filter).width(Length::Fill).center_x()
+ ]
+ .spacing(20)
.width(Length::Fill)
- .height(Length::Fill)
- .padding(20)
- .center_x()
- .center_y()
- .into()
+ .height(Length::Fill),
+ )
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .padding(20)
+ .center_x()
+ .center_y()
+ .into()
}
}
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index 30cf7581..1d4b68a6 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -125,9 +125,9 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how
/// to listen to time.
///
-/// [examples]: https://github.com/iced-rs/iced/tree/0.5/examples
-/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.5/examples/download_progress
-/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.5/examples/stopwatch
+/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples
+/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.6/examples/download_progress
+/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.6/examples/stopwatch
pub trait Recipe<Hasher: std::hash::Hasher, Event> {
/// The events that will be produced by a [`Subscription`] with this
/// [`Recipe`].
diff --git a/glow/Cargo.toml b/glow/Cargo.toml
index 1977f4b6..f586d24d 100644
--- a/glow/Cargo.toml
+++ b/glow/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_glow"
-version = "0.4.1"
+version = "0.5.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A glow renderer for iced"
@@ -34,11 +34,11 @@ bytemuck = "1.4"
log = "0.4"
[dependencies.iced_native]
-version = "0.6"
+version = "0.7"
path = "../native"
[dependencies.iced_graphics]
-version = "0.4"
+version = "0.5"
path = "../graphics"
features = ["font-fallback", "font-icons", "opengl"]
diff --git a/glow/src/backend.rs b/glow/src/backend.rs
index 1a41d540..416c3b94 100644
--- a/glow/src/backend.rs
+++ b/glow/src/backend.rs
@@ -128,8 +128,14 @@ impl Backend {
let scaled = transformation
* Transformation::scale(scale_factor, scale_factor);
- self.image_pipeline
- .draw(gl, scaled, scale_factor, &layer.images);
+ self.image_pipeline.draw(
+ gl,
+ target_height,
+ scaled,
+ scale_factor,
+ &layer.images,
+ bounds,
+ );
}
if !layer.text.is_empty() {
diff --git a/glow/src/image.rs b/glow/src/image.rs
index f906cd4c..521a01e7 100644
--- a/glow/src/image.rs
+++ b/glow/src/image.rs
@@ -14,6 +14,7 @@ use iced_graphics::image::raster;
use iced_graphics::image::vector;
use iced_graphics::layer;
+use iced_graphics::Rectangle;
use iced_graphics::Size;
use glow::HasContext;
@@ -141,14 +142,17 @@ impl Pipeline {
pub fn draw(
&mut self,
mut gl: &glow::Context,
+ target_height: u32,
transformation: Transformation,
_scale_factor: f32,
images: &[layer::Image],
+ layer_bounds: Rectangle<u32>,
) {
unsafe {
gl.use_program(Some(self.program));
gl.bind_vertex_array(Some(self.vertex_array));
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
+ gl.enable(glow::SCISSOR_TEST);
}
#[cfg(feature = "image")]
@@ -168,11 +172,16 @@ impl Pipeline {
layer::Image::Raster { handle: _, bounds } => (None, bounds),
#[cfg(feature = "svg")]
- layer::Image::Vector { handle, bounds } => {
+ layer::Image::Vector {
+ handle,
+ color,
+ bounds,
+ } => {
let size = [bounds.width, bounds.height];
(
vector_cache.upload(
handle,
+ *color,
size,
_scale_factor,
&mut gl,
@@ -183,10 +192,18 @@ impl Pipeline {
}
#[cfg(not(feature = "svg"))]
- layer::Image::Vector { handle: _, bounds } => (None, bounds),
+ layer::Image::Vector { bounds, .. } => (None, bounds),
};
unsafe {
+ gl.scissor(
+ layer_bounds.x as i32,
+ (target_height - (layer_bounds.y + layer_bounds.height))
+ as i32,
+ layer_bounds.width as i32,
+ layer_bounds.height as i32,
+ );
+
if let Some(storage::Entry { texture, .. }) = entry {
gl.bind_texture(glow::TEXTURE_2D, Some(*texture))
} else {
@@ -213,6 +230,7 @@ impl Pipeline {
gl.bind_buffer(glow::ARRAY_BUFFER, None);
gl.bind_vertex_array(None);
gl.use_program(None);
+ gl.disable(glow::SCISSOR_TEST);
}
}
diff --git a/glow/src/lib.rs b/glow/src/lib.rs
index e6ca0562..710ac36d 100644
--- a/glow/src/lib.rs
+++ b/glow/src/lib.rs
@@ -3,7 +3,7 @@
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//!
//! [`glow`]: https://github.com/grovesNL/glow
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs
index 28a5ea7c..e909162c 100644
--- a/glow/src/quad/compatibility.rs
+++ b/glow/src/quad/compatibility.rs
@@ -254,7 +254,7 @@ unsafe fn create_buffers(
gl.enable_vertex_attrib_array(4);
gl.vertex_attrib_pointer_f32(
4,
- 1,
+ 4,
glow::FLOAT,
false,
stride,
@@ -268,7 +268,7 @@ unsafe fn create_buffers(
glow::FLOAT,
false,
stride,
- 4 * (2 + 2 + 4 + 4 + 1),
+ 4 * (2 + 2 + 4 + 4 + 4),
);
gl.enable_vertex_attrib_array(6);
@@ -278,7 +278,7 @@ unsafe fn create_buffers(
glow::FLOAT,
false,
stride,
- 4 * (2 + 2 + 4 + 4 + 1 + 1),
+ 4 * (2 + 2 + 4 + 4 + 4 + 1),
);
gl.bind_vertex_array(None);
@@ -307,7 +307,7 @@ pub struct Vertex {
pub border_color: [f32; 4],
/// The border radius of the [`Vertex`].
- pub border_radius: f32,
+ pub border_radius: [f32; 4],
/// The border width of the [`Vertex`].
pub border_width: f32,
diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs
index 16bec385..89036530 100644
--- a/glow/src/quad/core.rs
+++ b/glow/src/quad/core.rs
@@ -218,7 +218,7 @@ unsafe fn create_instance_buffer(
gl.enable_vertex_attrib_array(4);
gl.vertex_attrib_pointer_f32(
4,
- 1,
+ 4,
glow::FLOAT,
false,
stride,
@@ -233,7 +233,7 @@ unsafe fn create_instance_buffer(
glow::FLOAT,
false,
stride,
- 4 * (2 + 2 + 4 + 4 + 1),
+ 4 * (2 + 2 + 4 + 4 + 4),
);
gl.vertex_attrib_divisor(5, 1);
diff --git a/glow/src/shader/compatibility/quad.frag b/glow/src/shader/compatibility/quad.frag
index 8ea5693d..bb9d8122 100644
--- a/glow/src/shader/compatibility/quad.frag
+++ b/glow/src/shader/compatibility/quad.frag
@@ -12,7 +12,7 @@ varying vec4 v_Color;
varying vec4 v_BorderColor;
varying vec2 v_Pos;
varying vec2 v_Scale;
-varying float v_BorderRadius;
+varying vec4 v_BorderRadius;
varying float v_BorderWidth;
float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius)
@@ -33,10 +33,26 @@ float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius)
return sqrt(distance.x * distance.x + distance.y * distance.y);
}
+float selectBorderRadius(vec4 radi, vec2 position, vec2 center)
+{
+ float rx = radi.x;
+ float ry = radi.y;
+ rx = position.x > center.x ? radi.y : radi.x;
+ ry = position.x > center.x ? radi.z : radi.w;
+ rx = position.y > center.y ? ry : rx;
+ return rx;
+}
+
void main() {
vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
- float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0);
+ float border_radius = selectBorderRadius(
+ v_BorderRadius,
+ fragCoord,
+ (v_Pos + v_Scale * 0.5).xy
+ );
+
+ float internal_border = max(border_radius - v_BorderWidth, 0.0);
float internal_distance = _distance(
fragCoord,
@@ -57,11 +73,11 @@ void main() {
fragCoord,
v_Pos,
v_Scale,
- v_BorderRadius
+ border_radius
);
float radius_alpha =
- 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d);
+ 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d);
gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
}
diff --git a/glow/src/shader/compatibility/quad.vert b/glow/src/shader/compatibility/quad.vert
index abe70c0e..89931f06 100644
--- a/glow/src/shader/compatibility/quad.vert
+++ b/glow/src/shader/compatibility/quad.vert
@@ -5,7 +5,7 @@ attribute vec2 i_Pos;
attribute vec2 i_Scale;
attribute vec4 i_Color;
attribute vec4 i_BorderColor;
-attribute float i_BorderRadius;
+attribute vec4 i_BorderRadius;
attribute float i_BorderWidth;
attribute vec2 q_Pos;
@@ -13,7 +13,7 @@ varying vec4 v_Color;
varying vec4 v_BorderColor;
varying vec2 v_Pos;
varying vec2 v_Scale;
-varying float v_BorderRadius;
+varying vec4 v_BorderRadius;
varying float v_BorderWidth;
@@ -21,9 +21,11 @@ void main() {
vec2 p_Pos = i_Pos * u_Scale;
vec2 p_Scale = i_Scale * u_Scale;
- float i_BorderRadius = min(
- i_BorderRadius,
- min(i_Scale.x, i_Scale.y) / 2.0
+ vec4 i_BorderRadius = vec4(
+ min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0),
+ min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0),
+ min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0),
+ min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0)
);
mat4 i_Transform = mat4(
diff --git a/glow/src/shader/core/quad.frag b/glow/src/shader/core/quad.frag
index 57e2e8e7..71147aa5 100644
--- a/glow/src/shader/core/quad.frag
+++ b/glow/src/shader/core/quad.frag
@@ -17,7 +17,7 @@ in vec4 v_Color;
in vec4 v_BorderColor;
in vec2 v_Pos;
in vec2 v_Scale;
-in float v_BorderRadius;
+in vec4 v_BorderRadius;
in float v_BorderWidth;
float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius)
@@ -38,14 +38,30 @@ float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius)
return sqrt(distance.x * distance.x + distance.y * distance.y);
}
+float selectBorderRadius(vec4 radi, vec2 position, vec2 center)
+{
+ float rx = radi.x;
+ float ry = radi.y;
+ rx = position.x > center.x ? radi.y : radi.x;
+ ry = position.x > center.x ? radi.z : radi.w;
+ rx = position.y > center.y ? ry : rx;
+ return rx;
+}
+
void main() {
vec4 mixed_color;
vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
+ float border_radius = selectBorderRadius(
+ v_BorderRadius,
+ fragCoord,
+ (v_Pos + v_Scale * 0.5).xy
+ );
+
// TODO: Remove branching (?)
if(v_BorderWidth > 0.0) {
- float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0);
+ float internal_border = max(border_radius - v_BorderWidth, 0.0);
float internal_distance = fDistance(
fragCoord,
@@ -69,11 +85,11 @@ void main() {
fragCoord,
v_Pos,
v_Scale,
- v_BorderRadius
+ border_radius
);
float radius_alpha =
- 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d);
+ 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d);
gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
}
diff --git a/glow/src/shader/core/quad.vert b/glow/src/shader/core/quad.vert
index b1fb2365..17c3e641 100644
--- a/glow/src/shader/core/quad.vert
+++ b/glow/src/shader/core/quad.vert
@@ -5,14 +5,14 @@ in vec2 i_Pos;
in vec2 i_Scale;
in vec4 i_Color;
in vec4 i_BorderColor;
-in float i_BorderRadius;
+in vec4 i_BorderRadius;
in float i_BorderWidth;
out vec4 v_Color;
out vec4 v_BorderColor;
out vec2 v_Pos;
out vec2 v_Scale;
-out float v_BorderRadius;
+out vec4 v_BorderRadius;
out float v_BorderWidth;
vec2 positions[4] = vec2[](
@@ -27,9 +27,11 @@ void main() {
vec2 p_Pos = i_Pos * u_Scale;
vec2 p_Scale = i_Scale * u_Scale;
- float i_BorderRadius = min(
- i_BorderRadius,
- min(i_Scale.x, i_Scale.y) / 2.0
+ vec4 i_BorderRadius = vec4(
+ min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0),
+ min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0),
+ min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0),
+ min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0)
);
mat4 i_Transform = mat4(
diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml
index 730ee6f7..022457b1 100644
--- a/glutin/Cargo.toml
+++ b/glutin/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_glutin"
-version = "0.4.0"
+version = "0.5.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A glutin runtime for Iced"
@@ -23,15 +23,15 @@ git = "https://github.com/iced-rs/glutin"
rev = "da8d291486b4c9bec12487a46c119c4b1d386abf"
[dependencies.iced_native]
-version = "0.6"
+version = "0.7"
path = "../native"
[dependencies.iced_winit]
-version = "0.5"
+version = "0.6"
path = "../winit"
features = ["application"]
[dependencies.iced_graphics]
-version = "0.4"
+version = "0.5"
path = "../graphics"
features = ["opengl"]
diff --git a/glutin/src/application.rs b/glutin/src/application.rs
index f474cbba..3e9d11f9 100644
--- a/glutin/src/application.rs
+++ b/glutin/src/application.rs
@@ -200,6 +200,7 @@ async fn run_instance<A, E, C>(
let mut cache = user_interface::Cache::default();
let mut state = application::State::new(&application, context.window());
let mut viewport_version = state.viewport_version();
+ let mut should_exit = false;
application::run_command(
&application,
@@ -209,6 +210,7 @@ async fn run_instance<A, E, C>(
init_command,
&mut runtime,
&mut clipboard,
+ &mut should_exit,
&mut proxy,
&mut debug,
context.window(),
@@ -271,6 +273,7 @@ async fn run_instance<A, E, C>(
&mut renderer,
&mut runtime,
&mut clipboard,
+ &mut should_exit,
&mut proxy,
&mut debug,
&mut messages,
@@ -281,8 +284,6 @@ async fn run_instance<A, E, C>(
// Update window
state.synchronize(&application, context.window());
- let should_exit = application.should_exit();
-
user_interface =
ManuallyDrop::new(application::build_user_interface(
&application,
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index b601f37c..823a05f4 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_graphics"
-version = "0.4.0"
+version = "0.5.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced"
@@ -44,7 +44,7 @@ version = "1.4"
features = ["derive"]
[dependencies.iced_native]
-version = "0.6"
+version = "0.7"
path = "../native"
[dependencies.iced_style]
diff --git a/graphics/src/image/storage.rs b/graphics/src/image/storage.rs
index 2098c7b2..1b5b5c35 100644
--- a/graphics/src/image/storage.rs
+++ b/graphics/src/image/storage.rs
@@ -20,7 +20,7 @@ pub trait Storage {
state: &mut Self::State<'_>,
) -> Option<Self::Entry>;
- /// Romve a [`Self::Entry`] from the [`Storage`].
+ /// Remove a [`Self::Entry`] from the [`Storage`].
fn remove(&mut self, entry: &Self::Entry, state: &mut Self::State<'_>);
}
diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs
index 42f4b500..82d77aff 100644
--- a/graphics/src/image/vector.rs
+++ b/graphics/src/image/vector.rs
@@ -1,5 +1,6 @@
//! Vector image loading and caching
use crate::image::Storage;
+use crate::Color;
use iced_native::svg;
use iced_native::Size;
@@ -33,11 +34,13 @@ impl Svg {
#[derive(Debug)]
pub struct Cache<T: Storage> {
svgs: HashMap<u64, Svg>,
- rasterized: HashMap<(u64, u32, u32), T::Entry>,
+ rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>,
svg_hits: HashSet<u64>,
- rasterized_hits: HashSet<(u64, u32, u32)>,
+ rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>,
}
+type ColorFilter = Option<[u8; 4]>;
+
impl<T: Storage> Cache<T> {
/// Load svg
pub fn load(&mut self, handle: &svg::Handle) -> &Svg {
@@ -76,6 +79,7 @@ impl<T: Storage> Cache<T> {
pub fn upload(
&mut self,
handle: &svg::Handle,
+ color: Option<Color>,
[width, height]: [f32; 2],
scale: f32,
state: &mut T::State<'_>,
@@ -88,15 +92,18 @@ impl<T: Storage> Cache<T> {
(scale * height).ceil() as u32,
);
+ let color = color.map(Color::into_rgba8);
+ let key = (id, width, height, color);
+
// TODO: Optimize!
// We currently rerasterize the SVG when its size changes. This is slow
// as heck. A GPU rasterizer like `pathfinder` may perform better.
// It would be cool to be able to smooth resize the `svg` example.
- if self.rasterized.contains_key(&(id, width, height)) {
+ if self.rasterized.contains_key(&key) {
let _ = self.svg_hits.insert(id);
- let _ = self.rasterized_hits.insert((id, width, height));
+ let _ = self.rasterized_hits.insert(key);
- return self.rasterized.get(&(id, width, height));
+ return self.rasterized.get(&key);
}
match self.load(handle) {
@@ -121,15 +128,26 @@ impl<T: Storage> Cache<T> {
img.as_mut(),
)?;
- let allocation =
- storage.upload(width, height, img.data(), state)?;
+ let mut rgba = img.take();
+
+ if let Some(color) = color {
+ rgba.chunks_exact_mut(4).for_each(|rgba| {
+ if rgba[3] > 0 {
+ rgba[0] = color[0];
+ rgba[1] = color[1];
+ rgba[2] = color[2];
+ }
+ });
+ }
+
+ let allocation = storage.upload(width, height, &rgba, state)?;
log::debug!("allocating {} {}x{}", id, width, height);
let _ = self.svg_hits.insert(id);
- let _ = self.rasterized_hits.insert((id, width, height));
- let _ = self.rasterized.insert((id, width, height), allocation);
+ let _ = self.rasterized_hits.insert(key);
+ let _ = self.rasterized.insert(key, allocation);
- self.rasterized.get(&(id, width, height))
+ self.rasterized.get(&key)
}
Svg::NotFound => None,
}
diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs
index fd670f48..1d453caa 100644
--- a/graphics/src/layer.rs
+++ b/graphics/src/layer.rs
@@ -251,11 +251,16 @@ impl<'a> Layer<'a> {
bounds: *bounds + translation,
});
}
- Primitive::Svg { handle, bounds } => {
+ Primitive::Svg {
+ handle,
+ color,
+ bounds,
+ } => {
let layer = &mut layers[current_layer];
layer.images.push(Image::Vector {
handle: handle.clone(),
+ color: *color,
bounds: *bounds + translation,
});
}
diff --git a/graphics/src/layer/image.rs b/graphics/src/layer/image.rs
index 045ec665..3eff2397 100644
--- a/graphics/src/layer/image.rs
+++ b/graphics/src/layer/image.rs
@@ -1,4 +1,5 @@
-use crate::Rectangle;
+use crate::{Color, Rectangle};
+
use iced_native::{image, svg};
/// A raster or vector image.
@@ -17,6 +18,9 @@ pub enum Image {
/// The handle of a vector image.
handle: svg::Handle,
+ /// The [`Color`] filter
+ color: Option<Color>,
+
/// The bounds of the image.
bounds: Rectangle,
},
diff --git a/graphics/src/layer/quad.rs b/graphics/src/layer/quad.rs
index 4def8427..0d8bde9d 100644
--- a/graphics/src/layer/quad.rs
+++ b/graphics/src/layer/quad.rs
@@ -17,7 +17,7 @@ pub struct Quad {
pub border_color: [f32; 4],
/// The border radius of the [`Quad`].
- pub border_radius: f32,
+ pub border_radius: [f32; 4],
/// The border width of the [`Quad`].
pub border_width: f32,
diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs
index 9759d97a..5a163a2f 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -42,7 +42,7 @@ pub enum Primitive {
/// The background of the quad
background: Background,
/// The border radius of the quad
- border_radius: f32,
+ border_radius: [f32; 4],
/// The border width of the quad
border_width: f32,
/// The border color of the quad
@@ -60,6 +60,9 @@ pub enum Primitive {
/// The path of the SVG file
handle: svg::Handle,
+ /// The [`Color`] filter
+ color: Option<Color>,
+
/// The bounds of the viewport
bounds: Rectangle,
},
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index 036b398c..aabdf7fc 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -6,7 +6,7 @@ use iced_native::layout;
use iced_native::renderer;
use iced_native::svg;
use iced_native::text::{self, Text};
-use iced_native::{Background, Element, Font, Point, Rectangle, Size};
+use iced_native::{Background, Color, Element, Font, Point, Rectangle, Size};
pub use iced_native::renderer::Style;
@@ -109,7 +109,7 @@ where
self.primitives.push(Primitive::Quad {
bounds: quad.bounds,
background: background.into(),
- border_radius: quad.border_radius,
+ border_radius: quad.border_radius.into(),
border_width: quad.border_width,
border_color: quad.border_color,
});
@@ -200,7 +200,16 @@ where
self.backend().viewport_dimensions(handle)
}
- fn draw(&mut self, handle: svg::Handle, bounds: Rectangle) {
- self.draw_primitive(Primitive::Svg { handle, bounds })
+ fn draw(
+ &mut self,
+ handle: svg::Handle,
+ color: Option<Color>,
+ bounds: Rectangle,
+ ) {
+ self.draw_primitive(Primitive::Svg {
+ handle,
+ color,
+ bounds,
+ })
}
}
diff --git a/lazy/Cargo.toml b/lazy/Cargo.toml
index 51459148..1b26e5c9 100644
--- a/lazy/Cargo.toml
+++ b/lazy/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_lazy"
-version = "0.2.0"
+version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "Lazy widgets for Iced"
@@ -14,5 +14,5 @@ categories = ["gui"]
ouroboros = "0.13"
[dependencies.iced_native]
-version = "0.6"
+version = "0.7"
path = "../native"
diff --git a/lazy/src/component.rs b/lazy/src/component.rs
index 4f1df650..d8f21f8a 100644
--- a/lazy/src/component.rs
+++ b/lazy/src/component.rs
@@ -11,7 +11,7 @@ use iced_native::{
};
use ouroboros::self_referencing;
-use std::cell::{Ref, RefCell};
+use std::cell::RefCell;
use std::marker::PhantomData;
/// A reusable, custom widget that uses The Elm Architecture.
@@ -46,6 +46,16 @@ pub trait Component<Message, Renderer> {
/// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event)
/// on user interaction.
fn view(&self, state: &Self::State) -> Element<'_, Self::Event, Renderer>;
+
+ /// Update the [`Component`] state based on the provided [`Operation`](widget::Operation)
+ ///
+ /// By default, it does nothing.
+ fn operate(
+ &self,
+ _state: &mut Self::State,
+ _operation: &mut dyn widget::Operation<Message>,
+ ) {
+ }
}
/// Turns an implementor of [`Component`] into an [`Element`] that can be
@@ -106,6 +116,26 @@ where
);
}
+ fn rebuild_element_with_operation(
+ &self,
+ state: &mut S,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ let heads = self.state.borrow_mut().take().unwrap().into_heads();
+
+ heads.component.operate(state, operation);
+
+ *self.state.borrow_mut() = Some(
+ StateBuilder {
+ component: heads.component,
+ message: PhantomData,
+ state: PhantomData,
+ element_builder: |component| Some(component.view(state)),
+ }
+ .build(),
+ );
+ }
+
fn with_element<T>(
&self,
f: impl FnOnce(&Element<'_, Event, Renderer>) -> T,
@@ -234,8 +264,14 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
+ self.rebuild_element_with_operation(
+ tree.state.downcast_mut(),
+ operation,
+ );
+
struct MapOperation<'a, B> {
operation: &'a mut dyn widget::Operation<B>,
}
@@ -274,6 +310,7 @@ where
element.as_widget().operate(
&mut tree.children[0],
layout,
+ renderer,
&mut MapOperation { operation },
);
});
@@ -322,25 +359,25 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
let overlay = OverlayBuilder {
instance: self,
- instance_ref_builder: |instance| instance.state.borrow(),
tree,
types: PhantomData,
overlay_builder: |instance, tree| {
- instance
- .as_ref()
- .unwrap()
- .borrow_element()
- .as_ref()
- .unwrap()
- .as_widget()
- .overlay(&mut tree.children[0], layout, renderer)
+ instance.state.get_mut().as_mut().unwrap().with_element_mut(
+ move |element| {
+ element.as_mut().unwrap().as_widget_mut().overlay(
+ &mut tree.children[0],
+ layout,
+ renderer,
+ )
+ },
+ )
},
}
.build();
@@ -362,15 +399,11 @@ where
#[self_referencing]
struct Overlay<'a, 'b, Message, Renderer, Event, S> {
- instance: &'a Instance<'b, Message, Renderer, Event, S>,
+ instance: &'a mut Instance<'b, Message, Renderer, Event, S>,
tree: &'a mut Tree,
types: PhantomData<(Message, Event, S)>,
- #[borrows(instance)]
- #[covariant]
- instance_ref: Ref<'this, Option<State<'a, Message, Renderer, Event, S>>>,
-
- #[borrows(instance_ref, mut tree)]
+ #[borrows(mut instance, mut tree)]
#[covariant]
overlay: Option<overlay::Element<'this, Event, Renderer>>,
}
@@ -514,7 +547,6 @@ where
self.overlay = Some(
OverlayBuilder {
instance: overlay.instance,
- instance_ref_builder: |instance| instance.state.borrow(),
tree: overlay.tree,
types: PhantomData,
overlay_builder: |_, _| None,
diff --git a/lazy/src/lazy.rs b/lazy/src/lazy.rs
index d61cc77e..ec35e8f0 100644
--- a/lazy/src/lazy.rs
+++ b/lazy/src/lazy.rs
@@ -130,12 +130,14 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
self.with_element(|element| {
element.as_widget().operate(
&mut tree.children[0],
layout,
+ renderer,
operation,
);
});
@@ -207,7 +209,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -216,12 +218,12 @@ where
cached: self,
tree: &mut tree.children[0],
types: PhantomData,
- element_ref_builder: |cached| cached.element.borrow(),
- element_builder: |element_ref| {
- element_ref.as_ref().unwrap().borrow()
- },
- overlay_builder: |element, tree| {
- element.as_widget().overlay(tree, layout, renderer)
+ overlay_builder: |cached, tree| {
+ Rc::get_mut(cached.element.get_mut().as_mut().unwrap())
+ .unwrap()
+ .get_mut()
+ .as_widget_mut()
+ .overlay(tree, layout, renderer)
},
}
.build();
@@ -237,20 +239,11 @@ where
#[self_referencing]
struct Overlay<'a, 'b, Message, Renderer, Dependency, View> {
- cached: &'a Lazy<'b, Message, Renderer, Dependency, View>,
+ cached: &'a mut Lazy<'b, Message, Renderer, Dependency, View>,
tree: &'a mut Tree,
types: PhantomData<(Message, Dependency, View)>,
- #[borrows(cached)]
- #[covariant]
- element_ref:
- Ref<'this, Option<Rc<RefCell<Element<'static, Message, Renderer>>>>>,
-
- #[borrows(element_ref)]
- #[covariant]
- element: Ref<'this, Element<'static, Message, Renderer>>,
-
- #[borrows(element, mut tree)]
+ #[borrows(mut cached, mut tree)]
#[covariant]
overlay: Option<overlay::Element<'this, Message, Renderer>>,
}
diff --git a/lazy/src/responsive.rs b/lazy/src/responsive.rs
index 0b7ae6de..945c935a 100644
--- a/lazy/src/responsive.rs
+++ b/lazy/src/responsive.rs
@@ -3,8 +3,8 @@ use iced_native::layout::{self, Layout};
use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
-use iced_native::widget::horizontal_space;
use iced_native::widget::tree::{self, Tree};
+use iced_native::widget::{self, horizontal_space};
use iced_native::{
Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget,
};
@@ -142,6 +142,29 @@ where
layout::Node::new(limits.max())
}
+ fn operate(
+ &self,
+ tree: &mut Tree,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ let state = tree.state.downcast_mut::<State>();
+ let mut content = self.content.borrow_mut();
+
+ content.resolve(
+ &mut state.tree.borrow_mut(),
+ renderer,
+ layout,
+ &self.view,
+ |tree, renderer, layout, element| {
+ element
+ .as_widget()
+ .operate(tree, layout, renderer, operation);
+ },
+ );
+ }
+
fn on_event(
&mut self,
tree: &mut Tree,
@@ -235,18 +258,20 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
+ use std::ops::DerefMut;
+
let state = tree.state.downcast_ref::<State>();
let overlay = OverlayBuilder {
content: self.content.borrow_mut(),
tree: state.tree.borrow_mut(),
types: PhantomData,
- overlay_builder: |content, tree| {
+ overlay_builder: |content: &mut RefMut<Content<_, _>>, tree| {
content.update(
tree,
renderer,
@@ -254,16 +279,18 @@ where
&self.view,
);
+ let Content {
+ element, layout, ..
+ } = content.deref_mut();
+
let content_layout = Layout::with_offset(
- layout.position() - Point::ORIGIN,
- &content.layout,
+ layout.bounds().position() - Point::ORIGIN,
+ layout,
);
- content.element.as_widget().overlay(
- tree,
- content_layout,
- renderer,
- )
+ element
+ .as_widget_mut()
+ .overlay(tree, content_layout, renderer)
},
}
.build();
diff --git a/native/Cargo.toml b/native/Cargo.toml
index 558909be..bbf92951 100644
--- a/native/Cargo.toml
+++ b/native/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_native"
-version = "0.6.1"
+version = "0.7.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A renderer-agnostic library for native GUIs"
@@ -25,5 +25,5 @@ path = "../futures"
features = ["thread-pool"]
[dependencies.iced_style]
-version = "0.5"
+version = "0.5.1"
path = "../style"
diff --git a/native/src/element.rs b/native/src/element.rs
index f941a490..2409b1c9 100644
--- a/native/src/element.rs
+++ b/native/src/element.rs
@@ -290,6 +290,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<B>,
) {
struct MapOperation<'a, B> {
@@ -334,8 +335,12 @@ where
}
}
- self.widget
- .operate(tree, layout, &mut MapOperation { operation });
+ self.widget.operate(
+ tree,
+ layout,
+ renderer,
+ &mut MapOperation { operation },
+ );
}
fn on_event(
@@ -405,7 +410,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -473,9 +478,12 @@ where
&self,
state: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
- self.element.widget.operate(state, layout, operation)
+ self.element
+ .widget
+ .operate(state, layout, renderer, operation)
}
fn on_event(
@@ -519,7 +527,7 @@ where
bounds: layout.bounds(),
border_color: color,
border_width: 1.0,
- border_radius: 0.0,
+ border_radius: 0.0.into(),
},
Color::TRANSPARENT,
);
@@ -560,7 +568,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
state: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
diff --git a/native/src/layout/limits.rs b/native/src/layout/limits.rs
index 6d5f6563..33a452d0 100644
--- a/native/src/layout/limits.rs
+++ b/native/src/layout/limits.rs
@@ -51,7 +51,7 @@ impl Limits {
}
Length::Units(units) => {
let new_width =
- (units as f32).min(self.max.width).max(self.min.width);
+ (units as f32).clamp(self.min.width, self.max.width);
self.min.width = new_width;
self.max.width = new_width;
@@ -73,7 +73,7 @@ impl Limits {
}
Length::Units(units) => {
let new_height =
- (units as f32).min(self.max.height).max(self.min.height);
+ (units as f32).clamp(self.min.height, self.max.height);
self.min.height = new_height;
self.max.height = new_height;
@@ -86,16 +86,14 @@ impl Limits {
/// Applies a minimum width constraint to the current [`Limits`].
pub fn min_width(mut self, min_width: u32) -> Limits {
- self.min.width =
- self.min.width.max(min_width as f32).min(self.max.width);
+ self.min.width = self.min.width.clamp(min_width as f32, self.max.width);
self
}
/// Applies a maximum width constraint to the current [`Limits`].
pub fn max_width(mut self, max_width: u32) -> Limits {
- self.max.width =
- self.max.width.min(max_width as f32).max(self.min.width);
+ self.max.width = self.max.width.clamp(self.min.width, max_width as f32);
self
}
@@ -103,7 +101,7 @@ impl Limits {
/// Applies a minimum height constraint to the current [`Limits`].
pub fn min_height(mut self, min_height: u32) -> Limits {
self.min.height =
- self.min.height.max(min_height as f32).min(self.max.height);
+ self.min.height.clamp(min_height as f32, self.max.height);
self
}
@@ -111,7 +109,7 @@ impl Limits {
/// Applies a maximum height constraint to the current [`Limits`].
pub fn max_height(mut self, max_height: u32) -> Limits {
self.max.height =
- self.max.height.min(max_height as f32).max(self.min.height);
+ self.max.height.clamp(self.min.height, max_height as f32);
self
}
@@ -157,14 +155,10 @@ impl Limits {
/// intrinsic size of some content.
pub fn resolve(&self, intrinsic_size: Size) -> Size {
Size::new(
- intrinsic_size
- .width
- .min(self.max.width)
- .max(self.fill.width),
+ intrinsic_size.width.clamp(self.fill.width, self.max.width),
intrinsic_size
.height
- .min(self.max.height)
- .max(self.fill.height),
+ .clamp(self.fill.height, self.max.height),
)
}
}
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 62b8f372..ce7c010d 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -23,8 +23,8 @@
//! - Build a new renderer, see the [renderer] module.
//! - Build a custom widget, start at the [`Widget`] trait.
//!
-//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.5/core
-//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.5/winit
+//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.6/core
+//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.6/winit
//! [`druid`]: https://github.com/xi-editor/druid
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
//! [renderer]: crate::renderer
diff --git a/native/src/overlay.rs b/native/src/overlay.rs
index 8c01581f..22f8b6ec 100644
--- a/native/src/overlay.rs
+++ b/native/src/overlay.rs
@@ -44,8 +44,9 @@ where
/// Applies a [`widget::Operation`] to the [`Overlay`].
fn operate(
- &self,
+ &mut self,
_layout: Layout<'_>,
+ _renderer: &Renderer,
_operation: &mut dyn widget::Operation<Message>,
) {
}
@@ -93,7 +94,7 @@ where
/// This method will generally only be used by advanced users that are
/// implementing the [`Widget`](crate::Widget) trait.
pub fn from_children<'a, Message, Renderer>(
- children: &'a [crate::Element<'_, Message, Renderer>],
+ children: &'a mut [crate::Element<'_, Message, Renderer>],
tree: &'a mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -102,11 +103,11 @@ where
Renderer: crate::Renderer,
{
children
- .iter()
+ .iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.filter_map(|((child, state), layout)| {
- child.as_widget().overlay(state, layout, renderer)
+ child.as_widget_mut().overlay(state, layout, renderer)
})
.next()
}
diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs
index 09eee86d..498e9ae3 100644
--- a/native/src/overlay/element.rs
+++ b/native/src/overlay/element.rs
@@ -106,11 +106,12 @@ where
/// Applies a [`widget::Operation`] to the [`Element`].
pub fn operate(
- &self,
+ &mut self,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
- self.overlay.operate(layout, operation);
+ self.overlay.operate(layout, renderer, operation);
}
}
@@ -142,8 +143,9 @@ where
}
fn operate(
- &self,
+ &mut self,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<B>,
) {
struct MapOperation<'a, B> {
@@ -189,7 +191,7 @@ where
}
self.content
- .operate(layout, &mut MapOperation { operation });
+ .operate(layout, renderer, &mut MapOperation { operation });
}
fn on_event(
diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs
index 2e4f70b5..099b1a97 100644
--- a/native/src/overlay/menu.rs
+++ b/native/src/overlay/menu.rs
@@ -287,7 +287,7 @@ where
},
border_color: appearance.border_color,
border_width: appearance.border_width,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
},
appearance.background,
);
@@ -479,7 +479,7 @@ where
bounds,
border_color: Color::TRANSPARENT,
border_width: 0.0,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
},
appearance.selected_background,
);
diff --git a/native/src/renderer.rs b/native/src/renderer.rs
index ef64ac36..5e776be6 100644
--- a/native/src/renderer.rs
+++ b/native/src/renderer.rs
@@ -50,7 +50,7 @@ pub struct Quad {
pub bounds: Rectangle,
/// The border radius of the [`Quad`].
- pub border_radius: f32,
+ pub border_radius: BorderRadius,
/// The border width of the [`Quad`].
pub border_width: f32,
@@ -59,6 +59,29 @@ pub struct Quad {
pub border_color: Color,
}
+/// The border radi for the corners of a graphics primitive in the order:
+/// top-left, top-right, bottom-right, bottom-left.
+#[derive(Debug, Clone, Copy, PartialEq, Default)]
+pub struct BorderRadius([f32; 4]);
+
+impl From<f32> for BorderRadius {
+ fn from(w: f32) -> Self {
+ Self([w; 4])
+ }
+}
+
+impl From<[f32; 4]> for BorderRadius {
+ fn from(radi: [f32; 4]) -> Self {
+ Self(radi)
+ }
+}
+
+impl From<BorderRadius> for [f32; 4] {
+ fn from(radi: BorderRadius) -> Self {
+ radi.0
+ }
+}
+
/// The styling attributes of a [`Renderer`].
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Style {
diff --git a/native/src/subscription.rs b/native/src/subscription.rs
index d24801d4..c60b1281 100644
--- a/native/src/subscription.rs
+++ b/native/src/subscription.rs
@@ -155,7 +155,7 @@ where
/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
/// connection open.
///
-/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.5/examples/websocket
+/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.6/examples/websocket
pub fn unfold<I, T, Fut, Message>(
id: I,
initial: T,
diff --git a/native/src/svg.rs b/native/src/svg.rs
index a8e481d2..2168e409 100644
--- a/native/src/svg.rs
+++ b/native/src/svg.rs
@@ -1,5 +1,5 @@
//! Load and draw vector graphics.
-use crate::{Hasher, Rectangle, Size};
+use crate::{Color, Hasher, Rectangle, Size};
use std::borrow::Cow;
use std::hash::{Hash, Hasher as _};
@@ -84,6 +84,6 @@ pub trait Renderer: crate::Renderer {
/// Returns the default dimensions of an SVG for the given [`Handle`].
fn dimensions(&self, handle: &Handle) -> Size<u32>;
- /// Draws an SVG with the given [`Handle`] and inside the provided `bounds`.
- fn draw(&mut self, handle: Handle, bounds: Rectangle);
+ /// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`.
+ fn draw(&mut self, handle: Handle, color: Option<Color>, bounds: Rectangle);
}
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 7c82878c..2b43829d 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -18,8 +18,8 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// The [`integration_opengl`] & [`integration_wgpu`] examples use a
/// [`UserInterface`] to integrate Iced in an existing graphical application.
///
-/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_opengl
-/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_wgpu
+/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_opengl
+/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_wgpu
#[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> {
root: Element<'a, Message, Renderer>,
@@ -190,7 +190,7 @@ where
let mut state = State::Updated;
let mut manual_overlay =
- ManuallyDrop::new(self.root.as_widget().overlay(
+ ManuallyDrop::new(self.root.as_widget_mut().overlay(
&mut self.state,
Layout::new(&self.base),
renderer,
@@ -226,7 +226,7 @@ where
);
manual_overlay =
- ManuallyDrop::new(self.root.as_widget().overlay(
+ ManuallyDrop::new(self.root.as_widget_mut().overlay(
&mut self.state,
Layout::new(&self.base),
renderer,
@@ -395,11 +395,11 @@ where
let viewport = Rectangle::with_size(self.bounds);
- let base_cursor = if let Some(overlay) = self.root.as_widget().overlay(
- &mut self.state,
- Layout::new(&self.base),
- renderer,
- ) {
+ let base_cursor = if let Some(overlay) = self
+ .root
+ .as_widget_mut()
+ .overlay(&mut self.state, Layout::new(&self.base), renderer)
+ {
let overlay_layout = self
.overlay
.take()
@@ -452,7 +452,7 @@ where
overlay
.as_ref()
.and_then(|layout| {
- root.as_widget()
+ root.as_widget_mut()
.overlay(&mut self.state, Layout::new(base), renderer)
.map(|overlay| {
let overlay_interaction = overlay.mouse_interaction(
@@ -493,17 +493,24 @@ where
self.root.as_widget().operate(
&mut self.state,
Layout::new(&self.base),
+ renderer,
operation,
);
- if let Some(layout) = self.overlay.as_ref() {
- if let Some(overlay) = self.root.as_widget().overlay(
- &mut self.state,
- Layout::new(&self.base),
- renderer,
- ) {
- overlay.operate(Layout::new(layout), operation);
+ if let Some(mut overlay) = self.root.as_widget_mut().overlay(
+ &mut self.state,
+ Layout::new(&self.base),
+ renderer,
+ ) {
+ if self.overlay.is_none() {
+ self.overlay = Some(overlay.layout(renderer, self.bounds));
}
+
+ overlay.operate(
+ Layout::new(self.overlay.as_ref().unwrap()),
+ renderer,
+ operation,
+ );
}
}
diff --git a/native/src/widget.rs b/native/src/widget.rs
index 526c7d00..f714e28a 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -33,6 +33,7 @@ pub mod text_input;
pub mod toggler;
pub mod tooltip;
pub mod tree;
+pub mod vertical_slider;
mod action;
mod id;
@@ -79,6 +80,8 @@ pub use toggler::Toggler;
pub use tooltip::Tooltip;
#[doc(no_inline)]
pub use tree::Tree;
+#[doc(no_inline)]
+pub use vertical_slider::VerticalSlider;
pub use action::Action;
pub use id::Id;
@@ -107,12 +110,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
/// - [`geometry`], a custom widget showcasing how to draw geometry with the
/// `Mesh2D` primitive in [`iced_wgpu`].
///
-/// [examples]: https://github.com/iced-rs/iced/tree/0.5/examples
-/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.5/examples/bezier_tool
-/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.5/examples/custom_widget
-/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.5/examples/geometry
+/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples
+/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool
+/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget
+/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry
/// [`lyon`]: https://github.com/nical/lyon
-/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/wgpu
+/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu
pub trait Widget<Message, Renderer>
where
Renderer: crate::Renderer,
@@ -172,6 +175,7 @@ where
&self,
_state: &mut Tree,
_layout: Layout<'_>,
+ _renderer: &Renderer,
_operation: &mut dyn Operation<Message>,
) {
}
@@ -208,7 +212,7 @@ where
/// Returns the overlay of the [`Widget`], if there is any.
fn overlay<'a>(
- &'a self,
+ &'a mut self,
_state: &'a mut Tree,
_layout: Layout<'_>,
_renderer: &Renderer,
diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs
index fa5da24b..b4276317 100644
--- a/native/src/widget/button.rs
+++ b/native/src/widget/button.rs
@@ -169,12 +169,14 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
+ renderer,
operation,
);
});
@@ -260,12 +262,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- self.content.as_widget().overlay(
+ self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
@@ -393,7 +395,7 @@ where
y: bounds.y + styling.shadow_offset.y,
..bounds
},
- border_radius: styling.border_radius,
+ border_radius: styling.border_radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -404,7 +406,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: styling.border_radius,
+ border_radius: styling.border_radius.into(),
border_width: styling.border_width,
border_color: styling.border_color,
},
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index 77d639a9..bec5c448 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -236,7 +236,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: custom_style.border_radius,
+ border_radius: custom_style.border_radius.into(),
border_width: custom_style.border_width,
border_color: custom_style.border_color,
},
diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs
index a8b0f183..f2ef132a 100644
--- a/native/src/widget/column.rs
+++ b/native/src/widget/column.rs
@@ -147,6 +147,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
@@ -155,7 +156,9 @@ where
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
- child.as_widget().operate(state, layout, operation);
+ child
+ .as_widget()
+ .operate(state, layout, renderer, operation);
})
});
}
@@ -242,12 +245,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- overlay::from_children(&self.children, tree, layout, renderer)
+ overlay::from_children(&mut self.children, tree, layout, renderer)
}
}
diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs
index 9d3e4d9b..cdf1c859 100644
--- a/native/src/widget/container.rs
+++ b/native/src/widget/container.rs
@@ -169,12 +169,14 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
+ renderer,
operation,
);
});
@@ -248,12 +250,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- self.content.as_widget().overlay(
+ self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
@@ -321,7 +323,7 @@ pub fn draw_background<Renderer>(
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
border_width: appearance.border_width,
border_color: appearance.border_color,
},
diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs
index 3bce9e60..5b241f83 100644
--- a/native/src/widget/helpers.rs
+++ b/native/src/widget/helpers.rs
@@ -162,7 +162,7 @@ where
Renderer: crate::text::Renderer,
Renderer::Theme: widget::toggler::StyleSheet,
{
- widget::Toggler::new(is_checked, label, f)
+ widget::Toggler::new(label, is_checked, f)
}
/// Creates a new [`TextInput`].
@@ -198,6 +198,23 @@ where
widget::Slider::new(range, value, on_change)
}
+/// Creates a new [`VerticalSlider`].
+///
+/// [`VerticalSlider`]: widget::VerticalSlider
+pub fn vertical_slider<'a, T, Message, Renderer>(
+ range: std::ops::RangeInclusive<T>,
+ value: T,
+ on_change: impl Fn(T) -> Message + 'a,
+) -> widget::VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+ Message: Clone,
+ Renderer: crate::Renderer,
+ Renderer::Theme: widget::slider::StyleSheet,
+{
+ widget::VerticalSlider::new(range, value, on_change)
+}
+
/// Creates a new [`PickList`].
///
/// [`PickList`]: widget::PickList
@@ -285,6 +302,12 @@ where
///
/// [`Svg`]: widget::Svg
/// [`Handle`]: widget::svg::Handle
-pub fn svg(handle: impl Into<widget::svg::Handle>) -> widget::Svg {
+pub fn svg<Renderer>(
+ handle: impl Into<widget::svg::Handle>,
+) -> widget::Svg<Renderer>
+where
+ Renderer: crate::svg::Renderer,
+ Renderer::Theme: widget::svg::StyleSheet,
+{
widget::Svg::new(handle)
}
diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs
index 9c83287e..fdbd3216 100644
--- a/native/src/widget/image/viewer.rs
+++ b/native/src/widget/image/viewer.rs
@@ -170,8 +170,7 @@ where
} else {
state.scale / (1.0 + self.scale_step)
})
- .max(self.min_scale)
- .min(self.max_scale);
+ .clamp(self.min_scale, self.max_scale);
let image_size = image_size(
renderer,
@@ -251,16 +250,14 @@ where
let x = if bounds.width < image_size.width {
(state.starting_offset.x - delta.x)
- .min(hidden_width)
- .max(-hidden_width)
+ .clamp(-hidden_width, hidden_width)
} else {
0.0
};
let y = if bounds.height < image_size.height {
(state.starting_offset.y - delta.y)
- .min(hidden_height)
- .max(-hidden_height)
+ .clamp(-hidden_height, hidden_height)
} else {
0.0
};
@@ -374,8 +371,8 @@ impl State {
(image_size.height - bounds.height / 2.0).max(0.0).round();
Vector::new(
- self.current_offset.x.min(hidden_width).max(-hidden_width),
- self.current_offset.y.min(hidden_height).max(-hidden_height),
+ self.current_offset.x.clamp(-hidden_width, hidden_width),
+ self.current_offset.y.clamp(-hidden_height, hidden_height),
)
}
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs
index 8f9065b0..f8dbab74 100644
--- a/native/src/widget/pane_grid.rs
+++ b/native/src/widget/pane_grid.rs
@@ -6,7 +6,7 @@
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
-//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid
+//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid
mod axis;
mod configuration;
mod content;
@@ -294,6 +294,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
operation.container(None, &mut |operation| {
@@ -302,7 +303,7 @@ where
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|(((_pane, content), state), layout)| {
- content.operate(state, layout, operation);
+ content.operate(state, layout, renderer, operation);
})
});
}
@@ -444,13 +445,13 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'_, Message, Renderer>> {
self.contents
- .iter()
+ .iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.filter_map(|(((_, pane), tree), layout)| {
@@ -630,13 +631,13 @@ pub fn update<'a, Message, T: Draggable>(
let position =
cursor_position.y - bounds.y - rectangle.y;
- (position / rectangle.height).max(0.1).min(0.9)
+ (position / rectangle.height).clamp(0.1, 0.9)
}
Axis::Vertical => {
let position =
cursor_position.x - bounds.x - rectangle.x;
- (position / rectangle.width).max(0.1).min(0.9)
+ (position / rectangle.width).clamp(0.1, 0.9)
}
};
@@ -877,7 +878,7 @@ pub fn draw<Renderer, T>(
height: split_region.height,
},
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs
index 5e843cff..c9b0df07 100644
--- a/native/src/widget/pane_grid/content.rs
+++ b/native/src/widget/pane_grid/content.rs
@@ -187,6 +187,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
let body_layout = if let Some(title_bar) = &self.title_bar {
@@ -195,6 +196,7 @@ where
title_bar.operate(
&mut tree.children[1],
children.next().unwrap(),
+ renderer,
operation,
);
@@ -206,6 +208,7 @@ where
self.body.as_widget().operate(
&mut tree.children[0],
body_layout,
+ renderer,
operation,
);
}
@@ -305,12 +308,12 @@ where
}
pub(crate) fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- if let Some(title_bar) = self.title_bar.as_ref() {
+ if let Some(title_bar) = self.title_bar.as_mut() {
let mut children = layout.children();
let title_bar_layout = children.next()?;
@@ -321,14 +324,14 @@ where
match title_bar.overlay(title_bar_state, title_bar_layout, renderer)
{
Some(overlay) => Some(overlay),
- None => self.body.as_widget().overlay(
+ None => self.body.as_widget_mut().overlay(
body_state,
children.next()?,
renderer,
),
}
} else {
- self.body.as_widget().overlay(
+ self.body.as_widget_mut().overlay(
&mut tree.children[0],
layout,
renderer,
diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs
index 115f6270..ea0969aa 100644
--- a/native/src/widget/pane_grid/title_bar.rs
+++ b/native/src/widget/pane_grid/title_bar.rs
@@ -261,6 +261,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
let mut children = layout.children();
@@ -282,6 +283,7 @@ where
controls.as_widget().operate(
&mut tree.children[1],
controls_layout,
+ renderer,
operation,
)
};
@@ -290,6 +292,7 @@ where
self.content.as_widget().operate(
&mut tree.children[0],
title_layout,
+ renderer,
operation,
)
}
@@ -395,7 +398,7 @@ where
}
pub(crate) fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -415,13 +418,13 @@ where
let controls_state = states.next().unwrap();
content
- .as_widget()
+ .as_widget_mut()
.overlay(title_state, title_layout, renderer)
.or_else(move || {
- controls.as_ref().and_then(|controls| {
+ controls.as_mut().and_then(|controls| {
let controls_layout = children.next()?;
- controls.as_widget().overlay(
+ controls.as_widget_mut().overlay(
controls_state,
controls_layout,
renderer,
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index 43ae7ebb..c2853314 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -282,7 +282,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
_renderer: &Renderer,
@@ -600,7 +600,7 @@ pub fn draw<T, Renderer>(
bounds,
border_color: style.border_color,
border_width: style.border_width,
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
},
style.background,
);
diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs
index b053d959..7d5d5be5 100644
--- a/native/src/widget/progress_bar.rs
+++ b/native/src/widget/progress_bar.rs
@@ -47,7 +47,7 @@ where
/// * the current value of the [`ProgressBar`]
pub fn new(range: RangeInclusive<f32>, value: f32) -> Self {
ProgressBar {
- value: value.max(*range.start()).min(*range.end()),
+ value: value.clamp(*range.start(), *range.end()),
range,
width: Length::Fill,
height: None,
@@ -129,7 +129,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle { ..bounds },
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -143,7 +143,7 @@ where
width: active_progress_width,
..bounds
},
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs
index 743689c7..b95ccc5b 100644
--- a/native/src/widget/radio.rs
+++ b/native/src/widget/radio.rs
@@ -245,7 +245,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: size / 2.0,
+ border_radius: (size / 2.0).into(),
border_width: custom_style.border_width,
border_color: custom_style.border_color,
},
@@ -261,7 +261,7 @@ where
width: bounds.width - dot_size,
height: bounds.height - dot_size,
},
- border_radius: dot_size / 2.0,
+ border_radius: (dot_size / 2.0).into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs
index eda7c2d3..108e98e4 100644
--- a/native/src/widget/row.rs
+++ b/native/src/widget/row.rs
@@ -134,6 +134,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
@@ -142,7 +143,9 @@ where
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
- child.as_widget().operate(state, layout, operation);
+ child
+ .as_widget()
+ .operate(state, layout, renderer, operation);
})
});
}
@@ -229,12 +232,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- overlay::from_children(&self.children, tree, layout, renderer)
+ overlay::from_children(&mut self.children, tree, layout, renderer)
}
}
diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs
index e44d8d99..2dc7b6f0 100644
--- a/native/src/widget/rule.rs
+++ b/native/src/widget/rule.rs
@@ -123,7 +123,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: style.radius,
+ border_radius: style.radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 32ec6eb3..20780f89 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -164,6 +164,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
let state = tree.state.downcast_mut::<State>();
@@ -174,6 +175,7 @@ where
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
+ renderer,
operation,
);
});
@@ -276,13 +278,13 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
self.content
- .as_widget()
+ .as_widget_mut()
.overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
@@ -704,7 +706,7 @@ pub fn draw<Renderer>(
renderer.fill_quad(
renderer::Quad {
bounds: scrollbar.bounds,
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
border_width: style.border_width,
border_color: style.border_color,
},
@@ -714,14 +716,13 @@ pub fn draw<Renderer>(
);
}
- if is_mouse_over
- || state.is_scroller_grabbed()
- || is_scrollbar_visible
+ if (is_mouse_over || state.is_scroller_grabbed())
+ && is_scrollbar_visible
{
renderer.fill_quad(
renderer::Quad {
bounds: scrollbar.scroller.bounds,
- border_radius: style.scroller.border_radius,
+ border_radius: style.scroller.border_radius.into(),
border_width: style.scroller.border_width,
border_color: style.scroller.border_color,
},
@@ -882,8 +883,7 @@ impl State {
self.offset = Offset::Absolute(
(self.offset.absolute(bounds, content_bounds) - delta_y)
- .max(0.0)
- .min((content_bounds.height - bounds.height) as f32),
+ .clamp(0.0, content_bounds.height - bounds.height),
);
}
@@ -906,7 +906,7 @@ impl State {
/// `0` represents scrollbar at the top, while `1` represents scrollbar at
/// the bottom.
pub fn snap_to(&mut self, percentage: f32) {
- self.offset = Offset::Relative(percentage.max(0.0).min(1.0));
+ self.offset = Offset::Relative(percentage.clamp(0.0, 1.0));
}
/// Unsnaps the current scroll position, if snapped, given the bounds of the
diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs
index fd9160a4..87030a4d 100644
--- a/native/src/widget/slider.rs
+++ b/native/src/widget/slider.rs
@@ -380,7 +380,7 @@ pub fn draw<T, R>(
width: bounds.width,
height: 2.0,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -395,7 +395,7 @@ pub fn draw<T, R>(
width: bounds.width,
height: 2.0,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -435,7 +435,7 @@ pub fn draw<T, R>(
width: handle_width,
height: handle_height,
},
- border_radius: handle_border_radius,
+ border_radius: handle_border_radius.into(),
border_width: style.handle.border_width,
border_color: style.handle.border_color,
},
diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs
index 1015ed0a..f83f5acf 100644
--- a/native/src/widget/svg.rs
+++ b/native/src/widget/svg.rs
@@ -9,6 +9,7 @@ use crate::{
use std::path::PathBuf;
+pub use iced_style::svg::{Appearance, StyleSheet};
pub use svg::Handle;
/// A vector graphics image.
@@ -17,15 +18,24 @@ pub use svg::Handle;
///
/// [`Svg`] images can have a considerable rendering cost when resized,
/// specially when they are complex.
-#[derive(Debug, Clone)]
-pub struct Svg {
+#[allow(missing_debug_implementations)]
+pub struct Svg<Renderer>
+where
+ Renderer: svg::Renderer,
+ Renderer::Theme: StyleSheet,
+{
handle: Handle,
width: Length,
height: Length,
content_fit: ContentFit,
+ style: <Renderer::Theme as StyleSheet>::Style,
}
-impl Svg {
+impl<Renderer> Svg<Renderer>
+where
+ Renderer: svg::Renderer,
+ Renderer::Theme: StyleSheet,
+{
/// Creates a new [`Svg`] from the given [`Handle`].
pub fn new(handle: impl Into<Handle>) -> Self {
Svg {
@@ -33,22 +43,26 @@ impl Svg {
width: Length::Fill,
height: Length::Shrink,
content_fit: ContentFit::Contain,
+ style: Default::default(),
}
}
/// Creates a new [`Svg`] that will display the contents of the file at the
/// provided path.
+ #[must_use]
pub fn from_path(path: impl Into<PathBuf>) -> Self {
Self::new(Handle::from_path(path))
}
/// Sets the width of the [`Svg`].
+ #[must_use]
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Svg`].
+ #[must_use]
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
@@ -57,17 +71,29 @@ impl Svg {
/// Sets the [`ContentFit`] of the [`Svg`].
///
/// Defaults to [`ContentFit::Contain`]
+ #[must_use]
pub fn content_fit(self, content_fit: ContentFit) -> Self {
Self {
content_fit,
..self
}
}
+
+ /// Sets the style variant of this [`Svg`].
+ #[must_use]
+ pub fn style(
+ mut self,
+ style: <Renderer::Theme as StyleSheet>::Style,
+ ) -> Self {
+ self.style = style;
+ self
+ }
}
-impl<Message, Renderer> Widget<Message, Renderer> for Svg
+impl<Message, Renderer> Widget<Message, Renderer> for Svg<Renderer>
where
Renderer: svg::Renderer,
+ Renderer::Theme: iced_style::svg::StyleSheet,
{
fn width(&self) -> Length {
self.width
@@ -114,7 +140,7 @@ where
&self,
_state: &Tree,
renderer: &mut Renderer,
- _theme: &Renderer::Theme,
+ theme: &Renderer::Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor_position: Point,
@@ -138,7 +164,13 @@ where
..bounds
};
- renderer.draw(self.handle.clone(), drawing_bounds + offset)
+ let appearance = theme.appearance(&self.style);
+
+ renderer.draw(
+ self.handle.clone(),
+ appearance.color,
+ drawing_bounds + offset,
+ );
};
if adjusted_fit.width > bounds.width
@@ -146,16 +178,18 @@ where
{
renderer.with_layer(bounds, render);
} else {
- render(renderer)
+ render(renderer);
}
}
}
-impl<'a, Message, Renderer> From<Svg> for Element<'a, Message, Renderer>
+impl<'a, Message, Renderer> From<Svg<Renderer>>
+ for Element<'a, Message, Renderer>
where
- Renderer: svg::Renderer,
+ Renderer: svg::Renderer + 'a,
+ Renderer::Theme: iced_style::svg::StyleSheet,
{
- fn from(icon: Svg) -> Element<'a, Message, Renderer> {
+ fn from(icon: Svg<Renderer>) -> Element<'a, Message, Renderer> {
Element::new(icon)
}
}
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 14e7e1b7..8b4514e3 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -228,6 +228,7 @@ where
&self,
tree: &mut Tree,
_layout: Layout<'_>,
+ _renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
let state = tree.state.downcast_mut::<State>();
@@ -453,9 +454,17 @@ where
)
} else {
None
- };
+ }
+ .unwrap_or(0);
- state.cursor.move_to(position.unwrap_or(0));
+ if state.keyboard_modifiers.shift() {
+ state.cursor.select_range(
+ state.cursor.start(value),
+ position,
+ );
+ } else {
+ state.cursor.move_to(position);
+ }
state.is_dragging = true;
}
click::Kind::Double => {
@@ -801,7 +810,7 @@ pub fn draw<Renderer>(
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
border_width: appearance.border_width,
border_color: appearance.border_color,
},
@@ -833,7 +842,7 @@ pub fn draw<Renderer>(
width: 1.0,
height: text_bounds.height,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -877,7 +886,7 @@ pub fn draw<Renderer>(
width,
height: text_bounds.height,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs
index 99a56ea8..f0a944a3 100644
--- a/native/src/widget/toggler.rs
+++ b/native/src/widget/toggler.rs
@@ -24,9 +24,9 @@ pub use iced_style::toggler::{Appearance, StyleSheet};
/// TogglerToggled(bool),
/// }
///
-/// let is_active = true;
+/// let is_toggled = true;
///
-/// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b));
+/// Toggler::new(String::from("Toggle me!"), is_toggled, |b| Message::TogglerToggled(b));
/// ```
#[allow(missing_debug_implementations)]
pub struct Toggler<'a, Message, Renderer>
@@ -34,7 +34,7 @@ where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
- is_active: bool,
+ is_toggled: bool,
on_toggle: Box<dyn Fn(bool) -> Message + 'a>,
label: Option<String>,
width: Length,
@@ -63,15 +63,15 @@ where
/// will receive the new state of the [`Toggler`] and must produce a
/// `Message`.
pub fn new<F>(
- is_active: bool,
label: impl Into<Option<String>>,
+ is_toggled: bool,
f: F,
) -> Self
where
F: 'a + Fn(bool) -> Message,
{
Toggler {
- is_active,
+ is_toggled,
on_toggle: Box::new(f),
label: label.into(),
width: Length::Fill,
@@ -193,7 +193,7 @@ where
let mouse_over = layout.bounds().contains(cursor_position);
if mouse_over {
- shell.publish((self.on_toggle)(!self.is_active));
+ shell.publish((self.on_toggle)(!self.is_toggled));
event::Status::Captured
} else {
@@ -260,13 +260,13 @@ where
let is_mouse_over = bounds.contains(cursor_position);
let style = if is_mouse_over {
- theme.hovered(&self.style, self.is_active)
+ theme.hovered(&self.style, self.is_toggled)
} else {
- theme.active(&self.style, self.is_active)
+ theme.active(&self.style, self.is_toggled)
};
- let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO;
- let space = SPACE_RATIO * bounds.height as f32;
+ let border_radius = bounds.height / BORDER_RADIUS_RATIO;
+ let space = SPACE_RATIO * bounds.height;
let toggler_background_bounds = Rectangle {
x: bounds.x + space,
@@ -278,7 +278,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: toggler_background_bounds,
- border_radius,
+ border_radius: border_radius.into(),
border_width: 1.0,
border_color: style
.background_border
@@ -289,7 +289,7 @@ where
let toggler_foreground_bounds = Rectangle {
x: bounds.x
- + if self.is_active {
+ + if self.is_toggled {
bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
} else {
2.0 * space
@@ -302,7 +302,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: toggler_foreground_bounds,
- border_radius,
+ border_radius: border_radius.into(),
border_width: 1.0,
border_color: style
.foreground_border
diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs
index 9347a886..084dc269 100644
--- a/native/src/widget/tooltip.rs
+++ b/native/src/widget/tooltip.rs
@@ -221,12 +221,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- self.content.as_widget().overlay(
+ self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout,
renderer,
diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs
new file mode 100644
index 00000000..28e8405c
--- /dev/null
+++ b/native/src/widget/vertical_slider.rs
@@ -0,0 +1,470 @@
+//! Display an interactive selector of a single value from a range of values.
+//!
+//! A [`VerticalSlider`] has some local [`State`].
+use std::ops::RangeInclusive;
+
+pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet};
+
+use crate::event::{self, Event};
+use crate::widget::tree::{self, Tree};
+use crate::{
+ layout, mouse, renderer, touch, Background, Clipboard, Color, Element,
+ Layout, Length, Point, Rectangle, Shell, Size, Widget,
+};
+
+/// An vertical bar and a handle that selects a single value from a range of
+/// values.
+///
+/// A [`VerticalSlider`] will try to fill the vertical space of its container.
+///
+/// The [`VerticalSlider`] range of numeric values is generic and its step size defaults
+/// to 1 unit.
+///
+/// # Example
+/// ```
+/// # use iced_native::widget::vertical_slider;
+/// # use iced_native::renderer::Null;
+/// #
+/// # type VerticalSlider<'a, T, Message> = vertical_slider::VerticalSlider<'a, T, Message, Null>;
+/// #
+/// #[derive(Clone)]
+/// pub enum Message {
+/// SliderChanged(f32),
+/// }
+///
+/// let value = 50.0;
+///
+/// VerticalSlider::new(0.0..=100.0, value, Message::SliderChanged);
+/// ```
+#[allow(missing_debug_implementations)]
+pub struct VerticalSlider<'a, T, Message, Renderer>
+where
+ Renderer: crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ range: RangeInclusive<T>,
+ step: T,
+ value: T,
+ on_change: Box<dyn Fn(T) -> Message + 'a>,
+ on_release: Option<Message>,
+ width: u16,
+ height: Length,
+ style: <Renderer::Theme as StyleSheet>::Style,
+}
+
+impl<'a, T, Message, Renderer> VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+ Message: Clone,
+ Renderer: crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ /// The default width of a [`VerticalSlider`].
+ pub const DEFAULT_WIDTH: u16 = 22;
+
+ /// Creates a new [`VerticalSlider`].
+ ///
+ /// It expects:
+ /// * an inclusive range of possible values
+ /// * the current value of the [`VerticalSlider`]
+ /// * a function that will be called when the [`VerticalSlider`] is dragged.
+ /// It receives the new value of the [`VerticalSlider`] and must produce a
+ /// `Message`.
+ pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self
+ where
+ F: 'a + Fn(T) -> Message,
+ {
+ let value = if value >= *range.start() {
+ value
+ } else {
+ *range.start()
+ };
+
+ let value = if value <= *range.end() {
+ value
+ } else {
+ *range.end()
+ };
+
+ VerticalSlider {
+ value,
+ range,
+ step: T::from(1),
+ on_change: Box::new(on_change),
+ on_release: None,
+ width: Self::DEFAULT_WIDTH,
+ height: Length::Fill,
+ style: Default::default(),
+ }
+ }
+
+ /// Sets the release message of the [`VerticalSlider`].
+ /// This is called when the mouse is released from the slider.
+ ///
+ /// Typically, the user's interaction with the slider is finished when this message is produced.
+ /// This is useful if you need to spawn a long-running task from the slider's result, where
+ /// the default on_change message could create too many events.
+ pub fn on_release(mut self, on_release: Message) -> Self {
+ self.on_release = Some(on_release);
+ self
+ }
+
+ /// Sets the width of the [`VerticalSlider`].
+ pub fn width(mut self, width: u16) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`VerticalSlider`].
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the style of the [`VerticalSlider`].
+ pub fn style(
+ mut self,
+ style: impl Into<<Renderer::Theme as StyleSheet>::Style>,
+ ) -> Self {
+ self.style = style.into();
+ self
+ }
+
+ /// Sets the step size of the [`VerticalSlider`].
+ pub fn step(mut self, step: T) -> Self {
+ self.step = step;
+ self
+ }
+}
+
+impl<'a, T, Message, Renderer> Widget<Message, Renderer>
+ for VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + Into<f64> + num_traits::FromPrimitive,
+ Message: Clone,
+ Renderer: crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ fn tag(&self) -> tree::Tag {
+ tree::Tag::of::<State>()
+ }
+
+ fn state(&self) -> tree::State {
+ tree::State::new(State::new())
+ }
+
+ fn width(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn height(&self) -> Length {
+ self.height
+ }
+
+ fn layout(
+ &self,
+ _renderer: &Renderer,
+ limits: &layout::Limits,
+ ) -> layout::Node {
+ let limits =
+ limits.width(Length::Units(self.width)).height(self.height);
+
+ let size = limits.resolve(Size::ZERO);
+
+ layout::Node::new(size)
+ }
+
+ fn on_event(
+ &mut self,
+ tree: &mut Tree,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ _renderer: &Renderer,
+ _clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ update(
+ event,
+ layout,
+ cursor_position,
+ shell,
+ tree.state.downcast_mut::<State>(),
+ &mut self.value,
+ &self.range,
+ self.step,
+ self.on_change.as_ref(),
+ &self.on_release,
+ )
+ }
+
+ fn draw(
+ &self,
+ tree: &Tree,
+ renderer: &mut Renderer,
+ theme: &Renderer::Theme,
+ _style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ _viewport: &Rectangle,
+ ) {
+ draw(
+ renderer,
+ layout,
+ cursor_position,
+ tree.state.downcast_ref::<State>(),
+ self.value,
+ &self.range,
+ theme,
+ &self.style,
+ )
+ }
+
+ fn mouse_interaction(
+ &self,
+ tree: &Tree,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ _viewport: &Rectangle,
+ _renderer: &Renderer,
+ ) -> mouse::Interaction {
+ mouse_interaction(
+ layout,
+ cursor_position,
+ tree.state.downcast_ref::<State>(),
+ )
+ }
+}
+
+impl<'a, T, Message, Renderer> From<VerticalSlider<'a, T, Message, Renderer>>
+ for Element<'a, Message, Renderer>
+where
+ T: 'a + Copy + Into<f64> + num_traits::FromPrimitive,
+ Message: 'a + Clone,
+ Renderer: 'a + crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ fn from(
+ slider: VerticalSlider<'a, T, Message, Renderer>,
+ ) -> Element<'a, Message, Renderer> {
+ Element::new(slider)
+ }
+}
+
+/// Processes an [`Event`] and updates the [`State`] of a [`VerticalSlider`]
+/// accordingly.
+pub fn update<Message, T>(
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ shell: &mut Shell<'_, Message>,
+ state: &mut State,
+ value: &mut T,
+ range: &RangeInclusive<T>,
+ step: T,
+ on_change: &dyn Fn(T) -> Message,
+ on_release: &Option<Message>,
+) -> event::Status
+where
+ T: Copy + Into<f64> + num_traits::FromPrimitive,
+ Message: Clone,
+{
+ let is_dragging = state.is_dragging;
+
+ let mut change = || {
+ let bounds = layout.bounds();
+ let new_value = if cursor_position.y >= bounds.y + bounds.height {
+ *range.start()
+ } else if cursor_position.y <= bounds.y {
+ *range.end()
+ } else {
+ let step = step.into();
+ let start = (*range.start()).into();
+ let end = (*range.end()).into();
+
+ let percent = 1.0
+ - f64::from(cursor_position.y - bounds.y)
+ / f64::from(bounds.height);
+
+ let steps = (percent * (end - start) / step).round();
+ let value = steps * step + start;
+
+ if let Some(value) = T::from_f64(value) {
+ value
+ } else {
+ return;
+ }
+ };
+
+ if ((*value).into() - new_value.into()).abs() > f64::EPSILON {
+ shell.publish((on_change)(new_value));
+
+ *value = new_value;
+ }
+ };
+
+ match event {
+ Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerPressed { .. }) => {
+ if layout.bounds().contains(cursor_position) {
+ change();
+ state.is_dragging = true;
+
+ return event::Status::Captured;
+ }
+ }
+ Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerLifted { .. })
+ | Event::Touch(touch::Event::FingerLost { .. }) => {
+ if is_dragging {
+ if let Some(on_release) = on_release.clone() {
+ shell.publish(on_release);
+ }
+ state.is_dragging = false;
+
+ return event::Status::Captured;
+ }
+ }
+ Event::Mouse(mouse::Event::CursorMoved { .. })
+ | Event::Touch(touch::Event::FingerMoved { .. }) => {
+ if is_dragging {
+ change();
+
+ return event::Status::Captured;
+ }
+ }
+ _ => {}
+ }
+
+ event::Status::Ignored
+}
+
+/// Draws a [`VerticalSlider`].
+pub fn draw<T, R>(
+ renderer: &mut R,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ state: &State,
+ value: T,
+ range: &RangeInclusive<T>,
+ style_sheet: &dyn StyleSheet<Style = <R::Theme as StyleSheet>::Style>,
+ style: &<R::Theme as StyleSheet>::Style,
+) where
+ T: Into<f64> + Copy,
+ R: crate::Renderer,
+ R::Theme: StyleSheet,
+{
+ let bounds = layout.bounds();
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ let style = if state.is_dragging {
+ style_sheet.dragging(style)
+ } else if is_mouse_over {
+ style_sheet.hovered(style)
+ } else {
+ style_sheet.active(style)
+ };
+
+ let rail_x = bounds.x + (bounds.width / 2.0).round();
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: rail_x - 1.0,
+ y: bounds.y,
+ width: 2.0,
+ height: bounds.height,
+ },
+ border_radius: 0.0.into(),
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ style.rail_colors.0,
+ );
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: rail_x + 1.0,
+ y: bounds.y,
+ width: 2.0,
+ height: bounds.height,
+ },
+ border_radius: 0.0.into(),
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ Background::Color(style.rail_colors.1),
+ );
+
+ let (handle_width, handle_height, handle_border_radius) = match style
+ .handle
+ .shape
+ {
+ HandleShape::Circle { radius } => (radius * 2.0, radius * 2.0, radius),
+ HandleShape::Rectangle {
+ width,
+ border_radius,
+ } => (f32::from(width), bounds.width, border_radius),
+ };
+
+ let value = value.into() as f32;
+ let (range_start, range_end) = {
+ let (start, end) = range.clone().into_inner();
+
+ (start.into() as f32, end.into() as f32)
+ };
+
+ let handle_offset = if range_start >= range_end {
+ 0.0
+ } else {
+ bounds.height * (value - range_end) / (range_start - range_end)
+ - handle_width / 2.0
+ };
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: rail_x - (handle_height / 2.0),
+ y: bounds.y + handle_offset.round(),
+ width: handle_height,
+ height: handle_width,
+ },
+ border_radius: handle_border_radius.into(),
+ border_width: style.handle.border_width,
+ border_color: style.handle.border_color,
+ },
+ style.handle.color,
+ );
+}
+
+/// Computes the current [`mouse::Interaction`] of a [`VerticalSlider`].
+pub fn mouse_interaction(
+ layout: Layout<'_>,
+ cursor_position: Point,
+ state: &State,
+) -> mouse::Interaction {
+ let bounds = layout.bounds();
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ if state.is_dragging {
+ mouse::Interaction::Grabbing
+ } else if is_mouse_over {
+ mouse::Interaction::Grab
+ } else {
+ mouse::Interaction::default()
+ }
+}
+
+/// The local state of a [`VerticalSlider`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub struct State {
+ is_dragging: bool,
+}
+
+impl State {
+ /// Creates a new [`State`].
+ pub fn new() -> State {
+ State::default()
+ }
+}
diff --git a/native/src/window.rs b/native/src/window.rs
index f910b8f2..1b97e655 100644
--- a/native/src/window.rs
+++ b/native/src/window.rs
@@ -2,7 +2,9 @@
mod action;
mod event;
mod mode;
+mod user_attention;
pub use action::Action;
pub use event::Event;
pub use mode::Mode;
+pub use user_attention::UserAttention;
diff --git a/native/src/window/action.rs b/native/src/window/action.rs
index 009dcc27..f0fe845d 100644
--- a/native/src/window/action.rs
+++ b/native/src/window/action.rs
@@ -1,10 +1,12 @@
-use crate::window::Mode;
+use crate::window::{Mode, UserAttention};
use iced_futures::MaybeSend;
use std::fmt;
/// An operation to be performed on some window.
pub enum Action<T> {
+ /// Closes the current window and exits the application.
+ Close,
/// Moves the window with the left mouse button until the button is
/// released.
///
@@ -33,10 +35,29 @@ pub enum Action<T> {
},
/// Set the [`Mode`] of the window.
SetMode(Mode),
- /// Sets the window to maximized or back
- ToggleMaximize,
/// Fetch the current [`Mode`] of the window.
FetchMode(Box<dyn FnOnce(Mode) -> T + 'static>),
+ /// Sets the window to maximized or back
+ ToggleMaximize,
+ /// Toggles whether window has decorations
+ /// ## Platform-specific
+ /// - **X11:** Not implemented.
+ /// - **Web:** Unsupported.
+ ToggleDecorations,
+ /// Requests user attention to the window, this has no effect if the application
+ /// is already focused. How requesting for user attention manifests is platform dependent,
+ /// see [`UserAttentionType`] for details.
+ ///
+ /// Providing `None` will unset the request for user attention. Unsetting the request for
+ /// user attention might not be done automatically by the WM when the window receives input.
+ ///
+ /// ## Platform-specific
+ ///
+ /// - **iOS / Android / Web:** Unsupported.
+ /// - **macOS:** `None` has no effect.
+ /// - **X11:** Requests for user attention must be manually cleared.
+ /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
+ RequestUserAttention(Option<UserAttention>),
}
impl<T> Action<T> {
@@ -49,14 +70,19 @@ impl<T> Action<T> {
T: 'static,
{
match self {
+ Self::Close => Action::Close,
Self::Drag => Action::Drag,
Self::Resize { width, height } => Action::Resize { width, height },
Self::Maximize(bool) => Action::Maximize(bool),
Self::Minimize(bool) => Action::Minimize(bool),
Self::Move { x, y } => Action::Move { x, y },
Self::SetMode(mode) => Action::SetMode(mode),
- Self::ToggleMaximize => Action::ToggleMaximize,
Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))),
+ Self::ToggleMaximize => Action::ToggleMaximize,
+ Self::ToggleDecorations => Action::ToggleDecorations,
+ Self::RequestUserAttention(attention_type) => {
+ Action::RequestUserAttention(attention_type)
+ }
}
}
}
@@ -64,6 +90,7 @@ impl<T> Action<T> {
impl<T> fmt::Debug for Action<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
+ Self::Close => write!(f, "Action::Close"),
Self::Drag => write!(f, "Action::Drag"),
Self::Resize { width, height } => write!(
f,
@@ -76,8 +103,12 @@ impl<T> fmt::Debug for Action<T> {
write!(f, "Action::Move {{ x: {}, y: {} }}", x, y)
}
Self::SetMode(mode) => write!(f, "Action::SetMode({:?})", mode),
- Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"),
Self::FetchMode(_) => write!(f, "Action::FetchMode"),
+ Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"),
+ Self::ToggleDecorations => write!(f, "Action::ToggleDecorations"),
+ Self::RequestUserAttention(_) => {
+ write!(f, "Action::RequestUserAttention")
+ }
}
}
}
diff --git a/native/src/window/user_attention.rs b/native/src/window/user_attention.rs
new file mode 100644
index 00000000..b03dfeef
--- /dev/null
+++ b/native/src/window/user_attention.rs
@@ -0,0 +1,21 @@
+/// The type of user attention to request.
+///
+/// ## Platform-specific
+///
+/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between [`Critical`] and [`Informational`].
+///
+/// [`Critical`]: Self::Critical
+/// [`Informational`]: Self::Informational
+#[derive(Debug, Clone, Copy)]
+pub enum UserAttention {
+ /// ## Platform-specific
+ ///
+ /// - **macOS:** Bounces the dock icon until the application is in focus.
+ /// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
+ Critical,
+ /// ## Platform-specific
+ ///
+ /// - **macOS:** Bounces the dock icon once.
+ /// - **Windows:** Flashes the taskbar button until the application is in focus.
+ Informational,
+}
diff --git a/src/application.rs b/src/application.rs
index 0f514f66..f2b7c955 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -39,15 +39,15 @@ pub use iced_native::application::{Appearance, StyleSheet};
/// to listen to time.
/// - [`todos`], a todos tracker inspired by [TodoMVC].
///
-/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.5/examples
-/// [`clock`]: https://github.com/iced-rs/iced/tree/0.5/examples/clock
-/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.5/examples/download_progress
-/// [`events`]: https://github.com/iced-rs/iced/tree/0.5/examples/events
-/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.5/examples/game_of_life
-/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.5/examples/pokedex
-/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.5/examples/solar_system
-/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.5/examples/stopwatch
-/// [`todos`]: https://github.com/iced-rs/iced/tree/0.5/examples/todos
+/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.6/examples
+/// [`clock`]: https://github.com/iced-rs/iced/tree/0.6/examples/clock
+/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.6/examples/download_progress
+/// [`events`]: https://github.com/iced-rs/iced/tree/0.6/examples/events
+/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.6/examples/game_of_life
+/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.6/examples/pokedex
+/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.6/examples/solar_system
+/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.6/examples/stopwatch
+/// [`todos`]: https://github.com/iced-rs/iced/tree/0.6/examples/todos
/// [`Sandbox`]: crate::Sandbox
/// [`Canvas`]: crate::widget::Canvas
/// [PokéAPI]: https://pokeapi.co/
@@ -270,8 +270,4 @@ where
fn scale_factor(&self) -> f64 {
self.0.scale_factor()
}
-
- fn should_exit(&self) -> bool {
- self.0.should_exit()
- }
}
diff --git a/src/lib.rs b/src/lib.rs
index 4d0484a7..00176827 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,11 +26,11 @@
//! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md
//! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/0.4/master/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
-//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.5/wgpu
-//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.5/winit
+//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.6/wgpu
+//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.6/winit
//! [`dodrio`]: https://github.com/fitzgen/dodrio
//! [web runtime]: https://github.com/iced-rs/iced_web
-//! [examples]: https://github.com/iced-rs/iced/tree/0.5/examples
+//! [examples]: https://github.com/iced-rs/iced/tree/0.6/examples
//! [repository]: https://github.com/iced-rs/iced
//!
//! # Overview
diff --git a/src/sandbox.rs b/src/sandbox.rs
index dc69b6bb..47bad831 100644
--- a/src/sandbox.rs
+++ b/src/sandbox.rs
@@ -34,19 +34,19 @@ use crate::{Application, Command, Element, Error, Settings, Subscription};
/// - [`tour`], a simple UI tour that can run both on native platforms and the
/// web!
///
-/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.5/examples
-/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.5/examples/bezier_tool
-/// [`counter`]: https://github.com/iced-rs/iced/tree/0.5/examples/counter
-/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.5/examples/custom_widget
-/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.5/examples/geometry
-/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid
-/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.5/examples/progress_bar
-/// [`styling`]: https://github.com/iced-rs/iced/tree/0.5/examples/styling
-/// [`svg`]: https://github.com/iced-rs/iced/tree/0.5/examples/svg
-/// [`tour`]: https://github.com/iced-rs/iced/tree/0.5/examples/tour
+/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.6/examples
+/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool
+/// [`counter`]: https://github.com/iced-rs/iced/tree/0.6/examples/counter
+/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget
+/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry
+/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid
+/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.6/examples/progress_bar
+/// [`styling`]: https://github.com/iced-rs/iced/tree/0.6/examples/styling
+/// [`svg`]: https://github.com/iced-rs/iced/tree/0.6/examples/svg
+/// [`tour`]: https://github.com/iced-rs/iced/tree/0.6/examples/tour
/// [`Canvas widget`]: crate::widget::Canvas
/// [the overview]: index.html#overview
-/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/wgpu
+/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu
/// [`Svg` widget]: crate::widget::Svg
/// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
///
diff --git a/src/widget.rs b/src/widget.rs
index acc9da9a..d2d4a1b8 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -56,7 +56,7 @@ pub mod pane_grid {
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
- //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid
+ //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid
pub use iced_native::widget::pane_grid::{
Axis, Configuration, Direction, DragEvent, Line, Node, Pane,
ResizeEvent, Split, State, StyleSheet,
@@ -141,6 +141,7 @@ pub mod tooltip {
pub use iced_native::widget::progress_bar;
pub use iced_native::widget::rule;
pub use iced_native::widget::slider;
+pub use iced_native::widget::vertical_slider;
pub use iced_native::widget::Space;
pub use button::Button;
@@ -157,6 +158,7 @@ pub use text::Text;
pub use text_input::TextInput;
pub use toggler::Toggler;
pub use tooltip::Tooltip;
+pub use vertical_slider::VerticalSlider;
#[cfg(feature = "canvas")]
#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
@@ -194,7 +196,7 @@ pub use iced_graphics::widget::qr_code;
pub mod svg {
//! Display vector graphics in your application.
pub use iced_native::svg::Handle;
- pub use iced_native::widget::Svg;
+ pub use iced_native::widget::svg::{Appearance, StyleSheet, Svg};
}
#[cfg(feature = "canvas")]
diff --git a/style/Cargo.toml b/style/Cargo.toml
index 559b4462..9f7d904a 100644
--- a/style/Cargo.toml
+++ b/style/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_style"
-version = "0.5.0"
+version = "0.5.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "The default set of styles of Iced"
diff --git a/style/src/lib.rs b/style/src/lib.rs
index 3242602c..59eb1eb8 100644
--- a/style/src/lib.rs
+++ b/style/src/lib.rs
@@ -32,6 +32,7 @@ pub mod radio;
pub mod rule;
pub mod scrollable;
pub mod slider;
+pub mod svg;
pub mod text;
pub mod text_input;
pub mod theme;
diff --git a/style/src/svg.rs b/style/src/svg.rs
new file mode 100644
index 00000000..9378c1a7
--- /dev/null
+++ b/style/src/svg.rs
@@ -0,0 +1,23 @@
+//! Change the appearance of a svg.
+
+use iced_core::Color;
+
+/// The appearance of an SVG.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Appearance {
+ /// The [`Color`] filter of an SVG.
+ ///
+ /// Useful for coloring a symbolic icon.
+ ///
+ /// `None` keeps the original color.
+ pub color: Option<Color>,
+}
+
+/// The stylesheet of a svg.
+pub trait StyleSheet {
+ /// The supported style of the [`StyleSheet`].
+ type Style: Default;
+
+ /// Produces the [`Appearance`] of the svg.
+ fn appearance(&self, style: &Self::Style) -> Appearance;
+}
diff --git a/style/src/theme.rs b/style/src/theme.rs
index bd0ae28e..a766b279 100644
--- a/style/src/theme.rs
+++ b/style/src/theme.rs
@@ -16,6 +16,7 @@ use crate::radio;
use crate::rule;
use crate::scrollable;
use crate::slider;
+use crate::svg;
use crate::text;
use crate::text_input;
use crate::toggler;
@@ -559,7 +560,7 @@ impl pick_list::StyleSheet for Theme {
border_color: palette.primary.strong.color,
}
}
- PickList::Custom(custom, _) => custom.active(self),
+ PickList::Custom(custom, _) => custom.hovered(self),
}
}
}
@@ -823,6 +824,44 @@ impl rule::StyleSheet for fn(&Theme) -> rule::Appearance {
}
}
+/**
+ * Svg
+ */
+#[derive(Default)]
+pub enum Svg {
+ /// No filtering to the rendered SVG.
+ #[default]
+ Default,
+ /// A custom style.
+ Custom(Box<dyn svg::StyleSheet<Style = Theme>>),
+}
+
+impl Svg {
+ /// Creates a custom [`Svg`] style.
+ pub fn custom_fn(f: fn(&Theme) -> svg::Appearance) -> Self {
+ Self::Custom(Box::new(f))
+ }
+}
+
+impl svg::StyleSheet for Theme {
+ type Style = Svg;
+
+ fn appearance(&self, style: &Self::Style) -> svg::Appearance {
+ match style {
+ Svg::Default => Default::default(),
+ Svg::Custom(custom) => custom.appearance(self),
+ }
+ }
+}
+
+impl svg::StyleSheet for fn(&Theme) -> svg::Appearance {
+ type Style = Theme;
+
+ fn appearance(&self, style: &Self::Style) -> svg::Appearance {
+ (self)(style)
+ }
+}
+
/// The style of a scrollable.
#[derive(Default)]
pub enum Scrollable {
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index b1fb0dd1..a40d9967 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_wgpu"
-version = "0.6.1"
+version = "0.7.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A wgpu renderer for Iced"
@@ -42,11 +42,11 @@ version = "1.9"
features = ["derive"]
[dependencies.iced_native]
-version = "0.6"
+version = "0.7"
path = "../native"
[dependencies.iced_graphics]
-version = "0.4"
+version = "0.5"
path = "../graphics"
features = ["font-fallback", "font-icons"]
diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs
index 18be03dd..88289b98 100644
--- a/wgpu/src/buffer/dynamic.rs
+++ b/wgpu/src/buffer/dynamic.rs
@@ -80,7 +80,7 @@ impl<T: ShaderType + WriteInto> Buffer<T> {
pub fn push(&mut self, value: &T) {
//this write operation on the cpu buffer will adjust for uniform alignment requirements
let offset = self.cpu.write(value);
- self.offsets.push(offset as u32);
+ self.offsets.push(offset);
}
/// Resize buffer contents if necessary. This will re-create the GPU buffer if current size is
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index d06815bb..390bad90 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -318,11 +318,16 @@ impl Pipeline {
layer::Image::Raster { .. } => {}
#[cfg(feature = "svg")]
- layer::Image::Vector { handle, bounds } => {
+ layer::Image::Vector {
+ handle,
+ color,
+ bounds,
+ } => {
let size = [bounds.width, bounds.height];
if let Some(atlas_entry) = vector_cache.upload(
handle,
+ *color,
size,
_scale,
&mut (device, encoder),
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 74152945..e4a38005 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -16,7 +16,7 @@
//! - Meshes of triangles, useful to draw geometry freely.
//!
//! [Iced]: https://github.com/iced-rs/iced
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs
index a117df64..027a34be 100644
--- a/wgpu/src/quad.rs
+++ b/wgpu/src/quad.rs
@@ -91,7 +91,7 @@ impl Pipeline {
2 => Float32x2,
3 => Float32x4,
4 => Float32x4,
- 5 => Float32,
+ 5 => Float32x4,
6 => Float32,
),
},
diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index 73edd97c..cf4f7e4d 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -11,7 +11,7 @@ struct VertexInput {
@location(2) scale: vec2<f32>,
@location(3) color: vec4<f32>,
@location(4) border_color: vec4<f32>,
- @location(5) border_radius: f32,
+ @location(5) border_radius: vec4<f32>,
@location(6) border_width: f32,
}
@@ -21,7 +21,7 @@ struct VertexOutput {
@location(1) border_color: vec4<f32>,
@location(2) pos: vec2<f32>,
@location(3) scale: vec2<f32>,
- @location(4) border_radius: f32,
+ @location(4) border_radius: vec4<f32>,
@location(5) border_width: f32,
}
@@ -32,9 +32,12 @@ fn vs_main(input: VertexInput) -> VertexOutput {
var pos: vec2<f32> = input.pos * globals.scale;
var scale: vec2<f32> = input.scale * globals.scale;
- var border_radius: f32 = min(
- input.border_radius,
- min(input.scale.x, input.scale.y) / 2.0
+ var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
+ var border_radius: vec4<f32> = vec4<f32>(
+ min(input.border_radius.x, min_border_radius),
+ min(input.border_radius.y, min_border_radius),
+ min(input.border_radius.z, min_border_radius),
+ min(input.border_radius.w, min_border_radius)
);
var transform: mat4x4<f32> = mat4x4<f32>(
@@ -76,6 +79,18 @@ fn distance_alg(
return sqrt(dist.x * dist.x + dist.y * dist.y);
}
+// Based on the fragement position and the center of the quad, select one of the 4 radi.
+// Order matches CSS border radius attribute:
+// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left
+fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>) -> f32 {
+ var rx = radi.x;
+ var ry = radi.y;
+ rx = select(radi.x, radi.y, position.x > center.x);
+ ry = select(radi.w, radi.z, position.x > center.x);
+ rx = select(rx, ry, position.y > center.y);
+ return rx;
+}
+
@fragment
fn fs_main(
@@ -83,14 +98,17 @@ fn fs_main(
) -> @location(0) vec4<f32> {
var mixed_color: vec4<f32> = input.color;
+ var border_radius = select_border_radius(
+ input.border_radius,
+ input.position.xy,
+ (input.pos + input.scale * 0.5).xy
+ );
+
if (input.border_width > 0.0) {
- var internal_border: f32 = max(
- input.border_radius - input.border_width,
- 0.0
- );
+ var internal_border: f32 = max(border_radius - input.border_width, 0.0);
var internal_distance: f32 = distance_alg(
- vec2<f32>(input.position.x, input.position.y),
+ input.position.xy,
input.pos + vec2<f32>(input.border_width, input.border_width),
input.scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
internal_border
@@ -109,13 +127,14 @@ fn fs_main(
vec2<f32>(input.position.x, input.position.y),
input.pos,
input.scale,
- input.border_radius
+ border_radius
);
var radius_alpha: f32 = 1.0 - smoothstep(
- max(input.border_radius - 0.5, 0.0),
- input.border_radius + 0.5,
- dist);
+ max(border_radius - 0.5, 0.0),
+ border_radius + 0.5,
+ dist
+ );
return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
}
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index b33b488a..061154b6 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -331,11 +331,7 @@ impl Pipeline {
wgpu::IndexFormat::Uint32,
);
- render_pass.draw_indexed(
- 0..(self.index_strides[index] as u32),
- 0,
- 0..1,
- );
+ render_pass.draw_indexed(0..self.index_strides[index], 0, 0..1);
}
}
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index a3ac3ddd..ebbadb12 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_winit"
-version = "0.5.1"
+version = "0.6.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A winit runtime for Iced"
@@ -26,11 +26,11 @@ git = "https://github.com/iced-rs/winit.git"
rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c"
[dependencies.iced_native]
-version = "0.6"
+version = "0.7"
path = "../native"
[dependencies.iced_graphics]
-version = "0.4"
+version = "0.5"
path = "../graphics"
[dependencies.iced_futures]
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 1706d2e9..7092e124 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -93,13 +93,6 @@ where
fn scale_factor(&self) -> f64 {
1.0
}
-
- /// Returns whether the [`Application`] should be terminated.
- ///
- /// By default, it returns `false`.
- fn should_exit(&self) -> bool {
- false
- }
}
/// Runs an [`Application`] with an executor, compositor, and the provided
@@ -255,6 +248,7 @@ async fn run_instance<A, E, C>(
let mut clipboard = Clipboard::connect(&window);
let mut cache = user_interface::Cache::default();
let mut surface = compositor.create_surface(&window);
+ let mut should_exit = false;
let mut state = State::new(&application, &window);
let mut viewport_version = state.viewport_version();
@@ -275,6 +269,7 @@ async fn run_instance<A, E, C>(
init_command,
&mut runtime,
&mut clipboard,
+ &mut should_exit,
&mut proxy,
&mut debug,
&window,
@@ -336,6 +331,7 @@ async fn run_instance<A, E, C>(
&mut renderer,
&mut runtime,
&mut clipboard,
+ &mut should_exit,
&mut proxy,
&mut debug,
&mut messages,
@@ -346,8 +342,6 @@ async fn run_instance<A, E, C>(
// Update window
state.synchronize(&application, &window);
- let should_exit = application.should_exit();
-
user_interface = ManuallyDrop::new(build_user_interface(
&application,
cache,
@@ -555,6 +549,7 @@ pub fn update<A: Application, E: Executor>(
renderer: &mut A::Renderer,
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
clipboard: &mut Clipboard,
+ should_exit: &mut bool,
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
debug: &mut Debug,
messages: &mut Vec<A::Message>,
@@ -578,6 +573,7 @@ pub fn update<A: Application, E: Executor>(
command,
runtime,
clipboard,
+ should_exit,
proxy,
debug,
window,
@@ -598,6 +594,7 @@ pub fn run_command<A, E>(
command: Command<A::Message>,
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
clipboard: &mut Clipboard,
+ should_exit: &mut bool,
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
debug: &mut Debug,
window: &winit::window::Window,
@@ -629,6 +626,9 @@ pub fn run_command<A, E>(
}
},
command::Action::Window(action) => match action {
+ window::Action::Close => {
+ *should_exit = true;
+ }
window::Action::Drag => {
let _res = window.drag_window();
}
@@ -657,9 +657,6 @@ pub fn run_command<A, E>(
mode,
));
}
- window::Action::ToggleMaximize => {
- window.set_maximized(!window.is_maximized())
- }
window::Action::FetchMode(tag) => {
let mode = if window.is_visible().unwrap_or(true) {
conversion::mode(window.fullscreen())
@@ -671,6 +668,16 @@ pub fn run_command<A, E>(
.send_event(tag(mode))
.expect("Send message to event loop");
}
+ window::Action::ToggleMaximize => {
+ window.set_maximized(!window.is_maximized())
+ }
+ window::Action::ToggleDecorations => {
+ window.set_decorations(!window.is_decorated())
+ }
+ window::Action::RequestUserAttention(user_attention) => window
+ .request_user_attention(
+ user_attention.map(conversion::user_attention),
+ ),
},
command::Action::System(action) => match action {
system::Action::QueryInformation(_tag) => {
diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs
index 0707aed5..1418e346 100644
--- a/winit/src/conversion.rs
+++ b/winit/src/conversion.rs
@@ -1,7 +1,7 @@
//! Convert [`winit`] types into [`iced_native`] types, and viceversa.
//!
//! [`winit`]: https://github.com/rust-windowing/winit
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
use crate::keyboard;
use crate::mouse;
use crate::touch;
@@ -218,7 +218,7 @@ pub fn mode(mode: Option<winit::window::Fullscreen>) -> window::Mode {
/// Converts a `MouseCursor` from [`iced_native`] to a [`winit`] cursor icon.
///
/// [`winit`]: https://github.com/rust-windowing/winit
-/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
pub fn mouse_interaction(
interaction: mouse::Interaction,
) -> winit::window::CursorIcon {
@@ -242,7 +242,7 @@ pub fn mouse_interaction(
/// Converts a `MouseButton` from [`winit`] to an [`iced_native`] mouse button.
///
/// [`winit`]: https://github.com/rust-windowing/winit
-/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
match mouse_button {
winit::event::MouseButton::Left => mouse::Button::Left,
@@ -258,7 +258,7 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
/// modifiers state.
///
/// [`winit`]: https://github.com/rust-windowing/winit
-/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
pub fn modifiers(
modifiers: winit::event::ModifiersState,
) -> keyboard::Modifiers {
@@ -285,7 +285,7 @@ pub fn cursor_position(
/// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event.
///
/// [`winit`]: https://github.com/rust-windowing/winit
-/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
pub fn touch_event(
touch: winit::event::Touch,
scale_factor: f64,
@@ -316,7 +316,7 @@ pub fn touch_event(
/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code.
///
/// [`winit`]: https://github.com/rust-windowing/winit
-/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
pub fn key_code(
virtual_keycode: winit::event::VirtualKeyCode,
) -> keyboard::KeyCode {
@@ -493,6 +493,22 @@ pub fn key_code(
}
}
+/// Converts some [`UserAttention`] into it's `winit` counterpart.
+///
+/// [`UserAttention`]: window::UserAttention
+pub fn user_attention(
+ user_attention: window::UserAttention,
+) -> winit::window::UserAttentionType {
+ match user_attention {
+ window::UserAttention::Critical => {
+ winit::window::UserAttentionType::Critical
+ }
+ window::UserAttention::Informational => {
+ winit::window::UserAttentionType::Informational
+ }
+ }
+}
+
// As defined in: http://www.unicode.org/faq/private_use.html
pub(crate) fn is_private_use_character(c: char) -> bool {
matches!(
diff --git a/winit/src/lib.rs b/winit/src/lib.rs
index bb3a3d5b..b8ed492d 100644
--- a/winit/src/lib.rs
+++ b/winit/src/lib.rs
@@ -11,7 +11,7 @@
//! Additionally, a [`conversion`] module is available for users that decide to
//! implement a custom event loop.
//!
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.5/native
+//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.6/native
//! [`winit`]: https://github.com/rust-windowing/winit
//! [`conversion`]: crate::conversion
#![doc(
diff --git a/winit/src/window.rs b/winit/src/window.rs
index 1e704c5b..89db3262 100644
--- a/winit/src/window.rs
+++ b/winit/src/window.rs
@@ -2,7 +2,12 @@
use crate::command::{self, Command};
use iced_native::window;
-pub use window::{Event, Mode};
+pub use window::{Event, Mode, UserAttention};
+
+/// Closes the current window and exits the application.
+pub fn close<Message>() -> Command<Message> {
+ Command::single(command::Action::Window(window::Action::Close))
+}
/// Begins dragging the window while the left mouse button is held.
pub fn drag<Message>() -> Command<Message> {