Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91b71b97dc | ||
|
|
b9c4856e3f | ||
|
|
79dcc156c9 | ||
|
|
c0fcc0345c | ||
|
|
444a2cd138 | ||
|
|
23fb71ee56 | ||
|
|
01a9b8633d | ||
|
|
29d390da52 | ||
|
|
7b318e3271 | ||
|
|
89491a5949 | ||
|
|
a08f61a80c | ||
|
|
4d9839f380 | ||
|
|
b3d3da5dab | ||
|
|
f5864da3c4 | ||
|
|
0106257be0 | ||
|
|
bafaa1db6d |
No files matched your search
Generated
+229
-203
@@ -234,18 +234,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.10.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2f926cc3060f09db9ebc5b52823d85268d24bb917e472c0c4bea35780a7d"
|
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-vec"
|
name = "bit-vec"
|
||||||
version = "0.9.1"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
@@ -274,6 +274,12 @@ dependencies = [
|
|||||||
"core2",
|
"core2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block2"
|
name = "block2"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -283,15 +289,6 @@ dependencies = [
|
|||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block2"
|
|
||||||
version = "0.6.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
|
||||||
dependencies = [
|
|
||||||
"objc2 0.6.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "built"
|
name = "built"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -394,9 +391,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg_aliases"
|
name = "cfg_aliases"
|
||||||
version = "0.2.2"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clipboard-win"
|
||||||
@@ -409,9 +406,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.13.1"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681"
|
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
@@ -453,6 +450,16 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
@@ -466,8 +473,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
|
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-graphics-types",
|
"core-graphics-types 0.1.3",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -479,7 +486,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"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.10.0",
|
||||||
|
"core-foundation 0.10.1",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -721,6 +739,15 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "font-types"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "font-types"
|
name = "font-types"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@@ -744,7 +771,7 @@ dependencies = [
|
|||||||
"objc2-core-text",
|
"objc2-core-text",
|
||||||
"objc2-foundation 0.3.2",
|
"objc2-foundation 0.3.2",
|
||||||
"parlance",
|
"parlance",
|
||||||
"read-fonts",
|
"read-fonts 0.41.0",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows",
|
"windows",
|
||||||
@@ -779,30 +806,6 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"pin-project-lite",
|
|
||||||
"slab",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fxhash"
|
name = "fxhash"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -857,9 +860,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.17.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5"
|
checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
@@ -890,6 +893,26 @@ dependencies = [
|
|||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-descriptor"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"gpu-descriptor-types",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpu-descriptor-types"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "half"
|
name = "half"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
@@ -910,7 +933,7 @@ checksum = "c03d949a14aa089bbb282f7dd76a498a7f684428e4257202efc119ec010376f9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"read-fonts",
|
"read-fonts 0.41.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -949,6 +972,12 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hexf-parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@@ -1079,9 +1108,9 @@ checksum = "ae293c039020f9ec10710af98d29ce6aa2051486638b49c9a6409f3b4a9e98ad"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.10"
|
version = "0.25.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
|
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
@@ -1097,8 +1126,8 @@ dependencies = [
|
|||||||
"rayon",
|
"rayon",
|
||||||
"rgb",
|
"rgb",
|
||||||
"tiff",
|
"tiff",
|
||||||
"zune-core",
|
"zune-core 0.5.0",
|
||||||
"zune-jpeg",
|
"zune-jpeg 0.5.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1218,12 +1247,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.105"
|
version = "0.3.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e"
|
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"once_cell",
|
||||||
"futures-util",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1252,9 +1280,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.189"
|
version = "0.2.179"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -1347,6 +1375,15 @@ dependencies = [
|
|||||||
"imgref",
|
"imgref",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "malloc_buf"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maybe-rayon"
|
name = "maybe-rayon"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1372,6 +1409,21 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metal"
|
||||||
|
version = "0.33.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"block",
|
||||||
|
"core-graphics-types 0.2.0",
|
||||||
|
"foreign-types",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -1384,9 +1436,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "moxcms"
|
name = "moxcms"
|
||||||
version = "0.8.1"
|
version = "0.7.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
|
checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pxfm",
|
"pxfm",
|
||||||
@@ -1394,9 +1446,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a616d2fb8c89516ac2723a581f69d6c18576046bed761bd6b305e5618e6ae130"
|
checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
@@ -1405,11 +1457,11 @@ dependencies = [
|
|||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"half",
|
"half",
|
||||||
"hashbrown 0.17.1",
|
"hashbrown 0.16.1",
|
||||||
|
"hexf-parse",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"libm",
|
"libm",
|
||||||
"log",
|
"log",
|
||||||
"naga-types",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
@@ -1418,18 +1470,6 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "naga-types"
|
|
||||||
version = "30.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "590afbf58a6f4f62873cd5cff4468061844bafa1cdf399cc954537c22d768d49"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"indexmap",
|
|
||||||
"rustc-hash",
|
|
||||||
"thiserror 2.0.17",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ndk"
|
name = "ndk"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -1554,6 +1594,15 @@ dependencies = [
|
|||||||
"syn 2.0.113",
|
"syn 2.0.113",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||||
|
dependencies = [
|
||||||
|
"malloc_buf",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc-sys"
|
name = "objc-sys"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@@ -1586,13 +1635,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
|
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"libc",
|
"libc",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-core-data",
|
"objc2-core-data",
|
||||||
"objc2-core-image",
|
"objc2-core-image",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
"objc2-quartz-core 0.2.2",
|
"objc2-quartz-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1614,7 +1663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
|
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-core-location",
|
"objc2-core-location",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
@@ -1626,7 +1675,7 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
|
checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
]
|
]
|
||||||
@@ -1638,7 +1687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
|
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
]
|
]
|
||||||
@@ -1673,10 +1722,10 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
|
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
"objc2-metal 0.2.2",
|
"objc2-metal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1685,7 +1734,7 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
|
checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-contacts",
|
"objc2-contacts",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
@@ -1714,7 +1763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
|
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"libc",
|
"libc",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
@@ -1748,7 +1797,7 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
|
checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-app-kit 0.2.2",
|
"objc2-app-kit 0.2.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
@@ -1761,23 +1810,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
|
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-metal"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"block2 0.6.2",
|
|
||||||
"objc2 0.6.4",
|
|
||||||
"objc2-foundation 0.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-quartz-core"
|
name = "objc2-quartz-core"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -1785,24 +1822,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
|
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
"objc2-metal 0.2.2",
|
"objc2-metal",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-quartz-core"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"objc2 0.6.4",
|
|
||||||
"objc2-core-foundation",
|
|
||||||
"objc2-core-graphics",
|
|
||||||
"objc2-foundation 0.3.2",
|
|
||||||
"objc2-metal 0.3.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1822,7 +1845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
|
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-cloud-kit",
|
"objc2-cloud-kit",
|
||||||
"objc2-core-data",
|
"objc2-core-data",
|
||||||
@@ -1830,7 +1853,7 @@ dependencies = [
|
|||||||
"objc2-core-location",
|
"objc2-core-location",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
"objc2-link-presentation",
|
"objc2-link-presentation",
|
||||||
"objc2-quartz-core 0.2.2",
|
"objc2-quartz-core",
|
||||||
"objc2-symbols",
|
"objc2-symbols",
|
||||||
"objc2-uniform-type-identifiers",
|
"objc2-uniform-type-identifiers",
|
||||||
"objc2-user-notifications",
|
"objc2-user-notifications",
|
||||||
@@ -1842,7 +1865,7 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
|
checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
]
|
]
|
||||||
@@ -1854,7 +1877,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
|
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-core-location",
|
"objc2-core-location",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
@@ -1948,7 +1971,7 @@ dependencies = [
|
|||||||
"linebender_resource_handle",
|
"linebender_resource_handle",
|
||||||
"parlance",
|
"parlance",
|
||||||
"parley_data",
|
"parley_data",
|
||||||
"skrifa",
|
"skrifa 0.44.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2252,9 +2275,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ravif"
|
name = "ravif"
|
||||||
version = "0.13.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45"
|
checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"avif-serialize",
|
"avif-serialize",
|
||||||
"imgref",
|
"imgref",
|
||||||
@@ -2271,18 +2294,6 @@ version = "0.6.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "raw-window-metal"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135"
|
|
||||||
dependencies = [
|
|
||||||
"objc2 0.6.4",
|
|
||||||
"objc2-core-foundation",
|
|
||||||
"objc2-foundation 0.3.2",
|
|
||||||
"objc2-quartz-core 0.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@@ -2303,6 +2314,16 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "read-fonts"
|
||||||
|
version = "0.36.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5eaa2941a4c05443ee3a7b26ab076a553c343ad5995230cc2b1d3e993bdc6345"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"font-types 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "read-fonts"
|
name = "read-fonts"
|
||||||
version = "0.41.0"
|
version = "0.41.0"
|
||||||
@@ -2310,7 +2331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709"
|
checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"font-types",
|
"font-types 0.12.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2485,6 +2506,16 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skrifa"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9eb0b904a04d09bd68c65d946617b8ff733009999050f3b851c32fb3cfb60e"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"read-fonts 0.36.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "skrifa"
|
name = "skrifa"
|
||||||
version = "0.44.0"
|
version = "0.44.0"
|
||||||
@@ -2492,7 +2523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68"
|
checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"read-fonts",
|
"read-fonts 0.41.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2552,9 +2583,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spirv"
|
name = "spirv"
|
||||||
version = "0.4.0+sdk-1.4.341.0"
|
version = "0.3.0+sdk-1.3.268.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f"
|
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
]
|
]
|
||||||
@@ -2583,7 +2614,7 @@ version = "0.2.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e"
|
checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"skrifa",
|
"skrifa 0.39.0",
|
||||||
"yazi",
|
"yazi",
|
||||||
"zeno",
|
"zeno",
|
||||||
]
|
]
|
||||||
@@ -2672,16 +2703,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.11.3"
|
version = "0.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
|
checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fax",
|
"fax",
|
||||||
"flate2",
|
"flate2",
|
||||||
"half",
|
"half",
|
||||||
"quick-error",
|
"quick-error",
|
||||||
"weezl",
|
"weezl",
|
||||||
"zune-jpeg",
|
"zune-jpeg 0.4.21",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2854,9 +2885,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.128"
|
version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf"
|
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -2867,19 +2898,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.78"
|
version = "0.4.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928"
|
checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.128"
|
version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed"
|
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -2887,22 +2921,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.128"
|
version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a"
|
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 3.0.5",
|
"syn 2.0.113",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.128"
|
version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e"
|
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -3018,9 +3052,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.105"
|
version = "0.3.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8"
|
checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -3044,9 +3078,9 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu"
|
name = "wgpu"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "527ccdf43dd5b2e8676eed9984ce00e2bbb0a1b85b70c1969dcb6cd2eb55ab9e"
|
checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
@@ -3054,7 +3088,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"document-features",
|
"document-features",
|
||||||
"hashbrown 0.17.1",
|
"hashbrown 0.16.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
@@ -3074,9 +3108,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c018fce9b6270aa203c2fdd56f3cce996713534bd757e4ea58c8560b121f14"
|
checksum = "8bb4c8b5db5f00e56f1f08869d870a0dff7c8bc7ebc01091fec140b0cf0211a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
@@ -3085,11 +3119,10 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"document-features",
|
"document-features",
|
||||||
"hashbrown 0.17.1",
|
"hashbrown 0.16.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
"naga-types",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
@@ -3102,70 +3135,66 @@ dependencies = [
|
|||||||
"wgpu-core-deps-emscripten",
|
"wgpu-core-deps-emscripten",
|
||||||
"wgpu-core-deps-windows-linux-android",
|
"wgpu-core-deps-windows-linux-android",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
"wgpu-naga-bridge",
|
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core-deps-apple"
|
name = "wgpu-core-deps-apple"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "061f3d319a40d39d00b1ecc2c33b89fe21d4e6fe01859df3500a3a8ecccd6b68"
|
checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core-deps-emscripten"
|
name = "wgpu-core-deps-emscripten"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d98b86cf4abf524a902dd35f18ca6a3f08fc2ae9847c8f10b48e30491b1f0b86"
|
checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core-deps-windows-linux-android"
|
name = "wgpu-core-deps-windows-linux-android"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7586165fd5f6d881cb9ce4bb71f40d6caab2c0f1837e3fc1d9788a197fb6004f"
|
checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-hal"
|
name = "wgpu-hal"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6b7fb58561a792bc237628ba0792e332de418fefe145f13b5ed8201e6d52f58"
|
checksum = "293080d77fdd14d6b08a67c5487dfddbf874534bb7921526db56a7b75d7e3bef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_system_properties",
|
"android_system_properties",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"ash",
|
"ash",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.6.2",
|
"block",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
|
"core-graphics-types 0.2.0",
|
||||||
"glow",
|
"glow",
|
||||||
"glutin_wgl_sys",
|
"glutin_wgl_sys",
|
||||||
"gpu-allocator",
|
"gpu-allocator",
|
||||||
"hashbrown 0.17.1",
|
"gpu-descriptor",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"khronos-egl",
|
"khronos-egl",
|
||||||
"libc",
|
"libc",
|
||||||
"libloading",
|
"libloading",
|
||||||
"log",
|
"log",
|
||||||
|
"metal",
|
||||||
"naga",
|
"naga",
|
||||||
"naga-types",
|
|
||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
"objc2 0.6.4",
|
"objc",
|
||||||
"objc2-core-foundation",
|
|
||||||
"objc2-core-graphics",
|
|
||||||
"objc2-foundation 0.3.2",
|
|
||||||
"objc2-metal 0.3.2",
|
|
||||||
"objc2-quartz-core 0.3.2",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
@@ -3174,44 +3203,26 @@ dependencies = [
|
|||||||
"profiling",
|
"profiling",
|
||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"raw-window-metal",
|
|
||||||
"renderdoc-sys",
|
"renderdoc-sys",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"static_assertions",
|
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wayland-sys",
|
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"wgpu-naga-bridge",
|
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
"windows",
|
"windows",
|
||||||
"windows-core",
|
"windows-core",
|
||||||
"windows-result",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wgpu-naga-bridge"
|
|
||||||
version = "30.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2f62e73117bb7a62bfd9c5a5841438a823f6566c6442a808ee269d2d055c081"
|
|
||||||
dependencies = [
|
|
||||||
"naga",
|
|
||||||
"wgpu-types",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-types"
|
name = "wgpu-types"
|
||||||
version = "30.0.1"
|
version = "28.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99dad6f1fbdbbdb4c278a6508b059d44688f5cebddf78d005a46a31340269286"
|
checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"naga-types",
|
|
||||||
"raw-window-handle",
|
|
||||||
"static_assertions",
|
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3575,12 +3586,12 @@ dependencies = [
|
|||||||
"android-activity",
|
"android-activity",
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2 0.5.1",
|
"block2",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"calloop",
|
"calloop",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"cursor-icon",
|
"cursor-icon",
|
||||||
"dpi",
|
"dpi",
|
||||||
@@ -3847,6 +3858,12 @@ dependencies = [
|
|||||||
"syn 3.0.5",
|
"syn 3.0.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-core"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zune-core"
|
name = "zune-core"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -3862,11 +3879,20 @@ dependencies = [
|
|||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-jpeg"
|
||||||
|
version = "0.4.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
|
||||||
|
dependencies = [
|
||||||
|
"zune-core 0.4.12",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zune-jpeg"
|
name = "zune-jpeg"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
|
checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zune-core",
|
"zune-core 0.5.0",
|
||||||
]
|
]
|
||||||
+2
-2
@@ -29,9 +29,9 @@ edition = "2024"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
winit = "0.30.12"
|
winit = "0.30.12"
|
||||||
wgpu = "30.0.1"
|
wgpu = "28.0.0"
|
||||||
bytemuck = "1.23.1"
|
bytemuck = "1.23.1"
|
||||||
image = "0.25.10"
|
image = "0.25.6"
|
||||||
parley = "0.11.1"
|
parley = "0.11.1"
|
||||||
swash = "0.2.10"
|
swash = "0.2.10"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ type EventData<Rsc, E> = (E, Rc<dyn for<'a> EventFn<Rsc, <E as Event>::Data<'a>>
|
|||||||
pub struct TypeEventManager<Rsc: HasEvents, E: Event> {
|
pub struct TypeEventManager<Rsc: HasEvents, E: Event> {
|
||||||
// TODO: reduce visiblity!!
|
// TODO: reduce visiblity!!
|
||||||
pub active: HashMap<LayerId, HashMap<WidgetId, E::State>>,
|
pub active: HashMap<LayerId, HashMap<WidgetId, E::State>>,
|
||||||
pub global: E::Global,
|
|
||||||
map: HashMap<WidgetId, Vec<EventData<Rsc, E>>>,
|
map: HashMap<WidgetId, Vec<EventData<Rsc, E>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +107,6 @@ impl<Rsc: HasEvents, E: Event> Default for TypeEventManager<Rsc, E> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
active: Default::default(),
|
active: Default::default(),
|
||||||
global: Default::default(),
|
|
||||||
map: Default::default(),
|
map: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,13 +138,11 @@ impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
|||||||
pub fn run_fn<'a>(
|
pub fn run_fn<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: impl IdLike,
|
id: impl IdLike,
|
||||||
) -> impl for<'b> FnOnce(EventCtx<'_, Rsc, E::Data<'b>>, &mut Rsc) -> bool + 'a {
|
) -> impl for<'b> FnOnce(EventCtx<'_, Rsc, E::Data<'b>>, &mut Rsc) + 'a {
|
||||||
let fs = self.map.get(&id.id()).cloned().unwrap_or_default();
|
let fs = self.map.get(&id.id()).cloned().unwrap_or_default();
|
||||||
move |ctx, rsc| {
|
move |ctx, rsc| {
|
||||||
let mut consumed = false;
|
|
||||||
for (e, f) in fs {
|
for (e, f) in fs {
|
||||||
if let Some(data) = e.should_run(&ctx.data) {
|
if let Some(data) = e.should_run(&ctx.data) {
|
||||||
consumed |= e.consumes(&data);
|
|
||||||
f(
|
f(
|
||||||
EventCtx {
|
EventCtx {
|
||||||
state: ctx.state,
|
state: ctx.state,
|
||||||
@@ -156,7 +152,6 @@ impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
consumed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,19 +9,10 @@ pub use rsc::*;
|
|||||||
pub trait Event: Sized + 'static + Clone {
|
pub trait Event: Sized + 'static + Clone {
|
||||||
type Data<'a>: Clone = ();
|
type Data<'a>: Clone = ();
|
||||||
type State: Default = ();
|
type State: Default = ();
|
||||||
/// State the whole event type keeps, rather than one copy per widget.
|
|
||||||
type Global: Default = ();
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
||||||
Some(data.clone())
|
Some(data.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether having run on this data uses up whatever triggered it, so
|
|
||||||
/// nothing further should see it.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn consumes(&self, data: &Self::Data<'_>) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventLike {
|
pub trait EventLike {
|
||||||
|
|||||||
@@ -21,13 +21,12 @@ pub trait HasEvents: Sized + UiRsc + HasState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait RunEvents: HasEvents {
|
pub trait RunEvents: HasEvents {
|
||||||
/// Whether anything that ran used up what triggered it.
|
|
||||||
fn run_event<E: EventLike>(
|
fn run_event<E: EventLike>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: impl IdLike,
|
id: impl IdLike,
|
||||||
data: <E::Event as Event>::Data<'_>,
|
data: <E::Event as Event>::Data<'_>,
|
||||||
state: &mut Self::State,
|
state: &mut Self::State,
|
||||||
) -> bool {
|
) {
|
||||||
let f = self.events_mut().get_type::<E>().run_fn(id);
|
let f = self.events_mut().get_type::<E>().run_fn(id);
|
||||||
f(EventCtx { state, data }, self)
|
f(EventCtx { state, data }, self)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ impl Display for UiRegion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug)]
|
||||||
pub struct PixelRegion {
|
pub struct PixelRegion {
|
||||||
pub top_left: Vec2,
|
pub top_left: Vec2,
|
||||||
pub bot_right: Vec2,
|
pub bot_right: Vec2,
|
||||||
|
|||||||
@@ -186,8 +186,8 @@ impl UiRenderNode {
|
|||||||
for source in ®istry.sources()[self.primitives.len()..] {
|
for source in ®istry.sources()[self.primitives.len()..] {
|
||||||
let render = (source.render)(device, queue);
|
let render = (source.render)(device, queue);
|
||||||
let data_layout = Self::data_layout(device, source.stride);
|
let data_layout = Self::data_layout(device, source.stride);
|
||||||
let mut groups = vec![Some(&self.shared_layout), Some(&data_layout)];
|
let mut groups = vec![&self.shared_layout, &data_layout];
|
||||||
groups.extend(render.layout().map(Some));
|
groups.extend(render.layout());
|
||||||
let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||||
label: Some(source.label),
|
label: Some(source.label),
|
||||||
bind_group_layouts: &groups,
|
bind_group_layouts: &groups,
|
||||||
@@ -219,7 +219,7 @@ impl UiRenderNode {
|
|||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
module: &module,
|
module: &module,
|
||||||
entry_point: Some("vs_main"),
|
entry_point: Some("vs_main"),
|
||||||
buffers: &[Some(PrimitiveInstance::desc())],
|
buffers: &[PrimitiveInstance::desc()],
|
||||||
compilation_options: Default::default(),
|
compilation_options: Default::default(),
|
||||||
},
|
},
|
||||||
fragment: Some(FragmentState {
|
fragment: Some(FragmentState {
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
use crate::{LayerId, MaskIdx, PrimitiveHandle, Size, TextureHandle, UiRegion, WidgetId};
|
use crate::{LayerId, MaskIdx, PrimitiveHandle, TextureHandle, UiRegion, WidgetId};
|
||||||
|
|
||||||
/// important non rendering data for retained drawing
|
/// important non rendering data for retained drawing
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ActiveData {
|
pub struct ActiveData {
|
||||||
pub id: WidgetId,
|
pub id: WidgetId,
|
||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
/// What the widget said it used of `region`, the last time it drew.
|
|
||||||
pub size: Size,
|
|
||||||
pub parent: Option<WidgetId>,
|
pub parent: Option<WidgetId>,
|
||||||
pub textures: Vec<TextureHandle>,
|
pub textures: Vec<TextureHandle>,
|
||||||
pub primitives: Vec<PrimitiveHandle>,
|
pub primitives: Vec<PrimitiveHandle>,
|
||||||
pub children: Vec<WidgetId>,
|
pub children: Vec<WidgetId>,
|
||||||
/// The children whose size this widget read while drawing.
|
|
||||||
pub size_deps: Vec<WidgetId>,
|
|
||||||
pub mask: MaskIdx,
|
pub mask: MaskIdx,
|
||||||
pub layer: LayerId,
|
pub layer: LayerId,
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use crate::{BothAxis, Len, UiVec2, WidgetId, util::HashMap};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Cache {
|
||||||
|
pub size: BothAxis<HashMap<WidgetId, (UiVec2, Len)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cache {
|
||||||
|
pub fn remove(&mut self, id: WidgetId) {
|
||||||
|
self.size.x.remove(&id);
|
||||||
|
self.size.y.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.size.x.clear();
|
||||||
|
self.size.y.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,15 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod active;
|
mod active;
|
||||||
|
mod cache;
|
||||||
mod painter;
|
mod painter;
|
||||||
mod render_state;
|
mod render_state;
|
||||||
|
mod size;
|
||||||
|
|
||||||
pub use active::*;
|
pub use active::*;
|
||||||
pub use painter::{Painter, PrimitiveLike};
|
pub use painter::{Painter, PrimitiveLike};
|
||||||
pub use render_state::*;
|
pub use render_state::*;
|
||||||
|
pub use size::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct UiData {
|
pub struct UiData {
|
||||||
|
|||||||
+21
-87
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Axis, Len, RenderedText, Size, StrongWidget, TextAttrs, TextBuffer, TextData, TextureHandle,
|
Axis, Len, RenderedText, Size, SizeCtx, StrongWidget, TextAttrs, TextBuffer, TextData,
|
||||||
UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId,
|
TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, Widget, WidgetId,
|
||||||
render::{
|
render::{
|
||||||
GlyphPrimitive, Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, PrimitiveKind,
|
GlyphPrimitive, Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, PrimitiveKind,
|
||||||
TexturePrimitive,
|
TexturePrimitive,
|
||||||
@@ -18,9 +18,6 @@ pub struct Painter<'a> {
|
|||||||
pub(super) textures: Vec<TextureHandle>,
|
pub(super) textures: Vec<TextureHandle>,
|
||||||
pub(super) primitives: Vec<PrimitiveHandle>,
|
pub(super) primitives: Vec<PrimitiveHandle>,
|
||||||
pub(super) children: Vec<WidgetId>,
|
pub(super) children: Vec<WidgetId>,
|
||||||
/// The children whose size this widget read while drawing.
|
|
||||||
pub(super) size_deps: Vec<WidgetId>,
|
|
||||||
pub(super) size: Option<Size>,
|
|
||||||
pub layer: usize,
|
pub layer: usize,
|
||||||
pub(super) id: WidgetId,
|
pub(super) id: WidgetId,
|
||||||
}
|
}
|
||||||
@@ -70,40 +67,20 @@ impl<'a> Painter<'a> {
|
|||||||
self.mask = self.rsc.ui_mut().masks.push(Mask { region });
|
self.mask = self.rsc.ui_mut().masks.push(Mask { region });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// States what this widget uses of the box it was handed, once per draw.
|
|
||||||
/// This is the size a parent reads.
|
|
||||||
pub fn set_size(&mut self, size: impl Into<Size>) {
|
|
||||||
debug_assert!(
|
|
||||||
self.size.is_none(),
|
|
||||||
"{} set its size twice in one draw",
|
|
||||||
self.label(),
|
|
||||||
);
|
|
||||||
self.size = Some(size.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a widget within this widget's region.
|
/// Draws a widget within this widget's region.
|
||||||
pub fn widget<W: ?Sized>(&mut self, id: &StrongWidget<W>) -> DrawResult<'_, 'a> {
|
pub fn widget<W: ?Sized>(&mut self, id: &StrongWidget<W>) {
|
||||||
self.widget_at(id, self.region)
|
self.widget_at(id, self.region);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a widget somewhere within this one.
|
/// Draws a widget somewhere within this one.
|
||||||
/// Useful for drawing child widgets in select areas.
|
/// Useful for drawing child widgets in select areas.
|
||||||
pub fn widget_within<W: ?Sized>(
|
pub fn widget_within<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) {
|
||||||
&mut self,
|
self.widget_at(id, region.within(&self.region));
|
||||||
id: &StrongWidget<W>,
|
|
||||||
region: UiRegion,
|
|
||||||
) -> DrawResult<'_, 'a> {
|
|
||||||
let region = region.within(&self.region);
|
|
||||||
self.widget_at(id, region)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_at<W: ?Sized>(
|
fn widget_at<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) {
|
||||||
&mut self,
|
|
||||||
id: &StrongWidget<W>,
|
|
||||||
region: UiRegion,
|
|
||||||
) -> DrawResult<'_, 'a> {
|
|
||||||
self.children.push(id.id());
|
self.children.push(id.id());
|
||||||
let size = self.state.draw_inner(
|
self.state.draw_inner(
|
||||||
self.layer,
|
self.layer,
|
||||||
id.id(),
|
id.id(),
|
||||||
region,
|
region,
|
||||||
@@ -112,44 +89,6 @@ impl<'a> Painter<'a> {
|
|||||||
None,
|
None,
|
||||||
self.rsc,
|
self.rsc,
|
||||||
);
|
);
|
||||||
DrawResult {
|
|
||||||
child: id.id(),
|
|
||||||
painter: self,
|
|
||||||
size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// What a child says its length is without being drawn, if it can say.
|
|
||||||
pub fn size_hint<W: ?Sized>(&self, id: &StrongWidget<W>, axis: Axis) -> Option<Len> {
|
|
||||||
self.rsc.widgets().get_dyn(id.id())?.size_hint(axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gives a child its final box once this widget knows what that is,
|
|
||||||
/// keeping the drawing it already has where it can.
|
|
||||||
pub fn place<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) -> Size {
|
|
||||||
let region = region.within(&self.region);
|
|
||||||
let id = id.id();
|
|
||||||
// Choosing a child's box depends on its size, from a draw or a hint.
|
|
||||||
// Claiming it is separate: one left out of `children` is removed.
|
|
||||||
if !self.size_deps.contains(&id) {
|
|
||||||
self.size_deps.push(id);
|
|
||||||
}
|
|
||||||
if !self.children.contains(&id) {
|
|
||||||
self.children.push(id);
|
|
||||||
}
|
|
||||||
if self.state.active.contains_key(&id) {
|
|
||||||
return self.state.place(id, region, self.rsc);
|
|
||||||
}
|
|
||||||
// Not drawn yet: its length came from a hint, so this is its one draw.
|
|
||||||
self.state.draw_inner(
|
|
||||||
self.layer,
|
|
||||||
id,
|
|
||||||
region,
|
|
||||||
Some(self.id),
|
|
||||||
self.mask,
|
|
||||||
None,
|
|
||||||
self.rsc,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_text(
|
pub fn render_text(
|
||||||
@@ -190,6 +129,17 @@ impl<'a> Painter<'a> {
|
|||||||
self.region
|
self.region
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size<W: ?Sized + Widget>(&mut self, id: &StrongWidget<W>) -> Size {
|
||||||
|
self.size_ctx().size(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len_axis<W: ?Sized + Widget>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Len {
|
||||||
|
match axis {
|
||||||
|
Axis::X => self.size_ctx().width(id),
|
||||||
|
Axis::Y => self.size_ctx().height(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn output_size(&self) -> Vec2 {
|
pub fn output_size(&self) -> Vec2 {
|
||||||
self.state.output_size
|
self.state.output_size
|
||||||
}
|
}
|
||||||
@@ -217,25 +167,9 @@ impl<'a> Painter<'a> {
|
|||||||
pub fn id(&self) -> &WidgetId {
|
pub fn id(&self) -> &WidgetId {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// A child that has just been drawn. Reading its size records that this
|
pub fn size_ctx(&mut self) -> SizeCtx<'_> {
|
||||||
/// widget's own size depends on it; dropping it without reading draws the
|
self.state.size_ctx(self.id, self.region.size(), self.rsc)
|
||||||
/// child and leaves the parent independent of what it came to.
|
|
||||||
pub struct DrawResult<'p, 'a> {
|
|
||||||
painter: &'p mut Painter<'a>,
|
|
||||||
child: WidgetId,
|
|
||||||
size: Size,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DrawResult<'_, '_> {
|
|
||||||
pub fn size(self) -> Size {
|
|
||||||
self.painter.size_deps.push(self.child);
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(self, axis: Axis) -> Len {
|
|
||||||
self.size().axis(axis)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-128
@@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActiveData, Axis, DrawLayers, IdLike, MaskIdx, Painter, PixelRegion, Size, SizeDependence,
|
ActiveData, Axis, DrawLayers, IdLike, MaskIdx, Painter, PixelRegion, SizeCtx, StrongWidget,
|
||||||
StrongWidget, UiRegion, UiRsc, WidgetId, Widgets,
|
UiRegion, UiRsc, UiVec2, WidgetId, Widgets,
|
||||||
|
ui::cache::Cache,
|
||||||
util::{HashMap, HashSet, Vec2, forget_ref},
|
util::{HashMap, HashSet, Vec2, forget_ref},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@ pub struct UiRenderState {
|
|||||||
pub active: HashMap<WidgetId, ActiveData>,
|
pub active: HashMap<WidgetId, ActiveData>,
|
||||||
pub layers: DrawLayers,
|
pub layers: DrawLayers,
|
||||||
pub(super) output_size: Vec2,
|
pub(super) output_size: Vec2,
|
||||||
|
pub cache: Cache,
|
||||||
|
|
||||||
old_root: Option<WidgetId>,
|
old_root: Option<WidgetId>,
|
||||||
resized: bool,
|
resized: bool,
|
||||||
@@ -19,6 +21,7 @@ impl UiRenderState {
|
|||||||
Self {
|
Self {
|
||||||
active: Default::default(),
|
active: Default::default(),
|
||||||
layers: Default::default(),
|
layers: Default::default(),
|
||||||
|
cache: Default::default(),
|
||||||
output_size: Vec2::ZERO,
|
output_size: Vec2::ZERO,
|
||||||
old_root: None,
|
old_root: None,
|
||||||
resized: false,
|
resized: false,
|
||||||
@@ -31,10 +34,6 @@ impl UiRenderState {
|
|||||||
self.resized = true;
|
self.resized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_size(&self) -> Vec2 {
|
|
||||||
self.output_size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update<'a>(&mut self, root: impl Into<Option<&'a StrongWidget>>, rsc: &mut dyn UiRsc) {
|
pub fn update<'a>(&mut self, root: impl Into<Option<&'a StrongWidget>>, rsc: &mut dyn UiRsc) {
|
||||||
// safety mechanism for memory leaks; might wanna return a result instead so user can
|
// safety mechanism for memory leaks; might wanna return a result instead so user can
|
||||||
// decide whether to panic or not
|
// decide whether to panic or not
|
||||||
@@ -81,11 +80,19 @@ impl UiRenderState {
|
|||||||
mask: MaskIdx,
|
mask: MaskIdx,
|
||||||
old_children: Option<Vec<WidgetId>>,
|
old_children: Option<Vec<WidgetId>>,
|
||||||
rsc: &mut dyn UiRsc,
|
rsc: &mut dyn UiRsc,
|
||||||
) -> Size {
|
) {
|
||||||
let mut old_children = old_children.unwrap_or_default();
|
let mut old_children = old_children.unwrap_or_default();
|
||||||
if self.active.contains_key(&id) {
|
if let Some(active) = self.active.get_mut(&id)
|
||||||
if let Some(size) = self.try_reuse(id, region, rsc) {
|
&& !rsc.widgets().needs_redraw.contains(&id)
|
||||||
return size;
|
{
|
||||||
|
// check to see if we can skip drawing first
|
||||||
|
if active.region == region {
|
||||||
|
return;
|
||||||
|
} else if active.region.size() == region.size() {
|
||||||
|
// TODO: epsilon?
|
||||||
|
let from = active.region;
|
||||||
|
self.mov(id, from, region);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// if not, then maintain resize and track old children to remove unneeded
|
// if not, then maintain resize and track old children to remove unneeded
|
||||||
let active = self.remove(id, false, rsc).unwrap();
|
let active = self.remove(id, false, rsc).unwrap();
|
||||||
@@ -104,8 +111,6 @@ impl UiRenderState {
|
|||||||
textures: Vec::new(),
|
textures: Vec::new(),
|
||||||
primitives: Vec::new(),
|
primitives: Vec::new(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
size_deps: Vec::new(),
|
|
||||||
size: None,
|
|
||||||
rsc,
|
rsc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,34 +126,18 @@ impl UiRenderState {
|
|||||||
textures,
|
textures,
|
||||||
primitives,
|
primitives,
|
||||||
children,
|
children,
|
||||||
size_deps,
|
|
||||||
size,
|
|
||||||
layer,
|
layer,
|
||||||
id,
|
id,
|
||||||
} = painter;
|
} = painter;
|
||||||
|
|
||||||
let size = size.unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"'{}' ({id:?}) drew without a size; every widget calls Painter::set_size",
|
|
||||||
rsc.widgets().label(id)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
debug_assert!(
|
|
||||||
Self::hints_agree(id, size, rsc),
|
|
||||||
"'{}' ({id:?}) drew a size its size_hint disagrees with",
|
|
||||||
rsc.widgets().label(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
// add to active
|
// add to active
|
||||||
let active = ActiveData {
|
let active = ActiveData {
|
||||||
id,
|
id,
|
||||||
region,
|
region,
|
||||||
size,
|
|
||||||
parent,
|
parent,
|
||||||
textures,
|
textures,
|
||||||
primitives,
|
primitives,
|
||||||
children,
|
children,
|
||||||
size_deps,
|
|
||||||
mask,
|
mask,
|
||||||
layer,
|
layer,
|
||||||
};
|
};
|
||||||
@@ -162,86 +151,6 @@ impl UiRenderState {
|
|||||||
|
|
||||||
rsc.on_draw(&active);
|
rsc.on_draw(&active);
|
||||||
self.active.insert(id, active);
|
self.active.insert(id, active);
|
||||||
size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gives an already drawn widget a new box, keeping its drawing if it can
|
|
||||||
/// and drawing it again if it cannot.
|
|
||||||
pub(super) fn place(&mut self, id: WidgetId, region: UiRegion, rsc: &mut dyn UiRsc) -> Size {
|
|
||||||
if let Some(size) = self.try_reuse(id, region, rsc) {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
let Some(active) = self.remove(id, false, rsc) else {
|
|
||||||
return Size::ZERO;
|
|
||||||
};
|
|
||||||
self.draw_inner(
|
|
||||||
active.layer,
|
|
||||||
id,
|
|
||||||
region,
|
|
||||||
active.parent,
|
|
||||||
active.mask,
|
|
||||||
Some(active.children),
|
|
||||||
rsc,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The drawing a widget already has, kept for a new box if the box has not
|
|
||||||
/// changed in a way it depends on.
|
|
||||||
fn try_reuse(&mut self, id: WidgetId, region: UiRegion, rsc: &dyn UiRsc) -> Option<Size> {
|
|
||||||
if rsc.widgets().needs_redraw.contains(&id) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let active = self.active.get(&id)?;
|
|
||||||
let (size, was) = (active.size, active.region);
|
|
||||||
if was == region {
|
|
||||||
return Some(size);
|
|
||||||
}
|
|
||||||
// TODO: epsilon?
|
|
||||||
if was.size() == region.size() {
|
|
||||||
self.mov(id, was, region);
|
|
||||||
return Some(size);
|
|
||||||
}
|
|
||||||
if self.reusable(id, region, rsc) {
|
|
||||||
// Its drawing stands; the box is written into the primitives.
|
|
||||||
self.mov(id, was, region);
|
|
||||||
return Some(size);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the widget can keep the drawing it has and be given `region`
|
|
||||||
/// instead, asked one axis at a time: a change on an axis it does not
|
|
||||||
/// depend on costs nothing, whatever it depends on elsewhere.
|
|
||||||
fn reusable(&self, id: WidgetId, mut region: UiRegion, rsc: &dyn UiRsc) -> bool {
|
|
||||||
let Some(active) = self.active.get(&id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let Some(widget) = rsc.widgets().get_dyn(id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let mut was = active.region;
|
|
||||||
[Axis::X, Axis::Y].into_iter().all(|axis| {
|
|
||||||
let offered = region.axis_mut(axis).len();
|
|
||||||
let had = was.axis_mut(axis).len();
|
|
||||||
match widget.size_dependence(axis) {
|
|
||||||
SizeDependence::None => true,
|
|
||||||
// `Internal` could also keep its drawing when only the room
|
|
||||||
// around it changed, but that is a translation rather than a
|
|
||||||
// remap, so it waits for the move chain.
|
|
||||||
SizeDependence::Internal | SizeDependence::External => offered == had,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hints_agree(id: WidgetId, size: Size, rsc: &dyn UiRsc) -> bool {
|
|
||||||
let Some(widget) = rsc.widgets().get_dyn(id) else {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
[Axis::X, Axis::Y].into_iter().all(|axis| {
|
|
||||||
widget
|
|
||||||
.size_hint(axis)
|
|
||||||
.is_none_or(|hint| hint == size.axis(axis))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
|
fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
|
||||||
@@ -278,6 +187,7 @@ impl UiRenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn remove_rec(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) -> Option<ActiveData> {
|
fn remove_rec(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) -> Option<ActiveData> {
|
||||||
|
self.cache.remove(id);
|
||||||
let inst = self.remove(id, true, rsc);
|
let inst = self.remove(id, true, rsc);
|
||||||
if let Some(inst) = &inst {
|
if let Some(inst) = &inst {
|
||||||
for c in &inst.children {
|
for c in &inst.children {
|
||||||
@@ -291,6 +201,7 @@ impl UiRenderState {
|
|||||||
for (_, active) in self.active.drain() {
|
for (_, active) in self.active.drain() {
|
||||||
rsc.on_undraw(&active);
|
rsc.on_undraw(&active);
|
||||||
}
|
}
|
||||||
|
self.cache.clear();
|
||||||
self.layers.clear();
|
self.layers.clear();
|
||||||
rsc.widgets_mut().needs_redraw.clear();
|
rsc.widgets_mut().needs_redraw.clear();
|
||||||
rsc.free();
|
rsc.free();
|
||||||
@@ -352,11 +263,19 @@ impl UiRenderState {
|
|||||||
pub fn redraw(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
|
pub fn redraw(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
|
||||||
rsc.widgets_mut().needs_redraw.remove(&id);
|
rsc.widgets_mut().needs_redraw.remove(&id);
|
||||||
self.draw_started.remove(&id);
|
self.draw_started.remove(&id);
|
||||||
// Whoever read this widget's size may be a different size now, so the
|
// check if parent depends on the desired size of this, if so then redraw it first
|
||||||
// highest reader is what draws; it reaches this one on the way down.
|
for axis in [Axis::X, Axis::Y] {
|
||||||
if let Some(top) = self.highest_reader(id) {
|
if let Some(&(outer, old)) = self.cache.size.axis_dyn(axis).get(&id)
|
||||||
self.redraw(top, rsc);
|
&& let Some(current) = self.active.get(&id)
|
||||||
return;
|
&& let Some(pid) = current.parent
|
||||||
|
{
|
||||||
|
self.cache.size.axis_dyn(axis).remove(&id);
|
||||||
|
let new = self.size_ctx(id, outer, rsc).len_axis(id, axis);
|
||||||
|
self.cache.size.axis_dyn(axis).insert(id, (outer, new));
|
||||||
|
if new != old {
|
||||||
|
self.redraw(pid, rsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.draw_started.contains(&id) {
|
if self.draw_started.contains(&id) {
|
||||||
@@ -378,22 +297,22 @@ impl UiRenderState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The furthest ancestor that read this widget's size, directly or through
|
pub(super) fn size_ctx<'b>(
|
||||||
/// widgets that did the same.
|
&'b mut self,
|
||||||
fn highest_reader(&self, id: WidgetId) -> Option<WidgetId> {
|
source: WidgetId,
|
||||||
let mut top = None;
|
outer: UiVec2,
|
||||||
let mut at = id;
|
rsc: &'b mut dyn UiRsc,
|
||||||
while let Some(active) = self.active.get(&at)
|
) -> SizeCtx<'b> {
|
||||||
&& let Some(parent) = active.parent
|
let ui = rsc.ui_mut();
|
||||||
&& self
|
SizeCtx {
|
||||||
.active
|
source,
|
||||||
.get(&parent)
|
cache: &mut self.cache,
|
||||||
.is_some_and(|p| p.size_deps.contains(&at))
|
text: &mut ui.text,
|
||||||
{
|
widgets: &ui.widgets,
|
||||||
top = Some(parent);
|
outer,
|
||||||
at = parent;
|
output_size: self.output_size,
|
||||||
|
id: source,
|
||||||
}
|
}
|
||||||
top
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
use crate::{
|
||||||
|
Axis, AxisT, IdLike, Len, RenderedText, Size, TextAttrs, TextBuffer, TextData, UiVec2,
|
||||||
|
WidgetAxisFns, WidgetId, Widgets, XAxis, YAxis, ui::cache::Cache, util::Vec2,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SizeCtx<'a> {
|
||||||
|
pub text: &'a mut TextData,
|
||||||
|
pub(super) source: WidgetId,
|
||||||
|
pub(super) widgets: &'a Widgets,
|
||||||
|
pub(super) cache: &'a mut Cache,
|
||||||
|
/// TODO: should this be pub? rn used for sized
|
||||||
|
pub outer: UiVec2,
|
||||||
|
pub(super) output_size: Vec2,
|
||||||
|
pub(super) id: WidgetId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeCtx<'_> {
|
||||||
|
pub fn id(&self) -> &WidgetId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> &WidgetId {
|
||||||
|
&self.source
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn len_inner<A: const AxisT>(&mut self, id: WidgetId) -> Len {
|
||||||
|
if let Some((_, len)) = self.cache.size.axis::<A>().get(&id) {
|
||||||
|
return *len;
|
||||||
|
}
|
||||||
|
let len = self
|
||||||
|
.widgets
|
||||||
|
.get_dyn_dynamic(id)
|
||||||
|
.desired_len::<A>(&mut SizeCtx {
|
||||||
|
text: self.text,
|
||||||
|
source: self.source,
|
||||||
|
widgets: self.widgets,
|
||||||
|
cache: self.cache,
|
||||||
|
outer: self.outer,
|
||||||
|
output_size: self.output_size,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
self.cache.size.axis::<A>().insert(id, (self.outer, len));
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&mut self, id: impl IdLike) -> Len {
|
||||||
|
self.len_inner::<XAxis>(id.id())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&mut self, id: impl IdLike) -> Len {
|
||||||
|
self.len_inner::<YAxis>(id.id())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len_axis(&mut self, id: impl IdLike, axis: Axis) -> Len {
|
||||||
|
match axis {
|
||||||
|
Axis::X => self.width(id),
|
||||||
|
Axis::Y => self.height(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&mut self, id: impl IdLike) -> Size {
|
||||||
|
let id = id.id();
|
||||||
|
Size {
|
||||||
|
x: self.width(id),
|
||||||
|
y: self.height(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn px_size(&mut self) -> Vec2 {
|
||||||
|
self.outer.to_abs(self.output_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn output_size(&mut self) -> Vec2 {
|
||||||
|
self.output_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_text(
|
||||||
|
&mut self,
|
||||||
|
buffer: &mut TextBuffer,
|
||||||
|
attrs: &TextAttrs,
|
||||||
|
width: Option<f32>,
|
||||||
|
) -> RenderedText {
|
||||||
|
self.text.render(buffer, attrs, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label(&self, id: WidgetId) -> &String {
|
||||||
|
self.widgets.label(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-35
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Axis, Len, Painter, Size};
|
use crate::{Axis, AxisT, Len, Painter, SizeCtx};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
mod data;
|
mod data;
|
||||||
@@ -15,49 +15,32 @@ pub use tag::*;
|
|||||||
pub use view::*;
|
pub use view::*;
|
||||||
pub use widgets::*;
|
pub use widgets::*;
|
||||||
|
|
||||||
/// How much of the box a widget was handed its drawing depends on, and so
|
|
||||||
/// what has to change before it must be drawn again. Asked per axis, because
|
|
||||||
/// wrapped text depends on the width it is offered and not on the height.
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub enum SizeDependence {
|
|
||||||
/// None of it: the box only says where the primitives go, so a new one is
|
|
||||||
/// written into them instead of drawn.
|
|
||||||
None,
|
|
||||||
/// Its own extent, whatever box that sits in. Reusable in any box that
|
|
||||||
/// leaves that extent unchanged, including a larger one it does not fill.
|
|
||||||
#[default]
|
|
||||||
Internal,
|
|
||||||
/// The extent of the box itself, used or not.
|
|
||||||
External,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Widget: Any {
|
pub trait Widget: Any {
|
||||||
/// Draws the widget, and states what it used with `Painter::set_size`.
|
|
||||||
fn draw(&mut self, painter: &mut Painter);
|
fn draw(&mut self, painter: &mut Painter);
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len;
|
||||||
/// An exact length the widget can give without a painter or its children.
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len;
|
||||||
/// Optional, and saves a draw rather than changing one: a hint that
|
|
||||||
/// disagrees with the eventual draw fails a debug assertion.
|
|
||||||
fn size_hint(&self, _axis: Axis) -> Option<Len> {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_dependence(&self, _axis: Axis) -> SizeDependence {
|
pub trait WidgetAxisFns {
|
||||||
SizeDependence::Internal
|
fn desired_len<A: AxisT>(&mut self, ctx: &mut SizeCtx) -> Len;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Widget + ?Sized> WidgetAxisFns for W {
|
||||||
|
fn desired_len<A: AxisT>(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
match A::get() {
|
||||||
|
Axis::X => self.desired_width(ctx),
|
||||||
|
Axis::Y => self.desired_height(ctx),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for () {
|
impl Widget for () {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, _: &mut Painter) {}
|
||||||
painter.set_size(Size::ZERO);
|
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
|
||||||
|
Len::ZERO
|
||||||
}
|
}
|
||||||
|
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
|
||||||
fn size_hint(&self, _axis: Axis) -> Option<Len> {
|
Len::ZERO
|
||||||
Some(Len::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_dependence(&self, _axis: Axis) -> SizeDependence {
|
|
||||||
SizeDependence::None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -10,7 +10,11 @@ struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultAppState for State {
|
impl DefaultAppState for State {
|
||||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, _: Proxy<Self>) -> Self {
|
fn new(
|
||||||
|
mut ui_state: DefaultUiState,
|
||||||
|
rsc: &mut DefaultRsc<Self>,
|
||||||
|
_: Proxy<Self::Event>,
|
||||||
|
) -> Self {
|
||||||
rect(Color::RED).set_root(rsc, &mut ui_state);
|
rect(Color::RED).set_root(rsc, &mut ui_state);
|
||||||
Self { ui_state }
|
Self { ui_state }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ pub struct Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultAppState for Client {
|
impl DefaultAppState for Client {
|
||||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, _: Proxy<Self>) -> Self {
|
fn new(
|
||||||
|
mut ui_state: DefaultUiState,
|
||||||
|
rsc: &mut DefaultRsc<Self>,
|
||||||
|
_: Proxy<Self::Event>,
|
||||||
|
) -> Self {
|
||||||
let rrect = rect(Color::WHITE).radius(20);
|
let rrect = rect(Color::WHITE).radius(20);
|
||||||
let pad_test = (
|
let pad_test = (
|
||||||
rrect.color(Color::BLUE),
|
rrect.color(Color::BLUE),
|
||||||
|
|||||||
+5
-1
@@ -11,7 +11,11 @@ struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultAppState for State {
|
impl DefaultAppState for State {
|
||||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, _: Proxy<Self>) -> Self {
|
fn new(
|
||||||
|
mut ui_state: DefaultUiState,
|
||||||
|
rsc: &mut DefaultRsc<Self>,
|
||||||
|
_: Proxy<Self::Event>,
|
||||||
|
) -> Self {
|
||||||
let rect = rect(Color::RED).add(rsc);
|
let rect = rect(Color::RED).add(rsc);
|
||||||
rect.task_on(CursorSense::click(), async move |mut ctx| {
|
rect.task_on(CursorSense::click(), async move |mut ctx| {
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
//! Text sizing: wrapped text reads the width it is offered, fixed text does
|
|
||||||
//! not, and both report a height their container lays out around.
|
|
||||||
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
DefaultApp::<State>::run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(DefaultUiState)]
|
|
||||||
struct State {
|
|
||||||
ui_state: DefaultUiState,
|
|
||||||
}
|
|
||||||
|
|
||||||
const SAMPLE: &str = "Wrapping shapes one source into as many lines as its container \
|
|
||||||
leaves room for, so the height of a paragraph is an answer rather than a setting, and \
|
|
||||||
the same words in a narrower box come back taller. Resize the window and watch the \
|
|
||||||
text below reflow into a different number of lines while nothing about it changes.";
|
|
||||||
|
|
||||||
impl DefaultAppState for State {
|
|
||||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, _: Proxy<Self>) -> Self {
|
|
||||||
let panel = || rect(Color::WHITE.darker(0.85));
|
|
||||||
|
|
||||||
let wrapped = wtext(SAMPLE)
|
|
||||||
.size(28)
|
|
||||||
.wrap(true)
|
|
||||||
.text_align(Align::LEFT)
|
|
||||||
.pad(16)
|
|
||||||
.background(panel());
|
|
||||||
|
|
||||||
let aligned = (
|
|
||||||
wtext("left").size(24).text_align(Align::LEFT),
|
|
||||||
wtext("centred").size(24).text_align(Align::CENTER),
|
|
||||||
wtext("right").size(24).text_align(Align::RIGHT),
|
|
||||||
)
|
|
||||||
.span(Dir::DOWN)
|
|
||||||
.gap(8)
|
|
||||||
.pad(16)
|
|
||||||
.background(panel());
|
|
||||||
|
|
||||||
// The same words in half the width, which is a different number of
|
|
||||||
// lines and so a different height. A declared width only holds along
|
|
||||||
// a span's own axis, hence the row.
|
|
||||||
let narrow = (
|
|
||||||
wtext(SAMPLE)
|
|
||||||
.size(20)
|
|
||||||
.wrap(true)
|
|
||||||
.pad(16)
|
|
||||||
.background(panel())
|
|
||||||
.align(Align::TOP)
|
|
||||||
.width(rel(0.5)),
|
|
||||||
rect(Color::WHITE.darker(0.95)),
|
|
||||||
)
|
|
||||||
.span(Dir::RIGHT);
|
|
||||||
|
|
||||||
(wrapped, aligned, narrow)
|
|
||||||
.span(Dir::DOWN)
|
|
||||||
.gap(12)
|
|
||||||
.pad(12)
|
|
||||||
.set_root(rsc, &mut ui_state);
|
|
||||||
Self { ui_state }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+5
-1
@@ -36,7 +36,11 @@ impl Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultAppState for State {
|
impl DefaultAppState for State {
|
||||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, _: Proxy<Self>) -> Self {
|
fn new(
|
||||||
|
mut ui_state: DefaultUiState,
|
||||||
|
rsc: &mut DefaultRsc<Self>,
|
||||||
|
_: Proxy<Self::Event>,
|
||||||
|
) -> Self {
|
||||||
let test = Test::new(rsc);
|
let test = Test::new(rsc);
|
||||||
|
|
||||||
test.on(CursorSense::click(), move |_, rsc| {
|
test.on(CursorSense::click(), move |_, rsc| {
|
||||||
|
|||||||
+44
-57
@@ -1,6 +1,10 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use arboard::Clipboard;
|
use arboard::Clipboard;
|
||||||
use std::{marker::PhantomData, sync::Arc, time::Instant};
|
use std::{
|
||||||
|
marker::{PhantomData, Sized},
|
||||||
|
sync::Arc,
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{Ime, WindowEvent},
|
event::{Ime, WindowEvent},
|
||||||
event_loop::{ActiveEventLoop, EventLoopProxy},
|
event_loop::{ActiveEventLoop, EventLoopProxy},
|
||||||
@@ -25,35 +29,7 @@ pub use sense::*;
|
|||||||
pub use state::*;
|
pub use state::*;
|
||||||
pub use task::*;
|
pub use task::*;
|
||||||
|
|
||||||
/// Sends an application's own events to its event loop. It wraps the proxy
|
pub type Proxy<Event> = EventLoopProxy<Event>;
|
||||||
/// rather than being one because task updates travel the same way: what an
|
|
||||||
/// application sends is its `Event`, not the loop's whole message type.
|
|
||||||
pub struct Proxy<State: DefaultAppState>(EventLoopProxy<DefaultEvent<State>>);
|
|
||||||
|
|
||||||
impl<State: DefaultAppState> Clone for Proxy<State> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<State: DefaultAppState> Proxy<State> {
|
|
||||||
pub fn send_event(&self, event: State::Event) {
|
|
||||||
let _ = self.0.send_event(DefaultEvent::User(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// What the event loop carries: the application's own events, and the
|
|
||||||
/// updates tasks send back to the ui thread.
|
|
||||||
pub enum DefaultEvent<State: DefaultAppState> {
|
|
||||||
User(State::Event),
|
|
||||||
Update(Box<dyn TaskUpdate<DefaultRsc<State>>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<State: DefaultAppState> TaskQueue<DefaultRsc<State>> for Proxy<State> {
|
|
||||||
fn send(&self, update: Box<dyn TaskUpdate<DefaultRsc<State>>>) {
|
|
||||||
let _ = self.0.send_event(DefaultEvent::Update(update));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DefaultUiState {
|
pub struct DefaultUiState {
|
||||||
pub root: Option<StrongWidget>,
|
pub root: Option<StrongWidget>,
|
||||||
@@ -94,8 +70,9 @@ pub trait HasDefaultUiState: Sized + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait DefaultAppState: HasDefaultUiState {
|
pub trait DefaultAppState: HasDefaultUiState {
|
||||||
type Event: Send = ();
|
type Event = ();
|
||||||
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, proxy: Proxy<Self>) -> Self;
|
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, proxy: Proxy<Self::Event>)
|
||||||
|
-> Self;
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn event(
|
fn event(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -128,14 +105,18 @@ pub struct DefaultRsc<State: 'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<State> DefaultRsc<State> {
|
impl<State> DefaultRsc<State> {
|
||||||
pub fn init(queue: Arc<dyn TaskQueue<Self>>) -> Self {
|
fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Self>) {
|
||||||
|
let (tasks, recv) = Tasks::init(window);
|
||||||
|
(
|
||||||
Self {
|
Self {
|
||||||
ui: Default::default(),
|
ui: Default::default(),
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
tasks: Tasks::init(queue),
|
tasks,
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
_state: Default::default(),
|
_state: Default::default(),
|
||||||
}
|
},
|
||||||
|
recv,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_state<T: 'static>(&mut self, id: impl IdLike, data: T) -> WeakState<T> {
|
pub fn create_state<T: 'static>(&mut self, id: impl IdLike, data: T) -> WeakState<T> {
|
||||||
@@ -200,32 +181,43 @@ pub struct DefaultApp<State: DefaultAppState> {
|
|||||||
rsc: DefaultRsc<State>,
|
rsc: DefaultRsc<State>,
|
||||||
render: UiRenderState,
|
render: UiRenderState,
|
||||||
state: State,
|
state: State,
|
||||||
|
task_recv: TaskMsgReceiver<DefaultRsc<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
||||||
type Event = DefaultEvent<State>;
|
type Event = State::Event;
|
||||||
|
|
||||||
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
|
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
|
||||||
let window = event_loop
|
let window = event_loop
|
||||||
.create_window(State::window_attributes())
|
.create_window(State::window_attributes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let default_state = DefaultUiState::new(window);
|
let default_state = DefaultUiState::new(window);
|
||||||
let mut rsc = DefaultRsc::init(Arc::new(Proxy(proxy.clone())));
|
let (mut rsc, task_recv) = DefaultRsc::init(default_state.window.clone());
|
||||||
let state = State::new(default_state, &mut rsc, Proxy(proxy));
|
let state = State::new(default_state, &mut rsc, proxy);
|
||||||
let render = UiRenderState::new();
|
let render = UiRenderState::new();
|
||||||
Self { rsc, state, render }
|
Self {
|
||||||
|
rsc,
|
||||||
|
state,
|
||||||
|
render,
|
||||||
|
task_recv,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
|
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
|
||||||
match event {
|
self.state.event(event, &mut self.rsc, &mut self.render);
|
||||||
DefaultEvent::User(event) => self.state.event(event, &mut self.rsc, &mut self.render),
|
|
||||||
DefaultEvent::Update(update) => update(&mut self.state, &mut self.rsc),
|
|
||||||
}
|
|
||||||
self.request_redraw_if_needed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
|
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
|
||||||
let Self { rsc, render, state } = self;
|
let Self {
|
||||||
|
rsc,
|
||||||
|
render,
|
||||||
|
state,
|
||||||
|
task_recv,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
for update in task_recv.try_iter() {
|
||||||
|
update(state, rsc);
|
||||||
|
}
|
||||||
|
|
||||||
let ui_state = state.default_state_mut();
|
let ui_state = state.default_state_mut();
|
||||||
let input_changed = ui_state.input.event(&event);
|
let input_changed = ui_state.input.event(&event);
|
||||||
@@ -235,7 +227,8 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
|||||||
ui_state.focus = None;
|
ui_state.focus = None;
|
||||||
}
|
}
|
||||||
if input_changed {
|
if input_changed {
|
||||||
render.run_sensors(rsc, state, cursor_state);
|
let window_size = ui_state.window_size();
|
||||||
|
render.run_sensors(rsc, state, cursor_state, window_size);
|
||||||
}
|
}
|
||||||
let ui_state = state.default_state_mut();
|
let ui_state = state.default_state_mut();
|
||||||
if old != ui_state.focus
|
if old != ui_state.focus
|
||||||
@@ -304,8 +297,11 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
state.window_event(event, rsc, render);
|
state.window_event(event, rsc, render);
|
||||||
self.request_redraw_if_needed();
|
let ui_state = self.state.default_state_mut();
|
||||||
self.state.default_state_mut().input.end_frame();
|
if render.needs_redraw(&ui_state.root, rsc.widgets()) {
|
||||||
|
ui_state.renderer.window().request_redraw();
|
||||||
|
}
|
||||||
|
ui_state.input.end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit(&mut self) {
|
fn exit(&mut self) {
|
||||||
@@ -313,15 +309,6 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State: DefaultAppState> DefaultApp<State> {
|
|
||||||
fn request_redraw_if_needed(&mut self) {
|
|
||||||
let ui_state = self.state.default_state_mut();
|
|
||||||
if self.render.needs_redraw(&ui_state.root, self.rsc.widgets()) {
|
|
||||||
ui_state.renderer.window().request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RscIdx<Rsc> {
|
pub trait RscIdx<Rsc> {
|
||||||
type Output;
|
type Output;
|
||||||
fn get(self, rsc: &Rsc) -> &Self::Output;
|
fn get(self, rsc: &Rsc) -> &Self::Output;
|
||||||
|
|||||||
+4
-20
@@ -22,20 +22,7 @@ impl UiRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
pub fn draw(&mut self) {
|
||||||
let output = match self.surface.get_current_texture() {
|
let output = self.surface.get_current_texture().unwrap();
|
||||||
CurrentSurfaceTexture::Success(texture) => texture,
|
|
||||||
CurrentSurfaceTexture::Suboptimal(texture) => {
|
|
||||||
self.surface.configure(&self.device, &self.config);
|
|
||||||
texture
|
|
||||||
}
|
|
||||||
CurrentSurfaceTexture::Outdated | CurrentSurfaceTexture::Lost => {
|
|
||||||
self.surface.configure(&self.device, &self.config);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CurrentSurfaceTexture::Timeout
|
|
||||||
| CurrentSurfaceTexture::Occluded
|
|
||||||
| CurrentSurfaceTexture::Validation => return,
|
|
||||||
};
|
|
||||||
let view = output
|
let view = output
|
||||||
.texture
|
.texture
|
||||||
.create_view(&TextureViewDescriptor::default());
|
.create_view(&TextureViewDescriptor::default());
|
||||||
@@ -59,7 +46,7 @@ impl UiRenderer {
|
|||||||
|
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
self.window.pre_present_notify();
|
self.window.pre_present_notify();
|
||||||
self.queue.present(output);
|
output.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, size: &PhysicalSize<u32>) {
|
pub fn resize(&mut self, size: &PhysicalSize<u32>) {
|
||||||
@@ -78,10 +65,9 @@ impl UiRenderer {
|
|||||||
pub fn new(window: Arc<Window>) -> Self {
|
pub fn new(window: Arc<Window>) -> Self {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
|
|
||||||
let instance = Instance::new(InstanceDescriptor {
|
let instance = Instance::new(&InstanceDescriptor {
|
||||||
backends: Backends::PRIMARY,
|
backends: Backends::PRIMARY,
|
||||||
display: Some(Box::new(window.clone())),
|
..Default::default()
|
||||||
..InstanceDescriptor::new_without_display_handle()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let surface = instance
|
let surface = instance
|
||||||
@@ -93,7 +79,6 @@ impl UiRenderer {
|
|||||||
power_preference: PowerPreference::default(),
|
power_preference: PowerPreference::default(),
|
||||||
compatible_surface: Some(&surface),
|
compatible_surface: Some(&surface),
|
||||||
force_fallback_adapter: false,
|
force_fallback_adapter: false,
|
||||||
apply_limit_buckets: false,
|
|
||||||
})
|
})
|
||||||
.block_on()
|
.block_on()
|
||||||
.expect("Could not get adapter!");
|
.expect("Could not get adapter!");
|
||||||
@@ -120,7 +105,6 @@ impl UiRenderer {
|
|||||||
let config = SurfaceConfiguration {
|
let config = SurfaceConfiguration {
|
||||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||||
format: surface_format,
|
format: surface_format,
|
||||||
color_space: SurfaceColorSpace::Auto,
|
|
||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
present_mode: PresentMode::AutoVsync,
|
present_mode: PresentMode::AutoVsync,
|
||||||
|
|||||||
+31
-99
@@ -4,14 +4,14 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum CursorButton {
|
pub enum CursorButton {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
Middle,
|
Middle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum CursorSense {
|
pub enum CursorSense {
|
||||||
PressStart(CursorButton),
|
PressStart(CursorButton),
|
||||||
Pressing(CursorButton),
|
Pressing(CursorButton),
|
||||||
@@ -27,7 +27,7 @@ pub struct CursorSenses(Vec<CursorSense>);
|
|||||||
|
|
||||||
impl Event for CursorSenses {
|
impl Event for CursorSenses {
|
||||||
type Data<'a> = CursorData<'a>;
|
type Data<'a> = CursorData<'a>;
|
||||||
type Global = Hovered;
|
type State = SensorState;
|
||||||
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
||||||
if let Some(sense) = should_run(self, &data.cursor, data.hover) {
|
if let Some(sense) = should_run(self, &data.cursor, data.hover) {
|
||||||
let mut data = data.clone();
|
let mut data = data.clone();
|
||||||
@@ -37,24 +37,6 @@ impl Event for CursorSenses {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A press or a scroll is used up by whatever answered it, so it stops
|
|
||||||
/// there. Hovering is not: a cursor resting somewhere goes on resting.
|
|
||||||
fn consumes(&self, data: &Self::Data<'_>) -> bool {
|
|
||||||
!data.sense.position_only()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Who the cursor was inside, before and after an input. The difference is
|
|
||||||
/// whose hover has ended -- including a widget a higher layer has covered,
|
|
||||||
/// which the walk stops before reaching.
|
|
||||||
///
|
|
||||||
/// Two buffers that swap rather than one rebuilt, so an input allocates
|
|
||||||
/// nothing once they have grown.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Hovered {
|
|
||||||
was: Vec<WidgetId>,
|
|
||||||
now: Vec<WidgetId>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CursorSense {
|
impl CursorSense {
|
||||||
@@ -70,12 +52,6 @@ impl CursorSense {
|
|||||||
pub fn is_dragging(&self) -> bool {
|
pub fn is_dragging(&self) -> bool {
|
||||||
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// False if the sense is a button or a scroll, true if it is only about
|
|
||||||
/// where the cursor is.
|
|
||||||
fn position_only(&self) -> bool {
|
|
||||||
matches!(self, Self::HoverStart | Self::Hovering | Self::HoverEnd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@@ -120,12 +96,6 @@ impl CursorButtons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CursorState {
|
impl CursorState {
|
||||||
/// True if the cursor is only reporting where it is: no button and no
|
|
||||||
/// scroll this frame.
|
|
||||||
pub fn position_only(&self) -> bool {
|
|
||||||
self.scroll_delta == Vec2::ZERO && self.buttons.iter().all(|(_, state)| state.is_off())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end_frame(&mut self) {
|
pub fn end_frame(&mut self) {
|
||||||
self.buttons.end_frame();
|
self.buttons.end_frame();
|
||||||
self.scroll_delta = Vec2::ZERO;
|
self.scroll_delta = Vec2::ZERO;
|
||||||
@@ -153,6 +123,11 @@ pub struct Sensor<Ctx: HasEvents, Data> {
|
|||||||
|
|
||||||
pub type SenseShape = UiRegion;
|
pub type SenseShape = UiRegion;
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct SensorState {
|
||||||
|
pub hover: ActivationState,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CursorData<'a> {
|
pub struct CursorData<'a> {
|
||||||
/// where this widget was hit
|
/// where this widget was hit
|
||||||
@@ -172,6 +147,7 @@ pub trait SensorUi {
|
|||||||
rsc: &mut Rsc,
|
rsc: &mut Rsc,
|
||||||
state: &mut Rsc::State,
|
state: &mut Rsc::State,
|
||||||
cursor: CursorState,
|
cursor: CursorState,
|
||||||
|
window_size: Vec2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,85 +157,46 @@ impl SensorUi for UiRenderState {
|
|||||||
rsc: &mut Rsc,
|
rsc: &mut Rsc,
|
||||||
state: &mut Rsc::State,
|
state: &mut Rsc::State,
|
||||||
cursor: CursorState,
|
cursor: CursorState,
|
||||||
|
window_size: Vec2,
|
||||||
) {
|
) {
|
||||||
// in order to remove this take, need to store active list in UiRenderState somehow
|
// in order to remove this take, need to store active list in UiRenderState somehow
|
||||||
// this would probably be done through a generic parameter that adds yet another rsc /
|
// this would probably be done through a generic parameter that adds yet another rsc /
|
||||||
// state like thing, but local to render state, and is passed to UiRsc events so you can
|
// state like thing, but local to render state, and is passed to UiRsc events so you can
|
||||||
// update it there?
|
// update it there?
|
||||||
let active = std::mem::take(&mut rsc.events_mut().get_type::<CursorSense>().active);
|
let mut active = std::mem::take(&mut rsc.events_mut().get_type::<CursorSense>().active);
|
||||||
let mut hovered = std::mem::take(&mut rsc.events_mut().get_type::<CursorSense>().global);
|
|
||||||
hovered.now.clear();
|
|
||||||
let position_only = cursor.position_only();
|
|
||||||
let region_of = |id| self.window_region(&id);
|
|
||||||
|
|
||||||
for layer in self.layers.indices().rev() {
|
for layer in self.layers.indices().rev() {
|
||||||
let mut consumed = false;
|
let mut sensed = false;
|
||||||
for id in active.get(&layer).into_flat_iter().map(|(id, _)| *id) {
|
for (id, sensor) in active.get_mut(&layer).into_flat_iter() {
|
||||||
let Some(region) = region_of(id) else {
|
let shape = self.active.get(id).unwrap().region;
|
||||||
continue;
|
let region = shape.to_px(window_size);
|
||||||
};
|
let in_shape = cursor.exists && region.contains(cursor.pos);
|
||||||
if !cursor.exists || !region.contains(cursor.pos) {
|
sensor.hover.update(in_shape);
|
||||||
|
if sensor.hover == ActivationState::Off {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hovered.now.push(id);
|
sensed = true;
|
||||||
let hover = match hovered.was.contains(&id) {
|
|
||||||
true => ActivationState::On,
|
|
||||||
false => ActivationState::Start,
|
|
||||||
};
|
|
||||||
// A press or a scroll stops where something answered it, so a
|
|
||||||
// button over a list does not swallow the list's scrolling.
|
|
||||||
consumed |= deliver(self, rsc, state, id, hover, &cursor, region);
|
|
||||||
// A cursor doing neither stops at whatever it is over, so
|
|
||||||
// hovering does not reach through.
|
|
||||||
consumed |= position_only;
|
|
||||||
}
|
|
||||||
// Applied after the layer, never during it: senses on one layer do
|
|
||||||
// not block each other.
|
|
||||||
if consumed {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whatever the cursor was inside and is not now, whether it left or a
|
let cursor = cursor.clone();
|
||||||
// layer above took the input before the walk reached it. A widget that
|
|
||||||
// stopped being drawn has no region to report and is simply dropped.
|
|
||||||
for &id in &hovered.was {
|
|
||||||
if !hovered.now.contains(&id)
|
|
||||||
&& let Some(region) = region_of(id)
|
|
||||||
{
|
|
||||||
deliver(self, rsc, state, id, ActivationState::End, &cursor, region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::mem::swap(&mut hovered.was, &mut hovered.now);
|
|
||||||
|
|
||||||
let senses = rsc.events_mut().get_type::<CursorSense>();
|
|
||||||
senses.active = active;
|
|
||||||
senses.global = hovered;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs one widget's cursor senses, and says whether they used up the input.
|
|
||||||
fn deliver<Rsc: HasEvents>(
|
|
||||||
render: &UiRenderState,
|
|
||||||
rsc: &mut Rsc,
|
|
||||||
state: &mut Rsc::State,
|
|
||||||
id: WidgetId,
|
|
||||||
hover: ActivationState,
|
|
||||||
cursor: &CursorState,
|
|
||||||
region: PixelRegion,
|
|
||||||
) -> bool {
|
|
||||||
let data = CursorData {
|
let data = CursorData {
|
||||||
pos: cursor.pos - region.top_left,
|
pos: cursor.pos - region.top_left,
|
||||||
size: region.bot_right - region.top_left,
|
size: region.bot_right - region.top_left,
|
||||||
scroll_delta: cursor.scroll_delta,
|
scroll_delta: cursor.scroll_delta,
|
||||||
hover,
|
hover: sensor.hover,
|
||||||
cursor: cursor.clone(),
|
cursor,
|
||||||
// this does not have any meaning;
|
// this does not have any meaning;
|
||||||
// might wanna set up Event to have a prepare stage
|
// might wanna set up Event to have a prepare stage
|
||||||
sense: CursorSense::Hovering,
|
sense: CursorSense::Hovering,
|
||||||
render,
|
render: self,
|
||||||
};
|
};
|
||||||
rsc.run_event::<CursorSense>(id, data, state)
|
rsc.run_event::<CursorSense>(*id, data, state);
|
||||||
|
}
|
||||||
|
if sensed {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rsc.events_mut().get_type::<CursorSense>().active = active;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_run(
|
pub fn should_run(
|
||||||
@@ -268,11 +205,6 @@ pub fn should_run(
|
|||||||
hover: ActivationState,
|
hover: ActivationState,
|
||||||
) -> Option<CursorSense> {
|
) -> Option<CursorSense> {
|
||||||
for sense in senses.iter() {
|
for sense in senses.iter() {
|
||||||
// A widget the cursor is no longer inside senses only its position:
|
|
||||||
// the press that ended its hover landed on something else.
|
|
||||||
if !hover.is_on() && !sense.position_only() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if match sense {
|
if match sense {
|
||||||
CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(),
|
CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(),
|
||||||
CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(),
|
CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(),
|
||||||
|
|||||||
+34
-16
@@ -1,5 +1,11 @@
|
|||||||
use iris_core::HasState;
|
use iris_core::HasState;
|
||||||
use std::{pin::Pin, sync::Arc};
|
use std::{
|
||||||
|
pin::Pin,
|
||||||
|
sync::{
|
||||||
|
Arc,
|
||||||
|
mpsc::{Receiver as SyncReceiver, Sender as SyncSender, channel as sync_channel},
|
||||||
|
},
|
||||||
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
runtime::Runtime,
|
runtime::Runtime,
|
||||||
sync::mpsc::{
|
sync::mpsc::{
|
||||||
@@ -7,52 +13,64 @@ use tokio::{
|
|||||||
unbounded_channel as async_channel,
|
unbounded_channel as async_channel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use winit::window::Window;
|
||||||
|
|
||||||
|
pub type TaskMsgSender<Rsc> = SyncSender<Box<dyn TaskUpdate<Rsc>>>;
|
||||||
|
pub type TaskMsgReceiver<Rsc> = SyncReceiver<Box<dyn TaskUpdate<Rsc>>>;
|
||||||
|
|
||||||
pub trait TaskUpdate<Rsc: HasState>: FnOnce(&mut Rsc::State, &mut Rsc) + Send {}
|
pub trait TaskUpdate<Rsc: HasState>: FnOnce(&mut Rsc::State, &mut Rsc) + Send {}
|
||||||
impl<F: FnOnce(&mut Rsc::State, &mut Rsc) + Send, Rsc: HasState> TaskUpdate<Rsc> for F {}
|
impl<F: FnOnce(&mut Rsc::State, &mut Rsc) + Send, Rsc: HasState> TaskUpdate<Rsc> for F {}
|
||||||
|
|
||||||
/// Hands an update from a task to the thread that owns the ui. Delivery and
|
|
||||||
/// waking are one act: a host posts the update as a message its loop already
|
|
||||||
/// carries, so nothing has to wake the loop separately, or claim a redraw to
|
|
||||||
/// be looked at.
|
|
||||||
pub trait TaskQueue<Rsc: HasState>: Send + Sync + 'static {
|
|
||||||
fn send(&self, update: Box<dyn TaskUpdate<Rsc>>);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Tasks<Rsc: HasState> {
|
pub struct Tasks<Rsc: HasState> {
|
||||||
start: AsyncSender<BoxTask>,
|
start: AsyncSender<BoxTask>,
|
||||||
queue: Arc<dyn TaskQueue<Rsc>>,
|
window: Arc<Window>,
|
||||||
|
msg_send: SyncSender<Box<dyn TaskUpdate<Rsc>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TaskCtx<Rsc: HasState> {
|
pub struct TaskCtx<Rsc: HasState> {
|
||||||
queue: Arc<dyn TaskQueue<Rsc>>,
|
send: TaskMsgSender<Rsc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Rsc: HasState> TaskCtx<Rsc> {
|
impl<Rsc: HasState> TaskCtx<Rsc> {
|
||||||
pub fn update(&mut self, f: impl TaskUpdate<Rsc> + 'static) {
|
pub fn update(&mut self, f: impl TaskUpdate<Rsc> + 'static) {
|
||||||
self.queue.send(Box::new(f));
|
let _ = self.send.send(Box::new(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Rsc: HasState + 'static> TaskCtx<Rsc> {
|
||||||
|
fn new(send: TaskMsgSender<Rsc>) -> Self {
|
||||||
|
Self { send }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoxTask = Pin<Box<dyn Future<Output = ()> + Send>>;
|
type BoxTask = Pin<Box<dyn Future<Output = ()> + Send>>;
|
||||||
|
|
||||||
impl<Rsc: HasState> Tasks<Rsc> {
|
impl<Rsc: HasState> Tasks<Rsc> {
|
||||||
pub fn init(queue: Arc<dyn TaskQueue<Rsc>>) -> Self {
|
pub fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Rsc>) {
|
||||||
let (start, start_recv) = async_channel();
|
let (start, start_recv) = async_channel();
|
||||||
|
let (msgs, msgs_recv) = sync_channel();
|
||||||
std::thread::spawn(|| {
|
std::thread::spawn(|| {
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(listen(start_recv))
|
rt.block_on(listen(start_recv))
|
||||||
});
|
});
|
||||||
Self { start, queue }
|
(
|
||||||
|
Self {
|
||||||
|
start,
|
||||||
|
msg_send: msgs,
|
||||||
|
window,
|
||||||
|
},
|
||||||
|
msgs_recv,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn<F: AsyncFnOnce(TaskCtx<Rsc>) + 'static + std::marker::Send>(&mut self, task: F)
|
pub fn spawn<F: AsyncFnOnce(TaskCtx<Rsc>) + 'static + std::marker::Send>(&mut self, task: F)
|
||||||
where
|
where
|
||||||
F::CallOnceFuture: Send,
|
F::CallOnceFuture: Send,
|
||||||
{
|
{
|
||||||
let queue = self.queue.clone();
|
let send = self.msg_send.clone();
|
||||||
|
let window = self.window.clone();
|
||||||
let _ = self.start.send(Box::pin(async move {
|
let _ = self.start.send(Box::pin(async move {
|
||||||
task(TaskCtx { queue }).await;
|
task(TaskCtx::new(send)).await;
|
||||||
|
window.request_redraw();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-180
@@ -1,180 +0,0 @@
|
|||||||
//! A ui with no window: build a tree, run frames, move a pointer, and read
|
|
||||||
//! back where widgets landed.
|
|
||||||
//!
|
|
||||||
//! It does not draw. A claim about pixels still needs a real surface.
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use std::{
|
|
||||||
sync::{
|
|
||||||
Arc,
|
|
||||||
mpsc::{Receiver, SyncSender, sync_channel},
|
|
||||||
},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// There is no loop here to post to, so updates queue until the test asks
|
|
||||||
/// for them.
|
|
||||||
struct Queue(SyncSender<Box<dyn TaskUpdate<DefaultRsc<HarnessState>>>>);
|
|
||||||
|
|
||||||
impl TaskQueue<DefaultRsc<HarnessState>> for Queue {
|
|
||||||
fn send(&self, update: Box<dyn TaskUpdate<DefaultRsc<HarnessState>>>) {
|
|
||||||
let _ = self.0.send(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `assert_eq!` for where a frame put a widget, written as its two corners.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! assert_corners {
|
|
||||||
($harness:expr, $id:expr, ($x0:expr, $y0:expr), ($x1:expr, $y1:expr)) => {
|
|
||||||
assert_eq!(
|
|
||||||
$harness.region(&$id).expect("widget drew nothing"),
|
|
||||||
$crate::core::PixelRegion {
|
|
||||||
top_left: $crate::core::util::Vec2::new($x0 as f32, $y0 as f32),
|
|
||||||
bot_right: $crate::core::util::Vec2::new($x1 as f32, $y1 as f32),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub use crate::assert_corners;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct HarnessState {
|
|
||||||
pub root: Option<StrongWidget>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasRoot for HarnessState {
|
|
||||||
fn set_root(&mut self, root: StrongWidget) {
|
|
||||||
self.root = Some(root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Harness {
|
|
||||||
pub rsc: DefaultRsc<HarnessState>,
|
|
||||||
pub render: UiRenderState,
|
|
||||||
pub state: HarnessState,
|
|
||||||
updates: Receiver<Box<dyn TaskUpdate<DefaultRsc<HarnessState>>>>,
|
|
||||||
cursor: CursorState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Harness {
|
|
||||||
/// `size` is the output in physical pixels.
|
|
||||||
pub fn new(size: impl Into<Vec2>) -> Self {
|
|
||||||
// A `TaskQueue` must be `Sync`, which `mpsc::Sender` is not; the
|
|
||||||
// bound that comes with `SyncSender` is far past anything a test
|
|
||||||
// leaves unread.
|
|
||||||
let (send, updates) = sync_channel(1024);
|
|
||||||
let rsc = DefaultRsc::init(Arc::new(Queue(send)));
|
|
||||||
let mut render = UiRenderState::new();
|
|
||||||
render.resize(size);
|
|
||||||
Self {
|
|
||||||
rsc,
|
|
||||||
render,
|
|
||||||
state: HarnessState::default(),
|
|
||||||
updates,
|
|
||||||
cursor: CursorState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> Vec2 {
|
|
||||||
self.render.output_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(&mut self, size: impl Into<Vec2>) {
|
|
||||||
self.render.resize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the root and lays it out, so a pointer event has something to hit.
|
|
||||||
pub fn set_root<T>(&mut self, widget: impl WidgetLike<DefaultRsc<HarnessState>, T>) {
|
|
||||||
widget.set_root(&mut self.rsc, &mut self.state);
|
|
||||||
self.frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn needs_redraw(&self) -> bool {
|
|
||||||
self.render
|
|
||||||
.needs_redraw(&self.state.root, self.rsc.widgets())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_updates(&mut self) -> usize {
|
|
||||||
let mut applied = 0;
|
|
||||||
while let Ok(update) = self.updates.try_recv() {
|
|
||||||
update(&mut self.state, &mut self.rsc);
|
|
||||||
applied += 1;
|
|
||||||
}
|
|
||||||
applied
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits for a task's first update, then applies everything waiting.
|
|
||||||
/// False if none arrived in time.
|
|
||||||
#[must_use]
|
|
||||||
pub fn await_update(&mut self, timeout: Duration) -> bool {
|
|
||||||
let Ok(update) = self.updates.recv_timeout(timeout) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
update(&mut self.state, &mut self.rsc);
|
|
||||||
self.apply_updates();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lays the tree out and builds its primitives.
|
|
||||||
pub fn frame(&mut self) {
|
|
||||||
self.apply_updates();
|
|
||||||
self.render.update(&self.state.root, &mut self.rsc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Where the last frame put a widget, or `None` if it drew nothing.
|
|
||||||
pub fn region(&self, id: &impl IdLike) -> Option<PixelRegion> {
|
|
||||||
self.render.window_region(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn move_to(&mut self, pos: impl Into<Vec2>) {
|
|
||||||
self.cursor.pos = pos.into();
|
|
||||||
self.cursor.exists = true;
|
|
||||||
self.sense();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn leave(&mut self) {
|
|
||||||
self.cursor.exists = false;
|
|
||||||
self.sense();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn press(&mut self, button: CursorButton) {
|
|
||||||
self.button(button).update(true);
|
|
||||||
self.sense();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(&mut self, button: CursorButton) {
|
|
||||||
self.button(button).update(false);
|
|
||||||
self.sense();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wheel carries no position, so this goes wherever the cursor was last
|
|
||||||
/// moved to -- nowhere, until it has been moved.
|
|
||||||
pub fn scroll(&mut self, delta: impl Into<Vec2>) {
|
|
||||||
self.cursor.scroll_delta = delta.into();
|
|
||||||
self.sense();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn click(&mut self, pos: impl Into<Vec2>) {
|
|
||||||
self.move_to(pos);
|
|
||||||
self.press(CursorButton::Left);
|
|
||||||
self.release(CursorButton::Left);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn button(&mut self, button: CursorButton) -> &mut ActivationState {
|
|
||||||
let buttons = &mut self.cursor.buttons;
|
|
||||||
match button {
|
|
||||||
CursorButton::Left => &mut buttons.left,
|
|
||||||
CursorButton::Middle => &mut buttons.middle,
|
|
||||||
CursorButton::Right => &mut buttons.right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dispatches against the layout of the last frame, which is what a
|
|
||||||
/// window delivers input against too.
|
|
||||||
fn sense(&mut self) {
|
|
||||||
let cursor = self.cursor.clone();
|
|
||||||
self.render
|
|
||||||
.run_sensors(&mut self.rsc, &mut self.state, cursor);
|
|
||||||
self.cursor.end_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
pub mod default;
|
pub mod default;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod harness;
|
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
|
|
||||||
pub use iris_core as core;
|
pub use iris_core as core;
|
||||||
|
|||||||
+4
-5
@@ -8,15 +8,14 @@ pub struct Image {
|
|||||||
impl Widget for Image {
|
impl Widget for Image {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
painter.primitive(&self.handle);
|
painter.primitive(&self.handle);
|
||||||
painter.set_size(Size::abs(self.handle.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_hint(&self, axis: Axis) -> Option<Len> {
|
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
|
||||||
Some(Len::abs(self.handle.size().axis(axis)))
|
Len::abs(self.handle.size().x)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_dependence(&self, _: Axis) -> SizeDependence {
|
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
|
||||||
SizeDependence::None
|
Len::abs(self.handle.size().y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -7,12 +7,14 @@ pub struct Masked {
|
|||||||
impl Widget for Masked {
|
impl Widget for Masked {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
painter.set_mask(painter.region());
|
painter.set_mask(painter.region());
|
||||||
let size = painter.widget(&self.inner).size();
|
painter.widget(&self.inner);
|
||||||
painter.set_size(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// It clips to the box it was given, not to the part its child used.
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
fn size_dependence(&self, _: Axis) -> SizeDependence {
|
ctx.width(&self.inner)
|
||||||
SizeDependence::External
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.height(&self.inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,16 +7,29 @@ pub struct Aligned {
|
|||||||
|
|
||||||
impl Widget for Aligned {
|
impl Widget for Aligned {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
// Drawn where it may be too big, then given its aligned box once its
|
|
||||||
// size is known.
|
|
||||||
let size = painter.widget(&self.inner).size();
|
|
||||||
let region = match self.align.tuple() {
|
let region = match self.align.tuple() {
|
||||||
(Some(x), Some(y)) => size.to_uivec2().align(RegionAlign { x, y }),
|
(Some(x), Some(y)) => painter
|
||||||
(Some(x), None) => UiRegion::new(size.x.apply_rest().align(x), UiSpan::FULL),
|
.size(&self.inner)
|
||||||
(None, Some(y)) => UiRegion::new(UiSpan::FULL, size.y.apply_rest().align(y)),
|
.to_uivec2()
|
||||||
|
.align(RegionAlign { x, y }),
|
||||||
|
(Some(x), None) => {
|
||||||
|
let x = painter.size_ctx().width(&self.inner).apply_rest().align(x);
|
||||||
|
UiRegion::new(x, UiSpan::FULL)
|
||||||
|
}
|
||||||
|
(None, Some(y)) => {
|
||||||
|
let y = painter.size_ctx().height(&self.inner).apply_rest().align(y);
|
||||||
|
UiRegion::new(UiSpan::FULL, y)
|
||||||
|
}
|
||||||
(None, None) => UiRegion::FULL,
|
(None, None) => UiRegion::FULL,
|
||||||
};
|
};
|
||||||
painter.place(&self.inner, region);
|
painter.widget_within(&self.inner, region);
|
||||||
painter.set_size(size);
|
}
|
||||||
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.width(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.height(&self.inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,14 @@ impl Widget for LayerOffset {
|
|||||||
for _ in 0..self.offset {
|
for _ in 0..self.offset {
|
||||||
painter.next_layer();
|
painter.next_layer();
|
||||||
}
|
}
|
||||||
let size = painter.widget(&self.inner).size();
|
painter.widget(&self.inner);
|
||||||
painter.set_size(size);
|
}
|
||||||
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.width(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.height(&self.inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,22 +6,43 @@ pub struct MaxSize {
|
|||||||
pub y: Option<Len>,
|
pub y: Option<Len>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaxSize {}
|
impl MaxSize {
|
||||||
|
fn apply_to_outer(&self, ctx: &mut SizeCtx) {
|
||||||
|
if let Some(x) = self.x {
|
||||||
|
ctx.outer.x.select_len(x.apply_rest());
|
||||||
|
}
|
||||||
|
if let Some(y) = self.y {
|
||||||
|
ctx.outer.y.select_len(y.apply_rest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Widget for MaxSize {
|
impl Widget for MaxSize {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let child = painter.widget(&self.inner).size();
|
painter.widget(&self.inner);
|
||||||
let output = painter.output_size();
|
}
|
||||||
painter.set_size(Size {
|
|
||||||
x: capped(child.x, self.x, output.x),
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
y: capped(child.y, self.y, output.y),
|
self.apply_to_outer(ctx);
|
||||||
});
|
let width = ctx.width(&self.inner);
|
||||||
|
if let Some(x) = self.x {
|
||||||
|
let width_px = width.apply_rest().to_abs(ctx.output_size().x);
|
||||||
|
let x_px = x.apply_rest().to_abs(ctx.output_size().x);
|
||||||
|
if width_px > x_px { x } else { width }
|
||||||
|
} else {
|
||||||
|
width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capped(len: Len, max: Option<Len>, output: f32) -> Len {
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
match max {
|
self.apply_to_outer(ctx);
|
||||||
Some(max) if len.apply_rest().to_abs(output) > max.apply_rest().to_abs(output) => max,
|
let height = ctx.height(&self.inner);
|
||||||
_ => len,
|
if let Some(y) = self.y {
|
||||||
|
let height_px = height.apply_rest().to_abs(ctx.output_size().y);
|
||||||
|
let y_px = y.apply_rest().to_abs(ctx.output_size().y);
|
||||||
|
if height_px > y_px { y } else { height }
|
||||||
|
} else {
|
||||||
|
height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ mod max_size;
|
|||||||
mod offset;
|
mod offset;
|
||||||
mod pad;
|
mod pad;
|
||||||
mod scroll;
|
mod scroll;
|
||||||
mod set_size;
|
mod sized;
|
||||||
mod span;
|
mod span;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
|
||||||
@@ -14,6 +14,6 @@ pub use max_size::*;
|
|||||||
pub use offset::*;
|
pub use offset::*;
|
||||||
pub use pad::*;
|
pub use pad::*;
|
||||||
pub use scroll::*;
|
pub use scroll::*;
|
||||||
pub use set_size::*;
|
pub use sized::*;
|
||||||
pub use span::*;
|
pub use span::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
@@ -8,7 +8,14 @@ pub struct Offset {
|
|||||||
impl Widget for Offset {
|
impl Widget for Offset {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let region = UiRegion::FULL.offset(self.amt);
|
let region = UiRegion::FULL.offset(self.amt);
|
||||||
let size = painter.widget_within(&self.inner, region).size();
|
painter.widget_within(&self.inner, region);
|
||||||
painter.set_size(size);
|
}
|
||||||
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.width(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.height(&self.inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-13
@@ -7,19 +7,27 @@ pub struct Pad {
|
|||||||
|
|
||||||
impl Widget for Pad {
|
impl Widget for Pad {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let inner = painter
|
painter.widget_within(&self.inner, self.padding.region());
|
||||||
.widget_within(&self.inner, self.padding.region())
|
}
|
||||||
.size();
|
|
||||||
painter.set_size(Size {
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
x: Len {
|
let width = self.padding.left + self.padding.right;
|
||||||
abs: inner.x.abs + self.padding.left + self.padding.right,
|
let height = self.padding.top + self.padding.bottom;
|
||||||
..inner.x
|
ctx.outer.x.abs -= width;
|
||||||
},
|
ctx.outer.y.abs -= height;
|
||||||
y: Len {
|
let mut size = ctx.width(&self.inner);
|
||||||
abs: inner.y.abs + self.padding.top + self.padding.bottom,
|
size.abs += width;
|
||||||
..inner.y
|
size
|
||||||
},
|
}
|
||||||
});
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
let width = self.padding.left + self.padding.right;
|
||||||
|
let height = self.padding.top + self.padding.bottom;
|
||||||
|
ctx.outer.x.abs -= width;
|
||||||
|
ctx.outer.y.abs -= height;
|
||||||
|
let mut size = ctx.height(&self.inner);
|
||||||
|
size.abs += height;
|
||||||
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,8 @@ impl Widget for Scroll {
|
|||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let output_len = painter.output_size().axis(self.axis);
|
let output_len = painter.output_size().axis(self.axis);
|
||||||
let container_len = painter.region().axis(self.axis).len();
|
let container_len = painter.region().axis(self.axis).len();
|
||||||
// Drawn in the whole container to learn its length, then placed at
|
let content_len = painter
|
||||||
// the scrolled offset.
|
.len_axis(&self.inner, self.axis)
|
||||||
let child = painter.widget(&self.inner).size();
|
|
||||||
let content_len = child
|
|
||||||
.axis(self.axis)
|
|
||||||
.apply_rest()
|
.apply_rest()
|
||||||
.within_len(container_len)
|
.within_len(container_len)
|
||||||
.to_abs(output_len);
|
.to_abs(output_len);
|
||||||
@@ -31,8 +28,15 @@ impl Widget for Scroll {
|
|||||||
|
|
||||||
let mut region = UiRegion::FULL.offset(Vec2::from_axis(self.axis, -self.amt, 0.0));
|
let mut region = UiRegion::FULL.offset(Vec2::from_axis(self.axis, -self.amt, 0.0));
|
||||||
region.axis_mut(self.axis).end = region.axis(self.axis).start.offset(self.content_len);
|
region.axis_mut(self.axis).end = region.axis(self.axis).start.offset(self.content_len);
|
||||||
painter.place(&self.inner, region);
|
painter.widget_within(&self.inner, region);
|
||||||
painter.set_size(child);
|
}
|
||||||
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.width(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
ctx.height(&self.inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
pub struct SetSize {
|
|
||||||
pub inner: StrongWidget,
|
|
||||||
pub x: Option<Len>,
|
|
||||||
pub y: Option<Len>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SetSize {}
|
|
||||||
|
|
||||||
impl Widget for SetSize {
|
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
|
||||||
let child = painter.widget(&self.inner).size();
|
|
||||||
painter.set_size(Size {
|
|
||||||
x: self.x.unwrap_or(child.x),
|
|
||||||
y: self.y.unwrap_or(child.y),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A declared axis is known without looking at the child, which is what
|
|
||||||
/// lets a span lay out around `.height(rest(1))` without drawing it.
|
|
||||||
fn size_hint(&self, axis: Axis) -> Option<Len> {
|
|
||||||
match axis {
|
|
||||||
Axis::X => self.x,
|
|
||||||
Axis::Y => self.y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub struct Sized {
|
||||||
|
pub inner: StrongWidget,
|
||||||
|
pub x: Option<Len>,
|
||||||
|
pub y: Option<Len>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sized {
|
||||||
|
fn apply_to_outer(&self, ctx: &mut SizeCtx) {
|
||||||
|
if let Some(x) = self.x {
|
||||||
|
ctx.outer.x.select_len(x.apply_rest());
|
||||||
|
}
|
||||||
|
if let Some(y) = self.y {
|
||||||
|
ctx.outer.y.select_len(y.apply_rest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Sized {
|
||||||
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
|
painter.widget(&self.inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
self.apply_to_outer(ctx);
|
||||||
|
self.x.unwrap_or_else(|| ctx.width(&self.inner))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
self.apply_to_outer(ctx);
|
||||||
|
self.y.unwrap_or_else(|| ctx.height(&self.inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
+100
-31
@@ -9,26 +9,12 @@ pub struct Span {
|
|||||||
|
|
||||||
impl Widget for Span {
|
impl Widget for Span {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let axis = self.dir.axis;
|
let total = self.len_sum(&mut painter.size_ctx());
|
||||||
// A length for every child before any is placed: from its own hint
|
|
||||||
// where it has one, and from drawing it where it does not.
|
|
||||||
let lens: Vec<Len> = self
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.map(|child| match painter.size_hint(child, axis) {
|
|
||||||
Some(len) => len,
|
|
||||||
None => painter.widget(child).len(axis),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let gap = self.gap * self.children.len().saturating_sub(1) as f32;
|
|
||||||
let total = lens.iter().fold(Len::abs(gap), |sum, len| sum + *len);
|
|
||||||
|
|
||||||
let mut start = UiScalar::rel_min();
|
let mut start = UiScalar::rel_min();
|
||||||
let mut ortho = Len::ZERO;
|
for child in &self.children {
|
||||||
for (child, len) in self.children.iter().zip(&lens) {
|
|
||||||
let mut span = UiSpan::FULL;
|
let mut span = UiSpan::FULL;
|
||||||
span.start = start;
|
span.start = start;
|
||||||
|
let len = painter.len_axis(child, self.dir.axis);
|
||||||
if len.rest > 0.0 {
|
if len.rest > 0.0 {
|
||||||
let offset = UiScalar::new(total.rel, total.abs);
|
let offset = UiScalar::new(total.rel, total.abs);
|
||||||
let rel_end = UiScalar::rel(len.rest / total.rest);
|
let rel_end = UiScalar::rel(len.rest / total.rest);
|
||||||
@@ -38,25 +24,27 @@ impl Widget for Span {
|
|||||||
start.abs += len.abs;
|
start.abs += len.abs;
|
||||||
start.rel += len.rel;
|
start.rel += len.rel;
|
||||||
span.end = start;
|
span.end = start;
|
||||||
let mut region = UiRegion::from_axis(axis, span, UiSpan::FULL);
|
let mut child_region = UiRegion::from_axis(self.dir.axis, span, UiSpan::FULL);
|
||||||
if self.dir.sign == Sign::Neg {
|
if self.dir.sign == Sign::Neg {
|
||||||
region.flip(axis);
|
child_region.flip(self.dir.axis);
|
||||||
}
|
|
||||||
let used = painter.place(child, region).axis(!axis);
|
|
||||||
// TODO: rel shouldn't do this, but no easy way before actually calculating pixels
|
|
||||||
if used.rel > 0.0 || used.rest > 0.0 {
|
|
||||||
ortho = Len::REST;
|
|
||||||
} else if ortho.rest == 0.0 {
|
|
||||||
ortho.abs = ortho.abs.max(used.abs);
|
|
||||||
}
|
}
|
||||||
|
painter.widget_within(child, child_region);
|
||||||
start.abs += self.gap;
|
start.abs += self.gap;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let along = match total.rest == 0.0 && total.rel == 0.0 {
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
true => total,
|
match self.dir.axis {
|
||||||
false => Len::default(),
|
Axis::X => self.desired_len(ctx),
|
||||||
};
|
Axis::Y => self.desired_ortho(ctx),
|
||||||
painter.set_size(Size::from_axis(axis, along, ortho));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
match self.dir.axis {
|
||||||
|
Axis::X => self.desired_ortho(ctx),
|
||||||
|
Axis::Y => self.desired_len(ctx),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +69,87 @@ impl Span {
|
|||||||
pub fn pop(&mut self) -> Option<StrongWidget> {
|
pub fn pop(&mut self) -> Option<StrongWidget> {
|
||||||
self.children.pop()
|
self.children.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn len_sum(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
let gap = self.gap * self.children.len().saturating_sub(1) as f32;
|
||||||
|
self.children.iter().fold(Len::abs(gap), |mut s, id| {
|
||||||
|
// it's tempting to subtract the abs & rel from the ctx outer,
|
||||||
|
// but that would create inconsistent sizing if you put
|
||||||
|
// a rest first vs last & only speed up in one direction.
|
||||||
|
// I think this is only solvable by restricting how you can
|
||||||
|
// compute size, bc currently you need child to define parent's
|
||||||
|
// sectioning and you need parent's sectioning to define child.
|
||||||
|
// Fortunately, that doesn't matter in most cases
|
||||||
|
let len = ctx.len_axis(id, self.dir.axis);
|
||||||
|
s += len;
|
||||||
|
s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
let len = self.len_sum(ctx);
|
||||||
|
if len.rest == 0.0 && len.rel == 0.0 {
|
||||||
|
len
|
||||||
|
} else {
|
||||||
|
Len::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
// this is a weird hack to get text wrapping to work properly when in a downward span
|
||||||
|
// the correct solution here is to add a function to widget that lets them
|
||||||
|
// request that ctx.outer has an axis "resolved" before checking the other,
|
||||||
|
// and panicking or warning if two request opposite axis (unsolvable in that case)
|
||||||
|
let outer = ctx.outer.axis(self.dir.axis);
|
||||||
|
if self.dir.axis == Axis::X {
|
||||||
|
// so....... this literally copies draw so that the lengths are correctly set in the
|
||||||
|
// context, which makes this slow and not cool
|
||||||
|
let total = self.len_sum(ctx);
|
||||||
|
let mut start = UiScalar::rel_min();
|
||||||
|
let mut ortho_len = Len::ZERO;
|
||||||
|
for child in &self.children {
|
||||||
|
let mut span = UiSpan::FULL;
|
||||||
|
span.start = start;
|
||||||
|
let len = ctx.len_axis(child, self.dir.axis);
|
||||||
|
if len.rest > 0.0 {
|
||||||
|
let offset = UiScalar::new(total.rel, total.abs);
|
||||||
|
let rel_end = UiScalar::rel(len.rest / total.rest);
|
||||||
|
let end = (UiScalar::rel_max() + start) - offset;
|
||||||
|
start = rel_end.within(&start.to(end));
|
||||||
|
}
|
||||||
|
start.abs += len.abs;
|
||||||
|
start.rel += len.rel;
|
||||||
|
span.end = start;
|
||||||
|
|
||||||
|
let scalar = span.len();
|
||||||
|
*ctx.outer.axis_mut(self.dir.axis) = outer.select_len(scalar);
|
||||||
|
let ortho = ctx.len_axis(child, !self.dir.axis);
|
||||||
|
// TODO: rel shouldn't do this, but no easy way before actually calculating pixels
|
||||||
|
if ortho.rel > 0.0 || ortho.rest > 0.0 {
|
||||||
|
ortho_len.rest = 1.0;
|
||||||
|
ortho_len.abs = 0.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ortho_len.abs = ortho_len.abs.max(ortho.abs);
|
||||||
|
start.abs += self.gap;
|
||||||
|
}
|
||||||
|
ortho_len
|
||||||
|
} else {
|
||||||
|
let mut ortho_len = Len::ZERO;
|
||||||
|
let ortho = !self.dir.axis;
|
||||||
|
for child in &self.children {
|
||||||
|
let len = ctx.len_axis(child, ortho);
|
||||||
|
// TODO: rel shouldn't do this, but no easy way before actually calculating pixels
|
||||||
|
if len.rel > 0.0 || len.rest > 0.0 {
|
||||||
|
ortho_len.rest = 1.0;
|
||||||
|
ortho_len.abs = 0.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ortho_len.abs = ortho_len.abs.max(len.abs);
|
||||||
|
}
|
||||||
|
ortho_len
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
||||||
|
|||||||
@@ -9,24 +9,29 @@ pub struct Stack {
|
|||||||
|
|
||||||
impl Widget for Stack {
|
impl Widget for Stack {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let sizing = match self.size {
|
let mut iter = self.children.iter();
|
||||||
StackSize::Default => None,
|
if let Some(child) = iter.next() {
|
||||||
StackSize::Child(i) => Some(i),
|
painter.child_layer();
|
||||||
};
|
painter.widget(child);
|
||||||
let mut size = Size::default();
|
|
||||||
for (i, child) in self.children.iter().enumerate() {
|
|
||||||
match i {
|
|
||||||
0 => painter.child_layer(),
|
|
||||||
_ => painter.next_layer(),
|
|
||||||
}
|
}
|
||||||
let drawn = painter.widget(child);
|
for child in iter {
|
||||||
// Only the child that sizes the stack is read, so the others
|
painter.next_layer();
|
||||||
// changing size does not redraw it.
|
painter.widget(child);
|
||||||
if sizing == Some(i) {
|
|
||||||
size = drawn.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
painter.set_size(size);
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
match self.size {
|
||||||
|
StackSize::Default => Len::default(),
|
||||||
|
StackSize::Child(i) => ctx.width(&self.children[i]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
match self.size {
|
||||||
|
StackSize::Default => Len::default(),
|
||||||
|
StackSize::Child(i) => ctx.height(&self.children[i]),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-6
@@ -1,5 +1,5 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::marker::Unsize;
|
use std::marker::{Sized, Unsize};
|
||||||
|
|
||||||
pub struct WidgetPtr {
|
pub struct WidgetPtr {
|
||||||
pub inner: Option<StrongWidget>,
|
pub inner: Option<StrongWidget>,
|
||||||
@@ -7,11 +7,25 @@ pub struct WidgetPtr {
|
|||||||
|
|
||||||
impl Widget for WidgetPtr {
|
impl Widget for WidgetPtr {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let size = match &self.inner {
|
if let Some(id) = &self.inner {
|
||||||
Some(id) => painter.widget(id).size(),
|
painter.widget(id);
|
||||||
None => Size::ZERO,
|
}
|
||||||
};
|
}
|
||||||
painter.set_size(size);
|
|
||||||
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
if let Some(id) = &self.inner {
|
||||||
|
ctx.width(id)
|
||||||
|
} else {
|
||||||
|
Len::ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
if let Some(id) = &self.inner {
|
||||||
|
ctx.height(id)
|
||||||
|
} else {
|
||||||
|
Len::ZERO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-6
@@ -35,16 +35,14 @@ impl Widget for Rect {
|
|||||||
thickness: self.thickness,
|
thickness: self.thickness,
|
||||||
inner_radius: self.inner_radius,
|
inner_radius: self.inner_radius,
|
||||||
});
|
});
|
||||||
painter.set_size(Size::REST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_hint(&self, _: Axis) -> Option<Len> {
|
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
|
||||||
Some(Len::REST)
|
Len::rest(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Its box is its primitive's own region, so a new one is written there.
|
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
|
||||||
fn size_dependence(&self, _: Axis) -> SizeDependence {
|
Len::rest(1)
|
||||||
SizeDependence::None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::{PhantomData, Sized};
|
||||||
|
|
||||||
pub struct TextBuilder<State, O = TextOutput, H: WidgetOption<State> = ()> {
|
pub struct TextBuilder<State, O = TextOutput, H: WidgetOption<State> = ()> {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ impl Widget for TextEdit {
|
|||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
let base = painter.layer;
|
let base = painter.layer;
|
||||||
painter.child_layer();
|
painter.child_layer();
|
||||||
let (_, size) = self.view.draw(painter);
|
self.view.draw(painter);
|
||||||
painter.set_size(size);
|
|
||||||
painter.layer = base;
|
painter.layer = base;
|
||||||
let region = self.region();
|
let region = self.region();
|
||||||
|
|
||||||
@@ -88,8 +87,12 @@ impl Widget for TextEdit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_dependence(&self, axis: Axis) -> SizeDependence {
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
self.view.size_dependence(axis)
|
self.view.desired_width(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
self.view.desired_height(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-29
@@ -52,15 +52,15 @@ impl TextView {
|
|||||||
.align(self.align)
|
.align(self.align)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, painter: &mut Painter) -> &RenderedText {
|
fn render(&mut self, ctx: &mut SizeCtx) -> &RenderedText {
|
||||||
let width = if self.attrs.wrap {
|
let width = if self.attrs.wrap {
|
||||||
Some(painter.px_size().x)
|
Some(ctx.px_size().x)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if width != self.width || self.tex.is_none() || self.attrs.changed || self.buf.changed {
|
if width != self.width || self.tex.is_none() || self.attrs.changed || self.buf.changed {
|
||||||
self.width = width;
|
self.width = width;
|
||||||
self.tex = Some(painter.render_text(&mut self.buf, &self.attrs, width));
|
self.tex = Some(ctx.draw_text(&mut self.buf, &self.attrs, width));
|
||||||
self.attrs.changed = false;
|
self.attrs.changed = false;
|
||||||
self.buf.changed = false;
|
self.buf.changed = false;
|
||||||
}
|
}
|
||||||
@@ -69,33 +69,39 @@ impl TextView {
|
|||||||
pub fn tex(&self) -> Option<&RenderedText> {
|
pub fn tex(&self) -> Option<&RenderedText> {
|
||||||
self.tex.as_ref()
|
self.tex.as_ref()
|
||||||
}
|
}
|
||||||
/// Draws the text, and says where the glyphs went and what they use.
|
pub fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
pub fn draw(&mut self, painter: &mut Painter) -> (UiRegion, Size) {
|
if self.is_empty()
|
||||||
|
&& let Some(hint) = &self.hint
|
||||||
|
{
|
||||||
|
ctx.width(hint)
|
||||||
|
} else {
|
||||||
|
Len::abs(self.render(ctx).size.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
if self.is_empty()
|
||||||
|
&& let Some(hint) = &self.hint
|
||||||
|
{
|
||||||
|
ctx.height(hint)
|
||||||
|
} else {
|
||||||
|
Len::abs(self.render(ctx).size.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn draw(&mut self, painter: &mut Painter) -> UiRegion {
|
||||||
let align = self.align;
|
let align = self.align;
|
||||||
if self.is_empty() && self.hint.is_some() {
|
if self.is_empty() && self.hint.is_some() {
|
||||||
let region = self.render(painter).size.align(align);
|
let region = self.render(&mut painter.size_ctx()).size.align(align);
|
||||||
let size = match &self.hint {
|
if let Some(hint) = &self.hint {
|
||||||
Some(hint) => painter.widget(hint).size(),
|
painter.widget(hint);
|
||||||
None => Size::ZERO,
|
}
|
||||||
};
|
return region;
|
||||||
return (region, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tex = self.render(painter);
|
let tex = self.render(&mut painter.size_ctx());
|
||||||
let region = tex.size.align(align);
|
let region = tex.size.align(align);
|
||||||
let size = Size::abs(tex.size);
|
|
||||||
let within = region.within(&painter.region());
|
let within = region.within(&painter.region());
|
||||||
painter.glyphs(tex, within);
|
painter.glyphs(tex, within);
|
||||||
(region, size)
|
region
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapping reads the width it is offered, so a wider box reshapes it; a
|
|
||||||
/// taller one never does.
|
|
||||||
pub fn size_dependence(&self, axis: Axis) -> SizeDependence {
|
|
||||||
match axis == Axis::X && self.attrs.wrap {
|
|
||||||
true => SizeDependence::External,
|
|
||||||
false => SizeDependence::Internal,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content(&self) -> String {
|
pub fn content(&self) -> String {
|
||||||
@@ -111,7 +117,7 @@ impl Text {
|
|||||||
content: content.into(),
|
content: content.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn update_buf(&mut self) {
|
fn update_buf(&mut self, _ctx: &mut SizeCtx) {
|
||||||
if self.content.changed {
|
if self.content.changed {
|
||||||
self.content.changed = false;
|
self.content.changed = false;
|
||||||
self.view.buf.set_text(self.content.as_str());
|
self.view.buf.set_text(self.content.as_str());
|
||||||
@@ -121,13 +127,18 @@ impl Text {
|
|||||||
|
|
||||||
impl Widget for Text {
|
impl Widget for Text {
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
self.update_buf();
|
self.update_buf(&mut painter.size_ctx());
|
||||||
let (_, size) = self.view.draw(painter);
|
self.view.draw(painter);
|
||||||
painter.set_size(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_dependence(&self, axis: Axis) -> SizeDependence {
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
self.view.size_dependence(axis)
|
self.update_buf(ctx);
|
||||||
|
self.view.desired_width(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||||
|
self.update_buf(ctx);
|
||||||
|
self.view.desired_height(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ widget_trait! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sized(self, size: impl Into<Size>) -> impl WidgetFn<Rsc, SetSize> {
|
fn sized(self, size: impl Into<Size>) -> impl WidgetFn<Rsc, Sized> {
|
||||||
let size = size.into();
|
let size = size.into();
|
||||||
move |state| SetSize {
|
move |state| Sized {
|
||||||
inner: self.add_strong(state),
|
inner: self.add_strong(state),
|
||||||
x: Some(size.x),
|
x: Some(size.x),
|
||||||
y: Some(size.y),
|
y: Some(size.y),
|
||||||
@@ -58,18 +58,18 @@ widget_trait! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, SetSize> {
|
fn width(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, Sized> {
|
||||||
let len = len.into();
|
let len = len.into();
|
||||||
move |state| SetSize {
|
move |state| Sized {
|
||||||
inner: self.add_strong(state),
|
inner: self.add_strong(state),
|
||||||
x: Some(len),
|
x: Some(len),
|
||||||
y: None,
|
y: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, SetSize> {
|
fn height(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, Sized> {
|
||||||
let len = len.into();
|
let len = len.into();
|
||||||
move |state| SetSize {
|
move |state| Sized {
|
||||||
inner: self.add_strong(state),
|
inner: self.add_strong(state),
|
||||||
x: None,
|
x: None,
|
||||||
y: Some(len),
|
y: Some(len),
|
||||||
|
|||||||
+17
-17
@@ -4,19 +4,20 @@
|
|||||||
//!
|
//!
|
||||||
//! cargo test --release --test draw_cost -- --ignored --nocapture
|
//! cargo test --release --test draw_cost -- --ignored --nocapture
|
||||||
//!
|
//!
|
||||||
//! Wall time is the wrong number to read for anything under a few percent --
|
//! **Read the instruction count, not the clock.** Wall time here swings by 2x
|
||||||
//! it varied by 2x between runs of one unchanged binary where instructions
|
//! between runs of one binary on this machine -- more under `cargo test` than
|
||||||
//! retired varied by 0.1%. Count those instead:
|
//! run directly -- while instructions retired are stable to 0.1%:
|
||||||
//!
|
//!
|
||||||
//! perf stat -e instructions:u target/release/.../draw_cost-* --ignored
|
//! perf stat -e instructions:u target/release/.../draw_cost-* --ignored
|
||||||
//!
|
//!
|
||||||
//! That is how `PrimitiveRender` was measured against a match in the renderer:
|
//! Measured that way on 2026-09-13, drawing each primitive through its own
|
||||||
//! 6 instructions per list drawn, against the ~5,400 wgpu spends recording
|
//! `PrimitiveRender` rather than a match in the renderer costs **6
|
||||||
//! one.
|
//! instructions per list drawn**, which is 0.1% of a frame at both 256 and
|
||||||
|
//! 1024 layers. Recording one list into the pass costs wgpu ~5,400.
|
||||||
//!
|
//!
|
||||||
//! The instance is leaked deliberately. A Vulkan loader may unload the driver
|
//! The instance is leaked on purpose. Dropping the last one makes the Vulkan
|
||||||
//! when the last one drops, which can fault as a thread that used it exits --
|
//! loader unload Mesa's ICD, which faults when a thread that touched Vulkan
|
||||||
//! and every test runs on a spawned thread.
|
//! exits -- and libtest runs every test on a spawned thread.
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -29,20 +30,20 @@ use wgpu::{Color as GpuColor, *};
|
|||||||
|
|
||||||
const SIZE: u32 = 1024;
|
const SIZE: u32 = 1024;
|
||||||
const FRAMES: u32 = 200;
|
const FRAMES: u32 = 200;
|
||||||
/// Reported as the best of this many batches, since the mean moves by more
|
/// Reported as the best of this many batches. The mean moves by 15% between
|
||||||
/// than the thing being measured.
|
/// runs on this machine, which is more than the thing being measured.
|
||||||
const BATCHES: u32 = 8;
|
const BATCHES: u32 = 8;
|
||||||
|
|
||||||
fn gpu() -> Option<(Device, Queue)> {
|
fn gpu() -> Option<(Device, Queue)> {
|
||||||
// Probed rather than assumed: there may be no Vulkan adapter, and GL is
|
// Probed rather than assumed: this machine's Vulkan device comes and goes,
|
||||||
// what is left when there is not.
|
// and GL is what is left when it is gone.
|
||||||
let all = Instance::new(InstanceDescriptor::new_without_display_handle());
|
let all = Instance::new(&InstanceDescriptor::default());
|
||||||
let instance = match pollster::block_on(all.request_adapter(&RequestAdapterOptions::default()))
|
let instance = match pollster::block_on(all.request_adapter(&RequestAdapterOptions::default()))
|
||||||
{
|
{
|
||||||
Ok(_) => all,
|
Ok(_) => all,
|
||||||
Err(_) => Instance::new(InstanceDescriptor {
|
Err(_) => Instance::new(&InstanceDescriptor {
|
||||||
backends: Backends::GL,
|
backends: Backends::GL,
|
||||||
..InstanceDescriptor::new_without_display_handle()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
// Leaked rather than dropped: see the note at the top of the file.
|
// Leaked rather than dropped: see the note at the top of the file.
|
||||||
@@ -57,7 +58,6 @@ fn config(format: TextureFormat) -> SurfaceConfiguration {
|
|||||||
SurfaceConfiguration {
|
SurfaceConfiguration {
|
||||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||||
format,
|
format,
|
||||||
color_space: SurfaceColorSpace::Auto,
|
|
||||||
width: SIZE,
|
width: SIZE,
|
||||||
height: SIZE,
|
height: SIZE,
|
||||||
present_mode: PresentMode::Fifo,
|
present_mode: PresentMode::Fifo,
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
//! Where a frame puts things, with no window to put them in.
|
|
||||||
|
|
||||||
use iris::harness::{Harness, assert_corners};
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
/// A fixed 100 wide, and the rest of the 400 to its neighbour.
|
|
||||||
fn two_rects(h: &mut Harness) -> (WidgetId, WidgetId) {
|
|
||||||
let left = rect(Color::RED).width(100).add(&mut h.rsc);
|
|
||||||
let right = rect(Color::BLUE).add(&mut h.rsc);
|
|
||||||
h.set_root((left, right).span(Dir::RIGHT));
|
|
||||||
(left.id(), right.id())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_span_gives_each_child_the_width_it_asked_for() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let (left, right) = two_rects(&mut h);
|
|
||||||
|
|
||||||
assert_corners!(h, left, (0, 0), (100, 200));
|
|
||||||
assert_corners!(h, right, (100, 0), (400, 200));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resizing_relays_out_against_the_new_output() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let (left, right) = two_rects(&mut h);
|
|
||||||
|
|
||||||
h.resize((800, 100));
|
|
||||||
assert!(h.needs_redraw());
|
|
||||||
h.frame();
|
|
||||||
|
|
||||||
assert_corners!(h, left, (0, 0), (100, 100));
|
|
||||||
assert_corners!(h, right, (100, 0), (800, 100));
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
//! Which widget an input reaches.
|
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use iris::harness::Harness;
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_press_reaches_only_the_widget_under_the_cursor() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let clicks = Rc::new(RefCell::new(Vec::new()));
|
|
||||||
|
|
||||||
let (on_left, on_right) = (clicks.clone(), clicks.clone());
|
|
||||||
let left = rect(Color::RED)
|
|
||||||
.width(100)
|
|
||||||
.on(CursorSense::click(), move |_, _| {
|
|
||||||
on_left.borrow_mut().push("left")
|
|
||||||
})
|
|
||||||
.add(&mut h.rsc);
|
|
||||||
let right = rect(Color::BLUE)
|
|
||||||
.on(CursorSense::click(), move |_, _| {
|
|
||||||
on_right.borrow_mut().push("right")
|
|
||||||
})
|
|
||||||
.add(&mut h.rsc);
|
|
||||||
h.set_root((left, right).span(Dir::RIGHT));
|
|
||||||
|
|
||||||
h.click((50, 100));
|
|
||||||
assert_eq!(*clicks.borrow(), ["left"]);
|
|
||||||
|
|
||||||
h.click((300, 100));
|
|
||||||
assert_eq!(*clicks.borrow(), ["left", "right"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hover_ends_when_the_cursor_leaves_the_window() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let hovered = Rc::new(RefCell::new(0));
|
|
||||||
let ended = Rc::new(RefCell::new(0));
|
|
||||||
|
|
||||||
let (h_count, e_count) = (hovered.clone(), ended.clone());
|
|
||||||
let widget = rect(Color::RED)
|
|
||||||
.on(CursorSense::HoverStart, move |_, _| {
|
|
||||||
*h_count.borrow_mut() += 1
|
|
||||||
})
|
|
||||||
.on(CursorSense::HoverEnd, move |_, _| {
|
|
||||||
*e_count.borrow_mut() += 1
|
|
||||||
})
|
|
||||||
.add(&mut h.rsc);
|
|
||||||
h.set_root(widget);
|
|
||||||
|
|
||||||
h.move_to((200, 100));
|
|
||||||
assert_eq!((*hovered.borrow(), *ended.borrow()), (1, 0));
|
|
||||||
|
|
||||||
// A second sample inside the same widget is not a second hover.
|
|
||||||
h.move_to((210, 100));
|
|
||||||
assert_eq!((*hovered.borrow(), *ended.borrow()), (1, 0));
|
|
||||||
|
|
||||||
h.leave();
|
|
||||||
assert_eq!((*hovered.borrow(), *ended.borrow()), (1, 1));
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
//! Input across layers: what stops at a layer, what passes through it, and
|
|
||||||
//! where hovering stops.
|
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use iris::harness::Harness;
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
const WINDOW: f32 = 100.0;
|
|
||||||
|
|
||||||
/// Every sense that has fired on one widget since it was last read.
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct Fired(Rc<RefCell<Vec<CursorSense>>>);
|
|
||||||
|
|
||||||
impl Fired {
|
|
||||||
fn take(&self) -> Vec<CursorSense> {
|
|
||||||
std::mem::take(&mut self.0.borrow_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A widget filling whatever it is given, recording the senses it is sent.
|
|
||||||
fn listener(h: &mut Harness, senses: impl Into<CursorSenses>) -> (WeakWidget<Rect>, Fired) {
|
|
||||||
let fired = Fired::default();
|
|
||||||
let record = fired.clone();
|
|
||||||
let id = rect(Color::WHITE)
|
|
||||||
.on(senses.into(), move |ctx, _| {
|
|
||||||
record.0.borrow_mut().push(ctx.data.sense)
|
|
||||||
})
|
|
||||||
.add(&mut h.rsc);
|
|
||||||
(id, fired)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A widget with no senses of its own, to leave a gap beside one that has.
|
|
||||||
fn blank(h: &mut Harness) -> WeakWidget<Rect> {
|
|
||||||
rect(Color::WHITE).add(&mut h.rsc)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn harness() -> Harness {
|
|
||||||
Harness::new((WINDOW, WINDOW))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hover_stops_at_the_topmost_widget() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (bottom, bottom_hover) = listener(&mut h, CursorSense::HoverStart);
|
|
||||||
let (middle, middle_hover) = listener(&mut h, CursorSense::HoverStart);
|
|
||||||
let (top, top_hover) = listener(&mut h, CursorSense::HoverStart);
|
|
||||||
h.set_root((bottom, middle, top).stack());
|
|
||||||
|
|
||||||
h.move_to((50, 50));
|
|
||||||
|
|
||||||
assert_eq!(top_hover.take(), [CursorSense::HoverStart]);
|
|
||||||
assert_eq!(
|
|
||||||
middle_hover.take(),
|
|
||||||
[],
|
|
||||||
"hover is not shared with a layer below"
|
|
||||||
);
|
|
||||||
assert_eq!(bottom_hover.take(), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_scroll_passes_through_every_widget_that_does_not_want_it() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (list, scrolled) = listener(&mut h, CursorSense::Scroll);
|
|
||||||
let (button, clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
let (overlay, overlay_clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
h.set_root((list, button, overlay).stack());
|
|
||||||
|
|
||||||
h.move_to((50, 50));
|
|
||||||
h.scroll((0, 10));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
scrolled.take(),
|
|
||||||
[CursorSense::Scroll],
|
|
||||||
"two layers of click-only widgets do not stop a scroll"
|
|
||||||
);
|
|
||||||
assert_eq!(clicked.take(), []);
|
|
||||||
assert_eq!(overlay_clicked.take(), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hovering_a_button_above_does_not_stop_a_later_scroll() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (list, scrolled) = listener(&mut h, CursorSense::Scroll);
|
|
||||||
let (button, _clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
h.set_root((list, button).stack());
|
|
||||||
|
|
||||||
// The hover arrives in its own frame, as a window delivers it.
|
|
||||||
h.move_to((50, 50));
|
|
||||||
assert_eq!(scrolled.take(), []);
|
|
||||||
|
|
||||||
h.scroll((0, 10));
|
|
||||||
assert_eq!(
|
|
||||||
scrolled.take(),
|
|
||||||
[CursorSense::Scroll],
|
|
||||||
"a hover already resting on the button must not consume the wheel"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn only_the_topmost_listener_takes_a_press() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (below, below_clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
let (above, above_clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
h.set_root((below, above).stack());
|
|
||||||
|
|
||||||
h.click((50, 50));
|
|
||||||
|
|
||||||
assert_eq!(above_clicked.take(), [CursorSense::click()]);
|
|
||||||
assert_eq!(below_clicked.take(), [], "one press goes to one widget");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_press_beside_the_button_reaches_the_layer_below() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (list, list_clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
// The row above the list covers it, but only its left half is the button.
|
|
||||||
let (button, button_clicked) = listener(&mut h, CursorSense::click());
|
|
||||||
let row = (button, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
|
||||||
h.set_root((list, row).stack());
|
|
||||||
|
|
||||||
h.click((20, 50));
|
|
||||||
assert_eq!(button_clicked.take(), [CursorSense::click()]);
|
|
||||||
assert_eq!(list_clicked.take(), []);
|
|
||||||
|
|
||||||
h.click((80, 50));
|
|
||||||
assert_eq!(button_clicked.take(), [], "the cursor is not on the button");
|
|
||||||
assert_eq!(
|
|
||||||
list_clicked.take(),
|
|
||||||
[CursorSense::click()],
|
|
||||||
"a press beside the button belongs to what is under it"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn leaving_a_widget_still_ends_its_hover() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (widget, hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
|
||||||
h.set_root(widget);
|
|
||||||
|
|
||||||
h.move_to((50, 50));
|
|
||||||
assert_eq!(hover.take(), [CursorSense::HoverStart]);
|
|
||||||
|
|
||||||
h.leave();
|
|
||||||
assert_eq!(hover.take(), [CursorSense::HoverEnd]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn leaving_a_widget_does_not_block_the_layer_below() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (below, below_hover) = listener(&mut h, CursorSense::HoverStart);
|
|
||||||
// Only the left half of the layer above is a widget, so the cursor can
|
|
||||||
// leave it without leaving the one underneath.
|
|
||||||
let (above, above_hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
|
||||||
let row = (above, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
|
||||||
h.set_root((below, row).stack());
|
|
||||||
|
|
||||||
h.move_to((20, 50));
|
|
||||||
assert_eq!(above_hover.take(), [CursorSense::HoverStart]);
|
|
||||||
assert_eq!(below_hover.take(), [], "the layer above is over it");
|
|
||||||
|
|
||||||
h.move_to((80, 50));
|
|
||||||
assert_eq!(above_hover.take(), [CursorSense::HoverEnd]);
|
|
||||||
assert_eq!(
|
|
||||||
below_hover.take(),
|
|
||||||
[CursorSense::HoverStart],
|
|
||||||
"ending a hover above must not stop the hover below"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn covering_a_widget_ends_its_hover() {
|
|
||||||
let mut h = harness();
|
|
||||||
let (below, below_hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
|
||||||
let (above, above_hover) = listener(&mut h, CursorSense::HoverStart);
|
|
||||||
let row = (above, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
|
||||||
h.set_root((below, row).stack());
|
|
||||||
|
|
||||||
h.move_to((80, 50));
|
|
||||||
assert_eq!(below_hover.take(), [CursorSense::HoverStart]);
|
|
||||||
|
|
||||||
h.move_to((20, 50));
|
|
||||||
assert_eq!(above_hover.take(), [CursorSense::HoverStart]);
|
|
||||||
assert_eq!(
|
|
||||||
below_hover.take(),
|
|
||||||
[CursorSense::HoverEnd],
|
|
||||||
"a widget covered by one that took the input is no longer hovered"
|
|
||||||
);
|
|
||||||
|
|
||||||
h.move_to((80, 50));
|
|
||||||
assert_eq!(
|
|
||||||
below_hover.take(),
|
|
||||||
[CursorSense::HoverStart],
|
|
||||||
"uncovering it hovers it again"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hover_starts_and_ends_once_each() {
|
|
||||||
let mut h = harness();
|
|
||||||
// Only the left half is the widget, so the cursor can leave it without
|
|
||||||
// leaving the window.
|
|
||||||
let (widget, hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
|
||||||
let row = (widget, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
|
||||||
h.set_root(row);
|
|
||||||
|
|
||||||
h.move_to((20, 50));
|
|
||||||
assert_eq!(hover.take(), [CursorSense::HoverStart]);
|
|
||||||
|
|
||||||
h.move_to((30, 50));
|
|
||||||
assert_eq!(hover.take(), [], "staying inside is not a second start");
|
|
||||||
|
|
||||||
h.move_to((80, 50));
|
|
||||||
assert_eq!(hover.take(), [CursorSense::HoverEnd]);
|
|
||||||
|
|
||||||
h.move_to((90, 50));
|
|
||||||
assert_eq!(hover.take(), [], "an ended hover does not end again");
|
|
||||||
|
|
||||||
h.move_to((20, 50));
|
|
||||||
assert_eq!(
|
|
||||||
hover.take(),
|
|
||||||
[CursorSense::HoverStart],
|
|
||||||
"re-entering starts it"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
//! What a second frame draws again, and what it keeps.
|
|
||||||
|
|
||||||
use std::{cell::Cell, rc::Rc};
|
|
||||||
|
|
||||||
use iris::harness::{Harness, assert_corners};
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
/// A leaf that counts its draws and reports whatever size it is given, so a
|
|
||||||
/// test can see what the retained path skipped.
|
|
||||||
struct Counted {
|
|
||||||
draws: Rc<Cell<usize>>,
|
|
||||||
size: Size,
|
|
||||||
dependence: SizeDependence,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Widget for Counted {
|
|
||||||
fn draw(&mut self, painter: &mut Painter) {
|
|
||||||
self.draws.set(self.draws.get() + 1);
|
|
||||||
painter.set_size(self.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_dependence(&self, _: Axis) -> SizeDependence {
|
|
||||||
self.dependence
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Counts(Rc<Cell<usize>>);
|
|
||||||
|
|
||||||
impl Counts {
|
|
||||||
fn get(&self) -> usize {
|
|
||||||
self.0.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn counted(
|
|
||||||
h: &mut Harness,
|
|
||||||
size: Size,
|
|
||||||
dependence: SizeDependence,
|
|
||||||
) -> (WeakWidget<Counted>, Counts) {
|
|
||||||
let draws = Rc::new(Cell::new(0));
|
|
||||||
let id = Counted {
|
|
||||||
draws: draws.clone(),
|
|
||||||
size,
|
|
||||||
dependence,
|
|
||||||
}
|
|
||||||
.add(&mut h.rsc);
|
|
||||||
(id, Counts(draws))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A fixed-width leaf beside one that takes the rest, so changing the first
|
|
||||||
/// hands the second a different box without the output changing.
|
|
||||||
fn pair(h: &mut Harness, rest: SizeDependence) -> (WeakWidget<Counted>, Counts, WidgetId) {
|
|
||||||
let (first, _) = counted(h, Size::from((100, 200)), SizeDependence::Internal);
|
|
||||||
let (second, draws) = counted(h, Size::REST, rest);
|
|
||||||
h.set_root((first, second).span(Dir::RIGHT));
|
|
||||||
(first, draws, second.id())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_leaf_that_ignores_its_box_is_not_drawn_again_when_the_box_changes() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let (first, draws, second) = pair(&mut h, SizeDependence::None);
|
|
||||||
let settled = draws.get();
|
|
||||||
assert_corners!(h, second, (100, 0), (400, 200));
|
|
||||||
|
|
||||||
h.rsc[first].size = Size::from((150, 200));
|
|
||||||
h.frame();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
draws.get(),
|
|
||||||
settled,
|
|
||||||
"its box is a field to write, not a reason to draw"
|
|
||||||
);
|
|
||||||
assert_corners!(h, second, (150, 0), (400, 200));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_leaf_that_depends_on_its_box_is_drawn_again_when_the_box_changes() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let (first, draws, second) = pair(&mut h, SizeDependence::External);
|
|
||||||
let settled = draws.get();
|
|
||||||
|
|
||||||
h.rsc[first].size = Size::from((150, 200));
|
|
||||||
h.frame();
|
|
||||||
|
|
||||||
// Twice: once for the span to measure it, once for its real box. A child
|
|
||||||
// that can hint its length is spared the first, and a smaller number here
|
|
||||||
// means someone has made that cheaper rather than broken it.
|
|
||||||
assert_eq!(draws.get(), settled + 2);
|
|
||||||
assert_corners!(h, second, (150, 0), (400, 200));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_span_child_that_declares_its_length_is_drawn_once() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let (told, told_draws) = counted(&mut h, Size::from((100, 200)), SizeDependence::Internal);
|
|
||||||
let (asked, asked_draws) = counted(&mut h, Size::from((100, 200)), SizeDependence::Internal);
|
|
||||||
// The span takes one child's length from its hint and has to draw the
|
|
||||||
// other to find out, so only the second is drawn before it is placed.
|
|
||||||
let hinted = told.width(100).add(&mut h.rsc);
|
|
||||||
h.set_root((hinted, asked).span(Dir::RIGHT));
|
|
||||||
|
|
||||||
assert_eq!(told_draws.get(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
asked_draws.get(),
|
|
||||||
2,
|
|
||||||
"drawn to be measured, then again to be placed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_span_relays_out_when_a_child_it_measured_changes() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let (first, _, second) = pair(&mut h, SizeDependence::Internal);
|
|
||||||
|
|
||||||
h.rsc[first].size = Size::from((250, 200));
|
|
||||||
h.frame();
|
|
||||||
|
|
||||||
assert_corners!(h, first, (0, 0), (250, 200));
|
|
||||||
assert_corners!(h, second, (250, 0), (400, 200));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_placed_child_survives_the_next_frame() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
// Both children declare a length, so the span places them from their hints
|
|
||||||
// rather than drawing them to find out.
|
|
||||||
let top = rect(Color::RED).height(80).add(&mut h.rsc);
|
|
||||||
let bottom = rect(Color::BLUE).height(120).add(&mut h.rsc);
|
|
||||||
h.set_root((top, bottom).span(Dir::DOWN));
|
|
||||||
|
|
||||||
h.rsc.widgets_mut().get_dyn_mut(top.id());
|
|
||||||
h.frame();
|
|
||||||
|
|
||||||
assert_corners!(h, top, (0, 0), (400, 80));
|
|
||||||
assert_corners!(h, bottom, (0, 80), (400, 200));
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
//! Scrolling moves content and stops at its ends.
|
|
||||||
|
|
||||||
use iris::harness::{Harness, assert_corners};
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_wheel_scrolls_the_content_and_stops_at_its_end() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
// Twice the window's height, so there is 200 to scroll.
|
|
||||||
let top = rect(Color::RED).height(200).add(&mut h.rsc);
|
|
||||||
let bottom = rect(Color::BLUE).height(200).add(&mut h.rsc);
|
|
||||||
h.set_root((top, bottom).span(Dir::DOWN).scrollable());
|
|
||||||
h.move_to((200, 100));
|
|
||||||
|
|
||||||
// `Scroll` starts snapped to the end.
|
|
||||||
assert_corners!(h, top, (0, -200), (400, 0));
|
|
||||||
|
|
||||||
// The handler scales a wheel line by 50.
|
|
||||||
h.scroll((0, 1));
|
|
||||||
h.frame();
|
|
||||||
assert_corners!(h, top, (0, -150), (400, 50));
|
|
||||||
|
|
||||||
h.scroll((0, 10));
|
|
||||||
h.frame();
|
|
||||||
assert_corners!(h, top, (0, 0), (400, 200));
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//! What a background task can change, and how it gets back to the ui.
|
|
||||||
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use iris::harness::Harness;
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_task_update_reaches_the_tree() {
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let widget = rect(Color::RED).add(&mut h.rsc);
|
|
||||||
h.set_root(widget.task_on(CursorSense::click(), async move |mut ctx| {
|
|
||||||
ctx.update(move |_, rsc| widget(rsc).color = Color::BLUE);
|
|
||||||
}));
|
|
||||||
|
|
||||||
h.click((200, 100));
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
h.await_update(Duration::from_secs(5)),
|
|
||||||
"the task sent no update"
|
|
||||||
);
|
|
||||||
assert_eq!(h.rsc[widget].color, Color::BLUE);
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user