summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test.yml2
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7343
-rw-r--r--Cargo.toml19
-rw-r--r--core/Cargo.toml1
-rw-r--r--core/src/keyboard/key.rs6
-rw-r--r--core/src/lib.rs2
-rw-r--r--core/src/renderer.rs19
-rw-r--r--core/src/settings.rs (renamed from src/settings.rs)13
-rw-r--r--core/src/theme.rs47
-rw-r--r--core/src/theme/palette.rs155
-rw-r--r--core/src/time.rs25
-rw-r--r--core/src/widget/operation.rs199
-rw-r--r--core/src/widget/operation/focusable.rs35
-rw-r--r--core/src/widget/operation/scrollable.rs6
-rw-r--r--core/src/widget/operation/text_input.rs28
-rw-r--r--core/src/widget/text.rs10
-rw-r--r--core/src/window.rs4
-rw-r--r--core/src/window/direction.rs27
-rw-r--r--core/src/window/event.rs4
-rw-r--r--core/src/window/screenshot.rs (renamed from runtime/src/window/screenshot.rs)2
-rw-r--r--core/src/window/settings.rs9
-rw-r--r--examples/arc/src/main.rs4
-rw-r--r--examples/bezier_tool/src/main.rs7
-rw-r--r--examples/clock/src/main.rs53
-rw-r--r--examples/color_palette/src/main.rs1
-rw-r--r--examples/counter/Cargo.toml5
-rw-r--r--examples/counter/src/main.rs28
-rw-r--r--examples/editor/src/main.rs6
-rw-r--r--examples/game_of_life/src/main.rs7
-rw-r--r--examples/geometry/src/main.rs8
-rw-r--r--examples/gradient/src/main.rs10
-rw-r--r--examples/integration/src/controls.rs11
-rw-r--r--examples/layout/src/main.rs11
-rw-r--r--examples/loading_spinners/Cargo.toml1
-rw-r--r--examples/loading_spinners/src/easing.rs15
-rw-r--r--examples/multi_window/src/main.rs4
-rw-r--r--examples/pane_grid/src/main.rs13
-rw-r--r--examples/progress_bar/src/main.rs42
-rw-r--r--examples/screenshot/src/main.rs10
-rw-r--r--examples/scrollable/Cargo.toml2
-rw-r--r--examples/scrollable/src/main.rs5
-rw-r--r--examples/stopwatch/src/main.rs6
-rw-r--r--examples/styling/src/main.rs54
-rw-r--r--examples/svg/src/main.rs12
-rw-r--r--examples/the_matrix/src/main.rs6
-rw-r--r--examples/toast/src/main.rs7
-rw-r--r--examples/todos/Cargo.toml5
-rw-r--r--examples/todos/snapshots/creates_a_new_task.sha2561
-rw-r--r--examples/todos/src/main.rs61
-rw-r--r--examples/tour/Cargo.toml2
-rw-r--r--examples/tour/src/main.rs22
-rw-r--r--examples/visible_bounds/Cargo.toml2
-rw-r--r--examples/visible_bounds/src/main.rs10
-rw-r--r--examples/websocket/Cargo.toml1
-rw-r--r--examples/websocket/src/main.rs5
-rw-r--r--futures/src/backend/native/tokio.rs60
-rw-r--r--futures/src/subscription.rs26
-rw-r--r--graphics/Cargo.toml1
-rw-r--r--graphics/src/text.rs25
-rw-r--r--graphics/src/text/paragraph.rs4
-rw-r--r--highlighter/Cargo.toml1
-rw-r--r--highlighter/src/lib.rs10
-rw-r--r--renderer/src/lib.rs28
-rw-r--r--runtime/src/window.rs81
-rw-r--r--src/application.rs9
-rw-r--r--src/daemon.rs9
-rw-r--r--src/lib.rs13
-rw-r--r--src/program.rs29
-rw-r--r--src/time.rs2
-rw-r--r--src/window/icon.rs4
-rw-r--r--test/Cargo.toml24
-rw-r--r--test/src/lib.rs637
-rw-r--r--test/src/selector.rs29
-rw-r--r--tiny_skia/src/lib.rs25
-rw-r--r--wgpu/Cargo.toml1
-rw-r--r--widget/Cargo.toml1
-rw-r--r--widget/src/button.rs15
-rw-r--r--widget/src/checkbox.rs10
-rw-r--r--widget/src/container.rs2
-rw-r--r--widget/src/helpers.rs168
-rw-r--r--widget/src/markdown.rs11
-rw-r--r--widget/src/progress_bar.rs94
-rw-r--r--widget/src/scrollable.rs50
-rw-r--r--widget/src/text_editor.rs4
-rw-r--r--widget/src/text_input.rs15
-rw-r--r--widget/src/vertical_slider.rs39
-rw-r--r--winit/src/conversion.rs32
-rw-r--r--winit/src/program.rs393
-rw-r--r--winit/src/program/state.rs27
-rw-r--r--winit/src/program/window_manager.rs15
-rw-r--r--winit/src/settings.rs11
92 files changed, 9661 insertions, 638 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ea941509..517bd23f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
- rust: [stable, beta, "1.80"]
+ rust: [stable, beta, "1.81"]
steps:
- uses: hecrj/setup-rust-action@v2
with:
diff --git a/.gitignore b/.gitignore
index f05ec438..9d164436 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
target/
pkg/
**/*.rs.bk
-Cargo.lock
dist/
traces/
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 00000000..9b64572e
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7343 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ab_glyph"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "aliasable"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
+
+[[package]]
+name = "aligned-vec"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
+
+[[package]]
+name = "android-activity"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
+dependencies = [
+ "android-properties",
+ "bitflags 2.8.0",
+ "cc",
+ "cesu8",
+ "jni",
+ "jni-sys",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys 0.6.0+11769913",
+ "num_enum",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anyhow"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
+
+[[package]]
+name = "arc"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "arg_enum_proc_macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "as-raw-xcb-connection"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
+
+[[package]]
+name = "ash"
+version = "0.38.0+1.3.281"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "ashpd"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3"
+dependencies = [
+ "async-fs 2.1.2",
+ "async-net 2.0.0",
+ "enumflags2",
+ "futures-channel",
+ "futures-util",
+ "rand",
+ "serde",
+ "serde_repr",
+ "url",
+ "zbus",
+]
+
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener 5.4.0",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
+dependencies = [
+ "concurrent-queue",
+ "event-listener 2.5.3",
+ "futures-core",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand 2.3.0",
+ "futures-lite 2.6.0",
+ "slab",
+]
+
+[[package]]
+name = "async-fs"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
+dependencies = [
+ "async-lock 2.8.0",
+ "autocfg",
+ "blocking",
+ "futures-lite 1.13.0",
+]
+
+[[package]]
+name = "async-fs"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
+dependencies = [
+ "async-lock 3.4.0",
+ "blocking",
+ "futures-lite 2.6.0",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
+dependencies = [
+ "async-channel 2.3.1",
+ "async-executor",
+ "async-io 2.4.0",
+ "async-lock 3.4.0",
+ "blocking",
+ "futures-lite 2.6.0",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
+dependencies = [
+ "async-lock 2.8.0",
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-lite 1.13.0",
+ "log",
+ "parking",
+ "polling 2.8.0",
+ "rustix 0.37.28",
+ "slab",
+ "socket2 0.4.10",
+ "waker-fn",
+]
+
+[[package]]
+name = "async-io"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
+dependencies = [
+ "async-lock 3.4.0",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite 2.6.0",
+ "parking",
+ "polling 3.7.4",
+ "rustix 0.38.43",
+ "slab",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
+dependencies = [
+ "event-listener 2.5.3",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+dependencies = [
+ "event-listener 5.4.0",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-net"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f"
+dependencies = [
+ "async-io 1.13.0",
+ "blocking",
+ "futures-lite 1.13.0",
+]
+
+[[package]]
+name = "async-net"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
+dependencies = [
+ "async-io 2.4.0",
+ "blocking",
+ "futures-lite 2.6.0",
+]
+
+[[package]]
+name = "async-process"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88"
+dependencies = [
+ "async-io 1.13.0",
+ "async-lock 2.8.0",
+ "async-signal",
+ "blocking",
+ "cfg-if",
+ "event-listener 3.1.0",
+ "futures-lite 1.13.0",
+ "rustix 0.38.43",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "async-process"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
+dependencies = [
+ "async-channel 2.3.1",
+ "async-io 2.4.0",
+ "async-lock 3.4.0",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener 5.4.0",
+ "futures-lite 2.6.0",
+ "rustix 0.38.43",
+ "tracing",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
+dependencies = [
+ "async-io 2.4.0",
+ "async-lock 3.4.0",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix 0.38.43",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "async-std"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615"
+dependencies = [
+ "async-channel 1.9.0",
+ "async-global-executor",
+ "async-io 2.4.0",
+ "async-lock 3.4.0",
+ "async-process 2.3.0",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite 2.6.0",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
+[[package]]
+name = "async-trait"
+version = "0.1.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-tungstenite"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cca750b12e02c389c1694d35c16539f88b8bbaa5945934fdc1b41a776688589"
+dependencies = [
+ "futures-io",
+ "futures-util",
+ "log",
+ "pin-project-lite",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.25.0",
+ "tungstenite",
+ "webpki-roots",
+]
+
+[[package]]
+name = "atk-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "av1-grain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
+dependencies = [
+ "anyhow",
+ "arrayvec",
+ "log",
+ "nom",
+ "num-rational",
+ "v_frame",
+]
+
+[[package]]
+name = "avif-serialize"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bezier_tool"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+
+[[package]]
+name = "bitstream-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2",
+]
+
+[[package]]
+name = "blocking"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
+dependencies = [
+ "async-channel 2.3.1",
+ "async-task",
+ "futures-io",
+ "futures-lite 2.6.0",
+ "piper",
+]
+
+[[package]]
+name = "built"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b"
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "by_address"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
+
+[[package]]
+name = "bytemuck"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
+[[package]]
+name = "bytes"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+
+[[package]]
+name = "bytesize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "calloop"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
+dependencies = [
+ "bitflags 2.8.0",
+ "log",
+ "polling 3.7.4",
+ "rustix 0.38.43",
+ "slab",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
+dependencies = [
+ "calloop",
+ "rustix 0.38.43",
+ "wayland-backend",
+ "wayland-client",
+]
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cc"
+version = "1.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
+dependencies = [
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "changelog"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "log",
+ "reqwest",
+ "serde",
+ "thiserror 1.0.69",
+ "tokio",
+ "tracing-subscriber",
+ "webbrowser",
+]
+
+[[package]]
+name = "checkbox"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "chrono"
+version = "0.4.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
+dependencies = [
+ "anstyle",
+ "clap_lex",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "clipboard-win"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
+dependencies = [
+ "error-code",
+]
+
+[[package]]
+name = "clipboard_macos"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f"
+dependencies = [
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "clipboard_wayland"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8"
+dependencies = [
+ "smithay-clipboard",
+]
+
+[[package]]
+name = "clipboard_x11"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c"
+dependencies = [
+ "thiserror 1.0.69",
+ "x11rb",
+]
+
+[[package]]
+name = "clock"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "iced",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_palette"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "palette",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "combo_box"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "console_log"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
+dependencies = [
+ "log",
+ "web-sys",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "core-graphics-types 0.1.3",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation 0.10.0",
+ "core-graphics-types 0.2.0",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation 0.10.0",
+ "libc",
+]
+
+[[package]]
+name = "cosmic-text"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2"
+dependencies = [
+ "bitflags 2.8.0",
+ "fontdb 0.16.2",
+ "log",
+ "rangemap",
+ "rayon",
+ "rustc-hash 1.1.0",
+ "rustybuzz",
+ "self_cell",
+ "swash",
+ "sys-locale",
+ "ttf-parser 0.21.1",
+ "unicode-bidi",
+ "unicode-linebreak",
+ "unicode-script",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "counter"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "iced_test",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "criterion"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
+dependencies = [
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "is-terminal",
+ "itertools 0.10.5",
+ "num-traits",
+ "once_cell",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
+dependencies = [
+ "cast",
+ "itertools 0.10.5",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ctor-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b"
+
+[[package]]
+name = "cursor-icon"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
+
+[[package]]
+name = "custom_quad"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "custom_shader"
+version = "0.1.0"
+dependencies = [
+ "bytemuck",
+ "glam",
+ "iced",
+ "image",
+ "rand",
+]
+
+[[package]]
+name = "custom_widget"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "dark-light"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18e1a09f280e29a8b00bc7e81eca5ac87dca0575639c9422a5fa25a07bb884b8"
+dependencies = [
+ "ashpd",
+ "async-std",
+ "objc2",
+ "objc2-foundation",
+ "web-sys",
+ "winreg",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
+
+[[package]]
+name = "data-url"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "directories-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "download_progress"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "reqwest",
+]
+
+[[package]]
+name = "dpi"
+version = "0.1.1"
+source = "git+https://github.com/iced-rs/winit.git?rev=11414b6aa45699f038114e61b4ddf5102b2d3b4b#11414b6aa45699f038114e61b4ddf5102b2d3b4b"
+
+[[package]]
+name = "drm"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1"
+dependencies = [
+ "bitflags 2.8.0",
+ "bytemuck",
+ "drm-ffi",
+ "drm-fourcc",
+ "rustix 0.38.43",
+]
+
+[[package]]
+name = "drm-ffi"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53"
+dependencies = [
+ "drm-sys",
+ "rustix 0.38.43",
+]
+
+[[package]]
+name = "drm-fourcc"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
+
+[[package]]
+name = "drm-sys"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986"
+dependencies = [
+ "libc",
+ "linux-raw-sys 0.6.5",
+]
+
+[[package]]
+name = "editor"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "rfd",
+ "tokio",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "endi"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
+
+[[package]]
+name = "enumflags2"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "error-code"
+version = "3.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
+
+[[package]]
+name = "etagere"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e2f1e3be19fb10f549be8c1bf013e8675b4066c445e36eb76d2ebb2f54ee495"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "euclid"
+version = "0.22.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
+[[package]]
+name = "event-listener"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
+dependencies = [
+ "event-listener 5.4.0",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "events"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "exit"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "exr"
+version = "1.73.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fast-srgb8"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "ferris"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "float-cmp"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+
+[[package]]
+name = "float_next_after"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foldhash"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
+
+[[package]]
+name = "font-types"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "fontconfig-parser"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7"
+dependencies = [
+ "roxmltree",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3"
+dependencies = [
+ "fontconfig-parser",
+ "log",
+ "memmap2",
+ "slotmap",
+ "tinyvec",
+ "ttf-parser 0.20.0",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770"
+dependencies = [
+ "fontconfig-parser",
+ "log",
+ "memmap2",
+ "slotmap",
+ "tinyvec",
+ "ttf-parser 0.21.1",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-lite"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
+dependencies = [
+ "fastrand 1.9.0",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
+[[package]]
+name = "futures-lite"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
+dependencies = [
+ "fastrand 2.3.0",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "game_of_life"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "itertools 0.12.1",
+ "rustc-hash 2.1.0",
+ "tokio",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gdk-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "geometry"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "gio-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+ "winapi",
+]
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glam"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "glow"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "glyphon"
+version = "0.5.0"
+source = "git+https://github.com/hecrj/glyphon.git?rev=09712a70df7431e9a3b1ac1bbd4fb634096cb3b4#09712a70df7431e9a3b1ac1bbd4fb634096cb3b4"
+dependencies = [
+ "cosmic-text",
+ "etagere",
+ "lru",
+ "rustc-hash 2.1.0",
+ "wgpu",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+dependencies = [
+ "bitflags 2.8.0",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
+dependencies = [
+ "log",
+ "presser",
+ "thiserror 1.0.69",
+ "windows 0.58.0",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca"
+dependencies = [
+ "bitflags 2.8.0",
+ "gpu-descriptor-types",
+ "hashbrown",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "gradient"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "gtk-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414"
+dependencies = [
+ "atk-sys",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "guillotiere"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.12",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "headers"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270"
+dependencies = [
+ "base64 0.21.7",
+ "bytes",
+ "headers-core",
+ "http 0.2.12",
+ "httpdate",
+ "mime",
+ "sha1",
+]
+
+[[package]]
+name = "headers-core"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
+dependencies = [
+ "http 0.2.12",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http 0.2.12",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http 1.2.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2 0.5.8",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
+dependencies = [
+ "futures-util",
+ "http 1.2.0",
+ "hyper 1.5.2",
+ "hyper-util",
+ "rustls 0.23.21",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tower-service",
+ "webpki-roots",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "hyper 1.5.2",
+ "pin-project-lite",
+ "socket2 0.5.8",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core 0.52.0",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "iced"
+version = "0.14.0-dev"
+dependencies = [
+ "criterion",
+ "iced_core",
+ "iced_futures",
+ "iced_highlighter",
+ "iced_renderer",
+ "iced_wgpu",
+ "iced_widget",
+ "iced_winit",
+ "image",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "iced_core"
+version = "0.14.0-dev"
+dependencies = [
+ "approx",
+ "bitflags 2.8.0",
+ "bytes",
+ "dark-light",
+ "glam",
+ "log",
+ "num-traits",
+ "palette",
+ "rustc-hash 2.1.0",
+ "smol_str",
+ "thiserror 1.0.69",
+ "web-time",
+]
+
+[[package]]
+name = "iced_futures"
+version = "0.14.0-dev"
+dependencies = [
+ "async-std",
+ "futures",
+ "iced_core",
+ "log",
+ "rustc-hash 2.1.0",
+ "smol",
+ "tokio",
+ "wasm-bindgen-futures",
+ "wasm-timer",
+]
+
+[[package]]
+name = "iced_graphics"
+version = "0.14.0-dev"
+dependencies = [
+ "bitflags 2.8.0",
+ "bytemuck",
+ "cosmic-text",
+ "half",
+ "iced_core",
+ "iced_futures",
+ "image",
+ "kamadak-exif",
+ "log",
+ "lyon_path",
+ "raw-window-handle 0.6.2",
+ "rustc-hash 2.1.0",
+ "thiserror 1.0.69",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "iced_highlighter"
+version = "0.14.0-dev"
+dependencies = [
+ "iced_core",
+ "syntect",
+]
+
+[[package]]
+name = "iced_renderer"
+version = "0.14.0-dev"
+dependencies = [
+ "iced_graphics",
+ "iced_tiny_skia",
+ "iced_wgpu",
+ "log",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "iced_runtime"
+version = "0.14.0-dev"
+dependencies = [
+ "bytes",
+ "iced_core",
+ "iced_futures",
+ "raw-window-handle 0.6.2",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "iced_test"
+version = "0.14.0-dev"
+dependencies = [
+ "iced_renderer",
+ "iced_runtime",
+ "png",
+ "sha2",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "iced_tiny_skia"
+version = "0.14.0-dev"
+dependencies = [
+ "bytemuck",
+ "cosmic-text",
+ "iced_graphics",
+ "kurbo 0.10.4",
+ "log",
+ "resvg",
+ "rustc-hash 2.1.0",
+ "softbuffer",
+ "tiny-skia",
+]
+
+[[package]]
+name = "iced_wgpu"
+version = "0.14.0-dev"
+dependencies = [
+ "bitflags 2.8.0",
+ "bytemuck",
+ "futures",
+ "glam",
+ "glyphon",
+ "guillotiere",
+ "iced_graphics",
+ "log",
+ "lyon",
+ "resvg",
+ "rustc-hash 2.1.0",
+ "thiserror 1.0.69",
+ "wgpu",
+]
+
+[[package]]
+name = "iced_widget"
+version = "0.14.0-dev"
+dependencies = [
+ "iced_highlighter",
+ "iced_renderer",
+ "iced_runtime",
+ "num-traits",
+ "ouroboros",
+ "pulldown-cmark",
+ "qrcode",
+ "rustc-hash 2.1.0",
+ "thiserror 1.0.69",
+ "unicode-segmentation",
+ "url",
+]
+
+[[package]]
+name = "iced_winit"
+version = "0.14.0-dev"
+dependencies = [
+ "iced_futures",
+ "iced_graphics",
+ "iced_runtime",
+ "log",
+ "rustc-hash 2.1.0",
+ "sysinfo",
+ "thiserror 1.0.69",
+ "tracing",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winapi",
+ "window_clipboard",
+ "winit",
+]
+
+[[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "image"
+version = "0.25.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "color_quant",
+ "exr",
+ "gif",
+ "image-webp",
+ "num-traits",
+ "png",
+ "qoi",
+ "ravif",
+ "rayon",
+ "rgb",
+ "tiff",
+ "zune-core",
+ "zune-jpeg",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f"
+dependencies = [
+ "byteorder-lite",
+ "quick-error",
+]
+
+[[package]]
+name = "imagesize"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
+
+[[package]]
+name = "imgref"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
+
+[[package]]
+name = "indexmap"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "integration"
+version = "0.1.0"
+dependencies = [
+ "console_error_panic_hook",
+ "console_log",
+ "iced_wgpu",
+ "iced_widget",
+ "iced_winit",
+ "tracing-subscriber",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "interpolate_name"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "is-docker"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+dependencies = [
+ "hermit-abi 0.4.0",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "is-wsl"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
+dependencies = [
+ "is-docker",
+ "once_cell",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kamadak-exif"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077"
+dependencies = [
+ "mutate_once",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
+dependencies = [
+ "libc",
+ "libloading",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "kurbo"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440"
+dependencies = [
+ "arrayvec",
+ "smallvec",
+]
+
+[[package]]
+name = "kurbo"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
+dependencies = [
+ "arrayvec",
+ "smallvec",
+]
+
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "layout"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "lazy"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa"
+dependencies = [
+ "arbitrary",
+ "cc",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.8.0",
+ "libc",
+ "redox_syscall 0.5.8",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7"
+
+[[package]]
+name = "litemap"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
+name = "loading_spinners"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "lyon_algorithms",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
+dependencies = [
+ "value-bag",
+]
+
+[[package]]
+name = "loop9"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
+dependencies = [
+ "imgref",
+]
+
+[[package]]
+name = "loupe"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "lru"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
+
+[[package]]
+name = "lyon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f"
+dependencies = [
+ "lyon_algorithms",
+ "lyon_tessellation",
+]
+
+[[package]]
+name = "lyon_algorithms"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08"
+dependencies = [
+ "lyon_path",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_geom"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570"
+dependencies = [
+ "arrayvec",
+ "euclid",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_path"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e0b8aec2f58586f6eef237985b9a9b7cb3a3aff4417c575075cf95bf925252e"
+dependencies = [
+ "lyon_geom",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_tessellation"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c"
+dependencies = [
+ "float_next_after",
+ "lyon_path",
+ "num-traits",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "markdown"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "open",
+]
+
+[[package]]
+name = "maybe-rayon"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+dependencies = [
+ "cfg-if",
+ "rayon",
+]
+
+[[package]]
+name = "maybe_parallel_iterator"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5069b219d51d2ba2d9388623bd7eead5d4e7974bc8ff3ec9edbe36b09c0ef477"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memmap2"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metal"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
+dependencies = [
+ "bitflags 2.8.0",
+ "block",
+ "core-graphics-types 0.1.3",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "modal"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "multer"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
+dependencies = [
+ "bytes",
+ "encoding_rs",
+ "futures-util",
+ "http 0.2.12",
+ "httparse",
+ "log",
+ "memchr",
+ "mime",
+ "spin",
+ "version_check",
+]
+
+[[package]]
+name = "multi_window"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "multitouch"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "tracing-subscriber",
+ "voronator",
+]
+
+[[package]]
+name = "mutate_once"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
+
+[[package]]
+name = "naga"
+version = "23.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f"
+dependencies = [
+ "arrayvec",
+ "bit-set",
+ "bitflags 2.8.0",
+ "cfg_aliases 0.1.1",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "rustc-hash 1.1.0",
+ "spirv",
+ "termcolor",
+ "thiserror 1.0.69",
+ "unicode-xid",
+]
+
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.8.0",
+ "jni-sys",
+ "log",
+ "ndk-sys 0.6.0+11769913",
+ "num_enum",
+ "raw-window-handle 0.6.2",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.5.0+25.2.9519653"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags 2.8.0",
+ "cfg-if",
+ "cfg_aliases 0.2.1",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "noop_proc_macro"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
+dependencies = [
+ "block",
+ "objc",
+ "objc_id",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "libc",
+ "objc2",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-foundation",
+ "objc2-quartz-core",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "objc2",
+ "objc2-core-location",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-contacts"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-foundation",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-contacts",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "dispatch",
+ "libc",
+ "objc2",
+]
+
+[[package]]
+name = "objc2-link-presentation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "objc2",
+ "objc2-foundation",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-symbols"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "objc2",
+ "objc2-cloud-kit",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-core-location",
+ "objc2-foundation",
+ "objc2-link-presentation",
+ "objc2-quartz-core",
+ "objc2-symbols",
+ "objc2-uniform-type-identifiers",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-uniform-type-identifiers"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "objc2",
+ "objc2-core-location",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc_id"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+dependencies = [
+ "objc",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
+[[package]]
+name = "onig"
+version = "6.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+ "once_cell",
+ "onig_sys",
+]
+
+[[package]]
+name = "onig_sys"
+version = "69.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "oorandom"
+version = "11.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
+
+[[package]]
+name = "open"
+version = "5.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95"
+dependencies = [
+ "is-wsl",
+ "libc",
+ "pathdiff",
+]
+
+[[package]]
+name = "orbclient"
+version = "0.3.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43"
+dependencies = [
+ "libredox",
+]
+
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "ouroboros"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
+dependencies = [
+ "aliasable",
+ "ouroboros_macro",
+ "static_assertions",
+]
+
+[[package]]
+name = "ouroboros_macro"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
+dependencies = [
+ "ttf-parser 0.25.1",
+]
+
+[[package]]
+name = "palette"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6"
+dependencies = [
+ "approx",
+ "fast-srgb8",
+ "palette_derive",
+ "phf",
+]
+
+[[package]]
+name = "palette_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30"
+dependencies = [
+ "by_address",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pane_grid"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.6",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.10",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.5.8",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pick_list"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "pico-args"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
+
+[[package]]
+name = "pin-project"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "piper"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
+dependencies = [
+ "atomic-waker",
+ "fastrand 2.3.0",
+ "futures-io",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+
+[[package]]
+name = "plist"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
+dependencies = [
+ "base64 0.22.1",
+ "indexmap",
+ "quick-xml 0.32.0",
+ "serde",
+ "time",
+]
+
+[[package]]
+name = "plotters"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "pokedex"
+version = "0.1.0"
+dependencies = [
+ "getrandom",
+ "iced",
+ "rand",
+ "reqwest",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "polling"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
+dependencies = [
+ "autocfg",
+ "bitflags 1.3.2",
+ "cfg-if",
+ "concurrent-queue",
+ "libc",
+ "log",
+ "pin-project-lite",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "polling"
+version = "3.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi 0.4.0",
+ "pin-project-lite",
+ "rustix 0.38.43",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "presser"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+ "yansi",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
+dependencies = [
+ "profiling-procmacros",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "progress_bar"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "pulldown-cmark"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
+dependencies = [
+ "bitflags 2.8.0",
+ "getopts",
+ "memchr",
+ "pulldown-cmark-escape",
+ "unicase",
+]
+
+[[package]]
+name = "pulldown-cmark-escape"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "qr_code"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "qrcode"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "166f136dfdb199f98186f3649cf7a0536534a61417a1a30221b492b4fb60ce3f"
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "quick-xml"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
+dependencies = [
+ "bytes",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.1.0",
+ "rustls 0.23.21",
+ "socket2 0.5.8",
+ "thiserror 2.0.11",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
+dependencies = [
+ "bytes",
+ "getrandom",
+ "rand",
+ "ring",
+ "rustc-hash 2.1.0",
+ "rustls 0.23.21",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.11",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904"
+dependencies = [
+ "cfg_aliases 0.2.1",
+ "libc",
+ "once_cell",
+ "socket2 0.5.8",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
+
+[[package]]
+name = "rangemap"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684"
+
+[[package]]
+name = "rav1e"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
+dependencies = [
+ "arbitrary",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "av1-grain",
+ "bitstream-io",
+ "built",
+ "cfg-if",
+ "interpolate_name",
+ "itertools 0.12.1",
+ "libc",
+ "libfuzzer-sys",
+ "log",
+ "maybe-rayon",
+ "new_debug_unreachable",
+ "noop_proc_macro",
+ "num-derive",
+ "num-traits",
+ "once_cell",
+ "paste",
+ "profiling",
+ "rand",
+ "rand_chacha",
+ "simd_helpers",
+ "system-deps",
+ "thiserror 1.0.69",
+ "v_frame",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ravif"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6"
+dependencies = [
+ "avif-serialize",
+ "imgref",
+ "loop9",
+ "quick-error",
+ "rav1e",
+ "rayon",
+ "rgb",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "read-fonts"
+version = "0.22.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f"
+dependencies = [
+ "bytemuck",
+ "font-types",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "renderdoc-sys"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+
+[[package]]
+name = "reqwest"
+version = "0.12.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.5.2",
+ "hyper-rustls",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.21",
+ "rustls-pemfile",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tokio-util",
+ "tower",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "webpki-roots",
+ "windows-registry",
+]
+
+[[package]]
+name = "resvg"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051"
+dependencies = [
+ "gif",
+ "jpeg-decoder",
+ "log",
+ "pico-args",
+ "rgb",
+ "svgtypes",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "rfd"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0d8ab342bcc5436e04d3a4c1e09e17d74958bfaddf8d5fad6f85607df0f994f"
+dependencies = [
+ "block",
+ "dispatch",
+ "glib-sys",
+ "gobject-sys",
+ "gtk-sys",
+ "js-sys",
+ "log",
+ "objc",
+ "objc-foundation",
+ "objc_id",
+ "raw-window-handle 0.5.2",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
+
+[[package]]
+name = "rustix"
+version = "0.37.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.3.8",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
+dependencies = [
+ "bitflags 2.8.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
+dependencies = [
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
+dependencies = [
+ "web-time",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+
+[[package]]
+name = "rustybuzz"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
+dependencies = [
+ "bitflags 2.8.0",
+ "bytemuck",
+ "libm",
+ "smallvec",
+ "ttf-parser 0.21.1",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-properties",
+ "unicode-script",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "screenshot"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "image",
+ "tokio",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "scrollable"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "sctk-adwaita"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
+dependencies = [
+ "ab_glyph",
+ "log",
+ "memmap2",
+ "smithay-client-toolkit",
+ "tiny-skia",
+]
+
+[[package]]
+name = "self_cell"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
+
+[[package]]
+name = "serde"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "sierpinski_triangle"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "rand",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "simd_helpers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+dependencies = [
+ "quote",
+]
+
+[[package]]
+name = "simplecss"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
+[[package]]
+name = "skrifa"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe"
+dependencies = [
+ "bytemuck",
+ "read-fonts",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "slider"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
+dependencies = [
+ "bitflags 2.8.0",
+ "calloop",
+ "calloop-wayland-source",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 0.38.43",
+ "thiserror 1.0.69",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smithay-clipboard"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846"
+dependencies = [
+ "libc",
+ "smithay-client-toolkit",
+ "wayland-backend",
+]
+
+[[package]]
+name = "smol"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1"
+dependencies = [
+ "async-channel 1.9.0",
+ "async-executor",
+ "async-fs 1.6.0",
+ "async-io 1.13.0",
+ "async-lock 2.8.0",
+ "async-net 1.8.0",
+ "async-process 1.8.1",
+ "blocking",
+ "futures-lite 1.13.0",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "softbuffer"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08"
+dependencies = [
+ "as-raw-xcb-connection",
+ "bytemuck",
+ "cfg_aliases 0.2.1",
+ "core-graphics 0.24.0",
+ "drm",
+ "fastrand 2.3.0",
+ "foreign-types",
+ "js-sys",
+ "log",
+ "memmap2",
+ "objc2",
+ "objc2-foundation",
+ "objc2-quartz-core",
+ "raw-window-handle 0.6.2",
+ "redox_syscall 0.5.8",
+ "rustix 0.38.43",
+ "tiny-xlib",
+ "wasm-bindgen",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-sys",
+ "web-sys",
+ "windows-sys 0.59.0",
+ "x11rb",
+]
+
+[[package]]
+name = "solar_system"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "rand",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spirv"
+version = "0.3.0+sdk-1.3.268.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "stopwatch"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "strict-num"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
+dependencies = [
+ "float-cmp",
+]
+
+[[package]]
+name = "styling"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "svg"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "svg_fmt"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa"
+
+[[package]]
+name = "svgtypes"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
+dependencies = [
+ "kurbo 0.11.1",
+ "siphasher",
+]
+
+[[package]]
+name = "swash"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2"
+dependencies = [
+ "skrifa",
+ "yazi",
+ "zeno",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syntect"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
+dependencies = [
+ "bincode",
+ "bitflags 1.3.2",
+ "flate2",
+ "fnv",
+ "once_cell",
+ "onig",
+ "plist",
+ "regex-syntax",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "thiserror 1.0.69",
+ "walkdir",
+ "yaml-rust",
+]
+
+[[package]]
+name = "sys-locale"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.30.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "rayon",
+ "windows 0.52.0",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck 0.5.0",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "system_information"
+version = "0.1.0"
+dependencies = [
+ "bytesize",
+ "iced",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "tempfile"
+version = "3.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
+dependencies = [
+ "cfg-if",
+ "fastrand 2.3.0",
+ "getrandom",
+ "once_cell",
+ "rustix 0.38.43",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "the_matrix"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "rand",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
+dependencies = [
+ "thiserror-impl 2.0.11",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
+name = "time"
+version = "0.3.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "log",
+ "png",
+ "tiny-skia-path",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
+[[package]]
+name = "tiny-xlib"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e"
+dependencies = [
+ "as-raw-xcb-connection",
+ "ctor-lite",
+ "libloading",
+ "pkg-config",
+ "tracing",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "toast"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "todos"
+version = "0.1.0"
+dependencies = [
+ "async-std",
+ "directories-next",
+ "iced",
+ "iced_test",
+ "serde",
+ "serde_json",
+ "tracing-subscriber",
+ "uuid",
+ "wasm-timer",
+ "web-sys",
+]
+
+[[package]]
+name = "tokio"
+version = "1.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2 0.5.8",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
+dependencies = [
+ "rustls 0.22.4",
+ "rustls-pki-types",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
+dependencies = [
+ "rustls 0.23.21",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-tungstenite"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
+dependencies = [
+ "futures-util",
+ "log",
+ "tokio",
+ "tungstenite",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "tooltip"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "tour"
+version = "0.1.0"
+dependencies = [
+ "console_error_panic_hook",
+ "console_log",
+ "iced",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "ttf-parser"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
+
+[[package]]
+name = "ttf-parser"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
+
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+
+[[package]]
+name = "tungstenite"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
+dependencies = [
+ "byteorder",
+ "bytes",
+ "data-encoding",
+ "http 1.2.0",
+ "httparse",
+ "log",
+ "rand",
+ "rustls 0.22.4",
+ "rustls-pki-types",
+ "sha1",
+ "thiserror 1.0.69",
+ "url",
+ "utf-8",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "uds_windows"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "winapi",
+]
+
+[[package]]
+name = "unicase"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-properties"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
+
+[[package]]
+name = "unicode-script"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-vo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "url_handler"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "usvg"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
+dependencies = [
+ "base64 0.22.1",
+ "data-url",
+ "flate2",
+ "fontdb 0.18.0",
+ "imagesize",
+ "kurbo 0.11.1",
+ "log",
+ "pico-args",
+ "roxmltree",
+ "rustybuzz",
+ "simplecss",
+ "siphasher",
+ "strict-num",
+ "svgtypes",
+ "tiny-skia-path",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "uuid"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
+dependencies = [
+ "getrandom",
+ "rand",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "v_frame"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
+dependencies = [
+ "aligned-vec",
+ "num-traits",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "value-bag"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
+
+[[package]]
+name = "vectorial_text"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "visible_bounds"
+version = "0.1.0"
+dependencies = [
+ "iced",
+]
+
+[[package]]
+name = "voronator"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07d8e6fe4b9b3b443f2e4ada327cf4b08ab5cdd27a42cd5c9f38dd29092c10ee"
+dependencies = [
+ "maybe_parallel_iterator",
+]
+
+[[package]]
+name = "waker-fn"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "warp"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "headers",
+ "http 0.2.12",
+ "hyper 0.14.32",
+ "log",
+ "mime",
+ "mime_guess",
+ "multer",
+ "percent-encoding",
+ "pin-project",
+ "scoped-tls",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "tokio",
+ "tokio-tungstenite",
+ "tokio-util",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasm-streams"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-timer"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
+dependencies = [
+ "futures",
+ "js-sys",
+ "parking_lot 0.11.2",
+ "pin-utils",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 0.38.43",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280"
+dependencies = [
+ "bitflags 2.8.0",
+ "rustix 0.38.43",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-csd-frame"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
+dependencies = [
+ "bitflags 2.8.0",
+ "cursor-icon",
+ "wayland-backend",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c"
+dependencies = [
+ "rustix 0.38.43",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e"
+dependencies = [
+ "bitflags 2.8.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd"
+dependencies = [
+ "bitflags 2.8.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022"
+dependencies = [
+ "bitflags 2.8.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3"
+dependencies = [
+ "proc-macro2",
+ "quick-xml 0.36.2",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09"
+dependencies = [
+ "dlib",
+ "log",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webbrowser"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea9fe1ebb156110ff855242c1101df158b822487e4957b0556d9ffce9db0f535"
+dependencies = [
+ "block2",
+ "core-foundation 0.10.0",
+ "home",
+ "jni",
+ "log",
+ "ndk-context",
+ "objc2",
+ "objc2-foundation",
+ "url",
+ "web-sys",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "websocket"
+version = "1.0.0"
+dependencies = [
+ "async-tungstenite",
+ "iced",
+ "tokio",
+ "warp",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
+[[package]]
+name = "wgpu"
+version = "23.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a"
+dependencies = [
+ "arrayvec",
+ "cfg_aliases 0.1.1",
+ "document-features",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot 0.12.3",
+ "profiling",
+ "raw-window-handle 0.6.2",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "23.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags 2.8.0",
+ "cfg_aliases 0.1.1",
+ "document-features",
+ "indexmap",
+ "log",
+ "naga",
+ "once_cell",
+ "parking_lot 0.12.3",
+ "profiling",
+ "raw-window-handle 0.6.2",
+ "rustc-hash 1.1.0",
+ "smallvec",
+ "thiserror 1.0.69",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "23.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags 2.8.0",
+ "block",
+ "bytemuck",
+ "cfg_aliases 0.1.1",
+ "core-graphics-types 0.1.3",
+ "glow",
+ "glutin_wgl_sys",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading",
+ "log",
+ "metal",
+ "naga",
+ "ndk-sys 0.5.0+25.2.9519653",
+ "objc",
+ "once_cell",
+ "parking_lot 0.12.3",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle 0.6.2",
+ "renderdoc-sys",
+ "rustc-hash 1.1.0",
+ "smallvec",
+ "thiserror 1.0.69",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "23.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068"
+dependencies = [
+ "bitflags 2.8.0",
+ "js-sys",
+ "web-sys",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "window_clipboard"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6d692d46038c433f9daee7ad8757e002a4248c20b0a3fbc991d99521d3bcb6d"
+dependencies = [
+ "clipboard-win",
+ "clipboard_macos",
+ "clipboard_wayland",
+ "clipboard_x11",
+ "raw-window-handle 0.6.2",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core 0.52.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
+dependencies = [
+ "windows-core 0.58.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-result",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winit"
+version = "0.30.8"
+source = "git+https://github.com/iced-rs/winit.git?rev=11414b6aa45699f038114e61b4ddf5102b2d3b4b#11414b6aa45699f038114e61b4ddf5102b2d3b4b"
+dependencies = [
+ "ahash",
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.8.0",
+ "block2",
+ "bytemuck",
+ "calloop",
+ "cfg_aliases 0.2.1",
+ "concurrent-queue",
+ "core-foundation 0.9.4",
+ "core-graphics 0.23.2",
+ "cursor-icon",
+ "dpi",
+ "js-sys",
+ "libc",
+ "memmap2",
+ "ndk",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "objc2-ui-kit",
+ "orbclient",
+ "percent-encoding",
+ "pin-project",
+ "raw-window-handle 0.6.2",
+ "redox_syscall 0.4.1",
+ "rustix 0.38.43",
+ "sctk-adwaita",
+ "smithay-client-toolkit",
+ "smol_str",
+ "tracing",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.52.0",
+ "x11-dl",
+ "x11rb",
+ "xkbcommon-dl",
+]
+
+[[package]]
+name = "winnow"
+version = "0.6.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winreg"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "write16"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
+dependencies = [
+ "as-raw-xcb-connection",
+ "gethostname",
+ "libc",
+ "libloading",
+ "once_cell",
+ "rustix 0.38.43",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
+
+[[package]]
+name = "xcursor"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61"
+
+[[package]]
+name = "xdg-home"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.8.0",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
+
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
+[[package]]
+name = "yazi"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
+
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zbus"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "192a0d989036cd60a1e91a54c9851fb9ad5bd96125d41803eed79d2e2ef74bd7"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-fs 2.1.2",
+ "async-io 2.4.0",
+ "async-lock 3.4.0",
+ "async-process 2.3.0",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener 5.4.0",
+ "futures-core",
+ "futures-util",
+ "hex",
+ "nix",
+ "ordered-stream",
+ "serde",
+ "serde_repr",
+ "static_assertions",
+ "tracing",
+ "uds_windows",
+ "windows-sys 0.59.0",
+ "winnow",
+ "xdg-home",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3685b5c81fce630efc3e143a4ded235b107f1b1cdf186c3f115529e5e5ae4265"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zbus_names",
+ "zvariant",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8"
+dependencies = [
+ "serde",
+ "static_assertions",
+ "winnow",
+ "zvariant",
+]
+
+[[package]]
+name = "zeno"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
+dependencies = [
+ "zune-core",
+]
+
+[[package]]
+name = "zvariant"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "static_assertions",
+ "url",
+ "winnow",
+ "zvariant_derive",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "static_assertions",
+ "syn",
+ "winnow",
+]
diff --git a/Cargo.toml b/Cargo.toml
index fc38a89d..958af111 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@ all-features = true
maintenance = { status = "actively-developed" }
[features]
-default = ["wgpu", "tiny-skia", "fira-sans", "auto-detect-theme"]
+default = ["wgpu", "tiny-skia", "auto-detect-theme"]
# Enables the `wgpu` GPU-accelerated renderer backend
wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"]
# Enables the `tiny-skia` software renderer backend
@@ -53,13 +53,13 @@ smol = ["iced_futures/smol"]
system = ["iced_winit/system"]
# Enables broken "sRGB linear" blending to reproduce color management of the Web
web-colors = ["iced_renderer/web-colors"]
-# Enables the WebGL backend, replacing WebGPU
+# Enables the WebGL backend
webgl = ["iced_renderer/webgl"]
# Enables syntax highligthing
highlighter = ["iced_highlighter", "iced_widget/highlighter"]
# Enables the advanced module
advanced = ["iced_core/advanced", "iced_widget/advanced"]
-# Embeds Fira Sans as the default font on Wasm builds
+# Embeds Fira Sans into the final application; useful for testing and Wasm builds
fira-sans = ["iced_renderer/fira-sans"]
# Auto-detects light/dark mode for the built-in theme
auto-detect-theme = ["iced_core/auto-detect-theme"]
@@ -111,6 +111,7 @@ members = [
"highlighter",
"renderer",
"runtime",
+ "test",
"tiny_skia",
"wgpu",
"widget",
@@ -127,7 +128,7 @@ repository = "https://github.com/iced-rs/iced"
homepage = "https://iced.rs"
categories = ["gui"]
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
-rust-version = "1.80"
+rust-version = "1.81"
[workspace.dependencies]
iced = { version = "0.14.0-dev", path = "." }
@@ -137,6 +138,7 @@ iced_graphics = { version = "0.14.0-dev", path = "graphics" }
iced_highlighter = { version = "0.14.0-dev", path = "highlighter" }
iced_renderer = { version = "0.14.0-dev", path = "renderer" }
iced_runtime = { version = "0.14.0-dev", path = "runtime" }
+iced_test = { version = "0.14.0-dev", path = "test" }
iced_tiny_skia = { version = "0.14.0-dev", path = "tiny_skia" }
iced_wgpu = { version = "0.14.0-dev", path = "wgpu" }
iced_widget = { version = "0.14.0-dev", path = "widget" }
@@ -147,27 +149,28 @@ bitflags = "2.0"
bytemuck = { version = "1.0", features = ["derive"] }
bytes = "1.6"
cosmic-text = "0.12"
-dark-light = "1.0"
+dark-light = "2.0"
futures = "0.3"
glam = "0.25"
glyphon = { git = "https://github.com/hecrj/glyphon.git", rev = "09712a70df7431e9a3b1ac1bbd4fb634096cb3b4" }
guillotiere = "0.6"
half = "2.2"
-image = { version = "0.24", default-features = false }
+image = { version = "0.25", default-features = false }
kamadak-exif = "0.5"
kurbo = "0.10"
log = "0.4"
lyon = "1.0"
lyon_path = "1.0"
num-traits = "0.2"
-once_cell = "1.0"
ouroboros = "0.18"
palette = "0.7"
+png = "0.17"
pulldown-cmark = "0.11"
qrcode = { version = "0.13", default-features = false }
raw-window-handle = "0.6"
resvg = "0.42"
rustc-hash = "2.0"
+sha2 = "0.10"
smol = "1.0"
smol_str = "0.2"
softbuffer = "0.4"
@@ -186,7 +189,7 @@ web-time = "1.1"
wgpu = "23.0"
winapi = "0.3"
window_clipboard = "0.4.1"
-winit = { git = "https://github.com/iced-rs/winit.git", rev = "254d6b3420ce4e674f516f7a2bd440665e05484d" }
+winit = { git = "https://github.com/iced-rs/winit.git", rev = "11414b6aa45699f038114e61b4ddf5102b2d3b4b" }
[workspace.lints.rust]
rust_2018_idioms = { level = "forbid", priority = -1 }
diff --git a/core/Cargo.toml b/core/Cargo.toml
index a1228909..a3bc6745 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -23,7 +23,6 @@ bytes.workspace = true
glam.workspace = true
log.workspace = true
num-traits.workspace = true
-once_cell.workspace = true
palette.workspace = true
rustc-hash.workspace = true
smol_str.workspace = true
diff --git a/core/src/keyboard/key.rs b/core/src/keyboard/key.rs
index 69a91902..47169d9a 100644
--- a/core/src/keyboard/key.rs
+++ b/core/src/keyboard/key.rs
@@ -32,6 +32,12 @@ impl Key {
}
}
+impl From<Named> for Key {
+ fn from(named: Named) -> Self {
+ Self::Named(named)
+ }
+}
+
/// A named key.
///
/// This is mostly the `NamedKey` type found in [`winit`].
diff --git a/core/src/lib.rs b/core/src/lib.rs
index df599f45..645f7a90 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -40,6 +40,7 @@ mod pixels;
mod point;
mod rectangle;
mod rotation;
+mod settings;
mod shadow;
mod shell;
mod size;
@@ -67,6 +68,7 @@ pub use point::Point;
pub use rectangle::Rectangle;
pub use renderer::Renderer;
pub use rotation::Rotation;
+pub use settings::Settings;
pub use shadow::Shadow;
pub use shell::Shell;
pub use size::Size;
diff --git a/core/src/renderer.rs b/core/src/renderer.rs
index 6684517f..68e070e8 100644
--- a/core/src/renderer.rs
+++ b/core/src/renderer.rs
@@ -3,7 +3,8 @@
mod null;
use crate::{
- Background, Border, Color, Rectangle, Shadow, Size, Transformation, Vector,
+ Background, Border, Color, Font, Pixels, Rectangle, Shadow, Size,
+ Transformation, Vector,
};
/// A component that can be used by widgets to draw themselves on a screen.
@@ -100,3 +101,19 @@ impl Default for Style {
}
}
}
+
+/// A headless renderer is a renderer that can render offscreen without
+/// a window nor a compositor.
+pub trait Headless {
+ /// Creates a new [`Headless`] renderer;
+ fn new(default_font: Font, default_text_size: Pixels) -> Self;
+
+ /// Draws offscreen into a screenshot, returning a collection of
+ /// bytes representing the rendered pixels in RGBA order.
+ fn screenshot(
+ &mut self,
+ size: Size<u32>,
+ scale_factor: f32,
+ background_color: Color,
+ ) -> Vec<u8>;
+}
diff --git a/src/settings.rs b/core/src/settings.rs
index ebac7a86..3189c8d1 100644
--- a/src/settings.rs
+++ b/core/src/settings.rs
@@ -29,11 +29,9 @@ pub struct Settings {
/// primitives.
///
/// Enabling it can produce a smoother result in some widgets, like the
- /// [`Canvas`], at a performance cost.
+ /// `canvas` widget, at a performance cost.
///
/// By default, it is disabled.
- ///
- /// [`Canvas`]: crate::widget::Canvas
pub antialiasing: bool,
}
@@ -48,12 +46,3 @@ impl Default for Settings {
}
}
}
-
-impl From<Settings> for iced_winit::Settings {
- fn from(settings: Settings) -> iced_winit::Settings {
- iced_winit::Settings {
- id: settings.id,
- fonts: settings.fonts,
- }
- }
-}
diff --git a/core/src/theme.rs b/core/src/theme.rs
index 6b2c04da..cc5b77df 100644
--- a/core/src/theme.rs
+++ b/core/src/theme.rs
@@ -3,6 +3,8 @@ pub mod palette;
pub use palette::Palette;
+use crate::Color;
+
use std::fmt;
use std::sync::Arc;
@@ -164,15 +166,18 @@ impl Default for Theme {
fn default() -> Self {
#[cfg(feature = "auto-detect-theme")]
{
- use once_cell::sync::Lazy;
+ use std::sync::LazyLock;
- static DEFAULT: Lazy<Theme> =
- Lazy::new(|| match dark_light::detect() {
+ static DEFAULT: LazyLock<Theme> = LazyLock::new(|| {
+ match dark_light::detect()
+ .unwrap_or(dark_light::Mode::Unspecified)
+ {
dark_light::Mode::Dark => Theme::Dark,
- dark_light::Mode::Light | dark_light::Mode::Default => {
+ dark_light::Mode::Light | dark_light::Mode::Unspecified => {
Theme::Light
}
- });
+ }
+ });
DEFAULT.clone()
}
@@ -246,3 +251,35 @@ impl fmt::Display for Custom {
write!(f, "{}", self.name)
}
}
+
+/// The base style of a [`Theme`].
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Style {
+ /// The background [`Color`] of the application.
+ pub background_color: Color,
+
+ /// The default text [`Color`] of the application.
+ pub text_color: Color,
+}
+
+/// The default blank style of a [`Theme`].
+pub trait Base {
+ /// Returns the default base [`Style`] of a [`Theme`].
+ fn base(&self) -> Style;
+}
+
+impl Base for Theme {
+ fn base(&self) -> Style {
+ default(self)
+ }
+}
+
+/// The default [`Style`] of a built-in [`Theme`].
+pub fn default(theme: &Theme) -> Style {
+ let palette = theme.extended_palette();
+
+ Style {
+ background_color: palette.background.base.color,
+ text_color: palette.background.base.text,
+ }
+}
diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs
index e0ff397a..696c01d0 100644
--- a/core/src/theme/palette.rs
+++ b/core/src/theme/palette.rs
@@ -1,11 +1,12 @@
//! Define the colors of a theme.
use crate::{color, Color};
-use once_cell::sync::Lazy;
use palette::color_difference::Wcag21RelativeContrast;
use palette::rgb::Rgb;
use palette::{FromColor, Hsl, Mix};
+use std::sync::LazyLock;
+
/// A color palette.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Palette {
@@ -17,6 +18,8 @@ pub struct Palette {
pub primary: Color,
/// The success [`Color`] of the [`Palette`].
pub success: Color,
+ /// The warning [`Color`] of the [`Palette`].
+ pub warning: Color,
/// The danger [`Color`] of the [`Palette`].
pub danger: Color,
}
@@ -36,6 +39,11 @@ impl Palette {
0x66 as f32 / 255.0,
0x4F as f32 / 255.0,
),
+ warning: Color::from_rgb(
+ 0xFF as f32 / 255.0,
+ 0xC1 as f32 / 255.0,
+ 0x4E as f32 / 255.0,
+ ),
danger: Color::from_rgb(
0xC3 as f32 / 255.0,
0x42 as f32 / 255.0,
@@ -61,6 +69,11 @@ impl Palette {
0x66 as f32 / 255.0,
0x4F as f32 / 255.0,
),
+ warning: Color::from_rgb(
+ 0xFF as f32 / 255.0,
+ 0xC1 as f32 / 255.0,
+ 0x4E as f32 / 255.0,
+ ),
danger: Color::from_rgb(
0xC3 as f32 / 255.0,
0x42 as f32 / 255.0,
@@ -76,6 +89,7 @@ impl Palette {
text: color!(0xf8f8f2), // FOREGROUND
primary: color!(0xbd93f9), // PURPLE
success: color!(0x50fa7b), // GREEN
+ warning: color!(0xf1fa8c), // YELLOW
danger: color!(0xff5555), // RED
};
@@ -87,6 +101,7 @@ impl Palette {
text: color!(0xeceff4), // nord6
primary: color!(0x8fbcbb), // nord7
success: color!(0xa3be8c), // nord14
+ warning: color!(0xebcb8b), // nord13
danger: color!(0xbf616a), // nord11
};
@@ -98,6 +113,7 @@ impl Palette {
text: color!(0x657b83), // base00
primary: color!(0x2aa198), // cyan
success: color!(0x859900), // green
+ warning: color!(0xb58900), // yellow
danger: color!(0xdc322f), // red
};
@@ -109,6 +125,7 @@ impl Palette {
text: color!(0x839496), // base0
primary: color!(0x2aa198), // cyan
success: color!(0x859900), // green
+ warning: color!(0xb58900), // yellow
danger: color!(0xdc322f), // red
};
@@ -120,6 +137,7 @@ impl Palette {
text: color!(0x282828), // light FG0_29
primary: color!(0x458588), // light BLUE_4
success: color!(0x98971a), // light GREEN_2
+ warning: color!(0xd79921), // light YELLOW_3
danger: color!(0xcc241d), // light RED_1
};
@@ -131,6 +149,7 @@ impl Palette {
text: color!(0xfbf1c7), // dark FG0_29
primary: color!(0x458588), // dark BLUE_4
success: color!(0x98971a), // dark GREEN_2
+ warning: color!(0xd79921), // dark YELLOW_3
danger: color!(0xcc241d), // dark RED_1
};
@@ -142,6 +161,7 @@ impl Palette {
text: color!(0x4c4f69), // Text
primary: color!(0x1e66f5), // Blue
success: color!(0x40a02b), // Green
+ warning: color!(0xdf8e1d), // Yellow
danger: color!(0xd20f39), // Red
};
@@ -153,6 +173,7 @@ impl Palette {
text: color!(0xc6d0f5), // Text
primary: color!(0x8caaee), // Blue
success: color!(0xa6d189), // Green
+ warning: color!(0xe5c890), // Yellow
danger: color!(0xe78284), // Red
};
@@ -164,6 +185,7 @@ impl Palette {
text: color!(0xcad3f5), // Text
primary: color!(0x8aadf4), // Blue
success: color!(0xa6da95), // Green
+ warning: color!(0xeed49f), // Yellow
danger: color!(0xed8796), // Red
};
@@ -175,6 +197,7 @@ impl Palette {
text: color!(0xcdd6f4), // Text
primary: color!(0x89b4fa), // Blue
success: color!(0xa6e3a1), // Green
+ warning: color!(0xf9e2af), // Yellow
danger: color!(0xf38ba8), // Red
};
@@ -186,6 +209,7 @@ impl Palette {
text: color!(0x9aa5ce), // Text
primary: color!(0x2ac3de), // Blue
success: color!(0x9ece6a), // Green
+ warning: color!(0xe0af68), // Yellow
danger: color!(0xf7768e), // Red
};
@@ -197,6 +221,7 @@ impl Palette {
text: color!(0x9aa5ce), // Text
primary: color!(0x2ac3de), // Blue
success: color!(0x9ece6a), // Green
+ warning: color!(0xe0af68), // Yellow
danger: color!(0xf7768e), // Red
};
@@ -208,6 +233,7 @@ impl Palette {
text: color!(0x565a6e), // Text
primary: color!(0x166775), // Blue
success: color!(0x485e30), // Green
+ warning: color!(0x8f5e15), // Yellow
danger: color!(0x8c4351), // Red
};
@@ -219,6 +245,7 @@ impl Palette {
text: color!(0xCD7BA), // Fuji White
primary: color!(0x2D4F67), // Wave Blue 2
success: color!(0x76946A), // Autumn Green
+ warning: color!(0xff9e3b), // Ronin Yellow
danger: color!(0xC34043), // Autumn Red
};
@@ -230,6 +257,7 @@ impl Palette {
text: color!(0xc5c9c5), // Dragon White
primary: color!(0x223249), // Wave Blue 1
success: color!(0x8a9a7b), // Dragon Green 2
+ warning: color!(0xff9e3b), // Ronin Yellow
danger: color!(0xc4746e), // Dragon Red
};
@@ -241,6 +269,7 @@ impl Palette {
text: color!(0x545464), // Lotus Ink 1
primary: color!(0xc9cbd1), // Lotus Violet 3
success: color!(0x6f894e), // Lotus Green
+ warning: color!(0xe98a00), // Lotus Orange 2
danger: color!(0xc84053), // Lotus Red
};
@@ -252,6 +281,7 @@ impl Palette {
text: color!(0xbdbdbd), // Foreground
primary: color!(0x80a0ff), // Blue (normal)
success: color!(0x8cc85f), // Green (normal)
+ warning: color!(0xe3c78a), // Yellow (normal)
danger: color!(0xff5454), // Red (normal)
};
@@ -263,6 +293,7 @@ impl Palette {
text: color!(0xbdc1c6), // Foreground
primary: color!(0x82aaff), // Blue (normal)
success: color!(0xa1cd5e), // Green (normal)
+ warning: color!(0xe3d18a), // Yellow (normal)
danger: color!(0xfc514e), // Red (normal)
};
@@ -274,6 +305,7 @@ impl Palette {
text: color!(0xd0d0d0),
primary: color!(0x00b4ff),
success: color!(0x00c15a),
+ warning: color!(0xbe95ff), // Base 14
danger: color!(0xf62d0f),
};
@@ -285,6 +317,7 @@ impl Palette {
text: color!(0xfecdb2),
primary: color!(0xd1d1e0),
success: color!(0xb1b695),
+ warning: color!(0xf5d76e), // Honey
danger: color!(0xe06b75),
};
}
@@ -300,6 +333,8 @@ pub struct Extended {
pub secondary: Secondary,
/// The set of success colors.
pub success: Success,
+ /// The set of warning colors.
+ pub warning: Warning,
/// The set of danger colors.
pub danger: Danger,
/// Whether the palette is dark or not.
@@ -307,92 +342,92 @@ pub struct Extended {
}
/// The built-in light variant of an [`Extended`] palette.
-pub static EXTENDED_LIGHT: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::LIGHT));
+pub static EXTENDED_LIGHT: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::LIGHT));
/// The built-in dark variant of an [`Extended`] palette.
-pub static EXTENDED_DARK: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::DARK));
+pub static EXTENDED_DARK: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::DARK));
/// The built-in Dracula variant of an [`Extended`] palette.
-pub static EXTENDED_DRACULA: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::DRACULA));
+pub static EXTENDED_DRACULA: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::DRACULA));
/// The built-in Nord variant of an [`Extended`] palette.
-pub static EXTENDED_NORD: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::NORD));
+pub static EXTENDED_NORD: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::NORD));
/// The built-in Solarized Light variant of an [`Extended`] palette.
-pub static EXTENDED_SOLARIZED_LIGHT: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::SOLARIZED_LIGHT));
+pub static EXTENDED_SOLARIZED_LIGHT: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::SOLARIZED_LIGHT));
/// The built-in Solarized Dark variant of an [`Extended`] palette.
-pub static EXTENDED_SOLARIZED_DARK: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::SOLARIZED_DARK));
+pub static EXTENDED_SOLARIZED_DARK: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::SOLARIZED_DARK));
/// The built-in Gruvbox Light variant of an [`Extended`] palette.
-pub static EXTENDED_GRUVBOX_LIGHT: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::GRUVBOX_LIGHT));
+pub static EXTENDED_GRUVBOX_LIGHT: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::GRUVBOX_LIGHT));
/// The built-in Gruvbox Dark variant of an [`Extended`] palette.
-pub static EXTENDED_GRUVBOX_DARK: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::GRUVBOX_DARK));
+pub static EXTENDED_GRUVBOX_DARK: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::GRUVBOX_DARK));
/// The built-in Catppuccin Latte variant of an [`Extended`] palette.
-pub static EXTENDED_CATPPUCCIN_LATTE: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::CATPPUCCIN_LATTE));
+pub static EXTENDED_CATPPUCCIN_LATTE: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::CATPPUCCIN_LATTE));
/// The built-in Catppuccin Frappé variant of an [`Extended`] palette.
-pub static EXTENDED_CATPPUCCIN_FRAPPE: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::CATPPUCCIN_FRAPPE));
+pub static EXTENDED_CATPPUCCIN_FRAPPE: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::CATPPUCCIN_FRAPPE));
/// The built-in Catppuccin Macchiato variant of an [`Extended`] palette.
-pub static EXTENDED_CATPPUCCIN_MACCHIATO: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::CATPPUCCIN_MACCHIATO));
+pub static EXTENDED_CATPPUCCIN_MACCHIATO: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::CATPPUCCIN_MACCHIATO));
/// The built-in Catppuccin Mocha variant of an [`Extended`] palette.
-pub static EXTENDED_CATPPUCCIN_MOCHA: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::CATPPUCCIN_MOCHA));
+pub static EXTENDED_CATPPUCCIN_MOCHA: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::CATPPUCCIN_MOCHA));
/// The built-in Tokyo Night variant of an [`Extended`] palette.
-pub static EXTENDED_TOKYO_NIGHT: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::TOKYO_NIGHT));
+pub static EXTENDED_TOKYO_NIGHT: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::TOKYO_NIGHT));
/// The built-in Tokyo Night Storm variant of an [`Extended`] palette.
-pub static EXTENDED_TOKYO_NIGHT_STORM: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::TOKYO_NIGHT_STORM));
+pub static EXTENDED_TOKYO_NIGHT_STORM: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::TOKYO_NIGHT_STORM));
/// The built-in Tokyo Night variant of an [`Extended`] palette.
-pub static EXTENDED_TOKYO_NIGHT_LIGHT: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::TOKYO_NIGHT_LIGHT));
+pub static EXTENDED_TOKYO_NIGHT_LIGHT: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::TOKYO_NIGHT_LIGHT));
/// The built-in Kanagawa Wave variant of an [`Extended`] palette.
-pub static EXTENDED_KANAGAWA_WAVE: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::KANAGAWA_WAVE));
+pub static EXTENDED_KANAGAWA_WAVE: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::KANAGAWA_WAVE));
/// The built-in Kanagawa Dragon variant of an [`Extended`] palette.
-pub static EXTENDED_KANAGAWA_DRAGON: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::KANAGAWA_DRAGON));
+pub static EXTENDED_KANAGAWA_DRAGON: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::KANAGAWA_DRAGON));
/// The built-in Kanagawa Lotus variant of an [`Extended`] palette.
-pub static EXTENDED_KANAGAWA_LOTUS: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::KANAGAWA_LOTUS));
+pub static EXTENDED_KANAGAWA_LOTUS: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::KANAGAWA_LOTUS));
/// The built-in Moonfly variant of an [`Extended`] palette.
-pub static EXTENDED_MOONFLY: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::MOONFLY));
+pub static EXTENDED_MOONFLY: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::MOONFLY));
/// The built-in Nightfly variant of an [`Extended`] palette.
-pub static EXTENDED_NIGHTFLY: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::NIGHTFLY));
+pub static EXTENDED_NIGHTFLY: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::NIGHTFLY));
/// The built-in Oxocarbon variant of an [`Extended`] palette.
-pub static EXTENDED_OXOCARBON: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::OXOCARBON));
+pub static EXTENDED_OXOCARBON: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::OXOCARBON));
/// The built-in Ferra variant of an [`Extended`] palette.
-pub static EXTENDED_FERRA: Lazy<Extended> =
- Lazy::new(|| Extended::generate(Palette::FERRA));
+pub static EXTENDED_FERRA: LazyLock<Extended> =
+ LazyLock::new(|| Extended::generate(Palette::FERRA));
impl Extended {
/// Generates an [`Extended`] palette from a simple [`Palette`].
@@ -410,6 +445,11 @@ impl Extended {
palette.background,
palette.text,
),
+ warning: Warning::generate(
+ palette.warning,
+ palette.background,
+ palette.text,
+ ),
danger: Danger::generate(
palette.danger,
palette.background,
@@ -545,6 +585,31 @@ impl Success {
}
}
+/// A set of warning colors.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Warning {
+ /// The base warning color.
+ pub base: Pair,
+ /// A weaker version of the base warning color.
+ pub weak: Pair,
+ /// A stronger version of the base warning color.
+ pub strong: Pair,
+}
+
+impl Warning {
+ /// Generates a set of [`Warning`] colors from the base, background, and text colors.
+ pub fn generate(base: Color, background: Color, text: Color) -> Self {
+ let weak = mix(base, background, 0.4);
+ let strong = deviate(base, 0.1);
+
+ Self {
+ base: Pair::new(base, text),
+ weak: Pair::new(weak, text),
+ strong: Pair::new(strong, text),
+ }
+ }
+}
+
/// A set of danger colors.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Danger {
diff --git a/core/src/time.rs b/core/src/time.rs
index dcfe4e41..c6e30444 100644
--- a/core/src/time.rs
+++ b/core/src/time.rs
@@ -2,3 +2,28 @@
pub use web_time::Duration;
pub use web_time::Instant;
+
+/// Creates a [`Duration`] representing the given amount of milliseconds.
+pub fn milliseconds(milliseconds: u64) -> Duration {
+ Duration::from_millis(milliseconds)
+}
+
+/// Creates a [`Duration`] representing the given amount of seconds.
+pub fn seconds(seconds: u64) -> Duration {
+ Duration::from_secs(seconds)
+}
+
+/// Creates a [`Duration`] representing the given amount of minutes.
+pub fn minutes(minutes: u64) -> Duration {
+ seconds(minutes * 60)
+}
+
+/// Creates a [`Duration`] representing the given amount of hours.
+pub fn hours(hours: u64) -> Duration {
+ minutes(hours * 60)
+}
+
+/// Creates a [`Duration`] representing the given amount of days.
+pub fn days(days: u64) -> Duration {
+ hours(days * 24)
+}
diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs
index 6bdb27f6..8fc627bf 100644
--- a/core/src/widget/operation.rs
+++ b/core/src/widget/operation.rs
@@ -30,24 +30,45 @@ pub trait Operation<T = ()>: Send {
);
/// Operates on a widget that can be focused.
- fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {}
+ fn focusable(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ _state: &mut dyn Focusable,
+ ) {
+ }
/// Operates on a widget that can be scrolled.
fn scrollable(
&mut self,
- _state: &mut dyn Scrollable,
_id: Option<&Id>,
_bounds: Rectangle,
_content_bounds: Rectangle,
_translation: Vector,
+ _state: &mut dyn Scrollable,
) {
}
/// Operates on a widget that has text input.
- fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
+ fn text_input(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ _state: &mut dyn TextInput,
+ ) {
+ }
+
+ /// Operates on a widget that contains some text.
+ fn text(&mut self, _id: Option<&Id>, _bounds: Rectangle, _text: &str) {}
/// Operates on a custom widget with some state.
- fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {}
+ fn custom(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ _state: &mut dyn Any,
+ ) {
+ }
/// Finishes the [`Operation`] and returns its [`Outcome`].
fn finish(&self) -> Outcome<T> {
@@ -68,33 +89,52 @@ where
self.as_mut().container(id, bounds, operate_on_children);
}
- fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
- self.as_mut().focusable(state, id);
+ fn focusable(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
+ self.as_mut().focusable(id, bounds, state);
}
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,
translation: Vector,
+ state: &mut dyn Scrollable,
) {
self.as_mut().scrollable(
- state,
id,
bounds,
content_bounds,
translation,
+ state,
);
}
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
- self.as_mut().text_input(state, id);
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
+ self.as_mut().text_input(id, bounds, state);
+ }
+
+ fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
+ self.as_mut().text(id, bounds, text);
}
- fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
- self.as_mut().custom(state, id);
+ fn custom(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Any,
+ ) {
+ self.as_mut().custom(id, bounds, state);
}
fn finish(&self) -> Outcome<O> {
@@ -150,33 +190,52 @@ where
});
}
- fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
- self.operation.focusable(state, id);
+ fn focusable(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
+ self.operation.focusable(id, bounds, state);
}
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,
translation: Vector,
+ state: &mut dyn Scrollable,
) {
self.operation.scrollable(
- state,
id,
bounds,
content_bounds,
translation,
+ state,
);
}
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
- self.operation.text_input(state, id);
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
+ self.operation.text_input(id, bounds, state);
}
- fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
- self.operation.custom(state, id);
+ fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
+ self.operation.text(id, bounds, text);
+ }
+
+ fn custom(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Any,
+ ) {
+ self.operation.custom(id, bounds, state);
}
fn finish(&self) -> Outcome<O> {
@@ -234,39 +293,55 @@ where
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,
translation: Vector,
+ state: &mut dyn Scrollable,
) {
self.operation.scrollable(
- state,
id,
bounds,
content_bounds,
translation,
+ state,
);
}
fn focusable(
&mut self,
- state: &mut dyn Focusable,
id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Focusable,
) {
- self.operation.focusable(state, id);
+ self.operation.focusable(id, bounds, state);
}
fn text_input(
&mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
state: &mut dyn TextInput,
+ ) {
+ self.operation.text_input(id, bounds, state);
+ }
+
+ fn text(
+ &mut self,
id: Option<&Id>,
+ bounds: Rectangle,
+ text: &str,
) {
- self.operation.text_input(state, id);
+ self.operation.text(id, bounds, text);
}
- fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
- self.operation.custom(state, id);
+ fn custom(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Any,
+ ) {
+ self.operation.custom(id, bounds, state);
}
}
@@ -275,33 +350,52 @@ where
MapRef { operation }.container(id, bounds, operate_on_children);
}
- fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
- self.operation.focusable(state, id);
+ fn focusable(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
+ self.operation.focusable(id, bounds, state);
}
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,
translation: Vector,
+ state: &mut dyn Scrollable,
) {
self.operation.scrollable(
- state,
id,
bounds,
content_bounds,
translation,
+ state,
);
}
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
- self.operation.text_input(state, id);
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
+ self.operation.text_input(id, bounds, state);
+ }
+
+ fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
+ self.operation.text(id, bounds, text);
}
- fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
- self.operation.custom(state, id);
+ fn custom(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Any,
+ ) {
+ self.operation.custom(id, bounds, state);
}
fn finish(&self) -> Outcome<B> {
@@ -361,33 +455,52 @@ where
});
}
- fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
- self.operation.focusable(state, id);
+ fn focusable(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
+ self.operation.focusable(id, bounds, state);
}
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,
translation: crate::Vector,
+ state: &mut dyn Scrollable,
) {
self.operation.scrollable(
- state,
id,
bounds,
content_bounds,
translation,
+ state,
);
}
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
- self.operation.text_input(state, id);
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
+ self.operation.text_input(id, bounds, state);
}
- fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) {
- self.operation.custom(state, id);
+ fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
+ self.operation.text(id, bounds, text);
+ }
+
+ fn custom(
+ &mut self,
+ id: Option<&Id>,
+ bounds: Rectangle,
+ state: &mut dyn Any,
+ ) {
+ self.operation.custom(id, bounds, state);
}
fn finish(&self) -> Outcome<B> {
diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs
index 867c682e..8f66e575 100644
--- a/core/src/widget/operation/focusable.rs
+++ b/core/src/widget/operation/focusable.rs
@@ -32,7 +32,12 @@ pub fn focus<T>(target: Id) -> impl Operation<T> {
}
impl<T> Operation<T> for Focus {
- fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
+ fn focusable(
+ &mut self,
+ id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
match id {
Some(id) if id == &self.target => {
state.focus();
@@ -64,7 +69,12 @@ pub fn count() -> impl Operation<Count> {
}
impl Operation<Count> for CountFocusable {
- fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {
+ fn focusable(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
if state.is_focused() {
self.count.focused = Some(self.count.total);
}
@@ -104,7 +114,12 @@ where
}
impl<T> Operation<T> for FocusPrevious {
- fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {
+ fn focusable(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
if self.count.total == 0 {
return;
}
@@ -147,7 +162,12 @@ where
}
impl<T> Operation<T> for FocusNext {
- fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {
+ fn focusable(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
match self.count.focused {
None if self.current == 0 => state.focus(),
Some(focused) if focused == self.current => state.unfocus(),
@@ -179,7 +199,12 @@ pub fn find_focused() -> impl Operation<Id> {
}
impl Operation<Id> for FindFocused {
- fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
+ fn focusable(
+ &mut self,
+ id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
if state.is_focused() && id.is_some() {
self.focused = id.cloned();
}
diff --git a/core/src/widget/operation/scrollable.rs b/core/src/widget/operation/scrollable.rs
index c2fecf56..7c78c087 100644
--- a/core/src/widget/operation/scrollable.rs
+++ b/core/src/widget/operation/scrollable.rs
@@ -39,11 +39,11 @@ pub fn snap_to<T>(target: Id, offset: RelativeOffset) -> impl Operation<T> {
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
_bounds: Rectangle,
_content_bounds: Rectangle,
_translation: Vector,
+ state: &mut dyn Scrollable,
) {
if Some(&self.target) == id {
state.snap_to(self.offset);
@@ -74,11 +74,11 @@ pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
_bounds: Rectangle,
_content_bounds: Rectangle,
_translation: Vector,
+ state: &mut dyn Scrollable,
) {
if Some(&self.target) == id {
state.scroll_to(self.offset);
@@ -109,11 +109,11 @@ pub fn scroll_by<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
fn scrollable(
&mut self,
- state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,
_translation: Vector,
+ state: &mut dyn Scrollable,
) {
if Some(&self.target) == id {
state.scroll_by(self.offset, bounds, content_bounds);
diff --git a/core/src/widget/operation/text_input.rs b/core/src/widget/operation/text_input.rs
index 41731d4c..a46f1a2d 100644
--- a/core/src/widget/operation/text_input.rs
+++ b/core/src/widget/operation/text_input.rs
@@ -23,7 +23,12 @@ pub fn move_cursor_to_front<T>(target: Id) -> impl Operation<T> {
}
impl<T> Operation<T> for MoveCursor {
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
match id {
Some(id) if id == &self.target => {
state.move_cursor_to_front();
@@ -53,7 +58,12 @@ pub fn move_cursor_to_end<T>(target: Id) -> impl Operation<T> {
}
impl<T> Operation<T> for MoveCursor {
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
match id {
Some(id) if id == &self.target => {
state.move_cursor_to_end();
@@ -84,7 +94,12 @@ pub fn move_cursor_to<T>(target: Id, position: usize) -> impl Operation<T> {
}
impl<T> Operation<T> for MoveCursor {
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
match id {
Some(id) if id == &self.target => {
state.move_cursor_to(self.position);
@@ -113,7 +128,12 @@ pub fn select_all<T>(target: Id) -> impl Operation<T> {
}
impl<T> Operation<T> for MoveCursor {
- fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ fn text_input(
+ &mut self,
+ id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn TextInput,
+ ) {
match id {
Some(id) if id == &self.target => {
state.select_all();
diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs
index d3d1cffd..c7ec3c8b 100644
--- a/core/src/widget/text.rs
+++ b/core/src/widget/text.rs
@@ -267,6 +267,16 @@ where
draw(renderer, defaults, layout, state.0.raw(), style, viewport);
}
+
+ fn operate(
+ &self,
+ _state: &mut Tree,
+ layout: Layout<'_>,
+ _renderer: &Renderer,
+ operation: &mut dyn super::Operation,
+ ) {
+ operation.text(None, layout.bounds(), &self.fragment);
+ }
}
/// Produces the [`layout::Node`] of a [`Text`] widget.
diff --git a/core/src/window.rs b/core/src/window.rs
index 448ffc45..d0e741d8 100644
--- a/core/src/window.rs
+++ b/core/src/window.rs
@@ -1,7 +1,9 @@
//! Build window-based GUI applications.
pub mod icon;
+pub mod screenshot;
pub mod settings;
+mod direction;
mod event;
mod id;
mod level;
@@ -10,6 +12,7 @@ mod position;
mod redraw_request;
mod user_attention;
+pub use direction::Direction;
pub use event::Event;
pub use icon::Icon;
pub use id::Id;
@@ -17,5 +20,6 @@ pub use level::Level;
pub use mode::Mode;
pub use position::Position;
pub use redraw_request::RedrawRequest;
+pub use screenshot::Screenshot;
pub use settings::Settings;
pub use user_attention::UserAttention;
diff --git a/core/src/window/direction.rs b/core/src/window/direction.rs
new file mode 100644
index 00000000..b757961e
--- /dev/null
+++ b/core/src/window/direction.rs
@@ -0,0 +1,27 @@
+/// The cardinal directions relative to the center of a window.
+#[derive(Debug, Clone, Copy)]
+pub enum Direction {
+ /// Points to the top edge of a window.
+ North,
+
+ /// Points to the bottom edge of a window.
+ South,
+
+ /// Points to the right edge of a window.
+ East,
+
+ /// Points to the left edge of a window.
+ West,
+
+ /// Points to the top-right corner of a window.
+ NorthEast,
+
+ /// Points to the top-left corner of a window.
+ NorthWest,
+
+ /// Points to the bottom-right corner of a window.
+ SouthEast,
+
+ /// Points to the bottom-left corner of a window.
+ SouthWest,
+}
diff --git a/core/src/window/event.rs b/core/src/window/event.rs
index 4e2751ee..45d29179 100644
--- a/core/src/window/event.rs
+++ b/core/src/window/event.rs
@@ -9,8 +9,8 @@ pub enum Event {
/// A window was opened.
Opened {
/// The position of the opened window. This is relative to the top-left corner of the desktop
- /// the window is on, including virtual desktops. Refers to window's "inner" position,
- /// or the client area, in logical pixels.
+ /// the window is on, including virtual desktops. Refers to window's "outer" position,
+ /// or the window area, in logical pixels.
///
/// **Note**: Not available in Wayland.
position: Option<Point>,
diff --git a/runtime/src/window/screenshot.rs b/core/src/window/screenshot.rs
index d9adbc01..424168bb 100644
--- a/runtime/src/window/screenshot.rs
+++ b/core/src/window/screenshot.rs
@@ -1,5 +1,5 @@
//! Take screenshots of a window.
-use crate::core::{Rectangle, Size};
+use crate::{Rectangle, Size};
use bytes::Bytes;
use std::fmt::{Debug, Formatter};
diff --git a/core/src/window/settings.rs b/core/src/window/settings.rs
index fbbf86ab..9432eaaa 100644
--- a/core/src/window/settings.rs
+++ b/core/src/window/settings.rs
@@ -28,12 +28,19 @@ use crate::window::{Icon, Level, Position};
use crate::Size;
pub use platform::PlatformSpecific;
+
/// The window settings of an application.
#[derive(Debug, Clone)]
pub struct Settings {
/// The initial logical dimensions of the window.
pub size: Size,
+ /// Whether the window should start maximized.
+ pub maximized: bool,
+
+ /// Whether the window should start fullscreen.
+ pub fullscreen: bool,
+
/// The initial position of the window.
pub position: Position,
@@ -79,6 +86,8 @@ impl Default for Settings {
fn default() -> Self {
Self {
size: Size::new(1024.0, 768.0),
+ maximized: false,
+ fullscreen: false,
position: Position::default(),
min_size: None,
max_size: None,
diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs
index 18873259..88544caa 100644
--- a/examples/arc/src/main.rs
+++ b/examples/arc/src/main.rs
@@ -4,6 +4,7 @@ use iced::mouse;
use iced::widget::canvas::{
self, stroke, Cache, Canvas, Geometry, Path, Stroke,
};
+use iced::window;
use iced::{Element, Fill, Point, Rectangle, Renderer, Subscription, Theme};
pub fn main() -> iced::Result {
@@ -34,8 +35,7 @@ impl Arc {
}
fn subscription(&self) -> Subscription<Message> {
- iced::time::every(std::time::Duration::from_millis(10))
- .map(|_| Message::Tick)
+ window::frames().map(|_| Message::Tick)
}
}
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index e8f0efc9..4d438bd9 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -1,6 +1,6 @@
//! This example showcases an interactive `Canvas` for drawing Bézier curves.
-use iced::widget::{button, container, horizontal_space, hover};
-use iced::{Element, Fill, Theme};
+use iced::widget::{button, container, horizontal_space, hover, right};
+use iced::{Element, Theme};
pub fn main() -> iced::Result {
iced::application("Bezier Tool - Iced", Example::update, Example::view)
@@ -41,13 +41,12 @@ impl Example {
if self.curves.is_empty() {
container(horizontal_space())
} else {
- container(
+ right(
button("Clear")
.style(button::danger)
.on_press(Message::Clear),
)
.padding(10)
- .align_right(Fill)
},
))
.padding(20)
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs
index ef3064c7..0810594f 100644
--- a/examples/clock/src/main.rs
+++ b/examples/clock/src/main.rs
@@ -1,11 +1,11 @@
-use iced::alignment;
use iced::mouse;
-use iced::time;
+use iced::time::{self, milliseconds};
use iced::widget::canvas::{stroke, Cache, Geometry, LineCap, Path, Stroke};
use iced::widget::{canvas, container};
+use iced::{alignment, Radians};
use iced::{
- Degrees, Element, Fill, Font, Point, Rectangle, Renderer, Subscription,
- Theme, Vector,
+ Degrees, Element, Fill, Font, Point, Rectangle, Renderer, Size,
+ Subscription, Theme, Vector,
};
pub fn main() -> iced::Result {
@@ -49,7 +49,7 @@ impl Clock {
}
fn subscription(&self) -> Subscription<Message> {
- time::every(time::Duration::from_millis(500))
+ time::every(milliseconds(500))
.map(|_| Message::Tick(chrono::offset::Local::now()))
}
@@ -117,9 +117,14 @@ impl<Message> canvas::Program<Message> for Clock {
};
frame.translate(Vector::new(center.x, center.y));
+ let minutes_portion =
+ Radians::from(hand_rotation(self.now.minute(), 60)) / 12.0;
+ let hour_hand_angle =
+ Radians::from(hand_rotation(self.now.hour(), 12))
+ + minutes_portion;
frame.with_save(|frame| {
- frame.rotate(hand_rotation(self.now.hour(), 12));
+ frame.rotate(hour_hand_angle);
frame.stroke(&short_hand, wide_stroke());
});
@@ -155,6 +160,42 @@ impl<Message> canvas::Program<Message> for Clock {
..canvas::Text::default()
});
});
+
+ // Draw clock numbers
+ for hour in 1..=12 {
+ let angle = Radians::from(hand_rotation(hour, 12))
+ - Radians::from(Degrees(90.0));
+ let x = radius * angle.0.cos();
+ let y = radius * angle.0.sin();
+
+ frame.fill_text(canvas::Text {
+ content: format!("{}", hour),
+ size: (radius / 5.0).into(),
+ position: Point::new(x * 0.82, y * 0.82),
+ color: palette.secondary.strong.text,
+ horizontal_alignment: alignment::Horizontal::Center,
+ vertical_alignment: alignment::Vertical::Center,
+ font: Font::MONOSPACE,
+ ..canvas::Text::default()
+ });
+ }
+
+ // Draw ticks
+ for tick in 0..60 {
+ let angle = hand_rotation(tick, 60);
+ let width = if tick % 5 == 0 { 3.0 } else { 1.0 };
+
+ frame.with_save(|frame| {
+ frame.rotate(angle);
+ frame.fill(
+ &Path::rectangle(
+ Point::new(0.0, radius - 15.0),
+ Size::new(width, 7.0),
+ ),
+ palette.secondary.strong.text,
+ );
+ });
+ }
});
vec![clock]
diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs
index 7f21003b..1a86b168 100644
--- a/examples/color_palette/src/main.rs
+++ b/examples/color_palette/src/main.rs
@@ -89,6 +89,7 @@ impl ColorPalette {
primary: *self.theme.lower.first().unwrap(),
text: *self.theme.higher.last().unwrap(),
success: *self.theme.lower.last().unwrap(),
+ warning: *self.theme.higher.last().unwrap(),
danger: *self.theme.higher.last().unwrap(),
},
)
diff --git a/examples/counter/Cargo.toml b/examples/counter/Cargo.toml
index 22f86064..02eac329 100644
--- a/examples/counter/Cargo.toml
+++ b/examples/counter/Cargo.toml
@@ -10,4 +10,7 @@ iced.workspace = true
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced.workspace = true
-iced.features = ["webgl"]
+iced.features = ["webgl", "fira-sans"]
+
+[dev-dependencies]
+iced_test.workspace = true
diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs
index 81684c1c..18bb8cfe 100644
--- a/examples/counter/src/main.rs
+++ b/examples/counter/src/main.rs
@@ -38,3 +38,31 @@ impl Counter {
.align_x(Center)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use iced_test::selector::text;
+ use iced_test::{simulator, Error};
+
+ #[test]
+ fn it_counts() -> Result<(), Error> {
+ let mut counter = Counter { value: 0 };
+ let mut ui = simulator(counter.view());
+
+ let _ = ui.click(text("Increment"))?;
+ let _ = ui.click(text("Increment"))?;
+ let _ = ui.click(text("Decrement"))?;
+
+ for message in ui.into_messages() {
+ counter.update(message);
+ }
+
+ assert_eq!(counter.value, 1);
+
+ let mut ui = simulator(counter.view());
+ assert!(ui.find(text("1")).is_ok(), "Counter should display 1!");
+
+ Ok(())
+ }
+}
diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs
index d55f9bdf..7032324a 100644
--- a/examples/editor/src/main.rs
+++ b/examples/editor/src/main.rs
@@ -1,8 +1,8 @@
use iced::highlighter;
use iced::keyboard;
use iced::widget::{
- self, button, column, container, horizontal_space, pick_list, row, text,
- text_editor, toggler, tooltip,
+ self, button, center_x, column, container, horizontal_space, pick_list,
+ row, text, text_editor, toggler, tooltip,
};
use iced::{Center, Element, Fill, Font, Task, Theme};
@@ -288,7 +288,7 @@ fn action<'a, Message: Clone + 'a>(
label: &'a str,
on_press: Option<Message>,
) -> Element<'a, Message> {
- let action = button(container(content).center_x(30));
+ let action = button(center_x(content).width(30));
if let Some(on_press) = on_press {
tooltip(
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 7a7224d5..1008e477 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -5,12 +5,11 @@ mod preset;
use grid::Grid;
use preset::Preset;
-use iced::time;
+use iced::time::{self, milliseconds};
use iced::widget::{
button, checkbox, column, container, pick_list, row, slider, text,
};
use iced::{Center, Element, Fill, Subscription, Task, Theme};
-use std::time::Duration;
pub fn main() -> iced::Result {
tracing_subscriber::fmt::init();
@@ -112,7 +111,7 @@ impl GameOfLife {
fn subscription(&self) -> Subscription<Message> {
if self.is_playing {
- time::every(Duration::from_millis(1000 / self.speed as u64))
+ time::every(milliseconds(1000 / self.speed as u64))
.map(|_| Message::Tick)
} else {
Subscription::none()
@@ -191,6 +190,7 @@ mod grid {
use crate::Preset;
use iced::alignment;
use iced::mouse;
+ use iced::time::{Duration, Instant};
use iced::touch;
use iced::widget::canvas;
use iced::widget::canvas::{
@@ -202,7 +202,6 @@ mod grid {
use rustc_hash::{FxHashMap, FxHashSet};
use std::future::Future;
use std::ops::RangeInclusive;
- use std::time::{Duration, Instant};
pub struct Grid {
state: State,
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index d53ae6a5..44214c63 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -152,8 +152,8 @@ mod rainbow {
}
}
-use iced::widget::{column, container, scrollable};
-use iced::{Element, Length};
+use iced::widget::{center_x, center_y, column, scrollable};
+use iced::Element;
use rainbow::rainbow;
pub fn main() -> iced::Result {
@@ -176,7 +176,7 @@ fn view(_state: &()) -> Element<'_, ()> {
.spacing(20)
.max_width(500);
- let scrollable = scrollable(container(content).center_x(Length::Fill));
+ let scrollable = scrollable(center_x(content));
- container(scrollable).center_y(Length::Fill).into()
+ center_y(scrollable).into()
}
diff --git a/examples/gradient/src/main.rs b/examples/gradient/src/main.rs
index b2de069f..910ea9fc 100644
--- a/examples/gradient/src/main.rs
+++ b/examples/gradient/src/main.rs
@@ -1,5 +1,5 @@
-use iced::application;
use iced::gradient;
+use iced::theme;
use iced::widget::{
checkbox, column, container, horizontal_space, row, slider, text,
};
@@ -95,16 +95,14 @@ impl Gradient {
.into()
}
- fn style(&self, theme: &Theme) -> application::Appearance {
- use application::DefaultStyle;
-
+ fn style(&self, theme: &Theme) -> theme::Style {
if self.transparent {
- application::Appearance {
+ theme::Style {
background_color: Color::TRANSPARENT,
text_color: theme.palette().text,
}
} else {
- Theme::default_style(theme)
+ theme::default(theme)
}
}
}
diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs
index 0b11a323..b92e4987 100644
--- a/examples/integration/src/controls.rs
+++ b/examples/integration/src/controls.rs
@@ -1,6 +1,6 @@
use iced_wgpu::Renderer;
-use iced_widget::{column, container, row, slider, text, text_input};
-use iced_winit::core::{Color, Element, Length::*, Theme};
+use iced_widget::{bottom, column, row, slider, text, text_input};
+use iced_winit::core::{Color, Element, Theme};
use iced_winit::runtime::{Program, Task};
pub struct Controls {
@@ -74,18 +74,17 @@ impl Program for Controls {
.width(500)
.spacing(20);
- container(
+ bottom(
column![
text("Background color").color(Color::WHITE),
text!("{background_color:?}").size(14).color(Color::WHITE),
- text_input("Placeholder", &self.input)
- .on_input(Message::InputChanged),
sliders,
+ text_input("Type something...", &self.input)
+ .on_input(Message::InputChanged),
]
.spacing(10),
)
.padding(10)
- .align_bottom(Fill)
.into()
}
}
diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs
index e83a1f7d..b298dce4 100644
--- a/examples/layout/src/main.rs
+++ b/examples/layout/src/main.rs
@@ -2,9 +2,9 @@ use iced::border;
use iced::keyboard;
use iced::mouse;
use iced::widget::{
- button, canvas, center, checkbox, column, container, horizontal_rule,
- horizontal_space, pick_list, pin, row, scrollable, stack, text,
- vertical_rule,
+ button, canvas, center, center_y, checkbox, column, container,
+ horizontal_rule, horizontal_space, pick_list, pin, row, scrollable, stack,
+ text, vertical_rule,
};
use iced::{
color, Center, Element, Fill, Font, Length, Point, Rectangle, Renderer,
@@ -253,15 +253,14 @@ fn application<'a>() -> Element<'a, Message> {
.border(border::color(palette.background.strong.color).width(1))
});
- let sidebar = container(
+ let sidebar = center_y(
column!["Sidebar!", square(50), square(50)]
.spacing(40)
.padding(10)
.width(200)
.align_x(Center),
)
- .style(container::rounded_box)
- .center_y(Fill);
+ .style(container::rounded_box);
let content = container(
scrollable(
diff --git a/examples/loading_spinners/Cargo.toml b/examples/loading_spinners/Cargo.toml
index a32da386..abd28aec 100644
--- a/examples/loading_spinners/Cargo.toml
+++ b/examples/loading_spinners/Cargo.toml
@@ -10,4 +10,3 @@ iced.workspace = true
iced.features = ["advanced", "canvas"]
lyon_algorithms = "1.0"
-once_cell.workspace = true \ No newline at end of file
diff --git a/examples/loading_spinners/src/easing.rs b/examples/loading_spinners/src/easing.rs
index 45089ef6..8caf282d 100644
--- a/examples/loading_spinners/src/easing.rs
+++ b/examples/loading_spinners/src/easing.rs
@@ -2,40 +2,41 @@ use iced::Point;
use lyon_algorithms::measure::PathMeasurements;
use lyon_algorithms::path::{builder::NoAttributes, path::BuilderImpl, Path};
-use once_cell::sync::Lazy;
-pub static EMPHASIZED: Lazy<Easing> = Lazy::new(|| {
+use std::sync::LazyLock;
+
+pub static EMPHASIZED: LazyLock<Easing> = LazyLock::new(|| {
Easing::builder()
.cubic_bezier_to([0.05, 0.0], [0.133333, 0.06], [0.166666, 0.4])
.cubic_bezier_to([0.208333, 0.82], [0.25, 1.0], [1.0, 1.0])
.build()
});
-pub static EMPHASIZED_DECELERATE: Lazy<Easing> = Lazy::new(|| {
+pub static EMPHASIZED_DECELERATE: LazyLock<Easing> = LazyLock::new(|| {
Easing::builder()
.cubic_bezier_to([0.05, 0.7], [0.1, 1.0], [1.0, 1.0])
.build()
});
-pub static EMPHASIZED_ACCELERATE: Lazy<Easing> = Lazy::new(|| {
+pub static EMPHASIZED_ACCELERATE: LazyLock<Easing> = LazyLock::new(|| {
Easing::builder()
.cubic_bezier_to([0.3, 0.0], [0.8, 0.15], [1.0, 1.0])
.build()
});
-pub static STANDARD: Lazy<Easing> = Lazy::new(|| {
+pub static STANDARD: LazyLock<Easing> = LazyLock::new(|| {
Easing::builder()
.cubic_bezier_to([0.2, 0.0], [0.0, 1.0], [1.0, 1.0])
.build()
});
-pub static STANDARD_DECELERATE: Lazy<Easing> = Lazy::new(|| {
+pub static STANDARD_DECELERATE: LazyLock<Easing> = LazyLock::new(|| {
Easing::builder()
.cubic_bezier_to([0.0, 0.0], [0.0, 1.0], [1.0, 1.0])
.build()
});
-pub static STANDARD_ACCELERATE: Lazy<Easing> = Lazy::new(|| {
+pub static STANDARD_ACCELERATE: LazyLock<Easing> = LazyLock::new(|| {
Easing::builder()
.cubic_bezier_to([0.3, 0.0], [1.0, 1.0], [1.0, 1.0])
.build()
diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs
index b43a627a..f9021c8d 100644
--- a/examples/multi_window/src/main.rs
+++ b/examples/multi_window/src/main.rs
@@ -1,5 +1,5 @@
use iced::widget::{
- button, center, column, container, horizontal_space, scrollable, text,
+ button, center, center_x, column, horizontal_space, scrollable, text,
text_input,
};
use iced::window;
@@ -193,6 +193,6 @@ impl Window {
.align_x(Center),
);
- container(content).center_x(200).into()
+ center_x(content).width(200).into()
}
}
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index 67f4d27f..17ba5804 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -1,7 +1,7 @@
use iced::keyboard;
use iced::widget::pane_grid::{self, PaneGrid};
use iced::widget::{
- button, column, container, responsive, row, scrollable, text,
+ button, center_y, column, container, responsive, row, scrollable, text,
};
use iced::{Center, Color, Element, Fill, Size, Subscription};
@@ -196,11 +196,7 @@ impl Example {
.on_drag(Message::Dragged)
.on_resize(10, Message::Resized);
- container(pane_grid)
- .width(Fill)
- .height(Fill)
- .padding(10)
- .into()
+ container(pane_grid).padding(10).into()
}
}
@@ -295,10 +291,7 @@ fn view_content<'a>(
.spacing(10)
.align_x(Center);
- container(scrollable(content))
- .center_y(Fill)
- .padding(5)
- .into()
+ center_y(scrollable(content)).padding(5).into()
}
fn view_controls<'a>(
diff --git a/examples/progress_bar/src/main.rs b/examples/progress_bar/src/main.rs
index 67da62f2..e431a404 100644
--- a/examples/progress_bar/src/main.rs
+++ b/examples/progress_bar/src/main.rs
@@ -1,4 +1,7 @@
-use iced::widget::{column, progress_bar, slider};
+use iced::widget::{
+ center, center_x, checkbox, column, progress_bar, row, slider,
+ vertical_slider,
+};
use iced::Element;
pub fn main() -> iced::Result {
@@ -8,25 +11,58 @@ pub fn main() -> iced::Result {
#[derive(Default)]
struct Progress {
value: f32,
+ is_vertical: bool,
}
#[derive(Debug, Clone, Copy)]
enum Message {
SliderChanged(f32),
+ ToggleVertical(bool),
}
impl Progress {
fn update(&mut self, message: Message) {
match message {
Message::SliderChanged(x) => self.value = x,
+ Message::ToggleVertical(is_vertical) => {
+ self.is_vertical = is_vertical
+ }
}
}
fn view(&self) -> Element<Message> {
+ let bar = progress_bar(0.0..=100.0, self.value);
+
column![
- progress_bar(0.0..=100.0, self.value),
- slider(0.0..=100.0, self.value, Message::SliderChanged).step(0.01)
+ if self.is_vertical {
+ center(
+ row![
+ bar.vertical(),
+ vertical_slider(
+ 0.0..=100.0,
+ self.value,
+ Message::SliderChanged
+ )
+ .step(0.01)
+ ]
+ .spacing(20),
+ )
+ } else {
+ center(
+ column![
+ bar,
+ slider(0.0..=100.0, self.value, Message::SliderChanged)
+ .step(0.01)
+ ]
+ .spacing(20),
+ )
+ },
+ center_x(
+ checkbox("Vertical", self.is_vertical)
+ .on_toggle(Message::ToggleVertical)
+ ),
]
+ .spacing(20)
.padding(20)
.into()
}
diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs
index 5c105f6c..7766542d 100644
--- a/examples/screenshot/src/main.rs
+++ b/examples/screenshot/src/main.rs
@@ -1,5 +1,7 @@
use iced::keyboard;
-use iced::widget::{button, column, container, image, row, text, text_input};
+use iced::widget::{
+ button, center_y, column, container, image, row, text, text_input,
+};
use iced::window;
use iced::window::screenshot::{self, Screenshot};
use iced::{
@@ -131,8 +133,8 @@ impl Example {
text("Press the button to take a screenshot!").into()
};
- let image = container(image)
- .center_y(FillPortion(2))
+ let image = center_y(image)
+ .height(FillPortion(2))
.padding(10)
.style(container::rounded_box);
@@ -211,7 +213,7 @@ impl Example {
.spacing(40)
};
- let side_content = container(controls).center_y(Fill);
+ let side_content = center_y(controls);
let content = row![side_content, image]
.spacing(10)
diff --git a/examples/scrollable/Cargo.toml b/examples/scrollable/Cargo.toml
index f8c735c0..ba291520 100644
--- a/examples/scrollable/Cargo.toml
+++ b/examples/scrollable/Cargo.toml
@@ -8,5 +8,3 @@ publish = false
[dependencies]
iced.workspace = true
iced.features = ["debug"]
-
-once_cell.workspace = true
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index de4f2f9a..6359fb5a 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -4,9 +4,10 @@ use iced::widget::{
};
use iced::{Border, Center, Color, Element, Fill, Task, Theme};
-use once_cell::sync::Lazy;
+use std::sync::LazyLock;
-static SCROLLABLE_ID: Lazy<scrollable::Id> = Lazy::new(scrollable::Id::unique);
+static SCROLLABLE_ID: LazyLock<scrollable::Id> =
+ LazyLock::new(scrollable::Id::unique);
pub fn main() -> iced::Result {
iced::application(
diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs
index 0d824d36..a814da55 100644
--- a/examples/stopwatch/src/main.rs
+++ b/examples/stopwatch/src/main.rs
@@ -1,10 +1,8 @@
use iced::keyboard;
-use iced::time;
+use iced::time::{self, milliseconds, Duration, Instant};
use iced::widget::{button, center, column, row, text};
use iced::{Center, Element, Subscription, Theme};
-use std::time::{Duration, Instant};
-
pub fn main() -> iced::Result {
iced::application("Stopwatch - Iced", Stopwatch::update, Stopwatch::view)
.subscription(Stopwatch::subscription)
@@ -63,7 +61,7 @@ impl Stopwatch {
let tick = match self.state {
State::Idle => Subscription::none(),
State::Ticking { .. } => {
- time::every(Duration::from_millis(10)).map(Message::Tick)
+ time::every(milliseconds(10)).map(Message::Tick)
}
};
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index 534f5e32..594be4a7 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -1,12 +1,14 @@
+use iced::keyboard;
use iced::widget::{
button, center, checkbox, column, horizontal_rule, pick_list, progress_bar,
row, scrollable, slider, text, text_input, toggler, vertical_rule,
vertical_space,
};
-use iced::{Center, Element, Fill, Theme};
+use iced::{Center, Element, Fill, Subscription, Theme};
pub fn main() -> iced::Result {
iced::application("Styling - Iced", Styling::update, Styling::view)
+ .subscription(Styling::subscription)
.theme(Styling::theme)
.run()
}
@@ -28,6 +30,8 @@ enum Message {
SliderChanged(f32),
CheckboxToggled(bool),
TogglerToggled(bool),
+ PreviousTheme,
+ NextTheme,
}
impl Styling {
@@ -41,6 +45,23 @@ impl Styling {
Message::SliderChanged(value) => self.slider_value = value,
Message::CheckboxToggled(value) => self.checkbox_value = value,
Message::TogglerToggled(value) => self.toggler_value = value,
+ Message::PreviousTheme | Message::NextTheme => {
+ if let Some(current) = Theme::ALL
+ .iter()
+ .position(|candidate| &self.theme == candidate)
+ {
+ self.theme = if matches!(message, Message::NextTheme) {
+ Theme::ALL[(current + 1) % Theme::ALL.len()].clone()
+ } else if current == 0 {
+ Theme::ALL
+ .last()
+ .expect("Theme::ALL must not be empty")
+ .clone()
+ } else {
+ Theme::ALL[current - 1].clone()
+ };
+ }
+ }
}
}
@@ -57,9 +78,16 @@ impl Styling {
.padding(10)
.size(20);
- let button = button("Submit")
- .padding(10)
- .on_press(Message::ButtonPressed);
+ let styled_button = |label| {
+ button(text(label).width(Fill).center())
+ .padding(10)
+ .on_press(Message::ButtonPressed)
+ };
+
+ let primary = styled_button("Primary");
+ let success = styled_button("Success").style(button::success);
+ let warning = styled_button("Warning").style(button::warning);
+ let danger = styled_button("Danger").style(button::danger);
let slider =
slider(0.0..=100.0, self.slider_value, Message::SliderChanged);
@@ -85,7 +113,10 @@ impl Styling {
let content = column![
choose_theme,
horizontal_rule(38),
- row![text_input, button].spacing(10).align_y(Center),
+ text_input,
+ row![primary, success, warning, danger]
+ .spacing(10)
+ .align_y(Center),
slider,
progress_bar,
row![
@@ -104,6 +135,19 @@ impl Styling {
center(content).into()
}
+ fn subscription(&self) -> Subscription<Message> {
+ keyboard::on_key_press(|key, _modifiers| match key {
+ keyboard::Key::Named(
+ keyboard::key::Named::ArrowUp | keyboard::key::Named::ArrowLeft,
+ ) => Some(Message::PreviousTheme),
+ keyboard::Key::Named(
+ keyboard::key::Named::ArrowDown
+ | keyboard::key::Named::ArrowRight,
+ ) => Some(Message::NextTheme),
+ _ => None,
+ })
+ }
+
fn theme(&self) -> Theme {
self.theme.clone()
}
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index 02cb85cc..c4be8fb8 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -1,4 +1,4 @@
-use iced::widget::{center, checkbox, column, container, svg};
+use iced::widget::{center, center_x, checkbox, column, svg};
use iced::{color, Element, Fill};
pub fn main() -> iced::Result {
@@ -46,12 +46,8 @@ impl Tiger {
checkbox("Apply a color filter", self.apply_color_filter)
.on_toggle(Message::ToggleColorFilter);
- center(
- column![svg, container(apply_color_filter).center_x(Fill)]
- .spacing(20)
- .height(Fill),
- )
- .padding(20)
- .into()
+ center(column![svg, center_x(apply_color_filter)].spacing(20))
+ .padding(20)
+ .into()
}
}
diff --git a/examples/the_matrix/src/main.rs b/examples/the_matrix/src/main.rs
index 0ed52dda..315e9ee5 100644
--- a/examples/the_matrix/src/main.rs
+++ b/examples/the_matrix/src/main.rs
@@ -1,5 +1,5 @@
use iced::mouse;
-use iced::time::{self, Instant};
+use iced::time::{self, milliseconds, Instant};
use iced::widget::canvas;
use iced::{
Color, Element, Fill, Font, Point, Rectangle, Renderer, Subscription, Theme,
@@ -36,11 +36,11 @@ impl TheMatrix {
}
fn view(&self) -> Element<Message> {
- canvas(self as &Self).width(Fill).height(Fill).into()
+ canvas(self).width(Fill).height(Fill).into()
}
fn subscription(&self) -> Subscription<Message> {
- time::every(std::time::Duration::from_millis(50)).map(Message::Tick)
+ time::every(milliseconds(50)).map(Message::Tick)
}
}
diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs
index a1b5886f..2ae9bfe2 100644
--- a/examples/toast/src/main.rs
+++ b/examples/toast/src/main.rs
@@ -162,7 +162,6 @@ impl Default for App {
mod toast {
use std::fmt;
- use std::time::{Duration, Instant};
use iced::advanced::layout::{self, Layout};
use iced::advanced::overlay;
@@ -171,6 +170,7 @@ mod toast {
use iced::advanced::{Clipboard, Shell, Widget};
use iced::mouse;
use iced::theme;
+ use iced::time::{self, Duration, Instant};
use iced::widget::{
button, column, container, horizontal_rule, horizontal_space, row, text,
};
@@ -502,9 +502,8 @@ mod toast {
self.instants.iter_mut().enumerate().for_each(
|(index, maybe_instant)| {
if let Some(instant) = maybe_instant.as_mut() {
- let remaining =
- Duration::from_secs(self.timeout_secs)
- .saturating_sub(instant.elapsed());
+ let remaining = time::seconds(self.timeout_secs)
+ .saturating_sub(instant.elapsed());
if remaining == Duration::ZERO {
maybe_instant.take();
diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml
index 0d72be86..16f4fdd2 100644
--- a/examples/todos/Cargo.toml
+++ b/examples/todos/Cargo.toml
@@ -20,12 +20,15 @@ tracing-subscriber = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced.workspace = true
-iced.features = ["debug", "webgl"]
+iced.features = ["debug", "webgl", "fira-sans"]
uuid = { version = "1.0", features = ["js"] }
web-sys = { workspace = true, features = ["Window", "Storage"] }
wasm-timer.workspace = true
+[dev-dependencies]
+iced_test.workspace = true
+
[package.metadata.deb]
assets = [
["target/release-opt/todos", "usr/bin/iced-todos", "755"],
diff --git a/examples/todos/snapshots/creates_a_new_task.sha256 b/examples/todos/snapshots/creates_a_new_task.sha256
new file mode 100644
index 00000000..193132c5
--- /dev/null
+++ b/examples/todos/snapshots/creates_a_new_task.sha256
@@ -0,0 +1 @@
+3160686067cb7c738802009cdf2f3c5f5a5bd8c89ada70517388b7adbe64c313 \ No newline at end of file
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index 25e3ead2..7759552c 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -1,6 +1,6 @@
use iced::keyboard;
use iced::widget::{
- self, button, center, checkbox, column, container, keyed_column, row,
+ self, button, center, center_x, checkbox, column, keyed_column, row,
scrollable, text, text_input, Text,
};
use iced::window;
@@ -15,7 +15,7 @@ pub fn main() -> iced::Result {
iced::application(Todos::title, Todos::update, Todos::view)
.subscription(Todos::subscription)
- .font(include_bytes!("../fonts/icons.ttf").as_slice())
+ .font(Todos::ICON_FONT)
.window_size((500.0, 800.0))
.run_with(Todos::new)
}
@@ -48,6 +48,8 @@ enum Message {
}
impl Todos {
+ const ICON_FONT: &'static [u8] = include_bytes!("../fonts/icons.ttf");
+
fn new() -> (Self, Command<Message>) {
(
Self::Loading,
@@ -147,9 +149,7 @@ impl Todos {
}
}
Message::ToggleFullscreen(mode) => window::get_latest()
- .and_then(move |window| {
- window::change_mode(window, mode)
- }),
+ .and_then(move |window| window::set_mode(window, mode)),
Message::Loaded(_) => Command::none(),
};
@@ -237,7 +237,7 @@ impl Todos {
.spacing(20)
.max_width(800);
- scrollable(container(content).center_x(Fill).padding(40)).into()
+ scrollable(center_x(content).padding(40)).into()
}
}
}
@@ -449,11 +449,10 @@ fn empty_message(message: &str) -> Element<'_, Message> {
}
// Fonts
-const ICONS: Font = Font::with_name("Iced-Todos-Icons");
fn icon(unicode: char) -> Text<'static> {
text(unicode.to_string())
- .font(ICONS)
+ .font(Font::with_name("Iced-Todos-Icons"))
.width(20)
.align_x(Center)
}
@@ -584,3 +583,49 @@ impl SavedState {
Ok(())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use iced::{Settings, Theme};
+ use iced_test::selector::text;
+ use iced_test::{Error, Simulator};
+
+ fn simulator(todos: &Todos) -> Simulator<Message> {
+ Simulator::with_settings(
+ Settings {
+ fonts: vec![Todos::ICON_FONT.into()],
+ ..Settings::default()
+ },
+ todos.view(),
+ )
+ }
+
+ #[test]
+ fn it_creates_a_new_task() -> Result<(), Error> {
+ let (mut todos, _command) = Todos::new();
+ let _command = todos.update(Message::Loaded(Err(LoadError::File)));
+
+ let mut ui = simulator(&todos);
+ let _input = ui.click("new-task")?;
+
+ let _ = ui.typewrite("Create the universe");
+ let _ = ui.tap_key(keyboard::key::Named::Enter);
+
+ for message in ui.into_messages() {
+ let _command = todos.update(message);
+ }
+
+ let mut ui = simulator(&todos);
+ let _ = ui.find(text("Create the universe"))?;
+
+ let snapshot = ui.snapshot(&Theme::Dark)?;
+ assert!(
+ snapshot.matches_hash("snapshots/creates_a_new_task")?,
+ "snapshots should match!"
+ );
+
+ Ok(())
+ }
+}
diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml
index 9e984ad1..719d355f 100644
--- a/examples/tour/Cargo.toml
+++ b/examples/tour/Cargo.toml
@@ -14,7 +14,7 @@ tracing-subscriber = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced.workspace = true
-iced.features = ["image", "debug", "webgl"]
+iced.features = ["image", "debug", "webgl", "fira-sans"]
console_error_panic_hook = "0.1"
console_log = "1.0"
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index d8c0b29a..32720c47 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -1,6 +1,6 @@
use iced::widget::{
- button, checkbox, column, container, horizontal_space, image, radio, row,
- scrollable, slider, text, text_input, toggler, vertical_space,
+ button, center_x, center_y, checkbox, column, horizontal_space, image,
+ radio, row, scrollable, slider, text, text_input, toggler, vertical_space,
};
use iced::widget::{Button, Column, Container, Slider};
use iced::{Center, Color, Element, Fill, Font, Pixels};
@@ -166,16 +166,13 @@ impl Tour {
.padding(20)
.into();
- let scrollable = scrollable(
- container(if self.debug {
- content.explain(Color::BLACK)
- } else {
- content
- })
- .center_x(Fill),
- );
+ let scrollable = scrollable(center_x(if self.debug {
+ content.explain(Color::BLACK)
+ } else {
+ content
+ }));
- container(scrollable).center_y(Fill).into()
+ center_y(scrollable).into()
}
fn can_continue(&self) -> bool {
@@ -543,7 +540,7 @@ fn ferris<'a>(
width: u16,
filter_method: image::FilterMethod,
) -> Container<'a, Message> {
- container(
+ center_x(
// This should go away once we unify resource loading on native
// platforms
if cfg!(target_arch = "wasm32") {
@@ -554,7 +551,6 @@ fn ferris<'a>(
.filter_method(filter_method)
.width(width),
)
- .center_x(Fill)
}
fn padded_button<Message: Clone>(label: &str) -> Button<'_, Message> {
diff --git a/examples/visible_bounds/Cargo.toml b/examples/visible_bounds/Cargo.toml
index 37594b84..1193334d 100644
--- a/examples/visible_bounds/Cargo.toml
+++ b/examples/visible_bounds/Cargo.toml
@@ -8,5 +8,3 @@ publish = false
[dependencies]
iced.workspace = true
iced.features = ["debug"]
-
-once_cell.workspace = true
diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs
index 77fec65e..f8f9f420 100644
--- a/examples/visible_bounds/src/main.rs
+++ b/examples/visible_bounds/src/main.rs
@@ -157,9 +157,9 @@ impl Example {
}
}
-use once_cell::sync::Lazy;
+use std::sync::LazyLock;
-static OUTER_CONTAINER: Lazy<container::Id> =
- Lazy::new(|| container::Id::new("outer"));
-static INNER_CONTAINER: Lazy<container::Id> =
- Lazy::new(|| container::Id::new("inner"));
+static OUTER_CONTAINER: LazyLock<container::Id> =
+ LazyLock::new(|| container::Id::new("outer"));
+static INNER_CONTAINER: LazyLock<container::Id> =
+ LazyLock::new(|| container::Id::new("inner"));
diff --git a/examples/websocket/Cargo.toml b/examples/websocket/Cargo.toml
index c7075fb3..787dbbe1 100644
--- a/examples/websocket/Cargo.toml
+++ b/examples/websocket/Cargo.toml
@@ -9,7 +9,6 @@ publish = false
iced.workspace = true
iced.features = ["debug", "tokio"]
-once_cell.workspace = true
warp = "0.3"
[dependencies.async-tungstenite]
diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs
index 8b1efb41..ae658471 100644
--- a/examples/websocket/src/main.rs
+++ b/examples/websocket/src/main.rs
@@ -4,7 +4,7 @@ use iced::widget::{
self, button, center, column, row, scrollable, text, text_input,
};
use iced::{color, Center, Element, Fill, Subscription, Task};
-use once_cell::sync::Lazy;
+use std::sync::LazyLock;
pub fn main() -> iced::Result {
iced::application("WebSocket - Iced", WebSocket::update, WebSocket::view)
@@ -138,4 +138,5 @@ enum State {
Connected(echo::Connection),
}
-static MESSAGE_LOG: Lazy<scrollable::Id> = Lazy::new(scrollable::Id::unique);
+static MESSAGE_LOG: LazyLock<scrollable::Id> =
+ LazyLock::new(scrollable::Id::unique);
diff --git a/futures/src/backend/native/tokio.rs b/futures/src/backend/native/tokio.rs
index 9dc3593d..e0be83a6 100644
--- a/futures/src/backend/native/tokio.rs
+++ b/futures/src/backend/native/tokio.rs
@@ -22,40 +22,25 @@ impl crate::Executor for Executor {
pub mod time {
//! Listen and react to time.
- use crate::subscription::{self, Hasher, Subscription};
+ use crate::core::time::{Duration, Instant};
+ use crate::stream;
+ use crate::subscription::Subscription;
+ use crate::MaybeSend;
+
+ use futures::SinkExt;
+ use std::future::Future;
/// 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(
- duration: std::time::Duration,
- ) -> Subscription<std::time::Instant> {
- subscription::from_recipe(Every(duration))
- }
-
- #[derive(Debug)]
- struct Every(std::time::Duration);
-
- impl subscription::Recipe for Every {
- type Output = std::time::Instant;
-
- fn hash(&self, state: &mut Hasher) {
- use std::hash::Hash;
-
- std::any::TypeId::of::<Self>().hash(state);
- self.0.hash(state);
- }
-
- fn stream(
- self: Box<Self>,
- _input: subscription::EventStream,
- ) -> futures::stream::BoxStream<'static, Self::Output> {
+ pub fn every(duration: Duration) -> Subscription<Instant> {
+ Subscription::run_with(duration, |duration| {
use futures::stream::StreamExt;
- let start = tokio::time::Instant::now() + self.0;
+ let start = tokio::time::Instant::now() + *duration;
- let mut interval = tokio::time::interval_at(start, self.0);
+ let mut interval = tokio::time::interval_at(start, *duration);
interval.set_missed_tick_behavior(
tokio::time::MissedTickBehavior::Skip,
);
@@ -67,6 +52,27 @@ pub mod time {
};
stream.map(tokio::time::Instant::into_std).boxed()
- }
+ })
+ }
+
+ /// Returns a [`Subscription`] that runs the given async function at a
+ /// set interval; producing the result of the function as output.
+ pub fn repeat<F, T>(f: fn() -> F, interval: Duration) -> Subscription<T>
+ where
+ F: Future<Output = T> + MaybeSend + 'static,
+ T: MaybeSend + 'static,
+ {
+ Subscription::run_with((f, interval), |(f, interval)| {
+ let f = *f;
+ let interval = *interval;
+
+ stream::channel(1, move |mut output| async move {
+ loop {
+ let _ = output.send(f().await).await;
+
+ tokio::time::sleep(interval).await;
+ }
+ })
+ })
}
}
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index eaea1a1f..82cba9a1 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -202,8 +202,8 @@ impl<T> Subscription<T> {
T: 'static,
{
from_recipe(Runner {
- id: builder,
- spawn: move |_| builder(),
+ data: builder,
+ spawn: |builder, _| builder(),
})
}
@@ -211,15 +211,15 @@ impl<T> Subscription<T> {
/// given [`Stream`].
///
/// The `id` will be used to uniquely identify the [`Subscription`].
- pub fn run_with_id<I, S>(id: I, stream: S) -> Subscription<T>
+ pub fn run_with<D, S>(data: D, builder: fn(&D) -> S) -> Self
where
- I: Hash + 'static,
+ D: Hash + 'static,
S: Stream<Item = T> + MaybeSend + 'static,
T: 'static,
{
from_recipe(Runner {
- id,
- spawn: move |_| stream,
+ data: (data, builder),
+ spawn: |(data, builder), _| builder(data),
})
}
@@ -423,8 +423,8 @@ where
T: 'static + MaybeSend,
{
from_recipe(Runner {
- id,
- spawn: |events| {
+ data: id,
+ spawn: |_, events| {
use futures::future;
use futures::stream::StreamExt;
@@ -435,27 +435,27 @@ where
struct Runner<I, F, S, T>
where
- F: FnOnce(EventStream) -> S,
+ F: FnOnce(&I, EventStream) -> S,
S: Stream<Item = T>,
{
- id: I,
+ data: I,
spawn: F,
}
impl<I, F, S, T> Recipe for Runner<I, F, S, T>
where
I: Hash + 'static,
- F: FnOnce(EventStream) -> S,
+ F: FnOnce(&I, EventStream) -> S,
S: Stream<Item = T> + MaybeSend + 'static,
{
type Output = T;
fn hash(&self, state: &mut Hasher) {
std::any::TypeId::of::<I>().hash(state);
- self.id.hash(state);
+ self.data.hash(state);
}
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
- crate::boxed_stream((self.spawn)(input))
+ crate::boxed_stream((self.spawn)(&self.data, input))
}
}
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index 7e2d767b..43191a59 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -33,7 +33,6 @@ bytemuck.workspace = true
cosmic-text.workspace = true
half.workspace = true
log.workspace = true
-once_cell.workspace = true
raw-window-handle.workspace = true
rustc-hash.workspace = true
thiserror.workspace = true
diff --git a/graphics/src/text.rs b/graphics/src/text.rs
index feb9932a..7694ff1f 100644
--- a/graphics/src/text.rs
+++ b/graphics/src/text.rs
@@ -14,9 +14,9 @@ use crate::core::font::{self, Font};
use crate::core::text::{Shaping, Wrapping};
use crate::core::{Color, Pixels, Point, Rectangle, Size, Transformation};
-use once_cell::sync::OnceCell;
use std::borrow::Cow;
-use std::sync::{Arc, RwLock, Weak};
+use std::collections::HashSet;
+use std::sync::{Arc, OnceLock, RwLock, Weak};
/// A text primitive.
#[derive(Debug, Clone, PartialEq)]
@@ -146,16 +146,17 @@ impl Text {
/// The regular variant of the [Fira Sans] font.
///
-/// It is loaded as part of the default fonts in Wasm builds.
+/// It is loaded as part of the default fonts when the `fira-sans`
+/// feature is enabled.
///
/// [Fira Sans]: https://mozilla.github.io/Fira/
-#[cfg(all(target_arch = "wasm32", feature = "fira-sans"))]
-pub const FIRA_SANS_REGULAR: &'static [u8] =
+#[cfg(feature = "fira-sans")]
+pub const FIRA_SANS_REGULAR: &[u8] =
include_bytes!("../fonts/FiraSans-Regular.ttf").as_slice();
/// Returns the global [`FontSystem`].
pub fn font_system() -> &'static RwLock<FontSystem> {
- static FONT_SYSTEM: OnceCell<RwLock<FontSystem>> = OnceCell::new();
+ static FONT_SYSTEM: OnceLock<RwLock<FontSystem>> = OnceLock::new();
FONT_SYSTEM.get_or_init(|| {
RwLock::new(FontSystem {
@@ -163,11 +164,12 @@ pub fn font_system() -> &'static RwLock<FontSystem> {
cosmic_text::fontdb::Source::Binary(Arc::new(
include_bytes!("../fonts/Iced-Icons.ttf").as_slice(),
)),
- #[cfg(all(target_arch = "wasm32", feature = "fira-sans"))]
+ #[cfg(feature = "fira-sans")]
cosmic_text::fontdb::Source::Binary(Arc::new(
include_bytes!("../fonts/FiraSans-Regular.ttf").as_slice(),
)),
]),
+ loaded_fonts: HashSet::new(),
version: Version::default(),
})
})
@@ -177,6 +179,7 @@ pub fn font_system() -> &'static RwLock<FontSystem> {
#[allow(missing_debug_implementations)]
pub struct FontSystem {
raw: cosmic_text::FontSystem,
+ loaded_fonts: HashSet<usize>,
version: Version,
}
@@ -188,6 +191,14 @@ impl FontSystem {
/// Loads a font from its bytes.
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
+ if let Cow::Borrowed(bytes) = bytes {
+ let address = bytes.as_ptr() as usize;
+
+ if !self.loaded_fonts.insert(address) {
+ return;
+ }
+ }
+
let _ = self.raw.db_mut().load_font_source(
cosmic_text::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
);
diff --git a/graphics/src/text/paragraph.rs b/graphics/src/text/paragraph.rs
index 07ddbb82..48c8e9e6 100644
--- a/graphics/src/text/paragraph.rs
+++ b/graphics/src/text/paragraph.rs
@@ -80,6 +80,8 @@ impl core::text::Paragraph for Paragraph {
Some(text.bounds.height),
);
+ buffer.set_wrap(font_system.raw(), text::to_wrap(text.wrapping));
+
buffer.set_text(
font_system.raw(),
text.content,
@@ -122,6 +124,8 @@ impl core::text::Paragraph for Paragraph {
Some(text.bounds.height),
);
+ buffer.set_wrap(font_system.raw(), text::to_wrap(text.wrapping));
+
buffer.set_rich_text(
font_system.raw(),
text.content.iter().enumerate().map(|(i, span)| {
diff --git a/highlighter/Cargo.toml b/highlighter/Cargo.toml
index 7962b89d..4c20a678 100644
--- a/highlighter/Cargo.toml
+++ b/highlighter/Cargo.toml
@@ -16,5 +16,4 @@ workspace = true
[dependencies]
iced_core.workspace = true
-once_cell.workspace = true
syntect.workspace = true
diff --git a/highlighter/src/lib.rs b/highlighter/src/lib.rs
index 83a15cb1..d2abc6b1 100644
--- a/highlighter/src/lib.rs
+++ b/highlighter/src/lib.rs
@@ -5,16 +5,16 @@ use crate::core::font::{self, Font};
use crate::core::text::highlighter::{self, Format};
use crate::core::Color;
-use once_cell::sync::Lazy;
use std::ops::Range;
+use std::sync::LazyLock;
use syntect::highlighting;
use syntect::parsing;
-static SYNTAXES: Lazy<parsing::SyntaxSet> =
- Lazy::new(parsing::SyntaxSet::load_defaults_nonewlines);
+static SYNTAXES: LazyLock<parsing::SyntaxSet> =
+ LazyLock::new(parsing::SyntaxSet::load_defaults_nonewlines);
-static THEMES: Lazy<highlighting::ThemeSet> =
- Lazy::new(highlighting::ThemeSet::load_defaults);
+static THEMES: LazyLock<highlighting::ThemeSet> =
+ LazyLock::new(highlighting::ThemeSet::load_defaults);
const LINES_PER_SNAPSHOT: usize = 50;
diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs
index 220542e1..ee20a458 100644
--- a/renderer/src/lib.rs
+++ b/renderer/src/lib.rs
@@ -23,6 +23,9 @@ pub type Compositor = renderer::Compositor;
#[cfg(all(feature = "wgpu", feature = "tiny-skia"))]
mod renderer {
+ use crate::core::renderer;
+ use crate::core::{Color, Font, Pixels, Size};
+
pub type Renderer = crate::fallback::Renderer<
iced_wgpu::Renderer,
iced_tiny_skia::Renderer,
@@ -32,6 +35,31 @@ mod renderer {
iced_wgpu::window::Compositor,
iced_tiny_skia::window::Compositor,
>;
+
+ impl renderer::Headless for Renderer {
+ fn new(default_font: Font, default_text_size: Pixels) -> Self {
+ Self::Secondary(iced_tiny_skia::Renderer::new(
+ default_font,
+ default_text_size,
+ ))
+ }
+
+ fn screenshot(
+ &mut self,
+ size: Size<u32>,
+ scale_factor: f32,
+ background_color: Color,
+ ) -> Vec<u8> {
+ match self {
+ crate::fallback::Renderer::Primary(_) => unreachable!(
+ "iced_wgpu does not support headless mode yet!"
+ ),
+ crate::fallback::Renderer::Secondary(renderer) => {
+ renderer.screenshot(size, scale_factor, background_color)
+ }
+ }
+ }
+ }
}
#[cfg(all(feature = "wgpu", not(feature = "tiny-skia")))]
diff --git a/runtime/src/window.rs b/runtime/src/window.rs
index 382f4518..183fab97 100644
--- a/runtime/src/window.rs
+++ b/runtime/src/window.rs
@@ -1,11 +1,8 @@
//! Build window-based GUI applications.
-pub mod screenshot;
-
-pub use screenshot::Screenshot;
-
use crate::core::time::Instant;
use crate::core::window::{
- Event, Icon, Id, Level, Mode, Settings, UserAttention,
+ Direction, Event, Icon, Id, Level, Mode, Screenshot, Settings,
+ UserAttention,
};
use crate::core::{Point, Size};
use crate::futures::event;
@@ -35,10 +32,17 @@ pub enum Action {
/// Move the window with the left mouse button until the button is
/// released.
///
- /// There’s no guarantee that this will work unless the left mouse
+ /// There's no guarantee that this will work unless the left mouse
/// button was pressed immediately before this function is called.
Drag(Id),
+ /// Resize the window with the left mouse button until the button is
+ /// released.
+ ///
+ /// There's no guarantee that this will work unless the left mouse
+ /// button was pressed immediately before this function is called.
+ DragResize(Id, Direction),
+
/// Resize the window to the given logical dimensions.
Resize(Id, Size),
@@ -72,7 +76,7 @@ pub enum Action {
Move(Id, Point),
/// Change the [`Mode`] of the window.
- ChangeMode(Id, Mode),
+ SetMode(Id, Mode),
/// Get the current [`Mode`] of the window.
GetMode(Id, oneshot::Sender<Mode>),
@@ -115,7 +119,7 @@ pub enum Action {
GainFocus(Id),
/// Change the window [`Level`].
- ChangeLevel(Id, Level),
+ SetLevel(Id, Level),
/// Show the system menu at cursor position.
///
@@ -140,7 +144,7 @@ pub enum Action {
///
/// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That
/// said, it's usually in the same ballpark as on Windows.
- ChangeIcon(Id, Icon),
+ SetIcon(Id, Icon),
/// Runs the closure with the native window handle of the window with the given [`Id`].
RunWithHandle(Id, Box<dyn FnOnce(WindowHandle<'_>) + Send>),
@@ -159,6 +163,18 @@ pub enum Action {
/// This enables mouse events for the window and stops mouse events
/// from being passed to whatever is underneath.
DisableMousePassthrough(Id),
+
+ /// Set the minimum inner window size.
+ SetMinSize(Id, Option<Size>),
+
+ /// Set the maximum inner window size.
+ SetMaxSize(Id, Option<Size>),
+
+ /// Set the window to be resizable or not.
+ SetResizable(Id, bool),
+
+ /// Set the window size increment.
+ SetResizeIncrements(Id, Option<Size>),
}
/// Subscribes to the frames of the window of the running application.
@@ -264,11 +280,40 @@ pub fn drag<T>(id: Id) -> Task<T> {
task::effect(crate::Action::Window(Action::Drag(id)))
}
+/// Begins resizing the window while the left mouse button is held.
+pub fn drag_resize<T>(id: Id, direction: Direction) -> Task<T> {
+ task::effect(crate::Action::Window(Action::DragResize(id, direction)))
+}
+
/// Resizes the window to the given logical dimensions.
pub fn resize<T>(id: Id, new_size: Size) -> Task<T> {
task::effect(crate::Action::Window(Action::Resize(id, new_size)))
}
+/// Set the window to be resizable or not.
+pub fn set_resizable<T>(id: Id, resizable: bool) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetResizable(id, resizable)))
+}
+
+/// Set the inner maximum size of the window.
+pub fn set_max_size<T>(id: Id, size: Option<Size>) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetMaxSize(id, size)))
+}
+
+/// Set the inner minimum size of the window.
+pub fn set_min_size<T>(id: Id, size: Option<Size>) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetMinSize(id, size)))
+}
+
+/// Set the window size increment.
+///
+/// This is usually used by apps such as terminal emulators that need "blocky" resizing.
+pub fn set_resize_increments<T>(id: Id, increments: Option<Size>) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetResizeIncrements(
+ id, increments,
+ )))
+}
+
/// Get the window's size in logical dimensions.
pub fn get_size(id: Id) -> Task<Size> {
task::oneshot(move |channel| {
@@ -319,11 +364,6 @@ pub fn move_to<T>(id: Id, position: Point) -> Task<T> {
task::effect(crate::Action::Window(Action::Move(id, position)))
}
-/// Changes the [`Mode`] of the window.
-pub fn change_mode<T>(id: Id, mode: Mode) -> Task<T> {
- task::effect(crate::Action::Window(Action::ChangeMode(id, mode)))
-}
-
/// Gets the current [`Mode`] of the window.
pub fn get_mode(id: Id) -> Task<Mode> {
task::oneshot(move |channel| {
@@ -331,6 +371,11 @@ pub fn get_mode(id: Id) -> Task<Mode> {
})
}
+/// Changes the [`Mode`] of the window.
+pub fn set_mode<T>(id: Id, mode: Mode) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetMode(id, mode)))
+}
+
/// Toggles the window to maximized or back.
pub fn toggle_maximize<T>(id: Id) -> Task<T> {
task::effect(crate::Action::Window(Action::ToggleMaximize(id)))
@@ -368,8 +413,8 @@ pub fn gain_focus<T>(id: Id) -> Task<T> {
}
/// Changes the window [`Level`].
-pub fn change_level<T>(id: Id, level: Level) -> Task<T> {
- task::effect(crate::Action::Window(Action::ChangeLevel(id, level)))
+pub fn set_level<T>(id: Id, level: Level) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetLevel(id, level)))
}
/// Show the [system menu] at cursor position.
@@ -388,8 +433,8 @@ pub fn get_raw_id<Message>(id: Id) -> Task<u64> {
}
/// Changes the [`Icon`] of the window.
-pub fn change_icon<T>(id: Id, icon: Icon) -> Task<T> {
- task::effect(crate::Action::Window(Action::ChangeIcon(id, icon)))
+pub fn set_icon<T>(id: Id, icon: Icon) -> Task<T> {
+ task::effect(crate::Action::Window(Action::SetIcon(id, icon)))
}
/// Runs the given callback with the native window handle for the window with the given id.
diff --git a/src/application.rs b/src/application.rs
index 2ba764be..c79ed62b 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -31,6 +31,7 @@
//! }
//! ```
use crate::program::{self, Program};
+use crate::theme;
use crate::window;
use crate::{
Element, Executor, Font, Result, Settings, Size, Subscription, Task,
@@ -38,8 +39,6 @@ use crate::{
use std::borrow::Cow;
-pub use crate::shell::program::{Appearance, DefaultStyle};
-
/// Creates an iced [`Application`] given its title, update, and view logic.
///
/// # Example
@@ -76,7 +75,7 @@ pub fn application<State, Message, Theme, Renderer>(
where
State: 'static,
Message: Send + std::fmt::Debug + 'static,
- Theme: Default + DefaultStyle,
+ Theme: Default + theme::Base,
Renderer: program::Renderer,
{
use std::marker::PhantomData;
@@ -94,7 +93,7 @@ where
for Instance<State, Message, Theme, Renderer, Update, View>
where
Message: Send + std::fmt::Debug + 'static,
- Theme: Default + DefaultStyle,
+ Theme: Default + theme::Base,
Renderer: program::Renderer,
Update: self::Update<State, Message>,
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
@@ -352,7 +351,7 @@ impl<P: Program> Application<P> {
/// Sets the style logic of the [`Application`].
pub fn style(
self,
- f: impl Fn(&P::State, &P::Theme) -> Appearance,
+ f: impl Fn(&P::State, &P::Theme) -> theme::Style,
) -> Application<
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
> {
diff --git a/src/daemon.rs b/src/daemon.rs
index 81254bf9..fd6d0278 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -1,13 +1,12 @@
//! Create and run daemons that run in the background.
use crate::application;
use crate::program::{self, Program};
+use crate::theme;
use crate::window;
use crate::{Element, Executor, Font, Result, Settings, Subscription, Task};
use std::borrow::Cow;
-pub use crate::shell::program::{Appearance, DefaultStyle};
-
/// Creates an iced [`Daemon`] given its title, update, and view logic.
///
/// A [`Daemon`] will not open a window by default, but will run silently
@@ -26,7 +25,7 @@ pub fn daemon<State, Message, Theme, Renderer>(
where
State: 'static,
Message: Send + std::fmt::Debug + 'static,
- Theme: Default + DefaultStyle,
+ Theme: Default + theme::Base,
Renderer: program::Renderer,
{
use std::marker::PhantomData;
@@ -44,7 +43,7 @@ where
for Instance<State, Message, Theme, Renderer, Update, View>
where
Message: Send + std::fmt::Debug + 'static,
- Theme: Default + DefaultStyle,
+ Theme: Default + theme::Base,
Renderer: program::Renderer,
Update: application::Update<State, Message>,
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
@@ -201,7 +200,7 @@ impl<P: Program> Daemon<P> {
/// Sets the style logic of the [`Daemon`].
pub fn style(
self,
- f: impl Fn(&P::State, &P::Theme) -> Appearance,
+ f: impl Fn(&P::State, &P::Theme) -> theme::Style,
) -> Daemon<
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
> {
diff --git a/src/lib.rs b/src/lib.rs
index 8f526cfd..939943a9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -365,9 +365,9 @@
//!
//! As with tasks, some modules expose convenient functions that build a [`Subscription`] for you—like
//! [`time::every`] which can be used to listen to time, or [`keyboard::on_key_press`] which will notify you
-//! of any key presses. But you can also create your own with [`Subscription::run`] and [`run_with_id`].
+//! of any key presses. But you can also create your own with [`Subscription::run`] and [`run_with`].
//!
-//! [`run_with_id`]: Subscription::run_with_id
+//! [`run_with`]: Subscription::run_with
//!
//! ## Scaling Applications
//! The `update`, `view`, and `Message` triplet composes very nicely.
@@ -491,7 +491,6 @@ mod program;
pub mod application;
pub mod daemon;
-pub mod settings;
pub mod time;
pub mod window;
@@ -506,8 +505,8 @@ pub use crate::core::padding;
pub use crate::core::theme;
pub use crate::core::{
Alignment, Background, Border, Color, ContentFit, Degrees, Gradient,
- Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Shadow, Size,
- Theme, Transformation, Vector,
+ Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Settings,
+ Shadow, Size, Theme, Transformation, Vector,
};
pub use crate::runtime::exit;
pub use iced_futures::Subscription;
@@ -624,8 +623,8 @@ pub use error::Error;
pub use event::Event;
pub use executor::Executor;
pub use font::Font;
+pub use program::Program;
pub use renderer::Renderer;
-pub use settings::Settings;
pub use task::Task;
#[doc(inline)]
@@ -686,7 +685,7 @@ pub fn run<State, Message, Theme, Renderer>(
where
State: Default + 'static,
Message: std::fmt::Debug + Send + 'static,
- Theme: Default + program::DefaultStyle + 'static,
+ Theme: Default + theme::Base + 'static,
Renderer: program::Renderer + 'static,
{
application(title, update, view).run()
diff --git a/src/program.rs b/src/program.rs
index 94cb9a7d..ace4da74 100644
--- a/src/program.rs
+++ b/src/program.rs
@@ -1,11 +1,10 @@
use crate::core::text;
use crate::graphics::compositor;
use crate::shell;
+use crate::theme;
use crate::window;
use crate::{Element, Executor, Result, Settings, Subscription, Task};
-pub use crate::shell::program::{Appearance, DefaultStyle};
-
/// The internal definition of a [`Program`].
///
/// You should not need to implement this trait directly. Instead, use the
@@ -19,7 +18,7 @@ pub trait Program: Sized {
type Message: Send + std::fmt::Debug + 'static;
/// The theme of the program.
- type Theme: Default + DefaultStyle;
+ type Theme: Default + theme::Base;
/// The renderer of the program.
type Renderer: Renderer;
@@ -51,11 +50,11 @@ pub trait Program: Sized {
}
fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme {
- Self::Theme::default()
+ <Self::Theme as Default>::default()
}
- fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance {
- DefaultStyle::default_style(theme)
+ fn style(&self, _state: &Self::State, theme: &Self::Theme) -> theme::Style {
+ theme::Base::base(theme)
}
fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 {
@@ -153,7 +152,7 @@ pub trait Program: Sized {
self.program.theme(&self.state, window)
}
- fn style(&self, theme: &Self::Theme) -> Appearance {
+ fn style(&self, theme: &Self::Theme) -> theme::Style {
self.program.style(&self.state, theme)
}
@@ -252,7 +251,7 @@ pub fn with_title<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
- ) -> Appearance {
+ ) -> theme::Style {
self.program.style(state, theme)
}
@@ -322,7 +321,7 @@ pub fn with_subscription<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
- ) -> Appearance {
+ ) -> theme::Style {
self.program.style(state, theme)
}
@@ -395,7 +394,7 @@ pub fn with_theme<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
- ) -> Appearance {
+ ) -> theme::Style {
self.program.style(state, theme)
}
@@ -409,7 +408,7 @@ pub fn with_theme<P: Program>(
pub fn with_style<P: Program>(
program: P,
- f: impl Fn(&P::State, &P::Theme) -> Appearance,
+ f: impl Fn(&P::State, &P::Theme) -> theme::Style,
) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
struct WithStyle<P, F> {
program: P,
@@ -418,7 +417,7 @@ pub fn with_style<P: Program>(
impl<P: Program, F> Program for WithStyle<P, F>
where
- F: Fn(&P::State, &P::Theme) -> Appearance,
+ F: Fn(&P::State, &P::Theme) -> theme::Style,
{
type State = P::State;
type Message = P::Message;
@@ -430,7 +429,7 @@ pub fn with_style<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
- ) -> Appearance {
+ ) -> theme::Style {
(self.style)(state, theme)
}
@@ -535,7 +534,7 @@ pub fn with_scale_factor<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
- ) -> Appearance {
+ ) -> theme::Style {
self.program.style(state, theme)
}
@@ -609,7 +608,7 @@ pub fn with_executor<P: Program, E: Executor>(
&self,
state: &Self::State,
theme: &Self::Theme,
- ) -> Appearance {
+ ) -> theme::Style {
self.program.style(state, theme)
}
diff --git a/src/time.rs b/src/time.rs
index 26d31c0a..98a800ac 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -1,5 +1,5 @@
//! Listen and react to time.
-pub use crate::core::time::{Duration, Instant};
+pub use crate::core::time::*;
#[allow(unused_imports)]
#[cfg_attr(
diff --git a/src/window/icon.rs b/src/window/icon.rs
index 7fe4ca7b..f453d580 100644
--- a/src/window/icon.rs
+++ b/src/window/icon.rs
@@ -13,7 +13,7 @@ use std::path::Path;
/// This will return an error in case the file is missing at run-time. You may prefer [`from_file_data`] instead.
#[cfg(feature = "image")]
pub fn from_file<P: AsRef<Path>>(icon_path: P) -> Result<Icon, Error> {
- let icon = image::io::Reader::open(icon_path)?.decode()?.to_rgba8();
+ let icon = image::ImageReader::open(icon_path)?.decode()?.to_rgba8();
Ok(icon::from_rgba(icon.to_vec(), icon.width(), icon.height())?)
}
@@ -27,7 +27,7 @@ pub fn from_file_data(
data: &[u8],
explicit_format: Option<image::ImageFormat>,
) -> Result<Icon, Error> {
- let mut icon = image::io::Reader::new(std::io::Cursor::new(data));
+ let mut icon = image::ImageReader::new(std::io::Cursor::new(data));
let icon_with_format = match explicit_format {
Some(format) => {
diff --git a/test/Cargo.toml b/test/Cargo.toml
new file mode 100644
index 00000000..2dd35e7f
--- /dev/null
+++ b/test/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "iced_test"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+categories.workspace = true
+keywords.workspace = true
+rust-version.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+iced_runtime.workspace = true
+
+iced_renderer.workspace = true
+iced_renderer.features = ["fira-sans"]
+
+png.workspace = true
+sha2.workspace = true
+thiserror.workspace = true
diff --git a/test/src/lib.rs b/test/src/lib.rs
new file mode 100644
index 00000000..73726f08
--- /dev/null
+++ b/test/src/lib.rs
@@ -0,0 +1,637 @@
+//! Test your `iced` applications in headless mode.
+//!
+//! # Basic Usage
+//! Let's assume we want to test [the classical counter interface].
+//!
+//! First, we will want to create a [`Simulator`] of our interface:
+//!
+//! ```rust,no_run
+//! # struct Counter { value: i64 }
+//! # impl Counter {
+//! # pub fn view(&self) -> iced_runtime::core::Element<(), iced_runtime::core::Theme, iced_renderer::Renderer> { unimplemented!() }
+//! # }
+//! use iced_test::simulator;
+//!
+//! let mut counter = Counter { value: 0 };
+//! let mut ui = simulator(counter.view());
+//! ```
+//!
+//! Now we can simulate a user interacting with our interface. Let's use [`Simulator::click`] to click
+//! the counter buttons:
+//!
+//! ```rust,no_run
+//! # struct Counter { value: i64 }
+//! # impl Counter {
+//! # pub fn view(&self) -> iced_runtime::core::Element<(), iced_runtime::core::Theme, iced_renderer::Renderer> { unimplemented!() }
+//! # }
+//! use iced_test::selector::text;
+//! # use iced_test::simulator;
+//! #
+//! # let mut counter = Counter { value: 0 };
+//! # let mut ui = simulator(counter.view());
+//!
+//! let _ = ui.click(text("+"));
+//! let _ = ui.click(text("+"));
+//! let _ = ui.click(text("-"));
+//! ```
+//!
+//! [`Simulator::click`] takes a [`Selector`]. A [`Selector`] describes a way to query the widgets of an interface. In this case,
+//! [`selector::text`] lets us select a widget by the text it contains.
+//!
+//! We can now process any messages produced by these interactions and then assert that the final value of our counter is
+//! indeed `1`!
+//!
+//! ```rust,no_run
+//! # struct Counter { value: i64 }
+//! # impl Counter {
+//! # pub fn update(&mut self, message: ()) {}
+//! # pub fn view(&self) -> iced_runtime::core::Element<(), iced_runtime::core::Theme, iced_renderer::Renderer> { unimplemented!() }
+//! # }
+//! # use iced_test::selector::text;
+//! # use iced_test::simulator;
+//! #
+//! # let mut counter = Counter { value: 0 };
+//! # let mut ui = simulator(counter.view());
+//! #
+//! # let _ = ui.click(text("+"));
+//! # let _ = ui.click(text("+"));
+//! # let _ = ui.click(text("-"));
+//! #
+//! for message in ui.into_messages() {
+//! counter.update(message);
+//! }
+//!
+//! assert_eq!(counter.value, 1);
+//! ```
+//!
+//! We can even rebuild the interface to make sure the counter _displays_ the proper value with [`Simulator::find`]:
+//!
+//! ```rust,no_run
+//! # struct Counter { value: i64 }
+//! # impl Counter {
+//! # pub fn view(&self) -> iced_runtime::core::Element<(), iced_runtime::core::Theme, iced_renderer::Renderer> { unimplemented!() }
+//! # }
+//! # use iced_test::selector::text;
+//! # use iced_test::simulator;
+//! #
+//! # let mut counter = Counter { value: 0 };
+//! let mut ui = simulator(counter.view());
+//!
+//! assert!(ui.find(text("1")).is_ok(), "Counter should display 1!");
+//! ```
+//!
+//! And that's it! That's the gist of testing `iced` applications!
+//!
+//! [`Simulator`] contains additional operations you can use to simulate more interactions—like [`tap_key`](Simulator::tap_key) or
+//! [`typewrite`](Simulator::typewrite)—and even perform [_snapshot testing_](Simulator::snapshot)!
+//!
+//! [the classical counter interface]: https://book.iced.rs/architecture.html#dissecting-an-interface
+pub mod selector;
+
+pub use selector::Selector;
+
+use iced_renderer as renderer;
+use iced_runtime as runtime;
+use iced_runtime::core;
+
+use crate::core::clipboard;
+use crate::core::event;
+use crate::core::keyboard;
+use crate::core::mouse;
+use crate::core::theme;
+use crate::core::time;
+use crate::core::widget;
+use crate::core::window;
+use crate::core::{
+ Element, Event, Font, Point, Rectangle, Settings, Size, SmolStr,
+};
+use crate::runtime::user_interface;
+use crate::runtime::UserInterface;
+
+use std::borrow::Cow;
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::sync::Arc;
+
+/// Creates a new [`Simulator`].
+///
+/// This is just a function version of [`Simulator::new`].
+pub fn simulator<'a, Message, Theme, Renderer>(
+ element: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Simulator<'a, Message, Theme, Renderer>
+where
+ Theme: theme::Base,
+ Renderer: core::Renderer + core::renderer::Headless,
+{
+ Simulator::new(element)
+}
+
+/// A user interface that can be interacted with and inspected programmatically.
+#[allow(missing_debug_implementations)]
+pub struct Simulator<
+ 'a,
+ Message,
+ Theme = core::Theme,
+ Renderer = renderer::Renderer,
+> {
+ raw: UserInterface<'a, Message, Theme, Renderer>,
+ renderer: Renderer,
+ size: Size,
+ cursor: mouse::Cursor,
+ messages: Vec<Message>,
+}
+
+/// A specific area of a [`Simulator`], normally containing a widget.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Target {
+ /// The bounds of the area.
+ pub bounds: Rectangle,
+}
+
+impl<'a, Message, Theme, Renderer> Simulator<'a, Message, Theme, Renderer>
+where
+ Theme: theme::Base,
+ Renderer: core::Renderer + core::renderer::Headless,
+{
+ /// Creates a new [`Simulator`] with default [`Settings`] and a default size (1024x768).
+ pub fn new(
+ element: impl Into<Element<'a, Message, Theme, Renderer>>,
+ ) -> Self {
+ Self::with_settings(Settings::default(), element)
+ }
+
+ /// Creates a new [`Simulator`] with the given [`Settings`] and a default size (1024x768).
+ pub fn with_settings(
+ settings: Settings,
+ element: impl Into<Element<'a, Message, Theme, Renderer>>,
+ ) -> Self {
+ Self::with_size(settings, window::Settings::default().size, element)
+ }
+
+ /// Creates a new [`Simulator`] with the given [`Settings`] and size.
+ pub fn with_size(
+ settings: Settings,
+ size: impl Into<Size>,
+ element: impl Into<Element<'a, Message, Theme, Renderer>>,
+ ) -> Self {
+ let size = size.into();
+
+ let default_font = match settings.default_font {
+ Font::DEFAULT => Font::with_name("Fira Sans"),
+ _ => settings.default_font,
+ };
+
+ for font in settings.fonts {
+ load_font(font).expect("Font must be valid");
+ }
+
+ let mut renderer =
+ Renderer::new(default_font, settings.default_text_size);
+
+ let raw = UserInterface::build(
+ element,
+ size,
+ user_interface::Cache::default(),
+ &mut renderer,
+ );
+
+ Simulator {
+ raw,
+ renderer,
+ size,
+ cursor: mouse::Cursor::Unavailable,
+ messages: Vec::new(),
+ }
+ }
+
+ /// Finds the [`Target`] of the given widget [`Selector`] in the [`Simulator`].
+ pub fn find(
+ &mut self,
+ selector: impl Into<Selector>,
+ ) -> Result<Target, Error> {
+ let selector = selector.into();
+
+ match &selector {
+ Selector::Id(id) => {
+ struct FindById<'a> {
+ id: &'a widget::Id,
+ target: Option<Target>,
+ }
+
+ impl widget::Operation for FindById<'_> {
+ fn container(
+ &mut self,
+ id: Option<&widget::Id>,
+ bounds: Rectangle,
+ operate_on_children: &mut dyn FnMut(
+ &mut dyn widget::Operation<()>,
+ ),
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ if Some(self.id) == id {
+ self.target = Some(Target { bounds });
+ return;
+ }
+
+ operate_on_children(self);
+ }
+
+ fn scrollable(
+ &mut self,
+ id: Option<&widget::Id>,
+ bounds: Rectangle,
+ _content_bounds: Rectangle,
+ _translation: core::Vector,
+ _state: &mut dyn widget::operation::Scrollable,
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ if Some(self.id) == id {
+ self.target = Some(Target { bounds });
+ }
+ }
+
+ fn text_input(
+ &mut self,
+ id: Option<&widget::Id>,
+ bounds: Rectangle,
+ _state: &mut dyn widget::operation::TextInput,
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ if Some(self.id) == id {
+ self.target = Some(Target { bounds });
+ }
+ }
+
+ fn text(
+ &mut self,
+ id: Option<&widget::Id>,
+ bounds: Rectangle,
+ _text: &str,
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ if Some(self.id) == id {
+ self.target = Some(Target { bounds });
+ }
+ }
+
+ fn custom(
+ &mut self,
+ id: Option<&widget::Id>,
+ bounds: Rectangle,
+ _state: &mut dyn std::any::Any,
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ if Some(self.id) == id {
+ self.target = Some(Target { bounds });
+ }
+ }
+ }
+
+ let mut find = FindById { id, target: None };
+ self.raw.operate(&self.renderer, &mut find);
+
+ find.target.ok_or(Error::NotFound(selector))
+ }
+ Selector::Text(text) => {
+ struct FindByText<'a> {
+ text: &'a str,
+ target: Option<Target>,
+ }
+
+ impl widget::Operation for FindByText<'_> {
+ fn container(
+ &mut self,
+ _id: Option<&widget::Id>,
+ _bounds: Rectangle,
+ operate_on_children: &mut dyn FnMut(
+ &mut dyn widget::Operation<()>,
+ ),
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ operate_on_children(self);
+ }
+
+ fn text(
+ &mut self,
+ _id: Option<&widget::Id>,
+ bounds: Rectangle,
+ text: &str,
+ ) {
+ if self.target.is_some() {
+ return;
+ }
+
+ if self.text == text {
+ self.target = Some(Target { bounds });
+ }
+ }
+ }
+
+ let mut find = FindByText { text, target: None };
+ self.raw.operate(&self.renderer, &mut find);
+
+ find.target.ok_or(Error::NotFound(selector))
+ }
+ }
+ }
+
+ /// Points the mouse cursor at the given position in the [`Simulator`].
+ ///
+ /// This does _not_ produce mouse movement events!
+ pub fn point_at(&mut self, position: impl Into<Point>) {
+ self.cursor = mouse::Cursor::Available(position.into());
+ }
+
+ /// Clicks the [`Target`] found by the given [`Selector`], if any.
+ ///
+ /// This consists in:
+ /// - Pointing the mouse cursor at the center of the [`Target`].
+ /// - Simulating a [`click`].
+ pub fn click(
+ &mut self,
+ selector: impl Into<Selector>,
+ ) -> Result<Target, Error> {
+ let target = self.find(selector)?;
+ self.point_at(target.bounds.center());
+
+ let _ = self.simulate(click());
+
+ Ok(target)
+ }
+
+ /// Simulates a key press, followed by a release, in the [`Simulator`].
+ pub fn tap_key(&mut self, key: impl Into<keyboard::Key>) -> event::Status {
+ self.simulate(tap_key(key, None))
+ .first()
+ .copied()
+ .unwrap_or(event::Status::Ignored)
+ }
+
+ /// Simulates a user typing in the keyboard the given text in the [`Simulator`].
+ pub fn typewrite(&mut self, text: &str) -> event::Status {
+ let statuses = self.simulate(typewrite(text));
+
+ statuses
+ .into_iter()
+ .fold(event::Status::Ignored, event::Status::merge)
+ }
+
+ /// Simulates the given raw sequence of events in the [`Simulator`].
+ pub fn simulate(
+ &mut self,
+ events: impl IntoIterator<Item = Event>,
+ ) -> Vec<event::Status> {
+ let events: Vec<Event> = events.into_iter().collect();
+
+ let (_state, statuses) = self.raw.update(
+ &events,
+ self.cursor,
+ &mut self.renderer,
+ &mut clipboard::Null,
+ &mut self.messages,
+ );
+
+ statuses
+ }
+
+ /// Draws and takes a [`Snapshot`] of the interface in the [`Simulator`].
+ pub fn snapshot(&mut self, theme: &Theme) -> Result<Snapshot, Error> {
+ let base = theme.base();
+
+ let _ = self.raw.update(
+ &[Event::Window(window::Event::RedrawRequested(
+ time::Instant::now(),
+ ))],
+ self.cursor,
+ &mut self.renderer,
+ &mut clipboard::Null,
+ &mut self.messages,
+ );
+
+ let _ = self.raw.draw(
+ &mut self.renderer,
+ theme,
+ &core::renderer::Style {
+ text_color: base.text_color,
+ },
+ self.cursor,
+ );
+
+ let scale_factor = 2.0;
+
+ let physical_size = Size::new(
+ (self.size.width * scale_factor).round() as u32,
+ (self.size.height * scale_factor).round() as u32,
+ );
+
+ let rgba = self.renderer.screenshot(
+ physical_size,
+ scale_factor,
+ base.background_color,
+ );
+
+ Ok(Snapshot {
+ screenshot: window::Screenshot::new(
+ rgba,
+ physical_size,
+ f64::from(scale_factor),
+ ),
+ })
+ }
+
+ /// Turns the [`Simulator`] into the sequence of messages produced by any interactions.
+ pub fn into_messages(self) -> impl Iterator<Item = Message> {
+ self.messages.into_iter()
+ }
+}
+
+/// A frame of a user interface rendered by a [`Simulator`].
+#[derive(Debug, Clone)]
+pub struct Snapshot {
+ screenshot: window::Screenshot,
+}
+
+impl Snapshot {
+ /// Compares the [`Snapshot`] with the PNG image found in the given path, returning
+ /// `true` if they are identical.
+ ///
+ /// If the PNG image does not exist, it will be created by the [`Snapshot`] for future
+ /// testing and `true` will be returned.
+ pub fn matches_image(&self, path: impl AsRef<Path>) -> Result<bool, Error> {
+ let path = snapshot_path(path, "png");
+
+ if path.exists() {
+ let file = fs::File::open(&path)?;
+ let decoder = png::Decoder::new(file);
+
+ let mut reader = decoder.read_info()?;
+ let mut bytes = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut bytes)?;
+
+ Ok(self.screenshot.bytes == bytes[..info.buffer_size()])
+ } else {
+ if let Some(directory) = path.parent() {
+ fs::create_dir_all(directory)?;
+ }
+
+ let file = fs::File::create(path)?;
+
+ let mut encoder = png::Encoder::new(
+ file,
+ self.screenshot.size.width,
+ self.screenshot.size.height,
+ );
+ encoder.set_color(png::ColorType::Rgba);
+
+ let mut writer = encoder.write_header()?;
+ writer.write_image_data(&self.screenshot.bytes)?;
+ writer.finish()?;
+
+ Ok(true)
+ }
+ }
+
+ /// Compares the [`Snapshot`] with the SHA-256 hash file found in the given path, returning
+ /// `true` if they are identical.
+ ///
+ /// If the hash file does not exist, it will be created by the [`Snapshot`] for future
+ /// testing and `true` will be returned.
+ pub fn matches_hash(&self, path: impl AsRef<Path>) -> Result<bool, Error> {
+ use sha2::{Digest, Sha256};
+
+ let path = snapshot_path(path, "sha256");
+
+ let hash = {
+ let mut hasher = Sha256::new();
+ hasher.update(&self.screenshot.bytes);
+ format!("{:x}", hasher.finalize())
+ };
+
+ if path.exists() {
+ let saved_hash = fs::read_to_string(&path)?;
+
+ Ok(hash == saved_hash)
+ } else {
+ if let Some(directory) = path.parent() {
+ fs::create_dir_all(directory)?;
+ }
+
+ fs::write(path, hash)?;
+ Ok(true)
+ }
+ }
+}
+
+/// Returns the sequence of events of a click.
+pub fn click() -> impl Iterator<Item = Event> {
+ [
+ Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
+ Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
+ ]
+ .into_iter()
+}
+
+/// Returns the sequence of events of a "key tap" (i.e. pressing and releasing a key).
+pub fn tap_key(
+ key: impl Into<keyboard::Key>,
+ text: Option<SmolStr>,
+) -> impl Iterator<Item = Event> {
+ let key = key.into();
+
+ [
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ key: key.clone(),
+ modified_key: key.clone(),
+ physical_key: keyboard::key::Physical::Unidentified(
+ keyboard::key::NativeCode::Unidentified,
+ ),
+ location: keyboard::Location::Standard,
+ modifiers: keyboard::Modifiers::default(),
+ text,
+ }),
+ Event::Keyboard(keyboard::Event::KeyReleased {
+ key: key.clone(),
+ modified_key: key,
+ physical_key: keyboard::key::Physical::Unidentified(
+ keyboard::key::NativeCode::Unidentified,
+ ),
+ location: keyboard::Location::Standard,
+ modifiers: keyboard::Modifiers::default(),
+ }),
+ ]
+ .into_iter()
+}
+
+/// Returns the sequence of events of typewriting the given text in a keyboard.
+pub fn typewrite(text: &str) -> impl Iterator<Item = Event> + '_ {
+ text.chars()
+ .map(|c| SmolStr::new_inline(&c.to_string()))
+ .flat_map(|c| tap_key(keyboard::Key::Character(c.clone()), Some(c)))
+}
+
+/// A test error.
+#[derive(Debug, Clone, thiserror::Error)]
+pub enum Error {
+ /// No matching widget was found for the [`Selector`].
+ #[error("no matching widget was found for the selector: {0:?}")]
+ NotFound(Selector),
+ /// An IO operation failed.
+ #[error("an IO operation failed: {0}")]
+ IOFailed(Arc<io::Error>),
+ /// The decoding of some PNG image failed.
+ #[error("the decoding of some PNG image failed: {0}")]
+ PngDecodingFailed(Arc<png::DecodingError>),
+ /// The encoding of some PNG image failed.
+ #[error("the encoding of some PNG image failed: {0}")]
+ PngEncodingFailed(Arc<png::EncodingError>),
+}
+
+impl From<io::Error> for Error {
+ fn from(error: io::Error) -> Self {
+ Self::IOFailed(Arc::new(error))
+ }
+}
+
+impl From<png::DecodingError> for Error {
+ fn from(error: png::DecodingError) -> Self {
+ Self::PngDecodingFailed(Arc::new(error))
+ }
+}
+
+impl From<png::EncodingError> for Error {
+ fn from(error: png::EncodingError) -> Self {
+ Self::PngEncodingFailed(Arc::new(error))
+ }
+}
+
+fn load_font(font: impl Into<Cow<'static, [u8]>>) -> Result<(), Error> {
+ renderer::graphics::text::font_system()
+ .write()
+ .expect("Write to font system")
+ .load_font(font.into());
+
+ Ok(())
+}
+
+fn snapshot_path(path: impl AsRef<Path>, extension: &str) -> PathBuf {
+ path.as_ref().with_extension(extension)
+}
diff --git a/test/src/selector.rs b/test/src/selector.rs
new file mode 100644
index 00000000..7b8dcb7e
--- /dev/null
+++ b/test/src/selector.rs
@@ -0,0 +1,29 @@
+//! Select widgets of a user interface.
+use crate::core::text;
+use crate::core::widget;
+
+/// A selector describes a strategy to find a certain widget in a user interface.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Selector {
+ /// Find the widget with the given [`widget::Id`].
+ Id(widget::Id),
+ /// Find the widget containing the given [`text::Fragment`].
+ Text(text::Fragment<'static>),
+}
+
+impl From<widget::Id> for Selector {
+ fn from(id: widget::Id) -> Self {
+ Self::Id(id)
+ }
+}
+
+impl From<&'static str> for Selector {
+ fn from(id: &'static str) -> Self {
+ Self::Id(widget::Id::new(id))
+ }
+}
+
+/// Creates [`Selector`] that finds the widget containing the given text fragment.
+pub fn text(fragment: impl text::IntoFragment<'static>) -> Selector {
+ Selector::Text(fragment.into_fragment())
+}
diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs
index 758921d4..a42f1de4 100644
--- a/tiny_skia/src/lib.rs
+++ b/tiny_skia/src/lib.rs
@@ -29,7 +29,7 @@ pub use geometry::Geometry;
use crate::core::renderer;
use crate::core::{
- Background, Color, Font, Pixels, Point, Rectangle, Transformation,
+ Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
};
use crate::engine::Engine;
use crate::graphics::compositor;
@@ -405,3 +405,26 @@ impl core::svg::Renderer for Renderer {
impl compositor::Default for Renderer {
type Compositor = window::Compositor;
}
+
+impl renderer::Headless for Renderer {
+ fn new(default_font: Font, default_text_size: Pixels) -> Self {
+ Self::new(default_font, default_text_size)
+ }
+
+ fn screenshot(
+ &mut self,
+ size: Size<u32>,
+ scale_factor: f32,
+ background_color: Color,
+ ) -> Vec<u8> {
+ let viewport =
+ Viewport::with_physical_size(size, f64::from(scale_factor));
+
+ window::compositor::screenshot::<&str>(
+ self,
+ &viewport,
+ background_color,
+ &[],
+ )
+ }
+}
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index a8ebf3aa..4b6b0483 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -35,7 +35,6 @@ glam.workspace = true
glyphon.workspace = true
guillotiere.workspace = true
log.workspace = true
-once_cell.workspace = true
rustc-hash.workspace = true
thiserror.workspace = true
wgpu.workspace = true
diff --git a/widget/Cargo.toml b/widget/Cargo.toml
index 98a81145..e19cad08 100644
--- a/widget/Cargo.toml
+++ b/widget/Cargo.toml
@@ -33,7 +33,6 @@ iced_renderer.workspace = true
iced_runtime.workspace = true
num-traits.workspace = true
-once_cell.workspace = true
rustc-hash.workspace = true
thiserror.workspace = true
unicode-segmentation.workspace = true
diff --git a/widget/src/button.rs b/widget/src/button.rs
index a2f9945c..11839d5e 100644
--- a/widget/src/button.rs
+++ b/widget/src/button.rs
@@ -633,6 +633,21 @@ pub fn success(theme: &Theme, status: Status) -> Style {
}
}
+/// A warning button; denoting a risky action.
+pub fn warning(theme: &Theme, status: Status) -> Style {
+ let palette = theme.extended_palette();
+ let base = styled(palette.warning.base);
+
+ match status {
+ Status::Active | Status::Pressed => base,
+ Status::Hovered => Style {
+ background: Some(Background::Color(palette.warning.strong.color)),
+ ..base
+ },
+ Status::Disabled => disabled(base),
+ }
+}
+
/// A danger button; denoting a destructive action.
pub fn danger(theme: &Theme, status: Status) -> Style {
let palette = theme.extended_palette();
diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs
index 3686d34c..663bfad1 100644
--- a/widget/src/checkbox.rs
+++ b/widget/src/checkbox.rs
@@ -445,6 +445,16 @@ where
);
}
}
+
+ fn operate(
+ &self,
+ _state: &mut Tree,
+ layout: Layout<'_>,
+ _renderer: &Renderer,
+ operation: &mut dyn widget::Operation,
+ ) {
+ operation.text(None, layout.bounds(), &self.label);
+ }
}
impl<'a, Message, Theme, Renderer> From<Checkbox<'a, Message, Theme, Renderer>>
diff --git a/widget/src/container.rs b/widget/src/container.rs
index d9740f72..a411a7d2 100644
--- a/widget/src/container.rs
+++ b/widget/src/container.rs
@@ -493,11 +493,11 @@ pub fn visible_bounds(id: Id) -> Task<Option<Rectangle>> {
impl Operation<Option<Rectangle>> for VisibleBounds {
fn scrollable(
&mut self,
- _state: &mut dyn widget::operation::Scrollable,
_id: Option<&widget::Id>,
bounds: Rectangle,
_content_bounds: Rectangle,
translation: Vector,
+ _state: &mut dyn widget::operation::Scrollable,
) {
match self.scrollables.last() {
Some((last_translation, last_viewport, _depth)) => {
diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs
index cdfd2daf..b1e02943 100644
--- a/widget/src/helpers.rs
+++ b/widget/src/helpers.rs
@@ -232,10 +232,10 @@ where
///
/// This is equivalent to:
/// ```rust,no_run
-/// # use iced_widget::core::Length;
+/// # use iced_widget::core::Length::Fill;
/// # use iced_widget::Container;
/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
-/// let centered = container("Centered!").center(Length::Fill);
+/// let center = container("Center!").center(Fill);
/// ```
///
/// [`Container`]: crate::Container
@@ -249,6 +249,166 @@ where
container(content).center(Length::Fill)
}
+/// Creates a new [`Container`] that fills all the available space
+/// horizontally and centers its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let center_x = container("Horizontal Center!").center_x(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn center_x<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).center_x(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// vertically and centers its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let center_y = container("Vertical Center!").center_y(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn center_y<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).center_y(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// horizontally and right-aligns its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let right = container("Right!").align_right(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn right<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).align_right(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// and aligns its contents inside to the right center.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let right_center = container("Bottom Center!").align_right(Fill).center_y(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn right_center<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content)
+ .align_right(Length::Fill)
+ .center_y(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// vertically and bottom-aligns its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let bottom = container("Bottom!").align_bottom(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn bottom<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).align_bottom(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// and aligns its contents inside to the bottom center.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let bottom_center = container("Bottom Center!").center_x(Fill).align_bottom(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn bottom_center<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content)
+ .center_x(Length::Fill)
+ .align_bottom(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// and aligns its contents inside to the bottom right corner.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let bottom_right = container("Bottom!").align_right(Fill).align_bottom(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn bottom_right<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content)
+ .align_right(Length::Fill)
+ .align_bottom(Length::Fill)
+}
+
/// Creates a new [`Pin`] widget with the given content.
///
/// A [`Pin`] widget positions its contents at some fixed coordinates inside of its boundaries.
@@ -1708,9 +1868,9 @@ where
{
use crate::core::{Alignment, Font};
use crate::svg;
- use once_cell::sync::Lazy;
+ use std::sync::LazyLock;
- static LOGO: Lazy<svg::Handle> = Lazy::new(|| {
+ static LOGO: LazyLock<svg::Handle> = LazyLock::new(|| {
svg::Handle::from_memory(include_bytes!("../assets/iced-logo.svg"))
});
diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs
index d6bebb9b..c0648e9e 100644
--- a/widget/src/markdown.rs
+++ b/widget/src/markdown.rs
@@ -305,12 +305,21 @@ pub fn parse(markdown: &str) -> impl Iterator<Item = Item> + '_ {
None
}
pulldown_cmark::Tag::List(first_item) if !metadata && !table => {
+ let prev = if spans.is_empty() {
+ None
+ } else {
+ produce(
+ &mut lists,
+ Item::Paragraph(Text::new(spans.drain(..).collect())),
+ )
+ };
+
lists.push(List {
start: first_item,
items: Vec::new(),
});
- None
+ prev
}
pulldown_cmark::Tag::Item => {
lists
diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs
index 8f85dcf3..554e8a44 100644
--- a/widget/src/progress_bar.rs
+++ b/widget/src/progress_bar.rs
@@ -59,8 +59,9 @@ where
{
range: RangeInclusive<f32>,
value: f32,
- width: Length,
- height: Option<Length>,
+ length: Length,
+ girth: Length,
+ is_vertical: bool,
class: Theme::Class<'a>,
}
@@ -68,8 +69,8 @@ impl<'a, Theme> ProgressBar<'a, Theme>
where
Theme: Catalog,
{
- /// The default height of a [`ProgressBar`].
- pub const DEFAULT_HEIGHT: f32 = 30.0;
+ /// The default girth of a [`ProgressBar`].
+ pub const DEFAULT_GIRTH: f32 = 30.0;
/// Creates a new [`ProgressBar`].
///
@@ -80,21 +81,30 @@ where
ProgressBar {
value: value.clamp(*range.start(), *range.end()),
range,
- width: Length::Fill,
- height: None,
+ length: Length::Fill,
+ girth: Length::from(Self::DEFAULT_GIRTH),
+ is_vertical: false,
class: Theme::default(),
}
}
/// Sets the width of the [`ProgressBar`].
- pub fn width(mut self, width: impl Into<Length>) -> Self {
- self.width = width.into();
+ pub fn length(mut self, length: impl Into<Length>) -> Self {
+ self.length = length.into();
self
}
/// Sets the height of the [`ProgressBar`].
- pub fn height(mut self, height: impl Into<Length>) -> Self {
- self.height = Some(height.into());
+ pub fn girth(mut self, girth: impl Into<Length>) -> Self {
+ self.girth = girth.into();
+ self
+ }
+
+ /// Turns the [`ProgressBar`] into a vertical [`ProgressBar`].
+ ///
+ /// By default, a [`ProgressBar`] is horizontal.
+ pub fn vertical(mut self) -> Self {
+ self.is_vertical = true;
self
}
@@ -115,6 +125,22 @@ where
self.class = class.into();
self
}
+
+ fn width(&self) -> Length {
+ if self.is_vertical {
+ self.girth
+ } else {
+ self.length
+ }
+ }
+
+ fn height(&self) -> Length {
+ if self.is_vertical {
+ self.length
+ } else {
+ self.girth
+ }
+ }
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
@@ -125,8 +151,8 @@ where
{
fn size(&self) -> Size<Length> {
Size {
- width: self.width,
- height: self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
+ width: self.width(),
+ height: self.height(),
}
}
@@ -136,11 +162,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- layout::atomic(
- limits,
- self.width,
- self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
- )
+ layout::atomic(limits, self.width(), self.height())
}
fn draw(
@@ -156,11 +178,16 @@ where
let bounds = layout.bounds();
let (range_start, range_end) = self.range.clone().into_inner();
- let active_progress_width = if range_start >= range_end {
+ let length = if self.is_vertical {
+ bounds.height
+ } else {
+ bounds.width
+ };
+
+ let active_progress_length = if range_start >= range_end {
0.0
} else {
- bounds.width * (self.value - range_start)
- / (range_end - range_start)
+ length * (self.value - range_start) / (range_end - range_start)
};
let style = theme.style(&self.class);
@@ -174,13 +201,23 @@ where
style.background,
);
- if active_progress_width > 0.0 {
+ if active_progress_length > 0.0 {
+ let bounds = if self.is_vertical {
+ Rectangle {
+ y: bounds.y + bounds.height - active_progress_length,
+ height: active_progress_length,
+ ..bounds
+ }
+ } else {
+ Rectangle {
+ width: active_progress_length,
+ ..bounds
+ }
+ };
+
renderer.fill_quad(
renderer::Quad {
- bounds: Rectangle {
- width: active_progress_width,
- ..bounds
- },
+ bounds,
border: Border {
color: Color::TRANSPARENT,
..style.border
@@ -274,6 +311,13 @@ pub fn success(theme: &Theme) -> Style {
styled(palette.background.strong.color, palette.success.base.color)
}
+/// The warning style of a [`ProgressBar`].
+pub fn warning(theme: &Theme) -> Style {
+ let palette = theme.extended_palette();
+
+ styled(palette.background.strong.color, palette.warning.base.color)
+}
+
/// The danger style of a [`ProgressBar`].
pub fn danger(theme: &Theme) -> Style {
let palette = theme.extended_palette();
diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs
index 41bb15f9..b08d5d09 100644
--- a/widget/src/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -487,11 +487,11 @@ where
state.translation(self.direction, bounds, content_bounds);
operation.scrollable(
- state,
self.id.as_ref().map(|id| &id.0),
bounds,
content_bounds,
translation,
+ state,
);
operation.container(
@@ -906,14 +906,21 @@ where
is_vertical_scrollbar_dragged: state
.y_scroller_grabbed_at
.is_some(),
+ is_horizontal_scrollbar_disabled: scrollbars.is_x_disabled(),
+ is_vertical_scrollbar_disabled: scrollbars.is_y_disabled(),
}
} else if cursor_over_scrollable.is_some() {
Status::Hovered {
is_horizontal_scrollbar_hovered: mouse_over_x_scrollbar,
is_vertical_scrollbar_hovered: mouse_over_y_scrollbar,
+ is_horizontal_scrollbar_disabled: scrollbars.is_x_disabled(),
+ is_vertical_scrollbar_disabled: scrollbars.is_y_disabled(),
}
} else {
- Status::Active
+ Status::Active {
+ is_horizontal_scrollbar_disabled: scrollbars.is_x_disabled(),
+ is_vertical_scrollbar_disabled: scrollbars.is_y_disabled(),
+ }
};
if let Event::Window(window::Event::RedrawRequested(_now)) = event {
@@ -968,8 +975,13 @@ where
_ => mouse::Cursor::Unavailable,
};
- let style = theme
- .style(&self.class, self.last_status.unwrap_or(Status::Active));
+ let style = theme.style(
+ &self.class,
+ self.last_status.unwrap_or(Status::Active {
+ is_horizontal_scrollbar_disabled: false,
+ is_vertical_scrollbar_disabled: false,
+ }),
+ );
container::draw_background(renderer, &style.container, layout.bounds());
@@ -1661,6 +1673,7 @@ impl Scrollbars {
bounds: scrollbar_bounds,
scroller,
alignment: vertical.alignment,
+ disabled: content_bounds.height <= bounds.height,
})
} else {
None
@@ -1730,6 +1743,7 @@ impl Scrollbars {
bounds: scrollbar_bounds,
scroller,
alignment: horizontal.alignment,
+ disabled: content_bounds.width <= bounds.width,
})
} else {
None
@@ -1758,6 +1772,14 @@ impl Scrollbars {
}
}
+ fn is_y_disabled(&self) -> bool {
+ self.y.map(|y| y.disabled).unwrap_or(false)
+ }
+
+ fn is_x_disabled(&self) -> bool {
+ self.x.map(|x| x.disabled).unwrap_or(false)
+ }
+
fn grab_y_scroller(&self, cursor_position: Point) -> Option<f32> {
let scrollbar = self.y?;
let scroller = scrollbar.scroller?;
@@ -1804,6 +1826,7 @@ pub(super) mod internals {
pub bounds: Rectangle,
pub scroller: Option<Scroller>,
pub alignment: Anchor,
+ pub disabled: bool,
}
impl Scrollbar {
@@ -1867,13 +1890,22 @@ pub(super) mod internals {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
/// The [`Scrollable`] can be interacted with.
- Active,
+ Active {
+ /// Whether or not the horizontal scrollbar is disabled meaning the content isn't overflowing.
+ is_horizontal_scrollbar_disabled: bool,
+ /// Whether or not the vertical scrollbar is disabled meaning the content isn't overflowing.
+ is_vertical_scrollbar_disabled: bool,
+ },
/// The [`Scrollable`] is being hovered.
Hovered {
/// Indicates if the horizontal scrollbar is being hovered.
is_horizontal_scrollbar_hovered: bool,
/// Indicates if the vertical scrollbar is being hovered.
is_vertical_scrollbar_hovered: bool,
+ /// Whether or not the horizontal scrollbar is disabled meaning the content isn't overflowing.
+ is_horizontal_scrollbar_disabled: bool,
+ /// Whether or not the vertical scrollbar is disabled meaning the content isn't overflowing.
+ is_vertical_scrollbar_disabled: bool,
},
/// The [`Scrollable`] is being dragged.
Dragged {
@@ -1881,6 +1913,10 @@ pub enum Status {
is_horizontal_scrollbar_dragged: bool,
/// Indicates if the vertical scrollbar is being dragged.
is_vertical_scrollbar_dragged: bool,
+ /// Whether or not the horizontal scrollbar is disabled meaning the content isn't overflowing.
+ is_horizontal_scrollbar_disabled: bool,
+ /// Whether or not the vertical scrollbar is disabled meaning the content isn't overflowing.
+ is_vertical_scrollbar_disabled: bool,
},
}
@@ -1958,7 +1994,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
};
match status {
- Status::Active => Style {
+ Status::Active { .. } => Style {
container: container::Style::default(),
vertical_rail: scrollbar,
horizontal_rail: scrollbar,
@@ -1967,6 +2003,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
Status::Hovered {
is_horizontal_scrollbar_hovered,
is_vertical_scrollbar_hovered,
+ ..
} => {
let hovered_scrollbar = Rail {
scroller: Scroller {
@@ -1994,6 +2031,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
Status::Dragged {
is_horizontal_scrollbar_dragged,
is_vertical_scrollbar_dragged,
+ ..
} => {
let dragged_scrollbar = Rail {
scroller: Scroller {
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index ffd06b77..ad852ce9 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -971,13 +971,13 @@ where
fn operate(
&self,
tree: &mut widget::Tree,
- _layout: Layout<'_>,
+ layout: Layout<'_>,
_renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
let state = tree.state.downcast_mut::<State<Highlighter>>();
- operation.focusable(state, None);
+ operation.focusable(None, layout.bounds(), state);
}
}
diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs
index c3f0b25a..57ebe46a 100644
--- a/widget/src/text_input.rs
+++ b/widget/src/text_input.rs
@@ -617,14 +617,23 @@ where
fn operate(
&self,
tree: &mut Tree,
- _layout: Layout<'_>,
+ layout: Layout<'_>,
_renderer: &Renderer,
operation: &mut dyn Operation,
) {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
- operation.focusable(state, self.id.as_ref().map(|id| &id.0));
- operation.text_input(state, self.id.as_ref().map(|id| &id.0));
+ operation.focusable(
+ self.id.as_ref().map(|id| &id.0),
+ layout.bounds(),
+ state,
+ );
+
+ operation.text_input(
+ self.id.as_ref().map(|id| &id.0),
+ layout.bounds(),
+ state,
+ );
}
fn update(
diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs
index c1af142b..2ed9419a 100644
--- a/widget/src/vertical_slider.rs
+++ b/widget/src/vertical_slider.rs
@@ -42,6 +42,7 @@ use crate::core::mouse;
use crate::core::renderer;
use crate::core::touch;
use crate::core::widget::tree::{self, Tree};
+use crate::core::window;
use crate::core::{
self, Clipboard, Element, Event, Length, Pixels, Point, Rectangle, Shell,
Size, Widget,
@@ -98,6 +99,7 @@ where
width: f32,
height: Length,
class: Theme::Class<'a>,
+ status: Option<Status>,
}
impl<'a, T, Message, Theme> VerticalSlider<'a, T, Message, Theme>
@@ -144,6 +146,7 @@ where
width: Self::DEFAULT_WIDTH,
height: Length::Fill,
class: Theme::default(),
+ status: None,
}
}
@@ -390,7 +393,9 @@ where
shell.capture_event();
}
}
- Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ ref key, ..
+ }) => {
if cursor.is_over(layout.bounds()) {
match key {
Key::Named(key::Named::ArrowUp) => {
@@ -410,32 +415,36 @@ where
}
_ => {}
}
+
+ let current_status = if state.is_dragging {
+ Status::Dragged
+ } else if cursor.is_over(layout.bounds()) {
+ Status::Hovered
+ } else {
+ Status::Active
+ };
+
+ if let Event::Window(window::Event::RedrawRequested(_now)) = event {
+ self.status = Some(current_status);
+ } else if self.status.is_some_and(|status| status != current_status) {
+ shell.request_redraw();
+ }
}
fn draw(
&self,
- tree: &Tree,
+ _tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
- cursor: mouse::Cursor,
+ _cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
- let state = tree.state.downcast_ref::<State>();
let bounds = layout.bounds();
- let is_mouse_over = cursor.is_over(bounds);
- let style = theme.style(
- &self.class,
- if state.is_dragging {
- Status::Dragged
- } else if is_mouse_over {
- Status::Hovered
- } else {
- Status::Active
- },
- );
+ let style =
+ theme.style(&self.class, self.status.unwrap_or(Status::Active));
let (handle_width, handle_height, handle_border_radius) =
match style.handle.shape {
diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs
index 8e6f7aae..462be65b 100644
--- a/winit/src/conversion.rs
+++ b/winit/src/conversion.rs
@@ -23,6 +23,12 @@ pub fn window_attributes(
width: settings.size.width,
height: settings.size.height,
})
+ .with_maximized(settings.maximized)
+ .with_fullscreen(
+ settings
+ .fullscreen
+ .then_some(winit::window::Fullscreen::Borderless(None)),
+ )
.with_resizable(settings.resizable)
.with_enabled_buttons(if settings.resizable {
winit::window::WindowButtons::all()
@@ -1114,7 +1120,7 @@ pub fn native_key_code(
}
}
-/// Converts some [`UserAttention`] into it's `winit` counterpart.
+/// Converts some [`UserAttention`] into its `winit` counterpart.
///
/// [`UserAttention`]: window::UserAttention
pub fn user_attention(
@@ -1130,6 +1136,30 @@ pub fn user_attention(
}
}
+/// Converts some [`window::Direction`] into a [`winit::window::ResizeDirection`].
+pub fn resize_direction(
+ resize_direction: window::Direction,
+) -> winit::window::ResizeDirection {
+ match resize_direction {
+ window::Direction::North => winit::window::ResizeDirection::North,
+ window::Direction::South => winit::window::ResizeDirection::South,
+ window::Direction::East => winit::window::ResizeDirection::East,
+ window::Direction::West => winit::window::ResizeDirection::West,
+ window::Direction::NorthEast => {
+ winit::window::ResizeDirection::NorthEast
+ }
+ window::Direction::NorthWest => {
+ winit::window::ResizeDirection::NorthWest
+ }
+ window::Direction::SouthEast => {
+ winit::window::ResizeDirection::SouthEast
+ }
+ window::Direction::SouthWest => {
+ winit::window::ResizeDirection::SouthWest
+ }
+ }
+}
+
/// Converts some [`window::Icon`] into it's `winit` counterpart.
///
/// Returns `None` if there is an error during the conversion.
diff --git a/winit/src/program.rs b/winit/src/program.rs
index 13873edd..5387e5e5 100644
--- a/winit/src/program.rs
+++ b/winit/src/program.rs
@@ -8,10 +8,11 @@ use crate::conversion;
use crate::core;
use crate::core::mouse;
use crate::core::renderer;
+use crate::core::theme;
use crate::core::time::Instant;
use crate::core::widget::operation;
use crate::core::window;
-use crate::core::{Color, Element, Point, Size, Theme};
+use crate::core::{Element, Point, Size};
use crate::futures::futures::channel::mpsc;
use crate::futures::futures::channel::oneshot;
use crate::futures::futures::task;
@@ -46,7 +47,7 @@ use std::sync::Arc;
pub trait Program
where
Self: Sized,
- Self::Theme: DefaultStyle,
+ Self::Theme: theme::Base,
{
/// The type of __messages__ your [`Program`] will produce.
type Message: std::fmt::Debug + Send;
@@ -106,8 +107,8 @@ where
fn theme(&self, window: window::Id) -> Self::Theme;
/// Returns the `Style` variation of the `Theme`.
- fn style(&self, theme: &Self::Theme) -> Appearance {
- theme.default_style()
+ fn style(&self, theme: &Self::Theme) -> theme::Style {
+ theme::Base::base(theme)
}
/// Returns the event `Subscription` for the current state of the
@@ -138,37 +139,6 @@ where
}
}
-/// The appearance of a program.
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Appearance {
- /// The background [`Color`] of the application.
- pub background_color: Color,
-
- /// The default text [`Color`] of the application.
- pub text_color: Color,
-}
-
-/// The default style of a [`Program`].
-pub trait DefaultStyle {
- /// Returns the default style of a [`Program`].
- fn default_style(&self) -> Appearance;
-}
-
-impl DefaultStyle for Theme {
- fn default_style(&self) -> Appearance {
- default(self)
- }
-}
-
-/// The default [`Appearance`] of a [`Program`] with the built-in [`Theme`].
-pub fn default(theme: &Theme) -> Appearance {
- let palette = theme.extended_palette();
-
- Appearance {
- background_color: palette.background.base.color,
- text_color: palette.background.base.text,
- }
-}
/// Runs a [`Program`] with an executor, compositor, and the provided
/// settings.
pub fn run<P, C>(
@@ -180,7 +150,7 @@ pub fn run<P, C>(
where
P: Program + 'static,
C: Compositor<Renderer = P::Renderer> + 'static,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
use winit::event_loop::EventLoop;
@@ -222,7 +192,6 @@ where
runtime.enter(|| program.subscription().map(Action::Output)),
));
- let (boot_sender, boot_receiver) = oneshot::channel();
let (event_sender, event_receiver) = mpsc::unbounded();
let (control_sender, control_receiver) = mpsc::unbounded();
@@ -231,133 +200,49 @@ where
runtime,
proxy.clone(),
debug,
- boot_receiver,
event_receiver,
control_sender,
is_daemon,
+ graphics_settings,
+ settings.fonts,
));
let context = task::Context::from_waker(task::noop_waker_ref());
- struct Runner<Message: 'static, F, C> {
+ struct Runner<Message: 'static, F> {
instance: std::pin::Pin<Box<F>>,
context: task::Context<'static>,
id: Option<String>,
- boot: Option<BootConfig<C>>,
sender: mpsc::UnboundedSender<Event<Action<Message>>>,
receiver: mpsc::UnboundedReceiver<Control>,
error: Option<Error>,
#[cfg(target_arch = "wasm32")]
- is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
- #[cfg(target_arch = "wasm32")]
canvas: Option<web_sys::HtmlCanvasElement>,
}
- struct BootConfig<C> {
- sender: oneshot::Sender<Boot<C>>,
- fonts: Vec<Cow<'static, [u8]>>,
- graphics_settings: graphics::Settings,
- }
-
let runner = Runner {
instance,
context,
id: settings.id,
- boot: Some(BootConfig {
- sender: boot_sender,
- fonts: settings.fonts,
- graphics_settings,
- }),
sender: event_sender,
receiver: control_receiver,
error: None,
#[cfg(target_arch = "wasm32")]
- is_booted: std::rc::Rc::new(std::cell::RefCell::new(false)),
- #[cfg(target_arch = "wasm32")]
canvas: None,
};
- impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>>
- for Runner<Message, F, C>
+ impl<Message, F> winit::application::ApplicationHandler<Action<Message>>
+ for Runner<Message, F>
where
Message: std::fmt::Debug,
F: Future<Output = ()>,
- C: Compositor + 'static,
{
- fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
- let Some(BootConfig {
- sender,
- fonts,
- graphics_settings,
- }) = self.boot.take()
- else {
- return;
- };
-
- let window = {
- let attributes = winit::window::WindowAttributes::default();
-
- #[cfg(target_os = "windows")]
- let attributes = {
- use winit::platform::windows::WindowAttributesExtWindows;
- attributes.with_drag_and_drop(false)
- };
-
- match event_loop.create_window(attributes.with_visible(false)) {
- Ok(window) => Arc::new(window),
- Err(error) => {
- self.error = Some(Error::WindowCreationFailed(error));
- event_loop.exit();
- return;
- }
- }
- };
-
- #[cfg(target_arch = "wasm32")]
- {
- use winit::platform::web::WindowExtWebSys;
- self.canvas = window.canvas();
- }
-
- let finish_boot = async move {
- let mut compositor =
- C::new(graphics_settings, window.clone()).await?;
-
- for font in fonts {
- compositor.load_font(font);
- }
-
- sender
- .send(Boot { compositor })
- .ok()
- .expect("Send boot event");
-
- Ok::<_, graphics::Error>(())
- };
-
- #[cfg(not(target_arch = "wasm32"))]
- if let Err(error) =
- crate::futures::futures::executor::block_on(finish_boot)
- {
- self.error = Some(Error::GraphicsCreationFailed(error));
- event_loop.exit();
- }
-
- #[cfg(target_arch = "wasm32")]
- {
- let is_booted = self.is_booted.clone();
-
- wasm_bindgen_futures::spawn_local(async move {
- finish_boot.await.expect("Finish boot!");
-
- *is_booted.borrow_mut() = true;
- });
-
- event_loop
- .set_control_flow(winit::event_loop::ControlFlow::Poll);
- }
+ fn resumed(
+ &mut self,
+ _event_loop: &winit::event_loop::ActiveEventLoop,
+ ) {
}
fn new_events(
@@ -365,15 +250,6 @@ where
event_loop: &winit::event_loop::ActiveEventLoop,
cause: winit::event::StartCause,
) {
- if self.boot.is_some() {
- return;
- }
-
- #[cfg(target_arch = "wasm32")]
- if !*self.is_booted.borrow() {
- return;
- }
-
self.process_event(
event_loop,
Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)),
@@ -452,11 +328,6 @@ where
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
) {
- #[cfg(target_arch = "wasm32")]
- if !*self.is_booted.borrow() {
- return;
- }
-
self.process_event(
event_loop,
Event::EventLoopAwakened(winit::event::Event::AboutToWait),
@@ -464,10 +335,9 @@ where
}
}
- impl<Message, F, C> Runner<Message, F, C>
+ impl<Message, F> Runner<Message, F>
where
F: Future<Output = ()>,
- C: Compositor,
{
fn process_event(
&mut self,
@@ -538,10 +408,25 @@ where
log::info!("Window attributes for id `{id:#?}`: {window_attributes:#?}");
+ // On macOS, the `position` in `WindowAttributes` represents the "inner"
+ // position of the window; while on other platforms it's the "outer" position.
+ // We fix the inconsistency on macOS by positioning the window after creation.
+ #[cfg(target_os = "macos")]
+ let mut window_attributes = window_attributes;
+
+ #[cfg(target_os = "macos")]
+ let position =
+ window_attributes.position.take();
+
let window = event_loop
.create_window(window_attributes)
.expect("Create window");
+ #[cfg(target_os = "macos")]
+ if let Some(position) = position {
+ window.set_outer_position(position);
+ }
+
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
@@ -592,7 +477,7 @@ where
event_loop,
Event::WindowCreated {
id,
- window,
+ window: Arc::new(window),
exit_on_close_request,
make_visible: visible,
on_open,
@@ -602,6 +487,10 @@ where
Control::Exit => {
event_loop.exit();
}
+ Control::Crash(error) => {
+ self.error = Some(error);
+ event_loop.exit();
+ }
},
_ => {
break;
@@ -633,15 +522,11 @@ where
}
}
-struct Boot<C> {
- compositor: C,
-}
-
#[derive(Debug)]
enum Event<Message: 'static> {
WindowCreated {
id: window::Id,
- window: winit::window::Window,
+ window: Arc<winit::window::Window>,
exit_on_close_request: bool,
make_visible: bool,
on_open: oneshot::Sender<window::Id>,
@@ -653,6 +538,7 @@ enum Event<Message: 'static> {
enum Control {
ChangeFlow(winit::event_loop::ControlFlow),
Exit,
+ Crash(Error),
CreateWindow {
id: window::Id,
settings: window::Settings,
@@ -667,23 +553,23 @@ async fn run_instance<P, C>(
mut runtime: Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
mut proxy: Proxy<P::Message>,
mut debug: Debug,
- boot: oneshot::Receiver<Boot<C>>,
mut event_receiver: mpsc::UnboundedReceiver<Event<Action<P::Message>>>,
mut control_sender: mpsc::UnboundedSender<Control>,
is_daemon: bool,
+ graphics_settings: graphics::Settings,
+ default_fonts: Vec<Cow<'static, [u8]>>,
) where
P: Program + 'static,
C: Compositor<Renderer = P::Renderer> + 'static,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
use winit::event;
use winit::event_loop::ControlFlow;
- let Boot { mut compositor } = boot.await.expect("Receive boot");
-
let mut window_manager = WindowManager::new();
let mut is_window_opening = !is_daemon;
+ let mut compositor = None;
let mut events = Vec::new();
let mut messages = Vec::new();
let mut actions = 0;
@@ -691,12 +577,35 @@ async fn run_instance<P, C>(
let mut ui_caches = FxHashMap::default();
let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
let mut clipboard = Clipboard::unconnected();
+ let mut compositor_receiver: Option<oneshot::Receiver<_>> = None;
debug.startup_finished();
loop {
+ let event = if compositor_receiver.is_some() {
+ let compositor_receiver =
+ compositor_receiver.take().expect("Waiting for compositor");
+
+ match compositor_receiver.await {
+ Ok(Ok((new_compositor, event))) => {
+ compositor = Some(new_compositor);
+
+ Some(event)
+ }
+ Ok(Err(error)) => {
+ control_sender
+ .start_send(Control::Crash(
+ Error::GraphicsCreationFailed(error),
+ ))
+ .expect("Send control action");
+ break;
+ }
+ Err(error) => {
+ panic!("Compositor initialization failed: {error}")
+ }
+ }
// Empty the queue if possible
- let event = if let Ok(event) = event_receiver.try_next() {
+ } else if let Ok(event) = event_receiver.try_next() {
event
} else {
event_receiver.next().await
@@ -714,11 +623,63 @@ async fn run_instance<P, C>(
make_visible,
on_open,
} => {
+ if compositor.is_none() {
+ let (compositor_sender, new_compositor_receiver) =
+ oneshot::channel();
+
+ compositor_receiver = Some(new_compositor_receiver);
+
+ let create_compositor = {
+ let default_fonts = default_fonts.clone();
+
+ async move {
+ let mut compositor =
+ C::new(graphics_settings, window.clone()).await;
+
+ if let Ok(compositor) = &mut compositor {
+ for font in default_fonts {
+ compositor.load_font(font.clone());
+ }
+ }
+
+ compositor_sender
+ .send(compositor.map(|compositor| {
+ (
+ compositor,
+ Event::WindowCreated {
+ id,
+ window,
+ exit_on_close_request,
+ make_visible,
+ on_open,
+ },
+ )
+ }))
+ .ok()
+ .expect("Send compositor");
+ }
+ };
+
+ #[cfg(not(target_arch = "wasm32"))]
+ crate::futures::futures::executor::block_on(
+ create_compositor,
+ );
+
+ #[cfg(target_arch = "wasm32")]
+ {
+ wasm_bindgen_futures::spawn_local(create_compositor);
+ }
+
+ continue;
+ }
+
let window = window_manager.insert(
id,
- Arc::new(window),
+ window,
&program,
- &mut compositor,
+ compositor
+ .as_mut()
+ .expect("Compositor must be initialized"),
exit_on_close_request,
);
@@ -812,6 +773,10 @@ async fn run_instance<P, C>(
event: event::WindowEvent::RedrawRequested,
..
} => {
+ let Some(compositor) = &mut compositor else {
+ continue;
+ };
+
let Some((id, window)) =
window_manager.get_mut_alias(id)
else {
@@ -1170,7 +1135,7 @@ fn build_user_interface<'a, P: Program>(
id: window::Id,
) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
where
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
debug.view_started();
let view = program.view(id);
@@ -1189,7 +1154,7 @@ fn update<P: Program, E: Executor>(
debug: &mut Debug,
messages: &mut Vec<P::Message>,
) where
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
for message in messages.drain(..) {
debug.log_message(&message);
@@ -1210,7 +1175,7 @@ fn update<P: Program, E: Executor>(
fn run_action<P, C>(
action: Action<P::Message>,
program: &P,
- compositor: &mut C,
+ compositor: &mut Option<C>,
events: &mut Vec<(window::Id, core::Event)>,
messages: &mut Vec<P::Message>,
clipboard: &mut Clipboard,
@@ -1226,7 +1191,7 @@ fn run_action<P, C>(
) where
P: Program,
C: Compositor<Renderer = P::Renderer> + 'static,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
use crate::runtime::clipboard;
use crate::runtime::system;
@@ -1278,6 +1243,10 @@ fn run_action<P, C>(
core::Event::Window(core::window::Event::Closed),
));
}
+
+ if window_manager.is_empty() {
+ *compositor = None;
+ }
}
window::Action::GetOldest(channel) => {
let id =
@@ -1296,6 +1265,13 @@ fn run_action<P, C>(
let _ = window.raw.drag_window();
}
}
+ window::Action::DragResize(id, direction) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let _ = window.raw.drag_resize_window(
+ conversion::resize_direction(direction),
+ );
+ }
+ }
window::Action::Resize(id, size) => {
if let Some(window) = window_manager.get_mut(id) {
let _ = window.raw.request_inner_size(
@@ -1306,6 +1282,41 @@ fn run_action<P, C>(
);
}
}
+ window::Action::SetMinSize(id, size) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_min_inner_size(size.map(|size| {
+ winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ }
+ }));
+ }
+ }
+ window::Action::SetMaxSize(id, size) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_max_inner_size(size.map(|size| {
+ winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ }
+ }));
+ }
+ }
+ window::Action::SetResizeIncrements(id, increments) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_resize_increments(increments.map(|size| {
+ winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ }
+ }));
+ }
+ }
+ window::Action::SetResizable(id, resizable) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_resizable(resizable);
+ }
+ }
window::Action::GetSize(id, channel) => {
if let Some(window) = window_manager.get_mut(id) {
let size = window
@@ -1340,7 +1351,7 @@ fn run_action<P, C>(
if let Some(window) = window_manager.get(id) {
let position = window
.raw
- .inner_position()
+ .outer_position()
.map(|position| {
let position = position
.to_logical::<f32>(window.raw.scale_factor());
@@ -1369,7 +1380,7 @@ fn run_action<P, C>(
);
}
}
- window::Action::ChangeMode(id, mode) => {
+ window::Action::SetMode(id, mode) => {
if let Some(window) = window_manager.get_mut(id) {
window.raw.set_visible(conversion::visible(mode));
window.raw.set_fullscreen(conversion::fullscreen(
@@ -1378,7 +1389,7 @@ fn run_action<P, C>(
));
}
}
- window::Action::ChangeIcon(id, icon) => {
+ window::Action::SetIcon(id, icon) => {
if let Some(window) = window_manager.get_mut(id) {
window.raw.set_window_icon(conversion::icon(icon));
}
@@ -1416,7 +1427,7 @@ fn run_action<P, C>(
window.raw.focus_window();
}
}
- window::Action::ChangeLevel(id, level) => {
+ window::Action::SetLevel(id, level) => {
if let Some(window) = window_manager.get_mut(id) {
window
.raw
@@ -1454,18 +1465,20 @@ fn run_action<P, C>(
}
window::Action::Screenshot(id, channel) => {
if let Some(window) = window_manager.get_mut(id) {
- let bytes = compositor.screenshot(
- &mut window.renderer,
- window.state.viewport(),
- window.state.background_color(),
- &debug.overlay(),
- );
+ if let Some(compositor) = compositor {
+ let bytes = compositor.screenshot(
+ &mut window.renderer,
+ window.state.viewport(),
+ window.state.background_color(),
+ &debug.overlay(),
+ );
- let _ = channel.send(window::Screenshot::new(
- bytes,
- window.state.physical_size(),
- window.state.viewport().scale_factor(),
- ));
+ let _ = channel.send(core::window::Screenshot::new(
+ bytes,
+ window.state.physical_size(),
+ window.state.viewport().scale_factor(),
+ ));
+ }
}
}
window::Action::EnableMousePassthrough(id) => {
@@ -1483,14 +1496,16 @@ fn run_action<P, C>(
system::Action::QueryInformation(_channel) => {
#[cfg(feature = "system")]
{
- let graphics_info = compositor.fetch_information();
+ if let Some(compositor) = compositor {
+ let graphics_info = compositor.fetch_information();
- let _ = std::thread::spawn(move || {
- let information =
- crate::system::information(graphics_info);
+ let _ = std::thread::spawn(move || {
+ let information =
+ crate::system::information(graphics_info);
- let _ = _channel.send(information);
- });
+ let _ = _channel.send(information);
+ });
+ }
}
}
},
@@ -1514,10 +1529,12 @@ fn run_action<P, C>(
}
}
Action::LoadFont { bytes, channel } => {
- // TODO: Error handling (?)
- compositor.load_font(bytes.clone());
+ if let Some(compositor) = compositor {
+ // TODO: Error handling (?)
+ compositor.load_font(bytes.clone());
- let _ = channel.send(Ok(()));
+ let _ = channel.send(Ok(()));
+ }
}
Action::Exit => {
control_sender
@@ -1536,7 +1553,7 @@ pub fn build_user_interfaces<'a, P: Program, C>(
) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
where
C: Compositor<Renderer = P::Renderer>,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
cached_user_interfaces
.drain()
diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs
index b8a58960..e883d04a 100644
--- a/winit/src/program/state.rs
+++ b/winit/src/program/state.rs
@@ -1,17 +1,18 @@
use crate::conversion;
-use crate::core::{mouse, window};
+use crate::core::{mouse, theme, window};
use crate::core::{Color, Size};
use crate::graphics::Viewport;
-use crate::program::{self, Program};
-use std::fmt::{Debug, Formatter};
+use crate::program::Program;
use winit::event::{Touch, WindowEvent};
use winit::window::Window;
+use std::fmt::{Debug, Formatter};
+
/// The state of a multi-windowed [`Program`].
pub struct State<P: Program>
where
- P::Theme: program::DefaultStyle,
+ P::Theme: theme::Base,
{
title: String,
scale_factor: f64,
@@ -20,12 +21,12 @@ where
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
modifiers: winit::keyboard::ModifiersState,
theme: P::Theme,
- appearance: program::Appearance,
+ style: theme::Style,
}
impl<P: Program> Debug for State<P>
where
- P::Theme: program::DefaultStyle,
+ P::Theme: theme::Base,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("multi_window::State")
@@ -34,14 +35,14 @@ where
.field("viewport", &self.viewport)
.field("viewport_version", &self.viewport_version)
.field("cursor_position", &self.cursor_position)
- .field("appearance", &self.appearance)
+ .field("style", &self.style)
.finish()
}
}
impl<P: Program> State<P>
where
- P::Theme: program::DefaultStyle,
+ P::Theme: theme::Base,
{
/// Creates a new [`State`] for the provided [`Program`]'s `window`.
pub fn new(
@@ -52,7 +53,7 @@ where
let title = application.title(window_id);
let scale_factor = application.scale_factor(window_id);
let theme = application.theme(window_id);
- let appearance = application.style(&theme);
+ let style = application.style(&theme);
let viewport = {
let physical_size = window.inner_size();
@@ -71,7 +72,7 @@ where
cursor_position: None,
modifiers: winit::keyboard::ModifiersState::default(),
theme,
- appearance,
+ style,
}
}
@@ -127,12 +128,12 @@ where
/// Returns the current background [`Color`] of the [`State`].
pub fn background_color(&self) -> Color {
- self.appearance.background_color
+ self.style.background_color
}
/// Returns the current text [`Color`] of the [`State`].
pub fn text_color(&self) -> Color {
- self.appearance.text_color
+ self.style.text_color
}
/// Processes the provided window event and updates the [`State`] accordingly.
@@ -237,6 +238,6 @@ where
// Update theme and appearance
self.theme = application.theme(window_id);
- self.appearance = application.style(&self.theme);
+ self.style = application.style(&self.theme);
}
}
diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs
index 10a973fe..a3c991df 100644
--- a/winit/src/program/window_manager.rs
+++ b/winit/src/program/window_manager.rs
@@ -1,9 +1,10 @@
use crate::core::mouse;
+use crate::core::theme;
use crate::core::time::Instant;
use crate::core::window::Id;
use crate::core::{Point, Size};
use crate::graphics::Compositor;
-use crate::program::{DefaultStyle, Program, State};
+use crate::program::{Program, State};
use std::collections::BTreeMap;
use std::sync::Arc;
@@ -14,7 +15,7 @@ pub struct WindowManager<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
aliases: BTreeMap<winit::window::WindowId, Id>,
entries: BTreeMap<Id, Window<P, C>>,
@@ -24,7 +25,7 @@ impl<P, C> WindowManager<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
pub fn new() -> Self {
Self {
@@ -132,7 +133,7 @@ impl<P, C> Default for WindowManager<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
fn default() -> Self {
Self::new()
@@ -144,7 +145,7 @@ pub struct Window<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
pub raw: Arc<winit::window::Window>,
pub state: State<P>,
@@ -160,11 +161,11 @@ impl<P, C> Window<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
- P::Theme: DefaultStyle,
+ P::Theme: theme::Base,
{
pub fn position(&self) -> Option<Point> {
self.raw
- .inner_position()
+ .outer_position()
.ok()
.map(|position| position.to_logical(self.raw.scale_factor()))
.map(|position| Point {
diff --git a/winit/src/settings.rs b/winit/src/settings.rs
index 78368a04..e2bf8abf 100644
--- a/winit/src/settings.rs
+++ b/winit/src/settings.rs
@@ -1,4 +1,6 @@
//! Configure your application.
+use crate::core;
+
use std::borrow::Cow;
/// The settings of an application.
@@ -13,3 +15,12 @@ pub struct Settings {
/// The fonts to load on boot.
pub fonts: Vec<Cow<'static, [u8]>>,
}
+
+impl From<core::Settings> for Settings {
+ fn from(settings: core::Settings) -> Self {
+ Self {
+ id: settings.id,
+ fonts: settings.fonts,
+ }
+ }
+}