summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.github/workflows/test.yml2
-rw-r--r--Cargo.toml40
-rw-r--r--core/Cargo.toml4
-rw-r--r--core/src/alignment.rs3
-rw-r--r--core/src/clipboard.rs23
-rw-r--r--core/src/element.rs (renamed from native/src/element.rs)67
-rw-r--r--core/src/event.rs (renamed from native/src/event.rs)2
-rw-r--r--core/src/gradient.rs (renamed from graphics/src/gradient.rs)0
-rw-r--r--core/src/gradient/linear.rs (renamed from graphics/src/gradient/linear.rs)0
-rw-r--r--core/src/hasher.rs (renamed from native/src/hasher.rs)0
-rw-r--r--core/src/image.rs (renamed from native/src/image.rs)0
-rw-r--r--core/src/layout.rs (renamed from native/src/layout.rs)0
-rw-r--r--core/src/layout/DRUID_LICENSE (renamed from native/src/layout/DRUID_LICENSE)0
-rw-r--r--core/src/layout/flex.rs (renamed from native/src/layout/flex.rs)67
-rw-r--r--core/src/layout/limits.rs (renamed from native/src/layout/limits.rs)0
-rw-r--r--core/src/layout/node.rs (renamed from native/src/layout/node.rs)6
-rw-r--r--core/src/lib.rs26
-rw-r--r--core/src/mouse.rs3
-rw-r--r--core/src/mouse/click.rs (renamed from native/src/mouse/click.rs)0
-rw-r--r--core/src/overlay.rs (renamed from native/src/overlay.rs)3
-rw-r--r--core/src/overlay/element.rs (renamed from native/src/overlay/element.rs)0
-rw-r--r--core/src/overlay/group.rs (renamed from native/src/overlay/group.rs)4
-rw-r--r--core/src/padding.rs10
-rw-r--r--core/src/renderer.rs (renamed from native/src/renderer.rs)1
-rw-r--r--core/src/renderer/null.rs (renamed from native/src/renderer/null.rs)4
-rw-r--r--core/src/shell.rs (renamed from native/src/shell.rs)0
-rw-r--r--core/src/size.rs2
-rw-r--r--core/src/svg.rs (renamed from native/src/svg.rs)0
-rw-r--r--core/src/text.rs (renamed from native/src/text.rs)0
-rw-r--r--core/src/touch.rs (renamed from native/src/touch.rs)0
-rw-r--r--core/src/widget.rs (renamed from native/src/widget.rs)87
-rw-r--r--core/src/widget/id.rs (renamed from native/src/widget/id.rs)2
-rw-r--r--core/src/widget/operation.rs226
-rw-r--r--core/src/widget/operation/focusable.rs (renamed from native/src/widget/operation/focusable.rs)0
-rw-r--r--core/src/widget/operation/scrollable.rs (renamed from native/src/widget/operation/scrollable.rs)0
-rw-r--r--core/src/widget/operation/text_input.rs (renamed from native/src/widget/operation/text_input.rs)0
-rw-r--r--core/src/widget/text.rs (renamed from native/src/widget/text.rs)54
-rw-r--r--core/src/widget/tree.rs (renamed from native/src/widget/tree.rs)0
-rw-r--r--core/src/window.rs10
-rw-r--r--core/src/window/event.rs (renamed from native/src/window/event.rs)0
-rw-r--r--core/src/window/mode.rs (renamed from native/src/window/mode.rs)0
-rw-r--r--core/src/window/redraw_request.rs (renamed from native/src/window/redraw_request.rs)0
-rw-r--r--core/src/window/user_attention.rs (renamed from native/src/window/user_attention.rs)0
-rw-r--r--examples/arc/src/main.rs7
-rw-r--r--examples/bezier_tool/src/main.rs23
-rw-r--r--examples/clock/src/main.rs9
-rw-r--r--examples/color_palette/src/main.rs7
-rw-r--r--examples/component/Cargo.toml4
-rw-r--r--examples/component/src/main.rs27
-rw-r--r--examples/custom_quad/Cargo.toml3
-rw-r--r--examples/custom_quad/src/main.rs8
-rw-r--r--examples/custom_widget/Cargo.toml3
-rw-r--r--examples/custom_widget/src/main.rs8
-rw-r--r--examples/download_progress/Cargo.toml2
-rw-r--r--examples/download_progress/src/download.rs2
-rw-r--r--examples/events/Cargo.toml1
-rw-r--r--examples/events/src/main.rs9
-rw-r--r--examples/game_of_life/src/main.rs73
-rw-r--r--examples/geometry/Cargo.toml3
-rw-r--r--examples/geometry/src/main.rs44
-rw-r--r--examples/integration/Cargo.toml4
-rw-r--r--examples/integration/src/controls.rs10
-rw-r--r--examples/integration/src/main.rs91
-rw-r--r--examples/integration/src/scene.rs2
-rw-r--r--examples/lazy/Cargo.toml3
-rw-r--r--examples/lazy/src/main.rs3
-rw-r--r--examples/modal/Cargo.toml3
-rw-r--r--examples/modal/src/main.rs44
-rw-r--r--examples/modern_art/src/main.rs5
-rw-r--r--examples/multitouch/src/main.rs7
-rw-r--r--examples/pane_grid/Cargo.toml4
-rw-r--r--examples/pane_grid/src/main.rs8
-rw-r--r--examples/scrollable/src/main.rs1
-rw-r--r--examples/sierpinski_triangle/src/main.rs7
-rw-r--r--examples/solar_system/src/main.rs26
-rw-r--r--examples/toast/Cargo.toml3
-rw-r--r--examples/toast/src/main.rs24
-rw-r--r--examples/todos/src/main.rs21
-rw-r--r--examples/url_handler/Cargo.toml1
-rw-r--r--examples/url_handler/src/main.rs10
-rw-r--r--examples/websocket/Cargo.toml2
-rw-r--r--examples/websocket/src/echo.rs4
-rw-r--r--examples/websocket/src/echo/server.rs2
-rw-r--r--examples/websocket/src/main.rs4
-rw-r--r--futures/Cargo.toml4
-rw-r--r--futures/src/backend/native/async_std.rs14
-rw-r--r--futures/src/backend/native/smol.rs14
-rw-r--r--futures/src/backend/native/tokio.rs14
-rw-r--r--futures/src/backend/wasm/wasm_bindgen.rs14
-rw-r--r--futures/src/command.rs70
-rw-r--r--futures/src/lib.rs3
-rw-r--r--futures/src/runtime.rs25
-rw-r--r--futures/src/subscription.rs316
-rw-r--r--futures/src/subscription/tracker.rs51
-rw-r--r--graphics/Cargo.toml57
-rw-r--r--graphics/src/backend.rs8
-rw-r--r--graphics/src/compositor.rs (renamed from graphics/src/window/compositor.rs)8
-rw-r--r--graphics/src/geometry.rs36
-rw-r--r--graphics/src/geometry/fill.rs (renamed from graphics/src/widget/canvas/fill.rs)19
-rw-r--r--graphics/src/geometry/path.rs (renamed from graphics/src/widget/canvas/path.rs)52
-rw-r--r--graphics/src/geometry/path/arc.rs (renamed from graphics/src/widget/canvas/path/arc.rs)2
-rw-r--r--graphics/src/geometry/path/builder.rs (renamed from graphics/src/widget/canvas/path/builder.rs)27
-rw-r--r--graphics/src/geometry/stroke.rs (renamed from graphics/src/widget/canvas/stroke.rs)24
-rw-r--r--graphics/src/geometry/style.rs (renamed from graphics/src/widget/canvas/style.rs)2
-rw-r--r--graphics/src/geometry/text.rs (renamed from graphics/src/widget/canvas/text.rs)4
-rw-r--r--graphics/src/image.rs98
-rw-r--r--graphics/src/image/raster.rs242
-rw-r--r--graphics/src/image/storage.rs2
-rw-r--r--graphics/src/lib.rs30
-rw-r--r--graphics/src/overlay.rs2
-rw-r--r--graphics/src/overlay/menu.rs3
-rw-r--r--graphics/src/primitive.rs134
-rw-r--r--graphics/src/renderer.rs68
-rw-r--r--graphics/src/triangle.rs32
-rw-r--r--graphics/src/viewport.rs4
-rw-r--r--graphics/src/widget/canvas/geometry.rs24
-rw-r--r--graphics/src/window.rs10
-rw-r--r--graphics/src/window/gl_compositor.rs71
-rw-r--r--lazy/Cargo.toml18
-rw-r--r--native/src/mouse.rs6
-rw-r--r--native/src/runtime.rs18
-rw-r--r--native/src/subscription.rs248
-rw-r--r--native/src/widget/action.rs154
-rw-r--r--native/src/widget/helpers.rs317
-rw-r--r--native/src/widget/operation.rs112
-rw-r--r--native/src/window.rs30
-rw-r--r--renderer/Cargo.toml30
-rw-r--r--renderer/src/backend.rs98
-rw-r--r--renderer/src/compositor.rs185
-rw-r--r--renderer/src/geometry.rs179
-rw-r--r--renderer/src/geometry/cache.rs (renamed from graphics/src/widget/canvas/cache.rs)59
-rw-r--r--renderer/src/lib.rs22
-rw-r--r--renderer/src/settings.rs31
-rw-r--r--renderer/src/widget.rs (renamed from graphics/src/widget.rs)5
-rw-r--r--runtime/Cargo.toml (renamed from native/Cargo.toml)10
-rw-r--r--runtime/README.md (renamed from native/README.md)20
-rw-r--r--runtime/src/clipboard.rs (renamed from native/src/clipboard.rs)37
-rw-r--r--runtime/src/command.rs (renamed from native/src/command.rs)55
-rw-r--r--runtime/src/command/action.rs (renamed from native/src/command/action.rs)8
-rw-r--r--runtime/src/debug/basic.rs (renamed from native/src/debug/basic.rs)2
-rw-r--r--runtime/src/debug/null.rs (renamed from native/src/debug/null.rs)0
-rw-r--r--runtime/src/font.rs (renamed from native/src/font.rs)0
-rw-r--r--runtime/src/keyboard.rs (renamed from native/src/keyboard.rs)0
-rw-r--r--runtime/src/lib.rs (renamed from native/src/lib.rs)45
-rw-r--r--runtime/src/program.rs (renamed from native/src/program.rs)6
-rw-r--r--runtime/src/program/state.rs (renamed from native/src/program/state.rs)18
-rw-r--r--runtime/src/system.rs (renamed from native/src/system.rs)0
-rw-r--r--runtime/src/system/action.rs (renamed from native/src/system/action.rs)0
-rw-r--r--runtime/src/system/information.rs (renamed from native/src/system/information.rs)0
-rw-r--r--runtime/src/user_interface.rs (renamed from native/src/user_interface.rs)82
-rw-r--r--runtime/src/window.rs (renamed from winit/src/window.rs)70
-rw-r--r--runtime/src/window/action.rs (renamed from native/src/window/action.rs)4
-rw-r--r--src/advanced.rs14
-rw-r--r--src/application.rs14
-rw-r--r--src/clipboard.rs3
-rw-r--r--src/element.rs5
-rw-r--r--src/error.rs16
-rw-r--r--src/executor.rs14
-rw-r--r--src/keyboard.rs2
-rw-r--r--src/lib.rs140
-rw-r--r--src/mouse.rs2
-rw-r--r--src/overlay.rs18
-rw-r--r--src/result.rs6
-rw-r--r--src/touch.rs2
-rw-r--r--src/widget.rs238
-rw-r--r--src/window.rs2
-rw-r--r--style/src/lib.rs3
-rw-r--r--style/src/text.rs2
-rw-r--r--style/src/theme.rs2
-rw-r--r--tiny_skia/Cargo.toml40
-rw-r--r--tiny_skia/src/backend.rs548
-rw-r--r--tiny_skia/src/geometry.rs287
-rw-r--r--tiny_skia/src/lib.rs26
-rw-r--r--tiny_skia/src/primitive.rs82
-rw-r--r--tiny_skia/src/raster.rs116
-rw-r--r--tiny_skia/src/settings.rs24
-rw-r--r--tiny_skia/src/text.rs431
-rw-r--r--tiny_skia/src/vector.rs180
-rw-r--r--tiny_skia/src/window.rs3
-rw-r--r--tiny_skia/src/window/compositor.rs130
-rw-r--r--wgpu/Cargo.toml43
-rw-r--r--wgpu/src/backend.rs23
-rw-r--r--wgpu/src/geometry.rs (renamed from graphics/src/widget/canvas/frame.rs)158
-rw-r--r--wgpu/src/image.rs42
-rw-r--r--wgpu/src/image/atlas.rs192
-rw-r--r--wgpu/src/image/atlas/allocation.rs3
-rw-r--r--wgpu/src/image/atlas/allocator.rs4
-rw-r--r--wgpu/src/image/atlas/entry.rs9
-rw-r--r--wgpu/src/image/raster.rs121
-rw-r--r--wgpu/src/image/vector.rs (renamed from graphics/src/image/vector.rs)61
-rw-r--r--wgpu/src/layer.rs (renamed from graphics/src/layer.rs)78
-rw-r--r--wgpu/src/layer/image.rs (renamed from graphics/src/layer/image.rs)6
-rw-r--r--wgpu/src/layer/mesh.rs (renamed from graphics/src/layer/mesh.rs)8
-rw-r--r--wgpu/src/layer/quad.rs (renamed from graphics/src/layer/quad.rs)0
-rw-r--r--wgpu/src/layer/text.rs (renamed from graphics/src/layer/text.rs)3
-rw-r--r--wgpu/src/lib.rs21
-rw-r--r--wgpu/src/quad.rs7
-rw-r--r--wgpu/src/settings.rs5
-rw-r--r--wgpu/src/text.rs15
-rw-r--r--wgpu/src/triangle.rs91
-rw-r--r--wgpu/src/triangle/msaa.rs4
-rw-r--r--wgpu/src/window.rs3
-rw-r--r--wgpu/src/window/compositor.rs155
-rw-r--r--widget/Cargo.toml37
-rw-r--r--widget/src/button.rs (renamed from native/src/widget/button.rs)36
-rw-r--r--widget/src/canvas.rs (renamed from graphics/src/widget/canvas.rs)132
-rw-r--r--widget/src/canvas/cursor.rs (renamed from graphics/src/widget/canvas/cursor.rs)2
-rw-r--r--widget/src/canvas/event.rs (renamed from graphics/src/widget/canvas/event.rs)8
-rw-r--r--widget/src/canvas/program.rs (renamed from graphics/src/widget/canvas/program.rs)31
-rw-r--r--widget/src/checkbox.rs (renamed from native/src/widget/checkbox.rs)38
-rw-r--r--widget/src/column.rs (renamed from native/src/widget/column.rs)20
-rw-r--r--widget/src/container.rs (renamed from native/src/widget/container.rs)28
-rw-r--r--widget/src/helpers.rs362
-rw-r--r--widget/src/image.rs (renamed from native/src/widget/image.rs)17
-rw-r--r--widget/src/image/viewer.rs (renamed from native/src/widget/image/viewer.rs)14
-rw-r--r--widget/src/lazy.rs (renamed from lazy/src/lazy.rs)47
-rw-r--r--widget/src/lazy/cache.rs (renamed from lazy/src/cache.rs)4
-rw-r--r--widget/src/lazy/component.rs (renamed from lazy/src/component.rs)32
-rw-r--r--widget/src/lazy/helpers.rs (renamed from lazy/src/lib.rs)37
-rw-r--r--widget/src/lazy/responsive.rs (renamed from lazy/src/responsive.rs)40
-rw-r--r--widget/src/lib.rs122
-rw-r--r--widget/src/overlay.rs1
-rw-r--r--widget/src/overlay/menu.rs (renamed from native/src/overlay/menu.rs)34
-rw-r--r--widget/src/pane_grid.rs (renamed from native/src/widget/pane_grid.rs)42
-rw-r--r--widget/src/pane_grid/axis.rs (renamed from native/src/widget/pane_grid/axis.rs)2
-rw-r--r--widget/src/pane_grid/configuration.rs (renamed from native/src/widget/pane_grid/configuration.rs)2
-rw-r--r--widget/src/pane_grid/content.rs (renamed from native/src/widget/pane_grid/content.rs)30
-rw-r--r--widget/src/pane_grid/direction.rs (renamed from native/src/widget/pane_grid/direction.rs)0
-rw-r--r--widget/src/pane_grid/draggable.rs (renamed from native/src/widget/pane_grid/draggable.rs)2
-rw-r--r--widget/src/pane_grid/node.rs (renamed from native/src/widget/pane_grid/node.rs)4
-rw-r--r--widget/src/pane_grid/pane.rs (renamed from native/src/widget/pane_grid/pane.rs)0
-rw-r--r--widget/src/pane_grid/split.rs (renamed from native/src/widget/pane_grid/split.rs)0
-rw-r--r--widget/src/pane_grid/state.rs (renamed from native/src/widget/pane_grid/state.rs)6
-rw-r--r--widget/src/pane_grid/title_bar.rs (renamed from native/src/widget/pane_grid/title_bar.rs)24
-rw-r--r--widget/src/pick_list.rs (renamed from native/src/widget/pick_list.rs)33
-rw-r--r--widget/src/progress_bar.rs (renamed from native/src/widget/progress_bar.rs)26
-rw-r--r--widget/src/qr_code.rs (renamed from graphics/src/widget/qr_code.rs)100
-rw-r--r--widget/src/radio.rs (renamed from native/src/widget/radio.rs)33
-rw-r--r--widget/src/row.rs (renamed from native/src/widget/row.rs)20
-rw-r--r--widget/src/rule.rs (renamed from native/src/widget/rule.rs)20
-rw-r--r--widget/src/scrollable.rs (renamed from native/src/widget/scrollable.rs)168
-rw-r--r--widget/src/slider.rs (renamed from native/src/widget/slider.rs)34
-rw-r--r--widget/src/space.rs (renamed from native/src/widget/space.rs)13
-rw-r--r--widget/src/svg.rs (renamed from native/src/widget/svg.rs)14
-rw-r--r--widget/src/text.rs4
-rw-r--r--widget/src/text_input.rs (renamed from native/src/widget/text_input.rs)41
-rw-r--r--widget/src/text_input/cursor.rs (renamed from native/src/widget/text_input/cursor.rs)2
-rw-r--r--widget/src/text_input/editor.rs (renamed from native/src/widget/text_input/editor.rs)2
-rw-r--r--widget/src/text_input/value.rs (renamed from native/src/widget/text_input/value.rs)0
-rw-r--r--widget/src/toggler.rs (renamed from native/src/widget/toggler.rs)32
-rw-r--r--widget/src/tooltip.rs (renamed from native/src/widget/tooltip.rs)37
-rw-r--r--widget/src/vertical_slider.rs (renamed from native/src/widget/vertical_slider.rs)39
-rw-r--r--winit/Cargo.toml14
-rw-r--r--winit/src/application.rs110
-rw-r--r--winit/src/application/state.rs14
-rw-r--r--winit/src/clipboard.rs17
-rw-r--r--winit/src/conversion.rs11
-rw-r--r--winit/src/error.rs7
-rw-r--r--winit/src/lib.rs9
-rw-r--r--winit/src/proxy.rs2
-rw-r--r--winit/src/system.rs11
261 files changed, 5931 insertions, 3959 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 390e7bf3..a9a9b3f9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -37,5 +37,5 @@ jobs:
run: cargo build --package tour --target wasm32-unknown-unknown
- name: Check compilation of `todos` example
run: cargo build --package todos --target wasm32-unknown-unknown
- - name: Check compilation of `integration_wgpu` example
+ - name: Check compilation of `integration` example
run: cargo build --package integration --target wasm32-unknown-unknown
diff --git a/Cargo.toml b/Cargo.toml
index 551e12ac..38c35f43 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,14 +12,21 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[features]
+default = ["wgpu", "tiny-skia"]
+# Enable the `wgpu` GPU-accelerated renderer backend
+wgpu = ["iced_renderer/wgpu"]
+# Enable the `tiny-skia` software renderer backend
+tiny-skia = ["iced_renderer/tiny-skia"]
# Enables the `Image` widget
-image = ["iced_wgpu/image", "image_rs"]
+image = ["iced_widget/image", "image_rs"]
# Enables the `Svg` widget
-svg = ["iced_wgpu/svg"]
+svg = ["iced_widget/svg"]
# Enables the `Canvas` widget
-canvas = ["iced_graphics/canvas"]
+canvas = ["iced_widget/canvas"]
# Enables the `QRCode` widget
-qr_code = ["iced_graphics/qr_code"]
+qr_code = ["iced_widget/qr_code"]
+# Enables lazy widgets
+lazy = ["iced_widget/lazy"]
# Enables a debug view in native platforms (press F12)
debug = ["iced_winit/debug"]
# Enables `tokio` as the `executor::Default` on native platforms
@@ -32,11 +39,8 @@ smol = ["iced_futures/smol"]
palette = ["iced_core/palette"]
# Enables querying system information
system = ["iced_winit/system"]
-# Enables chrome traces
-chrome-trace = [
- "iced_winit/chrome-trace",
- "iced_wgpu/tracing",
-]
+# Enables the advanced module
+advanced = []
[badges]
maintenance = { status = "actively-developed" }
@@ -46,10 +50,12 @@ members = [
"core",
"futures",
"graphics",
- "lazy",
- "native",
+ "runtime",
+ "renderer",
"style",
+ "tiny_skia",
"wgpu",
+ "widget",
"winit",
"examples/*",
]
@@ -57,22 +63,16 @@ members = [
[dependencies]
iced_core = { version = "0.8", path = "core" }
iced_futures = { version = "0.6", path = "futures" }
-iced_native = { version = "0.9", path = "native" }
-iced_graphics = { version = "0.7", path = "graphics" }
+iced_renderer = { version = "0.1", path = "renderer" }
+iced_widget = { version = "0.1", path = "widget" }
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
-thiserror = "1.0"
+thiserror = "1"
[dependencies.image_rs]
version = "0.24"
package = "image"
optional = true
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-iced_wgpu = { version = "0.9", path = "wgpu" }
-
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-iced_wgpu = { version = "0.9", path = "wgpu", features = ["webgl"] }
-
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
features = ["image", "svg", "canvas", "qr_code"]
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 43865e4d..dac31828 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_core"
-version = "0.8.0"
+version = "0.8.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "The essential concepts of Iced"
@@ -9,6 +9,8 @@ repository = "https://github.com/iced-rs/iced"
[dependencies]
bitflags = "1.2"
+thiserror = "1"
+twox-hash = { version = "1.5", default-features = false }
[dependencies.palette]
version = "0.6"
diff --git a/core/src/alignment.rs b/core/src/alignment.rs
index 73f41d3f..51b7fca9 100644
--- a/core/src/alignment.rs
+++ b/core/src/alignment.rs
@@ -11,9 +11,6 @@ pub enum Alignment {
/// Align at the end of the axis.
End,
-
- /// Fill the entire axis.
- Fill,
}
impl From<Horizontal> for Alignment {
diff --git a/core/src/clipboard.rs b/core/src/clipboard.rs
new file mode 100644
index 00000000..081b4004
--- /dev/null
+++ b/core/src/clipboard.rs
@@ -0,0 +1,23 @@
+//! Access the clipboard.
+
+/// A buffer for short-term storage and transfer within and between
+/// applications.
+pub trait Clipboard {
+ /// Reads the current content of the [`Clipboard`] as text.
+ fn read(&self) -> Option<String>;
+
+ /// Writes the given text contents to the [`Clipboard`].
+ fn write(&mut self, contents: String);
+}
+
+/// A null implementation of the [`Clipboard`] trait.
+#[derive(Debug, Clone, Copy)]
+pub struct Null;
+
+impl Clipboard for Null {
+ fn read(&self) -> Option<String> {
+ None
+ }
+
+ fn write(&mut self, _contents: String) {}
+}
diff --git a/native/src/element.rs b/core/src/element.rs
index 0a677d20..98c53737 100644
--- a/native/src/element.rs
+++ b/core/src/element.rs
@@ -90,41 +90,65 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// We compose the previous __messages__ with the index of the counter
/// producing them. Let's implement our __view logic__ now:
///
- /// ```
+ /// ```no_run
/// # mod counter {
- /// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
- /// #
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {}
/// # pub struct Counter;
/// #
/// # impl Counter {
- /// # pub fn view(&mut self) -> Text {
- /// # Text::new("")
+ /// # pub fn view(
+ /// # &self,
+ /// # ) -> iced_core::Element<Message, iced_core::renderer::Null> {
+ /// # unimplemented!()
/// # }
/// # }
/// # }
/// #
- /// # mod iced_wgpu {
- /// # pub use iced_native::renderer::Null as Renderer;
- /// # }
+ /// # mod iced {
+ /// # pub use iced_core::renderer::Null as Renderer;
+ /// # pub use iced_core::Element;
/// #
- /// # use counter::Counter;
+ /// # pub mod widget {
+ /// # pub struct Row<Message> {
+ /// # _t: std::marker::PhantomData<Message>,
+ /// # }
/// #
- /// # struct ManyCounters {
- /// # counters: Vec<Counter>,
- /// # }
+ /// # impl<Message> Row<Message> {
+ /// # pub fn new() -> Self {
+ /// # unimplemented!()
+ /// # }
/// #
- /// # #[derive(Debug, Clone, Copy)]
- /// # pub enum Message {
- /// # Counter(usize, counter::Message)
+ /// # pub fn spacing(mut self, _: u32) -> Self {
+ /// # unimplemented!()
+ /// # }
+ /// #
+ /// # pub fn push(
+ /// # mut self,
+ /// # _: iced_core::Element<Message, iced_core::renderer::Null>,
+ /// # ) -> Self {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// # }
/// # }
- /// use iced_native::Element;
- /// use iced_native::widget::Row;
- /// use iced_wgpu::Renderer;
+ /// #
+ /// use counter::Counter;
+ ///
+ /// use iced::widget::Row;
+ /// use iced::{Element, Renderer};
+ ///
+ /// struct ManyCounters {
+ /// counters: Vec<Counter>,
+ /// }
+ ///
+ /// #[derive(Debug, Clone, Copy)]
+ /// pub enum Message {
+ /// Counter(usize, counter::Message),
+ /// }
///
/// impl ManyCounters {
- /// pub fn view(&mut self) -> Row<Message, Renderer> {
+ /// pub fn view(&mut self) -> Row<Message> {
/// // We can quickly populate a `Row` by folding over our counters
/// self.counters.iter_mut().enumerate().fold(
/// Row::new().spacing(20),
@@ -137,9 +161,10 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// // Here we turn our `Element<counter::Message>` into
/// // an `Element<Message>` by combining the `index` and the
/// // message of the `element`.
- /// element.map(move |message| Message::Counter(index, message))
+ /// element
+ /// .map(move |message| Message::Counter(index, message)),
/// )
- /// }
+ /// },
/// )
/// }
/// }
diff --git a/native/src/event.rs b/core/src/event.rs
index bcfaf891..953cd73f 100644
--- a/native/src/event.rs
+++ b/core/src/event.rs
@@ -62,7 +62,7 @@ impl Status {
/// `Captured` takes precedence over `Ignored`:
///
/// ```
- /// use iced_native::event::Status;
+ /// use iced_core::event::Status;
///
/// assert_eq!(Status::Ignored.merge(Status::Ignored), Status::Ignored);
/// assert_eq!(Status::Ignored.merge(Status::Captured), Status::Captured);
diff --git a/graphics/src/gradient.rs b/core/src/gradient.rs
index 61e919d6..61e919d6 100644
--- a/graphics/src/gradient.rs
+++ b/core/src/gradient.rs
diff --git a/graphics/src/gradient/linear.rs b/core/src/gradient/linear.rs
index c886db47..c886db47 100644
--- a/graphics/src/gradient/linear.rs
+++ b/core/src/gradient/linear.rs
diff --git a/native/src/hasher.rs b/core/src/hasher.rs
index fa52f16d..fa52f16d 100644
--- a/native/src/hasher.rs
+++ b/core/src/hasher.rs
diff --git a/native/src/image.rs b/core/src/image.rs
index 70fbade0..70fbade0 100644
--- a/native/src/image.rs
+++ b/core/src/image.rs
diff --git a/native/src/layout.rs b/core/src/layout.rs
index 04954fb9..04954fb9 100644
--- a/native/src/layout.rs
+++ b/core/src/layout.rs
diff --git a/native/src/layout/DRUID_LICENSE b/core/src/layout/DRUID_LICENSE
index d6456956..d6456956 100644
--- a/native/src/layout/DRUID_LICENSE
+++ b/core/src/layout/DRUID_LICENSE
diff --git a/native/src/layout/flex.rs b/core/src/layout/flex.rs
index 5d70c2fc..8b967849 100644
--- a/native/src/layout/flex.rs
+++ b/core/src/layout/flex.rs
@@ -81,32 +81,6 @@ where
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
nodes.resize(items.len(), Node::default());
- if align_items == Alignment::Fill {
- let mut fill_cross = axis.cross(limits.min());
-
- items.iter().for_each(|child| {
- let cross_fill_factor = match axis {
- Axis::Horizontal => child.as_widget().height(),
- Axis::Vertical => child.as_widget().width(),
- }
- .fill_factor();
-
- if cross_fill_factor == 0 {
- let (max_width, max_height) = axis.pack(available, max_cross);
-
- let child_limits =
- Limits::new(Size::ZERO, Size::new(max_width, max_height));
-
- let layout = child.as_widget().layout(renderer, &child_limits);
- let size = layout.size();
-
- fill_cross = fill_cross.max(axis.cross(size));
- }
- });
-
- cross = fill_cross;
- }
-
for (i, child) in items.iter().enumerate() {
let fill_factor = match axis {
Axis::Horizontal => child.as_widget().width(),
@@ -115,31 +89,16 @@ where
.fill_factor();
if fill_factor == 0 {
- let (min_width, min_height) = if align_items == Alignment::Fill {
- axis.pack(0.0, cross)
- } else {
- axis.pack(0.0, 0.0)
- };
+ let (max_width, max_height) = axis.pack(available, max_cross);
- let (max_width, max_height) = if align_items == Alignment::Fill {
- axis.pack(available, cross)
- } else {
- axis.pack(available, max_cross)
- };
-
- let child_limits = Limits::new(
- Size::new(min_width, min_height),
- Size::new(max_width, max_height),
- );
+ let child_limits =
+ Limits::new(Size::ZERO, Size::new(max_width, max_height));
let layout = child.as_widget().layout(renderer, &child_limits);
let size = layout.size();
available -= axis.main(size);
-
- if align_items != Alignment::Fill {
- cross = cross.max(axis.cross(size));
- }
+ cross = cross.max(axis.cross(size));
nodes[i] = layout;
} else {
@@ -164,17 +123,10 @@ where
max_main
};
- let (min_width, min_height) = if align_items == Alignment::Fill {
- axis.pack(min_main, cross)
- } else {
- axis.pack(min_main, axis.cross(limits.min()))
- };
+ let (min_width, min_height) =
+ axis.pack(min_main, axis.cross(limits.min()));
- let (max_width, max_height) = if align_items == Alignment::Fill {
- axis.pack(max_main, cross)
- } else {
- axis.pack(max_main, max_cross)
- };
+ let (max_width, max_height) = axis.pack(max_main, max_cross);
let child_limits = Limits::new(
Size::new(min_width, min_height),
@@ -182,10 +134,7 @@ where
);
let layout = child.as_widget().layout(renderer, &child_limits);
-
- if align_items != Alignment::Fill {
- cross = cross.max(axis.cross(layout.size()));
- }
+ cross = cross.max(axis.cross(layout.size()));
nodes[i] = layout;
}
diff --git a/native/src/layout/limits.rs b/core/src/layout/limits.rs
index 5d3c1556..5d3c1556 100644
--- a/native/src/layout/limits.rs
+++ b/core/src/layout/limits.rs
diff --git a/native/src/layout/node.rs b/core/src/layout/node.rs
index e0c7dcb2..2b44a7d5 100644
--- a/native/src/layout/node.rs
+++ b/core/src/layout/node.rs
@@ -56,9 +56,6 @@ impl Node {
Alignment::End => {
self.bounds.x += space.width - self.bounds.width;
}
- Alignment::Fill => {
- self.bounds.width = space.width;
- }
}
match vertical_alignment {
@@ -69,9 +66,6 @@ impl Node {
Alignment::End => {
self.bounds.y += space.height - self.bounds.height;
}
- Alignment::Fill => {
- self.bounds.height = space.height;
- }
}
}
diff --git a/core/src/lib.rs b/core/src/lib.rs
index d7314851..5bdcee6a 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -25,31 +25,57 @@
#![forbid(unsafe_code, rust_2018_idioms)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
pub mod alignment;
+pub mod clipboard;
+pub mod event;
pub mod font;
+pub mod gradient;
+pub mod image;
pub mod keyboard;
+pub mod layout;
pub mod mouse;
+pub mod overlay;
+pub mod renderer;
+pub mod svg;
+pub mod text;
pub mod time;
+pub mod touch;
+pub mod widget;
+pub mod window;
mod background;
mod color;
mod content_fit;
+mod element;
+mod hasher;
mod length;
mod padding;
mod pixels;
mod point;
mod rectangle;
+mod shell;
mod size;
mod vector;
pub use alignment::Alignment;
pub use background::Background;
+pub use clipboard::Clipboard;
pub use color::Color;
pub use content_fit::ContentFit;
+pub use element::Element;
+pub use event::Event;
pub use font::Font;
+pub use gradient::Gradient;
+pub use hasher::Hasher;
+pub use layout::Layout;
pub use length::Length;
+pub use overlay::Overlay;
pub use padding::Padding;
pub use pixels::Pixels;
pub use point::Point;
pub use rectangle::Rectangle;
+pub use renderer::Renderer;
+pub use shell::Shell;
pub use size::Size;
+pub use text::Text;
pub use vector::Vector;
+pub use widget::Widget;
diff --git a/core/src/mouse.rs b/core/src/mouse.rs
index 48214f65..0c405ce6 100644
--- a/core/src/mouse.rs
+++ b/core/src/mouse.rs
@@ -1,8 +1,11 @@
//! Handle mouse events.
+pub mod click;
+
mod button;
mod event;
mod interaction;
pub use button::Button;
+pub use click::Click;
pub use event::{Event, ScrollDelta};
pub use interaction::Interaction;
diff --git a/native/src/mouse/click.rs b/core/src/mouse/click.rs
index 4a7d796c..4a7d796c 100644
--- a/native/src/mouse/click.rs
+++ b/core/src/mouse/click.rs
diff --git a/native/src/overlay.rs b/core/src/overlay.rs
index 6cada416..b9f3c735 100644
--- a/native/src/overlay.rs
+++ b/core/src/overlay.rs
@@ -2,11 +2,8 @@
mod element;
mod group;
-pub mod menu;
-
pub use element::Element;
pub use group::Group;
-pub use menu::Menu;
use crate::event::{self, Event};
use crate::layout;
diff --git a/native/src/overlay/element.rs b/core/src/overlay/element.rs
index 237d25d1..237d25d1 100644
--- a/native/src/overlay/element.rs
+++ b/core/src/overlay/element.rs
diff --git a/native/src/overlay/group.rs b/core/src/overlay/group.rs
index 1126f0cf..0c48df34 100644
--- a/native/src/overlay/group.rs
+++ b/core/src/overlay/group.rs
@@ -1,12 +1,10 @@
-use iced_core::{Point, Rectangle, Size};
-
use crate::event;
use crate::layout;
use crate::mouse;
use crate::overlay;
use crate::renderer;
use crate::widget;
-use crate::{Clipboard, Event, Layout, Overlay, Shell};
+use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size};
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
/// children.
diff --git a/core/src/padding.rs b/core/src/padding.rs
index 752b2b86..0b1bba13 100644
--- a/core/src/padding.rs
+++ b/core/src/padding.rs
@@ -77,12 +77,14 @@ impl Padding {
/// Fits the [`Padding`] between the provided `inner` and `outer` [`Size`].
pub fn fit(self, inner: Size, outer: Size) -> Self {
let available = (outer - inner).max(Size::ZERO);
+ let new_top = self.top.min(available.height);
+ let new_left = self.left.min(available.width);
Padding {
- top: self.top.min(available.height / 2.0),
- right: self.right.min(available.width / 2.0),
- bottom: self.bottom.min(available.height / 2.0),
- left: self.left.min(available.width / 2.0),
+ top: new_top,
+ bottom: self.bottom.min(available.height - new_top),
+ left: new_left,
+ right: self.right.min(available.width - new_left),
}
}
}
diff --git a/native/src/renderer.rs b/core/src/renderer.rs
index 2ac78982..d6247e39 100644
--- a/native/src/renderer.rs
+++ b/core/src/renderer.rs
@@ -1,6 +1,7 @@
//! Write your own renderer.
#[cfg(debug_assertions)]
mod null;
+
#[cfg(debug_assertions)]
pub use null::Null;
diff --git a/native/src/renderer/null.rs b/core/src/renderer/null.rs
index 150ee786..d93338ae 100644
--- a/native/src/renderer/null.rs
+++ b/core/src/renderer/null.rs
@@ -1,6 +1,6 @@
use crate::renderer::{self, Renderer};
use crate::text::{self, Text};
-use crate::{Background, Font, Point, Rectangle, Size, Theme, Vector};
+use crate::{Background, Font, Point, Rectangle, Size, Vector};
use std::borrow::Cow;
@@ -18,7 +18,7 @@ impl Null {
}
impl Renderer for Null {
- type Theme = Theme;
+ type Theme = ();
fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {}
diff --git a/native/src/shell.rs b/core/src/shell.rs
index 74a5c616..74a5c616 100644
--- a/native/src/shell.rs
+++ b/core/src/shell.rs
diff --git a/core/src/size.rs b/core/src/size.rs
index fbe940ef..7ef2f602 100644
--- a/core/src/size.rs
+++ b/core/src/size.rs
@@ -1,7 +1,7 @@
use crate::{Padding, Vector};
/// An amount of space in 2 dimensions.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Size<T = f32> {
/// The width.
pub width: T,
diff --git a/native/src/svg.rs b/core/src/svg.rs
index 9b98877a..9b98877a 100644
--- a/native/src/svg.rs
+++ b/core/src/svg.rs
diff --git a/native/src/text.rs b/core/src/text.rs
index 4c72abc3..4c72abc3 100644
--- a/native/src/text.rs
+++ b/core/src/text.rs
diff --git a/native/src/touch.rs b/core/src/touch.rs
index 18120644..18120644 100644
--- a/native/src/touch.rs
+++ b/core/src/touch.rs
diff --git a/native/src/widget.rs b/core/src/widget.rs
index 2b3ca7be..70e2c2d9 100644
--- a/native/src/widget.rs
+++ b/core/src/widget.rs
@@ -1,98 +1,21 @@
-//! Use the built-in widgets or create your own.
-//!
-//! # Built-in widgets
-//! Every built-in drawable widget has its own module with a `Renderer` trait
-//! that must be implemented by a [renderer] before being able to use it as
-//! a [`Widget`].
-//!
-//! # Custom widgets
-//! If you want to implement a custom widget, you simply need to implement the
-//! [`Widget`] trait. You can use the API of the built-in widgets as a guide or
-//! source of inspiration.
-//!
-//! [renderer]: crate::renderer
-pub mod button;
-pub mod checkbox;
-pub mod column;
-pub mod container;
-pub mod helpers;
-pub mod image;
+//! Create custom widgets and operate on them.
pub mod operation;
-pub mod pane_grid;
-pub mod pick_list;
-pub mod progress_bar;
-pub mod radio;
-pub mod row;
-pub mod rule;
-pub mod scrollable;
-pub mod slider;
-pub mod space;
-pub mod svg;
pub mod text;
-pub mod text_input;
-pub mod toggler;
-pub mod tooltip;
pub mod tree;
-pub mod vertical_slider;
-mod action;
mod id;
-#[doc(no_inline)]
-pub use button::Button;
-#[doc(no_inline)]
-pub use checkbox::Checkbox;
-#[doc(no_inline)]
-pub use column::Column;
-#[doc(no_inline)]
-pub use container::Container;
-#[doc(no_inline)]
-pub use helpers::*;
-#[doc(no_inline)]
-pub use image::Image;
-#[doc(no_inline)]
-pub use pane_grid::PaneGrid;
-#[doc(no_inline)]
-pub use pick_list::PickList;
-#[doc(no_inline)]
-pub use progress_bar::ProgressBar;
-#[doc(no_inline)]
-pub use radio::Radio;
-#[doc(no_inline)]
-pub use row::Row;
-#[doc(no_inline)]
-pub use rule::Rule;
-#[doc(no_inline)]
-pub use scrollable::Scrollable;
-#[doc(no_inline)]
-pub use slider::Slider;
-#[doc(no_inline)]
-pub use space::Space;
-#[doc(no_inline)]
-pub use svg::Svg;
-#[doc(no_inline)]
-pub use text::Text;
-#[doc(no_inline)]
-pub use text_input::TextInput;
-#[doc(no_inline)]
-pub use toggler::Toggler;
-#[doc(no_inline)]
-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;
pub use operation::Operation;
+pub use text::Text;
+pub use tree::Tree;
use crate::event::{self, Event};
-use crate::layout;
+use crate::layout::{self, Layout};
use crate::mouse;
use crate::overlay;
use crate::renderer;
-use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
+use crate::{Clipboard, Length, Point, Rectangle, Shell};
/// A component that displays information and allows interaction.
///
diff --git a/native/src/widget/id.rs b/core/src/widget/id.rs
index 4b8fedf1..ae739bb7 100644
--- a/native/src/widget/id.rs
+++ b/core/src/widget/id.rs
@@ -24,7 +24,7 @@ impl Id {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum Internal {
+enum Internal {
Unique(usize),
Custom(borrow::Cow<'static, str>),
}
diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs
new file mode 100644
index 00000000..ad188c36
--- /dev/null
+++ b/core/src/widget/operation.rs
@@ -0,0 +1,226 @@
+//! Query or update internal widget state.
+pub mod focusable;
+pub mod scrollable;
+pub mod text_input;
+
+pub use focusable::Focusable;
+pub use scrollable::Scrollable;
+pub use text_input::TextInput;
+
+use crate::widget::Id;
+
+use std::any::Any;
+use std::fmt;
+use std::rc::Rc;
+
+/// A piece of logic that can traverse the widget tree of an application in
+/// order to query or update some widget state.
+pub trait Operation<T> {
+ /// Operates on a widget that contains other widgets.
+ ///
+ /// The `operate_on_children` function can be called to return control to
+ /// the widget tree and keep traversing it.
+ fn container(
+ &mut self,
+ id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
+ );
+
+ /// Operates on a widget that can be focused.
+ fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {}
+
+ /// Operates on a widget that can be scrolled.
+ fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {}
+
+ /// Operates on a widget that has text input.
+ fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
+
+ /// Operates on a custom widget with some state.
+ fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {}
+
+ /// Finishes the [`Operation`] and returns its [`Outcome`].
+ fn finish(&self) -> Outcome<T> {
+ Outcome::None
+ }
+}
+
+/// The result of an [`Operation`].
+pub enum Outcome<T> {
+ /// The [`Operation`] produced no result.
+ None,
+
+ /// The [`Operation`] produced some result.
+ Some(T),
+
+ /// The [`Operation`] needs to be followed by another [`Operation`].
+ Chain(Box<dyn Operation<T>>),
+}
+
+impl<T> fmt::Debug for Outcome<T>
+where
+ T: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::None => write!(f, "Outcome::None"),
+ Self::Some(output) => write!(f, "Outcome::Some({output:?})"),
+ Self::Chain(_) => write!(f, "Outcome::Chain(...)"),
+ }
+ }
+}
+
+/// Maps the output of an [`Operation`] using the given function.
+pub fn map<A, B>(
+ operation: Box<dyn Operation<A>>,
+ f: impl Fn(A) -> B + 'static,
+) -> impl Operation<B>
+where
+ A: 'static,
+ B: 'static,
+{
+ #[allow(missing_debug_implementations)]
+ struct Map<A, B> {
+ operation: Box<dyn Operation<A>>,
+ f: Rc<dyn Fn(A) -> B>,
+ }
+
+ impl<A, B> Operation<B> for Map<A, B>
+ where
+ A: 'static,
+ B: 'static,
+ {
+ fn container(
+ &mut self,
+ id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
+ ) {
+ struct MapRef<'a, A> {
+ operation: &'a mut dyn Operation<A>,
+ }
+
+ impl<'a, A, B> Operation<B> for MapRef<'a, A> {
+ fn container(
+ &mut self,
+ id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
+ ) {
+ let Self { operation, .. } = self;
+
+ operation.container(id, &mut |operation| {
+ operate_on_children(&mut MapRef { operation });
+ });
+ }
+
+ fn scrollable(
+ &mut self,
+ state: &mut dyn Scrollable,
+ id: Option<&Id>,
+ ) {
+ self.operation.scrollable(state, id);
+ }
+
+ fn focusable(
+ &mut self,
+ state: &mut dyn Focusable,
+ id: Option<&Id>,
+ ) {
+ self.operation.focusable(state, id);
+ }
+
+ fn text_input(
+ &mut self,
+ state: &mut dyn TextInput,
+ id: Option<&Id>,
+ ) {
+ self.operation.text_input(state, id);
+ }
+
+ fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
+ self.operation.custom(state, id);
+ }
+ }
+
+ let Self { operation, .. } = self;
+
+ MapRef {
+ operation: operation.as_mut(),
+ }
+ .container(id, operate_on_children);
+ }
+
+ fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
+ self.operation.focusable(state, id);
+ }
+
+ fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) {
+ self.operation.scrollable(state, id);
+ }
+
+ fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ self.operation.text_input(state, id);
+ }
+
+ fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
+ self.operation.custom(state, id);
+ }
+
+ fn finish(&self) -> Outcome<B> {
+ match self.operation.finish() {
+ Outcome::None => Outcome::None,
+ Outcome::Some(output) => Outcome::Some((self.f)(output)),
+ Outcome::Chain(next) => Outcome::Chain(Box::new(Map {
+ operation: next,
+ f: self.f.clone(),
+ })),
+ }
+ }
+ }
+
+ Map {
+ operation,
+ f: Rc::new(f),
+ }
+}
+
+/// Produces an [`Operation`] that applies the given [`Operation`] to the
+/// children of a container with the given [`Id`].
+pub fn scope<T: 'static>(
+ target: Id,
+ operation: impl Operation<T> + 'static,
+) -> impl Operation<T> {
+ struct ScopedOperation<Message> {
+ target: Id,
+ operation: Box<dyn Operation<Message>>,
+ }
+
+ impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
+ fn container(
+ &mut self,
+ id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
+ ) {
+ if id == Some(&self.target) {
+ operate_on_children(self.operation.as_mut());
+ } else {
+ operate_on_children(self);
+ }
+ }
+
+ fn finish(&self) -> Outcome<Message> {
+ match self.operation.finish() {
+ Outcome::Chain(next) => {
+ Outcome::Chain(Box::new(ScopedOperation {
+ target: self.target.clone(),
+ operation: next,
+ }))
+ }
+ outcome => outcome,
+ }
+ }
+ }
+
+ ScopedOperation {
+ target,
+ operation: Box::new(operation),
+ }
+}
diff --git a/native/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs
index 312e4894..312e4894 100644
--- a/native/src/widget/operation/focusable.rs
+++ b/core/src/widget/operation/focusable.rs
diff --git a/native/src/widget/operation/scrollable.rs b/core/src/widget/operation/scrollable.rs
index 3b20631f..3b20631f 100644
--- a/native/src/widget/operation/scrollable.rs
+++ b/core/src/widget/operation/scrollable.rs
diff --git a/native/src/widget/operation/text_input.rs b/core/src/widget/operation/text_input.rs
index 4c773e99..4c773e99 100644
--- a/native/src/widget/operation/text_input.rs
+++ b/core/src/widget/operation/text_input.rs
diff --git a/native/src/widget/text.rs b/core/src/widget/text.rs
index aede754a..485bb542 100644
--- a/native/src/widget/text.rs
+++ b/core/src/widget/text.rs
@@ -4,27 +4,13 @@ use crate::layout;
use crate::renderer;
use crate::text;
use crate::widget::Tree;
-use crate::{Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget};
+use crate::{
+ Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget,
+};
use std::borrow::Cow;
-pub use iced_style::text::{Appearance, StyleSheet};
-
/// A paragraph of text.
-///
-/// # Example
-///
-/// ```
-/// # use iced_native::Color;
-/// #
-/// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
-/// #
-/// Text::new("I <3 iced!")
-/// .size(40)
-/// .style(Color::from([0.0, 0.0, 1.0]));
-/// ```
-///
-/// ![Text drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text.png?raw=true)
#[allow(missing_debug_implementations)]
pub struct Text<'a, Renderer>
where
@@ -211,7 +197,7 @@ pub fn draw<Renderer>(
alignment::Vertical::Bottom => bounds.y + bounds.height,
};
- renderer.fill_text(crate::text::Text {
+ renderer.fill_text(crate::Text {
content,
size: size.unwrap_or_else(|| renderer.default_size()),
bounds: Rectangle { x, y, ..bounds },
@@ -252,12 +238,40 @@ where
}
}
+impl<'a, Renderer> From<&'a str> for Text<'a, Renderer>
+where
+ Renderer: text::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ fn from(content: &'a str) -> Self {
+ Self::new(content)
+ }
+}
+
impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer>
where
Renderer: text::Renderer + 'a,
Renderer::Theme: StyleSheet,
{
- fn from(contents: &'a str) -> Self {
- Text::new(contents).into()
+ fn from(content: &'a str) -> Self {
+ Text::from(content).into()
}
}
+
+/// The style sheet of some text.
+pub trait StyleSheet {
+ /// The supported style of the [`StyleSheet`].
+ type Style: Default + Copy;
+
+ /// Produces the [`Appearance`] of some text.
+ fn appearance(&self, style: Self::Style) -> Appearance;
+}
+
+/// The apperance of some text.
+#[derive(Debug, Clone, Copy, Default)]
+pub struct Appearance {
+ /// The [`Color`] of the text.
+ ///
+ /// The default, `None`, means using the inherited color.
+ pub color: Option<Color>,
+}
diff --git a/native/src/widget/tree.rs b/core/src/widget/tree.rs
index 0af40c33..0af40c33 100644
--- a/native/src/widget/tree.rs
+++ b/core/src/widget/tree.rs
diff --git a/core/src/window.rs b/core/src/window.rs
new file mode 100644
index 00000000..d829a4b4
--- /dev/null
+++ b/core/src/window.rs
@@ -0,0 +1,10 @@
+//! Build window-based GUI applications.
+mod event;
+mod mode;
+mod redraw_request;
+mod user_attention;
+
+pub use event::Event;
+pub use mode::Mode;
+pub use redraw_request::RedrawRequest;
+pub use user_attention::UserAttention;
diff --git a/native/src/window/event.rs b/core/src/window/event.rs
index e2fb5e66..e2fb5e66 100644
--- a/native/src/window/event.rs
+++ b/core/src/window/event.rs
diff --git a/native/src/window/mode.rs b/core/src/window/mode.rs
index fdce8e23..fdce8e23 100644
--- a/native/src/window/mode.rs
+++ b/core/src/window/mode.rs
diff --git a/native/src/window/redraw_request.rs b/core/src/window/redraw_request.rs
index 3b4f0fd3..3b4f0fd3 100644
--- a/native/src/window/redraw_request.rs
+++ b/core/src/window/redraw_request.rs
diff --git a/native/src/window/user_attention.rs b/core/src/window/user_attention.rs
index b03dfeef..b03dfeef 100644
--- a/native/src/window/user_attention.rs
+++ b/core/src/window/user_attention.rs
diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs
index 7b6ea0e1..80ad0b5b 100644
--- a/examples/arc/src/main.rs
+++ b/examples/arc/src/main.rs
@@ -5,8 +5,8 @@ use iced::widget::canvas::{
self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke,
};
use iced::{
- Application, Command, Element, Length, Point, Rectangle, Settings,
- Subscription, Theme,
+ Application, Command, Element, Length, Point, Rectangle, Renderer,
+ Settings, Subscription, Theme,
};
pub fn main() -> iced::Result {
@@ -75,11 +75,12 @@ impl<Message> canvas::Program<Message> for Arc {
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
- let geometry = self.cache.draw(bounds.size(), |frame| {
+ let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
let palette = theme.palette();
let center = frame.center();
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index 7c3916d4..f1c83a16 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -64,7 +64,7 @@ mod bezier {
use iced::widget::canvas::{
self, Canvas, Cursor, Frame, Geometry, Path, Stroke,
};
- use iced::{Element, Length, Point, Rectangle, Theme};
+ use iced::{Element, Length, Point, Rectangle, Renderer, Theme};
#[derive(Default)]
pub struct State {
@@ -152,22 +152,26 @@ mod bezier {
fn draw(
&self,
state: &Self::State,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
cursor: Cursor,
) -> Vec<Geometry> {
- let content =
- self.state.cache.draw(bounds.size(), |frame: &mut Frame| {
+ let content = self.state.cache.draw(
+ renderer,
+ bounds.size(),
+ |frame: &mut Frame| {
Curve::draw_all(self.curves, frame);
frame.stroke(
&Path::rectangle(Point::ORIGIN, frame.size()),
Stroke::default().with_width(2.0),
);
- });
+ },
+ );
if let Some(pending) = state {
- let pending_curve = pending.draw(bounds, cursor);
+ let pending_curve = pending.draw(renderer, bounds, cursor);
vec![content, pending_curve]
} else {
@@ -216,8 +220,13 @@ mod bezier {
}
impl Pending {
- fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Geometry {
- let mut frame = Frame::new(bounds.size());
+ fn draw(
+ &self,
+ renderer: &Renderer,
+ bounds: Rectangle,
+ cursor: Cursor,
+ ) -> Geometry {
+ let mut frame = Frame::new(renderer, bounds.size());
if let Some(cursor_position) = cursor.position_in(&bounds) {
match *self {
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs
index a389c54f..6425e2da 100644
--- a/examples/clock/src/main.rs
+++ b/examples/clock/src/main.rs
@@ -4,8 +4,8 @@ use iced::widget::canvas::{
};
use iced::widget::{canvas, container};
use iced::{
- Application, Color, Command, Element, Length, Point, Rectangle, Settings,
- Subscription, Theme, Vector,
+ Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
+ Settings, Subscription, Theme, Vector,
};
pub fn main() -> iced::Result {
@@ -83,17 +83,18 @@ impl Application for Clock {
}
}
-impl<Message> canvas::Program<Message> for Clock {
+impl<Message> canvas::Program<Message, Renderer> for Clock {
type State = ();
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
- let clock = self.clock.draw(bounds.size(), |frame| {
+ let clock = self.clock.draw(renderer, bounds.size(), |frame| {
let center = frame.center();
let radius = frame.width().min(frame.height()) / 2.0;
diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs
index a2df36c2..5c4304ee 100644
--- a/examples/color_palette/src/main.rs
+++ b/examples/color_palette/src/main.rs
@@ -1,8 +1,8 @@
use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path};
use iced::widget::{column, row, text, Slider};
use iced::{
- alignment, Alignment, Color, Element, Length, Point, Rectangle, Sandbox,
- Settings, Size, Vector,
+ alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer,
+ Sandbox, Settings, Size, Vector,
};
use palette::{self, convert::FromColor, Hsl, Srgb};
use std::marker::PhantomData;
@@ -243,11 +243,12 @@ impl<Message> canvas::Program<Message> for Theme {
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
_theme: &iced::Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
- let theme = self.canvas_cache.draw(bounds.size(), |frame| {
+ let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| {
self.draw(frame);
});
diff --git a/examples/component/Cargo.toml b/examples/component/Cargo.toml
index dd435201..9db1e6b4 100644
--- a/examples/component/Cargo.toml
+++ b/examples/component/Cargo.toml
@@ -6,6 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = ["debug"] }
-iced_native = { path = "../../native" }
-iced_lazy = { path = "../../lazy" }
+iced = { path = "../..", features = ["debug", "lazy"] }
diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs
index c407bb06..09e5e4a2 100644
--- a/examples/component/src/main.rs
+++ b/examples/component/src/main.rs
@@ -47,9 +47,8 @@ impl Sandbox for Component {
mod numeric_input {
use iced::alignment::{self, Alignment};
- use iced::widget::{self, button, row, text, text_input};
- use iced::{Element, Length};
- use iced_lazy::{self, Component};
+ use iced::widget::{button, component, row, text, text_input, Component};
+ use iced::{Element, Length, Renderer};
pub struct NumericInput<Message> {
value: Option<u32>,
@@ -82,13 +81,7 @@ mod numeric_input {
}
}
- impl<Message, Renderer> Component<Message, Renderer> for NumericInput<Message>
- where
- Renderer: iced_native::text::Renderer + 'static,
- Renderer::Theme: widget::button::StyleSheet
- + widget::text_input::StyleSheet
- + widget::text::StyleSheet,
- {
+ impl<Message> Component<Message, Renderer> for NumericInput<Message> {
type State = ();
type Event = Event;
@@ -127,7 +120,8 @@ mod numeric_input {
.horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center),
)
- .width(50)
+ .width(40)
+ .height(40)
.on_press(on_press)
};
@@ -145,23 +139,18 @@ mod numeric_input {
.padding(10),
button("+", Event::IncrementPressed),
]
- .align_items(Alignment::Fill)
+ .align_items(Alignment::Center)
.spacing(10)
.into()
}
}
- impl<'a, Message, Renderer> From<NumericInput<Message>>
- for Element<'a, Message, Renderer>
+ impl<'a, Message> From<NumericInput<Message>> for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: 'static + iced_native::text::Renderer,
- Renderer::Theme: widget::button::StyleSheet
- + widget::text_input::StyleSheet
- + widget::text::StyleSheet,
{
fn from(numeric_input: NumericInput<Message>) -> Self {
- iced_lazy::component(numeric_input)
+ component(numeric_input)
}
}
}
diff --git a/examples/custom_quad/Cargo.toml b/examples/custom_quad/Cargo.toml
index 39154786..f097c2dd 100644
--- a/examples/custom_quad/Cargo.toml
+++ b/examples/custom_quad/Cargo.toml
@@ -6,5 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../.." }
-iced_native = { path = "../../native" }
+iced = { path = "../..", features = ["advanced"] }
diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs
index 6509887c..b07f42ce 100644
--- a/examples/custom_quad/src/main.rs
+++ b/examples/custom_quad/src/main.rs
@@ -1,9 +1,9 @@
//! 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};
+ use iced::advanced::layout::{self, Layout};
+ use iced::advanced::renderer;
+ use iced::advanced::widget::{self, Widget};
+ use iced::{Color, Element, Length, Point, Rectangle, Size};
pub struct CustomQuad {
size: f32,
diff --git a/examples/custom_widget/Cargo.toml b/examples/custom_widget/Cargo.toml
index 067aab4a..dda0efe8 100644
--- a/examples/custom_widget/Cargo.toml
+++ b/examples/custom_widget/Cargo.toml
@@ -6,5 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../.." }
-iced_native = { path = "../../native" }
+iced = { path = "../..", features = ["advanced"] }
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index f6bb3b1e..7854548c 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -9,10 +9,10 @@ mod circle {
// Of course, you can choose to make the implementation renderer-agnostic,
// if you wish to, by creating your own `Renderer` trait, which could be
// implemented by `iced_wgpu` and other renderers.
- 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};
+ use iced::advanced::layout::{self, Layout};
+ use iced::advanced::renderer;
+ use iced::advanced::widget::{self, Widget};
+ use iced::{Color, Element, Length, Point, Rectangle, Size};
pub struct Circle {
radius: f32,
diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml
index f38679ea..212832f4 100644
--- a/examples/download_progress/Cargo.toml
+++ b/examples/download_progress/Cargo.toml
@@ -7,8 +7,6 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["tokio"] }
-iced_native = { path = "../../native" }
-iced_futures = { path = "../../futures" }
[dependencies.reqwest]
version = "0.11"
diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs
index 39dd843f..5ff951b3 100644
--- a/examples/download_progress/src/download.rs
+++ b/examples/download_progress/src/download.rs
@@ -1,4 +1,4 @@
-use iced_native::subscription;
+use iced::subscription;
use std::hash::Hash;
diff --git a/examples/events/Cargo.toml b/examples/events/Cargo.toml
index 8c56e471..15ffc0af 100644
--- a/examples/events/Cargo.toml
+++ b/examples/events/Cargo.toml
@@ -7,4 +7,3 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["debug"] }
-iced_native = { path = "../../native" }
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index 1b97018e..7f3a5e1d 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -1,12 +1,13 @@
use iced::alignment;
use iced::executor;
+use iced::subscription;
use iced::widget::{button, checkbox, container, text, Column};
use iced::window;
+use iced::Event;
use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription,
Theme,
};
-use iced_native::Event;
pub fn main() -> iced::Result {
Events::run(Settings {
@@ -17,13 +18,13 @@ pub fn main() -> iced::Result {
#[derive(Debug, Default)]
struct Events {
- last: Vec<iced_native::Event>,
+ last: Vec<Event>,
enabled: bool,
}
#[derive(Debug, Clone)]
enum Message {
- EventOccurred(iced_native::Event),
+ EventOccurred(Event),
Toggled(bool),
Exit,
}
@@ -70,7 +71,7 @@ impl Application for Events {
}
fn subscription(&self) -> Subscription<Message> {
- iced_native::subscription::events().map(Message::EventOccurred)
+ subscription::events().map(Message::EventOccurred)
}
fn view(&self) -> Element<Message> {
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index ed911160..eab8908b 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -145,7 +145,7 @@ impl Application for GameOfLife {
self.grid
.view()
.map(move |message| Message::Grid(message, version)),
- controls
+ controls,
];
container(content)
@@ -211,8 +211,8 @@ mod grid {
Cache, Canvas, Cursor, Frame, Geometry, Path, Text,
};
use iced::{
- alignment, mouse, Color, Element, Length, Point, Rectangle, Size,
- Theme, Vector,
+ alignment, mouse, Color, Element, Length, Point, Rectangle, Renderer,
+ Size, Theme, Vector,
};
use rustc_hash::{FxHashMap, FxHashSet};
use std::future::Future;
@@ -536,13 +536,14 @@ mod grid {
fn draw(
&self,
_interaction: &Interaction,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
cursor: Cursor,
) -> Vec<Geometry> {
let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0);
- let life = self.life_cache.draw(bounds.size(), |frame| {
+ let life = self.life_cache.draw(renderer, bounds.size(), |frame| {
let background = Path::rectangle(Point::ORIGIN, frame.size());
frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B));
@@ -565,7 +566,7 @@ mod grid {
});
let overlay = {
- let mut frame = Frame::new(bounds.size());
+ let mut frame = Frame::new(renderer, bounds.size());
let hovered_cell =
cursor.position_in(&bounds).map(|position| {
@@ -626,38 +627,40 @@ mod grid {
if self.scaling < 0.2 || !self.show_lines {
vec![life, overlay]
} else {
- let grid = self.grid_cache.draw(bounds.size(), |frame| {
- frame.translate(center);
- frame.scale(self.scaling);
- frame.translate(self.translation);
- frame.scale(Cell::SIZE as f32);
-
- let region = self.visible_region(frame.size());
- let rows = region.rows();
- let columns = region.columns();
- let (total_rows, total_columns) =
- (rows.clone().count(), columns.clone().count());
- let width = 2.0 / Cell::SIZE as f32;
- let color = Color::from_rgb8(70, 74, 83);
-
- frame.translate(Vector::new(-width / 2.0, -width / 2.0));
+ let grid =
+ self.grid_cache.draw(renderer, bounds.size(), |frame| {
+ frame.translate(center);
+ frame.scale(self.scaling);
+ frame.translate(self.translation);
+ frame.scale(Cell::SIZE as f32);
- for row in region.rows() {
- frame.fill_rectangle(
- Point::new(*columns.start() as f32, row as f32),
- Size::new(total_columns as f32, width),
- color,
- );
- }
+ let region = self.visible_region(frame.size());
+ let rows = region.rows();
+ let columns = region.columns();
+ let (total_rows, total_columns) =
+ (rows.clone().count(), columns.clone().count());
+ let width = 2.0 / Cell::SIZE as f32;
+ let color = Color::from_rgb8(70, 74, 83);
+
+ frame
+ .translate(Vector::new(-width / 2.0, -width / 2.0));
+
+ for row in region.rows() {
+ frame.fill_rectangle(
+ Point::new(*columns.start() as f32, row as f32),
+ Size::new(total_columns as f32, width),
+ color,
+ );
+ }
- for column in region.columns() {
- frame.fill_rectangle(
- Point::new(column as f32, *rows.start() as f32),
- Size::new(width, total_rows as f32),
- color,
- );
- }
- });
+ for column in region.columns() {
+ frame.fill_rectangle(
+ Point::new(column as f32, *rows.start() as f32),
+ Size::new(width, total_rows as f32),
+ color,
+ );
+ }
+ });
vec![life, grid, overlay]
}
diff --git a/examples/geometry/Cargo.toml b/examples/geometry/Cargo.toml
index 22ede0e0..79fe52d5 100644
--- a/examples/geometry/Cargo.toml
+++ b/examples/geometry/Cargo.toml
@@ -6,6 +6,5 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../.." }
-iced_native = { path = "../../native" }
+iced = { path = "../..", features = ["advanced"] }
iced_graphics = { path = "../../graphics" }
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index 9bacce7f..5cb41184 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -1,23 +1,13 @@
//! This example showcases a simple native custom widget that renders using
//! arbitrary low-level geometry.
mod rainbow {
- // For now, to implement a custom native widget you will need to add
- // `iced_native` and `iced_wgpu` to your dependencies.
- //
- // Then, you simply need to define your widget type and implement the
- // `iced_native::Widget` trait with the `iced_wgpu::Renderer`.
- //
- // Of course, you can choose to make the implementation renderer-agnostic,
- // if you wish to, by creating your own `Renderer` trait, which could be
- // implemented by `iced_wgpu` and other renderers.
- use iced_graphics::renderer::{self, Renderer};
- use iced_graphics::triangle::ColoredVertex2D;
- use iced_graphics::{Backend, Primitive};
-
- use iced_native::layout;
- use iced_native::widget::{self, Widget};
- use iced_native::{
- Element, Layout, Length, Point, Rectangle, Size, Vector,
+ use iced_graphics::primitive::{ColoredVertex2D, Primitive};
+
+ use iced::advanced::layout::{self, Layout};
+ use iced::advanced::renderer;
+ use iced::advanced::widget::{self, Widget};
+ use iced::{
+ Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector,
};
#[derive(Debug, Clone, Copy, Default)]
@@ -27,10 +17,7 @@ mod rainbow {
Rainbow
}
- impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow
- where
- B: Backend,
- {
+ impl<Message> Widget<Message, Renderer> for Rainbow {
fn width(&self) -> Length {
Length::Fill
}
@@ -41,7 +28,7 @@ mod rainbow {
fn layout(
&self,
- _renderer: &Renderer<B, T>,
+ _renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let size = limits.width(Length::Fill).resolve(Size::ZERO);
@@ -52,15 +39,15 @@ mod rainbow {
fn draw(
&self,
_tree: &widget::Tree,
- renderer: &mut Renderer<B, T>,
- _theme: &T,
+ renderer: &mut Renderer,
+ _theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) {
- use iced_graphics::triangle::Mesh2D;
- use iced_native::Renderer as _;
+ use iced::advanced::Renderer as _;
+ use iced_graphics::primitive::Mesh2D;
let b = layout.bounds();
@@ -151,10 +138,7 @@ mod rainbow {
}
}
- impl<'a, Message, B, T> From<Rainbow> for Element<'a, Message, Renderer<B, T>>
- where
- B: Backend,
- {
+ impl<'a, Message> From<Rainbow> for Element<'a, Message, Renderer> {
fn from(rainbow: Rainbow) -> Self {
Self::new(rainbow)
}
diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml
index 200306aa..f6863cd3 100644
--- a/examples/integration/Cargo.toml
+++ b/examples/integration/Cargo.toml
@@ -7,7 +7,9 @@ publish = false
[dependencies]
iced_winit = { path = "../../winit" }
-iced_wgpu = { path = "../../wgpu", features = ["webgl"] }
+iced_wgpu = { path = "../../wgpu" }
+iced_widget = { path = "../../widget" }
+iced_renderer = { path = "../../renderer", features = ["wgpu", "tiny-skia"] }
env_logger = "0.8"
[target.'cfg(target_arch = "wasm32")'.dependencies]
diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs
index 533cb6e2..5849f730 100644
--- a/examples/integration/src/controls.rs
+++ b/examples/integration/src/controls.rs
@@ -1,6 +1,8 @@
use iced_wgpu::Renderer;
-use iced_winit::widget::{slider, text_input, Column, Row, Text};
-use iced_winit::{Alignment, Color, Command, Element, Length, Program};
+use iced_widget::{slider, text_input, Column, Row, Text};
+use iced_winit::core::{Alignment, Color, Element, Length};
+use iced_winit::runtime::{Command, Program};
+use iced_winit::style::Theme;
pub struct Controls {
background_color: Color,
@@ -27,7 +29,7 @@ impl Controls {
}
impl Program for Controls {
- type Renderer = Renderer;
+ type Renderer = Renderer<Theme>;
type Message = Message;
fn update(&mut self, message: Message) -> Command<Message> {
@@ -43,7 +45,7 @@ impl Program for Controls {
Command::none()
}
- fn view(&self) -> Element<Message, Renderer> {
+ fn view(&self) -> Element<Message, Renderer<Theme>> {
let background_color = self.background_color;
let text = &self.text;
diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs
index a7a90ced..9707eda7 100644
--- a/examples/integration/src/main.rs
+++ b/examples/integration/src/main.rs
@@ -4,11 +4,14 @@ mod scene;
use controls::Controls;
use scene::Scene;
-use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
-use iced_winit::{
- conversion, futures, program, renderer, winit, Clipboard, Color, Debug,
- Size,
-};
+use iced_wgpu::graphics::Viewport;
+use iced_wgpu::{wgpu, Backend, Renderer, Settings};
+use iced_winit::core::renderer;
+use iced_winit::core::{Color, Size};
+use iced_winit::runtime::program;
+use iced_winit::runtime::Debug;
+use iced_winit::style::Theme;
+use iced_winit::{conversion, futures, winit, Clipboard};
use winit::{
dpi::PhysicalPosition,
@@ -73,43 +76,45 @@ pub fn main() {
let instance = wgpu::Instance::new(backend);
let surface = unsafe { instance.create_surface(&window) };
- let (format, (device, queue)) = futures::executor::block_on(async {
- let adapter = wgpu::util::initialize_adapter_from_env_or_default(
- &instance,
- backend,
- Some(&surface),
- )
- .await
- .expect("No suitable GPU adapters found on the system!");
-
- let adapter_features = adapter.features();
-
- #[cfg(target_arch = "wasm32")]
- let needed_limits = wgpu::Limits::downlevel_webgl2_defaults()
- .using_resolution(adapter.limits());
-
- #[cfg(not(target_arch = "wasm32"))]
- let needed_limits = wgpu::Limits::default();
-
- (
- surface
- .get_supported_formats(&adapter)
- .first()
- .copied()
- .expect("Get preferred format"),
- adapter
- .request_device(
- &wgpu::DeviceDescriptor {
- label: None,
- features: adapter_features & wgpu::Features::default(),
- limits: needed_limits,
- },
- None,
- )
- .await
- .expect("Request device"),
- )
- });
+ let (format, (device, queue)) =
+ futures::futures::executor::block_on(async {
+ let adapter = wgpu::util::initialize_adapter_from_env_or_default(
+ &instance,
+ backend,
+ Some(&surface),
+ )
+ .await
+ .expect("No suitable GPU adapters found on the system!");
+
+ let adapter_features = adapter.features();
+
+ #[cfg(target_arch = "wasm32")]
+ let needed_limits = wgpu::Limits::downlevel_webgl2_defaults()
+ .using_resolution(adapter.limits());
+
+ #[cfg(not(target_arch = "wasm32"))]
+ let needed_limits = wgpu::Limits::default();
+
+ (
+ surface
+ .get_supported_formats(&adapter)
+ .first()
+ .copied()
+ .expect("Get preferred format"),
+ adapter
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ label: None,
+ features: adapter_features
+ & wgpu::Features::default(),
+ limits: needed_limits,
+ },
+ None,
+ )
+ .await
+ .expect("Request device"),
+ )
+ });
surface.configure(
&device,
@@ -188,7 +193,7 @@ pub fn main() {
viewport.scale_factor(),
),
&mut renderer,
- &iced_wgpu::Theme::Dark,
+ &Theme::Dark,
&renderer::Style { text_color: Color::WHITE },
&mut clipboard,
&mut debug,
diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs
index 3e41fbda..90c7efbf 100644
--- a/examples/integration/src/scene.rs
+++ b/examples/integration/src/scene.rs
@@ -1,5 +1,5 @@
use iced_wgpu::wgpu;
-use iced_winit::Color;
+use iced_winit::core::Color;
pub struct Scene {
pipeline: wgpu::RenderPipeline,
diff --git a/examples/lazy/Cargo.toml b/examples/lazy/Cargo.toml
index 79255c25..e03e89a9 100644
--- a/examples/lazy/Cargo.toml
+++ b/examples/lazy/Cargo.toml
@@ -6,5 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = ["debug"] }
-iced_lazy = { path = "../../lazy" }
+iced = { path = "../..", features = ["debug", "lazy"] }
diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs
index 6512106f..e1cdaefe 100644
--- a/examples/lazy/src/main.rs
+++ b/examples/lazy/src/main.rs
@@ -1,10 +1,9 @@
use iced::theme;
use iced::widget::{
- button, column, horizontal_space, pick_list, row, scrollable, text,
+ button, column, horizontal_space, lazy, pick_list, row, scrollable, text,
text_input,
};
use iced::{Element, Length, Sandbox, Settings};
-use iced_lazy::lazy;
use std::collections::HashSet;
use std::hash::Hash;
diff --git a/examples/modal/Cargo.toml b/examples/modal/Cargo.toml
index 8770acac..3ac61e6a 100644
--- a/examples/modal/Cargo.toml
+++ b/examples/modal/Cargo.toml
@@ -6,5 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = [] }
-iced_native = { path = "../../native" }
+iced = { path = "../..", features = ["advanced"] }
diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs
index 54555684..214ec97e 100644
--- a/examples/modal/src/main.rs
+++ b/examples/modal/src/main.rs
@@ -178,12 +178,15 @@ impl App {
}
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,
- };
+ use iced::advanced::layout::{self, Layout};
+ use iced::advanced::overlay;
+ use iced::advanced::renderer;
+ use iced::advanced::widget::{self, Widget};
+ use iced::advanced::{self, Clipboard, Shell};
+ use iced::alignment::Alignment;
+ use iced::event;
+ use iced::mouse;
+ use iced::{Color, Element, Event, Length, Point, Rectangle, Size};
/// A widget that centers a modal element over some base element
pub struct Modal<'a, Message, Renderer> {
@@ -218,14 +221,17 @@ mod modal {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Modal<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: advanced::Renderer,
Message: Clone,
{
- fn children(&self) -> Vec<Tree> {
- vec![Tree::new(&self.base), Tree::new(&self.modal)]
+ fn children(&self) -> Vec<widget::Tree> {
+ vec![
+ widget::Tree::new(&self.base),
+ widget::Tree::new(&self.modal),
+ ]
}
- fn diff(&self, tree: &mut Tree) {
+ fn diff(&self, tree: &mut widget::Tree) {
tree.diff_children(&[&self.base, &self.modal]);
}
@@ -247,7 +253,7 @@ mod modal {
fn on_event(
&mut self,
- state: &mut Tree,
+ state: &mut widget::Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
@@ -268,9 +274,9 @@ mod modal {
fn draw(
&self,
- state: &Tree,
+ state: &widget::Tree,
renderer: &mut Renderer,
- theme: &<Renderer as iced_native::Renderer>::Theme,
+ theme: &<Renderer as advanced::Renderer>::Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
@@ -289,7 +295,7 @@ mod modal {
fn overlay<'b>(
&'b mut self,
- state: &'b mut Tree,
+ state: &'b mut widget::Tree,
layout: Layout<'_>,
_renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
@@ -306,7 +312,7 @@ mod modal {
fn mouse_interaction(
&self,
- state: &Tree,
+ state: &widget::Tree,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
@@ -323,7 +329,7 @@ mod modal {
fn operate(
&self,
- state: &mut Tree,
+ state: &mut widget::Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
@@ -339,7 +345,7 @@ mod modal {
struct Overlay<'a, 'b, Message, Renderer> {
content: &'b mut Element<'a, Message, Renderer>,
- tree: &'b mut Tree,
+ tree: &'b mut widget::Tree,
size: Size,
on_blur: Option<Message>,
}
@@ -347,7 +353,7 @@ mod modal {
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: advanced::Renderer,
Message: Clone,
{
fn layout(
@@ -469,7 +475,7 @@ mod modal {
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: 'a + iced_native::Renderer,
+ Renderer: 'a + advanced::Renderer,
Message: 'a + Clone,
{
fn from(modal: Modal<'a, Message, Renderer>) -> Self {
diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs
index 28ed3e21..a43a2b2b 100644
--- a/examples/modern_art/src/main.rs
+++ b/examples/modern_art/src/main.rs
@@ -55,17 +55,18 @@ impl Application for ModernArt {
}
}
-impl<Message> canvas::Program<Message> for ModernArt {
+impl<Message> canvas::Program<Message, Renderer> for ModernArt {
type State = ();
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
- let geometry = self.cache.draw(bounds.size(), |frame| {
+ let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
let num_squares = thread_rng().gen_range(0..1200);
let mut i = 0;
diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs
index f5faae0f..7df6c929 100644
--- a/examples/multitouch/src/main.rs
+++ b/examples/multitouch/src/main.rs
@@ -6,7 +6,7 @@ use iced::widget::canvas::stroke::{self, Stroke};
use iced::widget::canvas::{self, Canvas, Cursor, Geometry};
use iced::{
executor, touch, window, Application, Color, Command, Element, Length,
- Point, Rectangle, Settings, Subscription, Theme,
+ Point, Rectangle, Renderer, Settings, Subscription, Theme,
};
use std::collections::HashMap;
@@ -95,7 +95,7 @@ impl Application for Multitouch {
}
}
-impl canvas::Program<Message> for State {
+impl canvas::Program<Message, Renderer> for State {
type State = ();
fn update(
@@ -125,11 +125,12 @@ impl canvas::Program<Message> for State {
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
- let fingerweb = self.cache.draw(bounds.size(), |frame| {
+ let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| {
if self.fingers.len() < 2 {
return;
}
diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml
index dfd6dfa9..4c0bf072 100644
--- a/examples/pane_grid/Cargo.toml
+++ b/examples/pane_grid/Cargo.toml
@@ -6,6 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = ["debug"] }
-iced_native = { path = "../../native" }
-iced_lazy = { path = "../../lazy" }
+iced = { path = "../..", features = ["debug", "lazy"] }
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index c9f1376c..dfb80853 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -1,14 +1,16 @@
use iced::alignment::{self, Alignment};
+use iced::event::{self, Event};
use iced::executor;
use iced::keyboard;
+use iced::subscription;
use iced::theme::{self, Theme};
use iced::widget::pane_grid::{self, PaneGrid};
-use iced::widget::{button, column, container, row, scrollable, text};
+use iced::widget::{
+ button, column, container, responsive, row, scrollable, text,
+};
use iced::{
Application, Color, Command, Element, Length, Settings, Size, Subscription,
};
-use iced_lazy::responsive;
-use iced_native::{event, subscription, Event};
pub fn main() -> iced::Result {
Example::run(Settings::default())
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index 7c858961..a3ade54f 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -254,7 +254,6 @@ impl Application for ScrollableDemo {
scroll_to_beginning_button(),
vertical_space(40),
]
- .align_items(Alignment::Fill)
.spacing(40),
horizontal_space(1200),
text("Horizontal - End!"),
diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs
index 1d25d171..4faac6d6 100644
--- a/examples/sierpinski_triangle/src/main.rs
+++ b/examples/sierpinski_triangle/src/main.rs
@@ -5,8 +5,8 @@ use iced::widget::canvas::event::{self, Event};
use iced::widget::canvas::{self, Canvas};
use iced::widget::{column, row, slider, text};
use iced::{
- Application, Color, Command, Length, Point, Rectangle, Settings, Size,
- Theme,
+ Application, Color, Command, Length, Point, Rectangle, Renderer, Settings,
+ Size, Theme,
};
use rand::Rng;
@@ -134,11 +134,12 @@ impl canvas::Program<Message> for SierpinskiGraph {
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: canvas::Cursor,
) -> Vec<canvas::Geometry> {
- let geom = self.cache.draw(bounds.size(), |frame| {
+ let geom = self.cache.draw(renderer, bounds.size(), |frame| {
frame.stroke(
&canvas::Path::rectangle(Point::ORIGIN, frame.size()),
canvas::Stroke::default(),
diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs
index 9a4ee754..f2606feb 100644
--- a/examples/solar_system/src/main.rs
+++ b/examples/solar_system/src/main.rs
@@ -15,8 +15,8 @@ use iced::widget::canvas::stroke::{self, Stroke};
use iced::widget::canvas::{Cursor, Path};
use iced::window;
use iced::{
- Application, Color, Command, Element, Length, Point, Rectangle, Settings,
- Size, Subscription, Vector,
+ Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
+ Settings, Size, Subscription, Vector,
};
use std::time::Instant;
@@ -156,24 +156,26 @@ impl<Message> canvas::Program<Message> for State {
fn draw(
&self,
_state: &Self::State,
+ renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<canvas::Geometry> {
use std::f32::consts::PI;
- let background = self.space_cache.draw(bounds.size(), |frame| {
- let stars = Path::new(|path| {
- for (p, size) in &self.stars {
- path.rectangle(*p, Size::new(*size, *size));
- }
- });
+ let background =
+ self.space_cache.draw(renderer, bounds.size(), |frame| {
+ let stars = Path::new(|path| {
+ for (p, size) in &self.stars {
+ path.rectangle(*p, Size::new(*size, *size));
+ }
+ });
- frame.translate(frame.center() - Point::ORIGIN);
- frame.fill(&stars, Color::WHITE);
- });
+ frame.translate(frame.center() - Point::ORIGIN);
+ frame.fill(&stars, Color::WHITE);
+ });
- let system = self.system_cache.draw(bounds.size(), |frame| {
+ let system = self.system_cache.draw(renderer, bounds.size(), |frame| {
let center = frame.center();
let sun = Path::circle(center, Self::SUN_RADIUS);
diff --git a/examples/toast/Cargo.toml b/examples/toast/Cargo.toml
index f1f986aa..f703572c 100644
--- a/examples/toast/Cargo.toml
+++ b/examples/toast/Cargo.toml
@@ -6,5 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = [] }
-iced_native = { path = "../../native" }
+iced = { path = "../..", features = ["advanced"] }
diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs
index e74b3ee6..78fb9de1 100644
--- a/examples/toast/src/main.rs
+++ b/examples/toast/src/main.rs
@@ -176,17 +176,23 @@ mod toast {
use std::fmt;
use std::time::{Duration, Instant};
+ use iced::advanced;
+ use iced::advanced::layout::{self, Layout};
+ use iced::advanced::overlay;
+ use iced::advanced::renderer;
+ use iced::advanced::widget::{self, Operation, Tree};
+ use iced::advanced::{Clipboard, Shell, Widget};
+ use iced::event::{self, Event};
+ use iced::mouse;
use iced::theme;
use iced::widget::{
button, column, container, horizontal_rule, horizontal_space, row, text,
};
+ use iced::window;
use iced::{
Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme,
Vector,
};
- use iced_native::widget::{tree, Operation, Tree};
- use iced_native::{event, layout, mouse, overlay, renderer, window};
- use iced_native::{Clipboard, Event, Layout, Shell, Widget};
pub const DEFAULT_TIMEOUT: u64 = 5;
@@ -324,13 +330,13 @@ mod toast {
self.content.as_widget().layout(renderer, limits)
}
- fn tag(&self) -> tree::Tag {
+ fn tag(&self) -> widget::tree::Tag {
struct Marker(Vec<Instant>);
- iced_native::widget::tree::Tag::of::<Marker>()
+ widget::tree::Tag::of::<Marker>()
}
- fn state(&self) -> tree::State {
- iced_native::widget::tree::State::new(Vec::<Option<Instant>>::new())
+ fn state(&self) -> widget::tree::State {
+ widget::tree::State::new(Vec::<Option<Instant>>::new())
}
fn children(&self) -> Vec<Tree> {
@@ -584,7 +590,7 @@ mod toast {
fn draw(
&self,
renderer: &mut Renderer,
- theme: &<Renderer as iced_native::Renderer>::Theme,
+ theme: &<Renderer as advanced::Renderer>::Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
@@ -613,7 +619,7 @@ mod toast {
&mut self,
layout: Layout<'_>,
renderer: &Renderer,
- operation: &mut dyn iced_native::widget::Operation<Message>,
+ operation: &mut dyn widget::Operation<Message>,
) {
operation.container(None, &mut |operation| {
self.toasts
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index 5df4e968..1cc18eca 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -1,7 +1,7 @@
use iced::alignment::{self, Alignment};
use iced::event::{self, Event};
use iced::font::{self, Font};
-use iced::keyboard;
+use iced::keyboard::{self, KeyCode, Modifiers};
use iced::subscription;
use iced::theme::{self, Theme};
use iced::widget::{
@@ -52,6 +52,7 @@ enum Message {
FilterChanged(Filter),
TaskMessage(usize, TaskMessage),
TabPressed { shift: bool },
+ ToggleFullscreen(window::Mode),
}
impl Application for Todos {
@@ -162,6 +163,9 @@ impl Application for Todos {
widget::focus_next()
}
}
+ Message::ToggleFullscreen(mode) => {
+ window::change_mode(mode)
+ }
_ => Command::none(),
};
@@ -272,6 +276,21 @@ impl Application for Todos {
) => Some(Message::TabPressed {
shift: modifiers.shift(),
}),
+ (
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ key_code,
+ modifiers: Modifiers::SHIFT,
+ }),
+ event::Status::Ignored,
+ ) => match key_code {
+ KeyCode::Up => {
+ Some(Message::ToggleFullscreen(window::Mode::Fullscreen))
+ }
+ KeyCode::Down => {
+ Some(Message::ToggleFullscreen(window::Mode::Windowed))
+ }
+ _ => None,
+ },
_ => None,
})
}
diff --git a/examples/url_handler/Cargo.toml b/examples/url_handler/Cargo.toml
index 63c7ec27..4dcff92d 100644
--- a/examples/url_handler/Cargo.toml
+++ b/examples/url_handler/Cargo.toml
@@ -7,4 +7,3 @@ publish = false
[dependencies]
iced = { path = "../.." }
-iced_native = { path = "../../native" }
diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs
index 3257b519..f63fa06a 100644
--- a/examples/url_handler/src/main.rs
+++ b/examples/url_handler/src/main.rs
@@ -1,12 +1,10 @@
+use iced::event::{Event, MacOS, PlatformSpecific};
use iced::executor;
+use iced::subscription;
use iced::widget::{container, text};
use iced::{
Application, Command, Element, Length, Settings, Subscription, Theme,
};
-use iced_native::{
- event::{MacOS, PlatformSpecific},
- Event,
-};
pub fn main() -> iced::Result {
App::run(Settings::default())
@@ -19,7 +17,7 @@ struct App {
#[derive(Debug, Clone)]
enum Message {
- EventOccurred(iced_native::Event),
+ EventOccurred(Event),
}
impl Application for App {
@@ -52,7 +50,7 @@ impl Application for App {
}
fn subscription(&self) -> Subscription<Message> {
- iced_native::subscription::events().map(Message::EventOccurred)
+ subscription::events().map(Message::EventOccurred)
}
fn view(&self) -> Element<Message> {
diff --git a/examples/websocket/Cargo.toml b/examples/websocket/Cargo.toml
index c25f067b..03b240c6 100644
--- a/examples/websocket/Cargo.toml
+++ b/examples/websocket/Cargo.toml
@@ -7,8 +7,6 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["tokio", "debug"] }
-iced_native = { path = "../../native" }
-iced_futures = { path = "../../futures" }
once_cell = "1.15"
[dependencies.async-tungstenite]
diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs
index e74768a6..122c20db 100644
--- a/examples/websocket/src/echo.rs
+++ b/examples/websocket/src/echo.rs
@@ -1,7 +1,7 @@
pub mod server;
-use iced_futures::futures;
-use iced_native::subscription::{self, Subscription};
+use iced::futures;
+use iced::subscription::{self, Subscription};
use futures::channel::mpsc;
use futures::sink::SinkExt;
diff --git a/examples/websocket/src/echo/server.rs b/examples/websocket/src/echo/server.rs
index dd234984..168a635e 100644
--- a/examples/websocket/src/echo/server.rs
+++ b/examples/websocket/src/echo/server.rs
@@ -1,4 +1,4 @@
-use iced_futures::futures;
+use iced::futures;
use futures::channel::mpsc;
use futures::{SinkExt, StreamExt};
diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs
index ccd9c815..e617b8ce 100644
--- a/examples/websocket/src/main.rs
+++ b/examples/websocket/src/main.rs
@@ -146,7 +146,9 @@ impl Application for WebSocket {
}
}
- row![input, button].spacing(10).align_items(Alignment::Fill)
+ row![input, button]
+ .spacing(10)
+ .align_items(Alignment::Center)
};
column![message_log, new_message_input]
diff --git a/futures/Cargo.toml b/futures/Cargo.toml
index e4d355ee..411e7c2a 100644
--- a/futures/Cargo.toml
+++ b/futures/Cargo.toml
@@ -16,6 +16,10 @@ thread-pool = ["futures/thread-pool"]
[dependencies]
log = "0.4"
+[dependencies.iced_core]
+version = "0.8"
+path = "../core"
+
[dependencies.futures]
version = "0.3"
diff --git a/futures/src/backend/native/async_std.rs b/futures/src/backend/native/async_std.rs
index b324dbf1..52b0e914 100644
--- a/futures/src/backend/native/async_std.rs
+++ b/futures/src/backend/native/async_std.rs
@@ -18,28 +18,26 @@ impl crate::Executor for Executor {
pub mod time {
//! Listen and react to time.
+ use crate::core::Hasher;
use crate::subscription::{self, Subscription};
/// Returns a [`Subscription`] that produces messages at a set interval.
///
/// The first message is produced after a `duration`, and then continues to
/// produce more messages every `duration` after that.
- pub fn every<H: std::hash::Hasher, E>(
+ pub fn every(
duration: std::time::Duration,
- ) -> Subscription<H, E, std::time::Instant> {
+ ) -> Subscription<std::time::Instant> {
Subscription::from_recipe(Every(duration))
}
#[derive(Debug)]
struct Every(std::time::Duration);
- impl<H, E> subscription::Recipe<H, E> for Every
- where
- H: std::hash::Hasher,
- {
+ impl subscription::Recipe for Every {
type Output = std::time::Instant;
- fn hash(&self, state: &mut H) {
+ fn hash(&self, state: &mut Hasher) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
@@ -48,7 +46,7 @@ pub mod time {
fn stream(
self: Box<Self>,
- _input: futures::stream::BoxStream<'static, E>,
+ _input: subscription::EventStream,
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;
diff --git a/futures/src/backend/native/smol.rs b/futures/src/backend/native/smol.rs
index d5201cde..30bc8291 100644
--- a/futures/src/backend/native/smol.rs
+++ b/futures/src/backend/native/smol.rs
@@ -19,28 +19,26 @@ impl crate::Executor for Executor {
pub mod time {
//! Listen and react to time.
+ use crate::core::Hasher;
use crate::subscription::{self, Subscription};
/// Returns a [`Subscription`] that produces messages at a set interval.
///
/// The first message is produced after a `duration`, and then continues to
/// produce more messages every `duration` after that.
- pub fn every<H: std::hash::Hasher, E>(
+ pub fn every(
duration: std::time::Duration,
- ) -> Subscription<H, E, std::time::Instant> {
+ ) -> Subscription<std::time::Instant> {
Subscription::from_recipe(Every(duration))
}
#[derive(Debug)]
struct Every(std::time::Duration);
- impl<H, E> subscription::Recipe<H, E> for Every
- where
- H: std::hash::Hasher,
- {
+ impl subscription::Recipe for Every {
type Output = std::time::Instant;
- fn hash(&self, state: &mut H) {
+ fn hash(&self, state: &mut Hasher) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
@@ -49,7 +47,7 @@ pub mod time {
fn stream(
self: Box<Self>,
- _input: futures::stream::BoxStream<'static, E>,
+ _input: subscription::EventStream,
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;
diff --git a/futures/src/backend/native/tokio.rs b/futures/src/backend/native/tokio.rs
index dd818bd1..4698a105 100644
--- a/futures/src/backend/native/tokio.rs
+++ b/futures/src/backend/native/tokio.rs
@@ -22,28 +22,26 @@ impl crate::Executor for Executor {
pub mod time {
//! Listen and react to time.
+ use crate::core::Hasher;
use crate::subscription::{self, Subscription};
/// Returns a [`Subscription`] that produces messages at a set interval.
///
/// The first message is produced after a `duration`, and then continues to
/// produce more messages every `duration` after that.
- pub fn every<H: std::hash::Hasher, E>(
+ pub fn every(
duration: std::time::Duration,
- ) -> Subscription<H, E, std::time::Instant> {
+ ) -> Subscription<std::time::Instant> {
Subscription::from_recipe(Every(duration))
}
#[derive(Debug)]
struct Every(std::time::Duration);
- impl<H, E> subscription::Recipe<H, E> for Every
- where
- H: std::hash::Hasher,
- {
+ impl subscription::Recipe for Every {
type Output = std::time::Instant;
- fn hash(&self, state: &mut H) {
+ fn hash(&self, state: &mut Hasher) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
@@ -52,7 +50,7 @@ pub mod time {
fn stream(
self: Box<Self>,
- _input: futures::stream::BoxStream<'static, E>,
+ _input: subscription::EventStream,
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;
diff --git a/futures/src/backend/wasm/wasm_bindgen.rs b/futures/src/backend/wasm/wasm_bindgen.rs
index b726501a..2666f1b4 100644
--- a/futures/src/backend/wasm/wasm_bindgen.rs
+++ b/futures/src/backend/wasm/wasm_bindgen.rs
@@ -16,6 +16,7 @@ impl crate::Executor for Executor {
pub mod time {
//! Listen and react to time.
+ use crate::core::Hasher;
use crate::subscription::{self, Subscription};
use crate::BoxStream;
@@ -23,22 +24,19 @@ pub mod time {
///
/// The first message is produced after a `duration`, and then continues to
/// produce more messages every `duration` after that.
- pub fn every<H: std::hash::Hasher, E>(
+ pub fn every(
duration: std::time::Duration,
- ) -> Subscription<H, E, wasm_timer::Instant> {
+ ) -> Subscription<wasm_timer::Instant> {
Subscription::from_recipe(Every(duration))
}
#[derive(Debug)]
struct Every(std::time::Duration);
- impl<H, E> subscription::Recipe<H, E> for Every
- where
- H: std::hash::Hasher,
- {
+ impl subscription::Recipe for Every {
type Output = wasm_timer::Instant;
- fn hash(&self, state: &mut H) {
+ fn hash(&self, state: &mut Hasher) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
@@ -47,7 +45,7 @@ pub mod time {
fn stream(
self: Box<Self>,
- _input: BoxStream<E>,
+ _input: subscription::EventStream,
) -> BoxStream<Self::Output> {
use futures::stream::StreamExt;
diff --git a/futures/src/command.rs b/futures/src/command.rs
deleted file mode 100644
index 3d1ec3f9..00000000
--- a/futures/src/command.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-/// A set of asynchronous actions to be performed by some runtime.
-#[must_use = "`Command` must be returned to runtime to take effect"]
-#[derive(Debug)]
-pub struct Command<T>(Internal<T>);
-
-#[derive(Debug)]
-enum Internal<T> {
- None,
- Single(T),
- Batch(Vec<T>),
-}
-
-impl<T> Command<T> {
- /// Creates an empty [`Command`].
- ///
- /// In other words, a [`Command`] that does nothing.
- pub const fn none() -> Self {
- Self(Internal::None)
- }
-
- /// Creates a [`Command`] that performs a single action.
- pub const fn single(action: T) -> Self {
- Self(Internal::Single(action))
- }
-
- /// Creates a [`Command`] that performs the actions of all the given
- /// commands.
- ///
- /// Once this command is run, all the commands will be executed at once.
- pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
- let mut batch = Vec::new();
-
- for Command(command) in commands {
- match command {
- Internal::None => {}
- Internal::Single(command) => batch.push(command),
- Internal::Batch(commands) => batch.extend(commands),
- }
- }
-
- Self(Internal::Batch(batch))
- }
-
- /// Applies a transformation to the result of a [`Command`].
- pub fn map<A>(self, f: impl Fn(T) -> A) -> Command<A>
- where
- T: 'static,
- {
- let Command(command) = self;
-
- match command {
- Internal::None => Command::none(),
- Internal::Single(action) => Command::single(f(action)),
- Internal::Batch(batch) => {
- Command(Internal::Batch(batch.into_iter().map(f).collect()))
- }
- }
- }
-
- /// Returns all of the actions of the [`Command`].
- pub fn actions(self) -> Vec<T> {
- let Command(command) = self;
-
- match command {
- Internal::None => Vec::new(),
- Internal::Single(action) => vec![action],
- Internal::Batch(batch) => batch,
- }
- }
-}
diff --git a/futures/src/lib.rs b/futures/src/lib.rs
index c0982db7..397fc2d2 100644
--- a/futures/src/lib.rs
+++ b/futures/src/lib.rs
@@ -18,8 +18,8 @@
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub use futures;
+pub use iced_core as core;
-mod command;
mod maybe_send;
mod runtime;
@@ -27,7 +27,6 @@ pub mod backend;
pub mod executor;
pub mod subscription;
-pub use command::Command;
pub use executor::Executor;
pub use maybe_send::MaybeSend;
pub use platform::*;
diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs
index 24f9f241..2241a494 100644
--- a/futures/src/runtime.rs
+++ b/futures/src/runtime.rs
@@ -1,6 +1,7 @@
//! Run commands and keep track of subscriptions.
+use crate::core::event::{self, Event};
use crate::subscription;
-use crate::{BoxFuture, Executor, MaybeSend, Subscription};
+use crate::{BoxFuture, Executor, MaybeSend};
use futures::{channel::mpsc, Sink};
use std::marker::PhantomData;
@@ -12,18 +13,15 @@ use std::marker::PhantomData;
///
/// [`Command`]: crate::Command
#[derive(Debug)]
-pub struct Runtime<Hasher, Event, Executor, Sender, Message> {
+pub struct Runtime<Executor, Sender, Message> {
executor: Executor,
sender: Sender,
- subscriptions: subscription::Tracker<Hasher, Event>,
+ subscriptions: subscription::Tracker,
_message: PhantomData<Message>,
}
-impl<Hasher, Event, Executor, Sender, Message>
- Runtime<Hasher, Event, Executor, Sender, Message>
+impl<Executor, Sender, Message> Runtime<Executor, Sender, Message>
where
- Hasher: std::hash::Hasher + Default,
- Event: Send + Clone + 'static,
Executor: self::Executor,
Sender: Sink<Message, Error = mpsc::SendError>
+ Unpin
@@ -79,7 +77,9 @@ where
/// [`Tracker::update`]: subscription::Tracker::update
pub fn track(
&mut self,
- subscription: Subscription<Hasher, Event, Message>,
+ recipes: impl IntoIterator<
+ Item = Box<dyn subscription::Recipe<Output = Message>>,
+ >,
) {
let Runtime {
executor,
@@ -88,8 +88,9 @@ where
..
} = self;
- let futures = executor
- .enter(|| subscriptions.update(subscription, sender.clone()));
+ let futures = executor.enter(|| {
+ subscriptions.update(recipes.into_iter(), sender.clone())
+ });
for future in futures {
executor.spawn(future);
@@ -102,7 +103,7 @@ where
/// See [`Tracker::broadcast`] to learn more.
///
/// [`Tracker::broadcast`]: subscription::Tracker::broadcast
- pub fn broadcast(&mut self, event: Event) {
- self.subscriptions.broadcast(event);
+ pub fn broadcast(&mut self, event: Event, status: event::Status) {
+ self.subscriptions.broadcast(event, status);
}
}
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index fe53fd7e..876f29c2 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -3,7 +3,18 @@ mod tracker;
pub use tracker::Tracker;
-use crate::BoxStream;
+use crate::core::event::{self, Event};
+use crate::core::window;
+use crate::core::Hasher;
+use crate::futures::{Future, Stream};
+use crate::{BoxStream, MaybeSend};
+
+use std::hash::Hash;
+
+/// A stream of runtime events.
+///
+/// It is the input of a [`Subscription`].
+pub type EventStream = BoxStream<(Event, event::Status)>;
/// A request to listen to external events.
///
@@ -16,19 +27,13 @@ use crate::BoxStream;
/// For instance, you can use a [`Subscription`] to listen to a WebSocket
/// connection, keyboard presses, mouse events, time ticks, etc.
///
-/// This type is normally aliased by runtimes with a specific `Event` and/or
-/// `Hasher`.
-///
/// [`Command`]: crate::Command
#[must_use = "`Subscription` must be returned to runtime to take effect"]
-pub struct Subscription<Hasher, Event, Output> {
- recipes: Vec<Box<dyn Recipe<Hasher, Event, Output = Output>>>,
+pub struct Subscription<Message> {
+ recipes: Vec<Box<dyn Recipe<Output = Message>>>,
}
-impl<H, E, O> Subscription<H, E, O>
-where
- H: std::hash::Hasher,
-{
+impl<Message> Subscription<Message> {
/// Returns an empty [`Subscription`] that will not produce any output.
pub fn none() -> Self {
Self {
@@ -38,7 +43,7 @@ where
/// Creates a [`Subscription`] from a [`Recipe`] describing it.
pub fn from_recipe(
- recipe: impl Recipe<H, E, Output = O> + 'static,
+ recipe: impl Recipe<Output = Message> + 'static,
) -> Self {
Self {
recipes: vec![Box::new(recipe)],
@@ -48,7 +53,7 @@ where
/// Batches all the provided subscriptions and returns the resulting
/// [`Subscription`].
pub fn batch(
- subscriptions: impl IntoIterator<Item = Subscription<H, E, O>>,
+ subscriptions: impl IntoIterator<Item = Subscription<Message>>,
) -> Self {
Self {
recipes: subscriptions
@@ -59,18 +64,16 @@ where
}
/// Returns the different recipes of the [`Subscription`].
- pub fn recipes(self) -> Vec<Box<dyn Recipe<H, E, Output = O>>> {
+ pub fn into_recipes(self) -> Vec<Box<dyn Recipe<Output = Message>>> {
self.recipes
}
/// Adds a value to the [`Subscription`] context.
///
/// The value will be part of the identity of a [`Subscription`].
- pub fn with<T>(mut self, value: T) -> Subscription<H, E, (T, O)>
+ pub fn with<T>(mut self, value: T) -> Subscription<(T, Message)>
where
- H: 'static,
- E: 'static,
- O: 'static,
+ Message: 'static,
T: std::hash::Hash + Clone + Send + Sync + 'static,
{
Subscription {
@@ -79,18 +82,16 @@ where
.drain(..)
.map(|recipe| {
Box::new(With::new(recipe, value.clone()))
- as Box<dyn Recipe<H, E, Output = (T, O)>>
+ as Box<dyn Recipe<Output = (T, Message)>>
})
.collect(),
}
}
/// Transforms the [`Subscription`] output with the given function.
- pub fn map<A>(mut self, f: fn(O) -> A) -> Subscription<H, E, A>
+ pub fn map<A>(mut self, f: fn(Message) -> A) -> Subscription<A>
where
- H: 'static,
- E: 'static,
- O: 'static,
+ Message: 'static,
A: 'static,
{
Subscription {
@@ -98,15 +99,14 @@ where
.recipes
.drain(..)
.map(|recipe| {
- Box::new(Map::new(recipe, f))
- as Box<dyn Recipe<H, E, Output = A>>
+ Box::new(Map::new(recipe, f)) as Box<dyn Recipe<Output = A>>
})
.collect(),
}
}
}
-impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
+impl<Message> std::fmt::Debug for Subscription<Message> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Subscription").finish()
}
@@ -129,7 +129,7 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// [examples]: https://github.com/iced-rs/iced/tree/0.8/examples
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.8/examples/download_progress
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.8/examples/stopwatch
-pub trait Recipe<Hasher: std::hash::Hasher, Event> {
+pub trait Recipe {
/// The events that will be produced by a [`Subscription`] with this
/// [`Recipe`].
type Output;
@@ -141,45 +141,33 @@ pub trait Recipe<Hasher: std::hash::Hasher, Event> {
/// Executes the [`Recipe`] and produces the stream of events of its
/// [`Subscription`].
- ///
- /// It receives some stream of generic events, which is normally defined by
- /// shells.
- fn stream(
- self: Box<Self>,
- input: BoxStream<Event>,
- ) -> BoxStream<Self::Output>;
+ fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output>;
}
-struct Map<Hasher, Event, A, B> {
- recipe: Box<dyn Recipe<Hasher, Event, Output = A>>,
+struct Map<A, B> {
+ recipe: Box<dyn Recipe<Output = A>>,
mapper: fn(A) -> B,
}
-impl<H, E, A, B> Map<H, E, A, B> {
- fn new(
- recipe: Box<dyn Recipe<H, E, Output = A>>,
- mapper: fn(A) -> B,
- ) -> Self {
+impl<A, B> Map<A, B> {
+ fn new(recipe: Box<dyn Recipe<Output = A>>, mapper: fn(A) -> B) -> Self {
Map { recipe, mapper }
}
}
-impl<H, E, A, B> Recipe<H, E> for Map<H, E, A, B>
+impl<A, B> Recipe for Map<A, B>
where
A: 'static,
B: 'static,
- H: std::hash::Hasher,
{
type Output = B;
- fn hash(&self, state: &mut H) {
- use std::hash::Hash;
-
+ fn hash(&self, state: &mut Hasher) {
self.recipe.hash(state);
self.mapper.hash(state);
}
- fn stream(self: Box<Self>, input: BoxStream<E>) -> BoxStream<Self::Output> {
+ fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
use futures::StreamExt;
let mapper = self.mapper;
@@ -188,34 +176,31 @@ where
}
}
-struct With<Hasher, Event, A, B> {
- recipe: Box<dyn Recipe<Hasher, Event, Output = A>>,
+struct With<A, B> {
+ recipe: Box<dyn Recipe<Output = A>>,
value: B,
}
-impl<H, E, A, B> With<H, E, A, B> {
- fn new(recipe: Box<dyn Recipe<H, E, Output = A>>, value: B) -> Self {
+impl<A, B> With<A, B> {
+ fn new(recipe: Box<dyn Recipe<Output = A>>, value: B) -> Self {
With { recipe, value }
}
}
-impl<H, E, A, B> Recipe<H, E> for With<H, E, A, B>
+impl<A, B> Recipe for With<A, B>
where
A: 'static,
B: 'static + std::hash::Hash + Clone + Send + Sync,
- H: std::hash::Hasher,
{
type Output = (B, A);
- fn hash(&self, state: &mut H) {
- use std::hash::Hash;
-
+ fn hash(&self, state: &mut Hasher) {
std::any::TypeId::of::<B>().hash(state);
self.value.hash(state);
self.recipe.hash(state);
}
- fn stream(self: Box<Self>, input: BoxStream<E>) -> BoxStream<Self::Output> {
+ fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
use futures::StreamExt;
let value = self.value;
@@ -227,3 +212,222 @@ where
)
}
}
+
+/// Returns a [`Subscription`] to all the ignored runtime events.
+///
+/// This subscription will notify your application of any [`Event`] that was
+/// not captured by any widget.
+pub fn events() -> Subscription<Event> {
+ events_with(|event, status| match status {
+ event::Status::Ignored => Some(event),
+ event::Status::Captured => None,
+ })
+}
+
+/// Returns a [`Subscription`] that filters all the runtime events with the
+/// provided function, producing messages accordingly.
+///
+/// This subscription will call the provided function for every [`Event`]
+/// handled by the runtime. If the function:
+///
+/// - Returns `None`, the [`Event`] will be discarded.
+/// - Returns `Some` message, the `Message` will be produced.
+pub fn events_with<Message>(
+ f: fn(Event, event::Status) -> Option<Message>,
+) -> Subscription<Message>
+where
+ Message: 'static + MaybeSend,
+{
+ #[derive(Hash)]
+ struct EventsWith;
+
+ Subscription::from_recipe(Runner {
+ id: (EventsWith, f),
+ spawn: move |events| {
+ use futures::future;
+ use futures::stream::StreamExt;
+
+ events.filter_map(move |(event, status)| {
+ future::ready(match event {
+ Event::Window(window::Event::RedrawRequested(_)) => None,
+ _ => f(event, status),
+ })
+ })
+ },
+ })
+}
+
+/// Returns a [`Subscription`] that produces a message for every runtime event,
+/// including the redraw request events.
+///
+/// **Warning:** This [`Subscription`], if unfiltered, may produce messages in
+/// an infinite loop.
+pub fn raw_events<Message>(
+ f: fn(Event, event::Status) -> Option<Message>,
+) -> Subscription<Message>
+where
+ Message: 'static + MaybeSend,
+{
+ #[derive(Hash)]
+ struct RawEvents;
+
+ Subscription::from_recipe(Runner {
+ id: (RawEvents, f),
+ spawn: move |events| {
+ use futures::future;
+ use futures::stream::StreamExt;
+
+ events.filter_map(move |(event, status)| {
+ future::ready(f(event, status))
+ })
+ },
+ })
+}
+
+/// Returns a [`Subscription`] that will call the given function to create and
+/// asynchronously run the given [`Stream`].
+pub fn run<S, Message>(builder: fn() -> S) -> Subscription<Message>
+where
+ S: Stream<Item = Message> + MaybeSend + 'static,
+ Message: 'static,
+{
+ Subscription::from_recipe(Runner {
+ id: builder,
+ spawn: move |_| builder(),
+ })
+}
+
+/// Returns a [`Subscription`] that will create and asynchronously run the
+/// given [`Stream`].
+///
+/// The `id` will be used to uniquely identify the [`Subscription`].
+pub fn run_with_id<I, S, Message>(id: I, stream: S) -> Subscription<Message>
+where
+ I: Hash + 'static,
+ S: Stream<Item = Message> + MaybeSend + 'static,
+ Message: 'static,
+{
+ Subscription::from_recipe(Runner {
+ id,
+ spawn: move |_| stream,
+ })
+}
+
+/// Returns a [`Subscription`] that will create and asynchronously run a
+/// [`Stream`] that will call the provided closure to produce every `Message`.
+///
+/// The `id` will be used to uniquely identify the [`Subscription`].
+///
+/// # Creating an asynchronous worker with bidirectional communication
+/// You can leverage this helper to create a [`Subscription`] that spawns
+/// an asynchronous worker in the background and establish a channel of
+/// communication with an `iced` application.
+///
+/// You can achieve this by creating an `mpsc` channel inside the closure
+/// and returning the `Sender` as a `Message` for the `Application`:
+///
+/// ```
+/// use iced_futures::subscription::{self, Subscription};
+/// use iced_futures::futures;
+///
+/// use futures::channel::mpsc;
+///
+/// pub enum Event {
+/// Ready(mpsc::Sender<Input>),
+/// WorkFinished,
+/// // ...
+/// }
+///
+/// enum Input {
+/// DoSomeWork,
+/// // ...
+/// }
+///
+/// enum State {
+/// Starting,
+/// Ready(mpsc::Receiver<Input>),
+/// }
+///
+/// fn some_worker() -> Subscription<Event> {
+/// struct SomeWorker;
+///
+/// subscription::unfold(std::any::TypeId::of::<SomeWorker>(), State::Starting, |state| async move {
+/// match state {
+/// State::Starting => {
+/// // Create channel
+/// let (sender, receiver) = mpsc::channel(100);
+///
+/// (Some(Event::Ready(sender)), State::Ready(receiver))
+/// }
+/// State::Ready(mut receiver) => {
+/// use futures::StreamExt;
+///
+/// // Read next input sent from `Application`
+/// let input = receiver.select_next_some().await;
+///
+/// match input {
+/// Input::DoSomeWork => {
+/// // Do some async work...
+///
+/// // Finally, we can optionally return a message to tell the
+/// // `Application` the work is done
+/// (Some(Event::WorkFinished), State::Ready(receiver))
+/// }
+/// }
+/// }
+/// }
+/// })
+/// }
+/// ```
+///
+/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
+/// connection open.
+///
+/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.8/examples/websocket
+pub fn unfold<I, T, Fut, Message>(
+ id: I,
+ initial: T,
+ mut f: impl FnMut(T) -> Fut + MaybeSend + Sync + 'static,
+) -> Subscription<Message>
+where
+ I: Hash + 'static,
+ T: MaybeSend + 'static,
+ Fut: Future<Output = (Option<Message>, T)> + MaybeSend + 'static,
+ Message: 'static + MaybeSend,
+{
+ use futures::future::{self, FutureExt};
+ use futures::stream::StreamExt;
+
+ run_with_id(
+ id,
+ futures::stream::unfold(initial, move |state| f(state).map(Some))
+ .filter_map(future::ready),
+ )
+}
+
+struct Runner<I, F, S, Message>
+where
+ F: FnOnce(EventStream) -> S,
+ S: Stream<Item = Message>,
+{
+ id: I,
+ spawn: F,
+}
+
+impl<I, S, F, Message> Recipe for Runner<I, F, S, Message>
+where
+ I: Hash + 'static,
+ F: FnOnce(EventStream) -> S,
+ S: Stream<Item = Message> + MaybeSend + 'static,
+{
+ type Output = Message;
+
+ fn hash(&self, state: &mut Hasher) {
+ std::any::TypeId::of::<I>().hash(state);
+ self.id.hash(state);
+ }
+
+ fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
+ crate::boxed_stream((self.spawn)(input))
+ }
+}
diff --git a/futures/src/subscription/tracker.rs b/futures/src/subscription/tracker.rs
index 9fe110b0..ae71cd25 100644
--- a/futures/src/subscription/tracker.rs
+++ b/futures/src/subscription/tracker.rs
@@ -1,38 +1,35 @@
-use crate::{BoxFuture, MaybeSend, Subscription};
+use crate::core::event::{self, Event};
+use crate::core::Hasher;
+use crate::subscription::Recipe;
+use crate::{BoxFuture, MaybeSend};
-use futures::{
- channel::mpsc,
- sink::{Sink, SinkExt},
-};
-use std::{collections::HashMap, marker::PhantomData};
+use futures::channel::mpsc;
+use futures::sink::{Sink, SinkExt};
+
+use std::collections::HashMap;
+use std::hash::Hasher as _;
/// A registry of subscription streams.
///
/// If you have an application that continuously returns a [`Subscription`],
/// you can use a [`Tracker`] to keep track of the different recipes and keep
/// its executions alive.
-#[derive(Debug)]
-pub struct Tracker<Hasher, Event> {
- subscriptions: HashMap<u64, Execution<Event>>,
- _hasher: PhantomData<Hasher>,
+#[derive(Debug, Default)]
+pub struct Tracker {
+ subscriptions: HashMap<u64, Execution>,
}
#[derive(Debug)]
-pub struct Execution<Event> {
+pub struct Execution {
_cancel: futures::channel::oneshot::Sender<()>,
- listener: Option<futures::channel::mpsc::Sender<Event>>,
+ listener: Option<futures::channel::mpsc::Sender<(Event, event::Status)>>,
}
-impl<Hasher, Event> Tracker<Hasher, Event>
-where
- Hasher: std::hash::Hasher + Default,
- Event: 'static + Send + Clone,
-{
+impl Tracker {
/// Creates a new empty [`Tracker`].
pub fn new() -> Self {
Self {
subscriptions: HashMap::new(),
- _hasher: PhantomData,
}
}
@@ -56,7 +53,7 @@ where
/// [`Recipe`]: crate::subscription::Recipe
pub fn update<Message, Receiver>(
&mut self,
- subscription: Subscription<Hasher, Event, Message>,
+ recipes: impl Iterator<Item = Box<dyn Recipe<Output = Message>>>,
receiver: Receiver,
) -> Vec<BoxFuture<()>>
where
@@ -70,8 +67,6 @@ where
use futures::stream::StreamExt;
let mut futures: Vec<BoxFuture<()>> = Vec::new();
-
- let recipes = subscription.recipes();
let mut alive = std::collections::HashSet::new();
for recipe in recipes {
@@ -142,12 +137,12 @@ where
/// currently open.
///
/// [`Recipe::stream`]: crate::subscription::Recipe::stream
- pub fn broadcast(&mut self, event: Event) {
+ pub fn broadcast(&mut self, event: Event, status: event::Status) {
self.subscriptions
.values_mut()
.filter_map(|connection| connection.listener.as_mut())
.for_each(|listener| {
- if let Err(error) = listener.try_send(event.clone()) {
+ if let Err(error) = listener.try_send((event.clone(), status)) {
log::warn!(
"Error sending event to subscription: {:?}",
error
@@ -156,13 +151,3 @@ where
});
}
}
-
-impl<Hasher, Event> Default for Tracker<Hasher, Event>
-where
- Hasher: std::hash::Hasher + Default,
- Event: 'static + Send + Clone,
-{
- fn default() -> Self {
- Self::new()
- }
-}
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index 19d6af0c..a08c6707 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -11,23 +11,9 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[features]
-svg = ["resvg", "usvg", "tiny-skia"]
-image = ["png", "jpeg", "jpeg_rayon", "gif", "webp", "bmp"]
-png = ["image_rs/png"]
-jpeg = ["image_rs/jpeg"]
-jpeg_rayon = ["image_rs/jpeg_rayon"]
-gif = ["image_rs/gif"]
-webp = ["image_rs/webp"]
-pnm = ["image_rs/pnm"]
-ico = ["image_rs/ico"]
-bmp = ["image_rs/bmp"]
-hdr = ["image_rs/hdr"]
-dds = ["image_rs/dds"]
-farbfeld = ["image_rs/farbfeld"]
-canvas = ["lyon"]
-qr_code = ["qrcode", "canvas"]
+geometry = ["lyon_path"]
opengl = []
-image_rs = ["kamadak-exif"]
+image = ["dep:image", "kamadak-exif"]
[dependencies]
glam = "0.21.3"
@@ -40,45 +26,26 @@ bitflags = "1.2"
version = "1.4"
features = ["derive"]
-[dependencies.iced_native]
-version = "0.9"
-path = "../native"
+[dependencies.iced_core]
+version = "0.8"
+path = "../core"
-[dependencies.iced_style]
-version = "0.7"
-path = "../style"
-
-[dependencies.lyon]
-version = "1.0"
-optional = true
-
-[dependencies.qrcode]
-version = "0.12"
+[dependencies.tiny-skia]
+version = "0.8"
optional = true
-default-features = false
-[dependencies.image_rs]
+[dependencies.image]
version = "0.24"
-package = "image"
-default-features = false
-optional = true
-
-[dependencies.resvg]
-version = "0.18"
-optional = true
-
-[dependencies.usvg]
-version = "0.18"
-optional = true
-
-[dependencies.tiny-skia]
-version = "0.6"
optional = true
[dependencies.kamadak-exif]
version = "0.5"
optional = true
+[dependencies.lyon_path]
+version = "1"
+optional = true
+
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
all-features = true
diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs
index 8658cffe..dd2888ab 100644
--- a/graphics/src/backend.rs
+++ b/graphics/src/backend.rs
@@ -1,8 +1,8 @@
//! Write a graphics backend.
-use iced_native::image;
-use iced_native::svg;
-use iced_native::text;
-use iced_native::{Font, Point, Size};
+use iced_core::image;
+use iced_core::svg;
+use iced_core::text;
+use iced_core::{Font, Point, Size};
use std::borrow::Cow;
diff --git a/graphics/src/window/compositor.rs b/graphics/src/compositor.rs
index db4ba45d..d55e801a 100644
--- a/graphics/src/window/compositor.rs
+++ b/graphics/src/compositor.rs
@@ -1,6 +1,8 @@
//! A compositor is responsible for initializing a renderer and managing window
//! surfaces.
-use crate::{Color, Error, Viewport};
+use crate::{Error, Viewport};
+
+use iced_core::Color;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use thiserror::Error;
@@ -11,7 +13,7 @@ pub trait Compositor: Sized {
type Settings: Default;
/// The iced renderer of the backend.
- type Renderer: iced_native::Renderer;
+ type Renderer: iced_core::Renderer;
/// The surface of the backend.
type Surface;
@@ -28,6 +30,8 @@ pub trait Compositor: Sized {
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
&mut self,
window: &W,
+ width: u32,
+ height: u32,
) -> Self::Surface;
/// Configures a new [`Surface`] with the given dimensions.
diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs
new file mode 100644
index 00000000..8db1594a
--- /dev/null
+++ b/graphics/src/geometry.rs
@@ -0,0 +1,36 @@
+//! Draw 2D graphics for your users.
+//!
+//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a
+//! [`Frame`]. It can be used for animation, data visualization, game graphics,
+//! and more!
+pub mod fill;
+pub mod path;
+pub mod stroke;
+
+mod style;
+mod text;
+
+pub use fill::Fill;
+pub use path::Path;
+pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
+pub use style::Style;
+pub use text::Text;
+
+pub use iced_core::gradient::{self, Gradient};
+
+use crate::Primitive;
+
+#[derive(Debug, Clone)]
+pub struct Geometry(pub Primitive);
+
+impl From<Geometry> for Primitive {
+ fn from(geometry: Geometry) -> Self {
+ geometry.0
+ }
+}
+
+pub trait Renderer: iced_core::Renderer {
+ type Geometry;
+
+ fn draw(&mut self, geometry: Vec<Self::Geometry>);
+}
diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/geometry/fill.rs
index e954ebb5..2e8c1669 100644
--- a/graphics/src/widget/canvas/fill.rs
+++ b/graphics/src/geometry/fill.rs
@@ -1,7 +1,7 @@
//! Fill [crate::widget::canvas::Geometry] with a certain style.
-use crate::{Color, Gradient};
+use iced_core::{Color, Gradient};
-pub use crate::widget::canvas::Style;
+pub use crate::geometry::Style;
/// The style used to fill geometry.
#[derive(Debug, Clone)]
@@ -19,14 +19,14 @@ pub struct Fill {
/// By default, it is set to `NonZero`.
///
/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
- pub rule: FillRule,
+ pub rule: Rule,
}
impl Default for Fill {
fn default() -> Self {
Self {
style: Style::Solid(Color::BLACK),
- rule: FillRule::NonZero,
+ rule: Rule::NonZero,
}
}
}
@@ -57,16 +57,7 @@ impl From<Gradient> for Fill {
/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
-pub enum FillRule {
+pub enum Rule {
NonZero,
EvenOdd,
}
-
-impl From<FillRule> for lyon::tessellation::FillRule {
- fn from(rule: FillRule) -> lyon::tessellation::FillRule {
- match rule {
- FillRule::NonZero => lyon::tessellation::FillRule::NonZero,
- FillRule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
- }
- }
-}
diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/geometry/path.rs
index aeb2589e..c3127bdf 100644
--- a/graphics/src/widget/canvas/path.rs
+++ b/graphics/src/geometry/path.rs
@@ -7,18 +7,16 @@ mod builder;
pub use arc::Arc;
pub use builder::Builder;
-use crate::widget::canvas::LineDash;
+pub use lyon_path;
-use iced_native::{Point, Size};
-use lyon::algorithms::walk::{walk_along_path, RepeatedPattern, WalkerEvent};
-use lyon::path::iterator::PathIterator;
+use iced_core::{Point, Size};
/// An immutable set of points that may or may not be connected.
///
/// A single [`Path`] can represent different kinds of 2D shapes!
#[derive(Debug, Clone)]
pub struct Path {
- raw: lyon::path::Path,
+ raw: lyon_path::Path,
}
impl Path {
@@ -56,54 +54,14 @@ impl Path {
}
#[inline]
- pub(crate) fn raw(&self) -> &lyon::path::Path {
+ pub fn raw(&self) -> &lyon_path::Path {
&self.raw
}
#[inline]
- pub(crate) fn transformed(
- &self,
- transform: &lyon::math::Transform,
- ) -> Path {
+ pub fn transform(&self, transform: &lyon_path::math::Transform) -> Path {
Path {
raw: self.raw.clone().transformed(transform),
}
}
}
-
-pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
- Path::new(|builder| {
- let segments_odd = (line_dash.segments.len() % 2 == 1)
- .then(|| [line_dash.segments, line_dash.segments].concat());
-
- let mut draw_line = false;
-
- walk_along_path(
- path.raw().iter().flattened(0.01),
- 0.0,
- lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
- &mut RepeatedPattern {
- callback: |event: WalkerEvent<'_>| {
- let point = Point {
- x: event.position.x,
- y: event.position.y,
- };
-
- if draw_line {
- builder.line_to(point);
- } else {
- builder.move_to(point);
- }
-
- draw_line = !draw_line;
-
- true
- },
- index: line_dash.offset,
- intervals: segments_odd
- .as_deref()
- .unwrap_or(line_dash.segments),
- },
- );
- })
-}
diff --git a/graphics/src/widget/canvas/path/arc.rs b/graphics/src/geometry/path/arc.rs
index b8e72daf..2cdebb66 100644
--- a/graphics/src/widget/canvas/path/arc.rs
+++ b/graphics/src/geometry/path/arc.rs
@@ -1,5 +1,5 @@
//! Build and draw curves.
-use iced_native::{Point, Vector};
+use iced_core::{Point, Vector};
/// A segment of a differentiable curve.
#[derive(Debug, Clone, Copy)]
diff --git a/graphics/src/widget/canvas/path/builder.rs b/graphics/src/geometry/path/builder.rs
index 5121aa68..794dd3bc 100644
--- a/graphics/src/widget/canvas/path/builder.rs
+++ b/graphics/src/geometry/path/builder.rs
@@ -1,35 +1,38 @@
-use crate::widget::canvas::path::{arc, Arc, Path};
+use crate::geometry::path::{arc, Arc, Path};
-use iced_native::{Point, Size};
-use lyon::path::builder::SvgPathBuilder;
+use iced_core::{Point, Size};
+
+use lyon_path::builder::{self, SvgPathBuilder};
+use lyon_path::geom;
+use lyon_path::math;
/// A [`Path`] builder.
///
/// Once a [`Path`] is built, it can no longer be mutated.
#[allow(missing_debug_implementations)]
pub struct Builder {
- raw: lyon::path::builder::WithSvg<lyon::path::path::BuilderImpl>,
+ raw: builder::WithSvg<lyon_path::path::BuilderImpl>,
}
impl Builder {
/// Creates a new [`Builder`].
pub fn new() -> Builder {
Builder {
- raw: lyon::path::Path::builder().with_svg(),
+ raw: lyon_path::Path::builder().with_svg(),
}
}
/// Moves the starting point of a new sub-path to the given `Point`.
#[inline]
pub fn move_to(&mut self, point: Point) {
- let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y));
+ let _ = self.raw.move_to(math::Point::new(point.x, point.y));
}
/// Connects the last point in the [`Path`] to the given `Point` with a
/// straight line.
#[inline]
pub fn line_to(&mut self, point: Point) {
- let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y));
+ let _ = self.raw.line_to(math::Point::new(point.x, point.y));
}
/// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in
@@ -53,8 +56,6 @@ impl Builder {
/// See [the HTML5 specification of `arcTo`](https://html.spec.whatwg.org/multipage/canvas.html#building-paths:dom-context-2d-arcto)
/// for more details and examples.
pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {
- use lyon::{math, path};
-
let start = self.raw.current_position();
let mid = math::Point::new(a.x, a.y);
let end = math::Point::new(b.x, b.y);
@@ -92,7 +93,7 @@ impl Builder {
self.raw.arc_to(
math::Vector::new(radius, radius),
math::Angle::radians(0.0),
- path::ArcFlags {
+ lyon_path::ArcFlags {
large_arc: false,
sweep,
},
@@ -102,8 +103,6 @@ impl Builder {
/// Adds an ellipse to the [`Path`] using a clockwise direction.
pub fn ellipse(&mut self, arc: arc::Elliptical) {
- use lyon::{geom, math};
-
let arc = geom::Arc {
center: math::Point::new(arc.center.x, arc.center.y),
radii: math::Vector::new(arc.radii.x, arc.radii.y),
@@ -128,8 +127,6 @@ impl Builder {
control_b: Point,
to: Point,
) {
- use lyon::math;
-
let _ = self.raw.cubic_bezier_to(
math::Point::new(control_a.x, control_a.y),
math::Point::new(control_b.x, control_b.y),
@@ -141,8 +138,6 @@ impl Builder {
/// and its end point.
#[inline]
pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
- use lyon::math;
-
let _ = self.raw.quadratic_bezier_to(
math::Point::new(control.x, control.y),
math::Point::new(to.x, to.y),
diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/geometry/stroke.rs
index 4c19251d..2d760a6c 100644
--- a/graphics/src/widget/canvas/stroke.rs
+++ b/graphics/src/geometry/stroke.rs
@@ -1,7 +1,7 @@
//! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles.
-pub use crate::widget::canvas::Style;
+pub use crate::geometry::Style;
-use iced_native::Color;
+use iced_core::Color;
/// The style of a stroke.
#[derive(Debug, Clone)]
@@ -77,16 +77,6 @@ impl Default for LineCap {
}
}
-impl From<LineCap> for lyon::tessellation::LineCap {
- fn from(line_cap: LineCap) -> lyon::tessellation::LineCap {
- match line_cap {
- LineCap::Butt => lyon::tessellation::LineCap::Butt,
- LineCap::Square => lyon::tessellation::LineCap::Square,
- LineCap::Round => lyon::tessellation::LineCap::Round,
- }
- }
-}
-
/// The shape used at the corners of paths or basic shapes when they are
/// stroked.
#[derive(Debug, Clone, Copy)]
@@ -105,16 +95,6 @@ impl Default for LineJoin {
}
}
-impl From<LineJoin> for lyon::tessellation::LineJoin {
- fn from(line_join: LineJoin) -> lyon::tessellation::LineJoin {
- match line_join {
- LineJoin::Miter => lyon::tessellation::LineJoin::Miter,
- LineJoin::Round => lyon::tessellation::LineJoin::Round,
- LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel,
- }
- }
-}
-
/// The dash pattern used when stroking the line.
#[derive(Debug, Clone, Copy, Default)]
pub struct LineDash<'a> {
diff --git a/graphics/src/widget/canvas/style.rs b/graphics/src/geometry/style.rs
index 6794f2e7..be9ee376 100644
--- a/graphics/src/widget/canvas/style.rs
+++ b/graphics/src/geometry/style.rs
@@ -1,4 +1,4 @@
-use crate::{Color, Gradient};
+use iced_core::{Color, Gradient};
/// The coloring style of some drawing.
#[derive(Debug, Clone, PartialEq)]
diff --git a/graphics/src/widget/canvas/text.rs b/graphics/src/geometry/text.rs
index 8c0b2dfb..06e0b4d0 100644
--- a/graphics/src/widget/canvas/text.rs
+++ b/graphics/src/geometry/text.rs
@@ -1,5 +1,5 @@
-use crate::alignment;
-use crate::{Color, Font, Point};
+use iced_core::alignment;
+use iced_core::{Color, Font, Point};
/// A bunch of text that can be drawn to a canvas
#[derive(Debug, Clone)]
diff --git a/graphics/src/image.rs b/graphics/src/image.rs
index 04f4ff9d..2f634252 100644
--- a/graphics/src/image.rs
+++ b/graphics/src/image.rs
@@ -1,10 +1,94 @@
-//! Render images.
-#[cfg(feature = "image_rs")]
-pub mod raster;
+//! Load and operate on images.
+use crate::core::image::{Data, Handle};
-#[cfg(feature = "svg")]
-pub mod vector;
+use bitflags::bitflags;
-pub mod storage;
+pub use ::image as image_rs;
-pub use storage::Storage;
+pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> {
+ match handle.data() {
+ Data::Path(path) => {
+ let image = ::image::open(path)?;
+
+ let operation = std::fs::File::open(path)
+ .ok()
+ .map(std::io::BufReader::new)
+ .and_then(|mut reader| Operation::from_exif(&mut reader).ok())
+ .unwrap_or_else(Operation::empty);
+
+ Ok(operation.perform(image))
+ }
+ Data::Bytes(bytes) => {
+ let image = ::image::load_from_memory(bytes)?;
+ let operation =
+ Operation::from_exif(&mut std::io::Cursor::new(bytes))
+ .ok()
+ .unwrap_or_else(Operation::empty);
+
+ Ok(operation.perform(image))
+ }
+ Data::Rgba {
+ width,
+ height,
+ pixels,
+ } => {
+ if let Some(image) = image_rs::ImageBuffer::from_vec(
+ *width,
+ *height,
+ pixels.to_vec(),
+ ) {
+ Ok(image_rs::DynamicImage::ImageRgba8(image))
+ } else {
+ Err(image_rs::error::ImageError::Limits(
+ image_rs::error::LimitError::from_kind(
+ image_rs::error::LimitErrorKind::DimensionError,
+ ),
+ ))
+ }
+ }
+ }
+}
+
+bitflags! {
+ struct Operation: u8 {
+ const FLIP_HORIZONTALLY = 0b001;
+ const ROTATE_180 = 0b010;
+ const FLIP_DIAGONALLY = 0b100;
+ }
+}
+
+impl Operation {
+ // Meaning of the returned value is described e.g. at:
+ // https://magnushoff.com/articles/jpeg-orientation/
+ fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error>
+ where
+ R: std::io::BufRead + std::io::Seek,
+ {
+ let exif = exif::Reader::new().read_from_container(reader)?;
+
+ Ok(exif
+ .get_field(exif::Tag::Orientation, exif::In::PRIMARY)
+ .and_then(|field| field.value.get_uint(0))
+ .and_then(|value| u8::try_from(value).ok())
+ .and_then(|value| Self::from_bits(value.saturating_sub(1)))
+ .unwrap_or_else(Self::empty))
+ }
+
+ fn perform(self, mut image: image::DynamicImage) -> image::DynamicImage {
+ use image::imageops;
+
+ if self.contains(Self::FLIP_DIAGONALLY) {
+ imageops::flip_vertical_in_place(&mut image)
+ }
+
+ if self.contains(Self::ROTATE_180) {
+ imageops::rotate180_in_place(&mut image);
+ }
+
+ if self.contains(Self::FLIP_HORIZONTALLY) {
+ imageops::flip_horizontal_in_place(&mut image);
+ }
+
+ image
+ }
+}
diff --git a/graphics/src/image/raster.rs b/graphics/src/image/raster.rs
deleted file mode 100644
index da46c30f..00000000
--- a/graphics/src/image/raster.rs
+++ /dev/null
@@ -1,242 +0,0 @@
-//! Raster image loading and caching.
-use crate::image::Storage;
-use crate::Size;
-
-use iced_native::image;
-
-use bitflags::bitflags;
-use std::collections::{HashMap, HashSet};
-
-/// Entry in cache corresponding to an image handle
-#[derive(Debug)]
-pub enum Memory<T: Storage> {
- /// Image data on host
- Host(::image_rs::ImageBuffer<::image_rs::Rgba<u8>, Vec<u8>>),
- /// Storage entry
- Device(T::Entry),
- /// Image not found
- NotFound,
- /// Invalid image data
- Invalid,
-}
-
-impl<T: Storage> Memory<T> {
- /// Width and height of image
- pub fn dimensions(&self) -> Size<u32> {
- use crate::image::storage::Entry;
-
- match self {
- Memory::Host(image) => {
- let (width, height) = image.dimensions();
-
- Size::new(width, height)
- }
- Memory::Device(entry) => entry.size(),
- Memory::NotFound => Size::new(1, 1),
- Memory::Invalid => Size::new(1, 1),
- }
- }
-}
-
-/// Caches image raster data
-#[derive(Debug)]
-pub struct Cache<T: Storage> {
- map: HashMap<u64, Memory<T>>,
- hits: HashSet<u64>,
-}
-
-impl<T: Storage> Cache<T> {
- /// Load image
- pub fn load(&mut self, handle: &image::Handle) -> &mut Memory<T> {
- if self.contains(handle) {
- return self.get(handle).unwrap();
- }
-
- let memory = match handle.data() {
- image::Data::Path(path) => {
- if let Ok(image) = image_rs::open(path) {
- let operation = std::fs::File::open(path)
- .ok()
- .map(std::io::BufReader::new)
- .and_then(|mut reader| {
- Operation::from_exif(&mut reader).ok()
- })
- .unwrap_or_else(Operation::empty);
-
- Memory::Host(operation.perform(image.to_rgba8()))
- } else {
- Memory::NotFound
- }
- }
- image::Data::Bytes(bytes) => {
- if let Ok(image) = image_rs::load_from_memory(bytes) {
- let operation =
- Operation::from_exif(&mut std::io::Cursor::new(bytes))
- .ok()
- .unwrap_or_else(Operation::empty);
-
- Memory::Host(operation.perform(image.to_rgba8()))
- } else {
- Memory::Invalid
- }
- }
- image::Data::Rgba {
- width,
- height,
- pixels,
- } => {
- if let Some(image) = image_rs::ImageBuffer::from_vec(
- *width,
- *height,
- pixels.to_vec(),
- ) {
- Memory::Host(image)
- } else {
- Memory::Invalid
- }
- }
- };
-
- self.insert(handle, memory);
- self.get(handle).unwrap()
- }
-
- /// Load image and upload raster data
- pub fn upload(
- &mut self,
- handle: &image::Handle,
- state: &mut T::State<'_>,
- storage: &mut T,
- ) -> Option<&T::Entry> {
- let memory = self.load(handle);
-
- if let Memory::Host(image) = memory {
- let (width, height) = image.dimensions();
-
- let entry = storage.upload(width, height, image, state)?;
-
- *memory = Memory::Device(entry);
- }
-
- if let Memory::Device(allocation) = memory {
- Some(allocation)
- } else {
- None
- }
- }
-
- /// Trim cache misses from cache
- pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) {
- let hits = &self.hits;
-
- self.map.retain(|k, memory| {
- let retain = hits.contains(k);
-
- if !retain {
- if let Memory::Device(entry) = memory {
- storage.remove(entry, state);
- }
- }
-
- retain
- });
-
- self.hits.clear();
- }
-
- fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory<T>> {
- let _ = self.hits.insert(handle.id());
-
- self.map.get_mut(&handle.id())
- }
-
- fn insert(&mut self, handle: &image::Handle, memory: Memory<T>) {
- let _ = self.map.insert(handle.id(), memory);
- }
-
- fn contains(&self, handle: &image::Handle) -> bool {
- self.map.contains_key(&handle.id())
- }
-}
-
-impl<T: Storage> Default for Cache<T> {
- fn default() -> Self {
- Self {
- map: HashMap::new(),
- hits: HashSet::new(),
- }
- }
-}
-
-bitflags! {
- struct Operation: u8 {
- const FLIP_HORIZONTALLY = 0b001;
- const ROTATE_180 = 0b010;
- const FLIP_DIAGONALLY = 0b100;
- }
-}
-
-impl Operation {
- // Meaning of the returned value is described e.g. at:
- // https://magnushoff.com/articles/jpeg-orientation/
- fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error>
- where
- R: std::io::BufRead + std::io::Seek,
- {
- let exif = exif::Reader::new().read_from_container(reader)?;
-
- Ok(exif
- .get_field(exif::Tag::Orientation, exif::In::PRIMARY)
- .and_then(|field| field.value.get_uint(0))
- .and_then(|value| u8::try_from(value).ok())
- .and_then(|value| Self::from_bits(value.saturating_sub(1)))
- .unwrap_or_else(Self::empty))
- }
-
- fn perform<P>(
- self,
- image: image_rs::ImageBuffer<P, Vec<P::Subpixel>>,
- ) -> image_rs::ImageBuffer<P, Vec<P::Subpixel>>
- where
- P: image_rs::Pixel + 'static,
- {
- use image_rs::imageops;
-
- let mut image = if self.contains(Self::FLIP_DIAGONALLY) {
- flip_diagonally(image)
- } else {
- image
- };
-
- if self.contains(Self::ROTATE_180) {
- imageops::rotate180_in_place(&mut image);
- }
-
- if self.contains(Self::FLIP_HORIZONTALLY) {
- imageops::flip_horizontal_in_place(&mut image);
- }
-
- image
- }
-}
-
-fn flip_diagonally<I>(
- image: I,
-) -> image_rs::ImageBuffer<I::Pixel, Vec<<I::Pixel as image_rs::Pixel>::Subpixel>>
-where
- I: image_rs::GenericImage,
- I::Pixel: 'static,
-{
- let (width, height) = image.dimensions();
- let mut out = image_rs::ImageBuffer::new(height, width);
-
- for x in 0..width {
- for y in 0..height {
- let p = image.get_pixel(x, y);
-
- out.put_pixel(y, x, p);
- }
- }
-
- out
-}
diff --git a/graphics/src/image/storage.rs b/graphics/src/image/storage.rs
index 1b5b5c35..4caa6141 100644
--- a/graphics/src/image/storage.rs
+++ b/graphics/src/image/storage.rs
@@ -1,5 +1,5 @@
//! Store images.
-use crate::Size;
+use iced_core::Size;
use std::fmt::Debug;
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index 41bef2c3..0c50db52 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -9,7 +9,7 @@
)]
#![deny(
missing_debug_implementations,
- missing_docs,
+ //missing_docs,
unsafe_code,
unused_results,
clippy::extra_unused_lifetimes,
@@ -23,32 +23,30 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
mod antialiasing;
mod error;
-mod primitive;
mod transformation;
mod viewport;
pub mod backend;
-pub mod gradient;
-pub mod image;
-pub mod layer;
-pub mod overlay;
+pub mod compositor;
+pub mod primitive;
pub mod renderer;
-pub mod triangle;
-pub mod widget;
-pub mod window;
+
+#[cfg(feature = "geometry")]
+pub mod geometry;
+
+#[cfg(feature = "image")]
+pub mod image;
pub use antialiasing::Antialiasing;
pub use backend::Backend;
+pub use compositor::Compositor;
pub use error::Error;
-pub use gradient::Gradient;
-pub use layer::Layer;
pub use primitive::Primitive;
pub use renderer::Renderer;
pub use transformation::Transformation;
pub use viewport::Viewport;
-pub use window::compositor;
-pub use iced_native::alignment;
-pub use iced_native::{
- Alignment, Background, Color, Font, Point, Rectangle, Size, Vector,
-};
+#[cfg(feature = "geometry")]
+pub use geometry::Geometry;
+
+pub use iced_core as core;
diff --git a/graphics/src/overlay.rs b/graphics/src/overlay.rs
deleted file mode 100644
index bc0ed744..00000000
--- a/graphics/src/overlay.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-//! Display interactive elements on top of other widgets.
-pub mod menu;
diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs
deleted file mode 100644
index 8b489e5e..00000000
--- a/graphics/src/overlay/menu.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-//! Build and show dropdown menus.
-
-pub use iced_style::menu::{Appearance, StyleSheet};
diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs
index 5a163a2f..195b62da 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -1,23 +1,15 @@
-use iced_native::image;
-use iced_native::svg;
-use iced_native::{Background, Color, Font, Rectangle, Size, Vector};
-
-use crate::alignment;
-use crate::gradient::Gradient;
-use crate::triangle;
+use iced_core::alignment;
+use iced_core::image;
+use iced_core::svg;
+use iced_core::{Background, Color, Font, Gradient, Rectangle, Size, Vector};
+use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
/// A rendering primitive.
#[derive(Debug, Clone)]
+#[non_exhaustive]
pub enum Primitive {
- /// An empty primitive
- None,
- /// A group of primitives
- Group {
- /// The primitives of the group
- primitives: Vec<Primitive>,
- },
/// A text primitive
Text {
/// The contents of the text
@@ -66,27 +58,12 @@ pub enum Primitive {
/// The bounds of the viewport
bounds: Rectangle,
},
- /// A clip primitive
- Clip {
- /// The bounds of the clip
- bounds: Rectangle,
- /// The content of the clip
- content: Box<Primitive>,
- },
- /// A primitive that applies a translation
- Translate {
- /// The translation vector
- translation: Vector,
-
- /// The primitive to translate
- content: Box<Primitive>,
- },
/// A low-level primitive to render a mesh of triangles with a solid color.
///
/// It can be used to render many kinds of geometry freely.
SolidMesh {
/// The vertices and indices of the mesh.
- buffers: triangle::Mesh2D<triangle::ColoredVertex2D>,
+ buffers: Mesh2D<ColoredVertex2D>,
/// The size of the drawable region of the mesh.
///
@@ -98,7 +75,7 @@ pub enum Primitive {
/// It can be used to render many kinds of geometry freely.
GradientMesh {
/// The vertices and indices of the mesh.
- buffers: triangle::Mesh2D<triangle::Vertex2D>,
+ buffers: Mesh2D<Vertex2D>,
/// The size of the drawable region of the mesh.
///
@@ -108,18 +85,103 @@ pub enum Primitive {
/// The [`Gradient`] to apply to the mesh.
gradient: Gradient,
},
+ #[cfg(feature = "tiny-skia")]
+ Fill {
+ path: tiny_skia::Path,
+ paint: tiny_skia::Paint<'static>,
+ rule: tiny_skia::FillRule,
+ transform: tiny_skia::Transform,
+ },
+ #[cfg(feature = "tiny-skia")]
+ Stroke {
+ path: tiny_skia::Path,
+ paint: tiny_skia::Paint<'static>,
+ stroke: tiny_skia::Stroke,
+ transform: tiny_skia::Transform,
+ },
+ /// A group of primitives
+ Group {
+ /// The primitives of the group
+ primitives: Vec<Primitive>,
+ },
+ /// A clip primitive
+ Clip {
+ /// The bounds of the clip
+ bounds: Rectangle,
+ /// The content of the clip
+ content: Box<Primitive>,
+ },
+ /// A primitive that applies a translation
+ Translate {
+ /// The translation vector
+ translation: Vector,
+
+ /// The primitive to translate
+ content: Box<Primitive>,
+ },
/// A cached primitive.
///
/// This can be useful if you are implementing a widget where primitive
/// generation is expensive.
- Cached {
+ Cache {
/// The cached primitive
- cache: Arc<Primitive>,
+ content: Arc<Primitive>,
},
}
-impl Default for Primitive {
- fn default() -> Primitive {
- Primitive::None
+impl Primitive {
+ pub fn group(primitives: Vec<Self>) -> Self {
+ Self::Group { primitives }
+ }
+
+ pub fn clip(self, bounds: Rectangle) -> Self {
+ Self::Clip {
+ bounds,
+ content: Box::new(self),
+ }
+ }
+
+ pub fn translate(self, translation: Vector) -> Self {
+ Self::Translate {
+ translation,
+ content: Box::new(self),
+ }
+ }
+}
+
+/// A set of [`Vertex2D`] and indices representing a list of triangles.
+#[derive(Clone, Debug)]
+pub struct Mesh2D<T> {
+ /// The vertices of the mesh
+ pub vertices: Vec<T>,
+
+ /// The list of vertex indices that defines the triangles of the mesh.
+ ///
+ /// Therefore, this list should always have a length that is a multiple of 3.
+ pub indices: Vec<u32>,
+}
+
+/// A two-dimensional vertex.
+#[derive(Copy, Clone, Debug, Zeroable, Pod)]
+#[repr(C)]
+pub struct Vertex2D {
+ /// The vertex position in 2D space.
+ pub position: [f32; 2],
+}
+
+/// A two-dimensional vertex with a color.
+#[derive(Copy, Clone, Debug, Zeroable, Pod)]
+#[repr(C)]
+pub struct ColoredVertex2D {
+ /// The vertex position in 2D space.
+ pub position: [f32; 2],
+
+ /// The color of the vertex in __linear__ RGBA.
+ pub color: [f32; 4],
+}
+
+impl From<()> for Primitive {
+ fn from(_: ()) -> Self {
+ Self::Group { primitives: vec![] }
}
}
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index b052c094..7bc462ef 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -1,14 +1,15 @@
//! Create a renderer from a [`Backend`].
use crate::backend::{self, Backend};
-use crate::{Primitive, Vector};
-use iced_native::image;
-use iced_native::layout;
-use iced_native::renderer;
-use iced_native::svg;
-use iced_native::text::{self, Text};
-use iced_native::{Background, Color, Element, Font, Point, Rectangle, Size};
+use crate::Primitive;
-pub use iced_native::renderer::Style;
+use iced_core::image;
+use iced_core::layout;
+use iced_core::renderer;
+use iced_core::svg;
+use iced_core::text::{self, Text};
+use iced_core::{
+ Background, Color, Element, Font, Point, Rectangle, Size, Vector,
+};
use std::borrow::Cow;
use std::marker::PhantomData;
@@ -43,12 +44,15 @@ impl<B: Backend, T> Renderer<B, T> {
/// Runs the given closure with the [`Backend`] and the recorded primitives
/// of the [`Renderer`].
- pub fn with_primitives(&mut self, f: impl FnOnce(&mut B, &[Primitive])) {
- f(&mut self.backend, &self.primitives);
+ pub fn with_primitives<O>(
+ &mut self,
+ f: impl FnOnce(&mut B, &[Primitive]) -> O,
+ ) -> O {
+ f(&mut self.backend, &self.primitives)
}
}
-impl<B, T> iced_native::Renderer for Renderer<B, T>
+impl<B, T> iced_core::Renderer for Renderer<B, T>
where
B: Backend,
{
@@ -67,19 +71,13 @@ where
}
fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) {
- let current_primitives = std::mem::take(&mut self.primitives);
+ let current = std::mem::take(&mut self.primitives);
f(self);
- let layer_primitives =
- std::mem::replace(&mut self.primitives, current_primitives);
+ let layer = std::mem::replace(&mut self.primitives, current);
- self.primitives.push(Primitive::Clip {
- bounds,
- content: Box::new(Primitive::Group {
- primitives: layer_primitives,
- }),
- });
+ self.primitives.push(Primitive::group(layer).clip(bounds));
}
fn with_translation(
@@ -87,19 +85,14 @@ where
translation: Vector,
f: impl FnOnce(&mut Self),
) {
- let current_primitives = std::mem::take(&mut self.primitives);
+ let current = std::mem::take(&mut self.primitives);
f(self);
- let layer_primitives =
- std::mem::replace(&mut self.primitives, current_primitives);
+ let layer = std::mem::replace(&mut self.primitives, current);
- self.primitives.push(Primitive::Translate {
- translation,
- content: Box::new(Primitive::Group {
- primitives: layer_primitives,
- }),
- });
+ self.primitives
+ .push(Primitive::group(layer).translate(translation));
}
fn fill_quad(
@@ -196,7 +189,7 @@ where
}
fn draw(&mut self, handle: image::Handle, bounds: Rectangle) {
- self.draw_primitive(Primitive::Image { handle, bounds })
+ self.primitives.push(Primitive::Image { handle, bounds })
}
}
@@ -214,10 +207,23 @@ where
color: Option<Color>,
bounds: Rectangle,
) {
- self.draw_primitive(Primitive::Svg {
+ self.primitives.push(Primitive::Svg {
handle,
color,
bounds,
})
}
}
+
+#[cfg(feature = "geometry")]
+impl<B, T> crate::geometry::Renderer for Renderer<B, T>
+where
+ B: Backend,
+{
+ type Geometry = crate::Geometry;
+
+ fn draw(&mut self, layers: Vec<Self::Geometry>) {
+ self.primitives
+ .extend(layers.into_iter().map(crate::Geometry::into));
+ }
+}
diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs
index f52b2339..09b61767 100644
--- a/graphics/src/triangle.rs
+++ b/graphics/src/triangle.rs
@@ -1,33 +1 @@
//! Draw geometry using meshes of triangles.
-use bytemuck::{Pod, Zeroable};
-
-/// A set of [`Vertex2D`] and indices representing a list of triangles.
-#[derive(Clone, Debug)]
-pub struct Mesh2D<T> {
- /// The vertices of the mesh
- pub vertices: Vec<T>,
-
- /// The list of vertex indices that defines the triangles of the mesh.
- ///
- /// Therefore, this list should always have a length that is a multiple of 3.
- pub indices: Vec<u32>,
-}
-
-/// A two-dimensional vertex.
-#[derive(Copy, Clone, Debug, Zeroable, Pod)]
-#[repr(C)]
-pub struct Vertex2D {
- /// The vertex position in 2D space.
- pub position: [f32; 2],
-}
-
-/// A two-dimensional vertex with a color.
-#[derive(Copy, Clone, Debug, Zeroable, Pod)]
-#[repr(C)]
-pub struct ColoredVertex2D {
- /// The vertex position in 2D space.
- pub position: [f32; 2],
-
- /// The color of the vertex in __linear__ RGBA.
- pub color: [f32; 4],
-}
diff --git a/graphics/src/viewport.rs b/graphics/src/viewport.rs
index 2c0b541a..5792555d 100644
--- a/graphics/src/viewport.rs
+++ b/graphics/src/viewport.rs
@@ -1,4 +1,6 @@
-use crate::{Size, Transformation};
+use crate::Transformation;
+
+use iced_core::Size;
/// A viewing region for displaying computer graphics.
#[derive(Debug, Clone)]
diff --git a/graphics/src/widget/canvas/geometry.rs b/graphics/src/widget/canvas/geometry.rs
deleted file mode 100644
index e8ac621d..00000000
--- a/graphics/src/widget/canvas/geometry.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use crate::Primitive;
-
-/// A bunch of shapes that can be drawn.
-///
-/// [`Geometry`] can be easily generated with a [`Frame`] or stored in a
-/// [`Cache`].
-///
-/// [`Frame`]: crate::widget::canvas::Frame
-/// [`Cache`]: crate::widget::canvas::Cache
-#[derive(Debug, Clone)]
-pub struct Geometry(Primitive);
-
-impl Geometry {
- pub(crate) fn from_primitive(primitive: Primitive) -> Self {
- Self(primitive)
- }
-
- /// Turns the [`Geometry`] into a [`Primitive`].
- ///
- /// This can be useful if you are building a custom widget.
- pub fn into_primitive(self) -> Primitive {
- self.0
- }
-}
diff --git a/graphics/src/window.rs b/graphics/src/window.rs
deleted file mode 100644
index a38b81f3..00000000
--- a/graphics/src/window.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//! Draw graphics to window surfaces.
-pub mod compositor;
-
-#[cfg(feature = "opengl")]
-pub mod gl_compositor;
-
-pub use compositor::Compositor;
-
-#[cfg(feature = "opengl")]
-pub use gl_compositor::GLCompositor;
diff --git a/graphics/src/window/gl_compositor.rs b/graphics/src/window/gl_compositor.rs
deleted file mode 100644
index a45a7ca1..00000000
--- a/graphics/src/window/gl_compositor.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-//! A compositor is responsible for initializing a renderer and managing window
-//! surfaces.
-use crate::compositor::Information;
-use crate::{Color, Error, Size, Viewport};
-
-use core::ffi::c_void;
-
-/// A basic OpenGL compositor.
-///
-/// A compositor is responsible for initializing a renderer and managing window
-/// surfaces.
-///
-/// For now, this compositor only deals with a single global surface
-/// for drawing. However, the trait will most likely change in the near future
-/// to handle multiple surfaces at once.
-///
-/// If you implement an OpenGL renderer, you can implement this trait to ease
-/// integration with existing windowing shells, like `iced_glutin`.
-pub trait GLCompositor: Sized {
- /// The renderer of the [`GLCompositor`].
- ///
- /// This should point to your renderer type, which could be a type alias
- /// of the [`Renderer`] provided in this crate with with a specific
- /// [`Backend`].
- ///
- /// [`Renderer`]: crate::Renderer
- /// [`Backend`]: crate::Backend
- type Renderer: iced_native::Renderer;
-
- /// The settings of the [`GLCompositor`].
- ///
- /// It's up to you to decide the configuration supported by your renderer!
- type Settings: Default;
-
- /// Creates a new [`GLCompositor`] and [`Renderer`] with the given
- /// [`Settings`] and an OpenGL address loader function.
- ///
- /// # Safety
- /// The `loader_function` should resolve to valid OpenGL bindings.
- ///
- /// [`Renderer`]: crate::Renderer
- /// [`Backend`]: crate::Backend
- /// [`Settings`]: Self::Settings
- #[allow(unsafe_code)]
- unsafe fn new(
- settings: Self::Settings,
- loader_function: impl FnMut(&str) -> *const c_void,
- ) -> Result<(Self, Self::Renderer), Error>;
-
- /// Returns the amount of samples that should be used when configuring
- /// an OpenGL context for this [`GLCompositor`].
- fn sample_count(settings: &Self::Settings) -> u32;
-
- /// Resizes the viewport of the [`GLCompositor`].
- fn resize_viewport(&mut self, physical_size: Size<u32>);
-
- /// Returns [`Information`] used by this [`GLCompositor`].
- fn fetch_information(&self) -> Information;
-
- /// Presents the primitives of the [`Renderer`] to the next frame of the
- /// [`GLCompositor`].
- ///
- /// [`Renderer`]: crate::Renderer
- fn present<T: AsRef<str>>(
- &mut self,
- renderer: &mut Self::Renderer,
- viewport: &Viewport,
- background_color: Color,
- overlay: &[T],
- );
-}
diff --git a/lazy/Cargo.toml b/lazy/Cargo.toml
deleted file mode 100644
index c739b312..00000000
--- a/lazy/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "iced_lazy"
-version = "0.5.0"
-authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
-edition = "2021"
-description = "Lazy widgets for Iced"
-license = "MIT"
-repository = "https://github.com/iced-rs/iced"
-documentation = "https://docs.rs/iced_lazy"
-keywords = ["gui", "ui", "graphics", "interface", "widgets"]
-categories = ["gui"]
-
-[dependencies]
-ouroboros = "0.13"
-
-[dependencies.iced_native]
-version = "0.9"
-path = "../native"
diff --git a/native/src/mouse.rs b/native/src/mouse.rs
deleted file mode 100644
index 9ee406cf..00000000
--- a/native/src/mouse.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//! Track mouse events.
-
-pub mod click;
-
-pub use click::Click;
-pub use iced_core::mouse::*;
diff --git a/native/src/runtime.rs b/native/src/runtime.rs
deleted file mode 100644
index 5b0a6925..00000000
--- a/native/src/runtime.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//! Run commands and subscriptions.
-use crate::event::{self, Event};
-use crate::Hasher;
-
-/// A native runtime with a generic executor and receiver of results.
-///
-/// It can be used by shells to easily spawn a [`Command`] or track a
-/// [`Subscription`].
-///
-/// [`Command`]: crate::Command
-/// [`Subscription`]: crate::Subscription
-pub type Runtime<Executor, Receiver, Message> = iced_futures::Runtime<
- Hasher,
- (Event, event::Status),
- Executor,
- Receiver,
- Message,
->;
diff --git a/native/src/subscription.rs b/native/src/subscription.rs
deleted file mode 100644
index 16e78e82..00000000
--- a/native/src/subscription.rs
+++ /dev/null
@@ -1,248 +0,0 @@
-//! Listen to external events in your application.
-use crate::event::{self, Event};
-use crate::window;
-use crate::Hasher;
-
-use iced_futures::futures::{self, Future, Stream};
-use iced_futures::{BoxStream, MaybeSend};
-
-use std::hash::Hash;
-
-/// A request to listen to external events.
-///
-/// Besides performing async actions on demand with [`Command`], most
-/// applications also need to listen to external events passively.
-///
-/// A [`Subscription`] is normally provided to some runtime, like a [`Command`],
-/// and it will generate events as long as the user keeps requesting it.
-///
-/// For instance, you can use a [`Subscription`] to listen to a WebSocket
-/// connection, keyboard presses, mouse events, time ticks, etc.
-///
-/// [`Command`]: crate::Command
-pub type Subscription<T> =
- iced_futures::Subscription<Hasher, (Event, event::Status), T>;
-
-/// A stream of runtime events.
-///
-/// It is the input of a [`Subscription`] in the native runtime.
-pub type EventStream = BoxStream<(Event, event::Status)>;
-
-/// A native [`Subscription`] tracker.
-pub type Tracker =
- iced_futures::subscription::Tracker<Hasher, (Event, event::Status)>;
-
-pub use iced_futures::subscription::Recipe;
-
-/// Returns a [`Subscription`] to all the ignored runtime events.
-///
-/// This subscription will notify your application of any [`Event`] that was
-/// not captured by any widget.
-pub fn events() -> Subscription<Event> {
- events_with(|event, status| match status {
- event::Status::Ignored => Some(event),
- event::Status::Captured => None,
- })
-}
-
-/// Returns a [`Subscription`] that filters all the runtime events with the
-/// provided function, producing messages accordingly.
-///
-/// This subscription will call the provided function for every [`Event`]
-/// handled by the runtime. If the function:
-///
-/// - Returns `None`, the [`Event`] will be discarded.
-/// - Returns `Some` message, the `Message` will be produced.
-pub fn events_with<Message>(
- f: fn(Event, event::Status) -> Option<Message>,
-) -> Subscription<Message>
-where
- Message: 'static + MaybeSend,
-{
- #[derive(Hash)]
- struct EventsWith;
-
- Subscription::from_recipe(Runner {
- id: (EventsWith, f),
- spawn: move |events| {
- use futures::future;
- use futures::stream::StreamExt;
-
- events.filter_map(move |(event, status)| {
- future::ready(match event {
- Event::Window(window::Event::RedrawRequested(_)) => None,
- _ => f(event, status),
- })
- })
- },
- })
-}
-
-pub(crate) fn raw_events<Message>(
- f: fn(Event, event::Status) -> Option<Message>,
-) -> Subscription<Message>
-where
- Message: 'static + MaybeSend,
-{
- #[derive(Hash)]
- struct RawEvents;
-
- Subscription::from_recipe(Runner {
- id: (RawEvents, f),
- spawn: move |events| {
- use futures::future;
- use futures::stream::StreamExt;
-
- events.filter_map(move |(event, status)| {
- future::ready(f(event, status))
- })
- },
- })
-}
-
-/// Returns a [`Subscription`] that will call the given function to create and
-/// asynchronously run the given [`Stream`].
-pub fn run<S, Message>(builder: fn() -> S) -> Subscription<Message>
-where
- S: Stream<Item = Message> + MaybeSend + 'static,
- Message: 'static,
-{
- Subscription::from_recipe(Runner {
- id: builder,
- spawn: move |_| builder(),
- })
-}
-
-/// Returns a [`Subscription`] that will create and asynchronously run the
-/// given [`Stream`].
-///
-/// The `id` will be used to uniquely identify the [`Subscription`].
-pub fn run_with_id<I, S, Message>(id: I, stream: S) -> Subscription<Message>
-where
- I: Hash + 'static,
- S: Stream<Item = Message> + MaybeSend + 'static,
- Message: 'static,
-{
- Subscription::from_recipe(Runner {
- id,
- spawn: move |_| stream,
- })
-}
-
-/// Returns a [`Subscription`] that will create and asynchronously run a
-/// [`Stream`] that will call the provided closure to produce every `Message`.
-///
-/// The `id` will be used to uniquely identify the [`Subscription`].
-///
-/// # Creating an asynchronous worker with bidirectional communication
-/// You can leverage this helper to create a [`Subscription`] that spawns
-/// an asynchronous worker in the background and establish a channel of
-/// communication with an `iced` application.
-///
-/// You can achieve this by creating an `mpsc` channel inside the closure
-/// and returning the `Sender` as a `Message` for the `Application`:
-///
-/// ```
-/// use iced_native::subscription::{self, Subscription};
-/// use iced_native::futures::channel::mpsc;
-///
-/// pub enum Event {
-/// Ready(mpsc::Sender<Input>),
-/// WorkFinished,
-/// // ...
-/// }
-///
-/// enum Input {
-/// DoSomeWork,
-/// // ...
-/// }
-///
-/// enum State {
-/// Starting,
-/// Ready(mpsc::Receiver<Input>),
-/// }
-///
-/// fn some_worker() -> Subscription<Event> {
-/// struct SomeWorker;
-///
-/// subscription::unfold(std::any::TypeId::of::<SomeWorker>(), State::Starting, |state| async move {
-/// match state {
-/// State::Starting => {
-/// // Create channel
-/// let (sender, receiver) = mpsc::channel(100);
-///
-/// (Some(Event::Ready(sender)), State::Ready(receiver))
-/// }
-/// State::Ready(mut receiver) => {
-/// use iced_native::futures::StreamExt;
-///
-/// // Read next input sent from `Application`
-/// let input = receiver.select_next_some().await;
-///
-/// match input {
-/// Input::DoSomeWork => {
-/// // Do some async work...
-///
-/// // Finally, we can optionally return a message to tell the
-/// // `Application` the work is done
-/// (Some(Event::WorkFinished), State::Ready(receiver))
-/// }
-/// }
-/// }
-/// }
-/// })
-/// }
-/// ```
-///
-/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
-/// connection open.
-///
-/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.8/examples/websocket
-pub fn unfold<I, T, Fut, Message>(
- id: I,
- initial: T,
- mut f: impl FnMut(T) -> Fut + MaybeSend + Sync + 'static,
-) -> Subscription<Message>
-where
- I: Hash + 'static,
- T: MaybeSend + 'static,
- Fut: Future<Output = (Option<Message>, T)> + MaybeSend + 'static,
- Message: 'static + MaybeSend,
-{
- use futures::future::{self, FutureExt};
- use futures::stream::StreamExt;
-
- run_with_id(
- id,
- futures::stream::unfold(initial, move |state| f(state).map(Some))
- .filter_map(future::ready),
- )
-}
-
-struct Runner<I, F, S, Message>
-where
- F: FnOnce(EventStream) -> S,
- S: Stream<Item = Message>,
-{
- id: I,
- spawn: F,
-}
-
-impl<I, S, F, Message> Recipe<Hasher, (Event, event::Status)>
- for Runner<I, F, S, Message>
-where
- I: Hash + 'static,
- F: FnOnce(EventStream) -> S,
- S: Stream<Item = Message> + MaybeSend + 'static,
-{
- type Output = Message;
-
- fn hash(&self, state: &mut Hasher) {
- std::any::TypeId::of::<I>().hash(state);
- self.id.hash(state);
- }
-
- fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
- iced_futures::boxed_stream((self.spawn)(input))
- }
-}
diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs
deleted file mode 100644
index 3f1b6b6c..00000000
--- a/native/src/widget/action.rs
+++ /dev/null
@@ -1,154 +0,0 @@
-use crate::widget::operation::{
- self, Focusable, Operation, Scrollable, TextInput,
-};
-use crate::widget::Id;
-
-use iced_futures::MaybeSend;
-
-use std::any::Any;
-use std::rc::Rc;
-
-/// An operation to be performed on the widget tree.
-#[allow(missing_debug_implementations)]
-pub struct Action<T>(Box<dyn Operation<T>>);
-
-impl<T> Action<T> {
- /// Creates a new [`Action`] with the given [`Operation`].
- pub fn new(operation: impl Operation<T> + 'static) -> Self {
- Self(Box::new(operation))
- }
-
- /// Maps the output of an [`Action`] using the given function.
- pub fn map<A>(
- self,
- f: impl Fn(T) -> A + 'static + MaybeSend + Sync,
- ) -> Action<A>
- where
- T: 'static,
- A: 'static,
- {
- Action(Box::new(Map {
- operation: self.0,
- f: Rc::new(f),
- }))
- }
-
- /// Consumes the [`Action`] and returns the internal [`Operation`].
- pub fn into_operation(self) -> Box<dyn Operation<T>> {
- self.0
- }
-}
-
-#[allow(missing_debug_implementations)]
-struct Map<A, B> {
- operation: Box<dyn Operation<A>>,
- f: Rc<dyn Fn(A) -> B>,
-}
-
-impl<A, B> Operation<B> for Map<A, B>
-where
- A: 'static,
- B: 'static,
-{
- fn container(
- &mut self,
- id: Option<&Id>,
- operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
- ) {
- struct MapRef<'a, A> {
- operation: &'a mut dyn Operation<A>,
- }
-
- impl<'a, A, B> Operation<B> for MapRef<'a, A> {
- fn container(
- &mut self,
- id: Option<&Id>,
- operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
- ) {
- let Self { operation, .. } = self;
-
- operation.container(id, &mut |operation| {
- operate_on_children(&mut MapRef { operation });
- });
- }
-
- fn scrollable(
- &mut self,
- state: &mut dyn Scrollable,
- id: Option<&Id>,
- ) {
- self.operation.scrollable(state, id);
- }
-
- fn focusable(
- &mut self,
- state: &mut dyn Focusable,
- id: Option<&Id>,
- ) {
- self.operation.focusable(state, id);
- }
-
- fn text_input(
- &mut self,
- state: &mut dyn TextInput,
- id: Option<&Id>,
- ) {
- self.operation.text_input(state, id);
- }
-
- fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
- self.operation.custom(state, id);
- }
- }
-
- let Self { operation, .. } = self;
-
- MapRef {
- operation: operation.as_mut(),
- }
- .container(id, operate_on_children);
- }
-
- fn focusable(
- &mut self,
- state: &mut dyn operation::Focusable,
- id: Option<&Id>,
- ) {
- self.operation.focusable(state, id);
- }
-
- fn scrollable(
- &mut self,
- state: &mut dyn operation::Scrollable,
- id: Option<&Id>,
- ) {
- self.operation.scrollable(state, id);
- }
-
- fn text_input(
- &mut self,
- state: &mut dyn operation::TextInput,
- id: Option<&Id>,
- ) {
- self.operation.text_input(state, id);
- }
-
- fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
- self.operation.custom(state, id);
- }
-
- fn finish(&self) -> operation::Outcome<B> {
- match self.operation.finish() {
- operation::Outcome::None => operation::Outcome::None,
- operation::Outcome::Some(output) => {
- operation::Outcome::Some((self.f)(output))
- }
- operation::Outcome::Chain(next) => {
- operation::Outcome::Chain(Box::new(Map {
- operation: next,
- f: self.f.clone(),
- }))
- }
- }
- }
-}
diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs
deleted file mode 100644
index d13eca75..00000000
--- a/native/src/widget/helpers.rs
+++ /dev/null
@@ -1,317 +0,0 @@
-//! Helper functions to create pure widgets.
-use crate::overlay;
-use crate::widget;
-use crate::{Element, Length, Pixels};
-
-use std::borrow::Cow;
-use std::ops::RangeInclusive;
-
-/// Creates a [`Column`] with the given children.
-///
-/// [`Column`]: widget::Column
-#[macro_export]
-macro_rules! column {
- () => (
- $crate::widget::Column::new()
- );
- ($($x:expr),+ $(,)?) => (
- $crate::widget::Column::with_children(vec![$($crate::Element::from($x)),+])
- );
-}
-
-/// Creates a [`Row`] with the given children.
-///
-/// [`Row`]: widget::Row
-#[macro_export]
-macro_rules! row {
- () => (
- $crate::widget::Row::new()
- );
- ($($x:expr),+ $(,)?) => (
- $crate::widget::Row::with_children(vec![$($crate::Element::from($x)),+])
- );
-}
-
-/// Creates a new [`Container`] with the provided content.
-///
-/// [`Container`]: widget::Container
-pub fn container<'a, Message, Renderer>(
- content: impl Into<Element<'a, Message, Renderer>>,
-) -> widget::Container<'a, Message, Renderer>
-where
- Renderer: crate::Renderer,
- Renderer::Theme: widget::container::StyleSheet,
-{
- widget::Container::new(content)
-}
-
-/// Creates a new [`Column`] with the given children.
-///
-/// [`Column`]: widget::Column
-pub fn column<Message, Renderer>(
- children: Vec<Element<'_, Message, Renderer>>,
-) -> widget::Column<'_, Message, Renderer> {
- widget::Column::with_children(children)
-}
-
-/// Creates a new [`Row`] with the given children.
-///
-/// [`Row`]: widget::Row
-pub fn row<Message, Renderer>(
- children: Vec<Element<'_, Message, Renderer>>,
-) -> widget::Row<'_, Message, Renderer> {
- widget::Row::with_children(children)
-}
-
-/// Creates a new [`Scrollable`] with the provided content.
-///
-/// [`Scrollable`]: widget::Scrollable
-pub fn scrollable<'a, Message, Renderer>(
- content: impl Into<Element<'a, Message, Renderer>>,
-) -> widget::Scrollable<'a, Message, Renderer>
-where
- Renderer: crate::Renderer,
- Renderer::Theme: widget::scrollable::StyleSheet,
-{
- widget::Scrollable::new(content)
-}
-
-/// Creates a new [`Button`] with the provided content.
-///
-/// [`Button`]: widget::Button
-pub fn button<'a, Message, Renderer>(
- content: impl Into<Element<'a, Message, Renderer>>,
-) -> widget::Button<'a, Message, Renderer>
-where
- Renderer: crate::Renderer,
- Renderer::Theme: widget::button::StyleSheet,
- <Renderer::Theme as widget::button::StyleSheet>::Style: Default,
-{
- widget::Button::new(content)
-}
-
-/// Creates a new [`Tooltip`] with the provided content, tooltip text, and [`tooltip::Position`].
-///
-/// [`Tooltip`]: widget::Tooltip
-/// [`tooltip::Position`]: widget::tooltip::Position
-pub fn tooltip<'a, Message, Renderer>(
- content: impl Into<Element<'a, Message, Renderer>>,
- tooltip: impl ToString,
- position: widget::tooltip::Position,
-) -> widget::Tooltip<'a, Message, Renderer>
-where
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::container::StyleSheet + widget::text::StyleSheet,
-{
- widget::Tooltip::new(content, tooltip.to_string(), position)
-}
-
-/// Creates a new [`Text`] widget with the provided content.
-///
-/// [`Text`]: widget::Text
-pub fn text<'a, Renderer>(text: impl ToString) -> widget::Text<'a, Renderer>
-where
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::text::StyleSheet,
-{
- widget::Text::new(text.to_string())
-}
-
-/// Creates a new [`Checkbox`].
-///
-/// [`Checkbox`]: widget::Checkbox
-pub fn checkbox<'a, Message, Renderer>(
- label: impl Into<String>,
- is_checked: bool,
- f: impl Fn(bool) -> Message + 'a,
-) -> widget::Checkbox<'a, Message, Renderer>
-where
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::checkbox::StyleSheet + widget::text::StyleSheet,
-{
- widget::Checkbox::new(label, is_checked, f)
-}
-
-/// Creates a new [`Radio`].
-///
-/// [`Radio`]: widget::Radio
-pub fn radio<Message, Renderer, V>(
- label: impl Into<String>,
- value: V,
- selected: Option<V>,
- on_click: impl FnOnce(V) -> Message,
-) -> widget::Radio<Message, Renderer>
-where
- Message: Clone,
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::radio::StyleSheet,
- V: Copy + Eq,
-{
- widget::Radio::new(value, label, selected, on_click)
-}
-
-/// Creates a new [`Toggler`].
-///
-/// [`Toggler`]: widget::Toggler
-pub fn toggler<'a, Message, Renderer>(
- label: impl Into<Option<String>>,
- is_checked: bool,
- f: impl Fn(bool) -> Message + 'a,
-) -> widget::Toggler<'a, Message, Renderer>
-where
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::toggler::StyleSheet,
-{
- widget::Toggler::new(label, is_checked, f)
-}
-
-/// Creates a new [`TextInput`].
-///
-/// [`TextInput`]: widget::TextInput
-pub fn text_input<'a, Message, Renderer>(
- placeholder: &str,
- value: &str,
- on_change: impl Fn(String) -> Message + 'a,
-) -> widget::TextInput<'a, Message, Renderer>
-where
- Message: Clone,
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::text_input::StyleSheet,
-{
- widget::TextInput::new(placeholder, value, on_change)
-}
-
-/// Creates a new [`Slider`].
-///
-/// [`Slider`]: widget::Slider
-pub fn slider<'a, T, Message, Renderer>(
- range: std::ops::RangeInclusive<T>,
- value: T,
- on_change: impl Fn(T) -> Message + 'a,
-) -> widget::Slider<'a, T, Message, Renderer>
-where
- T: Copy + From<u8> + std::cmp::PartialOrd,
- Message: Clone,
- Renderer: crate::Renderer,
- Renderer::Theme: widget::slider::StyleSheet,
-{
- 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
-pub fn pick_list<'a, Message, Renderer, T>(
- options: impl Into<Cow<'a, [T]>>,
- selected: Option<T>,
- on_selected: impl Fn(T) -> Message + 'a,
-) -> widget::PickList<'a, T, Message, Renderer>
-where
- T: ToString + Eq + 'static,
- [T]: ToOwned<Owned = Vec<T>>,
- Renderer: crate::text::Renderer,
- Renderer::Theme: widget::pick_list::StyleSheet
- + widget::scrollable::StyleSheet
- + overlay::menu::StyleSheet
- + widget::container::StyleSheet,
- <Renderer::Theme as overlay::menu::StyleSheet>::Style:
- From<<Renderer::Theme as widget::pick_list::StyleSheet>::Style>,
-{
- widget::PickList::new(options, selected, on_selected)
-}
-
-/// Creates a new [`Image`].
-///
-/// [`Image`]: widget::Image
-pub fn image<Handle>(handle: impl Into<Handle>) -> widget::Image<Handle> {
- widget::Image::new(handle.into())
-}
-
-/// Creates a new horizontal [`Space`] with the given [`Length`].
-///
-/// [`Space`]: widget::Space
-pub fn horizontal_space(width: impl Into<Length>) -> widget::Space {
- widget::Space::with_width(width)
-}
-
-/// Creates a new vertical [`Space`] with the given [`Length`].
-///
-/// [`Space`]: widget::Space
-pub fn vertical_space(height: impl Into<Length>) -> widget::Space {
- widget::Space::with_height(height)
-}
-
-/// Creates a horizontal [`Rule`] with the given height.
-///
-/// [`Rule`]: widget::Rule
-pub fn horizontal_rule<Renderer>(
- height: impl Into<Pixels>,
-) -> widget::Rule<Renderer>
-where
- Renderer: crate::Renderer,
- Renderer::Theme: widget::rule::StyleSheet,
-{
- widget::Rule::horizontal(height)
-}
-
-/// Creates a vertical [`Rule`] with the given width.
-///
-/// [`Rule`]: widget::Rule
-pub fn vertical_rule<Renderer>(
- width: impl Into<Pixels>,
-) -> widget::Rule<Renderer>
-where
- Renderer: crate::Renderer,
- Renderer::Theme: widget::rule::StyleSheet,
-{
- widget::Rule::vertical(width)
-}
-
-/// Creates a new [`ProgressBar`].
-///
-/// It expects:
-/// * an inclusive range of possible values, and
-/// * the current value of the [`ProgressBar`].
-///
-/// [`ProgressBar`]: widget::ProgressBar
-pub fn progress_bar<Renderer>(
- range: RangeInclusive<f32>,
- value: f32,
-) -> widget::ProgressBar<Renderer>
-where
- Renderer: crate::Renderer,
- Renderer::Theme: widget::progress_bar::StyleSheet,
-{
- widget::ProgressBar::new(range, value)
-}
-
-/// Creates a new [`Svg`] widget from the given [`Handle`].
-///
-/// [`Svg`]: widget::Svg
-/// [`Handle`]: widget::svg::Handle
-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/operation.rs b/native/src/widget/operation.rs
deleted file mode 100644
index 53688a21..00000000
--- a/native/src/widget/operation.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-//! Query or update internal widget state.
-pub mod focusable;
-pub mod scrollable;
-pub mod text_input;
-
-pub use focusable::Focusable;
-pub use scrollable::Scrollable;
-pub use text_input::TextInput;
-
-use crate::widget::Id;
-
-use std::any::Any;
-use std::fmt;
-
-/// A piece of logic that can traverse the widget tree of an application in
-/// order to query or update some widget state.
-pub trait Operation<T> {
- /// Operates on a widget that contains other widgets.
- ///
- /// The `operate_on_children` function can be called to return control to
- /// the widget tree and keep traversing it.
- fn container(
- &mut self,
- id: Option<&Id>,
- operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
- );
-
- /// Operates on a widget that can be focused.
- fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {}
-
- /// Operates on a widget that can be scrolled.
- fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {}
-
- /// Operates on a widget that has text input.
- fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
-
- /// Operates on a custom widget with some state.
- fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {}
-
- /// Finishes the [`Operation`] and returns its [`Outcome`].
- fn finish(&self) -> Outcome<T> {
- Outcome::None
- }
-}
-
-/// The result of an [`Operation`].
-pub enum Outcome<T> {
- /// The [`Operation`] produced no result.
- None,
-
- /// The [`Operation`] produced some result.
- Some(T),
-
- /// The [`Operation`] needs to be followed by another [`Operation`].
- Chain(Box<dyn Operation<T>>),
-}
-
-impl<T> fmt::Debug for Outcome<T>
-where
- T: fmt::Debug,
-{
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::None => write!(f, "Outcome::None"),
- Self::Some(output) => write!(f, "Outcome::Some({output:?})"),
- Self::Chain(_) => write!(f, "Outcome::Chain(...)"),
- }
- }
-}
-
-/// Produces an [`Operation`] that applies the given [`Operation`] to the
-/// children of a container with the given [`Id`].
-pub fn scoped<T: 'static>(
- target: Id,
- operation: impl Operation<T> + 'static,
-) -> impl Operation<T> {
- struct ScopedOperation<Message> {
- target: Id,
- operation: Box<dyn Operation<Message>>,
- }
-
- impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
- fn container(
- &mut self,
- id: Option<&Id>,
- operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
- ) {
- if id == Some(&self.target) {
- operate_on_children(self.operation.as_mut());
- } else {
- operate_on_children(self);
- }
- }
-
- fn finish(&self) -> Outcome<Message> {
- match self.operation.finish() {
- Outcome::Chain(next) => {
- Outcome::Chain(Box::new(ScopedOperation {
- target: self.target.clone(),
- operation: next,
- }))
- }
- outcome => outcome,
- }
- }
- }
-
- ScopedOperation {
- target,
- operation: Box::new(operation),
- }
-}
diff --git a/native/src/window.rs b/native/src/window.rs
deleted file mode 100644
index a5cdc8ce..00000000
--- a/native/src/window.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-//! Build window-based GUI applications.
-mod action;
-mod event;
-mod mode;
-mod redraw_request;
-mod user_attention;
-
-pub use action::Action;
-pub use event::Event;
-pub use mode::Mode;
-pub use redraw_request::RedrawRequest;
-pub use user_attention::UserAttention;
-
-use crate::subscription::{self, Subscription};
-use crate::time::Instant;
-
-/// Subscribes to the frames of the window of the running application.
-///
-/// The resulting [`Subscription`] will produce items at a rate equal to the
-/// refresh rate of the window. Note that this rate may be variable, as it is
-/// normally managed by the graphics driver and/or the OS.
-///
-/// In any case, this [`Subscription`] is useful to smoothly draw application-driven
-/// animations without missing any frames.
-pub fn frames() -> Subscription<Instant> {
- subscription::raw_events(|event, _status| match event {
- crate::Event::Window(Event::RedrawRequested(at)) => Some(at),
- _ => None,
- })
-}
diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml
new file mode 100644
index 00000000..629c11ba
--- /dev/null
+++ b/renderer/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "iced_renderer"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+wgpu = ["iced_wgpu"]
+tiny-skia = ["iced_tiny_skia"]
+image = ["iced_wgpu/image", "iced_tiny_skia/image"]
+svg = ["iced_wgpu/svg", "iced_tiny_skia/svg"]
+geometry = ["iced_graphics/geometry", "iced_wgpu?/geometry", "iced_tiny_skia?/geometry"]
+tracing = ["iced_wgpu/tracing"]
+
+[dependencies]
+raw-window-handle = "0.5"
+thiserror = "1"
+
+[dependencies.iced_graphics]
+version = "0.7"
+path = "../graphics"
+
+[dependencies.iced_wgpu]
+version = "0.9"
+path = "../wgpu"
+optional = true
+
+[dependencies.iced_tiny_skia]
+version = "0.1"
+path = "../tiny_skia"
+optional = true
diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs
new file mode 100644
index 00000000..e77b708b
--- /dev/null
+++ b/renderer/src/backend.rs
@@ -0,0 +1,98 @@
+use crate::core::text;
+use crate::core::{Font, Point, Size};
+use crate::graphics::backend;
+
+use std::borrow::Cow;
+
+#[allow(clippy::large_enum_variant)]
+pub enum Backend {
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::Backend),
+ #[cfg(feature = "tiny-skia")]
+ TinySkia(iced_tiny_skia::Backend),
+}
+
+macro_rules! delegate {
+ ($backend:expr, $name:ident, $body:expr) => {
+ match $backend {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu($name) => $body,
+ #[cfg(feature = "tiny-skia")]
+ Self::TinySkia($name) => $body,
+ }
+ };
+}
+
+impl iced_graphics::Backend for Backend {
+ fn trim_measurements(&mut self) {
+ delegate!(self, backend, backend.trim_measurements());
+ }
+}
+
+impl backend::Text for Backend {
+ const ICON_FONT: Font = Font::Name("Iced-Icons");
+ const CHECKMARK_ICON: char = '\u{f00c}';
+ const ARROW_DOWN_ICON: char = '\u{e800}';
+
+ fn default_font(&self) -> Font {
+ delegate!(self, backend, backend.default_font())
+ }
+
+ fn default_size(&self) -> f32 {
+ delegate!(self, backend, backend.default_size())
+ }
+
+ fn measure(
+ &self,
+ contents: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ ) -> (f32, f32) {
+ delegate!(self, backend, backend.measure(contents, size, font, bounds))
+ }
+
+ fn hit_test(
+ &self,
+ contents: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ position: Point,
+ nearest_only: bool,
+ ) -> Option<text::Hit> {
+ delegate!(
+ self,
+ backend,
+ backend.hit_test(
+ contents,
+ size,
+ font,
+ bounds,
+ position,
+ nearest_only
+ )
+ )
+ }
+
+ fn load_font(&mut self, font: Cow<'static, [u8]>) {
+ delegate!(self, backend, backend.load_font(font));
+ }
+}
+
+#[cfg(feature = "image")]
+impl backend::Image for Backend {
+ fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
+ delegate!(self, backend, backend.dimensions(handle))
+ }
+}
+
+#[cfg(feature = "svg")]
+impl backend::Svg for Backend {
+ fn viewport_dimensions(
+ &self,
+ handle: &crate::core::svg::Handle,
+ ) -> Size<u32> {
+ delegate!(self, backend, backend.viewport_dimensions(handle))
+ }
+}
diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs
new file mode 100644
index 00000000..218e7e33
--- /dev/null
+++ b/renderer/src/compositor.rs
@@ -0,0 +1,185 @@
+use crate::core::Color;
+use crate::graphics::compositor::{Information, SurfaceError};
+use crate::graphics::{Error, Viewport};
+use crate::{Renderer, Settings};
+
+use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
+
+pub enum Compositor<Theme> {
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::window::Compositor<Theme>),
+ #[cfg(feature = "tiny-skia")]
+ TinySkia(iced_tiny_skia::window::Compositor<Theme>),
+}
+
+pub enum Surface {
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::window::Surface),
+ #[cfg(feature = "tiny-skia")]
+ TinySkia(iced_tiny_skia::window::Surface),
+}
+
+impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
+ type Settings = Settings;
+ type Renderer = Renderer<Theme>;
+ type Surface = Surface;
+
+ fn new<W: HasRawWindowHandle + HasRawDisplayHandle>(
+ settings: Self::Settings,
+ compatible_window: Option<&W>,
+ ) -> Result<(Self, Self::Renderer), Error> {
+ #[cfg(feature = "wgpu")]
+ let new_wgpu = |settings: Self::Settings, compatible_window| {
+ let (compositor, backend) = iced_wgpu::window::compositor::new(
+ iced_wgpu::Settings {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ antialiasing: settings.antialiasing,
+ ..iced_wgpu::Settings::from_env()
+ },
+ compatible_window,
+ )?;
+
+ Ok((
+ Self::Wgpu(compositor),
+ Renderer::new(crate::Backend::Wgpu(backend)),
+ ))
+ };
+
+ #[cfg(feature = "tiny-skia")]
+ let new_tiny_skia = |settings: Self::Settings, _compatible_window| {
+ let (compositor, backend) = iced_tiny_skia::window::compositor::new(
+ iced_tiny_skia::Settings {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ },
+ );
+
+ Ok((
+ Self::TinySkia(compositor),
+ Renderer::new(crate::Backend::TinySkia(backend)),
+ ))
+ };
+
+ let fail = |_, _| Err(Error::GraphicsAdapterNotFound);
+
+ let candidates = &[
+ #[cfg(feature = "wgpu")]
+ new_wgpu,
+ #[cfg(feature = "tiny-skia")]
+ new_tiny_skia,
+ fail,
+ ];
+
+ let mut error = Error::GraphicsAdapterNotFound;
+
+ for candidate in candidates {
+ match candidate(settings, compatible_window) {
+ Ok((compositor, renderer)) => {
+ return Ok((compositor, renderer))
+ }
+ Err(new_error) => {
+ error = new_error;
+ }
+ }
+ }
+
+ Err(error)
+ }
+
+ fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
+ &mut self,
+ window: &W,
+ width: u32,
+ height: u32,
+ ) -> Surface {
+ match self {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(compositor) => {
+ Surface::Wgpu(compositor.create_surface(window, width, height))
+ }
+ #[cfg(feature = "tiny-skia")]
+ Self::TinySkia(compositor) => Surface::TinySkia(
+ compositor.create_surface(window, width, height),
+ ),
+ }
+ }
+
+ fn configure_surface(
+ &mut self,
+ surface: &mut Surface,
+ width: u32,
+ height: u32,
+ ) {
+ match (self, surface) {
+ #[cfg(feature = "wgpu")]
+ (Self::Wgpu(compositor), Surface::Wgpu(surface)) => {
+ compositor.configure_surface(surface, width, height);
+ }
+ #[cfg(feature = "tiny-skia")]
+ (Self::TinySkia(compositor), Surface::TinySkia(surface)) => {
+ compositor.configure_surface(surface, width, height);
+ }
+ #[allow(unreachable_patterns)]
+ _ => panic!(
+ "The provided surface is not compatible with the compositor."
+ ),
+ }
+ }
+
+ fn fetch_information(&self) -> Information {
+ match self {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(compositor) => compositor.fetch_information(),
+ #[cfg(feature = "tiny-skia")]
+ Self::TinySkia(compositor) => compositor.fetch_information(),
+ }
+ }
+
+ fn present<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ surface: &mut Self::Surface,
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Result<(), SurfaceError> {
+ renderer.with_primitives(|backend, primitives| {
+ match (self, backend, surface) {
+ #[cfg(feature = "wgpu")]
+ (
+ Self::Wgpu(compositor),
+ crate::Backend::Wgpu(backend),
+ Surface::Wgpu(surface),
+ ) => iced_wgpu::window::compositor::present(
+ compositor,
+ backend,
+ surface,
+ primitives,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ #[cfg(feature = "tiny-skia")]
+ (
+ Self::TinySkia(compositor),
+ crate::Backend::TinySkia(backend),
+ Surface::TinySkia(surface),
+ ) => iced_tiny_skia::window::compositor::present(
+ compositor,
+ backend,
+ surface,
+ primitives,
+ viewport,
+ background_color,
+ overlay,
+ ),
+ #[allow(unreachable_patterns)]
+ _ => panic!(
+ "The provided renderer or surface are not compatible \
+ with the compositor."
+ ),
+ }
+ })
+ }
+}
diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs
new file mode 100644
index 00000000..21ef2c06
--- /dev/null
+++ b/renderer/src/geometry.rs
@@ -0,0 +1,179 @@
+mod cache;
+
+pub use cache::Cache;
+
+use crate::core::{Point, Rectangle, Size, Vector};
+use crate::graphics::geometry::{Fill, Geometry, Path, Stroke, Text};
+use crate::Backend;
+
+pub enum Frame {
+ #[cfg(feature = "wgpu")]
+ Wgpu(iced_wgpu::geometry::Frame),
+ #[cfg(feature = "tiny-skia")]
+ TinySkia(iced_tiny_skia::geometry::Frame),
+}
+
+macro_rules! delegate {
+ ($frame:expr, $name:ident, $body:expr) => {
+ match $frame {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu($name) => $body,
+ #[cfg(feature = "tiny-skia")]
+ Self::TinySkia($name) => $body,
+ }
+ };
+}
+
+impl Frame {
+ pub fn new<Theme>(renderer: &crate::Renderer<Theme>, size: Size) -> Self {
+ match renderer.backend() {
+ #[cfg(feature = "wgpu")]
+ Backend::Wgpu(_) => {
+ Frame::Wgpu(iced_wgpu::geometry::Frame::new(size))
+ }
+ #[cfg(feature = "tiny-skia")]
+ Backend::TinySkia(_) => {
+ Frame::TinySkia(iced_tiny_skia::geometry::Frame::new(size))
+ }
+ }
+ }
+
+ /// Returns the width of the [`Frame`].
+ #[inline]
+ pub fn width(&self) -> f32 {
+ delegate!(self, frame, frame.width())
+ }
+
+ /// Returns the height of the [`Frame`].
+ #[inline]
+ pub fn height(&self) -> f32 {
+ delegate!(self, frame, frame.height())
+ }
+
+ /// Returns the dimensions of the [`Frame`].
+ #[inline]
+ pub fn size(&self) -> Size {
+ delegate!(self, frame, frame.size())
+ }
+
+ /// Returns the coordinate of the center of the [`Frame`].
+ #[inline]
+ pub fn center(&self) -> Point {
+ delegate!(self, frame, frame.center())
+ }
+
+ /// Draws the given [`Path`] on the [`Frame`] by filling it with the
+ /// provided style.
+ pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
+ delegate!(self, frame, frame.fill(path, fill));
+ }
+
+ /// Draws an axis-aligned rectangle given its top-left corner coordinate and
+ /// its `Size` on the [`Frame`] by filling it with the provided style.
+ pub fn fill_rectangle(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ fill: impl Into<Fill>,
+ ) {
+ delegate!(self, frame, frame.fill_rectangle(top_left, size, fill));
+ }
+
+ /// Draws the stroke of the given [`Path`] on the [`Frame`] with the
+ /// provided style.
+ pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
+ delegate!(self, frame, frame.stroke(path, stroke));
+ }
+
+ /// Draws the characters of the given [`Text`] on the [`Frame`], filling
+ /// them with the given color.
+ ///
+ /// __Warning:__ Text currently does not work well with rotations and scale
+ /// transforms! The position will be correctly transformed, but the
+ /// resulting glyphs will not be rotated or scaled properly.
+ ///
+ /// Additionally, all text will be rendered on top of all the layers of
+ /// a [`Canvas`]. Therefore, it is currently only meant to be used for
+ /// overlays, which is the most common use case.
+ ///
+ /// Support for vectorial text is planned, and should address all these
+ /// limitations.
+ ///
+ /// [`Canvas`]: crate::widget::Canvas
+ pub fn fill_text(&mut self, text: impl Into<Text>) {
+ delegate!(self, frame, frame.fill_text(text));
+ }
+
+ /// Stores the current transform of the [`Frame`] and executes the given
+ /// drawing operations, restoring the transform afterwards.
+ ///
+ /// This method is useful to compose transforms and perform drawing
+ /// operations in different coordinate systems.
+ #[inline]
+ pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) {
+ delegate!(self, frame, frame.push_transform());
+
+ f(self);
+
+ delegate!(self, frame, frame.pop_transform());
+ }
+
+ /// Executes the given drawing operations within a [`Rectangle`] region,
+ /// clipping any geometry that overflows its bounds. Any transformations
+ /// performed are local to the provided closure.
+ ///
+ /// This method is useful to perform drawing operations that need to be
+ /// clipped.
+ #[inline]
+ pub fn with_clip(&mut self, region: Rectangle, f: impl FnOnce(&mut Frame)) {
+ let mut frame = match self {
+ #[cfg(feature = "wgpu")]
+ Self::Wgpu(_) => {
+ Self::Wgpu(iced_wgpu::geometry::Frame::new(region.size()))
+ }
+ #[cfg(feature = "tiny-skia")]
+ Self::TinySkia(_) => Self::TinySkia(
+ iced_tiny_skia::geometry::Frame::new(region.size()),
+ ),
+ };
+
+ f(&mut frame);
+
+ let translation = Vector::new(region.x, region.y);
+
+ match (self, frame) {
+ #[cfg(feature = "wgpu")]
+ (Self::Wgpu(target), Self::Wgpu(frame)) => {
+ target.clip(frame, translation);
+ }
+ #[cfg(feature = "tiny-skia")]
+ (Self::TinySkia(target), Self::TinySkia(frame)) => {
+ target.clip(frame, translation);
+ }
+ #[allow(unreachable_patterns)]
+ _ => unreachable!(),
+ };
+ }
+
+ /// Applies a translation to the current transform of the [`Frame`].
+ #[inline]
+ pub fn translate(&mut self, translation: Vector) {
+ delegate!(self, frame, frame.translate(translation));
+ }
+
+ /// Applies a rotation in radians to the current transform of the [`Frame`].
+ #[inline]
+ pub fn rotate(&mut self, angle: f32) {
+ delegate!(self, frame, frame.rotate(angle));
+ }
+
+ /// Applies a scaling to the current transform of the [`Frame`].
+ #[inline]
+ pub fn scale(&mut self, scale: f32) {
+ delegate!(self, frame, frame.scale(scale));
+ }
+
+ pub fn into_geometry(self) -> Geometry {
+ Geometry(delegate!(self, frame, frame.into_primitive()))
+ }
+}
diff --git a/graphics/src/widget/canvas/cache.rs b/renderer/src/geometry/cache.rs
index 52217bbb..2a3534d0 100644
--- a/graphics/src/widget/canvas/cache.rs
+++ b/renderer/src/geometry/cache.rs
@@ -1,22 +1,11 @@
-use crate::widget::canvas::{Frame, Geometry};
-use crate::Primitive;
+use crate::core::Size;
+use crate::geometry::{Frame, Geometry};
+use crate::graphics::Primitive;
+use crate::Renderer;
-use iced_native::Size;
-use std::{cell::RefCell, sync::Arc};
+use std::cell::RefCell;
+use std::sync::Arc;
-enum State {
- Empty,
- Filled {
- bounds: Size,
- primitive: Arc<Primitive>,
- },
-}
-
-impl Default for State {
- fn default() -> Self {
- State::Empty
- }
-}
/// A simple cache that stores generated [`Geometry`] to avoid recomputation.
///
/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer
@@ -26,6 +15,16 @@ pub struct Cache {
state: RefCell<State>,
}
+#[derive(Debug, Default)]
+enum State {
+ #[default]
+ Empty,
+ Filled {
+ bounds: Size,
+ primitive: Arc<Primitive>,
+ },
+}
+
impl Cache {
/// Creates a new empty [`Cache`].
pub fn new() -> Self {
@@ -49,8 +48,9 @@ impl Cache {
/// Otherwise, the previously stored [`Geometry`] will be returned. The
/// [`Cache`] is not cleared in this case. In other words, it will keep
/// returning the stored [`Geometry`] if needed.
- pub fn draw(
+ pub fn draw<Theme>(
&self,
+ renderer: &Renderer<Theme>,
bounds: Size,
draw_fn: impl FnOnce(&mut Frame),
) -> Geometry {
@@ -62,19 +62,19 @@ impl Cache {
} = self.state.borrow().deref()
{
if *cached_bounds == bounds {
- return Geometry::from_primitive(Primitive::Cached {
- cache: primitive.clone(),
+ return Geometry(Primitive::Cache {
+ content: primitive.clone(),
});
}
}
- let mut frame = Frame::new(bounds);
+ let mut frame = Frame::new(renderer, bounds);
draw_fn(&mut frame);
let primitive = {
let geometry = frame.into_geometry();
- Arc::new(geometry.into_primitive())
+ Arc::new(geometry.0)
};
*self.state.borrow_mut() = State::Filled {
@@ -82,19 +82,6 @@ impl Cache {
primitive: primitive.clone(),
};
- Geometry::from_primitive(Primitive::Cached { cache: primitive })
- }
-}
-
-impl std::fmt::Debug for State {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- State::Empty => write!(f, "Empty"),
- State::Filled { primitive, bounds } => f
- .debug_struct("Filled")
- .field("primitive", primitive)
- .field("bounds", bounds)
- .finish(),
- }
+ Geometry(Primitive::Cache { content: primitive })
}
}
diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs
new file mode 100644
index 00000000..ba737b11
--- /dev/null
+++ b/renderer/src/lib.rs
@@ -0,0 +1,22 @@
+#[cfg(not(any(feature = "wgpu", feature = "tiny-skia")))]
+compile_error!("No backend selected. Enable at least one backend feature: `wgpu` or `tiny-skia`.");
+
+pub mod compositor;
+
+#[cfg(feature = "geometry")]
+pub mod geometry;
+
+mod backend;
+mod settings;
+
+pub use iced_graphics as graphics;
+pub use iced_graphics::core;
+
+pub use backend::Backend;
+pub use compositor::Compositor;
+pub use settings::Settings;
+
+/// The default graphics renderer for [`iced`].
+///
+/// [`iced`]: https://github.com/iced-rs/iced
+pub type Renderer<Theme> = iced_graphics::Renderer<Backend, Theme>;
diff --git a/renderer/src/settings.rs b/renderer/src/settings.rs
new file mode 100644
index 00000000..d32c87d3
--- /dev/null
+++ b/renderer/src/settings.rs
@@ -0,0 +1,31 @@
+use crate::core::Font;
+use crate::graphics::Antialiasing;
+
+/// The settings of a [`Backend`].
+///
+/// [`Backend`]: crate::Backend
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Settings {
+ /// The default [`Font`] to use.
+ pub default_font: Font,
+
+ /// The default size of text.
+ ///
+ /// By default, it will be set to `16.0`.
+ pub default_text_size: f32,
+
+ /// The antialiasing strategy that will be used for triangle primitives.
+ ///
+ /// By default, it is `None`.
+ pub antialiasing: Option<Antialiasing>,
+}
+
+impl Default for Settings {
+ fn default() -> Settings {
+ Settings {
+ default_font: Font::SansSerif,
+ default_text_size: 16.0,
+ antialiasing: None,
+ }
+ }
+}
diff --git a/graphics/src/widget.rs b/renderer/src/widget.rs
index e7fab97c..6c0c2a83 100644
--- a/graphics/src/widget.rs
+++ b/renderer/src/widget.rs
@@ -1,16 +1,11 @@
-//! Use the graphical widgets supported out-of-the-box.
#[cfg(feature = "canvas")]
-#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
pub mod canvas;
#[cfg(feature = "canvas")]
-#[doc(no_inline)]
pub use canvas::Canvas;
#[cfg(feature = "qr_code")]
-#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
pub mod qr_code;
#[cfg(feature = "qr_code")]
-#[doc(no_inline)]
pub use qr_code::QRCode;
diff --git a/native/Cargo.toml b/runtime/Cargo.toml
index 3f92783e..2d3e8db3 100644
--- a/native/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "iced_native"
+name = "iced_runtime"
version = "0.9.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
@@ -11,9 +11,7 @@ repository = "https://github.com/iced-rs/iced"
debug = []
[dependencies]
-twox-hash = { version = "1.5", default-features = false }
-unicode-segmentation = "1.6"
-num-traits = "0.2"
+thiserror = "1"
[dependencies.iced_core]
version = "0.8"
@@ -23,7 +21,3 @@ path = "../core"
version = "0.6"
path = "../futures"
features = ["thread-pool"]
-
-[dependencies.iced_style]
-version = "0.7"
-path = "../style"
diff --git a/native/README.md b/runtime/README.md
index 996daa76..497fd145 100644
--- a/native/README.md
+++ b/runtime/README.md
@@ -1,22 +1,10 @@
-# `iced_native`
+# `iced_runtime`
[![Documentation](https://docs.rs/iced_native/badge.svg)][documentation]
[![Crates.io](https://img.shields.io/crates/v/iced_native.svg)](https://crates.io/crates/iced_native)
[![License](https://img.shields.io/crates/l/iced_native.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
-`iced_native` takes [`iced_core`] and builds a native runtime on top of it, featuring:
-- A custom layout engine, greatly inspired by [`druid`]
-- Event handling for all the built-in widgets
-- A renderer-agnostic API
-
-To achieve this, it introduces a bunch of reusable interfaces:
-- A `Widget` trait, which is used to implement new widgets: from layout requirements to event and drawing logic.
-- A bunch of `Renderer` traits, meant to keep the crate renderer-agnostic.
-- A `Windowed` trait, leveraging [`raw-window-handle`], which can be implemented by graphical renderers that target _windows_. Window-based shells (like [`iced_winit`]) can use this trait to stay renderer-agnostic.
-
-<p align="center">
- <img alt="The native target" src="../docs/graphs/native.png" width="80%">
-</p>
+`iced_runtime` takes [`iced_core`] and builds a native runtime on top of it.
[documentation]: https://docs.rs/iced_native
[`iced_core`]: ../core
@@ -25,10 +13,10 @@ To achieve this, it introduces a bunch of reusable interfaces:
[`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
## Installation
-Add `iced_native` as a dependency in your `Cargo.toml`:
+Add `iced_runtime` as a dependency in your `Cargo.toml`:
```toml
-iced_native = "0.9"
+iced_runtime = "0.9"
```
__Iced moves fast and the `master` branch can contain breaking changes!__ If
diff --git a/native/src/clipboard.rs b/runtime/src/clipboard.rs
index c9105bc0..bc450912 100644
--- a/native/src/clipboard.rs
+++ b/runtime/src/clipboard.rs
@@ -1,30 +1,9 @@
//! Access the clipboard.
-use iced_futures::MaybeSend;
+use crate::command::{self, Command};
+use crate::futures::MaybeSend;
use std::fmt;
-/// A buffer for short-term storage and transfer within and between
-/// applications.
-pub trait Clipboard {
- /// Reads the current content of the [`Clipboard`] as text.
- fn read(&self) -> Option<String>;
-
- /// Writes the given text contents to the [`Clipboard`].
- fn write(&mut self, contents: String);
-}
-
-/// A null implementation of the [`Clipboard`] trait.
-#[derive(Debug, Clone, Copy)]
-pub struct Null;
-
-impl Clipboard for Null {
- fn read(&self) -> Option<String> {
- None
- }
-
- fn write(&mut self, _contents: String) {}
-}
-
/// A clipboard action to be performed by some [`Command`].
///
/// [`Command`]: crate::Command
@@ -60,3 +39,15 @@ impl<T> fmt::Debug for Action<T> {
}
}
}
+
+/// Read the current contents of the clipboard.
+pub fn read<Message>(
+ f: impl Fn(Option<String>) -> Message + 'static,
+) -> Command<Message> {
+ Command::single(command::Action::Clipboard(Action::Read(Box::new(f))))
+}
+
+/// Write the given contents to the clipboard.
+pub fn write<Message>(contents: String) -> Command<Message> {
+ Command::single(command::Action::Clipboard(Action::Write(contents)))
+}
diff --git a/native/src/command.rs b/runtime/src/command.rs
index ca9d0b64..cd4c51ff 100644
--- a/native/src/command.rs
+++ b/runtime/src/command.rs
@@ -3,35 +3,39 @@ mod action;
pub use action::Action;
-use crate::widget;
-
-use iced_futures::MaybeSend;
+use crate::core::widget;
+use crate::futures::MaybeSend;
use std::fmt;
use std::future::Future;
/// A set of asynchronous actions to be performed by some runtime.
#[must_use = "`Command` must be returned to runtime to take effect"]
-pub struct Command<T>(iced_futures::Command<Action<T>>);
+pub struct Command<T>(Internal<Action<T>>);
+
+#[derive(Debug)]
+enum Internal<T> {
+ None,
+ Single(T),
+ Batch(Vec<T>),
+}
impl<T> Command<T> {
/// Creates an empty [`Command`].
///
/// In other words, a [`Command`] that does nothing.
pub const fn none() -> Self {
- Self(iced_futures::Command::none())
+ Self(Internal::None)
}
/// Creates a [`Command`] that performs a single [`Action`].
pub const fn single(action: Action<T>) -> Self {
- Self(iced_futures::Command::single(action))
+ Self(Internal::Single(action))
}
/// Creates a [`Command`] that performs a [`widget::Operation`].
pub fn widget(operation: impl widget::Operation<T> + 'static) -> Self {
- Self(iced_futures::Command::single(Action::Widget(
- widget::Action::new(operation),
- )))
+ Self::single(Action::Widget(Box::new(operation)))
}
/// Creates a [`Command`] that performs the action of the given future.
@@ -49,9 +53,17 @@ impl<T> Command<T> {
///
/// Once this command is run, all the commands will be executed at once.
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
- Self(iced_futures::Command::batch(
- commands.into_iter().map(|Command(command)| command),
- ))
+ let mut batch = Vec::new();
+
+ for Command(command) in commands {
+ match command {
+ Internal::None => {}
+ Internal::Single(command) => batch.push(command),
+ Internal::Batch(commands) => batch.extend(commands),
+ }
+ }
+
+ Self(Internal::Batch(batch))
}
/// Applies a transformation to the result of a [`Command`].
@@ -63,16 +75,27 @@ impl<T> Command<T> {
T: 'static,
A: 'static,
{
- let Command(command) = self;
-
- Command(command.map(move |action| action.map(f.clone())))
+ match self.0 {
+ Internal::None => Command::none(),
+ Internal::Single(action) => Command::single(action.map(f)),
+ Internal::Batch(batch) => Command(Internal::Batch(
+ batch
+ .into_iter()
+ .map(|action| action.map(f.clone()))
+ .collect(),
+ )),
+ }
}
/// Returns all of the actions of the [`Command`].
pub fn actions(self) -> Vec<Action<T>> {
let Command(command) = self;
- command.actions()
+ match command {
+ Internal::None => Vec::new(),
+ Internal::Single(action) => vec![action],
+ Internal::Batch(batch) => batch,
+ }
}
}
diff --git a/native/src/command/action.rs b/runtime/src/command/action.rs
index d1589c05..6c74f0ef 100644
--- a/native/src/command/action.rs
+++ b/runtime/src/command/action.rs
@@ -1,7 +1,7 @@
use crate::clipboard;
+use crate::core::widget;
use crate::font;
use crate::system;
-use crate::widget;
use crate::window;
use iced_futures::MaybeSend;
@@ -28,7 +28,7 @@ pub enum Action<T> {
System(system::Action<T>),
/// Run a widget action.
- Widget(widget::Action<T>),
+ Widget(Box<dyn widget::Operation<T>>),
/// Load a font from its bytes.
LoadFont {
@@ -59,7 +59,9 @@ impl<T> Action<T> {
Self::Clipboard(action) => Action::Clipboard(action.map(f)),
Self::Window(window) => Action::Window(window.map(f)),
Self::System(system) => Action::System(system.map(f)),
- Self::Widget(widget) => Action::Widget(widget.map(f)),
+ Self::Widget(operation) => {
+ Action::Widget(Box::new(widget::operation::map(operation, f)))
+ }
Self::LoadFont { bytes, tagger } => Action::LoadFont {
bytes,
tagger: Box::new(move |result| f(tagger(result))),
diff --git a/native/src/debug/basic.rs b/runtime/src/debug/basic.rs
index 92f614da..32f725a1 100644
--- a/native/src/debug/basic.rs
+++ b/runtime/src/debug/basic.rs
@@ -1,5 +1,5 @@
#![allow(missing_docs)]
-use crate::time;
+use crate::core::time;
use std::collections::VecDeque;
diff --git a/native/src/debug/null.rs b/runtime/src/debug/null.rs
index 2db0eebb..2db0eebb 100644
--- a/native/src/debug/null.rs
+++ b/runtime/src/debug/null.rs
diff --git a/native/src/font.rs b/runtime/src/font.rs
index 15359694..15359694 100644
--- a/native/src/font.rs
+++ b/runtime/src/font.rs
diff --git a/native/src/keyboard.rs b/runtime/src/keyboard.rs
index 012538e3..012538e3 100644
--- a/native/src/keyboard.rs
+++ b/runtime/src/keyboard.rs
diff --git a/native/src/lib.rs b/runtime/src/lib.rs
index 27b6fc0d..aa45e57a 100644
--- a/native/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -33,7 +33,7 @@
)]
#![deny(
missing_debug_implementations,
- missing_docs,
+ //missing_docs,
unused_results,
clippy::extra_unused_lifetimes,
clippy::from_over_into,
@@ -42,33 +42,16 @@
clippy::useless_conversion
)]
#![forbid(unsafe_code, rust_2018_idioms)]
-#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod clipboard;
pub mod command;
-pub mod event;
pub mod font;
-pub mod image;
pub mod keyboard;
-pub mod layout;
-pub mod mouse;
-pub mod overlay;
pub mod program;
-pub mod renderer;
-pub mod subscription;
-pub mod svg;
pub mod system;
-pub mod text;
-pub mod touch;
pub mod user_interface;
-pub mod widget;
pub mod window;
-mod element;
-mod hasher;
-mod runtime;
-mod shell;
-
// We disable debug capabilities on release builds unless the `debug` feature
// is explicitly enabled.
#[cfg(feature = "debug")]
@@ -78,33 +61,11 @@ mod debug;
#[path = "debug/null.rs"]
mod debug;
-pub use iced_core::alignment;
-pub use iced_core::time;
-pub use iced_core::{
- color, Alignment, Background, Color, ContentFit, Length, Padding, Pixels,
- Point, Rectangle, Size, Vector,
-};
-pub use iced_futures::{executor, futures};
-pub use iced_style::application;
-pub use iced_style::theme;
-
-#[doc(no_inline)]
-pub use executor::Executor;
+pub use iced_core as core;
+pub use iced_futures as futures;
-pub use clipboard::Clipboard;
pub use command::Command;
pub use debug::Debug;
-pub use element::Element;
-pub use event::Event;
pub use font::Font;
-pub use hasher::Hasher;
-pub use layout::Layout;
-pub use overlay::Overlay;
pub use program::Program;
-pub use renderer::Renderer;
-pub use runtime::Runtime;
-pub use shell::Shell;
-pub use subscription::Subscription;
-pub use theme::Theme;
pub use user_interface::UserInterface;
-pub use widget::Widget;
diff --git a/native/src/program.rs b/runtime/src/program.rs
index 25cab332..44585cc5 100644
--- a/native/src/program.rs
+++ b/runtime/src/program.rs
@@ -1,6 +1,8 @@
//! Build interactive programs using The Elm Architecture.
-use crate::text;
-use crate::{Command, Element, Renderer};
+use crate::Command;
+
+use iced_core::text;
+use iced_core::{Element, Renderer};
mod state;
diff --git a/native/src/program/state.rs b/runtime/src/program/state.rs
index 8ae1cacb..2fa9934d 100644
--- a/native/src/program/state.rs
+++ b/runtime/src/program/state.rs
@@ -1,9 +1,9 @@
-use crate::application;
-use crate::event::{self, Event};
-use crate::mouse;
-use crate::renderer;
+use crate::core::event::{self, Event};
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::{Clipboard, Point, Size};
use crate::user_interface::{self, UserInterface};
-use crate::{Clipboard, Command, Debug, Point, Program, Size};
+use crate::{Command, Debug, Program};
/// The execution state of a [`Program`]. It leverages caching, event
/// processing, and rendering primitive storage.
@@ -22,7 +22,6 @@ where
impl<P> State<P>
where
P: Program + 'static,
- <P::Renderer as crate::Renderer>::Theme: application::StyleSheet,
{
/// Creates a new [`State`] with the provided [`Program`], initializing its
/// primitive with the given logical bounds and renderer.
@@ -91,7 +90,7 @@ where
bounds: Size,
cursor_position: Point,
renderer: &mut P::Renderer,
- theme: &<P::Renderer as crate::Renderer>::Theme,
+ theme: &<P::Renderer as iced_core::Renderer>::Theme,
style: &renderer::Style,
clipboard: &mut dyn Clipboard,
debug: &mut Debug,
@@ -182,10 +181,7 @@ fn build_user_interface<'a, P: Program>(
renderer: &mut P::Renderer,
size: Size,
debug: &mut Debug,
-) -> UserInterface<'a, P::Message, P::Renderer>
-where
- <P::Renderer as crate::Renderer>::Theme: application::StyleSheet,
-{
+) -> UserInterface<'a, P::Message, P::Renderer> {
debug.view_started();
let view = program.view();
debug.view_finished();
diff --git a/native/src/system.rs b/runtime/src/system.rs
index 61c8ff29..61c8ff29 100644
--- a/native/src/system.rs
+++ b/runtime/src/system.rs
diff --git a/native/src/system/action.rs b/runtime/src/system/action.rs
index dea9536f..dea9536f 100644
--- a/native/src/system/action.rs
+++ b/runtime/src/system/action.rs
diff --git a/native/src/system/information.rs b/runtime/src/system/information.rs
index 93e7a5a4..93e7a5a4 100644
--- a/native/src/system/information.rs
+++ b/runtime/src/system/information.rs
diff --git a/native/src/user_interface.rs b/runtime/src/user_interface.rs
index 68ccda55..2c76fd8a 100644
--- a/native/src/user_interface.rs
+++ b/runtime/src/user_interface.rs
@@ -1,14 +1,12 @@
//! Implement your own event loop to drive a user interface.
-use crate::application;
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::widget;
-use crate::window;
-use crate::{
- Clipboard, Element, Layout, Point, Rectangle, Shell, Size, Vector,
-};
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::widget;
+use crate::core::window;
+use crate::core::{Clipboard, Point, Rectangle, Size, Vector};
+use crate::core::{Element, Layout, Shell};
/// A set of interactive graphical elements with a specific [`Layout`].
///
@@ -34,8 +32,7 @@ pub struct UserInterface<'a, Message, Renderer> {
impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
- Renderer::Theme: application::StyleSheet,
+ Renderer: crate::core::Renderer,
{
/// Builds a user interface for an [`Element`].
///
@@ -48,24 +45,21 @@ where
/// is naive way to set up our application loop:
///
/// ```no_run
- /// use iced_native::Size;
- /// use iced_native::user_interface::{self, UserInterface};
- /// use iced_wgpu::Renderer;
- ///
/// # mod iced_wgpu {
- /// # pub use iced_native::renderer::Null as Renderer;
+ /// # pub use iced_runtime::core::renderer::Null as Renderer;
/// # }
/// #
- /// # use iced_native::widget::Column;
- /// #
/// # pub struct Counter;
/// #
/// # impl Counter {
/// # pub fn new() -> Self { Counter }
- /// # pub fn view(&self) -> Column<(), Renderer> {
- /// # Column::new()
- /// # }
+ /// # pub fn view(&self) -> iced_core::Element<(), Renderer> { unimplemented!() }
+ /// # pub fn update(&mut self, _: ()) {}
/// # }
+ /// use iced_runtime::core::Size;
+ /// use iced_runtime::user_interface::{self, UserInterface};
+ /// use iced_wgpu::Renderer;
+ ///
/// // Initialization
/// let mut counter = Counter::new();
/// let mut cache = user_interface::Cache::new();
@@ -124,25 +118,21 @@ where
/// completing [the previous example](#example):
///
/// ```no_run
- /// use iced_native::{clipboard, Size, Point};
- /// use iced_native::user_interface::{self, UserInterface};
- /// use iced_wgpu::Renderer;
- ///
/// # mod iced_wgpu {
- /// # pub use iced_native::renderer::Null as Renderer;
+ /// # pub use iced_runtime::core::renderer::Null as Renderer;
/// # }
/// #
- /// # use iced_native::widget::Column;
- /// #
/// # pub struct Counter;
/// #
/// # impl Counter {
/// # pub fn new() -> Self { Counter }
- /// # pub fn view(&self) -> Column<(), Renderer> {
- /// # Column::new()
- /// # }
- /// # pub fn update(&mut self, message: ()) {}
+ /// # pub fn view(&self) -> iced_core::Element<(), Renderer> { unimplemented!() }
+ /// # pub fn update(&mut self, _: ()) {}
/// # }
+ /// use iced_runtime::core::{clipboard, Size, Point};
+ /// use iced_runtime::user_interface::{self, UserInterface};
+ /// use iced_wgpu::Renderer;
+ ///
/// let mut counter = Counter::new();
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
@@ -357,27 +347,24 @@ where
/// [completing the last example](#example-1):
///
/// ```no_run
- /// use iced_native::clipboard;
- /// use iced_native::renderer;
- /// use iced_native::user_interface::{self, UserInterface};
- /// use iced_native::{Size, Point, Theme};
- /// use iced_wgpu::Renderer;
- ///
/// # mod iced_wgpu {
- /// # pub use iced_native::renderer::Null as Renderer;
+ /// # pub use iced_runtime::core::renderer::Null as Renderer;
+ /// # pub type Theme = ();
/// # }
/// #
- /// # use iced_native::widget::Column;
- /// #
/// # pub struct Counter;
/// #
/// # impl Counter {
/// # pub fn new() -> Self { Counter }
- /// # pub fn view(&self) -> Column<(), Renderer> {
- /// # Column::new()
- /// # }
- /// # pub fn update(&mut self, message: ()) {}
+ /// # pub fn view(&self) -> Element<(), Renderer> { unimplemented!() }
+ /// # pub fn update(&mut self, _: ()) {}
/// # }
+ /// use iced_runtime::core::clipboard;
+ /// use iced_runtime::core::renderer;
+ /// use iced_runtime::core::{Element, Size, Point};
+ /// use iced_runtime::user_interface::{self, UserInterface};
+ /// use iced_wgpu::{Renderer, Theme};
+ ///
/// let mut counter = Counter::new();
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
@@ -386,6 +373,7 @@ where
/// let mut clipboard = clipboard::Null;
/// let mut events = Vec::new();
/// let mut messages = Vec::new();
+ /// let mut theme = Theme::default();
///
/// loop {
/// // Obtain system events...
@@ -407,7 +395,7 @@ where
/// );
///
/// // Draw the user interface
- /// let mouse_cursor = user_interface.draw(&mut renderer, &Theme::default(), &renderer::Style::default(), cursor_position);
+ /// let mouse_cursor = user_interface.draw(&mut renderer, &theme, &renderer::Style::default(), cursor_position);
///
/// cache = user_interface.into_cache();
///
diff --git a/winit/src/window.rs b/runtime/src/window.rs
index 961562bd..236064f7 100644
--- a/winit/src/window.rs
+++ b/runtime/src/window.rs
@@ -1,68 +1,78 @@
-//! Interact with the window of your application.
+//! Build window-based GUI applications.
+mod action;
+
+pub use action::Action;
+
use crate::command::{self, Command};
-use iced_native::window;
+use crate::core::time::Instant;
+use crate::core::window::{Event, Mode, UserAttention};
+use crate::futures::subscription::{self, Subscription};
-pub use window::{frames, Event, Mode, RedrawRequest, UserAttention};
+/// Subscribes to the frames of the window of the running application.
+///
+/// The resulting [`Subscription`] will produce items at a rate equal to the
+/// refresh rate of the window. Note that this rate may be variable, as it is
+/// normally managed by the graphics driver and/or the OS.
+///
+/// In any case, this [`Subscription`] is useful to smoothly draw application-driven
+/// animations without missing any frames.
+pub fn frames() -> Subscription<Instant> {
+ subscription::raw_events(|event, _status| match event {
+ iced_core::Event::Window(Event::RedrawRequested(at)) => Some(at),
+ _ => None,
+ })
+}
/// Closes the current window and exits the application.
pub fn close<Message>() -> Command<Message> {
- Command::single(command::Action::Window(window::Action::Close))
+ Command::single(command::Action::Window(Action::Close))
}
/// Begins dragging the window while the left mouse button is held.
pub fn drag<Message>() -> Command<Message> {
- Command::single(command::Action::Window(window::Action::Drag))
+ Command::single(command::Action::Window(Action::Drag))
}
/// Resizes the window to the given logical dimensions.
pub fn resize<Message>(width: u32, height: u32) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::Resize {
- width,
- height,
- }))
+ Command::single(command::Action::Window(Action::Resize { width, height }))
}
/// Maximizes the window.
pub fn maximize<Message>(maximized: bool) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::Maximize(
- maximized,
- )))
+ Command::single(command::Action::Window(Action::Maximize(maximized)))
}
/// Minimes the window.
pub fn minimize<Message>(minimized: bool) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::Minimize(
- minimized,
- )))
+ Command::single(command::Action::Window(Action::Minimize(minimized)))
}
/// Moves a window to the given logical coordinates.
pub fn move_to<Message>(x: i32, y: i32) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::Move { x, y }))
+ Command::single(command::Action::Window(Action::Move { x, y }))
}
/// Sets the [`Mode`] of the window.
pub fn change_mode<Message>(mode: Mode) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::ChangeMode(mode)))
+ Command::single(command::Action::Window(Action::ChangeMode(mode)))
}
/// Fetches the current [`Mode`] of the window.
pub fn fetch_mode<Message>(
f: impl FnOnce(Mode) -> Message + 'static,
) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::FetchMode(
- Box::new(f),
- )))
+ Command::single(command::Action::Window(Action::FetchMode(Box::new(f))))
}
/// Toggles the window to maximized or back.
pub fn toggle_maximize<Message>() -> Command<Message> {
- Command::single(command::Action::Window(window::Action::ToggleMaximize))
+ Command::single(command::Action::Window(Action::ToggleMaximize))
}
/// Toggles the window decorations.
pub fn toggle_decorations<Message>() -> Command<Message> {
- Command::single(command::Action::Window(window::Action::ToggleDecorations))
+ Command::single(command::Action::Window(Action::ToggleDecorations))
}
/// Request user attention to the window, this has no effect if the application
@@ -74,9 +84,9 @@ pub fn toggle_decorations<Message>() -> Command<Message> {
pub fn request_user_attention<Message>(
user_attention: Option<UserAttention>,
) -> Command<Message> {
- Command::single(command::Action::Window(
- window::Action::RequestUserAttention(user_attention),
- ))
+ Command::single(command::Action::Window(Action::RequestUserAttention(
+ user_attention,
+ )))
}
/// Brings the window to the front and sets input focus. Has no effect if the window is
@@ -86,21 +96,17 @@ pub fn request_user_attention<Message>(
/// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive
/// user experience.
pub fn gain_focus<Message>() -> Command<Message> {
- Command::single(command::Action::Window(window::Action::GainFocus))
+ Command::single(command::Action::Window(Action::GainFocus))
}
/// Changes whether or not the window will always be on top of other windows.
pub fn change_always_on_top<Message>(on_top: bool) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::ChangeAlwaysOnTop(
- on_top,
- )))
+ Command::single(command::Action::Window(Action::ChangeAlwaysOnTop(on_top)))
}
/// Fetches an identifier unique to the window.
pub fn fetch_id<Message>(
f: impl FnOnce(u64) -> Message + 'static,
) -> Command<Message> {
- Command::single(command::Action::Window(window::Action::FetchId(Box::new(
- f,
- ))))
+ Command::single(command::Action::Window(Action::FetchId(Box::new(f))))
}
diff --git a/native/src/window/action.rs b/runtime/src/window/action.rs
index ce36d129..c1dbd84f 100644
--- a/native/src/window/action.rs
+++ b/runtime/src/window/action.rs
@@ -1,6 +1,6 @@
-use crate::window::{Mode, UserAttention};
+use crate::core::window::{Mode, UserAttention};
+use crate::futures::MaybeSend;
-use iced_futures::MaybeSend;
use std::fmt;
/// An operation to be performed on some window.
diff --git a/src/advanced.rs b/src/advanced.rs
new file mode 100644
index 00000000..7afba85c
--- /dev/null
+++ b/src/advanced.rs
@@ -0,0 +1,14 @@
+//! Leverage advanced concepts like custom widgets.
+pub use crate::core::image;
+pub use crate::core::layout::{self, Layout};
+pub use crate::core::overlay::{self, Overlay};
+pub use crate::core::renderer::{self, Renderer};
+pub use crate::core::svg;
+pub use crate::core::text::{self, Text};
+pub use crate::core::widget::{self, Widget};
+pub use crate::core::{Clipboard, Shell};
+
+pub mod subscription {
+ //! Write your own subscriptions.
+ pub use crate::runtime::futures::subscription::{EventStream, Recipe};
+}
diff --git a/src/application.rs b/src/application.rs
index 9a1c1855..c9ddf840 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -1,7 +1,7 @@
//! Build interactive cross-platform applications.
use crate::{Command, Element, Executor, Settings, Subscription};
-pub use iced_native::application::{Appearance, StyleSheet};
+pub use crate::style::application::{Appearance, StyleSheet};
/// An interactive cross-platform application.
///
@@ -198,24 +198,24 @@ pub trait Application: Sized {
default_font: settings.default_font,
default_text_size: settings.default_text_size,
antialiasing: if settings.antialiasing {
- Some(crate::renderer::settings::Antialiasing::MSAAx4)
+ Some(crate::graphics::Antialiasing::MSAAx4)
} else {
None
},
- ..crate::renderer::Settings::from_env()
+ ..crate::renderer::Settings::default()
};
- Ok(crate::runtime::application::run::<
+ Ok(crate::shell::application::run::<
Instance<Self>,
Self::Executor,
- crate::renderer::window::Compositor<Self::Theme>,
+ crate::renderer::Compositor<Self::Theme>,
>(settings.into(), renderer_settings)?)
}
}
struct Instance<A: Application>(A);
-impl<A> iced_winit::Program for Instance<A>
+impl<A> crate::runtime::Program for Instance<A>
where
A: Application,
{
@@ -231,7 +231,7 @@ where
}
}
-impl<A> crate::runtime::Application for Instance<A>
+impl<A> crate::shell::Application for Instance<A>
where
A: Application,
{
diff --git a/src/clipboard.rs b/src/clipboard.rs
deleted file mode 100644
index dde17051..00000000
--- a/src/clipboard.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-//! Access the clipboard.
-#[cfg(not(target_arch = "wasm32"))]
-pub use crate::runtime::clipboard::{read, write};
diff --git a/src/element.rs b/src/element.rs
deleted file mode 100644
index 2eb1bb4d..00000000
--- a/src/element.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-/// A generic widget.
-///
-/// This is an alias of an `iced_native` element with a default `Renderer`.
-pub type Element<'a, Message, Renderer = crate::Renderer> =
- crate::runtime::Element<'a, Message, Renderer>;
diff --git a/src/error.rs b/src/error.rs
index 0bfa3ff1..111bedf2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,6 @@
-use iced_futures::futures;
+use crate::futures;
+use crate::graphics;
+use crate::shell;
/// An error that occurred while running an application.
#[derive(Debug, thiserror::Error)]
@@ -13,19 +15,19 @@ pub enum Error {
/// The application graphics context could not be created.
#[error("the application graphics context could not be created")]
- GraphicsCreationFailed(iced_graphics::Error),
+ GraphicsCreationFailed(graphics::Error),
}
-impl From<iced_winit::Error> for Error {
- fn from(error: iced_winit::Error) -> Error {
+impl From<shell::Error> for Error {
+ fn from(error: shell::Error) -> Error {
match error {
- iced_winit::Error::ExecutorCreationFailed(error) => {
+ shell::Error::ExecutorCreationFailed(error) => {
Error::ExecutorCreationFailed(error)
}
- iced_winit::Error::WindowCreationFailed(error) => {
+ shell::Error::WindowCreationFailed(error) => {
Error::WindowCreationFailed(Box::new(error))
}
- iced_winit::Error::GraphicsCreationFailed(error) => {
+ shell::Error::GraphicsCreationFailed(error) => {
Error::GraphicsCreationFailed(error)
}
}
diff --git a/src/executor.rs b/src/executor.rs
deleted file mode 100644
index 36ae274e..00000000
--- a/src/executor.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//! Choose your preferred executor to power your application.
-pub use crate::runtime::Executor;
-
-/// A default cross-platform executor.
-///
-/// - On native platforms, it will use:
-/// - `iced_futures::backend::native::tokio` when the `tokio` feature is enabled.
-/// - `iced_futures::backend::native::async-std` when the `async-std` feature is
-/// enabled.
-/// - `iced_futures::backend::native::smol` when the `smol` feature is enabled.
-/// - `iced_futures::backend::native::thread_pool` otherwise.
-///
-/// - On Wasm, it will use `iced_futures::backend::wasm::wasm_bindgen`.
-pub type Default = iced_futures::backend::default::Executor;
diff --git a/src/keyboard.rs b/src/keyboard.rs
deleted file mode 100644
index 2134a66b..00000000
--- a/src/keyboard.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-//! Listen and react to keyboard events.
-pub use crate::runtime::keyboard::{Event, KeyCode, Modifiers};
diff --git a/src/lib.rs b/src/lib.rs
index 31ec4f48..c59d5058 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -164,51 +164,139 @@
#![forbid(rust_2018_idioms, unsafe_code)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_cfg))]
+use iced_widget::graphics;
+use iced_widget::renderer;
+use iced_widget::style;
+use iced_winit as shell;
+use iced_winit::core;
+use iced_winit::runtime;
+
+pub use iced_futures::futures;
-mod element;
mod error;
-mod result;
mod sandbox;
pub mod application;
-pub mod clipboard;
-pub mod executor;
-pub mod keyboard;
-pub mod mouse;
-pub mod overlay;
pub mod settings;
pub mod time;
-pub mod touch;
-pub mod widget;
pub mod window;
-use iced_wgpu as renderer;
-use iced_winit as runtime;
+#[cfg(feature = "advanced")]
+pub mod advanced;
+
+pub use style::theme;
+
+pub use crate::core::alignment;
+pub use crate::core::event;
+pub use crate::core::{
+ color, Alignment, Background, Color, ContentFit, Length, Padding, Point,
+ Rectangle, Size, Vector,
+};
+pub use crate::runtime::Command;
+
+pub mod clipboard {
+ //! Access the clipboard.
+ pub use crate::runtime::clipboard::{read, write};
+}
+
+pub mod executor {
+ //! Choose your preferred executor to power your application.
+ pub use iced_futures::Executor;
-pub use iced_native::theme;
-pub use runtime::event;
-pub use runtime::font;
-pub use runtime::subscription;
+ /// A default cross-platform executor.
+ ///
+ /// - On native platforms, it will use:
+ /// - `iced_futures::backend::native::tokio` when the `tokio` feature is enabled.
+ /// - `iced_futures::backend::native::async-std` when the `async-std` feature is
+ /// enabled.
+ /// - `iced_futures::backend::native::smol` when the `smol` feature is enabled.
+ /// - `iced_futures::backend::native::thread_pool` otherwise.
+ ///
+ /// - On Wasm, it will use `iced_futures::backend::wasm::wasm_bindgen`.
+ pub type Default = iced_futures::backend::default::Executor;
+}
+
+pub mod font {
+ //! Load and use fonts.
+ pub use crate::core::font::*;
+ pub use crate::runtime::font::*;
+}
+
+pub mod keyboard {
+ //! Listen and react to keyboard events.
+ pub use crate::core::keyboard::{Event, KeyCode, Modifiers};
+}
+
+pub mod mouse {
+ //! Listen and react to mouse events.
+ pub use crate::core::mouse::{Button, Event, Interaction, ScrollDelta};
+}
+
+pub mod subscription {
+ //! Listen to external events in your application.
+ pub use iced_futures::subscription::{
+ events, events_with, run, run_with_id, unfold, Subscription,
+ };
+}
+
+#[cfg(feature = "system")]
+pub mod system {
+ //! Retrieve system information.
+ pub use crate::runtime::system::Information;
+ pub use crate::shell::system::*;
+}
+
+pub mod overlay {
+ //! Display interactive elements on top of other widgets.
+
+ /// A generic [`Overlay`].
+ ///
+ /// This is an alias of an `iced_native` element with a default `Renderer`.
+ ///
+ /// [`Overlay`]: iced_native::Overlay
+ pub type Element<'a, Message, Renderer = crate::Renderer> =
+ crate::core::overlay::Element<'a, Message, Renderer>;
+
+ pub use iced_widget::overlay::*;
+}
+
+pub mod touch {
+ //! Listen and react to touch events.
+ pub use crate::core::touch::{Event, Finger};
+}
+
+pub mod widget {
+ //! Use the built-in widgets or create your own.
+ pub use iced_widget::*;
+
+ // We hide the re-exported modules by `iced_widget`
+ mod core {}
+ mod graphics {}
+ mod native {}
+ mod renderer {}
+ mod style {}
+}
pub use application::Application;
-pub use element::Element;
pub use error::Error;
pub use event::Event;
pub use executor::Executor;
pub use font::Font;
-pub use renderer::Renderer;
-pub use result::Result;
pub use sandbox::Sandbox;
pub use settings::Settings;
pub use subscription::Subscription;
pub use theme::Theme;
-pub use runtime::alignment;
-pub use runtime::futures;
-pub use runtime::{
- color, Alignment, Background, Color, Command, ContentFit, Length, Padding,
- Point, Rectangle, Size, Vector,
-};
+/// The default renderer.
+pub type Renderer<Theme = style::Theme> = renderer::Renderer<Theme>;
-#[cfg(feature = "system")]
-pub use runtime::system;
+/// A generic widget.
+///
+/// This is an alias of an `iced_native` element with a default `Renderer`.
+pub type Element<'a, Message, Renderer = crate::Renderer> =
+ crate::core::Element<'a, Message, Renderer>;
+
+/// The result of running an [`Application`].
+///
+/// [`Application`]: crate::Application
+pub type Result = std::result::Result<(), Error>;
diff --git a/src/mouse.rs b/src/mouse.rs
deleted file mode 100644
index d61ed09a..00000000
--- a/src/mouse.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-//! Listen and react to mouse events.
-pub use crate::runtime::mouse::{Button, Event, Interaction, ScrollDelta};
diff --git a/src/overlay.rs b/src/overlay.rs
deleted file mode 100644
index c0f4c492..00000000
--- a/src/overlay.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//! Display interactive elements on top of other widgets.
-
-/// A generic [`Overlay`].
-///
-/// This is an alias of an `iced_native` element with a default `Renderer`.
-///
-/// [`Overlay`]: iced_native::Overlay
-pub type Element<'a, Message, Renderer = crate::Renderer> =
- iced_native::overlay::Element<'a, Message, Renderer>;
-
-pub mod menu {
- //! Build and show dropdown menus.
- pub use iced_native::overlay::menu::{Appearance, State, StyleSheet};
-
- /// A widget that produces a message when clicked.
- pub type Menu<'a, Message, Renderer = crate::Renderer> =
- iced_native::overlay::Menu<'a, Message, Renderer>;
-}
diff --git a/src/result.rs b/src/result.rs
deleted file mode 100644
index ef565bd6..00000000
--- a/src/result.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use crate::Error;
-
-/// The result of running an [`Application`].
-///
-/// [`Application`]: crate::Application
-pub type Result = std::result::Result<(), Error>;
diff --git a/src/touch.rs b/src/touch.rs
index 0b77c386..f2bdfca8 100644
--- a/src/touch.rs
+++ b/src/touch.rs
@@ -1,2 +1,2 @@
//! Listen and react to touch events.
-pub use crate::runtime::touch::{Event, Finger};
+pub use crate::core::touch::{Event, Finger};
diff --git a/src/widget.rs b/src/widget.rs
deleted file mode 100644
index e2b0537e..00000000
--- a/src/widget.rs
+++ /dev/null
@@ -1,238 +0,0 @@
-//! Display information and interactive controls in your application.
-pub use iced_native::widget::helpers::*;
-
-pub use iced_native::{column, row};
-
-/// A container that distributes its contents vertically.
-pub type Column<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Column<'a, Message, Renderer>;
-
-/// A container that distributes its contents horizontally.
-pub type Row<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Row<'a, Message, Renderer>;
-
-pub mod text {
- //! Write some text for your users to read.
- pub use iced_native::widget::text::{Appearance, StyleSheet};
-
- /// A paragraph of text.
- pub type Text<'a, Renderer = crate::Renderer> =
- iced_native::widget::Text<'a, Renderer>;
-}
-
-pub mod button {
- //! Allow your users to perform actions by pressing a button.
- pub use iced_native::widget::button::{Appearance, StyleSheet};
-
- /// A widget that produces a message when clicked.
- pub type Button<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Button<'a, Message, Renderer>;
-}
-
-pub mod checkbox {
- //! Show toggle controls using checkboxes.
- pub use iced_native::widget::checkbox::{Appearance, Icon, StyleSheet};
-
- /// A box that can be checked.
- pub type Checkbox<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Checkbox<'a, Message, Renderer>;
-}
-
-pub mod container {
- //! Decorate content and apply alignment.
- pub use iced_native::widget::container::{Appearance, StyleSheet};
-
- /// An element decorating some content.
- pub type Container<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Container<'a, Message, Renderer>;
-}
-
-pub mod pane_grid {
- //! Let your users split regions of your application and organize layout dynamically.
- //!
- //! [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish)
- //!
- //! # Example
- //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
- //! drag and drop, and hotkey support.
- //!
- //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.8/examples/pane_grid
- pub use iced_native::widget::pane_grid::{
- Axis, Configuration, Direction, DragEvent, Line, Node, Pane,
- ResizeEvent, Split, State, StyleSheet,
- };
-
- /// A collection of panes distributed using either vertical or horizontal splits
- /// to completely fill the space available.
- ///
- /// [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish)
- pub type PaneGrid<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::PaneGrid<'a, Message, Renderer>;
-
- /// The content of a [`Pane`].
- pub type Content<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::pane_grid::Content<'a, Message, Renderer>;
-
- /// The title bar of a [`Pane`].
- pub type TitleBar<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::pane_grid::TitleBar<'a, Message, Renderer>;
-}
-
-pub mod pick_list {
- //! Display a dropdown list of selectable values.
- pub use iced_native::widget::pick_list::{
- Appearance, Handle, Icon, StyleSheet,
- };
-
- /// A widget allowing the selection of a single value from a list of options.
- pub type PickList<'a, T, Message, Renderer = crate::Renderer> =
- iced_native::widget::PickList<'a, T, Message, Renderer>;
-}
-
-pub mod radio {
- //! Create choices using radio buttons.
- pub use iced_native::widget::radio::{Appearance, StyleSheet};
-
- /// A circular button representing a choice.
- pub type Radio<Message, Renderer = crate::Renderer> =
- iced_native::widget::Radio<Message, Renderer>;
-}
-
-pub mod scrollable {
- //! Navigate an endless amount of content with a scrollbar.
- pub use iced_native::widget::scrollable::{
- snap_to, style::Scrollbar, style::Scroller, Id, Properties,
- RelativeOffset, StyleSheet,
- };
-
- /// A widget that can vertically display an infinite amount of content
- /// with a scrollbar.
- pub type Scrollable<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Scrollable<'a, Message, Renderer>;
-}
-
-pub mod toggler {
- //! Show toggle controls using togglers.
- pub use iced_native::widget::toggler::{Appearance, StyleSheet};
-
- /// A toggler widget.
- pub type Toggler<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Toggler<'a, Message, Renderer>;
-}
-
-pub mod text_input {
- //! Display fields that can be filled with text.
- pub use iced_native::widget::text_input::{
- focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front,
- select_all, Appearance, Id, StyleSheet,
- };
-
- /// A field that can be filled with text.
- pub type TextInput<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::TextInput<'a, Message, Renderer>;
-}
-
-pub mod tooltip {
- //! Display a widget over another.
- pub use iced_native::widget::tooltip::Position;
-
- /// A widget allowing the selection of a single value from a list of options.
- pub type Tooltip<'a, Message, Renderer = crate::Renderer> =
- iced_native::widget::Tooltip<'a, Message, Renderer>;
-}
-
-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;
-pub use checkbox::Checkbox;
-pub use container::Container;
-pub use pane_grid::PaneGrid;
-pub use pick_list::PickList;
-pub use progress_bar::ProgressBar;
-pub use radio::Radio;
-pub use rule::Rule;
-pub use scrollable::Scrollable;
-pub use slider::Slider;
-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")))]
-pub use iced_graphics::widget::canvas;
-
-#[cfg(feature = "canvas")]
-#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
-/// Creates a new [`Canvas`].
-pub fn canvas<P, Message, Theme>(program: P) -> Canvas<Message, Theme, P>
-where
- P: canvas::Program<Message, Theme>,
-{
- Canvas::new(program)
-}
-
-#[cfg(feature = "image")]
-#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
-pub mod image {
- //! Display images in your user interface.
- pub use iced_native::image::Handle;
-
- /// A frame that displays an image.
- pub type Image = iced_native::widget::Image<Handle>;
-
- pub use iced_native::widget::image::viewer;
- pub use viewer::Viewer;
-}
-
-#[cfg(feature = "qr_code")]
-#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
-pub use iced_graphics::widget::qr_code;
-
-#[cfg(feature = "svg")]
-#[cfg_attr(docsrs, doc(cfg(feature = "svg")))]
-pub mod svg {
- //! Display vector graphics in your application.
- pub use iced_native::svg::Handle;
- pub use iced_native::widget::svg::{Appearance, StyleSheet, Svg};
-}
-
-#[cfg(feature = "canvas")]
-#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
-pub use canvas::Canvas;
-
-#[cfg(feature = "image")]
-#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
-pub use image::Image;
-
-#[cfg(feature = "qr_code")]
-#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
-pub use qr_code::QRCode;
-
-#[cfg(feature = "svg")]
-#[cfg_attr(docsrs, doc(cfg(feature = "svg")))]
-pub use svg::Svg;
-
-use crate::Command;
-use iced_native::widget::operation;
-
-/// Focuses the previous focusable widget.
-pub fn focus_previous<Message>() -> Command<Message>
-where
- Message: 'static,
-{
- Command::widget(operation::focusable::focus_previous())
-}
-
-/// Focuses the next focusable widget.
-pub fn focus_next<Message>() -> Command<Message>
-where
- Message: 'static,
-{
- Command::widget(operation::focusable::focus_next())
-}
diff --git a/src/window.rs b/src/window.rs
index 2018053f..824915b2 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -8,5 +8,5 @@ pub use icon::Icon;
pub use position::Position;
pub use settings::Settings;
-#[cfg(not(target_arch = "wasm32"))]
+pub use crate::core::window::*;
pub use crate::runtime::window::*;
diff --git a/style/src/lib.rs b/style/src/lib.rs
index 59eb1eb8..286ff9db 100644
--- a/style/src/lib.rs
+++ b/style/src/lib.rs
@@ -18,7 +18,7 @@
#![deny(missing_docs, unused_results)]
#![forbid(unsafe_code, rust_2018_idioms)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
-pub use iced_core::{Background, Color};
+pub use iced_core as core;
pub mod application;
pub mod button;
@@ -33,7 +33,6 @@ pub mod rule;
pub mod scrollable;
pub mod slider;
pub mod svg;
-pub mod text;
pub mod text_input;
pub mod theme;
pub mod toggler;
diff --git a/style/src/text.rs b/style/src/text.rs
index f31c2306..d5a16bef 100644
--- a/style/src/text.rs
+++ b/style/src/text.rs
@@ -1,5 +1,5 @@
//! Change the appearance of text.
-use iced_core::Color;
+use crate::core::Color;
/// The style sheet of some text.
pub trait StyleSheet {
diff --git a/style/src/theme.rs b/style/src/theme.rs
index 4ba4facf..b507d096 100644
--- a/style/src/theme.rs
+++ b/style/src/theme.rs
@@ -8,6 +8,7 @@ use crate::application;
use crate::button;
use crate::checkbox;
use crate::container;
+use crate::core::widget::text;
use crate::menu;
use crate::pane_grid;
use crate::pick_list;
@@ -17,7 +18,6 @@ use crate::rule;
use crate::scrollable;
use crate::slider;
use crate::svg;
-use crate::text;
use crate::text_input;
use crate::toggler;
diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml
new file mode 100644
index 00000000..69197589
--- /dev/null
+++ b/tiny_skia/Cargo.toml
@@ -0,0 +1,40 @@
+[package]
+name = "iced_tiny_skia"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+image = ["iced_graphics/image"]
+svg = ["resvg"]
+geometry = ["iced_graphics/geometry"]
+
+[dependencies]
+raw-window-handle = "0.5"
+softbuffer = "0.2"
+tiny-skia = "0.8"
+bytemuck = "1"
+rustc-hash = "1.1"
+ouroboros = "0.15"
+kurbo = "0.9"
+
+[dependencies.iced_graphics]
+version = "0.7"
+path = "../graphics"
+features = ["tiny-skia"]
+
+[dependencies.cosmic-text]
+features = ["std", "swash"]
+git = "https://github.com/hecrj/cosmic-text"
+rev = "81080c1b9498933b43c1889601a7ea6a3d16e161"
+
+[dependencies.twox-hash]
+version = "1.6"
+default-features = false
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies.twox-hash]
+version = "1.6.1"
+features = ["std"]
+
+[dependencies.resvg]
+version = "0.29"
+optional = true
diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs
new file mode 100644
index 00000000..ba063f4e
--- /dev/null
+++ b/tiny_skia/src/backend.rs
@@ -0,0 +1,548 @@
+use crate::core::alignment;
+use crate::core::text;
+use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector};
+use crate::graphics::backend;
+use crate::graphics::{Primitive, Viewport};
+use crate::Settings;
+
+use std::borrow::Cow;
+
+pub struct Backend {
+ default_font: Font,
+ default_text_size: f32,
+ text_pipeline: crate::text::Pipeline,
+
+ #[cfg(feature = "image")]
+ raster_pipeline: crate::raster::Pipeline,
+
+ #[cfg(feature = "svg")]
+ vector_pipeline: crate::vector::Pipeline,
+}
+
+impl Backend {
+ pub fn new(settings: Settings) -> Self {
+ Self {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ text_pipeline: crate::text::Pipeline::new(),
+
+ #[cfg(feature = "image")]
+ raster_pipeline: crate::raster::Pipeline::new(),
+
+ #[cfg(feature = "svg")]
+ vector_pipeline: crate::vector::Pipeline::new(),
+ }
+ }
+
+ pub fn draw<T: AsRef<str>>(
+ &mut self,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ clip_mask: &mut tiny_skia::ClipMask,
+ primitives: &[Primitive],
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) {
+ pixels.fill(into_color(background_color));
+
+ let scale_factor = viewport.scale_factor() as f32;
+
+ for primitive in primitives {
+ self.draw_primitive(
+ primitive,
+ pixels,
+ clip_mask,
+ None,
+ scale_factor,
+ Vector::ZERO,
+ );
+ }
+
+ for (i, text) in overlay.iter().enumerate() {
+ const OVERLAY_TEXT_SIZE: f32 = 20.0;
+
+ self.draw_primitive(
+ &Primitive::Text {
+ content: text.as_ref().to_owned(),
+ size: OVERLAY_TEXT_SIZE,
+ bounds: Rectangle {
+ x: 10.0,
+ y: 10.0 + i as f32 * OVERLAY_TEXT_SIZE * 1.2,
+ width: f32::INFINITY,
+ height: f32::INFINITY,
+ },
+ color: Color::BLACK,
+ font: Font::Monospace,
+ horizontal_alignment: alignment::Horizontal::Left,
+ vertical_alignment: alignment::Vertical::Top,
+ },
+ pixels,
+ clip_mask,
+ None,
+ scale_factor,
+ Vector::ZERO,
+ );
+ }
+
+ self.text_pipeline.trim_cache();
+
+ #[cfg(feature = "image")]
+ self.raster_pipeline.trim_cache();
+
+ #[cfg(feature = "svg")]
+ self.vector_pipeline.trim_cache();
+ }
+
+ fn draw_primitive(
+ &mut self,
+ primitive: &Primitive,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ clip_mask: &mut tiny_skia::ClipMask,
+ clip_bounds: Option<Rectangle>,
+ scale_factor: f32,
+ translation: Vector,
+ ) {
+ match primitive {
+ Primitive::Quad {
+ bounds,
+ background,
+ border_radius,
+ border_width,
+ border_color,
+ } => {
+ let transform = tiny_skia::Transform::from_translate(
+ translation.x,
+ translation.y,
+ )
+ .post_scale(scale_factor, scale_factor);
+
+ let path = rounded_rectangle(*bounds, *border_radius);
+ let clip_mask = clip_bounds.map(|_| clip_mask as &_);
+
+ pixels.fill_path(
+ &path,
+ &tiny_skia::Paint {
+ shader: match background {
+ Background::Color(color) => {
+ tiny_skia::Shader::SolidColor(into_color(
+ *color,
+ ))
+ }
+ },
+ anti_alias: true,
+ ..tiny_skia::Paint::default()
+ },
+ tiny_skia::FillRule::EvenOdd,
+ transform,
+ clip_mask,
+ );
+
+ if *border_width > 0.0 {
+ pixels.stroke_path(
+ &path,
+ &tiny_skia::Paint {
+ shader: tiny_skia::Shader::SolidColor(into_color(
+ *border_color,
+ )),
+ anti_alias: true,
+ ..tiny_skia::Paint::default()
+ },
+ &tiny_skia::Stroke {
+ width: *border_width,
+ ..tiny_skia::Stroke::default()
+ },
+ transform,
+ clip_mask,
+ );
+ }
+ }
+ Primitive::Text {
+ content,
+ bounds,
+ color,
+ size,
+ font,
+ horizontal_alignment,
+ vertical_alignment,
+ } => {
+ self.text_pipeline.draw(
+ content,
+ (*bounds + translation) * scale_factor,
+ *color,
+ *size * scale_factor,
+ *font,
+ *horizontal_alignment,
+ *vertical_alignment,
+ pixels,
+ clip_bounds.map(|_| clip_mask as &_),
+ );
+ }
+ #[cfg(feature = "image")]
+ Primitive::Image { handle, bounds } => {
+ let transform = tiny_skia::Transform::from_translate(
+ translation.x,
+ translation.y,
+ )
+ .post_scale(scale_factor, scale_factor);
+
+ self.raster_pipeline.draw(
+ handle,
+ *bounds,
+ pixels,
+ transform,
+ clip_bounds.map(|_| clip_mask as &_),
+ );
+ }
+ #[cfg(feature = "svg")]
+ Primitive::Svg {
+ handle,
+ bounds,
+ color,
+ } => {
+ self.vector_pipeline.draw(
+ handle,
+ *color,
+ (*bounds + translation) * scale_factor,
+ pixels,
+ clip_bounds.map(|_| clip_mask as &_),
+ );
+ }
+ Primitive::Fill {
+ path,
+ paint,
+ rule,
+ transform,
+ } => {
+ pixels.fill_path(
+ path,
+ paint,
+ *rule,
+ transform
+ .post_translate(translation.x, translation.y)
+ .post_scale(scale_factor, scale_factor),
+ clip_bounds.map(|_| clip_mask as &_),
+ );
+ }
+ Primitive::Stroke {
+ path,
+ paint,
+ stroke,
+ transform,
+ } => {
+ pixels.stroke_path(
+ path,
+ paint,
+ stroke,
+ transform
+ .post_translate(translation.x, translation.y)
+ .post_scale(scale_factor, scale_factor),
+ clip_bounds.map(|_| clip_mask as &_),
+ );
+ }
+ Primitive::Group { primitives } => {
+ for primitive in primitives {
+ self.draw_primitive(
+ primitive,
+ pixels,
+ clip_mask,
+ clip_bounds,
+ scale_factor,
+ translation,
+ );
+ }
+ }
+ Primitive::Translate {
+ translation: offset,
+ content,
+ } => {
+ self.draw_primitive(
+ content,
+ pixels,
+ clip_mask,
+ clip_bounds,
+ scale_factor,
+ translation + *offset,
+ );
+ }
+ Primitive::Clip { bounds, content } => {
+ let bounds = (*bounds + translation) * scale_factor;
+
+ adjust_clip_mask(clip_mask, pixels, bounds);
+
+ self.draw_primitive(
+ content,
+ pixels,
+ clip_mask,
+ Some(bounds),
+ scale_factor,
+ translation,
+ );
+
+ if let Some(bounds) = clip_bounds {
+ adjust_clip_mask(clip_mask, pixels, bounds);
+ } else {
+ clip_mask.clear();
+ }
+ }
+ Primitive::Cache { content } => {
+ self.draw_primitive(
+ content,
+ pixels,
+ clip_mask,
+ clip_bounds,
+ scale_factor,
+ translation,
+ );
+ }
+ Primitive::SolidMesh { .. } | Primitive::GradientMesh { .. } => {
+ // Not supported!
+ // TODO: Draw a placeholder (?) / Log it (?)
+ }
+ _ => {
+ // Not supported!
+ }
+ }
+ }
+}
+
+fn into_color(color: Color) -> tiny_skia::Color {
+ tiny_skia::Color::from_rgba(color.b, color.g, color.r, color.a)
+ .expect("Convert color from iced to tiny_skia")
+}
+
+fn rounded_rectangle(
+ bounds: Rectangle,
+ border_radius: [f32; 4],
+) -> tiny_skia::Path {
+ let [top_left, top_right, bottom_right, bottom_left] = border_radius;
+
+ if top_left == 0.0
+ && top_right == 0.0
+ && bottom_right == 0.0
+ && bottom_left == 0.0
+ {
+ return tiny_skia::PathBuilder::from_rect(
+ tiny_skia::Rect::from_xywh(
+ bounds.x,
+ bounds.y,
+ bounds.width,
+ bounds.height,
+ )
+ .expect("Build quad rectangle"),
+ );
+ }
+
+ if top_left == top_right
+ && top_left == bottom_right
+ && top_left == bottom_left
+ && top_left == bounds.width / 2.0
+ && top_left == bounds.height / 2.0
+ {
+ return tiny_skia::PathBuilder::from_circle(
+ bounds.x + bounds.width / 2.0,
+ bounds.y + bounds.height / 2.0,
+ top_left,
+ )
+ .expect("Build circle path");
+ }
+
+ let mut builder = tiny_skia::PathBuilder::new();
+
+ builder.move_to(bounds.x + top_left, bounds.y);
+ builder.line_to(bounds.x + bounds.width - top_right, bounds.y);
+
+ if top_right > 0.0 {
+ arc_to(
+ &mut builder,
+ bounds.x + bounds.width - top_right,
+ bounds.y,
+ bounds.x + bounds.width,
+ bounds.y + top_right,
+ top_right,
+ );
+ }
+
+ maybe_line_to(
+ &mut builder,
+ bounds.x + bounds.width,
+ bounds.y + bounds.height - bottom_right,
+ );
+
+ if bottom_right > 0.0 {
+ arc_to(
+ &mut builder,
+ bounds.x + bounds.width,
+ bounds.y + bounds.height - bottom_right,
+ bounds.x + bounds.width - bottom_right,
+ bounds.y + bounds.height,
+ bottom_right,
+ );
+ }
+
+ maybe_line_to(
+ &mut builder,
+ bounds.x + bottom_left,
+ bounds.y + bounds.height,
+ );
+
+ if bottom_right > 0.0 {
+ arc_to(
+ &mut builder,
+ bounds.x + bottom_left,
+ bounds.y + bounds.height,
+ bounds.x,
+ bounds.y + bounds.height - bottom_left,
+ bottom_left,
+ );
+ }
+
+ maybe_line_to(&mut builder, bounds.x, bounds.y + top_left);
+
+ if top_left > 0.0 {
+ arc_to(
+ &mut builder,
+ bounds.x,
+ bounds.y + top_left,
+ bounds.x + top_left,
+ bounds.y,
+ top_left,
+ );
+ }
+
+ builder.finish().expect("Build rounded rectangle path")
+}
+
+fn maybe_line_to(path: &mut tiny_skia::PathBuilder, x: f32, y: f32) {
+ if path.last_point() != Some(tiny_skia::Point { x, y }) {
+ path.line_to(x, y);
+ }
+}
+
+fn arc_to(
+ path: &mut tiny_skia::PathBuilder,
+ x_from: f32,
+ y_from: f32,
+ x_to: f32,
+ y_to: f32,
+ radius: f32,
+) {
+ let svg_arc = kurbo::SvgArc {
+ from: kurbo::Point::new(f64::from(x_from), f64::from(y_from)),
+ to: kurbo::Point::new(f64::from(x_to), f64::from(y_to)),
+ radii: kurbo::Vec2::new(f64::from(radius), f64::from(radius)),
+ x_rotation: 0.0,
+ large_arc: false,
+ sweep: true,
+ };
+
+ match kurbo::Arc::from_svg_arc(&svg_arc) {
+ Some(arc) => {
+ arc.to_cubic_beziers(0.1, |p1, p2, p| {
+ path.cubic_to(
+ p1.x as f32,
+ p1.y as f32,
+ p2.x as f32,
+ p2.y as f32,
+ p.x as f32,
+ p.y as f32,
+ );
+ });
+ }
+ None => {
+ path.line_to(x_to, y_to);
+ }
+ }
+}
+
+fn adjust_clip_mask(
+ clip_mask: &mut tiny_skia::ClipMask,
+ pixels: &tiny_skia::PixmapMut<'_>,
+ bounds: Rectangle,
+) {
+ let path = {
+ let mut builder = tiny_skia::PathBuilder::new();
+ builder.push_rect(bounds.x, bounds.y, bounds.width, bounds.height);
+
+ builder.finish().unwrap()
+ };
+
+ clip_mask
+ .set_path(
+ pixels.width(),
+ pixels.height(),
+ &path,
+ tiny_skia::FillRule::EvenOdd,
+ true,
+ )
+ .expect("Set path of clipping area");
+}
+
+impl iced_graphics::Backend for Backend {
+ fn trim_measurements(&mut self) {
+ self.text_pipeline.trim_measurement_cache();
+ }
+}
+
+impl backend::Text for Backend {
+ const ICON_FONT: Font = Font::Name("Iced-Icons");
+ const CHECKMARK_ICON: char = '\u{f00c}';
+ const ARROW_DOWN_ICON: char = '\u{e800}';
+
+ fn default_font(&self) -> Font {
+ self.default_font
+ }
+
+ fn default_size(&self) -> f32 {
+ self.default_text_size
+ }
+
+ fn measure(
+ &self,
+ contents: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ ) -> (f32, f32) {
+ self.text_pipeline.measure(contents, size, font, bounds)
+ }
+
+ fn hit_test(
+ &self,
+ contents: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ point: Point,
+ nearest_only: bool,
+ ) -> Option<text::Hit> {
+ self.text_pipeline.hit_test(
+ contents,
+ size,
+ font,
+ bounds,
+ point,
+ nearest_only,
+ )
+ }
+
+ fn load_font(&mut self, font: Cow<'static, [u8]>) {
+ self.text_pipeline.load_font(font);
+ }
+}
+
+#[cfg(feature = "image")]
+impl backend::Image for Backend {
+ fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
+ self.raster_pipeline.dimensions(handle)
+ }
+}
+
+#[cfg(feature = "svg")]
+impl backend::Svg for Backend {
+ fn viewport_dimensions(
+ &self,
+ handle: &crate::core::svg::Handle,
+ ) -> Size<u32> {
+ self.vector_pipeline.viewport_dimensions(handle)
+ }
+}
diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs
new file mode 100644
index 00000000..c66621dd
--- /dev/null
+++ b/tiny_skia/src/geometry.rs
@@ -0,0 +1,287 @@
+use crate::core::Gradient;
+use crate::core::{Point, Rectangle, Size, Vector};
+use crate::graphics::geometry::fill::{self, Fill};
+use crate::graphics::geometry::stroke::{self, Stroke};
+use crate::graphics::geometry::{Path, Style, Text};
+use crate::graphics::Primitive;
+
+pub struct Frame {
+ size: Size,
+ transform: tiny_skia::Transform,
+ stack: Vec<tiny_skia::Transform>,
+ primitives: Vec<Primitive>,
+}
+
+impl Frame {
+ pub fn new(size: Size) -> Self {
+ Self {
+ size,
+ transform: tiny_skia::Transform::identity(),
+ stack: Vec::new(),
+ primitives: Vec::new(),
+ }
+ }
+
+ pub fn width(&self) -> f32 {
+ self.size.width
+ }
+
+ pub fn height(&self) -> f32 {
+ self.size.height
+ }
+
+ pub fn size(&self) -> Size {
+ self.size
+ }
+
+ pub fn center(&self) -> Point {
+ Point::new(self.size.width / 2.0, self.size.height / 2.0)
+ }
+
+ pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
+ let path = convert_path(path);
+ let fill = fill.into();
+
+ self.primitives.push(Primitive::Fill {
+ path,
+ paint: into_paint(fill.style),
+ rule: into_fill_rule(fill.rule),
+ transform: self.transform,
+ });
+ }
+
+ pub fn fill_rectangle(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ fill: impl Into<Fill>,
+ ) {
+ let path = convert_path(&Path::rectangle(top_left, size));
+ let fill = fill.into();
+
+ self.primitives.push(Primitive::Fill {
+ path,
+ paint: tiny_skia::Paint {
+ anti_alias: false,
+ ..into_paint(fill.style)
+ },
+ rule: into_fill_rule(fill.rule),
+ transform: self.transform,
+ });
+ }
+
+ pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
+ let path = convert_path(path);
+ let stroke = stroke.into();
+ let skia_stroke = into_stroke(&stroke);
+
+ self.primitives.push(Primitive::Stroke {
+ path,
+ paint: into_paint(stroke.style),
+ stroke: skia_stroke,
+ transform: self.transform,
+ });
+ }
+
+ pub fn fill_text(&mut self, text: impl Into<Text>) {
+ let text = text.into();
+
+ let position = if self.transform.is_identity() {
+ text.position
+ } else {
+ let mut transformed = [tiny_skia::Point {
+ x: text.position.x,
+ y: text.position.y,
+ }];
+
+ self.transform.map_points(&mut transformed);
+
+ Point::new(transformed[0].x, transformed[0].y)
+ };
+
+ // TODO: Use vectorial text instead of primitive
+ self.primitives.push(Primitive::Text {
+ content: text.content,
+ bounds: Rectangle {
+ x: position.x,
+ y: position.y,
+ width: f32::INFINITY,
+ height: f32::INFINITY,
+ },
+ color: text.color,
+ size: text.size,
+ font: text.font,
+ horizontal_alignment: text.horizontal_alignment,
+ vertical_alignment: text.vertical_alignment,
+ });
+ }
+
+ pub fn push_transform(&mut self) {
+ self.stack.push(self.transform);
+ }
+
+ pub fn pop_transform(&mut self) {
+ self.transform = self.stack.pop().expect("Pop transform");
+ }
+
+ pub fn clip(&mut self, _frame: Self, _translation: Vector) {}
+
+ pub fn translate(&mut self, translation: Vector) {
+ self.transform =
+ self.transform.pre_translate(translation.x, translation.y);
+ }
+
+ pub fn rotate(&mut self, angle: f32) {
+ self.transform = self
+ .transform
+ .pre_concat(tiny_skia::Transform::from_rotate(angle.to_degrees()));
+ }
+
+ pub fn scale(&mut self, scale: f32) {
+ self.transform = self.transform.pre_scale(scale, scale);
+ }
+
+ pub fn into_primitive(self) -> Primitive {
+ Primitive::Clip {
+ bounds: Rectangle::new(Point::ORIGIN, self.size),
+ content: Box::new(Primitive::Group {
+ primitives: self.primitives,
+ }),
+ }
+ }
+}
+
+fn convert_path(path: &Path) -> tiny_skia::Path {
+ use iced_graphics::geometry::path::lyon_path;
+
+ let mut builder = tiny_skia::PathBuilder::new();
+ let mut last_point = Default::default();
+
+ for event in path.raw().iter() {
+ match event {
+ lyon_path::Event::Begin { at } => {
+ builder.move_to(at.x, at.y);
+
+ last_point = at;
+ }
+ lyon_path::Event::Line { from, to } => {
+ if last_point != from {
+ builder.move_to(from.x, from.y);
+ }
+
+ builder.line_to(to.x, to.y);
+
+ last_point = to;
+ }
+ lyon_path::Event::Quadratic { from, ctrl, to } => {
+ if last_point != from {
+ builder.move_to(from.x, from.y);
+ }
+
+ builder.quad_to(ctrl.x, ctrl.y, to.x, to.y);
+
+ last_point = to;
+ }
+ lyon_path::Event::Cubic {
+ from,
+ ctrl1,
+ ctrl2,
+ to,
+ } => {
+ if last_point != from {
+ builder.move_to(from.x, from.y);
+ }
+
+ builder
+ .cubic_to(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, to.x, to.y);
+
+ last_point = to;
+ }
+ lyon_path::Event::End { close, .. } => {
+ if close {
+ builder.close();
+ }
+ }
+ }
+ }
+
+ builder
+ .finish()
+ .expect("Convert lyon path to tiny_skia path")
+}
+
+pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> {
+ tiny_skia::Paint {
+ shader: match style {
+ Style::Solid(color) => tiny_skia::Shader::SolidColor(
+ tiny_skia::Color::from_rgba(color.b, color.g, color.r, color.a)
+ .expect("Create color"),
+ ),
+ Style::Gradient(gradient) => match gradient {
+ Gradient::Linear(linear) => tiny_skia::LinearGradient::new(
+ tiny_skia::Point {
+ x: linear.start.x,
+ y: linear.start.y,
+ },
+ tiny_skia::Point {
+ x: linear.end.x,
+ y: linear.end.y,
+ },
+ linear
+ .color_stops
+ .into_iter()
+ .map(|stop| {
+ tiny_skia::GradientStop::new(
+ stop.offset,
+ tiny_skia::Color::from_rgba(
+ stop.color.b,
+ stop.color.g,
+ stop.color.r,
+ stop.color.a,
+ )
+ .expect("Create color"),
+ )
+ })
+ .collect(),
+ tiny_skia::SpreadMode::Pad,
+ tiny_skia::Transform::identity(),
+ )
+ .expect("Create linear gradient"),
+ },
+ },
+ anti_alias: true,
+ ..Default::default()
+ }
+}
+
+pub fn into_fill_rule(rule: fill::Rule) -> tiny_skia::FillRule {
+ match rule {
+ fill::Rule::EvenOdd => tiny_skia::FillRule::EvenOdd,
+ fill::Rule::NonZero => tiny_skia::FillRule::Winding,
+ }
+}
+
+pub fn into_stroke(stroke: &Stroke) -> tiny_skia::Stroke {
+ tiny_skia::Stroke {
+ width: stroke.width,
+ line_cap: match stroke.line_cap {
+ stroke::LineCap::Butt => tiny_skia::LineCap::Butt,
+ stroke::LineCap::Square => tiny_skia::LineCap::Square,
+ stroke::LineCap::Round => tiny_skia::LineCap::Round,
+ },
+ line_join: match stroke.line_join {
+ stroke::LineJoin::Miter => tiny_skia::LineJoin::Miter,
+ stroke::LineJoin::Round => tiny_skia::LineJoin::Round,
+ stroke::LineJoin::Bevel => tiny_skia::LineJoin::Bevel,
+ },
+ dash: if stroke.line_dash.segments.is_empty() {
+ None
+ } else {
+ tiny_skia::StrokeDash::new(
+ stroke.line_dash.segments.into(),
+ stroke.line_dash.offset as f32,
+ )
+ },
+ ..Default::default()
+ }
+}
diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs
new file mode 100644
index 00000000..83baef1c
--- /dev/null
+++ b/tiny_skia/src/lib.rs
@@ -0,0 +1,26 @@
+pub mod window;
+
+mod backend;
+mod settings;
+mod text;
+
+#[cfg(feature = "image")]
+mod raster;
+
+#[cfg(feature = "svg")]
+mod vector;
+
+#[cfg(feature = "geometry")]
+pub mod geometry;
+
+pub use iced_graphics as graphics;
+pub use iced_graphics::core;
+
+pub use backend::Backend;
+pub use settings::Settings;
+
+/// A [`tiny-skia`] graphics renderer for [`iced`].
+///
+/// [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia
+/// [`iced`]: https://github.com/iced-rs/iced
+pub type Renderer<Theme> = iced_graphics::Renderer<Backend, Theme>;
diff --git a/tiny_skia/src/primitive.rs b/tiny_skia/src/primitive.rs
new file mode 100644
index 00000000..22daaedc
--- /dev/null
+++ b/tiny_skia/src/primitive.rs
@@ -0,0 +1,82 @@
+use crate::{Rectangle, Vector};
+
+use std::sync::Arc;
+
+#[derive(Debug, Clone)]
+pub enum Primitive {
+ /// A group of primitives
+ Group {
+ /// The primitives of the group
+ primitives: Vec<Primitive>,
+ },
+ /// A clip primitive
+ Clip {
+ /// The bounds of the clip
+ bounds: Rectangle,
+ /// The content of the clip
+ content: Box<Primitive>,
+ },
+ /// A primitive that applies a translation
+ Translate {
+ /// The translation vector
+ translation: Vector,
+
+ /// The primitive to translate
+ content: Box<Primitive>,
+ },
+ /// A cached primitive.
+ ///
+ /// This can be useful if you are implementing a widget where primitive
+ /// generation is expensive.
+ Cached {
+ /// The cached primitive
+ cache: Arc<Primitive>,
+ },
+ /// A basic primitive.
+ Basic(iced_graphics::Primitive),
+}
+
+impl iced_graphics::backend::Primitive for Primitive {
+ fn translate(self, translation: Vector) -> Self {
+ Self::Translate {
+ translation,
+ content: Box::new(self),
+ }
+ }
+
+ fn clip(self, bounds: Rectangle) -> Self {
+ Self::Clip {
+ bounds,
+ content: Box::new(self),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct Recording(pub(crate) Vec<Primitive>);
+
+impl iced_graphics::backend::Recording for Recording {
+ type Primitive = Primitive;
+
+ fn push(&mut self, primitive: Primitive) {
+ self.0.push(primitive);
+ }
+
+ fn push_basic(&mut self, basic: iced_graphics::Primitive) {
+ self.0.push(Primitive::Basic(basic));
+ }
+
+ fn group(self) -> Self::Primitive {
+ Primitive::Group { primitives: self.0 }
+ }
+
+ fn clear(&mut self) {
+ self.0.clear();
+ }
+}
+
+impl Recording {
+ pub fn primitives(&self) -> &[Primitive] {
+ &self.0
+ }
+}
diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs
new file mode 100644
index 00000000..2fd73f8c
--- /dev/null
+++ b/tiny_skia/src/raster.rs
@@ -0,0 +1,116 @@
+use crate::core::image as raster;
+use crate::core::{Rectangle, Size};
+use crate::graphics;
+
+use rustc_hash::{FxHashMap, FxHashSet};
+use std::cell::RefCell;
+use std::collections::hash_map;
+
+pub struct Pipeline {
+ cache: RefCell<Cache>,
+}
+
+impl Pipeline {
+ pub fn new() -> Self {
+ Self {
+ cache: RefCell::new(Cache::default()),
+ }
+ }
+
+ pub fn dimensions(&self, handle: &raster::Handle) -> Size<u32> {
+ if let Some(image) = self.cache.borrow_mut().allocate(handle) {
+ Size::new(image.width(), image.height())
+ } else {
+ Size::new(0, 0)
+ }
+ }
+
+ pub fn draw(
+ &mut self,
+ handle: &raster::Handle,
+ bounds: Rectangle,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ transform: tiny_skia::Transform,
+ clip_mask: Option<&tiny_skia::ClipMask>,
+ ) {
+ if let Some(image) = self.cache.borrow_mut().allocate(handle) {
+ let width_scale = bounds.width / image.width() as f32;
+ let height_scale = bounds.height / image.height() as f32;
+
+ let transform = transform.pre_scale(width_scale, height_scale);
+
+ pixels.draw_pixmap(
+ (bounds.x / width_scale) as i32,
+ (bounds.y / height_scale) as i32,
+ image,
+ &tiny_skia::PixmapPaint {
+ quality: tiny_skia::FilterQuality::Bilinear,
+ ..Default::default()
+ },
+ transform,
+ clip_mask,
+ );
+ }
+ }
+
+ pub fn trim_cache(&mut self) {
+ self.cache.borrow_mut().trim();
+ }
+}
+
+#[derive(Default)]
+struct Cache {
+ entries: FxHashMap<u64, Option<Entry>>,
+ hits: FxHashSet<u64>,
+}
+
+impl Cache {
+ pub fn allocate(
+ &mut self,
+ handle: &raster::Handle,
+ ) -> Option<tiny_skia::PixmapRef<'_>> {
+ let id = handle.id();
+
+ if let hash_map::Entry::Vacant(entry) = self.entries.entry(id) {
+ let image = graphics::image::load(handle).ok()?.into_rgba8();
+
+ let mut buffer =
+ vec![0u32; image.width() as usize * image.height() as usize];
+
+ for (i, pixel) in image.pixels().enumerate() {
+ let [r, g, b, a] = pixel.0;
+
+ buffer[i] = tiny_skia::ColorU8::from_rgba(b, g, r, a)
+ .premultiply()
+ .get();
+ }
+
+ entry.insert(Some(Entry {
+ width: image.width(),
+ height: image.height(),
+ pixels: buffer,
+ }));
+ }
+
+ self.hits.insert(id);
+ self.entries.get(&id).unwrap().as_ref().map(|entry| {
+ tiny_skia::PixmapRef::from_bytes(
+ bytemuck::cast_slice(&entry.pixels),
+ entry.width,
+ entry.height,
+ )
+ .expect("Build pixmap from image bytes")
+ })
+ }
+
+ fn trim(&mut self) {
+ self.entries.retain(|key, _| self.hits.contains(key));
+ self.hits.clear();
+ }
+}
+
+struct Entry {
+ width: u32,
+ height: u32,
+ pixels: Vec<u32>,
+}
diff --git a/tiny_skia/src/settings.rs b/tiny_skia/src/settings.rs
new file mode 100644
index 00000000..9e4be0c4
--- /dev/null
+++ b/tiny_skia/src/settings.rs
@@ -0,0 +1,24 @@
+use crate::core::Font;
+
+/// The settings of a [`Backend`].
+///
+/// [`Backend`]: crate::Backend
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Settings {
+ /// The default [`Font`] to use.
+ pub default_font: Font,
+
+ /// The default size of text.
+ ///
+ /// By default, it will be set to `16.0`.
+ pub default_text_size: f32,
+}
+
+impl Default for Settings {
+ fn default() -> Settings {
+ Settings {
+ default_font: Font::SansSerif,
+ default_text_size: 16.0,
+ }
+ }
+}
diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs
new file mode 100644
index 00000000..f2935efa
--- /dev/null
+++ b/tiny_skia/src/text.rs
@@ -0,0 +1,431 @@
+use crate::core::alignment;
+use crate::core::text::Hit;
+use crate::core::{Color, Font, Point, Rectangle, Size};
+
+use rustc_hash::{FxHashMap, FxHashSet};
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::hash_map;
+use std::hash::{BuildHasher, Hash, Hasher};
+use std::sync::Arc;
+
+#[allow(missing_debug_implementations)]
+pub struct Pipeline {
+ system: Option<System>,
+ glyph_cache: GlyphCache,
+}
+
+#[ouroboros::self_referencing]
+struct System {
+ fonts: cosmic_text::FontSystem,
+
+ #[borrows(fonts)]
+ #[not_covariant]
+ measurement_cache: RefCell<Cache<'this>>,
+
+ #[borrows(fonts)]
+ #[not_covariant]
+ render_cache: Cache<'this>,
+}
+
+impl Pipeline {
+ pub fn new() -> Self {
+ Pipeline {
+ system: Some(
+ SystemBuilder {
+ fonts: cosmic_text::FontSystem::new_with_fonts(
+ [cosmic_text::fontdb::Source::Binary(Arc::new(
+ include_bytes!("../../wgpu/fonts/Iced-Icons.ttf")
+ .as_slice(),
+ ))]
+ .into_iter(),
+ ),
+ measurement_cache_builder: |_| RefCell::new(Cache::new()),
+ render_cache_builder: |_| Cache::new(),
+ }
+ .build(),
+ ),
+ glyph_cache: GlyphCache::new(),
+ }
+ }
+
+ pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
+ let heads = self.system.take().unwrap().into_heads();
+
+ let (locale, mut db) = heads.fonts.into_locale_and_db();
+
+ db.load_font_source(cosmic_text::fontdb::Source::Binary(Arc::new(
+ bytes.into_owned(),
+ )));
+
+ self.system = Some(
+ SystemBuilder {
+ fonts: cosmic_text::FontSystem::new_with_locale_and_db(
+ locale, db,
+ ),
+ measurement_cache_builder: |_| RefCell::new(Cache::new()),
+ render_cache_builder: |_| Cache::new(),
+ }
+ .build(),
+ );
+ }
+
+ pub fn draw(
+ &mut self,
+ content: &str,
+ bounds: Rectangle,
+ color: Color,
+ size: f32,
+ font: Font,
+ horizontal_alignment: alignment::Horizontal,
+ vertical_alignment: alignment::Vertical,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ clip_mask: Option<&tiny_skia::ClipMask>,
+ ) {
+ self.system.as_mut().unwrap().with_mut(|fields| {
+ let key = Key {
+ bounds: {
+ let size = bounds.size();
+
+ // TODO: Reuse buffers from layouting
+ Size::new(size.width.ceil(), size.height.ceil())
+ },
+ content,
+ font,
+ size,
+ };
+
+ let (_, buffer) = fields.render_cache.allocate(fields.fonts, key);
+
+ let (total_lines, max_width) = buffer
+ .layout_runs()
+ .enumerate()
+ .fold((0, 0.0), |(_, max), (i, buffer)| {
+ (i + 1, buffer.line_w.max(max))
+ });
+
+ let total_height = total_lines as f32 * size * 1.2;
+
+ let x = match horizontal_alignment {
+ alignment::Horizontal::Left => bounds.x,
+ alignment::Horizontal::Center => bounds.x - max_width / 2.0,
+ alignment::Horizontal::Right => bounds.x - max_width,
+ };
+
+ let y = match vertical_alignment {
+ alignment::Vertical::Top => bounds.y,
+ alignment::Vertical::Center => bounds.y - total_height / 2.0,
+ alignment::Vertical::Bottom => bounds.y - total_height,
+ };
+
+ let mut swash = cosmic_text::SwashCache::new(fields.fonts);
+
+ for run in buffer.layout_runs() {
+ for glyph in run.glyphs {
+ if let Some((buffer, placement)) = self
+ .glyph_cache
+ .allocate(glyph.cache_key, color, &mut swash)
+ {
+ let pixmap = tiny_skia::PixmapRef::from_bytes(
+ buffer,
+ placement.width,
+ placement.height,
+ )
+ .expect("Create glyph pixel map");
+
+ pixels.draw_pixmap(
+ x as i32 + glyph.x_int + placement.left,
+ y as i32 - glyph.y_int - placement.top
+ + run.line_y as i32,
+ pixmap,
+ &tiny_skia::PixmapPaint::default(),
+ tiny_skia::Transform::identity(),
+ clip_mask,
+ );
+ }
+ }
+ }
+ });
+ }
+
+ pub fn trim_cache(&mut self) {
+ self.system
+ .as_mut()
+ .unwrap()
+ .with_render_cache_mut(|cache| cache.trim());
+
+ self.glyph_cache.trim();
+ }
+
+ pub fn measure(
+ &self,
+ content: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ ) -> (f32, f32) {
+ self.system.as_ref().unwrap().with(|fields| {
+ let mut measurement_cache = fields.measurement_cache.borrow_mut();
+
+ let (_, paragraph) = measurement_cache.allocate(
+ fields.fonts,
+ Key {
+ content,
+ size,
+ font,
+ bounds,
+ },
+ );
+
+ let (total_lines, max_width) = paragraph
+ .layout_runs()
+ .enumerate()
+ .fold((0, 0.0), |(_, max), (i, buffer)| {
+ (i + 1, buffer.line_w.max(max))
+ });
+
+ (max_width, size * 1.2 * total_lines as f32)
+ })
+ }
+
+ pub fn hit_test(
+ &self,
+ content: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ point: Point,
+ _nearest_only: bool,
+ ) -> Option<Hit> {
+ self.system.as_ref().unwrap().with(|fields| {
+ let mut measurement_cache = fields.measurement_cache.borrow_mut();
+
+ let (_, paragraph) = measurement_cache.allocate(
+ fields.fonts,
+ Key {
+ content,
+ size,
+ font,
+ bounds,
+ },
+ );
+
+ let cursor = paragraph.hit(point.x, point.y)?;
+
+ Some(Hit::CharOffset(cursor.index))
+ })
+ }
+
+ pub fn trim_measurement_cache(&mut self) {
+ self.system
+ .as_mut()
+ .unwrap()
+ .with_measurement_cache_mut(|cache| cache.borrow_mut().trim());
+ }
+}
+
+fn to_family(font: Font) -> cosmic_text::Family<'static> {
+ match font {
+ Font::Name(name) => cosmic_text::Family::Name(name),
+ Font::SansSerif => cosmic_text::Family::SansSerif,
+ Font::Serif => cosmic_text::Family::Serif,
+ Font::Cursive => cosmic_text::Family::Cursive,
+ Font::Fantasy => cosmic_text::Family::Fantasy,
+ Font::Monospace => cosmic_text::Family::Monospace,
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+struct GlyphCache {
+ entries: FxHashMap<
+ (cosmic_text::CacheKey, [u8; 3]),
+ (Vec<u32>, cosmic_text::Placement),
+ >,
+ recently_used: FxHashSet<(cosmic_text::CacheKey, [u8; 3])>,
+ trim_count: usize,
+}
+
+impl GlyphCache {
+ fn new() -> Self {
+ GlyphCache::default()
+ }
+
+ fn allocate(
+ &mut self,
+ cache_key: cosmic_text::CacheKey,
+ color: Color,
+ swash: &mut cosmic_text::SwashCache<'_>,
+ ) -> Option<(&[u8], cosmic_text::Placement)> {
+ let [r, g, b, _a] = color.into_rgba8();
+ let key = (cache_key, [r, g, b]);
+
+ if let hash_map::Entry::Vacant(entry) = self.entries.entry(key) {
+ // TODO: Outline support
+ let image = swash.get_image_uncached(cache_key)?;
+
+ let glyph_size = image.placement.width as usize
+ * image.placement.height as usize;
+
+ if glyph_size == 0 {
+ return None;
+ }
+
+ let mut buffer = vec![0u32; glyph_size];
+
+ match image.content {
+ cosmic_text::SwashContent::Mask => {
+ let mut i = 0;
+
+ // TODO: Blend alpha
+
+ for _y in 0..image.placement.height {
+ for _x in 0..image.placement.width {
+ buffer[i] = tiny_skia::ColorU8::from_rgba(
+ b,
+ g,
+ r,
+ image.data[i],
+ )
+ .premultiply()
+ .get();
+
+ i += 1;
+ }
+ }
+ }
+ cosmic_text::SwashContent::Color => {
+ let mut i = 0;
+
+ for _y in 0..image.placement.height {
+ for _x in 0..image.placement.width {
+ // TODO: Blend alpha
+ buffer[i >> 2] = tiny_skia::ColorU8::from_rgba(
+ image.data[i + 2],
+ image.data[i + 1],
+ image.data[i],
+ image.data[i + 3],
+ )
+ .premultiply()
+ .get();
+
+ i += 4;
+ }
+ }
+ }
+ cosmic_text::SwashContent::SubpixelMask => {
+ // TODO
+ }
+ }
+
+ entry.insert((buffer, image.placement));
+ }
+
+ self.recently_used.insert(key);
+
+ self.entries.get(&key).map(|(buffer, placement)| {
+ (bytemuck::cast_slice(buffer.as_slice()), *placement)
+ })
+ }
+
+ pub fn trim(&mut self) {
+ if self.trim_count > 300 {
+ self.entries
+ .retain(|key, _| self.recently_used.contains(key));
+
+ self.recently_used.clear();
+
+ self.trim_count = 0;
+ } else {
+ self.trim_count += 1;
+ }
+ }
+}
+
+struct Cache<'a> {
+ entries: FxHashMap<KeyHash, cosmic_text::Buffer<'a>>,
+ recently_used: FxHashSet<KeyHash>,
+ hasher: HashBuilder,
+ trim_count: usize,
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+type HashBuilder = twox_hash::RandomXxHashBuilder64;
+
+#[cfg(target_arch = "wasm32")]
+type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>;
+
+impl<'a> Cache<'a> {
+ const TRIM_INTERVAL: usize = 300;
+
+ fn new() -> Self {
+ Self {
+ entries: FxHashMap::default(),
+ recently_used: FxHashSet::default(),
+ hasher: HashBuilder::default(),
+ trim_count: 0,
+ }
+ }
+
+ fn allocate(
+ &mut self,
+ fonts: &'a cosmic_text::FontSystem,
+ key: Key<'_>,
+ ) -> (KeyHash, &mut cosmic_text::Buffer<'a>) {
+ let hash = {
+ let mut hasher = self.hasher.build_hasher();
+
+ key.content.hash(&mut hasher);
+ key.size.to_bits().hash(&mut hasher);
+ key.font.hash(&mut hasher);
+ key.bounds.width.to_bits().hash(&mut hasher);
+ key.bounds.height.to_bits().hash(&mut hasher);
+
+ hasher.finish()
+ };
+
+ if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
+ let metrics = cosmic_text::Metrics::new(key.size, key.size * 1.2);
+ let mut buffer = cosmic_text::Buffer::new(fonts, metrics);
+
+ buffer.set_size(
+ key.bounds.width,
+ key.bounds.height.max(key.size * 1.2),
+ );
+ buffer.set_text(
+ key.content,
+ cosmic_text::Attrs::new()
+ .family(to_family(key.font))
+ .monospaced(matches!(key.font, Font::Monospace)),
+ );
+
+ let _ = entry.insert(buffer);
+ }
+
+ let _ = self.recently_used.insert(hash);
+
+ (hash, self.entries.get_mut(&hash).unwrap())
+ }
+
+ fn trim(&mut self) {
+ if self.trim_count >= Self::TRIM_INTERVAL {
+ self.entries
+ .retain(|key, _| self.recently_used.contains(key));
+
+ self.recently_used.clear();
+
+ self.trim_count = 0;
+ } else {
+ self.trim_count += 1;
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+struct Key<'a> {
+ content: &'a str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+}
+
+type KeyHash = u64;
diff --git a/tiny_skia/src/vector.rs b/tiny_skia/src/vector.rs
new file mode 100644
index 00000000..8509b761
--- /dev/null
+++ b/tiny_skia/src/vector.rs
@@ -0,0 +1,180 @@
+use crate::core::svg::{Data, Handle};
+use crate::core::{Color, Rectangle, Size};
+
+use resvg::usvg;
+use rustc_hash::{FxHashMap, FxHashSet};
+
+use std::cell::RefCell;
+use std::collections::hash_map;
+use std::fs;
+
+pub struct Pipeline {
+ cache: RefCell<Cache>,
+}
+
+impl Pipeline {
+ pub fn new() -> Self {
+ Self {
+ cache: RefCell::new(Cache::default()),
+ }
+ }
+
+ pub fn viewport_dimensions(&self, handle: &Handle) -> Size<u32> {
+ self.cache
+ .borrow_mut()
+ .viewport_dimensions(handle)
+ .unwrap_or(Size::new(0, 0))
+ }
+
+ pub fn draw(
+ &mut self,
+ handle: &Handle,
+ color: Option<Color>,
+ bounds: Rectangle,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ clip_mask: Option<&tiny_skia::ClipMask>,
+ ) {
+ if let Some(image) = self.cache.borrow_mut().draw(
+ handle,
+ color,
+ Size::new(bounds.width as u32, bounds.height as u32),
+ ) {
+ pixels.draw_pixmap(
+ bounds.x as i32,
+ bounds.y as i32,
+ image,
+ &tiny_skia::PixmapPaint::default(),
+ tiny_skia::Transform::identity(),
+ clip_mask,
+ );
+ }
+ }
+
+ pub fn trim_cache(&mut self) {
+ self.cache.borrow_mut().trim();
+ }
+}
+
+#[derive(Default)]
+struct Cache {
+ trees: FxHashMap<u64, Option<resvg::usvg::Tree>>,
+ tree_hits: FxHashSet<u64>,
+ rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>,
+ raster_hits: FxHashSet<RasterKey>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+struct RasterKey {
+ id: u64,
+ color: Option<[u8; 4]>,
+ size: Size<u32>,
+}
+
+impl Cache {
+ fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> {
+ let id = handle.id();
+
+ if let hash_map::Entry::Vacant(entry) = self.trees.entry(id) {
+ let svg = match handle.data() {
+ Data::Path(path) => {
+ fs::read_to_string(path).ok().and_then(|contents| {
+ usvg::Tree::from_str(
+ &contents,
+ &usvg::Options::default(),
+ )
+ .ok()
+ })
+ }
+ Data::Bytes(bytes) => {
+ usvg::Tree::from_data(bytes, &usvg::Options::default()).ok()
+ }
+ };
+
+ entry.insert(svg);
+ }
+
+ self.tree_hits.insert(id);
+ self.trees.get(&id).unwrap().as_ref()
+ }
+
+ fn viewport_dimensions(&mut self, handle: &Handle) -> Option<Size<u32>> {
+ let tree = self.load(handle)?;
+
+ Some(Size::new(
+ tree.size.width() as u32,
+ tree.size.height() as u32,
+ ))
+ }
+
+ fn draw(
+ &mut self,
+ handle: &Handle,
+ color: Option<Color>,
+ size: Size<u32>,
+ ) -> Option<tiny_skia::PixmapRef<'_>> {
+ if size.width == 0 || size.height == 0 {
+ return None;
+ }
+
+ let key = RasterKey {
+ id: handle.id(),
+ color: color.map(Color::into_rgba8),
+ size,
+ };
+
+ #[allow(clippy::map_entry)]
+ if !self.rasters.contains_key(&key) {
+ let tree = self.load(handle)?;
+
+ let mut image = tiny_skia::Pixmap::new(size.width, size.height)?;
+
+ resvg::render(
+ tree,
+ if size.width > size.height {
+ usvg::FitTo::Width(size.width)
+ } else {
+ usvg::FitTo::Height(size.height)
+ },
+ tiny_skia::Transform::default(),
+ image.as_mut(),
+ )?;
+
+ if let Some([r, g, b, a]) = key.color {
+ // TODO: Blend alpha
+ let color = tiny_skia::ColorU8::from_rgba(b, g, r, a)
+ .premultiply()
+ .get()
+ & 0x00FFFFFF;
+
+ // Apply color filter
+ for pixel in
+ bytemuck::cast_slice_mut::<u8, u32>(image.data_mut())
+ {
+ *pixel = *pixel & 0xFF000000 | color;
+ }
+ } else {
+ // Swap R and B channels for `softbuffer` presentation
+ for pixel in
+ bytemuck::cast_slice_mut::<u8, u32>(image.data_mut())
+ {
+ *pixel = *pixel & 0xFF00FF00
+ | ((0x000000FF & *pixel) << 16)
+ | ((0x00FF0000 & *pixel) >> 16);
+ }
+ }
+
+ self.rasters.insert(key, image);
+ }
+
+ self.raster_hits.insert(key);
+ self.rasters.get(&key).map(tiny_skia::Pixmap::as_ref)
+ }
+
+ fn trim(&mut self) {
+ self.trees.retain(|key, _| self.tree_hits.contains(key));
+ self.rasters.retain(|key, _| self.raster_hits.contains(key));
+
+ self.tree_hits.clear();
+ self.raster_hits.clear();
+ }
+}
diff --git a/tiny_skia/src/window.rs b/tiny_skia/src/window.rs
new file mode 100644
index 00000000..d8d9378e
--- /dev/null
+++ b/tiny_skia/src/window.rs
@@ -0,0 +1,3 @@
+pub mod compositor;
+
+pub use compositor::{Compositor, Surface};
diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs
new file mode 100644
index 00000000..cea1cabf
--- /dev/null
+++ b/tiny_skia/src/window/compositor.rs
@@ -0,0 +1,130 @@
+use crate::core::Color;
+use crate::graphics::compositor::{self, Information, SurfaceError};
+use crate::graphics::{Error, Primitive, Viewport};
+use crate::{Backend, Renderer, Settings};
+
+use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
+use std::marker::PhantomData;
+
+pub struct Compositor<Theme> {
+ clip_mask: tiny_skia::ClipMask,
+ _theme: PhantomData<Theme>,
+}
+
+pub struct Surface {
+ window: softbuffer::GraphicsContext,
+ buffer: Vec<u32>,
+}
+
+impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
+ type Settings = Settings;
+ type Renderer = Renderer<Theme>;
+ type Surface = Surface;
+
+ fn new<W: HasRawWindowHandle + HasRawDisplayHandle>(
+ settings: Self::Settings,
+ _compatible_window: Option<&W>,
+ ) -> Result<(Self, Self::Renderer), Error> {
+ let (compositor, backend) = new(settings);
+
+ Ok((compositor, Renderer::new(backend)))
+ }
+
+ fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
+ &mut self,
+ window: &W,
+ width: u32,
+ height: u32,
+ ) -> Surface {
+ let window =
+ unsafe { softbuffer::GraphicsContext::new(window, window) }
+ .expect("Create softbuffer for window");
+
+ Surface {
+ window,
+ buffer: vec![0; width as usize * height as usize],
+ }
+ }
+
+ fn configure_surface(
+ &mut self,
+ surface: &mut Surface,
+ width: u32,
+ height: u32,
+ ) {
+ surface.buffer.resize((width * height) as usize, 0);
+ }
+
+ fn fetch_information(&self) -> Information {
+ Information {
+ adapter: String::from("CPU"),
+ backend: String::from("tiny-skia"),
+ }
+ }
+
+ fn present<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ surface: &mut Self::Surface,
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Result<(), SurfaceError> {
+ renderer.with_primitives(|backend, primitives| {
+ present(
+ self,
+ backend,
+ surface,
+ primitives,
+ viewport,
+ background_color,
+ overlay,
+ )
+ })
+ }
+}
+
+pub fn new<Theme>(settings: Settings) -> (Compositor<Theme>, Backend) {
+ // TOD
+ (
+ Compositor {
+ clip_mask: tiny_skia::ClipMask::new(),
+ _theme: PhantomData,
+ },
+ Backend::new(settings),
+ )
+}
+
+pub fn present<Theme, T: AsRef<str>>(
+ compositor: &mut Compositor<Theme>,
+ backend: &mut Backend,
+ surface: &mut Surface,
+ primitives: &[Primitive],
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+) -> Result<(), compositor::SurfaceError> {
+ let physical_size = viewport.physical_size();
+
+ backend.draw(
+ &mut tiny_skia::PixmapMut::from_bytes(
+ bytemuck::cast_slice_mut(&mut surface.buffer),
+ physical_size.width,
+ physical_size.height,
+ )
+ .expect("Create pixel map"),
+ &mut compositor.clip_mask,
+ primitives,
+ viewport,
+ background_color,
+ overlay,
+ );
+
+ surface.window.set_buffer(
+ &surface.buffer,
+ physical_size.width as u16,
+ physical_size.height as u16,
+ );
+
+ Ok(())
+}
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 9e80a76d..6a313d4f 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -8,23 +8,9 @@ license = "MIT AND OFL-1.1"
repository = "https://github.com/iced-rs/iced"
[features]
-svg = ["iced_graphics/svg"]
+geometry = ["iced_graphics/geometry", "lyon"]
image = ["iced_graphics/image"]
-png = ["iced_graphics/png"]
-jpeg = ["iced_graphics/jpeg"]
-jpeg_rayon = ["iced_graphics/jpeg_rayon"]
-gif = ["iced_graphics/gif"]
-webp = ["iced_graphics/webp"]
-pnm = ["iced_graphics/pnm"]
-ico = ["iced_graphics/ico"]
-bmp = ["iced_graphics/bmp"]
-hdr = ["iced_graphics/hdr"]
-dds = ["iced_graphics/dds"]
-farbfeld = ["iced_graphics/farbfeld"]
-canvas = ["iced_graphics/canvas"]
-qr_code = ["iced_graphics/qr_code"]
-spirv = ["wgpu/spirv"]
-webgl = ["wgpu/webgl"]
+svg = ["resvg"]
[dependencies]
wgpu = "0.14"
@@ -37,6 +23,9 @@ once_cell = "1.0"
rustc-hash = "1.1"
ouroboros = "0.15"
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+wgpu = { version = "0.14", features = ["webgl"] }
+
[dependencies.twox-hash]
version = "1.6"
default-features = false
@@ -49,10 +38,6 @@ features = ["std"]
version = "1.9"
features = ["derive"]
-[dependencies.iced_native]
-version = "0.9"
-path = "../native"
-
[dependencies.iced_graphics]
version = "0.7"
path = "../graphics"
@@ -60,11 +45,7 @@ path = "../graphics"
[dependencies.glyphon]
version = "0.2"
git = "https://github.com/hecrj/glyphon.git"
-rev = "810bc979f9005e2bd343b72b980e57e46174283f"
-
-[dependencies.tracing]
-version = "0.1.6"
-optional = true
+rev = "edd23695ad53db5f89d455c3c130172fd107d6a2"
[dependencies.encase]
version = "0.3.0"
@@ -73,6 +54,18 @@ features = ["glam"]
[dependencies.glam]
version = "0.21.3"
+[dependencies.lyon]
+version = "1.0"
+optional = true
+
+[dependencies.resvg]
+version = "0.29"
+optional = true
+
+[dependencies.tracing]
+version = "0.1.6"
+optional = true
+
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
all-features = true
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index e650d9a5..88c58554 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -1,11 +1,11 @@
+use crate::core;
+use crate::core::{Color, Font, Point, Size};
+use crate::graphics::backend;
+use crate::graphics::{Primitive, Transformation, Viewport};
use crate::quad;
use crate::text;
use crate::triangle;
-use crate::{Settings, Transformation};
-
-use iced_graphics::backend;
-use iced_graphics::layer::Layer;
-use iced_graphics::{Color, Font, Primitive, Size, Viewport};
+use crate::{Layer, Settings};
#[cfg(feature = "tracing")]
use tracing::info_span;
@@ -119,7 +119,7 @@ impl Backend {
self.triangle_pipeline.end_frame();
#[cfg(any(feature = "image", feature = "svg"))]
- self.image_pipeline.end_frame(device, queue, encoder);
+ self.image_pipeline.end_frame();
}
fn prepare_text(
@@ -364,9 +364,9 @@ impl backend::Text for Backend {
size: f32,
font: Font,
bounds: Size,
- point: iced_native::Point,
+ point: Point,
nearest_only: bool,
- ) -> Option<text::Hit> {
+ ) -> Option<core::text::Hit> {
self.text_pipeline.hit_test(
contents,
size,
@@ -384,17 +384,14 @@ impl backend::Text for Backend {
#[cfg(feature = "image")]
impl backend::Image for Backend {
- fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> {
+ fn dimensions(&self, handle: &core::image::Handle) -> Size<u32> {
self.image_pipeline.dimensions(handle)
}
}
#[cfg(feature = "svg")]
impl backend::Svg for Backend {
- fn viewport_dimensions(
- &self,
- handle: &iced_native::svg::Handle,
- ) -> Size<u32> {
+ fn viewport_dimensions(&self, handle: &core::svg::Handle) -> Size<u32> {
self.image_pipeline.viewport_dimensions(handle)
}
}
diff --git a/graphics/src/widget/canvas/frame.rs b/wgpu/src/geometry.rs
index d68548ae..59ec31fe 100644
--- a/graphics/src/widget/canvas/frame.rs
+++ b/wgpu/src/geometry.rs
@@ -1,9 +1,9 @@
-use crate::gradient::Gradient;
-use crate::triangle;
-use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text};
-use crate::Primitive;
-
-use iced_native::{Point, Rectangle, Size, Vector};
+use crate::core::{Gradient, Point, Rectangle, Size, Vector};
+use crate::graphics::geometry::fill::{self, Fill};
+use crate::graphics::geometry::{
+ LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
+};
+use crate::graphics::primitive::{self, Primitive};
use lyon::geom::euclid;
use lyon::tessellation;
@@ -23,9 +23,9 @@ pub struct Frame {
}
enum Buffer {
- Solid(tessellation::VertexBuffers<triangle::ColoredVertex2D, u32>),
+ Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),
Gradient(
- tessellation::VertexBuffers<triangle::Vertex2D, u32>,
+ tessellation::VertexBuffers<primitive::Vertex2D, u32>,
Gradient,
),
}
@@ -196,8 +196,8 @@ impl Frame {
.buffers
.get_fill(&self.transforms.current.transform_style(style));
- let options =
- tessellation::FillOptions::default().with_fill_rule(rule.into());
+ let options = tessellation::FillOptions::default()
+ .with_fill_rule(into_fill_rule(rule));
if self.transforms.current.is_identity {
self.fill_tessellator.tessellate_path(
@@ -206,7 +206,7 @@ impl Frame {
buffer.as_mut(),
)
} else {
- let path = path.transformed(&self.transforms.current.raw);
+ let path = path.transform(&self.transforms.current.raw);
self.fill_tessellator.tessellate_path(
path.raw(),
@@ -241,8 +241,8 @@ impl Frame {
lyon::math::Vector::new(size.width, size.height),
);
- let options =
- tessellation::FillOptions::default().with_fill_rule(rule.into());
+ let options = tessellation::FillOptions::default()
+ .with_fill_rule(into_fill_rule(rule));
self.fill_tessellator
.tessellate_rectangle(
@@ -264,14 +264,14 @@ impl Frame {
let mut options = tessellation::StrokeOptions::default();
options.line_width = stroke.width;
- options.start_cap = stroke.line_cap.into();
- options.end_cap = stroke.line_cap.into();
- options.line_join = stroke.line_join.into();
+ options.start_cap = into_line_cap(stroke.line_cap);
+ options.end_cap = into_line_cap(stroke.line_cap);
+ options.line_join = into_line_join(stroke.line_join);
let path = if stroke.line_dash.segments.is_empty() {
Cow::Borrowed(path)
} else {
- Cow::Owned(path::dashed(path, stroke.line_dash))
+ Cow::Owned(dashed(path, stroke.line_dash))
};
if self.transforms.current.is_identity {
@@ -281,7 +281,7 @@ impl Frame {
buffer.as_mut(),
)
} else {
- let path = path.transformed(&self.transforms.current.raw);
+ let path = path.transform(&self.transforms.current.raw);
self.stroke_tessellator.tessellate_path(
path.raw(),
@@ -344,10 +344,18 @@ impl Frame {
/// operations in different coordinate systems.
#[inline]
pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) {
- self.transforms.previous.push(self.transforms.current);
+ self.push_transform();
f(self);
+ self.pop_transform();
+ }
+
+ pub fn push_transform(&mut self) {
+ self.transforms.previous.push(self.transforms.current);
+ }
+
+ pub fn pop_transform(&mut self) {
self.transforms.current = self.transforms.previous.pop().unwrap();
}
@@ -363,14 +371,19 @@ impl Frame {
f(&mut frame);
+ let translation = Vector::new(region.x, region.y);
+
+ self.clip(frame, translation);
+ }
+
+ pub fn clip(&mut self, frame: Frame, translation: Vector) {
+ let size = frame.size();
let primitives = frame.into_primitives();
let (text, meshes) = primitives
.into_iter()
.partition(|primitive| matches!(primitive, Primitive::Text { .. }));
- let translation = Vector::new(region.x, region.y);
-
self.primitives.push(Primitive::Group {
primitives: vec![
Primitive::Translate {
@@ -380,7 +393,7 @@ impl Frame {
Primitive::Translate {
translation,
content: Box::new(Primitive::Clip {
- bounds: Rectangle::with_size(region.size()),
+ bounds: Rectangle::with_size(size),
content: Box::new(Primitive::Group {
primitives: text,
}),
@@ -423,11 +436,11 @@ impl Frame {
self.transforms.current.is_identity = false;
}
- /// Produces the [`Geometry`] representing everything drawn on the [`Frame`].
- pub fn into_geometry(self) -> Geometry {
- Geometry::from_primitive(Primitive::Group {
+ /// Produces the [`Primitive`] representing everything drawn on the [`Frame`].
+ pub fn into_primitive(self) -> Primitive {
+ Primitive::Group {
primitives: self.into_primitives(),
- })
+ }
}
fn into_primitives(mut self) -> Vec<Primitive> {
@@ -436,7 +449,7 @@ impl Frame {
Buffer::Solid(buffer) => {
if !buffer.indices.is_empty() {
self.primitives.push(Primitive::SolidMesh {
- buffers: triangle::Mesh2D {
+ buffers: primitive::Mesh2D {
vertices: buffer.vertices,
indices: buffer.indices,
},
@@ -447,7 +460,7 @@ impl Frame {
Buffer::Gradient(buffer, gradient) => {
if !buffer.indices.is_empty() {
self.primitives.push(Primitive::GradientMesh {
- buffers: triangle::Mesh2D {
+ buffers: primitive::Mesh2D {
vertices: buffer.vertices,
indices: buffer.indices,
},
@@ -465,31 +478,31 @@ impl Frame {
struct Vertex2DBuilder;
-impl tessellation::FillVertexConstructor<triangle::Vertex2D>
+impl tessellation::FillVertexConstructor<primitive::Vertex2D>
for Vertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::FillVertex<'_>,
- ) -> triangle::Vertex2D {
+ ) -> primitive::Vertex2D {
let position = vertex.position();
- triangle::Vertex2D {
+ primitive::Vertex2D {
position: [position.x, position.y],
}
}
}
-impl tessellation::StrokeVertexConstructor<triangle::Vertex2D>
+impl tessellation::StrokeVertexConstructor<primitive::Vertex2D>
for Vertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::StrokeVertex<'_, '_>,
- ) -> triangle::Vertex2D {
+ ) -> primitive::Vertex2D {
let position = vertex.position();
- triangle::Vertex2D {
+ primitive::Vertex2D {
position: [position.x, position.y],
}
}
@@ -497,34 +510,99 @@ impl tessellation::StrokeVertexConstructor<triangle::Vertex2D>
struct TriangleVertex2DBuilder([f32; 4]);
-impl tessellation::FillVertexConstructor<triangle::ColoredVertex2D>
+impl tessellation::FillVertexConstructor<primitive::ColoredVertex2D>
for TriangleVertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::FillVertex<'_>,
- ) -> triangle::ColoredVertex2D {
+ ) -> primitive::ColoredVertex2D {
let position = vertex.position();
- triangle::ColoredVertex2D {
+ primitive::ColoredVertex2D {
position: [position.x, position.y],
color: self.0,
}
}
}
-impl tessellation::StrokeVertexConstructor<triangle::ColoredVertex2D>
+impl tessellation::StrokeVertexConstructor<primitive::ColoredVertex2D>
for TriangleVertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::StrokeVertex<'_, '_>,
- ) -> triangle::ColoredVertex2D {
+ ) -> primitive::ColoredVertex2D {
let position = vertex.position();
- triangle::ColoredVertex2D {
+ primitive::ColoredVertex2D {
position: [position.x, position.y],
color: self.0,
}
}
}
+
+fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin {
+ match line_join {
+ LineJoin::Miter => lyon::tessellation::LineJoin::Miter,
+ LineJoin::Round => lyon::tessellation::LineJoin::Round,
+ LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel,
+ }
+}
+
+fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap {
+ match line_cap {
+ LineCap::Butt => lyon::tessellation::LineCap::Butt,
+ LineCap::Square => lyon::tessellation::LineCap::Square,
+ LineCap::Round => lyon::tessellation::LineCap::Round,
+ }
+}
+
+fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule {
+ match rule {
+ fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero,
+ fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
+ }
+}
+
+pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
+ use lyon::algorithms::walk::{
+ walk_along_path, RepeatedPattern, WalkerEvent,
+ };
+ use lyon::path::iterator::PathIterator;
+
+ Path::new(|builder| {
+ let segments_odd = (line_dash.segments.len() % 2 == 1)
+ .then(|| [line_dash.segments, line_dash.segments].concat());
+
+ let mut draw_line = false;
+
+ walk_along_path(
+ path.raw().iter().flattened(0.01),
+ 0.0,
+ lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
+ &mut RepeatedPattern {
+ callback: |event: WalkerEvent<'_>| {
+ let point = Point {
+ x: event.position.x,
+ y: event.position.y,
+ };
+
+ if draw_line {
+ builder.line_to(point);
+ } else {
+ builder.move_to(point);
+ }
+
+ draw_line = !draw_line;
+
+ true
+ },
+ index: line_dash.offset,
+ intervals: segments_odd
+ .as_deref()
+ .unwrap_or(line_dash.segments),
+ },
+ );
+ })
+}
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index db05d2ff..4163e338 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -1,16 +1,17 @@
mod atlas;
#[cfg(feature = "image")]
-use iced_graphics::image::raster;
+mod raster;
#[cfg(feature = "svg")]
-use iced_graphics::image::vector;
+mod vector;
-use crate::{Buffer, Transformation};
use atlas::Atlas;
-use iced_graphics::layer;
-use iced_native::{Rectangle, Size};
+use crate::core::{Rectangle, Size};
+use crate::graphics::Transformation;
+use crate::layer;
+use crate::Buffer;
use std::cell::RefCell;
use std::mem;
@@ -18,10 +19,10 @@ use std::mem;
use bytemuck::{Pod, Zeroable};
#[cfg(feature = "image")]
-use iced_native::image;
+use crate::core::image;
#[cfg(feature = "svg")]
-use iced_native::svg;
+use crate::core::svg;
#[cfg(feature = "tracing")]
use tracing::info_span;
@@ -29,9 +30,9 @@ use tracing::info_span;
#[derive(Debug)]
pub struct Pipeline {
#[cfg(feature = "image")]
- raster_cache: RefCell<raster::Cache<Atlas>>,
+ raster_cache: RefCell<raster::Cache>,
#[cfg(feature = "svg")]
- vector_cache: RefCell<vector::Cache<Atlas>>,
+ vector_cache: RefCell<vector::Cache>,
pipeline: wgpu::RenderPipeline,
vertices: wgpu::Buffer,
@@ -367,8 +368,10 @@ impl Pipeline {
#[cfg(feature = "image")]
layer::Image::Raster { handle, bounds } => {
if let Some(atlas_entry) = raster_cache.upload(
+ device,
+ queue,
+ encoder,
handle,
- &mut (device, queue, encoder),
&mut self.texture_atlas,
) {
add_instances(
@@ -391,11 +394,13 @@ impl Pipeline {
let size = [bounds.width, bounds.height];
if let Some(atlas_entry) = vector_cache.upload(
+ device,
+ queue,
+ encoder,
handle,
*color,
size,
_scale,
- &mut (device, queue, encoder),
&mut self.texture_atlas,
) {
add_instances(
@@ -476,21 +481,12 @@ impl Pipeline {
}
}
- pub fn end_frame(
- &mut self,
- device: &wgpu::Device,
- queue: &wgpu::Queue,
- encoder: &mut wgpu::CommandEncoder,
- ) {
+ pub fn end_frame(&mut self) {
#[cfg(feature = "image")]
- self.raster_cache
- .borrow_mut()
- .trim(&mut self.texture_atlas, &mut (device, queue, encoder));
+ self.raster_cache.borrow_mut().trim(&mut self.texture_atlas);
#[cfg(feature = "svg")]
- self.vector_cache
- .borrow_mut()
- .trim(&mut self.texture_atlas, &mut (device, queue, encoder));
+ self.vector_cache.borrow_mut().trim(&mut self.texture_atlas);
self.prepare_layer = 0;
}
diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index 7df67abd..c00b8cef 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -12,8 +12,7 @@ use allocator::Allocator;
pub const SIZE: u32 = 2048;
-use iced_graphics::image;
-use iced_graphics::Size;
+use crate::core::Size;
use std::num::NonZeroU32;
@@ -64,6 +63,97 @@ impl Atlas {
self.layers.len()
}
+ pub fn upload(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ encoder: &mut wgpu::CommandEncoder,
+ width: u32,
+ height: u32,
+ data: &[u8],
+ ) -> Option<Entry> {
+ let entry = {
+ let current_size = self.layers.len();
+ let entry = self.allocate(width, height)?;
+
+ // We grow the internal texture after allocating if necessary
+ let new_layers = self.layers.len() - current_size;
+ self.grow(new_layers, device, encoder);
+
+ entry
+ };
+
+ log::info!("Allocated atlas entry: {:?}", entry);
+
+ // It is a webgpu requirement that:
+ // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0
+ // So we calculate padded_width by rounding width up to the next
+ // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT.
+ let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
+ let padding = (align - (4 * width) % align) % align;
+ let padded_width = (4 * width + padding) as usize;
+ let padded_data_size = padded_width * height as usize;
+
+ let mut padded_data = vec![0; padded_data_size];
+
+ for row in 0..height as usize {
+ let offset = row * padded_width;
+
+ padded_data[offset..offset + 4 * width as usize].copy_from_slice(
+ &data[row * 4 * width as usize..(row + 1) * 4 * width as usize],
+ )
+ }
+
+ match &entry {
+ Entry::Contiguous(allocation) => {
+ self.upload_allocation(
+ &padded_data,
+ width,
+ height,
+ padding,
+ 0,
+ allocation,
+ queue,
+ );
+ }
+ Entry::Fragmented { fragments, .. } => {
+ for fragment in fragments {
+ let (x, y) = fragment.position;
+ let offset = (y * padded_width as u32 + 4 * x) as usize;
+
+ self.upload_allocation(
+ &padded_data,
+ width,
+ height,
+ padding,
+ offset,
+ &fragment.allocation,
+ queue,
+ );
+ }
+ }
+ }
+
+ log::info!("Current atlas: {:?}", self);
+
+ Some(entry)
+ }
+
+ pub fn remove(&mut self, entry: &Entry) {
+ log::info!("Removing atlas entry: {:?}", entry);
+
+ match entry {
+ Entry::Contiguous(allocation) => {
+ self.deallocate(allocation);
+ }
+ Entry::Fragmented { fragments, .. } => {
+ for fragment in fragments {
+ self.deallocate(&fragment.allocation);
+ }
+ }
+ }
+ }
+
fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
// Allocate one layer if texture fits perfectly
if width == SIZE && height == SIZE {
@@ -296,101 +386,3 @@ impl Atlas {
});
}
}
-
-impl image::Storage for Atlas {
- type Entry = Entry;
- type State<'a> = (
- &'a wgpu::Device,
- &'a wgpu::Queue,
- &'a mut wgpu::CommandEncoder,
- );
-
- fn upload(
- &mut self,
- width: u32,
- height: u32,
- data: &[u8],
- (device, queue, encoder): &mut Self::State<'_>,
- ) -> Option<Self::Entry> {
- let entry = {
- let current_size = self.layers.len();
- let entry = self.allocate(width, height)?;
-
- // We grow the internal texture after allocating if necessary
- let new_layers = self.layers.len() - current_size;
- self.grow(new_layers, device, encoder);
-
- entry
- };
-
- log::info!("Allocated atlas entry: {:?}", entry);
-
- // It is a webgpu requirement that:
- // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0
- // So we calculate padded_width by rounding width up to the next
- // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT.
- let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
- let padding = (align - (4 * width) % align) % align;
- let padded_width = (4 * width + padding) as usize;
- let padded_data_size = padded_width * height as usize;
-
- let mut padded_data = vec![0; padded_data_size];
-
- for row in 0..height as usize {
- let offset = row * padded_width;
-
- padded_data[offset..offset + 4 * width as usize].copy_from_slice(
- &data[row * 4 * width as usize..(row + 1) * 4 * width as usize],
- )
- }
-
- match &entry {
- Entry::Contiguous(allocation) => {
- self.upload_allocation(
- &padded_data,
- width,
- height,
- padding,
- 0,
- allocation,
- queue,
- );
- }
- Entry::Fragmented { fragments, .. } => {
- for fragment in fragments {
- let (x, y) = fragment.position;
- let offset = (y * padded_width as u32 + 4 * x) as usize;
-
- self.upload_allocation(
- &padded_data,
- width,
- height,
- padding,
- offset,
- &fragment.allocation,
- queue,
- );
- }
- }
- }
-
- log::info!("Current atlas: {:?}", self);
-
- Some(entry)
- }
-
- fn remove(&mut self, entry: &Entry, _: &mut Self::State<'_>) {
- log::info!("Removing atlas entry: {:?}", entry);
-
- match entry {
- Entry::Contiguous(allocation) => {
- self.deallocate(allocation);
- }
- Entry::Fragmented { fragments, .. } => {
- for fragment in fragments {
- self.deallocate(&fragment.allocation);
- }
- }
- }
- }
-}
diff --git a/wgpu/src/image/atlas/allocation.rs b/wgpu/src/image/atlas/allocation.rs
index 43aba875..11289771 100644
--- a/wgpu/src/image/atlas/allocation.rs
+++ b/wgpu/src/image/atlas/allocation.rs
@@ -1,7 +1,6 @@
+use crate::core::Size;
use crate::image::atlas::{self, allocator};
-use iced_graphics::Size;
-
#[derive(Debug)]
pub enum Allocation {
Partial {
diff --git a/wgpu/src/image/atlas/allocator.rs b/wgpu/src/image/atlas/allocator.rs
index 03effdcb..204a5c26 100644
--- a/wgpu/src/image/atlas/allocator.rs
+++ b/wgpu/src/image/atlas/allocator.rs
@@ -46,10 +46,10 @@ impl Region {
(rectangle.min.x as u32, rectangle.min.y as u32)
}
- pub fn size(&self) -> iced_graphics::Size<u32> {
+ pub fn size(&self) -> crate::core::Size<u32> {
let size = self.allocation.rectangle.size();
- iced_graphics::Size::new(size.width as u32, size.height as u32)
+ crate::core::Size::new(size.width as u32, size.height as u32)
}
}
diff --git a/wgpu/src/image/atlas/entry.rs b/wgpu/src/image/atlas/entry.rs
index 69c05a50..7e4c92a2 100644
--- a/wgpu/src/image/atlas/entry.rs
+++ b/wgpu/src/image/atlas/entry.rs
@@ -1,8 +1,6 @@
+use crate::core::Size;
use crate::image::atlas;
-use iced_graphics::image;
-use iced_graphics::Size;
-
#[derive(Debug)]
pub enum Entry {
Contiguous(atlas::Allocation),
@@ -12,8 +10,9 @@ pub enum Entry {
},
}
-impl image::storage::Entry for Entry {
- fn size(&self) -> Size<u32> {
+impl Entry {
+ #[cfg(feature = "image")]
+ pub fn size(&self) -> Size<u32> {
match self {
Entry::Contiguous(allocation) => allocation.size(),
Entry::Fragmented { size, .. } => *size,
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
new file mode 100644
index 00000000..9b38dce4
--- /dev/null
+++ b/wgpu/src/image/raster.rs
@@ -0,0 +1,121 @@
+use crate::core::image;
+use crate::core::Size;
+use crate::graphics;
+use crate::graphics::image::image_rs;
+use crate::image::atlas::{self, Atlas};
+
+use std::collections::{HashMap, HashSet};
+
+/// Entry in cache corresponding to an image handle
+#[derive(Debug)]
+pub enum Memory {
+ /// Image data on host
+ Host(image_rs::ImageBuffer<image_rs::Rgba<u8>, Vec<u8>>),
+ /// Storage entry
+ Device(atlas::Entry),
+ /// Image not found
+ NotFound,
+ /// Invalid image data
+ Invalid,
+}
+
+impl Memory {
+ /// Width and height of image
+ pub fn dimensions(&self) -> Size<u32> {
+ match self {
+ Memory::Host(image) => {
+ let (width, height) = image.dimensions();
+
+ Size::new(width, height)
+ }
+ Memory::Device(entry) => entry.size(),
+ Memory::NotFound => Size::new(1, 1),
+ Memory::Invalid => Size::new(1, 1),
+ }
+ }
+}
+
+/// Caches image raster data
+#[derive(Debug, Default)]
+pub struct Cache {
+ map: HashMap<u64, Memory>,
+ hits: HashSet<u64>,
+}
+
+impl Cache {
+ /// Load image
+ pub fn load(&mut self, handle: &image::Handle) -> &mut Memory {
+ if self.contains(handle) {
+ return self.get(handle).unwrap();
+ }
+
+ let memory = match graphics::image::load(handle) {
+ Ok(image) => Memory::Host(image.to_rgba8()),
+ Err(image_rs::error::ImageError::IoError(_)) => Memory::NotFound,
+ Err(_) => Memory::Invalid,
+ };
+
+ self.insert(handle, memory);
+ self.get(handle).unwrap()
+ }
+
+ /// Load image and upload raster data
+ pub fn upload(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ encoder: &mut wgpu::CommandEncoder,
+ handle: &image::Handle,
+ atlas: &mut Atlas,
+ ) -> Option<&atlas::Entry> {
+ let memory = self.load(handle);
+
+ if let Memory::Host(image) = memory {
+ let (width, height) = image.dimensions();
+
+ let entry =
+ atlas.upload(device, queue, encoder, width, height, image)?;
+
+ *memory = Memory::Device(entry);
+ }
+
+ if let Memory::Device(allocation) = memory {
+ Some(allocation)
+ } else {
+ None
+ }
+ }
+
+ /// Trim cache misses from cache
+ pub fn trim(&mut self, atlas: &mut Atlas) {
+ let hits = &self.hits;
+
+ self.map.retain(|k, memory| {
+ let retain = hits.contains(k);
+
+ if !retain {
+ if let Memory::Device(entry) = memory {
+ atlas.remove(entry);
+ }
+ }
+
+ retain
+ });
+
+ self.hits.clear();
+ }
+
+ fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory> {
+ let _ = self.hits.insert(handle.id());
+
+ self.map.get_mut(&handle.id())
+ }
+
+ fn insert(&mut self, handle: &image::Handle, memory: Memory) {
+ let _ = self.map.insert(handle.id(), memory);
+ }
+
+ fn contains(&self, handle: &image::Handle) -> bool {
+ self.map.contains_key(&handle.id())
+ }
+}
diff --git a/graphics/src/image/vector.rs b/wgpu/src/image/vector.rs
index 82d77aff..3624e46b 100644
--- a/graphics/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,10 +1,9 @@
-//! Vector image loading and caching
-use crate::image::Storage;
-use crate::Color;
-
-use iced_native::svg;
-use iced_native::Size;
+use crate::core::svg;
+use crate::core::{Color, Size};
+use crate::image::atlas::{self, Atlas};
+use resvg::tiny_skia;
+use resvg::usvg;
use std::collections::{HashMap, HashSet};
use std::fs;
@@ -21,7 +20,7 @@ impl Svg {
pub fn viewport_dimensions(&self) -> Size<u32> {
match self {
Svg::Loaded(tree) => {
- let size = tree.svg_node().size;
+ let size = tree.size;
Size::new(size.width() as u32, size.height() as u32)
}
@@ -31,17 +30,17 @@ impl Svg {
}
/// Caches svg vector and raster data
-#[derive(Debug)]
-pub struct Cache<T: Storage> {
+#[derive(Debug, Default)]
+pub struct Cache {
svgs: HashMap<u64, Svg>,
- rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>,
+ rasterized: HashMap<(u64, u32, u32, ColorFilter), atlas::Entry>,
svg_hits: HashSet<u64>,
rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>,
}
type ColorFilter = Option<[u8; 4]>;
-impl<T: Storage> Cache<T> {
+impl Cache {
/// Load svg
pub fn load(&mut self, handle: &svg::Handle) -> &Svg {
if self.svgs.contains_key(&handle.id()) {
@@ -51,20 +50,14 @@ impl<T: Storage> Cache<T> {
let svg = match handle.data() {
svg::Data::Path(path) => {
let tree = fs::read_to_string(path).ok().and_then(|contents| {
- usvg::Tree::from_str(
- &contents,
- &usvg::Options::default().to_ref(),
- )
- .ok()
+ usvg::Tree::from_str(&contents, &usvg::Options::default())
+ .ok()
});
tree.map(Svg::Loaded).unwrap_or(Svg::NotFound)
}
svg::Data::Bytes(bytes) => {
- match usvg::Tree::from_data(
- bytes,
- &usvg::Options::default().to_ref(),
- ) {
+ match usvg::Tree::from_data(bytes, &usvg::Options::default()) {
Ok(tree) => Svg::Loaded(tree),
Err(_) => Svg::NotFound,
}
@@ -78,13 +71,15 @@ impl<T: Storage> Cache<T> {
/// Load svg and upload raster data
pub fn upload(
&mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ encoder: &mut wgpu::CommandEncoder,
handle: &svg::Handle,
color: Option<Color>,
[width, height]: [f32; 2],
scale: f32,
- state: &mut T::State<'_>,
- storage: &mut T,
- ) -> Option<&T::Entry> {
+ atlas: &mut Atlas,
+ ) -> Option<&atlas::Entry> {
let id = handle.id();
let (width, height) = (
@@ -125,6 +120,7 @@ impl<T: Storage> Cache<T> {
} else {
usvg::FitTo::Height(height)
},
+ tiny_skia::Transform::default(),
img.as_mut(),
)?;
@@ -140,7 +136,9 @@ impl<T: Storage> Cache<T> {
});
}
- let allocation = storage.upload(width, height, &rgba, state)?;
+ let allocation = atlas
+ .upload(device, queue, encoder, width, height, &rgba)?;
+
log::debug!("allocating {} {}x{}", id, width, height);
let _ = self.svg_hits.insert(id);
@@ -154,7 +152,7 @@ impl<T: Storage> Cache<T> {
}
/// Load svg and upload raster data
- pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) {
+ pub fn trim(&mut self, atlas: &mut Atlas) {
let svg_hits = &self.svg_hits;
let rasterized_hits = &self.rasterized_hits;
@@ -163,7 +161,7 @@ impl<T: Storage> Cache<T> {
let retain = rasterized_hits.contains(k);
if !retain {
- storage.remove(entry, state);
+ atlas.remove(entry);
}
retain
@@ -173,17 +171,6 @@ impl<T: Storage> Cache<T> {
}
}
-impl<T: Storage> Default for Cache<T> {
- fn default() -> Self {
- Self {
- svgs: HashMap::new(),
- rasterized: HashMap::new(),
- svg_hits: HashSet::new(),
- rasterized_hits: HashSet::new(),
- }
- }
-}
-
impl std::fmt::Debug for Svg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
diff --git a/graphics/src/layer.rs b/wgpu/src/layer.rs
index f6eb2fdd..cb9d5e2f 100644
--- a/graphics/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -10,11 +10,9 @@ pub use mesh::Mesh;
pub use quad::Quad;
pub use text::Text;
-use crate::alignment;
-use crate::{
- Background, Color, Font, Point, Primitive, Rectangle, Size, Vector,
- Viewport,
-};
+use crate::core::alignment;
+use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector};
+use crate::graphics::{Primitive, Viewport};
/// A group of primitives that should be clipped together.
#[derive(Debug)]
@@ -110,18 +108,6 @@ impl<'a> Layer<'a> {
current_layer: usize,
) {
match primitive {
- Primitive::None => {}
- Primitive::Group { primitives } => {
- // TODO: Inspect a bit and regroup (?)
- for primitive in primitives {
- Self::process_primitive(
- layers,
- translation,
- primitive,
- current_layer,
- )
- }
- }
Primitive::Text {
content,
bounds,
@@ -167,6 +153,27 @@ impl<'a> Layer<'a> {
border_color: border_color.into_linear(),
});
}
+ Primitive::Image { handle, bounds } => {
+ let layer = &mut layers[current_layer];
+
+ layer.images.push(Image::Raster {
+ handle: handle.clone(),
+ bounds: *bounds + translation,
+ });
+ }
+ Primitive::Svg {
+ handle,
+ color,
+ bounds,
+ } => {
+ let layer = &mut layers[current_layer];
+
+ layer.images.push(Image::Vector {
+ handle: handle.clone(),
+ color: *color,
+ bounds: *bounds + translation,
+ });
+ }
Primitive::SolidMesh { buffers, size } => {
let layer = &mut layers[current_layer];
@@ -206,6 +213,17 @@ impl<'a> Layer<'a> {
});
}
}
+ Primitive::Group { primitives } => {
+ // TODO: Inspect a bit and regroup (?)
+ for primitive in primitives {
+ Self::process_primitive(
+ layers,
+ translation,
+ primitive,
+ current_layer,
+ )
+ }
+ }
Primitive::Clip { bounds, content } => {
let layer = &mut layers[current_layer];
let translated_bounds = *bounds + translation;
@@ -236,34 +254,16 @@ impl<'a> Layer<'a> {
current_layer,
);
}
- Primitive::Cached { cache } => {
+ Primitive::Cache { content } => {
Self::process_primitive(
layers,
translation,
- cache,
+ content,
current_layer,
);
}
- Primitive::Image { handle, bounds } => {
- let layer = &mut layers[current_layer];
-
- layer.images.push(Image::Raster {
- handle: handle.clone(),
- bounds: *bounds + translation,
- });
- }
- Primitive::Svg {
- handle,
- color,
- bounds,
- } => {
- let layer = &mut layers[current_layer];
-
- layer.images.push(Image::Vector {
- handle: handle.clone(),
- color: *color,
- bounds: *bounds + translation,
- });
+ _ => {
+ // Unsupported!
}
}
}
diff --git a/graphics/src/layer/image.rs b/wgpu/src/layer/image.rs
index 3eff2397..0de589f8 100644
--- a/graphics/src/layer/image.rs
+++ b/wgpu/src/layer/image.rs
@@ -1,6 +1,6 @@
-use crate::{Color, Rectangle};
-
-use iced_native::{image, svg};
+use crate::core::image;
+use crate::core::svg;
+use crate::core::{Color, Rectangle};
/// A raster or vector image.
#[derive(Debug, Clone)]
diff --git a/graphics/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs
index 7661c5c9..9dd14391 100644
--- a/graphics/src/layer/mesh.rs
+++ b/wgpu/src/layer/mesh.rs
@@ -1,6 +1,6 @@
//! A collection of triangle primitives.
-use crate::triangle;
-use crate::{Gradient, Point, Rectangle};
+use crate::core::{Gradient, Point, Rectangle};
+use crate::graphics::primitive;
/// A mesh of triangles.
#[derive(Debug, Clone, Copy)]
@@ -11,7 +11,7 @@ pub enum Mesh<'a> {
origin: Point,
/// The vertex and index buffers of the [`Mesh`].
- buffers: &'a triangle::Mesh2D<triangle::ColoredVertex2D>,
+ buffers: &'a primitive::Mesh2D<primitive::ColoredVertex2D>,
/// The clipping bounds of the [`Mesh`].
clip_bounds: Rectangle<f32>,
@@ -22,7 +22,7 @@ pub enum Mesh<'a> {
origin: Point,
/// The vertex and index buffers of the [`Mesh`].
- buffers: &'a triangle::Mesh2D<triangle::Vertex2D>,
+ buffers: &'a primitive::Mesh2D<primitive::Vertex2D>,
/// The clipping bounds of the [`Mesh`].
clip_bounds: Rectangle<f32>,
diff --git a/graphics/src/layer/quad.rs b/wgpu/src/layer/quad.rs
index 0d8bde9d..0d8bde9d 100644
--- a/graphics/src/layer/quad.rs
+++ b/wgpu/src/layer/quad.rs
diff --git a/graphics/src/layer/text.rs b/wgpu/src/layer/text.rs
index 38d62616..fdbdaafb 100644
--- a/graphics/src/layer/text.rs
+++ b/wgpu/src/layer/text.rs
@@ -1,4 +1,5 @@
-use crate::{alignment, Color, Font, Rectangle};
+use crate::core::alignment;
+use crate::core::{Color, Font, Rectangle};
/// A paragraph of text.
#[derive(Debug, Clone, Copy)]
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 9da40572..473f3621 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -25,7 +25,7 @@
)]
#![deny(
missing_debug_implementations,
- missing_docs,
+ //missing_docs,
unsafe_code,
unused_results,
clippy::extra_unused_lifetimes,
@@ -37,27 +37,29 @@
#![forbid(rust_2018_idioms)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_cfg))]
-
+pub mod layer;
pub mod settings;
pub mod window;
+#[cfg(feature = "geometry")]
+pub mod geometry;
+
mod backend;
mod buffer;
mod quad;
mod text;
mod triangle;
-pub use iced_graphics::{
- Antialiasing, Color, Error, Font, Primitive, Viewport,
-};
-pub use iced_native::Theme;
+pub use iced_graphics as graphics;
+pub use iced_graphics::core;
+
pub use wgpu;
pub use backend::Backend;
+pub use layer::Layer;
pub use settings::Settings;
-use crate::buffer::Buffer;
-use iced_graphics::Transformation;
+use buffer::Buffer;
#[cfg(any(feature = "image", feature = "svg"))]
mod image;
@@ -66,5 +68,4 @@ mod image;
///
/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
/// [`iced`]: https://github.com/iced-rs/iced
-pub type Renderer<Theme = iced_native::Theme> =
- iced_graphics::Renderer<Backend, Theme>;
+pub type Renderer<Theme> = iced_graphics::Renderer<Backend, Theme>;
diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs
index 246cc5e1..b55216d7 100644
--- a/wgpu/src/quad.rs
+++ b/wgpu/src/quad.rs
@@ -1,6 +1,7 @@
-use crate::{Buffer, Transformation};
-use iced_graphics::layer;
-use iced_native::Rectangle;
+use crate::core::Rectangle;
+use crate::graphics::Transformation;
+use crate::layer;
+use crate::Buffer;
use bytemuck::{Pod, Zeroable};
use std::mem;
diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs
index bd9cf473..7c0750ef 100644
--- a/wgpu/src/settings.rs
+++ b/wgpu/src/settings.rs
@@ -1,7 +1,6 @@
//! Configure a renderer.
-pub use crate::Antialiasing;
-
-use crate::Font;
+use crate::core::Font;
+use crate::graphics::Antialiasing;
/// The settings of a [`Backend`].
///
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 3839b31f..e99844e6 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -1,8 +1,7 @@
-pub use iced_native::text::Hit;
-
-use iced_graphics::layer::Text;
-use iced_native::alignment;
-use iced_native::{Font, Rectangle, Size};
+use crate::core::alignment;
+use crate::core::text::Hit;
+use crate::core::{Font, Point, Rectangle, Size};
+use crate::layer::Text;
use rustc_hash::{FxHashMap, FxHashSet};
use std::borrow::Cow;
@@ -275,9 +274,9 @@ impl Pipeline {
&self,
content: &str,
size: f32,
- font: iced_native::Font,
- bounds: iced_native::Size,
- point: iced_native::Point,
+ font: Font,
+ bounds: Size,
+ point: Point,
_nearest_only: bool,
) -> Option<Hit> {
self.system.as_ref().unwrap().with(|fields| {
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index 4b4fa16d..9fa521d7 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -2,12 +2,10 @@
mod msaa;
use crate::buffer::r#static::Buffer;
-use crate::settings;
-use crate::Transformation;
+use crate::core::{Gradient, Size};
+use crate::graphics::{Antialiasing, Transformation};
+use crate::layer::mesh::{self, Mesh};
-use iced_graphics::layer::mesh::{self, Mesh};
-use iced_graphics::triangle::ColoredVertex2D;
-use iced_graphics::Size;
#[cfg(feature = "tracing")]
use tracing::info_span;
@@ -137,7 +135,7 @@ impl Layer {
gradient_vertex_offset += written_bytes;
match gradient {
- iced_graphics::Gradient::Linear(linear) => {
+ Gradient::Linear(linear) => {
use glam::{IVec4, Vec4};
let start_offset = self.gradient.color_stop_offset;
@@ -319,7 +317,7 @@ impl Pipeline {
pub fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
- antialiasing: Option<settings::Antialiasing>,
+ antialiasing: Option<Antialiasing>,
) -> Pipeline {
Pipeline {
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
@@ -453,7 +451,7 @@ fn primitive_state() -> wgpu::PrimitiveState {
}
fn multisample_state(
- antialiasing: Option<settings::Antialiasing>,
+ antialiasing: Option<Antialiasing>,
) -> wgpu::MultisampleState {
wgpu::MultisampleState {
count: antialiasing.map(|a| a.sample_count()).unwrap_or(1),
@@ -465,10 +463,11 @@ fn multisample_state(
mod solid {
use crate::buffer::dynamic;
use crate::buffer::r#static::Buffer;
- use crate::settings;
+ use crate::graphics::primitive;
+ use crate::graphics::{Antialiasing, Transformation};
use crate::triangle;
+
use encase::ShaderType;
- use iced_graphics::Transformation;
#[derive(Debug)]
pub struct Pipeline {
@@ -478,7 +477,7 @@ mod solid {
#[derive(Debug)]
pub struct Layer {
- pub vertices: Buffer<triangle::ColoredVertex2D>,
+ pub vertices: Buffer<primitive::ColoredVertex2D>,
pub uniforms: dynamic::Buffer<Uniforms>,
pub constants: wgpu::BindGroup,
}
@@ -549,7 +548,7 @@ mod solid {
pub fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
- antialiasing: Option<settings::Antialiasing>,
+ antialiasing: Option<Antialiasing>,
) -> Self {
let constants_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
@@ -596,7 +595,7 @@ mod solid {
entry_point: "vs_main",
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<
- triangle::ColoredVertex2D,
+ primitive::ColoredVertex2D,
>()
as u64,
step_mode: wgpu::VertexStepMode::Vertex,
@@ -632,12 +631,12 @@ mod solid {
mod gradient {
use crate::buffer::dynamic;
use crate::buffer::r#static::Buffer;
- use crate::settings;
+ use crate::graphics::Antialiasing;
use crate::triangle;
use encase::ShaderType;
use glam::{IVec4, Vec4};
- use iced_graphics::triangle::Vertex2D;
+ use iced_graphics::primitive;
#[derive(Debug)]
pub struct Pipeline {
@@ -647,7 +646,7 @@ mod gradient {
#[derive(Debug)]
pub struct Layer {
- pub vertices: Buffer<Vertex2D>,
+ pub vertices: Buffer<primitive::Vertex2D>,
pub uniforms: dynamic::Buffer<Uniforms>,
pub storage: dynamic::Buffer<Storage>,
pub constants: wgpu::BindGroup,
@@ -754,7 +753,7 @@ mod gradient {
pub(super) fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
- antialiasing: Option<settings::Antialiasing>,
+ antialiasing: Option<Antialiasing>,
) -> Self {
let constants_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
@@ -810,34 +809,38 @@ mod gradient {
),
});
- let pipeline = device.create_render_pipeline(
- &wgpu::RenderPipelineDescriptor {
- label: Some("iced_wgpu::triangle::gradient pipeline"),
- layout: Some(&layout),
- vertex: wgpu::VertexState {
- module: &shader,
- entry_point: "vs_main",
- buffers: &[wgpu::VertexBufferLayout {
- array_stride: std::mem::size_of::<Vertex2D>()
- as u64,
- step_mode: wgpu::VertexStepMode::Vertex,
- attributes: &wgpu::vertex_attr_array!(
- // Position
- 0 => Float32x2,
- ),
- }],
+ let pipeline =
+ device.create_render_pipeline(
+ &wgpu::RenderPipelineDescriptor {
+ label: Some("iced_wgpu::triangle::gradient pipeline"),
+ layout: Some(&layout),
+ vertex: wgpu::VertexState {
+ module: &shader,
+ entry_point: "vs_main",
+ buffers: &[wgpu::VertexBufferLayout {
+ array_stride: std::mem::size_of::<
+ primitive::Vertex2D,
+ >(
+ )
+ as u64,
+ step_mode: wgpu::VertexStepMode::Vertex,
+ attributes: &wgpu::vertex_attr_array!(
+ // Position
+ 0 => Float32x2,
+ ),
+ }],
+ },
+ fragment: Some(wgpu::FragmentState {
+ module: &shader,
+ entry_point: "fs_main",
+ targets: &[triangle::fragment_target(format)],
+ }),
+ primitive: triangle::primitive_state(),
+ depth_stencil: None,
+ multisample: triangle::multisample_state(antialiasing),
+ multiview: None,
},
- fragment: Some(wgpu::FragmentState {
- module: &shader,
- entry_point: "fs_main",
- targets: &[triangle::fragment_target(format)],
- }),
- primitive: triangle::primitive_state(),
- depth_stencil: None,
- multisample: triangle::multisample_state(antialiasing),
- multiview: None,
- },
- );
+ );
Self {
pipeline,
diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs
index a3016ff8..7144125c 100644
--- a/wgpu/src/triangle/msaa.rs
+++ b/wgpu/src/triangle/msaa.rs
@@ -1,4 +1,4 @@
-use crate::settings;
+use crate::graphics;
#[derive(Debug)]
pub struct Blit {
@@ -14,7 +14,7 @@ impl Blit {
pub fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
- antialiasing: settings::Antialiasing,
+ antialiasing: graphics::Antialiasing,
) -> Blit {
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
diff --git a/wgpu/src/window.rs b/wgpu/src/window.rs
index aac5fb9e..9545a14e 100644
--- a/wgpu/src/window.rs
+++ b/wgpu/src/window.rs
@@ -1,4 +1,5 @@
//! Display rendering results on windows.
-mod compositor;
+pub mod compositor;
pub use compositor::Compositor;
+pub use wgpu::Surface;
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 365cb603..a67ac3c0 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -1,9 +1,12 @@
-use crate::{Backend, Color, Error, Renderer, Settings, Viewport};
+//! Connect a window with a renderer.
+use crate::core::Color;
+use crate::graphics;
+use crate::graphics::compositor;
+use crate::graphics::{Error, Primitive, Viewport};
+use crate::{Backend, Renderer, Settings};
use futures::stream::{self, StreamExt};
-use iced_graphics::compositor;
-use iced_native::futures;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use std::marker::PhantomData;
@@ -112,7 +115,78 @@ impl<Theme> Compositor<Theme> {
}
}
-impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
+/// Creates a [`Compositor`] and its [`Backend`] for the given [`Settings`] and
+/// window.
+pub fn new<Theme, W: HasRawWindowHandle + HasRawDisplayHandle>(
+ settings: Settings,
+ compatible_window: Option<&W>,
+) -> Result<(Compositor<Theme>, Backend), Error> {
+ let compositor = futures::executor::block_on(Compositor::request(
+ settings,
+ compatible_window,
+ ))
+ .ok_or(Error::GraphicsAdapterNotFound)?;
+
+ let backend = compositor.create_backend();
+
+ Ok((compositor, backend))
+}
+
+/// Presents the given primitives with the given [`Compositor`] and [`Backend`].
+pub fn present<Theme, T: AsRef<str>>(
+ compositor: &mut Compositor<Theme>,
+ backend: &mut Backend,
+ surface: &mut wgpu::Surface,
+ primitives: &[Primitive],
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+) -> Result<(), compositor::SurfaceError> {
+ match surface.get_current_texture() {
+ Ok(frame) => {
+ let mut encoder = compositor.device.create_command_encoder(
+ &wgpu::CommandEncoderDescriptor {
+ label: Some("iced_wgpu encoder"),
+ },
+ );
+
+ let view = &frame
+ .texture
+ .create_view(&wgpu::TextureViewDescriptor::default());
+
+ backend.present(
+ &compositor.device,
+ &compositor.queue,
+ &mut encoder,
+ Some(background_color),
+ view,
+ primitives,
+ viewport,
+ overlay,
+ );
+
+ // Submit work
+ let _submission = compositor.queue.submit(Some(encoder.finish()));
+ frame.present();
+
+ Ok(())
+ }
+ Err(error) => match error {
+ wgpu::SurfaceError::Timeout => {
+ Err(compositor::SurfaceError::Timeout)
+ }
+ wgpu::SurfaceError::Outdated => {
+ Err(compositor::SurfaceError::Outdated)
+ }
+ wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
+ wgpu::SurfaceError::OutOfMemory => {
+ Err(compositor::SurfaceError::OutOfMemory)
+ }
+ },
+ }
+}
+
+impl<Theme> graphics::Compositor for Compositor<Theme> {
type Settings = Settings;
type Renderer = Renderer<Theme>;
type Surface = wgpu::Surface;
@@ -121,13 +195,7 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
settings: Self::Settings,
compatible_window: Option<&W>,
) -> Result<(Self, Self::Renderer), Error> {
- let compositor = futures::executor::block_on(Self::request(
- settings,
- compatible_window,
- ))
- .ok_or(Error::GraphicsAdapterNotFound)?;
-
- let backend = compositor.create_backend();
+ let (compositor, backend) = new(settings, compatible_window)?;
Ok((compositor, Renderer::new(backend)))
}
@@ -135,11 +203,15 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
&mut self,
window: &W,
+ width: u32,
+ height: u32,
) -> wgpu::Surface {
#[allow(unsafe_code)]
- unsafe {
- self.instance.create_surface(window)
- }
+ let mut surface = unsafe { self.instance.create_surface(window) };
+
+ self.configure_surface(&mut surface, width, height);
+
+ surface
}
fn configure_surface(
@@ -178,49 +250,16 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
background_color: Color,
overlay: &[T],
) -> Result<(), compositor::SurfaceError> {
- match surface.get_current_texture() {
- Ok(frame) => {
- let mut encoder = self.device.create_command_encoder(
- &wgpu::CommandEncoderDescriptor {
- label: Some("iced_wgpu encoder"),
- },
- );
-
- let view = &frame
- .texture
- .create_view(&wgpu::TextureViewDescriptor::default());
-
- renderer.with_primitives(|backend, primitives| {
- backend.present(
- &self.device,
- &self.queue,
- &mut encoder,
- Some(background_color),
- view,
- primitives,
- viewport,
- overlay,
- );
- });
-
- // Submit work
- let _submission = self.queue.submit(Some(encoder.finish()));
- frame.present();
-
- Ok(())
- }
- Err(error) => match error {
- wgpu::SurfaceError::Timeout => {
- Err(compositor::SurfaceError::Timeout)
- }
- wgpu::SurfaceError::Outdated => {
- Err(compositor::SurfaceError::Outdated)
- }
- wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
- wgpu::SurfaceError::OutOfMemory => {
- Err(compositor::SurfaceError::OutOfMemory)
- }
- },
- }
+ renderer.with_primitives(|backend, primitives| {
+ present(
+ self,
+ backend,
+ surface,
+ primitives,
+ viewport,
+ background_color,
+ overlay,
+ )
+ })
}
}
diff --git a/widget/Cargo.toml b/widget/Cargo.toml
new file mode 100644
index 00000000..4c23f3e8
--- /dev/null
+++ b/widget/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "iced_widget"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+lazy = ["ouroboros"]
+image = ["iced_renderer/image"]
+svg = ["iced_renderer/svg"]
+canvas = ["iced_renderer/geometry"]
+qr_code = ["canvas", "qrcode"]
+
+[dependencies]
+unicode-segmentation = "1.6"
+num-traits = "0.2"
+thiserror = "1"
+
+[dependencies.iced_runtime]
+version = "0.9"
+path = "../runtime"
+
+[dependencies.iced_renderer]
+version = "0.1"
+path = "../renderer"
+
+[dependencies.iced_style]
+version = "0.7"
+path = "../style"
+
+[dependencies.ouroboros]
+version = "0.13"
+optional = true
+
+[dependencies.qrcode]
+version = "0.12"
+optional = true
+default-features = false
diff --git a/native/src/widget/button.rs b/widget/src/button.rs
index 39387173..7eee69cb 100644
--- a/native/src/widget/button.rs
+++ b/widget/src/button.rs
@@ -1,15 +1,15 @@
//! Allow your users to perform actions by pressing a button.
//!
//! A [`Button`] has some local [`State`].
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::touch;
-use crate::widget::tree::{self, Tree};
-use crate::widget::Operation;
-use crate::{
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::touch;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::widget::Operation;
+use crate::core::{
Background, Clipboard, Color, Element, Layout, Length, Padding, Point,
Rectangle, Shell, Vector, Widget,
};
@@ -18,9 +18,9 @@ pub use iced_style::button::{Appearance, StyleSheet};
/// A generic widget that produces a message when pressed.
///
-/// ```
+/// ```no_run
/// # type Button<'a, Message> =
-/// # iced_native::widget::Button<'a, Message, iced_native::renderer::Null>;
+/// # iced_widget::Button<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// #[derive(Clone)]
/// enum Message {
@@ -35,7 +35,7 @@ pub use iced_style::button::{Appearance, StyleSheet};
///
/// ```
/// # type Button<'a, Message> =
-/// # iced_native::widget::Button<'a, Message, iced_native::renderer::Null>;
+/// # iced_widget::Button<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// #[derive(Clone)]
/// enum Message {
@@ -51,9 +51,9 @@ pub use iced_style::button::{Appearance, StyleSheet};
/// }
/// ```
#[allow(missing_debug_implementations)]
-pub struct Button<'a, Message, Renderer>
+pub struct Button<'a, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
content: Element<'a, Message, Renderer>,
@@ -66,7 +66,7 @@ where
impl<'a, Message, Renderer> Button<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
/// Creates a new [`Button`] with the given content.
@@ -121,7 +121,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
for Button<'a, Message, Renderer>
where
Message: 'a + Clone,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn tag(&self) -> tree::Tag {
@@ -279,7 +279,7 @@ impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: Clone + 'a,
- Renderer: crate::Renderer + 'a,
+ Renderer: crate::core::Renderer + 'a,
Renderer::Theme: StyleSheet,
{
fn from(button: Button<'a, Message, Renderer>) -> Self {
@@ -355,7 +355,7 @@ pub fn update<'a, Message: Clone>(
}
/// Draws a [`Button`].
-pub fn draw<'a, Renderer: crate::Renderer>(
+pub fn draw<'a, Renderer: crate::core::Renderer>(
renderer: &mut Renderer,
bounds: Rectangle,
cursor_position: Point,
diff --git a/graphics/src/widget/canvas.rs b/widget/src/canvas.rs
index a8d050f5..171c4534 100644
--- a/graphics/src/widget/canvas.rs
+++ b/widget/src/canvas.rs
@@ -1,43 +1,24 @@
//! Draw 2D graphics for your users.
-//!
-//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a
-//! [`Frame`]. It can be used for animation, data visualization, game graphics,
-//! and more!
pub mod event;
-pub mod fill;
-pub mod path;
-pub mod stroke;
-mod cache;
mod cursor;
-mod frame;
-mod geometry;
mod program;
-mod style;
-mod text;
-pub use crate::gradient::{self, Gradient};
-pub use cache::Cache;
pub use cursor::Cursor;
pub use event::Event;
-pub use fill::{Fill, FillRule};
-pub use frame::Frame;
-pub use geometry::Geometry;
-pub use path::Path;
pub use program::Program;
-pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
-pub use style::Style;
-pub use text::Text;
-use crate::{Backend, Primitive, Renderer};
+pub use crate::graphics::geometry::*;
+pub use crate::renderer::geometry::*;
-use iced_native::layout::{self, Layout};
-use iced_native::mouse;
-use iced_native::renderer;
-use iced_native::widget::tree::{self, Tree};
-use iced_native::{
- Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, Widget,
-};
+use crate::core;
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{Clipboard, Element, Shell, Widget};
+use crate::core::{Length, Point, Rectangle, Size, Vector};
+use crate::graphics::geometry;
use std::marker::PhantomData;
@@ -47,15 +28,11 @@ use std::marker::PhantomData;
/// If you want to get a quick overview, here's how we can draw a simple circle:
///
/// ```no_run
-/// # mod iced {
-/// # pub mod widget {
-/// # pub use iced_graphics::widget::canvas;
-/// # }
-/// # pub use iced_native::{Color, Rectangle, Theme};
-/// # }
-/// use iced::widget::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program};
-/// use iced::{Color, Rectangle, Theme};
-///
+/// # use iced_widget::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program};
+/// # use iced_widget::core::{Color, Rectangle};
+/// # use iced_widget::style::Theme;
+/// #
+/// # pub type Renderer = iced_widget::renderer::Renderer<Theme>;
/// // First, we define the data we need for drawing
/// #[derive(Debug)]
/// struct Circle {
@@ -66,9 +43,9 @@ use std::marker::PhantomData;
/// impl Program<()> for Circle {
/// type State = ();
///
-/// fn draw(&self, _state: &(), _theme: &Theme, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{
+/// fn draw(&self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{
/// // We prepare a new `Frame`
-/// let mut frame = Frame::new(bounds.size());
+/// let mut frame = Frame::new(renderer, bounds.size());
///
/// // We create a `Path` representing a simple circle
/// let circle = Path::circle(frame.center(), self.radius);
@@ -85,20 +62,22 @@ use std::marker::PhantomData;
/// let canvas = Canvas::new(Circle { radius: 50.0 });
/// ```
#[derive(Debug)]
-pub struct Canvas<Message, Theme, P>
+pub struct Canvas<P, Message, Renderer = crate::Renderer>
where
- P: Program<Message, Theme>,
+ Renderer: geometry::Renderer,
+ P: Program<Message, Renderer>,
{
width: Length,
height: Length,
program: P,
message_: PhantomData<Message>,
- theme_: PhantomData<Theme>,
+ theme_: PhantomData<Renderer>,
}
-impl<Message, Theme, P> Canvas<Message, Theme, P>
+impl<P, Message, Renderer> Canvas<P, Message, Renderer>
where
- P: Program<Message, Theme>,
+ Renderer: geometry::Renderer,
+ P: Program<Message, Renderer>,
{
const DEFAULT_SIZE: f32 = 100.0;
@@ -126,10 +105,11 @@ where
}
}
-impl<Message, P, B, T> Widget<Message, Renderer<B, T>> for Canvas<Message, T, P>
+impl<P, Message, Renderer> Widget<Message, Renderer>
+ for Canvas<P, Message, Renderer>
where
- P: Program<Message, T>,
- B: Backend,
+ Renderer: geometry::Renderer,
+ P: Program<Message, Renderer>,
{
fn tag(&self) -> tree::Tag {
struct Tag<T>(T);
@@ -150,7 +130,7 @@ where
fn layout(
&self,
- _renderer: &Renderer<B, T>,
+ _renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(self.height);
@@ -162,23 +142,19 @@ where
fn on_event(
&mut self,
tree: &mut Tree,
- event: iced_native::Event,
+ event: core::Event,
layout: Layout<'_>,
cursor_position: Point,
- _renderer: &Renderer<B, T>,
+ _renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
let bounds = layout.bounds();
let canvas_event = match event {
- iced_native::Event::Mouse(mouse_event) => {
- Some(Event::Mouse(mouse_event))
- }
- iced_native::Event::Touch(touch_event) => {
- Some(Event::Touch(touch_event))
- }
- iced_native::Event::Keyboard(keyboard_event) => {
+ core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)),
+ core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)),
+ core::Event::Keyboard(keyboard_event) => {
Some(Event::Keyboard(keyboard_event))
}
_ => None,
@@ -208,7 +184,7 @@ where
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
- _renderer: &Renderer<B, T>,
+ _renderer: &Renderer,
) -> mouse::Interaction {
let bounds = layout.bounds();
let cursor = Cursor::from_window_position(cursor_position);
@@ -220,49 +196,43 @@ where
fn draw(
&self,
tree: &Tree,
- renderer: &mut Renderer<B, T>,
- theme: &T,
+ renderer: &mut Renderer,
+ theme: &Renderer::Theme,
_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) {
- use iced_native::Renderer as _;
-
let bounds = layout.bounds();
if bounds.width < 1.0 || bounds.height < 1.0 {
return;
}
- let translation = Vector::new(bounds.x, bounds.y);
let cursor = Cursor::from_window_position(cursor_position);
let state = tree.state.downcast_ref::<P::State>();
- renderer.with_translation(translation, |renderer| {
- renderer.draw_primitive(Primitive::Group {
- primitives: self
- .program
- .draw(state, theme, bounds, cursor)
- .into_iter()
- .map(Geometry::into_primitive)
- .collect(),
- });
- });
+ renderer.with_translation(
+ Vector::new(bounds.x, bounds.y),
+ |renderer| {
+ renderer.draw(
+ self.program.draw(state, renderer, theme, bounds, cursor),
+ );
+ },
+ );
}
}
-impl<'a, Message, P, B, T> From<Canvas<Message, T, P>>
- for Element<'a, Message, Renderer<B, T>>
+impl<'a, P, Message, Renderer> From<Canvas<P, Message, Renderer>>
+ for Element<'a, Message, Renderer>
where
Message: 'a,
- P: Program<Message, T> + 'a,
- B: Backend,
- T: 'a,
+ Renderer: 'a + geometry::Renderer,
+ P: Program<Message, Renderer> + 'a,
{
fn from(
- canvas: Canvas<Message, T, P>,
- ) -> Element<'a, Message, Renderer<B, T>> {
+ canvas: Canvas<P, Message, Renderer>,
+ ) -> Element<'a, Message, Renderer> {
Element::new(canvas)
}
}
diff --git a/graphics/src/widget/canvas/cursor.rs b/widget/src/canvas/cursor.rs
index 9588d129..5a65e9a7 100644
--- a/graphics/src/widget/canvas/cursor.rs
+++ b/widget/src/canvas/cursor.rs
@@ -1,4 +1,4 @@
-use iced_native::{Point, Rectangle};
+use crate::core::{Point, Rectangle};
/// The mouse cursor state.
#[derive(Debug, Clone, Copy, PartialEq)]
diff --git a/graphics/src/widget/canvas/event.rs b/widget/src/canvas/event.rs
index 7c733a4d..4508c184 100644
--- a/graphics/src/widget/canvas/event.rs
+++ b/widget/src/canvas/event.rs
@@ -1,9 +1,9 @@
//! Handle events of a canvas.
-use iced_native::keyboard;
-use iced_native::mouse;
-use iced_native::touch;
+use crate::core::keyboard;
+use crate::core::mouse;
+use crate::core::touch;
-pub use iced_native::event::Status;
+pub use crate::core::event::Status;
/// A [`Canvas`] event.
///
diff --git a/graphics/src/widget/canvas/program.rs b/widget/src/canvas/program.rs
index 656dbfa6..efb33c56 100644
--- a/graphics/src/widget/canvas/program.rs
+++ b/widget/src/canvas/program.rs
@@ -1,7 +1,8 @@
-use crate::widget::canvas::event::{self, Event};
-use crate::widget::canvas::mouse;
-use crate::widget::canvas::{Cursor, Geometry};
-use crate::Rectangle;
+use crate::canvas::event::{self, Event};
+use crate::canvas::mouse;
+use crate::canvas::Cursor;
+use crate::core::Rectangle;
+use crate::graphics::geometry;
/// The state and logic of a [`Canvas`].
///
@@ -9,7 +10,10 @@ use crate::Rectangle;
/// application.
///
/// [`Canvas`]: crate::widget::Canvas
-pub trait Program<Message, Theme = iced_native::Theme> {
+pub trait Program<Message, Renderer = crate::Renderer>
+where
+ Renderer: geometry::Renderer,
+{
/// The internal state mutated by the [`Program`].
type State: Default + 'static;
@@ -44,10 +48,11 @@ pub trait Program<Message, Theme = iced_native::Theme> {
fn draw(
&self,
state: &Self::State,
- theme: &Theme,
+ renderer: &Renderer,
+ theme: &Renderer::Theme,
bounds: Rectangle,
cursor: Cursor,
- ) -> Vec<Geometry>;
+ ) -> Vec<Renderer::Geometry>;
/// Returns the current mouse interaction of the [`Program`].
///
@@ -65,9 +70,10 @@ pub trait Program<Message, Theme = iced_native::Theme> {
}
}
-impl<Message, Theme, T> Program<Message, Theme> for &T
+impl<Message, Renderer, T> Program<Message, Renderer> for &T
where
- T: Program<Message, Theme>,
+ Renderer: geometry::Renderer,
+ T: Program<Message, Renderer>,
{
type State = T::State;
@@ -84,11 +90,12 @@ where
fn draw(
&self,
state: &Self::State,
- theme: &Theme,
+ renderer: &Renderer,
+ theme: &Renderer::Theme,
bounds: Rectangle,
cursor: Cursor,
- ) -> Vec<Geometry> {
- T::draw(self, state, theme, bounds, cursor)
+ ) -> Vec<Renderer::Geometry> {
+ T::draw(self, state, renderer, theme, bounds, cursor)
}
fn mouse_interaction(
diff --git a/native/src/widget/checkbox.rs b/widget/src/checkbox.rs
index cd8b9c6b..d1f886c6 100644
--- a/native/src/widget/checkbox.rs
+++ b/widget/src/checkbox.rs
@@ -1,16 +1,17 @@
//! Show toggle controls using checkboxes.
-use crate::alignment;
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::text;
-use crate::touch;
-use crate::widget::{self, Row, Text, Tree};
-use crate::{
+use crate::core::alignment;
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::text;
+use crate::core::touch;
+use crate::core::widget::Tree;
+use crate::core::{
Alignment, Clipboard, Element, Layout, Length, Pixels, Point, Rectangle,
Shell, Widget,
};
+use crate::{Row, Text};
pub use iced_style::checkbox::{Appearance, StyleSheet};
@@ -29,8 +30,9 @@ pub struct Icon<Font> {
///
/// # Example
///
-/// ```
-/// # type Checkbox<'a, Message> = iced_native::widget::Checkbox<'a, Message, iced_native::renderer::Null>;
+/// ```no_run
+/// # type Checkbox<'a, Message> =
+/// # iced_widget::Checkbox<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// pub enum Message {
/// CheckboxToggled(bool),
@@ -43,10 +45,10 @@ pub struct Icon<Font> {
///
/// ![Checkbox drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Checkbox<'a, Message, Renderer>
+pub struct Checkbox<'a, Message, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
is_checked: bool,
on_toggle: Box<dyn Fn(bool) -> Message + 'a>,
@@ -63,7 +65,7 @@ where
impl<'a, Message, Renderer> Checkbox<'a, Message, Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
/// The default size of a [`Checkbox`].
const DEFAULT_SIZE: f32 = 20.0;
@@ -153,7 +155,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
for Checkbox<'a, Message, Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
fn width(&self) -> Length {
self.width
@@ -289,14 +291,14 @@ where
{
let label_layout = children.next().unwrap();
- widget::text::draw(
+ crate::text::draw(
renderer,
style,
label_layout,
&self.label,
self.text_size,
self.font,
- widget::text::Appearance {
+ crate::text::Appearance {
color: custom_style.text_color,
},
alignment::Horizontal::Left,
@@ -311,7 +313,7 @@ impl<'a, Message, Renderer> From<Checkbox<'a, Message, Renderer>>
where
Message: 'a,
Renderer: 'a + text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
fn from(
checkbox: Checkbox<'a, Message, Renderer>,
diff --git a/native/src/widget/column.rs b/widget/src/column.rs
index ebe579d5..8f363ec6 100644
--- a/native/src/widget/column.rs
+++ b/widget/src/column.rs
@@ -1,18 +1,18 @@
//! Distribute content vertically.
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::widget::{Operation, Tree};
-use crate::{
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget::{Operation, Tree};
+use crate::core::{
Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Point,
Rectangle, Shell, Widget,
};
/// A container that distributes its contents vertically.
#[allow(missing_debug_implementations)]
-pub struct Column<'a, Message, Renderer> {
+pub struct Column<'a, Message, Renderer = crate::Renderer> {
spacing: f32,
padding: Padding,
width: Length,
@@ -102,7 +102,7 @@ impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Column<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
{
fn children(&self) -> Vec<Tree> {
self.children.iter().map(Tree::new).collect()
@@ -256,7 +256,7 @@ impl<'a, Message, Renderer> From<Column<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: crate::Renderer + 'a,
+ Renderer: crate::core::Renderer + 'a,
{
fn from(column: Column<'a, Message, Renderer>) -> Self {
Self::new(column)
diff --git a/native/src/widget/container.rs b/widget/src/container.rs
index b77bf50d..9d932772 100644
--- a/native/src/widget/container.rs
+++ b/widget/src/container.rs
@@ -1,12 +1,12 @@
//! Decorate content and apply alignment.
-use crate::alignment::{self, Alignment};
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::widget::{self, Operation, Tree};
-use crate::{
+use crate::core::alignment::{self, Alignment};
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget::{self, Operation, Tree};
+use crate::core::{
Background, Clipboard, Color, Element, Layout, Length, Padding, Pixels,
Point, Rectangle, Shell, Widget,
};
@@ -17,9 +17,9 @@ pub use iced_style::container::{Appearance, StyleSheet};
///
/// It is normally used for alignment purposes.
#[allow(missing_debug_implementations)]
-pub struct Container<'a, Message, Renderer>
+pub struct Container<'a, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
id: Option<Id>,
@@ -36,7 +36,7 @@ where
impl<'a, Message, Renderer> Container<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
/// Creates an empty [`Container`].
@@ -131,7 +131,7 @@ where
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Container<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn children(&self) -> Vec<Tree> {
@@ -276,7 +276,7 @@ impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(
@@ -326,7 +326,7 @@ pub fn draw_background<Renderer>(
appearance: &Appearance,
bounds: Rectangle,
) where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
{
if appearance.background.is_some() || appearance.border_width > 0.0 {
renderer.fill_quad(
diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs
new file mode 100644
index 00000000..a43e7248
--- /dev/null
+++ b/widget/src/helpers.rs
@@ -0,0 +1,362 @@
+//! Helper functions to create pure widgets.
+use crate::button::{self, Button};
+use crate::checkbox::{self, Checkbox};
+use crate::container::{self, Container};
+use crate::core;
+use crate::core::widget::operation;
+use crate::core::{Element, Length, Pixels};
+use crate::overlay;
+use crate::pick_list::{self, PickList};
+use crate::progress_bar::{self, ProgressBar};
+use crate::radio::{self, Radio};
+use crate::rule::{self, Rule};
+use crate::runtime::Command;
+use crate::scrollable::{self, Scrollable};
+use crate::slider::{self, Slider};
+use crate::text::{self, Text};
+use crate::text_input::{self, TextInput};
+use crate::toggler::{self, Toggler};
+use crate::tooltip::{self, Tooltip};
+use crate::{Column, Row, Space, VerticalSlider};
+
+use std::borrow::Cow;
+use std::ops::RangeInclusive;
+
+/// Creates a [`Column`] with the given children.
+///
+/// [`Column`]: widget::Column
+#[macro_export]
+macro_rules! column {
+ () => (
+ $crate::Column::new()
+ );
+ ($($x:expr),+ $(,)?) => (
+ $crate::Column::with_children(vec![$($crate::core::Element::from($x)),+])
+ );
+}
+
+/// Creates a [`Row`] with the given children.
+///
+/// [`Row`]: widget::Row
+#[macro_export]
+macro_rules! row {
+ () => (
+ $crate::Row::new()
+ );
+ ($($x:expr),+ $(,)?) => (
+ $crate::Row::with_children(vec![$($crate::core::Element::from($x)),+])
+ );
+}
+
+/// Creates a new [`Container`] with the provided content.
+///
+/// [`Container`]: widget::Container
+pub fn container<'a, Message, Renderer>(
+ content: impl Into<Element<'a, Message, Renderer>>,
+) -> Container<'a, Message, Renderer>
+where
+ Renderer: core::Renderer,
+ Renderer::Theme: container::StyleSheet,
+{
+ Container::new(content)
+}
+
+/// Creates a new [`Column`] with the given children.
+///
+/// [`Column`]: widget::Column
+pub fn column<Message, Renderer>(
+ children: Vec<Element<'_, Message, Renderer>>,
+) -> Column<'_, Message, Renderer> {
+ Column::with_children(children)
+}
+
+/// Creates a new [`Row`] with the given children.
+///
+/// [`Row`]: widget::Row
+pub fn row<Message, Renderer>(
+ children: Vec<Element<'_, Message, Renderer>>,
+) -> Row<'_, Message, Renderer> {
+ Row::with_children(children)
+}
+
+/// Creates a new [`Scrollable`] with the provided content.
+///
+/// [`Scrollable`]: widget::Scrollable
+pub fn scrollable<'a, Message, Renderer>(
+ content: impl Into<Element<'a, Message, Renderer>>,
+) -> Scrollable<'a, Message, Renderer>
+where
+ Renderer: core::Renderer,
+ Renderer::Theme: scrollable::StyleSheet,
+{
+ Scrollable::new(content)
+}
+
+/// Creates a new [`Button`] with the provided content.
+///
+/// [`Button`]: widget::Button
+pub fn button<'a, Message, Renderer>(
+ content: impl Into<Element<'a, Message, Renderer>>,
+) -> Button<'a, Message, Renderer>
+where
+ Renderer: core::Renderer,
+ Renderer::Theme: button::StyleSheet,
+ <Renderer::Theme as button::StyleSheet>::Style: Default,
+{
+ Button::new(content)
+}
+
+/// Creates a new [`Tooltip`] with the provided content, tooltip text, and [`tooltip::Position`].
+///
+/// [`Tooltip`]: widget::Tooltip
+/// [`tooltip::Position`]: widget::tooltip::Position
+pub fn tooltip<'a, Message, Renderer>(
+ content: impl Into<Element<'a, Message, Renderer>>,
+ tooltip: impl ToString,
+ position: tooltip::Position,
+) -> crate::Tooltip<'a, Message, Renderer>
+where
+ Renderer: core::text::Renderer,
+ Renderer::Theme: container::StyleSheet + text::StyleSheet,
+{
+ Tooltip::new(content, tooltip.to_string(), position)
+}
+
+/// Creates a new [`Text`] widget with the provided content.
+///
+/// [`Text`]: widget::Text
+pub fn text<'a, Renderer>(text: impl ToString) -> Text<'a, Renderer>
+where
+ Renderer: core::text::Renderer,
+ Renderer::Theme: text::StyleSheet,
+{
+ Text::new(text.to_string())
+}
+
+/// Creates a new [`Checkbox`].
+///
+/// [`Checkbox`]: widget::Checkbox
+pub fn checkbox<'a, Message, Renderer>(
+ label: impl Into<String>,
+ is_checked: bool,
+ f: impl Fn(bool) -> Message + 'a,
+) -> Checkbox<'a, Message, Renderer>
+where
+ Renderer: core::text::Renderer,
+ Renderer::Theme: checkbox::StyleSheet + text::StyleSheet,
+{
+ Checkbox::new(label, is_checked, f)
+}
+
+/// Creates a new [`Radio`].
+///
+/// [`Radio`]: widget::Radio
+pub fn radio<Message, Renderer, V>(
+ label: impl Into<String>,
+ value: V,
+ selected: Option<V>,
+ on_click: impl FnOnce(V) -> Message,
+) -> Radio<Message, Renderer>
+where
+ Message: Clone,
+ Renderer: core::text::Renderer,
+ Renderer::Theme: radio::StyleSheet,
+ V: Copy + Eq,
+{
+ Radio::new(value, label, selected, on_click)
+}
+
+/// Creates a new [`Toggler`].
+///
+/// [`Toggler`]: widget::Toggler
+pub fn toggler<'a, Message, Renderer>(
+ label: impl Into<Option<String>>,
+ is_checked: bool,
+ f: impl Fn(bool) -> Message + 'a,
+) -> Toggler<'a, Message, Renderer>
+where
+ Renderer: core::text::Renderer,
+ Renderer::Theme: toggler::StyleSheet,
+{
+ Toggler::new(label, is_checked, f)
+}
+
+/// Creates a new [`TextInput`].
+///
+/// [`TextInput`]: widget::TextInput
+pub fn text_input<'a, Message, Renderer>(
+ placeholder: &str,
+ value: &str,
+ on_change: impl Fn(String) -> Message + 'a,
+) -> TextInput<'a, Message, Renderer>
+where
+ Message: Clone,
+ Renderer: core::text::Renderer,
+ Renderer::Theme: text_input::StyleSheet,
+{
+ TextInput::new(placeholder, value, on_change)
+}
+
+/// Creates a new [`Slider`].
+///
+/// [`Slider`]: widget::Slider
+pub fn slider<'a, T, Message, Renderer>(
+ range: std::ops::RangeInclusive<T>,
+ value: T,
+ on_change: impl Fn(T) -> Message + 'a,
+) -> Slider<'a, T, Message, Renderer>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+ Message: Clone,
+ Renderer: core::Renderer,
+ Renderer::Theme: slider::StyleSheet,
+{
+ 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,
+) -> VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+ Message: Clone,
+ Renderer: core::Renderer,
+ Renderer::Theme: slider::StyleSheet,
+{
+ VerticalSlider::new(range, value, on_change)
+}
+
+/// Creates a new [`PickList`].
+///
+/// [`PickList`]: widget::PickList
+pub fn pick_list<'a, Message, Renderer, T>(
+ options: impl Into<Cow<'a, [T]>>,
+ selected: Option<T>,
+ on_selected: impl Fn(T) -> Message + 'a,
+) -> PickList<'a, T, Message, Renderer>
+where
+ T: ToString + Eq + 'static,
+ [T]: ToOwned<Owned = Vec<T>>,
+ Renderer: core::text::Renderer,
+ Renderer::Theme: pick_list::StyleSheet
+ + scrollable::StyleSheet
+ + overlay::menu::StyleSheet
+ + container::StyleSheet,
+ <Renderer::Theme as overlay::menu::StyleSheet>::Style:
+ From<<Renderer::Theme as pick_list::StyleSheet>::Style>,
+{
+ PickList::new(options, selected, on_selected)
+}
+
+/// Creates a new horizontal [`Space`] with the given [`Length`].
+///
+/// [`Space`]: widget::Space
+pub fn horizontal_space(width: impl Into<Length>) -> Space {
+ Space::with_width(width)
+}
+
+/// Creates a new vertical [`Space`] with the given [`Length`].
+///
+/// [`Space`]: widget::Space
+pub fn vertical_space(height: impl Into<Length>) -> Space {
+ Space::with_height(height)
+}
+
+/// Creates a horizontal [`Rule`] with the given height.
+///
+/// [`Rule`]: widget::Rule
+pub fn horizontal_rule<Renderer>(height: impl Into<Pixels>) -> Rule<Renderer>
+where
+ Renderer: core::Renderer,
+ Renderer::Theme: rule::StyleSheet,
+{
+ Rule::horizontal(height)
+}
+
+/// Creates a vertical [`Rule`] with the given width.
+///
+/// [`Rule`]: widget::Rule
+pub fn vertical_rule<Renderer>(width: impl Into<Pixels>) -> Rule<Renderer>
+where
+ Renderer: core::Renderer,
+ Renderer::Theme: rule::StyleSheet,
+{
+ Rule::vertical(width)
+}
+
+/// Creates a new [`ProgressBar`].
+///
+/// It expects:
+/// * an inclusive range of possible values, and
+/// * the current value of the [`ProgressBar`].
+///
+/// [`ProgressBar`]: widget::ProgressBar
+pub fn progress_bar<Renderer>(
+ range: RangeInclusive<f32>,
+ value: f32,
+) -> ProgressBar<Renderer>
+where
+ Renderer: core::Renderer,
+ Renderer::Theme: progress_bar::StyleSheet,
+{
+ ProgressBar::new(range, value)
+}
+
+/// Creates a new [`Image`].
+///
+/// [`Image`]: widget::Image
+#[cfg(feature = "image")]
+#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
+pub fn image<Handle>(handle: impl Into<Handle>) -> crate::Image<Handle> {
+ crate::Image::new(handle.into())
+}
+
+/// Creates a new [`Svg`] widget from the given [`Handle`].
+///
+/// [`Svg`]: widget::Svg
+/// [`Handle`]: widget::svg::Handle
+#[cfg(feature = "svg")]
+#[cfg_attr(docsrs, doc(cfg(feature = "svg")))]
+pub fn svg<Renderer>(
+ handle: impl Into<core::svg::Handle>,
+) -> crate::Svg<Renderer>
+where
+ Renderer: core::svg::Renderer,
+ Renderer::Theme: crate::svg::StyleSheet,
+{
+ crate::Svg::new(handle)
+}
+
+/// Creates a new [`Canvas`].
+#[cfg(feature = "canvas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
+pub fn canvas<P, Message, Renderer>(
+ program: P,
+) -> crate::Canvas<P, Message, Renderer>
+where
+ Renderer: crate::graphics::geometry::Renderer,
+ P: crate::canvas::Program<Message, Renderer>,
+{
+ crate::Canvas::new(program)
+}
+
+/// Focuses the previous focusable widget.
+pub fn focus_previous<Message>() -> Command<Message>
+where
+ Message: 'static,
+{
+ Command::widget(operation::focusable::focus_previous())
+}
+
+/// Focuses the next focusable widget.
+pub fn focus_next<Message>() -> Command<Message>
+where
+ Message: 'static,
+{
+ Command::widget(operation::focusable::focus_next())
+}
diff --git a/native/src/widget/image.rs b/widget/src/image.rs
index 73257a74..abcb6ef2 100644
--- a/native/src/widget/image.rs
+++ b/widget/src/image.rs
@@ -2,16 +2,18 @@
pub mod viewer;
pub use viewer::Viewer;
-use crate::image;
-use crate::layout;
-use crate::renderer;
-use crate::widget::Tree;
-use crate::{
+use crate::core::image;
+use crate::core::layout;
+use crate::core::renderer;
+use crate::core::widget::Tree;
+use crate::core::{
ContentFit, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
};
use std::hash::Hash;
+pub use image::Handle;
+
/// Creates a new [`Viewer`] with the given image `Handle`.
pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
Viewer::new(handle)
@@ -21,9 +23,8 @@ pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
///
/// # Example
///
-/// ```
-/// # use iced_native::widget::Image;
-/// # use iced_native::image;
+/// ```no_run
+/// # use iced_widget::image::{self, Image};
/// #
/// let image = Image::<image::Handle>::new("resources/ferris.png");
/// ```
diff --git a/native/src/widget/image/viewer.rs b/widget/src/image/viewer.rs
index 1f8d5d7a..0d60d818 100644
--- a/native/src/widget/image/viewer.rs
+++ b/widget/src/image/viewer.rs
@@ -1,11 +1,11 @@
//! Zoom and pan on an image.
-use crate::event::{self, Event};
-use crate::image;
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::widget::tree::{self, Tree};
-use crate::{
+use crate::core::event::{self, Event};
+use crate::core::image;
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size,
Vector, Widget,
};
diff --git a/lazy/src/lazy.rs b/widget/src/lazy.rs
index 5e909a49..b08ed8cb 100644
--- a/lazy/src/lazy.rs
+++ b/widget/src/lazy.rs
@@ -1,12 +1,25 @@
-use iced_native::event;
-use iced_native::layout::{self, Layout};
-use iced_native::mouse;
-use iced_native::overlay;
-use iced_native::renderer;
-use iced_native::widget::tree::{self, Tree};
-use iced_native::widget::{self, Widget};
-use iced_native::Element;
-use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell, Size};
+#![allow(clippy::await_holding_refcell_ref, clippy::type_complexity)]
+pub(crate) mod helpers;
+
+pub mod component;
+pub mod responsive;
+
+pub use component::Component;
+pub use responsive::Responsive;
+
+mod cache;
+
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::widget::{self, Widget};
+use crate::core::Element;
+use crate::core::{
+ self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size,
+};
use ouroboros::self_referencing;
use std::cell::RefCell;
@@ -41,7 +54,7 @@ where
fn with_element<T>(
&self,
- f: impl FnOnce(&Element<Message, Renderer>) -> T,
+ f: impl FnOnce(&Element<'_, Message, Renderer>) -> T,
) -> T {
f(self
.element
@@ -55,7 +68,7 @@ where
fn with_element_mut<T>(
&self,
- f: impl FnOnce(&mut Element<Message, Renderer>) -> T,
+ f: impl FnOnce(&mut Element<'_, Message, Renderer>) -> T,
) -> T {
f(self
.element
@@ -79,7 +92,7 @@ where
View: Into<Element<'static, Message, Renderer>> + 'static,
Dependency: Hash + 'a,
Message: 'static,
- Renderer: iced_native::Renderer + 'static,
+ Renderer: core::Renderer + 'static,
{
fn tag(&self) -> tree::Tag {
struct Tag<T>(T);
@@ -163,7 +176,7 @@ where
fn on_event(
&mut self,
tree: &mut Tree,
- event: iced_native::Event,
+ event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
@@ -304,7 +317,7 @@ impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> {
impl<'a, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
fn layout(
&self,
@@ -353,7 +366,7 @@ where
fn on_event(
&mut self,
- event: iced_native::Event,
+ event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
@@ -370,7 +383,7 @@ where
shell,
)
})
- .unwrap_or(iced_native::event::Status::Ignored)
+ .unwrap_or(event::Status::Ignored)
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
@@ -386,7 +399,7 @@ impl<'a, Message, Renderer, Dependency, View>
for Element<'a, Message, Renderer>
where
View: Into<Element<'static, Message, Renderer>> + 'static,
- Renderer: iced_native::Renderer + 'static,
+ Renderer: core::Renderer + 'static,
Message: 'static,
Dependency: Hash + 'a,
{
diff --git a/lazy/src/cache.rs b/widget/src/lazy/cache.rs
index 5b4a39f6..e7b87614 100644
--- a/lazy/src/cache.rs
+++ b/widget/src/lazy/cache.rs
@@ -1,5 +1,5 @@
-use iced_native::overlay;
-use iced_native::Element;
+use crate::core::overlay;
+use crate::core::Element;
use ouroboros::self_referencing;
diff --git a/lazy/src/component.rs b/widget/src/lazy/component.rs
index b23da9f7..0b8070af 100644
--- a/lazy/src/component.rs
+++ b/widget/src/lazy/component.rs
@@ -1,13 +1,13 @@
//! Build and reuse custom widgets using The Elm Architecture.
-use iced_native::event;
-use iced_native::layout::{self, Layout};
-use iced_native::mouse;
-use iced_native::overlay;
-use iced_native::renderer;
-use iced_native::widget;
-use iced_native::widget::tree::{self, Tree};
-use iced_native::{
- Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget,
+use crate::core::event;
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
+ self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget,
};
use ouroboros::self_referencing;
@@ -67,7 +67,7 @@ where
C: Component<Message, Renderer> + 'a,
C::State: 'static,
Message: 'a,
- Renderer: iced_native::Renderer + 'a,
+ Renderer: core::Renderer + 'a,
{
Element::new(Instance {
state: RefCell::new(Some(
@@ -159,7 +159,7 @@ impl<'a, Message, Renderer, Event, S> Widget<Message, Renderer>
for Instance<'a, Message, Renderer, Event, S>
where
S: 'static + Default,
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
fn tag(&self) -> tree::Tag {
struct Tag<T>(T);
@@ -203,7 +203,7 @@ where
fn on_event(
&mut self,
tree: &mut Tree,
- event: iced_native::Event,
+ event: core::Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
@@ -447,7 +447,7 @@ impl<'a, 'b, Message, Renderer, Event, S>
impl<'a, 'b, Message, Renderer, Event, S> overlay::Overlay<Message, Renderer>
for OverlayInstance<'a, 'b, Message, Renderer, Event, S>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
S: 'static + Default,
{
fn layout(
@@ -497,13 +497,13 @@ where
fn on_event(
&mut self,
- event: iced_native::Event,
+ event: core::Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
- ) -> iced_native::event::Status {
+ ) -> event::Status {
let mut local_messages = Vec::new();
let mut local_shell = Shell::new(&mut local_messages);
@@ -518,7 +518,7 @@ where
&mut local_shell,
)
})
- .unwrap_or(iced_native::event::Status::Ignored);
+ .unwrap_or(event::Status::Ignored);
local_shell.revalidate_layout(|| shell.invalidate_layout());
diff --git a/lazy/src/lib.rs b/widget/src/lazy/helpers.rs
index 41a28773..be60bb78 100644
--- a/lazy/src/lib.rs
+++ b/widget/src/lazy/helpers.rs
@@ -1,34 +1,7 @@
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
-)]
-#![deny(
- missing_debug_implementations,
- unused_results,
- clippy::extra_unused_lifetimes,
- clippy::from_over_into,
- clippy::needless_borrow,
- clippy::new_without_default,
- clippy::useless_conversion
-)]
-#![forbid(unsafe_code)]
-#![allow(
- clippy::await_holding_refcell_ref,
- clippy::inherent_to_string,
- clippy::type_complexity
-)]
-#![cfg_attr(docsrs, feature(doc_cfg))]
-mod lazy;
+use crate::core::{self, Element, Size};
+use crate::lazy::component::{self, Component};
+use crate::lazy::{Lazy, Responsive};
-pub mod component;
-pub mod responsive;
-
-pub use component::Component;
-pub use lazy::Lazy;
-pub use responsive::Responsive;
-
-mod cache;
-
-use iced_native::{Element, Size};
use std::hash::Hash;
pub fn lazy<'a, Message, Renderer, Dependency, View>(
@@ -51,7 +24,7 @@ where
C: Component<Message, Renderer> + 'a,
C::State: 'static,
Message: 'a,
- Renderer: iced_native::Renderer + 'a,
+ Renderer: core::Renderer + 'a,
{
component::view(component)
}
@@ -60,7 +33,7 @@ pub fn responsive<'a, Message, Renderer>(
f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a,
) -> Responsive<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
Responsive::new(f)
}
diff --git a/lazy/src/responsive.rs b/widget/src/lazy/responsive.rs
index 57c07de1..7b2fc37c 100644
--- a/lazy/src/responsive.rs
+++ b/widget/src/lazy/responsive.rs
@@ -1,13 +1,14 @@
-use iced_native::event;
-use iced_native::layout::{self, Layout};
-use iced_native::mouse;
-use iced_native::overlay;
-use iced_native::renderer;
-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,
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
+ self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget,
};
+use crate::horizontal_space;
use ouroboros::self_referencing;
use std::cell::{RefCell, RefMut};
@@ -19,14 +20,14 @@ use std::ops::Deref;
/// A [`Responsive`] widget will always try to fill all the available space of
/// its parent.
#[allow(missing_debug_implementations)]
-pub struct Responsive<'a, Message, Renderer> {
+pub struct Responsive<'a, Message, Renderer = crate::Renderer> {
view: Box<dyn Fn(Size) -> Element<'a, Message, Renderer> + 'a>,
content: RefCell<Content<'a, Message, Renderer>>,
}
impl<'a, Message, Renderer> Responsive<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
/// Creates a new [`Responsive`] widget with a closure that produces its
/// contents.
@@ -56,7 +57,7 @@ struct Content<'a, Message, Renderer> {
impl<'a, Message, Renderer> Content<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
fn update(
&mut self,
@@ -114,7 +115,7 @@ struct State {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Responsive<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<State>()
@@ -168,7 +169,7 @@ where
fn on_event(
&mut self,
tree: &mut Tree,
- event: iced_native::Event,
+ event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
@@ -271,7 +272,8 @@ where
content: self.content.borrow_mut(),
tree: state.tree.borrow_mut(),
types: PhantomData,
- overlay_builder: |content: &mut RefMut<Content<_, _>>, tree| {
+ overlay_builder: |content: &mut RefMut<'_, Content<'_, _, _>>,
+ tree| {
content.update(
tree,
renderer,
@@ -309,7 +311,7 @@ where
impl<'a, Message, Renderer> From<Responsive<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: iced_native::Renderer + 'a,
+ Renderer: core::Renderer + 'a,
Message: 'a,
{
fn from(responsive: Responsive<'a, Message, Renderer>) -> Self {
@@ -347,7 +349,7 @@ impl<'a, 'b, Message, Renderer> Overlay<'a, 'b, Message, Renderer> {
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer>
where
- Renderer: iced_native::Renderer,
+ Renderer: core::Renderer,
{
fn layout(
&self,
@@ -396,7 +398,7 @@ where
fn on_event(
&mut self,
- event: iced_native::Event,
+ event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
@@ -413,7 +415,7 @@ where
shell,
)
})
- .unwrap_or(iced_native::event::Status::Ignored)
+ .unwrap_or(event::Status::Ignored)
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
diff --git a/widget/src/lib.rs b/widget/src/lib.rs
new file mode 100644
index 00000000..a3e7c8bc
--- /dev/null
+++ b/widget/src/lib.rs
@@ -0,0 +1,122 @@
+//! Use the built-in widgets or create your own.
+#![doc(
+ html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
+)]
+#![deny(
+ missing_debug_implementations,
+ //missing_docs,
+ unused_results,
+ clippy::extra_unused_lifetimes,
+ clippy::from_over_into,
+ clippy::needless_borrow,
+ clippy::new_without_default,
+ clippy::useless_conversion
+)]
+#![forbid(unsafe_code, rust_2018_idioms)]
+#![allow(clippy::inherent_to_string, clippy::type_complexity)]
+pub use iced_renderer as renderer;
+pub use iced_renderer::graphics;
+pub use iced_runtime as runtime;
+pub use iced_runtime::core;
+pub use iced_style as style;
+
+mod column;
+mod row;
+
+pub mod button;
+pub mod checkbox;
+pub mod container;
+pub mod overlay;
+pub mod pane_grid;
+pub mod pick_list;
+pub mod progress_bar;
+pub mod radio;
+pub mod rule;
+pub mod scrollable;
+pub mod slider;
+pub mod space;
+pub mod text;
+pub mod text_input;
+pub mod toggler;
+pub mod tooltip;
+pub mod vertical_slider;
+
+mod helpers;
+
+pub use helpers::*;
+
+#[cfg(feature = "lazy")]
+mod lazy;
+
+#[cfg(feature = "lazy")]
+pub use crate::lazy::{Component, Lazy, Responsive};
+
+#[cfg(feature = "lazy")]
+pub use crate::lazy::helpers::*;
+
+#[doc(no_inline)]
+pub use button::Button;
+#[doc(no_inline)]
+pub use checkbox::Checkbox;
+#[doc(no_inline)]
+pub use column::Column;
+#[doc(no_inline)]
+pub use container::Container;
+#[doc(no_inline)]
+pub use pane_grid::PaneGrid;
+#[doc(no_inline)]
+pub use pick_list::PickList;
+#[doc(no_inline)]
+pub use progress_bar::ProgressBar;
+#[doc(no_inline)]
+pub use radio::Radio;
+#[doc(no_inline)]
+pub use row::Row;
+#[doc(no_inline)]
+pub use rule::Rule;
+#[doc(no_inline)]
+pub use scrollable::Scrollable;
+#[doc(no_inline)]
+pub use slider::Slider;
+#[doc(no_inline)]
+pub use space::Space;
+#[doc(no_inline)]
+pub use text::Text;
+#[doc(no_inline)]
+pub use text_input::TextInput;
+#[doc(no_inline)]
+pub use toggler::Toggler;
+#[doc(no_inline)]
+pub use tooltip::Tooltip;
+#[doc(no_inline)]
+pub use vertical_slider::VerticalSlider;
+
+#[cfg(feature = "svg")]
+pub mod svg;
+
+#[cfg(feature = "svg")]
+#[doc(no_inline)]
+pub use svg::Svg;
+
+#[cfg(feature = "image")]
+pub mod image;
+
+#[cfg(feature = "image")]
+#[doc(no_inline)]
+pub use image::Image;
+
+#[cfg(feature = "canvas")]
+pub mod canvas;
+
+#[cfg(feature = "canvas")]
+#[doc(no_inline)]
+pub use canvas::Canvas;
+
+#[cfg(feature = "qr_code")]
+pub mod qr_code;
+
+#[cfg(feature = "qr_code")]
+#[doc(no_inline)]
+pub use qr_code::QRCode;
+
+type Renderer<Theme = style::Theme> = renderer::Renderer<Theme>;
diff --git a/widget/src/overlay.rs b/widget/src/overlay.rs
new file mode 100644
index 00000000..b9a0e3e0
--- /dev/null
+++ b/widget/src/overlay.rs
@@ -0,0 +1 @@
+pub mod menu;
diff --git a/native/src/overlay/menu.rs b/widget/src/overlay/menu.rs
index bd58a122..c322c8ba 100644
--- a/native/src/overlay/menu.rs
+++ b/widget/src/overlay/menu.rs
@@ -1,25 +1,25 @@
//! Build and show dropdown menus.
-use crate::alignment;
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::text::{self, Text};
-use crate::touch;
-use crate::widget::container::{self, Container};
-use crate::widget::scrollable::{self, Scrollable};
-use crate::widget::Tree;
-use crate::{
- Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point,
- Rectangle, Shell, Size, Vector, Widget,
+use crate::container::{self, Container};
+use crate::core::alignment;
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::text::{self, Text};
+use crate::core::touch;
+use crate::core::widget::Tree;
+use crate::core::{
+ Clipboard, Color, Length, Padding, Pixels, Point, Rectangle, Size, Vector,
};
+use crate::core::{Element, Shell, Widget};
+use crate::scrollable::{self, Scrollable};
pub use iced_style::menu::{Appearance, StyleSheet};
/// A list of selectable options.
#[allow(missing_debug_implementations)]
-pub struct Menu<'a, T, Renderer>
+pub struct Menu<'a, T, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
@@ -137,7 +137,7 @@ impl Default for State {
struct Overlay<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet + container::StyleSheet,
{
state: &'a mut Tree,
@@ -193,7 +193,7 @@ where
}
}
-impl<'a, Message, Renderer> crate::Overlay<Message, Renderer>
+impl<'a, Message, Renderer> crate::core::Overlay<Message, Renderer>
for Overlay<'a, Message, Renderer>
where
Renderer: text::Renderer,
diff --git a/native/src/widget/pane_grid.rs b/widget/src/pane_grid.rs
index bcb17ebd..257c0144 100644
--- a/native/src/widget/pane_grid.rs
+++ b/widget/src/pane_grid.rs
@@ -30,18 +30,18 @@ pub use split::Split;
pub use state::State;
pub use title_bar::TitleBar;
-pub use iced_style::pane_grid::{Line, StyleSheet};
-
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay::{self, Group};
-use crate::renderer;
-use crate::touch;
-use crate::widget;
-use crate::widget::container;
-use crate::widget::tree::{self, Tree};
-use crate::{
+pub use crate::style::pane_grid::{Line, StyleSheet};
+
+use crate::container;
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay::{self, Group};
+use crate::core::renderer;
+use crate::core::touch;
+use crate::core::widget;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
Clipboard, Color, Element, Layout, Length, Pixels, Point, Rectangle, Shell,
Size, Vector, Widget,
};
@@ -67,11 +67,11 @@ use crate::{
///
/// ## Example
///
-/// ```
-/// # use iced_native::widget::{pane_grid, text};
+/// ```no_run
+/// # use iced_widget::{pane_grid, text};
/// #
/// # type PaneGrid<'a, Message> =
-/// # iced_native::widget::PaneGrid<'a, Message, iced_native::renderer::Null>;
+/// # iced_widget::PaneGrid<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// enum PaneState {
/// SomePane,
@@ -96,9 +96,9 @@ use crate::{
/// .on_resize(10, Message::PaneResized);
/// ```
#[allow(missing_debug_implementations)]
-pub struct PaneGrid<'a, Message, Renderer>
+pub struct PaneGrid<'a, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet + container::StyleSheet,
{
contents: Contents<'a, Content<'a, Message, Renderer>>,
@@ -113,7 +113,7 @@ where
impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet + container::StyleSheet,
{
/// Creates a [`PaneGrid`] with the given [`State`] and view function.
@@ -232,7 +232,7 @@ where
impl<'a, Message, Renderer> Widget<Message, Renderer>
for PaneGrid<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet + container::StyleSheet,
{
fn tag(&self) -> tree::Tag {
@@ -468,7 +468,7 @@ impl<'a, Message, Renderer> From<PaneGrid<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet + container::StyleSheet,
{
fn from(
@@ -755,7 +755,7 @@ pub fn draw<Renderer, T>(
&Rectangle,
),
) where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
let picked_pane = action.picked_pane();
diff --git a/native/src/widget/pane_grid/axis.rs b/widget/src/pane_grid/axis.rs
index 02bde064..a3049230 100644
--- a/native/src/widget/pane_grid/axis.rs
+++ b/widget/src/pane_grid/axis.rs
@@ -1,4 +1,4 @@
-use crate::Rectangle;
+use crate::core::Rectangle;
/// A fixed reference line for the measurement of coordinates.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
diff --git a/native/src/widget/pane_grid/configuration.rs b/widget/src/pane_grid/configuration.rs
index 7d68fb46..ddbc3bc2 100644
--- a/native/src/widget/pane_grid/configuration.rs
+++ b/widget/src/pane_grid/configuration.rs
@@ -1,4 +1,4 @@
-use crate::widget::pane_grid::Axis;
+use crate::pane_grid::Axis;
/// The arrangement of a [`PaneGrid`].
///
diff --git a/native/src/widget/pane_grid/content.rs b/widget/src/pane_grid/content.rs
index c9b0df07..035ef05b 100644
--- a/native/src/widget/pane_grid/content.rs
+++ b/widget/src/pane_grid/content.rs
@@ -1,20 +1,20 @@
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::widget::container;
-use crate::widget::pane_grid::{Draggable, TitleBar};
-use crate::widget::{self, Tree};
-use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
+use crate::container;
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget::{self, Tree};
+use crate::core::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
+use crate::pane_grid::{Draggable, TitleBar};
/// The content of a [`Pane`].
///
/// [`Pane`]: crate::widget::pane_grid::Pane
#[allow(missing_debug_implementations)]
-pub struct Content<'a, Message, Renderer>
+pub struct Content<'a, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
title_bar: Option<TitleBar<'a, Message, Renderer>>,
@@ -24,7 +24,7 @@ where
impl<'a, Message, Renderer> Content<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
/// Creates a new [`Content`] with the provided body.
@@ -57,7 +57,7 @@ where
impl<'a, Message, Renderer> Content<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
pub(super) fn state(&self) -> Tree {
@@ -342,7 +342,7 @@ where
impl<'a, Message, Renderer> Draggable for &Content<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
fn can_be_dragged_at(
@@ -364,7 +364,7 @@ where
impl<'a, T, Message, Renderer> From<T> for Content<'a, Message, Renderer>
where
T: Into<Element<'a, Message, Renderer>>,
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
fn from(element: T) -> Self {
diff --git a/native/src/widget/pane_grid/direction.rs b/widget/src/pane_grid/direction.rs
index b31a8737..b31a8737 100644
--- a/native/src/widget/pane_grid/direction.rs
+++ b/widget/src/pane_grid/direction.rs
diff --git a/native/src/widget/pane_grid/draggable.rs b/widget/src/pane_grid/draggable.rs
index 6044871d..a9274dad 100644
--- a/native/src/widget/pane_grid/draggable.rs
+++ b/widget/src/pane_grid/draggable.rs
@@ -1,4 +1,4 @@
-use crate::{Layout, Point};
+use crate::core::{Layout, Point};
/// A pane that can be dragged.
pub trait Draggable {
diff --git a/native/src/widget/pane_grid/node.rs b/widget/src/pane_grid/node.rs
index cc304b96..3976acd8 100644
--- a/native/src/widget/pane_grid/node.rs
+++ b/widget/src/pane_grid/node.rs
@@ -1,5 +1,5 @@
-use crate::widget::pane_grid::{Axis, Pane, Split};
-use crate::{Rectangle, Size};
+use crate::core::{Rectangle, Size};
+use crate::pane_grid::{Axis, Pane, Split};
use std::collections::BTreeMap;
diff --git a/native/src/widget/pane_grid/pane.rs b/widget/src/pane_grid/pane.rs
index d6fbab83..d6fbab83 100644
--- a/native/src/widget/pane_grid/pane.rs
+++ b/widget/src/pane_grid/pane.rs
diff --git a/native/src/widget/pane_grid/split.rs b/widget/src/pane_grid/split.rs
index 8132272a..8132272a 100644
--- a/native/src/widget/pane_grid/split.rs
+++ b/widget/src/pane_grid/split.rs
diff --git a/native/src/widget/pane_grid/state.rs b/widget/src/pane_grid/state.rs
index c4ae0a0e..a6e2ec7f 100644
--- a/native/src/widget/pane_grid/state.rs
+++ b/widget/src/pane_grid/state.rs
@@ -1,10 +1,8 @@
//! The state of a [`PaneGrid`].
//!
//! [`PaneGrid`]: crate::widget::PaneGrid
-use crate::widget::pane_grid::{
- Axis, Configuration, Direction, Node, Pane, Split,
-};
-use crate::{Point, Size};
+use crate::core::{Point, Size};
+use crate::pane_grid::{Axis, Configuration, Direction, Node, Pane, Split};
use std::collections::HashMap;
diff --git a/native/src/widget/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs
index 107078ef..2129937b 100644
--- a/native/src/widget/pane_grid/title_bar.rs
+++ b/widget/src/pane_grid/title_bar.rs
@@ -1,11 +1,11 @@
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::widget::container;
-use crate::widget::{self, Tree};
-use crate::{
+use crate::container;
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget::{self, Tree};
+use crate::core::{
Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size,
};
@@ -13,9 +13,9 @@ use crate::{
///
/// [`Pane`]: crate::widget::pane_grid::Pane
#[allow(missing_debug_implementations)]
-pub struct TitleBar<'a, Message, Renderer>
+pub struct TitleBar<'a, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
content: Element<'a, Message, Renderer>,
@@ -27,7 +27,7 @@ where
impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
/// Creates a new [`TitleBar`] with the given content.
@@ -84,7 +84,7 @@ where
impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: container::StyleSheet,
{
pub(super) fn state(&self) -> Tree {
diff --git a/native/src/widget/pick_list.rs b/widget/src/pick_list.rs
index 8ff82f3b..cd23cdd2 100644
--- a/native/src/widget/pick_list.rs
+++ b/widget/src/pick_list.rs
@@ -1,28 +1,29 @@
//! Display a dropdown list of selectable values.
-use crate::alignment;
-use crate::event::{self, Event};
-use crate::keyboard;
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::overlay::menu::{self, Menu};
-use crate::renderer;
-use crate::text::{self, Text};
-use crate::touch;
-use crate::widget::container;
-use crate::widget::scrollable;
-use crate::widget::tree::{self, Tree};
-use crate::{
+use crate::container;
+use crate::core::alignment;
+use crate::core::event::{self, Event};
+use crate::core::keyboard;
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::text::{self, Text};
+use crate::core::touch;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
Clipboard, Element, Layout, Length, Padding, Pixels, Point, Rectangle,
Shell, Size, Widget,
};
+use crate::overlay::menu::{self, Menu};
+use crate::scrollable;
+
use std::borrow::Cow;
-pub use iced_style::pick_list::{Appearance, StyleSheet};
+pub use crate::style::pick_list::{Appearance, StyleSheet};
/// A widget for selecting a single value from a list of options.
#[allow(missing_debug_implementations)]
-pub struct PickList<'a, T, Message, Renderer>
+pub struct PickList<'a, T, Message, Renderer = crate::Renderer>
where
[T]: ToOwned<Owned = Vec<T>>,
Renderer: text::Renderer,
diff --git a/native/src/widget/progress_bar.rs b/widget/src/progress_bar.rs
index dd46fa76..ef0d87d5 100644
--- a/native/src/widget/progress_bar.rs
+++ b/widget/src/progress_bar.rs
@@ -1,8 +1,10 @@
//! Provide progress feedback to your users.
-use crate::layout;
-use crate::renderer;
-use crate::widget::Tree;
-use crate::{Color, Element, Layout, Length, Point, Rectangle, Size, Widget};
+use crate::core::layout;
+use crate::core::renderer;
+use crate::core::widget::Tree;
+use crate::core::{
+ Color, Element, Layout, Length, Point, Rectangle, Size, Widget,
+};
use std::ops::RangeInclusive;
@@ -11,8 +13,10 @@ pub use iced_style::progress_bar::{Appearance, StyleSheet};
/// A bar that displays progress.
///
/// # Example
-/// ```
-/// # type ProgressBar = iced_native::widget::ProgressBar<iced_native::renderer::Null>;
+/// ```no_run
+/// # type ProgressBar =
+/// # iced_widget::ProgressBar<iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
+/// #
/// let value = 50.0;
///
/// ProgressBar::new(0.0..=100.0, value);
@@ -20,9 +24,9 @@ pub use iced_style::progress_bar::{Appearance, StyleSheet};
///
/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
#[allow(missing_debug_implementations)]
-pub struct ProgressBar<Renderer>
+pub struct ProgressBar<Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
range: RangeInclusive<f32>,
@@ -34,7 +38,7 @@ where
impl<Renderer> ProgressBar<Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
/// The default height of a [`ProgressBar`].
@@ -79,7 +83,7 @@ where
impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar<Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn width(&self) -> Length {
@@ -157,7 +161,7 @@ impl<'a, Message, Renderer> From<ProgressBar<Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(
diff --git a/graphics/src/widget/qr_code.rs b/widget/src/qr_code.rs
index 12ce5b1f..7709125f 100644
--- a/graphics/src/widget/qr_code.rs
+++ b/widget/src/qr_code.rs
@@ -1,13 +1,12 @@
//! Encode and display information in a QR code.
-use crate::renderer::{self, Renderer};
-use crate::widget::canvas;
-use crate::Backend;
-
-use iced_native::layout;
-use iced_native::widget::Tree;
-use iced_native::{
+use crate::canvas;
+use crate::core::layout;
+use crate::core::renderer::{self, Renderer as _};
+use crate::core::widget::Tree;
+use crate::core::{
Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
};
+use crate::Renderer;
use thiserror::Error;
const DEFAULT_CELL_SIZE: u16 = 4;
@@ -48,10 +47,7 @@ impl<'a> QRCode<'a> {
}
}
-impl<'a, Message, B, T> Widget<Message, Renderer<B, T>> for QRCode<'a>
-where
- B: Backend,
-{
+impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> {
fn width(&self) -> Length {
Length::Shrink
}
@@ -62,7 +58,7 @@ where
fn layout(
&self,
- _renderer: &Renderer<B, T>,
+ _renderer: &Renderer<Theme>,
_limits: &layout::Limits,
) -> layout::Node {
let side_length = (self.state.width + 2 * QUIET_ZONE) as f32
@@ -74,63 +70,63 @@ where
fn draw(
&self,
_state: &Tree,
- renderer: &mut Renderer<B, T>,
- _theme: &T,
+ renderer: &mut Renderer<Theme>,
+ _theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
) {
- use iced_native::Renderer as _;
-
let bounds = layout.bounds();
let side_length = self.state.width + 2 * QUIET_ZONE;
// Reuse cache if possible
- let geometry = self.state.cache.draw(bounds.size(), |frame| {
- // Scale units to cell size
- frame.scale(f32::from(self.cell_size));
-
- // Draw background
- frame.fill_rectangle(
- Point::ORIGIN,
- Size::new(side_length as f32, side_length as f32),
- self.light,
- );
-
- // Avoid drawing on the quiet zone
- frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32));
-
- // Draw contents
- self.state
- .contents
- .iter()
- .enumerate()
- .filter(|(_, value)| **value == qrcode::Color::Dark)
- .for_each(|(index, _)| {
- let row = index / self.state.width;
- let column = index % self.state.width;
-
- frame.fill_rectangle(
- Point::new(column as f32, row as f32),
- Size::UNIT,
- self.dark,
- );
- });
- });
+ let geometry =
+ self.state.cache.draw(renderer, bounds.size(), |frame| {
+ // Scale units to cell size
+ frame.scale(f32::from(self.cell_size));
+
+ // Draw background
+ frame.fill_rectangle(
+ Point::ORIGIN,
+ Size::new(side_length as f32, side_length as f32),
+ self.light,
+ );
+
+ // Avoid drawing on the quiet zone
+ frame.translate(Vector::new(
+ QUIET_ZONE as f32,
+ QUIET_ZONE as f32,
+ ));
+
+ // Draw contents
+ self.state
+ .contents
+ .iter()
+ .enumerate()
+ .filter(|(_, value)| **value == qrcode::Color::Dark)
+ .for_each(|(index, _)| {
+ let row = index / self.state.width;
+ let column = index % self.state.width;
+
+ frame.fill_rectangle(
+ Point::new(column as f32, row as f32),
+ Size::UNIT,
+ self.dark,
+ );
+ });
+ });
let translation = Vector::new(bounds.x, bounds.y);
renderer.with_translation(translation, |renderer| {
- renderer.draw_primitive(geometry.into_primitive());
+ renderer.draw_primitive(geometry.0);
});
}
}
-impl<'a, Message, B, T> From<QRCode<'a>>
- for Element<'a, Message, Renderer<B, T>>
-where
- B: Backend,
+impl<'a, Message, Theme> From<QRCode<'a>>
+ for Element<'a, Message, Renderer<Theme>>
{
fn from(qr_code: QRCode<'a>) -> Self {
Self::new(qr_code)
diff --git a/native/src/widget/radio.rs b/widget/src/radio.rs
index 5f60eaef..c2b6b017 100644
--- a/native/src/widget/radio.rs
+++ b/widget/src/radio.rs
@@ -1,25 +1,26 @@
//! Create choices using radio buttons.
-use crate::alignment;
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::text;
-use crate::touch;
-use crate::widget::{self, Row, Text, Tree};
-use crate::{
+use crate::core::alignment;
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::text;
+use crate::core::touch;
+use crate::core::widget::Tree;
+use crate::core::{
Alignment, Clipboard, Color, Element, Layout, Length, Pixels, Point,
Rectangle, Shell, Widget,
};
+use crate::{Row, Text};
pub use iced_style::radio::{Appearance, StyleSheet};
/// A circular button representing a choice.
///
/// # Example
-/// ```
+/// ```no_run
/// # type Radio<Message> =
-/// # iced_native::widget::Radio<Message, iced_native::renderer::Null>;
+/// # iced_widget::Radio<Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Choice {
@@ -41,7 +42,7 @@ pub use iced_style::radio::{Appearance, StyleSheet};
///
/// ![Radio buttons drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Radio<Message, Renderer>
+pub struct Radio<Message, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
@@ -144,7 +145,7 @@ impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message, Renderer>
where
Message: Clone,
Renderer: text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
fn width(&self) -> Length {
self.width
@@ -269,14 +270,14 @@ where
{
let label_layout = children.next().unwrap();
- widget::text::draw(
+ crate::text::draw(
renderer,
style,
label_layout,
&self.label,
self.text_size,
self.font,
- widget::text::Appearance {
+ crate::text::Appearance {
color: custom_style.text_color,
},
alignment::Horizontal::Left,
@@ -291,7 +292,7 @@ impl<'a, Message, Renderer> From<Radio<Message, Renderer>>
where
Message: 'a + Clone,
Renderer: 'a + text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
fn from(radio: Radio<Message, Renderer>) -> Element<'a, Message, Renderer> {
Element::new(radio)
diff --git a/native/src/widget/row.rs b/widget/src/row.rs
index 286c1c2d..3ce363f8 100644
--- a/native/src/widget/row.rs
+++ b/widget/src/row.rs
@@ -1,18 +1,18 @@
//! Distribute content horizontally.
-use crate::event::{self, Event};
-use crate::layout::{self, Layout};
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::widget::{Operation, Tree};
-use crate::{
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::widget::{Operation, Tree};
+use crate::core::{
Alignment, Clipboard, Element, Length, Padding, Pixels, Point, Rectangle,
Shell, Widget,
};
/// A container that distributes its contents horizontally.
#[allow(missing_debug_implementations)]
-pub struct Row<'a, Message, Renderer> {
+pub struct Row<'a, Message, Renderer = crate::Renderer> {
spacing: f32,
padding: Padding,
width: Length,
@@ -94,7 +94,7 @@ impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Row<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
{
fn children(&self) -> Vec<Tree> {
self.children.iter().map(Tree::new).collect()
@@ -245,7 +245,7 @@ impl<'a, Message, Renderer> From<Row<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: crate::Renderer + 'a,
+ Renderer: crate::core::Renderer + 'a,
{
fn from(row: Row<'a, Message, Renderer>) -> Self {
Self::new(row)
diff --git a/native/src/widget/rule.rs b/widget/src/rule.rs
index 1ab6a0d3..3749d7ce 100644
--- a/native/src/widget/rule.rs
+++ b/widget/src/rule.rs
@@ -1,18 +1,18 @@
//! Display a horizontal or vertical rule for dividing content.
-use crate::layout;
-use crate::renderer;
-use crate::widget::Tree;
-use crate::{
+use crate::core::layout;
+use crate::core::renderer;
+use crate::core::widget::Tree;
+use crate::core::{
Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget,
};
-pub use iced_style::rule::{Appearance, FillMode, StyleSheet};
+pub use crate::style::rule::{Appearance, FillMode, StyleSheet};
/// Display a horizontal or vertical rule for dividing content.
#[allow(missing_debug_implementations)]
-pub struct Rule<Renderer>
+pub struct Rule<Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
width: Length,
@@ -23,7 +23,7 @@ where
impl<Renderer> Rule<Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
/// Creates a horizontal [`Rule`] with the given height.
@@ -58,7 +58,7 @@ where
impl<Message, Renderer> Widget<Message, Renderer> for Rule<Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn width(&self) -> Length {
@@ -138,7 +138,7 @@ impl<'a, Message, Renderer> From<Rule<Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(rule: Rule<Renderer>) -> Element<'a, Message, Renderer> {
diff --git a/native/src/widget/scrollable.rs b/widget/src/scrollable.rs
index c1df8c39..5a7481f7 100644
--- a/native/src/widget/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -1,35 +1,29 @@
//! Navigate an endless amount of content with a scrollbar.
-use crate::event::{self, Event};
-use crate::keyboard;
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::touch;
-use crate::widget;
-use crate::widget::operation::{self, Operation};
-use crate::widget::tree::{self, Tree};
-use crate::{
- Background, Clipboard, Color, Command, Element, Layout, Length, Pixels,
- Point, Rectangle, Shell, Size, Vector, Widget,
+use crate::core::event::{self, Event};
+use crate::core::keyboard;
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::touch;
+use crate::core::widget;
+use crate::core::widget::operation::{self, Operation};
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
+ Background, Clipboard, Color, Element, Layout, Length, Pixels, Point,
+ Rectangle, Shell, Size, Vector, Widget,
};
+use crate::runtime::Command;
-pub use iced_style::scrollable::StyleSheet;
+pub use crate::style::scrollable::{Scrollbar, Scroller, StyleSheet};
pub use operation::scrollable::RelativeOffset;
-pub mod style {
- //! The styles of a [`Scrollable`].
- //!
- //! [`Scrollable`]: crate::widget::Scrollable
- pub use iced_style::scrollable::{Scrollbar, Scroller};
-}
-
/// A widget that can vertically display an infinite amount of content with a
/// scrollbar.
#[allow(missing_debug_implementations)]
-pub struct Scrollable<'a, Message, Renderer>
+pub struct Scrollable<'a, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
id: Option<Id>,
@@ -43,7 +37,7 @@ where
impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
/// Creates a new [`Scrollable`].
@@ -153,7 +147,7 @@ impl Properties {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Scrollable<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn tag(&self) -> tree::Tag {
@@ -345,7 +339,7 @@ impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(
@@ -763,7 +757,7 @@ pub fn draw<Renderer>(
style: &<Renderer::Theme as StyleSheet>::Style,
draw_content: impl FnOnce(&mut Renderer, Layout<'_>, Point, &Rectangle),
) where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
let bounds = layout.bounds();
@@ -809,8 +803,8 @@ pub fn draw<Renderer>(
let draw_scrollbar =
|renderer: &mut Renderer,
- style: style::Scrollbar,
- scrollbar: &Scrollbar| {
+ style: Scrollbar,
+ scrollbar: &internals::Scrollbar| {
//track
if style.background.is_some()
|| (style.border_color != Color::TRANSPARENT
@@ -1068,8 +1062,8 @@ impl State {
#[derive(Debug)]
/// State of both [`Scrollbar`]s.
struct Scrollbars {
- y: Option<Scrollbar>,
- x: Option<Scrollbar>,
+ y: Option<internals::Scrollbar>,
+ x: Option<internals::Scrollbar>,
}
impl Scrollbars {
@@ -1139,10 +1133,10 @@ impl Scrollbars {
height: scroller_height,
};
- Some(Scrollbar {
+ Some(internals::Scrollbar {
total_bounds: total_scrollbar_bounds,
bounds: scrollbar_bounds,
- scroller: Scroller {
+ scroller: internals::Scroller {
bounds: scroller_bounds,
},
})
@@ -1199,10 +1193,10 @@ impl Scrollbars {
height: scroller_width,
};
- Some(Scrollbar {
+ Some(internals::Scrollbar {
total_bounds: total_scrollbar_bounds,
bounds: scrollbar_bounds,
- scroller: Scroller {
+ scroller: internals::Scroller {
bounds: scroller_bounds,
},
})
@@ -1264,64 +1258,68 @@ impl Scrollbars {
}
}
-/// The scrollbar of a [`Scrollable`].
-#[derive(Debug, Copy, Clone)]
-struct Scrollbar {
- /// The total bounds of the [`Scrollbar`], including the scrollbar, the scroller,
- /// and the scrollbar margin.
- total_bounds: Rectangle,
+pub(super) mod internals {
+ use crate::core::{Point, Rectangle};
- /// The bounds of just the [`Scrollbar`].
- bounds: Rectangle,
+ /// The scrollbar of a [`Scrollable`].
+ #[derive(Debug, Copy, Clone)]
+ pub struct Scrollbar {
+ /// The total bounds of the [`Scrollbar`], including the scrollbar, the scroller,
+ /// and the scrollbar margin.
+ pub total_bounds: Rectangle,
- /// The state of this scrollbar's [`Scroller`].
- scroller: Scroller,
-}
+ /// The bounds of just the [`Scrollbar`].
+ pub bounds: Rectangle,
-impl Scrollbar {
- /// Returns whether the mouse is over the scrollbar or not.
- fn is_mouse_over(&self, cursor_position: Point) -> bool {
- self.total_bounds.contains(cursor_position)
+ /// The state of this scrollbar's [`Scroller`].
+ pub scroller: Scroller,
}
- /// Returns the y-axis scrolled percentage from the cursor position.
- fn scroll_percentage_y(
- &self,
- grabbed_at: f32,
- cursor_position: Point,
- ) -> f32 {
- if cursor_position.x < 0.0 && cursor_position.y < 0.0 {
- // cursor position is unavailable! Set to either end or beginning of scrollbar depending
- // on where the thumb currently is in the track
- (self.scroller.bounds.y / self.total_bounds.height).round()
- } else {
- (cursor_position.y
- - self.bounds.y
- - self.scroller.bounds.height * grabbed_at)
- / (self.bounds.height - self.scroller.bounds.height)
+ impl Scrollbar {
+ /// Returns whether the mouse is over the scrollbar or not.
+ pub fn is_mouse_over(&self, cursor_position: Point) -> bool {
+ self.total_bounds.contains(cursor_position)
}
- }
- /// Returns the x-axis scrolled percentage from the cursor position.
- fn scroll_percentage_x(
- &self,
- grabbed_at: f32,
- cursor_position: Point,
- ) -> f32 {
- if cursor_position.x < 0.0 && cursor_position.y < 0.0 {
- (self.scroller.bounds.x / self.total_bounds.width).round()
- } else {
- (cursor_position.x
- - self.bounds.x
- - self.scroller.bounds.width * grabbed_at)
- / (self.bounds.width - self.scroller.bounds.width)
+ /// Returns the y-axis scrolled percentage from the cursor position.
+ pub fn scroll_percentage_y(
+ &self,
+ grabbed_at: f32,
+ cursor_position: Point,
+ ) -> f32 {
+ if cursor_position.x < 0.0 && cursor_position.y < 0.0 {
+ // cursor position is unavailable! Set to either end or beginning of scrollbar depending
+ // on where the thumb currently is in the track
+ (self.scroller.bounds.y / self.total_bounds.height).round()
+ } else {
+ (cursor_position.y
+ - self.bounds.y
+ - self.scroller.bounds.height * grabbed_at)
+ / (self.bounds.height - self.scroller.bounds.height)
+ }
+ }
+
+ /// Returns the x-axis scrolled percentage from the cursor position.
+ pub fn scroll_percentage_x(
+ &self,
+ grabbed_at: f32,
+ cursor_position: Point,
+ ) -> f32 {
+ if cursor_position.x < 0.0 && cursor_position.y < 0.0 {
+ (self.scroller.bounds.x / self.total_bounds.width).round()
+ } else {
+ (cursor_position.x
+ - self.bounds.x
+ - self.scroller.bounds.width * grabbed_at)
+ / (self.bounds.width - self.scroller.bounds.width)
+ }
}
}
-}
-/// The handle of a [`Scrollbar`].
-#[derive(Debug, Clone, Copy)]
-struct Scroller {
- /// The bounds of the [`Scroller`].
- bounds: Rectangle,
+ /// The handle of a [`Scrollbar`].
+ #[derive(Debug, Clone, Copy)]
+ pub struct Scroller {
+ /// The bounds of the [`Scroller`].
+ pub bounds: Rectangle,
+ }
}
diff --git a/native/src/widget/slider.rs b/widget/src/slider.rs
index d3715b1c..e1153d2d 100644
--- a/native/src/widget/slider.rs
+++ b/widget/src/slider.rs
@@ -1,13 +1,13 @@
//! Display an interactive selector of a single value from a range of values.
//!
//! A [`Slider`] has some local [`State`].
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::touch;
-use crate::widget::tree::{self, Tree};
-use crate::{
+use crate::core::event::{self, Event};
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::touch;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
Background, Clipboard, Color, Element, Layout, Length, Pixels, Point,
Rectangle, Shell, Size, Widget,
};
@@ -25,11 +25,9 @@ pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet};
/// to 1 unit.
///
/// # Example
-/// ```
-/// # use iced_native::widget::slider;
-/// # use iced_native::renderer::Null;
-/// #
-/// # type Slider<'a, T, Message> = slider::Slider<'a, T, Message, Null>;
+/// ```no_run
+/// # type Slider<'a, T, Message> =
+/// # iced_widget::Slider<'a, Message, T, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// #[derive(Clone)]
/// pub enum Message {
@@ -43,9 +41,9 @@ pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet};
///
/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Slider<'a, T, Message, Renderer>
+pub struct Slider<'a, T, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
range: RangeInclusive<T>,
@@ -62,7 +60,7 @@ impl<'a, T, Message, Renderer> Slider<'a, T, Message, Renderer>
where
T: Copy + From<u8> + std::cmp::PartialOrd,
Message: Clone,
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
/// The default height of a [`Slider`].
@@ -148,7 +146,7 @@ impl<'a, T, Message, Renderer> Widget<Message, Renderer>
where
T: Copy + Into<f64> + num_traits::FromPrimitive,
Message: Clone,
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn tag(&self) -> tree::Tag {
@@ -245,7 +243,7 @@ impl<'a, T, Message, Renderer> From<Slider<'a, T, Message, Renderer>>
where
T: 'a + Copy + Into<f64> + num_traits::FromPrimitive,
Message: 'a + Clone,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(
@@ -354,7 +352,7 @@ pub fn draw<T, R>(
style: &<R::Theme as StyleSheet>::Style,
) where
T: Into<f64> + Copy,
- R: crate::Renderer,
+ R: crate::core::Renderer,
R::Theme: StyleSheet,
{
let bounds = layout.bounds();
diff --git a/native/src/widget/space.rs b/widget/src/space.rs
index a6fc977e..e1e09d5a 100644
--- a/native/src/widget/space.rs
+++ b/widget/src/space.rs
@@ -1,8 +1,9 @@
//! Distribute content vertically.
-use crate::layout;
-use crate::renderer;
-use crate::widget::Tree;
-use crate::{Element, Layout, Length, Point, Rectangle, Size, Widget};
+use crate::core;
+use crate::core::layout;
+use crate::core::renderer;
+use crate::core::widget::Tree;
+use crate::core::{Element, Layout, Length, Point, Rectangle, Size, Widget};
/// An amount of empty space.
///
@@ -41,7 +42,7 @@ impl Space {
impl<Message, Renderer> Widget<Message, Renderer> for Space
where
- Renderer: crate::Renderer,
+ Renderer: core::Renderer,
{
fn width(&self) -> Length {
self.width
@@ -76,7 +77,7 @@ where
impl<'a, Message, Renderer> From<Space> for Element<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: core::Renderer,
Message: 'a,
{
fn from(space: Space) -> Element<'a, Message, Renderer> {
diff --git a/native/src/widget/svg.rs b/widget/src/svg.rs
index f5ed0a6c..89017fcf 100644
--- a/native/src/widget/svg.rs
+++ b/widget/src/svg.rs
@@ -1,15 +1,15 @@
//! Display vector graphics in your application.
-use crate::layout;
-use crate::renderer;
-use crate::svg;
-use crate::widget::Tree;
-use crate::{
+use crate::core::layout;
+use crate::core::renderer;
+use crate::core::svg;
+use crate::core::widget::Tree;
+use crate::core::{
ContentFit, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
};
use std::path::PathBuf;
-pub use iced_style::svg::{Appearance, StyleSheet};
+pub use crate::style::svg::{Appearance, StyleSheet};
pub use svg::Handle;
/// A vector graphics image.
@@ -19,7 +19,7 @@ pub use svg::Handle;
/// [`Svg`] images can have a considerable rendering cost when resized,
/// specially when they are complex.
#[allow(missing_debug_implementations)]
-pub struct Svg<Renderer>
+pub struct Svg<Renderer = crate::Renderer>
where
Renderer: svg::Renderer,
Renderer::Theme: StyleSheet,
diff --git a/widget/src/text.rs b/widget/src/text.rs
new file mode 100644
index 00000000..04c31edc
--- /dev/null
+++ b/widget/src/text.rs
@@ -0,0 +1,4 @@
+pub use crate::core::widget::text::*;
+
+pub type Text<'a, Renderer = crate::Renderer> =
+ crate::core::widget::Text<'a, Renderer>;
diff --git a/native/src/widget/text_input.rs b/widget/src/text_input.rs
index 65a9bd3b..d066109a 100644
--- a/native/src/widget/text_input.rs
+++ b/widget/src/text_input.rs
@@ -11,31 +11,34 @@ pub use value::Value;
use editor::Editor;
-use crate::alignment;
-use crate::event::{self, Event};
-use crate::keyboard;
-use crate::layout;
-use crate::mouse::{self, click};
-use crate::renderer;
-use crate::text::{self, Text};
-use crate::time::{Duration, Instant};
-use crate::touch;
-use crate::widget;
-use crate::widget::operation::{self, Operation};
-use crate::widget::tree::{self, Tree};
-use crate::window;
-use crate::{
- Clipboard, Color, Command, Element, Layout, Length, Padding, Pixels, Point,
+use crate::core::alignment;
+use crate::core::event::{self, Event};
+use crate::core::keyboard;
+use crate::core::layout;
+use crate::core::mouse::{self, click};
+use crate::core::renderer;
+use crate::core::text::{self, Text};
+use crate::core::time::{Duration, Instant};
+use crate::core::touch;
+use crate::core::widget;
+use crate::core::widget::operation::{self, Operation};
+use crate::core::widget::tree::{self, Tree};
+use crate::core::window;
+use crate::core::{
+ Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point,
Rectangle, Shell, Size, Vector, Widget,
};
+use crate::runtime::Command;
pub use iced_style::text_input::{Appearance, StyleSheet};
/// A field that can be filled with text.
///
/// # Example
-/// ```
-/// # pub type TextInput<'a, Message> = iced_native::widget::TextInput<'a, Message, iced_native::renderer::Null>;
+/// ```no_run
+/// # pub type TextInput<'a, Message> =
+/// # iced_widget::TextInput<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
+/// #
/// #[derive(Debug, Clone)]
/// enum Message {
/// TextInputChanged(String),
@@ -52,7 +55,7 @@ pub use iced_style::text_input::{Appearance, StyleSheet};
/// ```
/// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct TextInput<'a, Message, Renderer>
+pub struct TextInput<'a, Message, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
@@ -1109,7 +1112,7 @@ impl operation::TextInput for State {
}
mod platform {
- use crate::keyboard;
+ use crate::core::keyboard;
pub fn is_jump_modifier_pressed(modifiers: keyboard::Modifiers) -> bool {
if cfg!(target_os = "macos") {
diff --git a/native/src/widget/text_input/cursor.rs b/widget/src/text_input/cursor.rs
index 4f3b159b..9680dfd7 100644
--- a/native/src/widget/text_input/cursor.rs
+++ b/widget/src/text_input/cursor.rs
@@ -1,5 +1,5 @@
//! Track the cursor of a text input.
-use crate::widget::text_input::Value;
+use crate::text_input::Value;
/// The cursor of a text input.
#[derive(Debug, Copy, Clone)]
diff --git a/native/src/widget/text_input/editor.rs b/widget/src/text_input/editor.rs
index d53fa8d9..f1fd641f 100644
--- a/native/src/widget/text_input/editor.rs
+++ b/widget/src/text_input/editor.rs
@@ -1,4 +1,4 @@
-use crate::widget::text_input::{Cursor, Value};
+use crate::text_input::{Cursor, Value};
pub struct Editor<'a> {
value: &'a mut Value,
diff --git a/native/src/widget/text_input/value.rs b/widget/src/text_input/value.rs
index cf4da562..cf4da562 100644
--- a/native/src/widget/text_input/value.rs
+++ b/widget/src/text_input/value.rs
diff --git a/native/src/widget/toggler.rs b/widget/src/toggler.rs
index d9c80ebe..713a9c30 100644
--- a/native/src/widget/toggler.rs
+++ b/widget/src/toggler.rs
@@ -1,24 +1,26 @@
//! Show toggle controls using togglers.
-use crate::alignment;
-use crate::event;
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::text;
-use crate::widget::{self, Row, Text, Tree};
-use crate::{
+use crate::core::alignment;
+use crate::core::event;
+use crate::core::layout;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::text;
+use crate::core::widget::Tree;
+use crate::core::{
Alignment, Clipboard, Element, Event, Layout, Length, Pixels, Point,
Rectangle, Shell, Widget,
};
+use crate::{Row, Text};
-pub use iced_style::toggler::{Appearance, StyleSheet};
+pub use crate::style::toggler::{Appearance, StyleSheet};
/// A toggler widget.
///
/// # Example
///
-/// ```
-/// # type Toggler<'a, Message> = iced_native::widget::Toggler<'a, Message, iced_native::renderer::Null>;
+/// ```no_run
+/// # type Toggler<'a, Message> =
+/// # iced_widget::Toggler<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// pub enum Message {
/// TogglerToggled(bool),
@@ -29,7 +31,7 @@ pub use iced_style::toggler::{Appearance, StyleSheet};
/// Toggler::new(String::from("Toggle me!"), is_toggled, |b| Message::TogglerToggled(b));
/// ```
#[allow(missing_debug_implementations)]
-pub struct Toggler<'a, Message, Renderer>
+pub struct Toggler<'a, Message, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
@@ -136,7 +138,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
for Toggler<'a, Message, Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
fn width(&self) -> Length {
self.width
@@ -237,7 +239,7 @@ where
if let Some(label) = &self.label {
let label_layout = children.next().unwrap();
- crate::widget::text::draw(
+ crate::text::draw(
renderer,
style,
label_layout,
@@ -314,7 +316,7 @@ impl<'a, Message, Renderer> From<Toggler<'a, Message, Renderer>>
where
Message: 'a,
Renderer: 'a + text::Renderer,
- Renderer::Theme: StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: StyleSheet + crate::text::StyleSheet,
{
fn from(
toggler: Toggler<'a, Message, Renderer>,
diff --git a/native/src/widget/tooltip.rs b/widget/src/tooltip.rs
index 2a24c055..084650d1 100644
--- a/native/src/widget/tooltip.rs
+++ b/widget/src/tooltip.rs
@@ -1,26 +1,27 @@
//! Display a widget over another.
-use crate::event;
-use crate::layout;
-use crate::mouse;
-use crate::renderer;
-use crate::text;
-use crate::widget;
-use crate::widget::container;
-use crate::widget::overlay;
-use crate::widget::{Text, Tree};
-use crate::{
- Clipboard, Element, Event, Layout, Length, Padding, Pixels, Point,
- Rectangle, Shell, Size, Vector, Widget,
+use crate::container;
+use crate::core;
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::text;
+use crate::core::widget::Tree;
+use crate::core::{
+ Clipboard, Element, Length, Padding, Pixels, Point, Rectangle, Shell, Size,
+ Vector, Widget,
};
+use crate::Text;
use std::borrow::Cow;
/// An element to display a widget over another.
#[allow(missing_debug_implementations)]
-pub struct Tooltip<'a, Message, Renderer: text::Renderer>
+pub struct Tooltip<'a, Message, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: container::StyleSheet + crate::text::StyleSheet,
{
content: Element<'a, Message, Renderer>,
tooltip: Text<'a, Renderer>,
@@ -34,7 +35,7 @@ where
impl<'a, Message, Renderer> Tooltip<'a, Message, Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: container::StyleSheet + crate::text::StyleSheet,
{
/// The default padding of a [`Tooltip`] drawn by this renderer.
const DEFAULT_PADDING: f32 = 5.0;
@@ -104,7 +105,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
for Tooltip<'a, Message, Renderer>
where
Renderer: text::Renderer,
- Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: container::StyleSheet + crate::text::StyleSheet,
{
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.content)]
@@ -239,7 +240,7 @@ impl<'a, Message, Renderer> From<Tooltip<'a, Message, Renderer>>
where
Message: 'a,
Renderer: 'a + text::Renderer,
- Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
+ Renderer::Theme: container::StyleSheet + crate::text::StyleSheet,
{
fn from(
tooltip: Tooltip<'a, Message, Renderer>,
@@ -285,7 +286,7 @@ pub fn draw<Renderer>(
&Rectangle,
),
) where
- Renderer: crate::Renderer,
+ Renderer: core::Renderer,
Renderer::Theme: container::StyleSheet,
{
use container::StyleSheet;
diff --git a/native/src/widget/vertical_slider.rs b/widget/src/vertical_slider.rs
index f1687e38..62dc997f 100644
--- a/native/src/widget/vertical_slider.rs
+++ b/widget/src/vertical_slider.rs
@@ -3,13 +3,18 @@
//! 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, Pixels, Point, Rectangle, Shell, Size, Widget,
+pub use crate::style::slider::{Appearance, Handle, HandleShape, StyleSheet};
+
+use crate::core;
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::touch;
+use crate::core::widget::tree::{self, Tree};
+use crate::core::{
+ Background, Clipboard, Color, Element, Length, Pixels, Point, Rectangle,
+ Shell, Size, Widget,
};
/// An vertical bar and a handle that selects a single value from a range of
@@ -21,11 +26,9 @@ use crate::{
/// 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>;
+/// ```no_run
+/// # type VerticalSlider<'a, T, Message> =
+/// # iced_widget::VerticalSlider<'a, T, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>;
/// #
/// #[derive(Clone)]
/// pub enum Message {
@@ -37,9 +40,9 @@ use crate::{
/// VerticalSlider::new(0.0..=100.0, value, Message::SliderChanged);
/// ```
#[allow(missing_debug_implementations)]
-pub struct VerticalSlider<'a, T, Message, Renderer>
+pub struct VerticalSlider<'a, T, Message, Renderer = crate::Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: core::Renderer,
Renderer::Theme: StyleSheet,
{
range: RangeInclusive<T>,
@@ -56,7 +59,7 @@ impl<'a, T, Message, Renderer> VerticalSlider<'a, T, Message, Renderer>
where
T: Copy + From<u8> + std::cmp::PartialOrd,
Message: Clone,
- Renderer: crate::Renderer,
+ Renderer: core::Renderer,
Renderer::Theme: StyleSheet,
{
/// The default width of a [`VerticalSlider`].
@@ -142,7 +145,7 @@ impl<'a, T, Message, Renderer> Widget<Message, Renderer>
where
T: Copy + Into<f64> + num_traits::FromPrimitive,
Message: Clone,
- Renderer: crate::Renderer,
+ Renderer: core::Renderer,
Renderer::Theme: StyleSheet,
{
fn tag(&self) -> tree::Tag {
@@ -239,7 +242,7 @@ impl<'a, T, Message, Renderer> From<VerticalSlider<'a, T, Message, Renderer>>
where
T: 'a + Copy + Into<f64> + num_traits::FromPrimitive,
Message: 'a + Clone,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(
@@ -349,7 +352,7 @@ pub fn draw<T, R>(
style: &<R::Theme as StyleSheet>::Style,
) where
T: Into<f64> + Copy,
- R: crate::Renderer,
+ R: core::Renderer,
R::Theme: StyleSheet,
{
let bounds = layout.bounds();
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index 60e464c6..1bf63804 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -13,7 +13,7 @@ categories = ["gui"]
[features]
trace = ["tracing", "tracing-core", "tracing-subscriber"]
chrome-trace = ["trace", "tracing-chrome"]
-debug = ["iced_native/debug"]
+debug = ["iced_runtime/debug"]
system = ["sysinfo"]
application = []
@@ -27,17 +27,17 @@ version = "0.27"
git = "https://github.com/iced-rs/winit.git"
rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c"
-[dependencies.iced_native]
+[dependencies.iced_runtime]
version = "0.9"
-path = "../native"
+path = "../runtime"
[dependencies.iced_graphics]
version = "0.7"
path = "../graphics"
-[dependencies.iced_futures]
-version = "0.6"
-path = "../futures"
+[dependencies.iced_style]
+version = "0.7"
+path = "../style"
[dependencies.tracing]
version = "0.1.37"
@@ -65,5 +65,5 @@ version = "0.3"
features = ["Document", "Window"]
[dependencies.sysinfo]
-version = "0.23"
+version = "0.28"
optional = true
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 889becad..48f94235 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -5,25 +5,25 @@ mod state;
pub use state::State;
-use crate::clipboard::{self, Clipboard};
use crate::conversion;
-use crate::mouse;
-use crate::renderer;
-use crate::widget::operation;
-use crate::{
- Command, Debug, Error, Event, Executor, Proxy, Runtime, Settings, Size,
- Subscription,
-};
-
-use iced_futures::futures;
-use iced_futures::futures::channel::mpsc;
-use iced_graphics::compositor;
-use iced_graphics::window;
-use iced_native::program::Program;
-use iced_native::time::Instant;
-use iced_native::user_interface::{self, UserInterface};
-
-pub use iced_native::application::{Appearance, StyleSheet};
+use crate::core;
+use crate::core::mouse;
+use crate::core::renderer;
+use crate::core::time::Instant;
+use crate::core::widget::operation;
+use crate::core::window;
+use crate::core::{Event, Size};
+use crate::futures::futures;
+use crate::futures::{Executor, Runtime, Subscription};
+use crate::graphics::compositor::{self, Compositor};
+use crate::runtime::clipboard;
+use crate::runtime::program::Program;
+use crate::runtime::user_interface::{self, UserInterface};
+use crate::runtime::{Command, Debug};
+use crate::style::application::{Appearance, StyleSheet};
+use crate::{Clipboard, Error, Proxy, Settings};
+
+use futures::channel::mpsc;
use std::mem::ManuallyDrop;
@@ -45,7 +45,7 @@ use tracing::{info_span, instrument::Instrument};
/// can be toggled by pressing `F12`.
pub trait Application: Program
where
- <Self::Renderer as crate::Renderer>::Theme: StyleSheet,
+ <Self::Renderer as core::Renderer>::Theme: StyleSheet,
{
/// The data needed to initialize your [`Application`].
type Flags;
@@ -67,12 +67,12 @@ where
fn title(&self) -> String;
/// Returns the current `Theme` of the [`Application`].
- fn theme(&self) -> <Self::Renderer as crate::Renderer>::Theme;
+ fn theme(&self) -> <Self::Renderer as core::Renderer>::Theme;
/// Returns the `Style` variation of the `Theme`.
fn style(
&self,
- ) -> <<Self::Renderer as crate::Renderer>::Theme as StyleSheet>::Style {
+ ) -> <<Self::Renderer as core::Renderer>::Theme as StyleSheet>::Style {
Default::default()
}
@@ -112,8 +112,8 @@ pub fn run<A, E, C>(
where
A: Application + 'static,
E: Executor + 'static,
- C: window::Compositor<Renderer = A::Renderer> + 'static,
- <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+ C: Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
{
use futures::task;
use futures::Future;
@@ -278,28 +278,25 @@ async fn run_instance<A, E, C>(
) where
A: Application + 'static,
E: Executor + 'static,
- C: window::Compositor<Renderer = A::Renderer> + 'static,
- <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+ C: Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
{
- use iced_futures::futures::stream::StreamExt;
+ use futures::stream::StreamExt;
use winit::event;
use winit::event_loop::ControlFlow;
- 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();
-
let physical_size = state.physical_size();
- compositor.configure_surface(
- &mut surface,
+ let mut clipboard = Clipboard::connect(&window);
+ let mut cache = user_interface::Cache::default();
+ let mut surface = compositor.create_surface(
+ &window,
physical_size.width,
physical_size.height,
);
+ let mut should_exit = false;
if should_be_visible {
window.set_visible(true);
@@ -319,7 +316,7 @@ async fn run_instance<A, E, C>(
&window,
|| compositor.fetch_information(),
);
- runtime.track(application.subscription());
+ runtime.track(application.subscription().into_recipes());
let mut user_interface = ManuallyDrop::new(build_user_interface(
&application,
@@ -363,8 +360,10 @@ async fn run_instance<A, E, C>(
debug.event_processing_finished();
- for event in events.drain(..).zip(statuses.into_iter()) {
- runtime.broadcast(event);
+ for (event, status) in
+ events.drain(..).zip(statuses.into_iter())
+ {
+ runtime.broadcast(event, status);
}
if !messages.is_empty()
@@ -414,7 +413,7 @@ async fn run_instance<A, E, C>(
// Then, we can use the `interface_state` here to decide if a redraw
// is needed right away, or simply wait until a specific time.
let redraw_event = Event::Window(
- crate::window::Event::RedrawRequested(Instant::now()),
+ window::Event::RedrawRequested(Instant::now()),
);
let (interface_state, _) = user_interface.update(
@@ -445,17 +444,14 @@ async fn run_instance<A, E, C>(
}
window.request_redraw();
- runtime
- .broadcast((redraw_event, crate::event::Status::Ignored));
+ runtime.broadcast(redraw_event, core::event::Status::Ignored);
let _ = control_sender.start_send(match interface_state {
user_interface::State::Updated {
redraw_request: Some(redraw_request),
} => match redraw_request {
- crate::window::RedrawRequest::NextFrame => {
- ControlFlow::Poll
- }
- crate::window::RedrawRequest::At(at) => {
+ window::RedrawRequest::NextFrame => ControlFlow::Poll,
+ window::RedrawRequest::At(at) => {
ControlFlow::WaitUntil(at)
}
},
@@ -467,9 +463,9 @@ async fn run_instance<A, E, C>(
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url),
)) => {
- use iced_native::event;
+ use crate::core::event;
- events.push(iced_native::Event::PlatformSpecific(
+ events.push(Event::PlatformSpecific(
event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
url,
)),
@@ -618,7 +614,7 @@ pub fn build_user_interface<'a, A: Application>(
debug: &mut Debug,
) -> UserInterface<'a, A::Message, A::Renderer>
where
- <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
{
#[cfg(feature = "trace")]
let view_span = info_span!("Application", "VIEW").entered();
@@ -659,7 +655,7 @@ pub fn update<A: Application, E: Executor>(
window: &winit::window::Window,
graphics_info: impl FnOnce() -> compositor::Information + Copy,
) where
- <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
{
for message in messages.drain(..) {
#[cfg(feature = "trace")]
@@ -691,7 +687,7 @@ pub fn update<A: Application, E: Executor>(
}
let subscription = application.subscription();
- runtime.track(subscription);
+ runtime.track(subscription.into_recipes());
}
/// Runs the actions of a [`Command`].
@@ -711,11 +707,11 @@ pub fn run_command<A, E>(
) where
A: Application,
E: Executor,
- <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
{
- use iced_native::command;
- use iced_native::system;
- use iced_native::window;
+ use crate::runtime::command;
+ use crate::runtime::system;
+ use crate::runtime::window;
for action in command.actions() {
match action {
@@ -762,7 +758,7 @@ pub fn run_command<A, E>(
window::Action::ChangeMode(mode) => {
window.set_visible(conversion::visible(mode));
window.set_fullscreen(conversion::fullscreen(
- window.primary_monitor(),
+ window.current_monitor(),
mode,
));
}
@@ -770,7 +766,7 @@ pub fn run_command<A, E>(
let mode = if window.is_visible().unwrap_or(true) {
conversion::mode(window.fullscreen())
} else {
- window::Mode::Hidden
+ core::window::Mode::Hidden
};
proxy
@@ -822,7 +818,7 @@ pub fn run_command<A, E>(
},
command::Action::Widget(action) => {
let mut current_cache = std::mem::take(cache);
- let mut current_operation = Some(action.into_operation());
+ let mut current_operation = Some(action);
let mut user_interface = build_user_interface(
application,
@@ -852,7 +848,7 @@ pub fn run_command<A, E>(
*cache = current_cache;
}
command::Action::LoadFont { bytes, tagger } => {
- use crate::text::Renderer;
+ use crate::core::text::Renderer;
// TODO: Error handling (?)
renderer.load_font(bytes);
diff --git a/winit/src/application/state.rs b/winit/src/application/state.rs
index 8d6a1df1..c37ccca6 100644
--- a/winit/src/application/state.rs
+++ b/winit/src/application/state.rs
@@ -1,6 +1,10 @@
use crate::application::{self, StyleSheet as _};
use crate::conversion;
-use crate::{Application, Color, Debug, Point, Size, Viewport};
+use crate::core;
+use crate::core::{Color, Point, Size};
+use crate::graphics::Viewport;
+use crate::runtime::Debug;
+use crate::Application;
use std::marker::PhantomData;
use winit::event::{Touch, WindowEvent};
@@ -10,7 +14,7 @@ use winit::window::Window;
#[allow(missing_debug_implementations)]
pub struct State<A: Application>
where
- <A::Renderer as crate::Renderer>::Theme: application::StyleSheet,
+ <A::Renderer as core::Renderer>::Theme: application::StyleSheet,
{
title: String,
scale_factor: f64,
@@ -18,14 +22,14 @@ where
viewport_version: usize,
cursor_position: winit::dpi::PhysicalPosition<f64>,
modifiers: winit::event::ModifiersState,
- theme: <A::Renderer as crate::Renderer>::Theme,
+ theme: <A::Renderer as core::Renderer>::Theme,
appearance: application::Appearance,
application: PhantomData<A>,
}
impl<A: Application> State<A>
where
- <A::Renderer as crate::Renderer>::Theme: application::StyleSheet,
+ <A::Renderer as core::Renderer>::Theme: application::StyleSheet,
{
/// Creates a new [`State`] for the provided [`Application`] and window.
pub fn new(application: &A, window: &Window) -> Self {
@@ -98,7 +102,7 @@ where
}
/// Returns the current theme of the [`State`].
- pub fn theme(&self) -> &<A::Renderer as crate::Renderer>::Theme {
+ pub fn theme(&self) -> &<A::Renderer as core::Renderer>::Theme {
&self.theme
}
diff --git a/winit/src/clipboard.rs b/winit/src/clipboard.rs
index c1fd8813..7271441d 100644
--- a/winit/src/clipboard.rs
+++ b/winit/src/clipboard.rs
@@ -1,7 +1,4 @@
//! Access the clipboard.
-pub use iced_native::clipboard::Action;
-
-use crate::command::{self, Command};
/// A buffer for short-term storage and transfer within and between
/// applications.
@@ -56,7 +53,7 @@ impl Clipboard {
}
}
-impl iced_native::Clipboard for Clipboard {
+impl crate::core::Clipboard for Clipboard {
fn read(&self) -> Option<String> {
self.read()
}
@@ -65,15 +62,3 @@ impl iced_native::Clipboard for Clipboard {
self.write(contents)
}
}
-
-/// Read the current contents of the clipboard.
-pub fn read<Message>(
- f: impl Fn(Option<String>) -> Message + 'static,
-) -> Command<Message> {
- Command::single(command::Action::Clipboard(Action::Read(Box::new(f))))
-}
-
-/// Write the given contents to the clipboard.
-pub fn write<Message>(contents: String) -> Command<Message> {
- Command::single(command::Action::Clipboard(Action::Write(contents)))
-}
diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs
index 1b2ead36..0759ad9d 100644
--- a/winit/src/conversion.rs
+++ b/winit/src/conversion.rs
@@ -2,11 +2,12 @@
//!
//! [`winit`]: https://github.com/rust-windowing/winit
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native
-use crate::keyboard;
-use crate::mouse;
-use crate::touch;
-use crate::window;
-use crate::{Event, Point, Position};
+use crate::core::keyboard;
+use crate::core::mouse;
+use crate::core::touch;
+use crate::core::window;
+use crate::core::{Event, Point};
+use crate::Position;
/// Converts a winit window event into an iced event.
pub fn window_event(
diff --git a/winit/src/error.rs b/winit/src/error.rs
index eaeafd51..7687fb17 100644
--- a/winit/src/error.rs
+++ b/winit/src/error.rs
@@ -1,4 +1,5 @@
-use iced_futures::futures;
+use crate::futures::futures;
+use crate::graphics;
/// An error that occurred while running an application.
#[derive(Debug, thiserror::Error)]
@@ -13,10 +14,10 @@ pub enum Error {
/// The application graphics context could not be created.
#[error("the application graphics context could not be created")]
- GraphicsCreationFailed(iced_graphics::Error),
+ GraphicsCreationFailed(graphics::Error),
}
-impl From<iced_graphics::Error> for Error {
+impl From<graphics::Error> for Error {
fn from(error: iced_graphics::Error) -> Error {
Error::GraphicsCreationFailed(error)
}
diff --git a/winit/src/lib.rs b/winit/src/lib.rs
index 3a33e174..5cde510a 100644
--- a/winit/src/lib.rs
+++ b/winit/src/lib.rs
@@ -30,9 +30,11 @@
#![forbid(rust_2018_idioms, unsafe_code)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_cfg))]
-
-#[doc(no_inline)]
-pub use iced_native::*;
+pub use iced_graphics as graphics;
+pub use iced_runtime as runtime;
+pub use iced_runtime::core;
+pub use iced_runtime::futures;
+pub use iced_style as style;
pub use winit;
#[cfg(feature = "application")]
@@ -40,7 +42,6 @@ pub mod application;
pub mod clipboard;
pub mod conversion;
pub mod settings;
-pub mod window;
#[cfg(feature = "system")]
pub mod system;
diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs
index 7b9074d7..1d6c48bb 100644
--- a/winit/src/proxy.rs
+++ b/winit/src/proxy.rs
@@ -1,4 +1,4 @@
-use iced_native::futures::{
+use crate::futures::futures::{
channel::mpsc,
task::{Context, Poll},
Sink,
diff --git a/winit/src/system.rs b/winit/src/system.rs
index 619086b8..145a4d92 100644
--- a/winit/src/system.rs
+++ b/winit/src/system.rs
@@ -1,8 +1,7 @@
//! Access the native system.
-use crate::command::{self, Command};
-pub use iced_native::system::*;
-
-use iced_graphics::compositor;
+use crate::graphics::compositor;
+use crate::runtime::command::{self, Command};
+use crate::runtime::system::{Action, Information};
/// Query for available system information.
pub fn fetch_information<Message>(
@@ -16,11 +15,11 @@ pub fn fetch_information<Message>(
pub(crate) fn information(
graphics_info: compositor::Information,
) -> Information {
- use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
+ use sysinfo::{CpuExt, ProcessExt, System, SystemExt};
let mut system = System::new_all();
system.refresh_all();
- let cpu = system.global_processor_info();
+ let cpu = system.global_cpu_info();
let memory_used = sysinfo::get_current_pid()
.and_then(|pid| system.process(pid).ok_or("Process not found"))