summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock345
-rw-r--r--Cargo.toml2
-rw-r--r--examples/changelog/Cargo.toml3
-rw-r--r--examples/download_progress/Cargo.toml3
-rw-r--r--examples/gallery/Cargo.toml23
-rw-r--r--examples/gallery/src/civitai.rs148
-rw-r--r--examples/gallery/src/main.rs308
-rw-r--r--examples/pokedex/Cargo.toml3
8 files changed, 714 insertions, 121 deletions
diff --git a/Cargo.lock b/Cargo.lock
index aa774409..48c31dd5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -85,7 +85,7 @@ dependencies = [
"ndk-context",
"ndk-sys 0.6.0+11769913",
"num_enum",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -747,7 +747,7 @@ dependencies = [
"polling 3.7.4",
"rustix 0.38.43",
"slab",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -821,7 +821,7 @@ dependencies = [
"log",
"reqwest",
"serde",
- "thiserror 1.0.69",
+ "thiserror",
"tokio",
"tracing-subscriber",
"webbrowser",
@@ -935,7 +935,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c"
dependencies = [
- "thiserror 1.0.69",
+ "thiserror",
"x11rb",
]
@@ -1053,7 +1053,7 @@ dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"core-graphics-types 0.1.3",
- "foreign-types",
+ "foreign-types 0.5.0",
"libc",
]
@@ -1066,7 +1066,7 @@ dependencies = [
"bitflags 2.8.0",
"core-foundation 0.10.0",
"core-graphics-types 0.2.0",
- "foreign-types",
+ "foreign-types 0.5.0",
"libc",
]
@@ -1692,12 +1692,21 @@ dependencies = [
[[package]]
name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared 0.1.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",
+ "foreign-types-shared 0.3.1",
]
[[package]]
@@ -1713,6 +1722,12 @@ dependencies = [
[[package]]
name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
@@ -1845,6 +1860,18 @@ dependencies = [
]
[[package]]
+name = "gallery"
+version = "0.1.0"
+dependencies = [
+ "bytes",
+ "iced",
+ "image",
+ "reqwest",
+ "serde",
+ "tokio",
+]
+
+[[package]]
name = "game_of_life"
version = "0.1.0"
dependencies = [
@@ -2076,7 +2103,7 @@ checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
dependencies = [
"log",
"presser",
- "thiserror 1.0.69",
+ "thiserror",
"windows 0.58.0",
]
@@ -2156,6 +2183,25 @@ dependencies = [
]
[[package]]
+name = "h2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.2.0",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2321,7 +2367,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
- "h2",
+ "h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
@@ -2344,6 +2390,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
+ "h2 0.4.7",
"http 1.2.0",
"http-body 1.0.1",
"httparse",
@@ -2369,7 +2416,22 @@ dependencies = [
"tokio",
"tokio-rustls 0.26.1",
"tower-service",
- "webpki-roots",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper 1.5.2",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
]
[[package]]
@@ -2427,7 +2489,7 @@ dependencies = [
"iced_widget",
"iced_winit",
"image",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -2445,7 +2507,7 @@ dependencies = [
"palette",
"rustc-hash 2.1.0",
"smol_str",
- "thiserror 1.0.69",
+ "thiserror",
"web-time",
]
@@ -2480,7 +2542,7 @@ dependencies = [
"lyon_path",
"raw-window-handle 0.6.2",
"rustc-hash 2.1.0",
- "thiserror 1.0.69",
+ "thiserror",
"unicode-segmentation",
]
@@ -2500,7 +2562,7 @@ dependencies = [
"iced_tiny_skia",
"iced_wgpu",
"log",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -2511,7 +2573,7 @@ dependencies = [
"iced_core",
"iced_futures",
"raw-window-handle 0.6.2",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -2522,7 +2584,7 @@ dependencies = [
"iced_runtime",
"png",
"sha2",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -2555,7 +2617,7 @@ dependencies = [
"lyon",
"resvg",
"rustc-hash 2.1.0",
- "thiserror 1.0.69",
+ "thiserror",
"wgpu",
]
@@ -2571,7 +2633,7 @@ dependencies = [
"pulldown-cmark",
"qrcode",
"rustc-hash 2.1.0",
- "thiserror 1.0.69",
+ "thiserror",
"unicode-segmentation",
"url",
]
@@ -2586,7 +2648,7 @@ dependencies = [
"log",
"rustc-hash 2.1.0",
"sysinfo",
- "thiserror 1.0.69",
+ "thiserror",
"tracing",
"wasm-bindgen-futures",
"web-sys",
@@ -2905,7 +2967,7 @@ dependencies = [
"combine",
"jni-sys",
"log",
- "thiserror 1.0.69",
+ "thiserror",
"walkdir",
"windows-sys 0.45.0",
]
@@ -3277,7 +3339,7 @@ dependencies = [
"bitflags 2.8.0",
"block",
"core-graphics-types 0.1.3",
- "foreign-types",
+ "foreign-types 0.5.0",
"log",
"objc",
"paste",
@@ -3390,11 +3452,28 @@ dependencies = [
"rustc-hash 1.1.0",
"spirv",
"termcolor",
- "thiserror 1.0.69",
+ "thiserror",
"unicode-xid",
]
[[package]]
+name = "native-tls"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
name = "ndk"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3406,7 +3485,7 @@ dependencies = [
"ndk-sys 0.6.0+11769913",
"num_enum",
"raw-window-handle 0.6.2",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -3862,6 +3941,50 @@ dependencies = [
]
[[package]]
+name = "openssl"
+version = "0.10.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e"
+dependencies = [
+ "bitflags 2.8.0",
+ "cfg-if",
+ "foreign-types 0.3.2",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "orbclient"
version = "0.3.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4379,58 +4502,6 @@ dependencies = [
]
[[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"
@@ -4511,7 +4582,7 @@ dependencies = [
"rand_chacha",
"simd_helpers",
"system-deps",
- "thiserror 1.0.69",
+ "thiserror",
"v_frame",
"wasm-bindgen",
]
@@ -4608,7 +4679,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"libredox",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
@@ -4654,31 +4725,33 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
dependencies = [
"base64 0.22.1",
"bytes",
+ "encoding_rs",
"futures-core",
"futures-util",
+ "h2 0.4.7",
"http 1.2.0",
"http-body 1.0.1",
"http-body-util",
"hyper 1.5.2",
"hyper-rustls",
+ "hyper-tls",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
+ "native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
- "quinn",
- "rustls 0.23.21",
"rustls-pemfile",
- "rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
+ "system-configuration",
"tokio",
- "tokio-rustls 0.26.1",
+ "tokio-native-tls",
"tokio-util",
"tower",
"tower-service",
@@ -4687,7 +4760,6 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
- "webpki-roots",
"windows-registry",
]
@@ -4826,7 +4898,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
dependencies = [
"once_cell",
- "ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -4847,9 +4918,6 @@ 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"
@@ -4901,6 +4969,15 @@ dependencies = [
]
[[package]]
+name = "schannel"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4943,6 +5020,29 @@ dependencies = [
]
[[package]]
+name = "security-framework"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation 0.9.4",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
name = "self_cell"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5151,7 +5251,7 @@ dependencies = [
"log",
"memmap2",
"rustix 0.38.43",
- "thiserror 1.0.69",
+ "thiserror",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
@@ -5231,7 +5331,7 @@ dependencies = [
"core-graphics 0.24.0",
"drm",
"fastrand 2.3.0",
- "foreign-types",
+ "foreign-types 0.5.0",
"js-sys",
"log",
"memmap2",
@@ -5398,7 +5498,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
- "thiserror 1.0.69",
+ "thiserror",
"walkdir",
"yaml-rust",
]
@@ -5428,6 +5528,27 @@ dependencies = [
]
[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5492,16 +5613,7 @@ 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",
+ "thiserror-impl",
]
[[package]]
@@ -5516,17 +5628,6 @@ dependencies = [
]
[[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"
@@ -5692,6 +5793,16 @@ dependencies = [
]
[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5913,7 +6024,7 @@ dependencies = [
"rustls 0.22.4",
"rustls-pki-types",
"sha1",
- "thiserror 1.0.69",
+ "thiserror",
"url",
"utf-8",
]
@@ -6113,6 +6224,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "vectorial_text"
version = "0.1.0"
dependencies = [
@@ -6523,7 +6640,7 @@ dependencies = [
"raw-window-handle 0.6.2",
"rustc-hash 1.1.0",
"smallvec",
- "thiserror 1.0.69",
+ "thiserror",
"wgpu-hal",
"wgpu-types",
]
@@ -6565,7 +6682,7 @@ dependencies = [
"renderdoc-sys",
"rustc-hash 1.1.0",
"smallvec",
- "thiserror 1.0.69",
+ "thiserror",
"wasm-bindgen",
"web-sys",
"wgpu-types",
@@ -6626,7 +6743,7 @@ dependencies = [
"clipboard_wayland",
"clipboard_x11",
"raw-window-handle 0.6.2",
- "thiserror 1.0.69",
+ "thiserror",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 156d673b..58b54897 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -193,7 +193,7 @@ window_clipboard = "0.4.1"
winit = { git = "https://github.com/iced-rs/winit.git", rev = "11414b6aa45699f038114e61b4ddf5102b2d3b4b" }
[workspace.lints.rust]
-rust_2018_idioms = { level = "forbid", priority = -1 }
+rust_2018_idioms = { level = "deny", priority = -1 }
missing_debug_implementations = "deny"
missing_docs = "deny"
unsafe_code = "deny"
diff --git a/examples/changelog/Cargo.toml b/examples/changelog/Cargo.toml
index eeb7b526..6e314947 100644
--- a/examples/changelog/Cargo.toml
+++ b/examples/changelog/Cargo.toml
@@ -23,5 +23,4 @@ tracing-subscriber = "0.3"
[dependencies.reqwest]
version = "0.12"
-default-features = false
-features = ["json", "rustls-tls"]
+features = ["json"]
diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml
index 61a1b257..8632c8b8 100644
--- a/examples/download_progress/Cargo.toml
+++ b/examples/download_progress/Cargo.toml
@@ -11,5 +11,4 @@ iced.features = ["tokio"]
[dependencies.reqwest]
version = "0.12"
-default-features = false
-features = ["stream", "rustls-tls"]
+features = ["stream"]
diff --git a/examples/gallery/Cargo.toml b/examples/gallery/Cargo.toml
new file mode 100644
index 00000000..573389b1
--- /dev/null
+++ b/examples/gallery/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "gallery"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced.workspace = true
+iced.features = ["tokio", "image", "web-colors", "debug"]
+
+reqwest.version = "0.12"
+reqwest.features = ["json"]
+
+serde.version = "1.0"
+serde.features = ["derive"]
+
+bytes.workspace = true
+image.workspace = true
+tokio.workspace = true
+
+[lints]
+workspace = true
diff --git a/examples/gallery/src/civitai.rs b/examples/gallery/src/civitai.rs
new file mode 100644
index 00000000..d1163013
--- /dev/null
+++ b/examples/gallery/src/civitai.rs
@@ -0,0 +1,148 @@
+use bytes::Bytes;
+use serde::Deserialize;
+use tokio::task;
+
+use std::fmt;
+use std::io;
+use std::sync::Arc;
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct Image {
+ pub id: Id,
+ url: String,
+}
+
+impl Image {
+ pub async fn list() -> Result<Vec<Self>, Error> {
+ let client = reqwest::Client::new();
+
+ #[derive(Deserialize)]
+ struct Response {
+ items: Vec<Image>,
+ }
+
+ let response: Response = client
+ .get(endpoint("/images"))
+ .query(&[
+ ("sort", "Most Reactions"),
+ ("period", "Day"),
+ ("nsfw", "None"),
+ ("limit", "99"),
+ ])
+ .send()
+ .await?
+ .error_for_status()?
+ .json()
+ .await?;
+
+ Ok(response.items)
+ }
+
+ pub async fn download(self, size: Size) -> Result<Rgba, Error> {
+ let client = reqwest::Client::new();
+
+ let bytes = client
+ .get(match size {
+ Size::Original => self.url,
+ Size::Thumbnail => self
+ .url
+ .split("/")
+ .map(|part| {
+ if part.starts_with("width=") {
+ "width=640"
+ } else {
+ part
+ }
+ })
+ .collect::<Vec<_>>()
+ .join("/"),
+ })
+ .send()
+ .await?
+ .error_for_status()?
+ .bytes()
+ .await?;
+
+ let image = task::spawn_blocking(move || {
+ Ok::<_, Error>(
+ image::ImageReader::new(io::Cursor::new(bytes))
+ .with_guessed_format()?
+ .decode()?
+ .to_rgba8(),
+ )
+ })
+ .await??;
+
+ Ok(Rgba {
+ width: image.width(),
+ height: image.height(),
+ pixels: Bytes::from(image.into_raw()),
+ })
+ }
+}
+
+#[derive(
+ Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize,
+)]
+pub struct Id(u32);
+
+#[derive(Clone)]
+pub struct Rgba {
+ pub width: u32,
+ pub height: u32,
+ pub pixels: Bytes,
+}
+
+impl fmt::Debug for Rgba {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Rgba")
+ .field("width", &self.width)
+ .field("height", &self.height)
+ .finish()
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Size {
+ Original,
+ Thumbnail,
+}
+
+fn endpoint(path: &str) -> String {
+ const API_URL: &str = "https://civitai.com/api/v1";
+
+ format!("{API_URL}{path}")
+}
+
+#[derive(Debug, Clone)]
+#[allow(dead_code)]
+pub enum Error {
+ RequestFailed(Arc<reqwest::Error>),
+ IOFailed(Arc<io::Error>),
+ JoinFailed(Arc<task::JoinError>),
+ ImageDecodingFailed(Arc<image::ImageError>),
+}
+
+impl From<reqwest::Error> for Error {
+ fn from(error: reqwest::Error) -> Self {
+ Self::RequestFailed(Arc::new(error))
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(error: io::Error) -> Self {
+ Self::IOFailed(Arc::new(error))
+ }
+}
+
+impl From<task::JoinError> for Error {
+ fn from(error: task::JoinError) -> Self {
+ Self::JoinFailed(Arc::new(error))
+ }
+}
+
+impl From<image::ImageError> for Error {
+ fn from(error: image::ImageError) -> Self {
+ Self::ImageDecodingFailed(Arc::new(error))
+ }
+}
diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs
new file mode 100644
index 00000000..a7c107f7
--- /dev/null
+++ b/examples/gallery/src/main.rs
@@ -0,0 +1,308 @@
+//! A simple gallery that displays the daily featured images of Civitai.
+//!
+//! Showcases lazy loading of images in the background, as well as
+//! some smooth animations.
+mod civitai;
+
+use crate::civitai::{Error, Id, Image, Rgba, Size};
+
+use iced::animation;
+use iced::time::Instant;
+use iced::widget::{
+ button, center_x, container, horizontal_space, image, mouse_area, opaque,
+ pop, row, scrollable, stack,
+};
+use iced::window;
+use iced::{
+ color, Animation, ContentFit, Element, Fill, Subscription, Task, Theme,
+};
+
+use std::collections::HashMap;
+
+fn main() -> iced::Result {
+ iced::application("Gallery - Iced", Gallery::update, Gallery::view)
+ .subscription(Gallery::subscription)
+ .theme(Gallery::theme)
+ .run_with(Gallery::new)
+}
+
+struct Gallery {
+ images: Vec<Image>,
+ thumbnails: HashMap<Id, Thumbnail>,
+ viewer: Viewer,
+ now: Instant,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ ImagesListed(Result<Vec<Image>, Error>),
+ ImagePoppedIn(Id),
+ ImageDownloaded(Result<Rgba, Error>),
+ ThumbnailDownloaded(Id, Result<Rgba, Error>),
+ ThumbnailHovered(Id, bool),
+ Open(Id),
+ Close,
+ Animate(Instant),
+}
+
+impl Gallery {
+ pub fn new() -> (Self, Task<Message>) {
+ (
+ Self {
+ images: Vec::new(),
+ thumbnails: HashMap::new(),
+ viewer: Viewer::new(),
+ now: Instant::now(),
+ },
+ Task::perform(Image::list(), Message::ImagesListed),
+ )
+ }
+
+ pub fn theme(&self) -> Theme {
+ Theme::TokyoNight
+ }
+
+ pub fn subscription(&self) -> Subscription<Message> {
+ let is_animating = self
+ .thumbnails
+ .values()
+ .any(|thumbnail| thumbnail.is_animating(self.now))
+ || self.viewer.is_animating(self.now);
+
+ if is_animating {
+ window::frames().map(Message::Animate)
+ } else {
+ Subscription::none()
+ }
+ }
+
+ pub fn update(&mut self, message: Message) -> Task<Message> {
+ match message {
+ Message::ImagesListed(Ok(images)) => {
+ self.images = images;
+
+ Task::none()
+ }
+ Message::ImagePoppedIn(id) => {
+ let Some(image) = self
+ .images
+ .iter()
+ .find(|candidate| candidate.id == id)
+ .cloned()
+ else {
+ return Task::none();
+ };
+
+ Task::perform(image.download(Size::Thumbnail), move |result| {
+ Message::ThumbnailDownloaded(id, result)
+ })
+ }
+ Message::ImageDownloaded(Ok(rgba)) => {
+ self.viewer.show(rgba);
+
+ Task::none()
+ }
+ Message::ThumbnailDownloaded(id, Ok(rgba)) => {
+ let thumbnail = Thumbnail {
+ handle: image::Handle::from_rgba(
+ rgba.width,
+ rgba.height,
+ rgba.pixels,
+ ),
+ fade_in: Animation::new(false).slow().go(true),
+ zoom: Animation::new(false)
+ .quick()
+ .easing(animation::Easing::EaseInOut),
+ };
+
+ let _ = self.thumbnails.insert(id, thumbnail);
+
+ Task::none()
+ }
+ Message::ThumbnailHovered(id, is_hovered) => {
+ if let Some(thumbnail) = self.thumbnails.get_mut(&id) {
+ thumbnail.zoom.go_mut(is_hovered);
+ }
+
+ Task::none()
+ }
+ Message::Open(id) => {
+ let Some(image) = self
+ .images
+ .iter()
+ .find(|candidate| candidate.id == id)
+ .cloned()
+ else {
+ return Task::none();
+ };
+
+ self.viewer.open();
+
+ Task::perform(
+ image.download(Size::Original),
+ Message::ImageDownloaded,
+ )
+ }
+ Message::Close => {
+ self.viewer.close();
+
+ Task::none()
+ }
+ Message::Animate(now) => {
+ self.now = now;
+
+ Task::none()
+ }
+ Message::ImagesListed(Err(error))
+ | Message::ImageDownloaded(Err(error))
+ | Message::ThumbnailDownloaded(_, Err(error)) => {
+ dbg!(error);
+
+ Task::none()
+ }
+ }
+ }
+
+ pub fn view(&self) -> Element<'_, Message> {
+ let gallery = row(self.images.iter().map(|image| {
+ card(image, self.thumbnails.get(&image.id), self.now)
+ }))
+ .spacing(10)
+ .wrap();
+
+ let content =
+ container(scrollable(center_x(gallery)).spacing(10)).padding(10);
+
+ let viewer = self.viewer.view(self.now);
+
+ stack![content, viewer].into()
+ }
+}
+
+struct Thumbnail {
+ handle: image::Handle,
+ fade_in: Animation<bool>,
+ zoom: Animation<bool>,
+}
+
+impl Thumbnail {
+ fn is_animating(&self, now: Instant) -> bool {
+ self.fade_in.in_progress(now) || self.zoom.in_progress(now)
+ }
+}
+
+fn card<'a>(
+ metadata: &'a Image,
+ thumbnail: Option<&'a Thumbnail>,
+ now: Instant,
+) -> Element<'a, Message> {
+ let image: Element<'_, _> = if let Some(thumbnail) = thumbnail {
+ image(&thumbnail.handle)
+ .width(Fill)
+ .height(Fill)
+ .content_fit(ContentFit::Cover)
+ .opacity(thumbnail.fade_in.interpolate(0.0, 1.0, now))
+ .scale(thumbnail.zoom.interpolate(1.0, 1.1, now))
+ .into()
+ } else {
+ horizontal_space().into()
+ };
+
+ let card = mouse_area(
+ container(image)
+ .width(320)
+ .height(410)
+ .style(container::dark),
+ )
+ .on_enter(Message::ThumbnailHovered(metadata.id, true))
+ .on_exit(Message::ThumbnailHovered(metadata.id, false));
+
+ if thumbnail.is_some() {
+ button(card)
+ .on_press(Message::Open(metadata.id))
+ .padding(0)
+ .style(button::text)
+ .into()
+ } else {
+ pop(card)
+ .on_show(Message::ImagePoppedIn(metadata.id))
+ .into()
+ }
+}
+
+struct Viewer {
+ image: Option<image::Handle>,
+ background_fade_in: Animation<bool>,
+ image_fade_in: Animation<bool>,
+}
+
+impl Viewer {
+ fn new() -> Self {
+ Self {
+ image: None,
+ background_fade_in: Animation::new(false)
+ .very_slow()
+ .easing(animation::Easing::EaseInOut),
+ image_fade_in: Animation::new(false)
+ .very_slow()
+ .easing(animation::Easing::EaseInOut),
+ }
+ }
+
+ fn open(&mut self) {
+ self.image = None;
+ self.background_fade_in.go_mut(true);
+ }
+
+ fn show(&mut self, rgba: Rgba) {
+ self.image = Some(image::Handle::from_rgba(
+ rgba.width,
+ rgba.height,
+ rgba.pixels,
+ ));
+ self.background_fade_in.go_mut(true);
+ self.image_fade_in.go_mut(true);
+ }
+
+ fn close(&mut self) {
+ self.background_fade_in.go_mut(false);
+ self.image_fade_in.go_mut(false);
+ }
+
+ fn is_animating(&self, now: Instant) -> bool {
+ self.background_fade_in.in_progress(now)
+ || self.image_fade_in.in_progress(now)
+ }
+
+ fn view(&self, now: Instant) -> Element<'_, Message> {
+ let opacity = self.background_fade_in.interpolate(0.0, 0.8, now);
+
+ let image: Element<'_, _> = if let Some(handle) = &self.image {
+ image(handle)
+ .width(Fill)
+ .height(Fill)
+ .opacity(self.image_fade_in.interpolate(0.0, 1.0, now))
+ .scale(self.image_fade_in.interpolate(1.5, 1.0, now))
+ .into()
+ } else {
+ horizontal_space().into()
+ };
+
+ if opacity > 0.0 {
+ opaque(
+ mouse_area(
+ container(image)
+ .center(Fill)
+ .style(move |_theme| {
+ container::Style::default()
+ .background(color!(0x000000, opacity))
+ })
+ .padding(20),
+ )
+ .on_press(Message::Close),
+ )
+ } else {
+ horizontal_space().into()
+ }
+ }
+}
diff --git a/examples/pokedex/Cargo.toml b/examples/pokedex/Cargo.toml
index 1a6d5445..b3be4e14 100644
--- a/examples/pokedex/Cargo.toml
+++ b/examples/pokedex/Cargo.toml
@@ -17,8 +17,7 @@ features = ["derive"]
[dependencies.reqwest]
version = "0.12"
-default-features = false
-features = ["json", "rustls-tls"]
+features = ["json"]
[dependencies.rand]
version = "0.8"