From 4bc3f5a8845bbf7e750aaa86f3b8ac708410814b Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Thu, 7 Nov 2024 14:27:08 -0800 Subject: [PATCH] Refactored VirtualList These set of changes attempt to resolve a few complexities from the original implementation: sizing and how to dynamically update the content in the list. On the sizing front, manually specifying the width and height of the rows felt like it was more complex than measuring the first widget and using that for all other widgets. This allows a user who wants to force an explicit size to use the Resize widget, while also supporting SizeToFit flows. Additionally, this paves the way for us to add horizontal scrolling to this list, but this commit was already complex enough I held off on that change for now. One workflow I wanted to see supported was going from 0 rows to 50 rows. When the item count comes from a trait, it was pretty complicated to determine how to tell the list to ask for a new row count. By having the user provide a Value, they can provide a `Dynamic` that can be updated with a new row count whenever the application determines there is new data. We still need to figure out a way to force a refresh of the data even if the row count doesn't change. Ultimately changing this allowed removing the trait and seemingly simplified the basic usage in addition to adding more flexibility. --- Cargo.lock | 567 ++++++++++++------ examples/virtual-list.rs | 24 +- guide/guide-examples/examples/virtual-list.rs | 26 +- src/lib.rs | 13 + src/widget.rs | 2 +- src/widgets.rs | 3 +- src/widgets/button.rs | 2 +- src/widgets/expand.rs | 12 +- src/widgets/virtual_list.rs | 352 ++++++----- 9 files changed, 633 insertions(+), 368 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b3fb0a..a85e624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,12 +27,6 @@ dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -117,9 +111,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "appit" @@ -141,9 +135,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "arboard" @@ -171,7 +165,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -339,7 +333,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -374,7 +368,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -385,23 +379,23 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attribute-derive" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f18fc482cf559bca9efe778ba2fd0d1c16a31d5d24a2c886ed16b2d217e454" +checksum = "f1800e974930e9079c965b9ffbcb6667a40401063a26396c7b4f15edc92da690" dependencies = [ "attribute-derive-macro", "derive-where", "manyhow", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] name = "attribute-derive-macro" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85958950e587256a16c72325ff3c4f3e4db25999173e9ca2864665be84ff63b" +checksum = "5d908eb786ef94296bff86f90130b3b748b49401dc81fd2bb8b3dccd44cfacbd" dependencies = [ "collection_literals", "interpolator", @@ -410,7 +404,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -451,7 +445,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.0", + "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", @@ -492,9 +486,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitstream-io" -version = "2.5.3" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] name = "block" @@ -568,7 +562,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -585,9 +579,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "calloop" @@ -617,9 +611,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" dependencies = [ "jobserver", "libc", @@ -923,7 +917,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -965,7 +959,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -984,6 +978,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "dlib" version = "0.5.2" @@ -1056,7 +1061,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1123,15 +1128,14 @@ dependencies = [ [[package]] name = "exr" -version = "1.72.0" +version = "1.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" dependencies = [ "bit_field", - "flume", "half", "lebe", - "miniz_oxide 0.7.4", + "miniz_oxide", "rayon-core", "smallvec", "zune-inflate", @@ -1151,18 +1155,18 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdeflate" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" dependencies = [ "simd-adler32", ] [[package]] name = "figures" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "097ad6ec659462a9d6efea9af358a9564e52d76981aa9d73343ab3c566d083c0" +checksum = "62cab407ec9ab5f649d809025b956687f756aad87fcea48d59fd56f47771c90d" dependencies = [ "bytemuck", "euclid", @@ -1179,7 +1183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -1188,20 +1192,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "spin", -] - [[package]] name = "font-types" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda6e36206148f69fc6ecb1bb6c0dedd7ee469f3db1d0dc2045beea28430ca43" +checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" dependencies = [ "bytemuck", ] @@ -1247,7 +1242,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1288,9 +1283,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" dependencies = [ "fastrand", "futures-core", @@ -1307,7 +1302,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1499,9 +1494,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "hassle-rs" @@ -1543,20 +1538,149 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "image" -version = "0.25.4" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" dependencies = [ "bytemuck", "byteorder-lite", @@ -1598,14 +1722,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] name = "insta" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -1633,7 +1757,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1729,7 +1853,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kludgine" version = "0.11.0" -source = "git+https://github.com/khonsulabs/kludgine#2f7755a1a9b7cae67711f7c41ee2cd2c6b12fd64" +source = "git+https://github.com/khonsulabs/kludgine#3d7b7164230449f914fa48cc4ccdf2d28b7188f2" dependencies = [ "ahash", "alot", @@ -1766,9 +1890,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libdbus-sys" @@ -1802,9 +1926,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -1829,6 +1953,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "litrs" version = "0.4.1" @@ -1862,9 +1992,9 @@ dependencies = [ [[package]] name = "lyon_geom" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edecfb8d234a2b0be031ab02ebcdd9f3b9ee418fb35e265f7a540a48d197bff9" +checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570" dependencies = [ "arrayvec", "euclid", @@ -1873,9 +2003,9 @@ dependencies = [ [[package]] name = "lyon_path" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c08a606c7a59638d6c6aa18ac91a06aa9fb5f765a7efb27e6a4da58700740d7" +checksum = "8e0b8aec2f58586f6eef237985b9a9b7cb3a3aff4417c575075cf95bf925252e" dependencies = [ "lyon_geom", "num-traits", @@ -1910,7 +2040,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1940,6 +2070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ "cfg-if", + "rayon", ] [[package]] @@ -1987,15 +2118,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -2135,7 +2257,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2186,7 +2308,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2471,7 +2593,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2545,7 +2667,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2559,29 +2681,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2634,14 +2756,14 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -2675,12 +2797,12 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2705,9 +2827,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -2728,7 +2850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2783,7 +2905,7 @@ dependencies = [ "proc-macro-utils", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2874,6 +2996,7 @@ dependencies = [ "loop9", "quick-error", "rav1e", + "rayon", "rgb", ] @@ -2905,9 +3028,9 @@ dependencies = [ [[package]] name = "read-fonts" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb94d9ac780fdcf9b6b252253f7d8f221379b84bd3573131139b383df69f85e1" +checksum = "4a04b892cb6f91951f144c33321843790c8574c825aafdb16d815fd7183b5229" dependencies = [ "bytemuck", "font-types", @@ -2933,9 +3056,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -3029,9 +3152,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" dependencies = [ "bitflags 2.6.0", "errno", @@ -3099,22 +3222,22 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -3125,7 +3248,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -3267,15 +3390,6 @@ dependencies = [ "serde", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -3285,6 +3399,12 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -3299,9 +3419,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "svg_fmt" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" +checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" [[package]] name = "swash" @@ -3327,9 +3447,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3337,10 +3457,21 @@ dependencies = [ ] [[package]] -name = "sys-locale" -version = "0.3.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" dependencies = [ "libc", ] @@ -3388,22 +3519,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -3452,6 +3583,16 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -3469,9 +3610,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "pin-project-lite", @@ -3530,7 +3671,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -3637,15 +3778,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-properties" version = "0.1.3" @@ -3678,9 +3810,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", @@ -3694,6 +3826,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "v_frame" version = "0.3.8" @@ -3761,7 +3905,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -3795,7 +3939,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3822,9 +3966,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ "bitflags 2.6.0", "rustix", @@ -3845,9 +3989,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" +checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" dependencies = [ "rustix", "wayland-client", @@ -3856,9 +4000,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.4" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -3868,9 +4012,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" +checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -3881,9 +4025,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" +checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -4378,6 +4522,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x11-dl" version = "2.21.0" @@ -4447,9 +4603,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" +checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f" [[package]] name = "yazi" @@ -4457,6 +4613,30 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zbus" version = "4.4.0" @@ -4504,7 +4684,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "zvariant_utils", ] @@ -4543,7 +4723,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", ] [[package]] @@ -4552,6 +4753,28 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "zune-core" version = "0.4.12" @@ -4599,7 +4822,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "zvariant_utils", ] @@ -4611,5 +4834,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] diff --git a/examples/virtual-list.rs b/examples/virtual-list.rs index 35c9637..7482f84 100644 --- a/examples/virtual-list.rs +++ b/examples/virtual-list.rs @@ -1,29 +1,9 @@ -use cushy::styles::Dimension; use cushy::widget::MakeWidget; -use cushy::widgets::virtual_list::{VirtualList, VirtualListContent}; +use cushy::widgets::VirtualList; use cushy::Run; -use figures::units::Lp; - -#[derive(Debug)] -struct TestVirtualList; - -impl VirtualListContent for TestVirtualList { - fn item_count(&self) -> impl cushy::value::IntoValue { - 50 - } - fn item_height(&self) -> impl cushy::value::IntoValue { - cushy::styles::Dimension::Lp(Lp::inches_f(0.5)) - } - fn widget_at(&self, index: usize) -> impl MakeWidget { - format!("Item {}", index) - } - fn width(&self) -> impl cushy::value::IntoValue { - Dimension::Lp(Lp::inches_f(10.)) - } -} fn list() -> impl MakeWidget { - VirtualList::new(TestVirtualList).expand() + VirtualList::new(50, |index| format!("Item {}", index)).expand() } fn main() -> cushy::Result { diff --git a/guide/guide-examples/examples/virtual-list.rs b/guide/guide-examples/examples/virtual-list.rs index 3781511..f32654a 100644 --- a/guide/guide-examples/examples/virtual-list.rs +++ b/guide/guide-examples/examples/virtual-list.rs @@ -1,32 +1,10 @@ -use cushy::figures::units::Lp; -use cushy::styles::Dimension; use cushy::widget::MakeWidget; -use cushy::widgets::virtual_list::{VirtualList, VirtualListContent}; +use cushy::widgets::VirtualList; use cushy::Run; -// ANCHOR: implementation -#[derive(Debug)] -struct TestVirtualList; - -impl VirtualListContent for TestVirtualList { - fn item_count(&self) -> impl cushy::value::IntoValue { - 50 - } - fn item_height(&self) -> impl cushy::value::IntoValue { - cushy::styles::Dimension::Lp(Lp::inches_f(0.5)) - } - fn widget_at(&self, index: usize) -> impl MakeWidget { - format!("Item {}", index) - } - fn width(&self) -> impl cushy::value::IntoValue { - Dimension::Lp(Lp::inches_f(10.)) - } -} -// ANCHOR_END: implementation - // ANCHOR: list fn list() -> impl MakeWidget { - VirtualList::new(TestVirtualList).expand() + VirtualList::new(50, |index| format!("Item {}", index)).expand() } // ANCHOR_END: list diff --git a/src/lib.rs b/src/lib.rs index 28e4308..ae48f5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,6 +195,19 @@ impl ConstraintLimit { ConstraintLimit::SizeToFit(_) => measured.into_unsigned(), } } + + /// When `self` is `SizeToFit`, the smallest of the constraint and + /// `measured` will be returned. When `self` is `Fill`, the fill size will + /// be returned. + pub fn fill_or_fit(self, measured: Unit) -> UPx + where + Unit: IntoUnsigned, + { + match self { + ConstraintLimit::Fill(size) => size, + ConstraintLimit::SizeToFit(size) => size.min(measured.into_unsigned()), + } + } } /// An extension trait for `Size`. diff --git a/src/widget.rs b/src/widget.rs index 809456f..8e4fca6 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1348,7 +1348,7 @@ pub trait MakeWidget: Sized { /// Creates a [`WidgetRef`] for use as child widget. #[must_use] - fn widget_ref(self) -> WidgetRef { + fn into_ref(self) -> WidgetRef { WidgetRef::new(self) } diff --git a/src/widgets.rs b/src/widgets.rs index 054c870..0bbeb18 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -25,7 +25,6 @@ pub mod progress; pub mod radio; mod resize; pub mod scroll; -pub mod virtual_list; pub mod select; pub mod shortcuts; pub mod slider; @@ -36,6 +35,7 @@ mod switcher; mod themed; mod tilemap; pub mod validated; +mod virtual_list; pub mod wrap; pub use self::align::Align; @@ -70,4 +70,5 @@ pub use self::switcher::Switcher; pub use self::themed::Themed; pub use self::tilemap::TileMap; pub use self::validated::Validated; +pub use self::virtual_list::VirtualList; pub use self::wrap::Wrap; diff --git a/src/widgets/button.rs b/src/widgets/button.rs index 027ae3b..fa57373 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -137,7 +137,7 @@ impl Button { /// Returns a new button with the provided label. pub fn new(content: impl MakeWidget) -> Self { Self { - content: content.widget_ref(), + content: content.into_ref(), on_click: None, per_window: WindowLocal::default(), kind: Value::Constant(ButtonKind::default()), diff --git a/src/widgets/expand.rs b/src/widgets/expand.rs index 0b096c1..5931ed0 100644 --- a/src/widgets/expand.rs +++ b/src/widgets/expand.rs @@ -107,7 +107,17 @@ impl WrapperWidget for Expand { available_space: Size, context: &mut LayoutContext<'_, '_, '_, '_>, ) -> WrappedLayout { - let available_space = available_space.map(|lim| ConstraintLimit::Fill(lim.max())); + let available_space = match &self.kind { + ExpandKind::Weighted(_) => available_space.map(|lim| ConstraintLimit::Fill(lim.max())), + ExpandKind::Horizontal => Size::new( + ConstraintLimit::Fill(available_space.width.max()), + ConstraintLimit::SizeToFit(available_space.height.max()), + ), + ExpandKind::Vertical => Size::new( + ConstraintLimit::SizeToFit(available_space.width.max()), + ConstraintLimit::Fill(available_space.height.max()), + ), + }; let child = self.child.mounted(&mut context.as_event_context()); let size = context.for_other(&child).layout(available_space); diff --git a/src/widgets/virtual_list.rs b/src/widgets/virtual_list.rs index b7bd93c..0d29042 100644 --- a/src/widgets/virtual_list.rs +++ b/src/widgets/virtual_list.rs @@ -1,22 +1,38 @@ -use std::{collections::VecDeque, fmt::Debug, ops::Range}; +use std::collections::VecDeque; +use std::fmt::Debug; +use std::ops::Range; -use crate::{context::{AsEventContext, EventContext}, figures::{units::{Px, UPx}, IntoSigned, Point, Rect, Round, ScreenScale, Size, Zero}, kludgine::app::winit::{event::{ MouseScrollDelta, TouchPhase}, window::CursorIcon}, styles::Dimension, value::{Destination, Dynamic, DynamicReader, IntoDynamic, IntoValue, Source}, widget::{EventHandling, MakeWidget, MountedWidget, Widget, HANDLED, IGNORED}, widgets::scroll::ScrollBar, window::DeviceId, ConstraintLimit}; +use cushy::context::LayoutContext; +use cushy::ConstraintLimit; +use figures::UnscaledUnit; use super::scroll::OwnedWidget; +use crate::context::{AsEventContext, EventContext}; +use crate::figures::units::{Px, UPx}; +use crate::figures::{IntoSigned, Point, Rect, Round, ScreenScale, Size, Zero}; +use crate::kludgine::app::winit::event::{MouseScrollDelta, TouchPhase}; +use crate::kludgine::app::winit::window::CursorIcon; +use crate::value::{Destination, Dynamic, DynamicReader, IntoDynamic, IntoValue, Source}; +use crate::widget::{ + Callback, EventHandling, MakeWidget, MountedWidget, Widget, WidgetInstance, HANDLED, IGNORED, +}; +use crate::widgets::scroll::ScrollBar; +use crate::window::DeviceId; -/// A virtual list contents. -/// This simple virtual list assumes that all items have the same height, width and that the item count is known. -/// All the values are dynamic, so the list will update when the values change. -pub trait VirtualListContent: Debug { - /// Single item height - fn item_height(&self) -> impl IntoValue; - /// Width of the items - fn width(&self) -> impl IntoValue; - /// Number of items - fn item_count(&self) -> impl IntoValue; - /// Create a widget for the item at the given index. - /// This is called when the widget comes into view. The widget may be removed at any moment (by scrolling it out of view) and recreated later. - fn widget_at(&self, index: usize) -> impl MakeWidget; +#[derive(Debug)] +struct RowMaker(Callback); + +impl RowMaker { + fn make_row( + &mut self, + index: usize, + context: &mut LayoutContext<'_, '_, '_, '_>, + ) -> VirtualListItem { + VirtualListItem { + index, + mounted: context.push_child(self.0.invoke(index)), + } + } } #[derive(Debug)] @@ -26,37 +42,47 @@ struct VirtualListItem { } #[derive(Debug)] -/// A virtual list widget. -/// Requires a [VirtualListContent] trait implementation to render the items. -/// Items are lazily recreated as they go in and out of view. -pub struct VirtualList { - virtual_list: T, +/// A virtuallized list view +/// +/// This widget allows scrolling a list of rows by lazily loading only the rows +/// that are currently being displayed to the screen. +pub struct VirtualList { + make_row: RowMaker, vertical_scroll: OwnedWidget, items: VecDeque, content_size: Dynamic>, - /// Maximum scroll value - max_scroll.y + control_size.height should be the height of the content. + /// Maximum scroll value - `max_scroll.y` + `control_size.height` should be the height of the content. pub max_scroll: DynamicReader>, - /// Current scroll value. The x value is always 0. Change the value to scroll the widget programmatically. + /// Current scroll value. Changes to this dynamic will scroll the list + /// programmatically. pub scroll: Dynamic>, control_size: Dynamic>, - /// Height of an item. Based on [VirtualListContent::item_height]. - pub item_height: DynamicReader, - /// Width of the items. Based on [VirtualListContent::width]. - pub width: DynamicReader, - /// Number of items. Based on [VirtualListContent::item_count]. - pub item_count: DynamicReader, + item_count: DynamicReader, + item_size: Dynamic>, - visible_range: Dynamic> + visible_range: Dynamic>, } -impl VirtualList { - /// Creates a new [VirtualList] based on the given [VirtualListContent]. - pub fn new(virtual_list: T) -> Self { +impl VirtualList { + /// Creates a new [`VirtualList`] that displays `item_count` rows, loading + /// each row as needed by invoking `make_row`. + /// + /// `make_row` will be called each time a new row becomes visible. As rows + /// are no longer visible, they will be freed, ensuring a minimum number of + /// widgets is kept in memory at any given time. + /// + /// Each row will be sized to match the first visible row. To ensure all + /// rows have a consistent size, use the [`Resize`](../Resize) widget. + pub fn new(item_count: impl IntoValue, mut make_row: MakeRow) -> Self + where + MakeRow: FnMut(usize) -> Row + Send + 'static, + Row: MakeWidget, + { + let make_row = RowMaker(Callback::new(move |row| make_row(row).make_widget())); let scroll = Dynamic::>::default(); - let item_height = virtual_list.item_height().into_value().into_dynamic().create_reader(); - let width = virtual_list.width().into_value().into_dynamic().create_reader(); - let item_count = virtual_list.item_count().into_value().into_dynamic().create_reader(); + let item_size = Dynamic::new(Size::ZERO); + let item_count = item_count.into_value().into_dynamic().into_reader(); let content_size = Dynamic::new(Size::default()); let y = scroll.map_each_cloned(|scroll| scroll.y); @@ -71,14 +97,14 @@ impl VirtualList { } }) .persist(); - let vertical = - ScrollBar::new(content_size.map_each_cloned(|size| size.height), y, true); - let max_scroll = (&vertical.max_scroll()) + let vertical = ScrollBar::new(content_size.map_each_cloned(|size| size.height), y, true); + let max_scroll = vertical + .max_scroll() .map_each_cloned(|y| Point::new(UPx::ZERO, y)) .into_reader(); Self { - virtual_list, + make_row, vertical_scroll: OwnedWidget::new(vertical), items: VecDeque::new(), control_size: Dynamic::new(Size::default()), @@ -86,10 +112,9 @@ impl VirtualList { max_scroll, scroll, - item_height, - width, + item_size, item_count, - visible_range: Default::default() + visible_range: Dynamic::default(), } } @@ -134,9 +159,140 @@ impl VirtualList { .expect("a ScrollBar") .hide(context); } + + fn layout_rows( + &mut self, + item_count: usize, + available_space: Size, + context: &mut LayoutContext<'_, '_, '_, '_>, + ) -> Size { + let mut item_size = self.calculate_item_size(available_space, context).ceil(); + + let content_height = item_size.height * u32::try_from(item_count).unwrap_or(u32::MAX); + let content_height = content_height.into_upx(context.gfx.scale()); + + let new_control_size = Size::new( + available_space.width.fill_or_fit(item_size.width), + available_space.height.fill_or_fit(content_height), + ) + .ceil(); + if item_size.width < new_control_size.width { + item_size.width = new_control_size.width; + } + + let vertical = self + .vertical_scroll + .make_if_needed() + .mounted(&mut context.as_event_context()); + let scrollbar_layout = context.for_other(&vertical).layout(available_space); + context.set_child_layout( + &vertical, + Rect::new( + Point::new( + available_space + .width + .fit_measured(new_control_size.width) + .saturating_sub(scrollbar_layout.width) + .into_signed(), + Px::ZERO, + ), + scrollbar_layout.into_signed(), + ), + ); + let scroll = self.scroll.get_tracking_invalidate(context); + + let start_item = (scroll.y.floor() / item_size.height).floor().get() as usize; + let end_item = ((scroll.y.ceil() + new_control_size.height) / item_size.height) + .ceil() + .get() as usize; + let end_item = end_item.min(item_count - 1); + + self.visible_range.set(start_item..end_item); + + let first = self.items.front().map(|t| t.index); + let last = self.items.back().map(|t| t.index); + + if self.items.is_empty() || first.unwrap() > end_item || last.unwrap() < start_item { + for item in self.items.drain(..) { + context.remove_child(&item.mounted); + } + self.items.extend( + (start_item..=end_item).map(|index| self.make_row.make_row(index, context)), + ); + } else { + let first = first.expect("List is not empty"); + let last = last.expect("List is not empty"); + while self + .items + .front() + .map_or(false, |item| item.index < start_item) + { + context.remove_child(&self.items.pop_front().expect("at least one item").mounted); + } + while self + .items + .back() + .map_or(false, |item| item.index > end_item) + { + self.items.pop_back(); + } + // no extend front :( + for item in (start_item..first).rev() { + self.items.push_front(self.make_row.make_row(item, context)); + } + self.items.extend( + ((last + 1)..=end_item).map(|index| self.make_row.make_row(index, context)), + ); + } + + // TODO add % to Figures + let mut y = + -UPx::from_unscaled(scroll.y.into_unscaled() % item_size.height.into_unscaled()) + .into_signed(); + let constraint = item_size.map(ConstraintLimit::SizeToFit); + for item in &self.items { + let child_size = context.for_other(&item.mounted).layout(constraint); + + context.set_child_layout( + &item.mounted, + Rect::new( + Point::new(Px::ZERO, y), + item_size.min(child_size).into_signed(), + ), + ); + y += item_size.height.into_signed(); + } + + self.control_size.set(new_control_size); + self.content_size + .set(Size::new(item_size.width, content_height)); + self.item_size.set(item_size); + + new_control_size + } + + fn calculate_item_size( + &mut self, + available_space: Size, + context: &mut LayoutContext<'_, '_, '_, '_>, + ) -> Size { + if self.items.is_empty() { + self.items.push_front(self.make_row.make_row(0, context)); + } + + context + .for_other( + &self + .items + .front() + .expect("at least one mounted item") + .mounted, + ) + .layout(available_space.map(|space| ConstraintLimit::SizeToFit(space.max()))) + } } -impl Widget for VirtualList { +impl Widget for VirtualList { fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_>) -> bool { true } @@ -173,104 +329,16 @@ impl Widget for VirtualList { } fn layout( - &mut self, - available_space: Size, - context: &mut cushy::context::LayoutContext<'_, '_, '_, '_>, - ) -> Size { - let item_height = self.item_height.get_tracking_invalidate(context); - let item_height_upx = item_height.into_upx(context.gfx.scale()); + &mut self, + available_space: Size, + context: &mut LayoutContext<'_, '_, '_, '_>, + ) -> Size { let item_count = self.item_count.get_tracking_invalidate(context); - let content_height = item_height * item_count as i32; - let content_height = content_height.into_upx(context.gfx.scale()); - let width = self.width.get_tracking_invalidate(context); - let width = width.into_upx(context.gfx.scale()); - - let new_control_size = Size::new( - width, - constrain_child(available_space.height, content_height), - ); - - let vertical = self - .vertical_scroll - .make_if_needed() - .mounted(&mut context.as_event_context()); - let scrollbar_layout = context.for_other(&vertical).layout(available_space); - context.set_child_layout( - &vertical, - Rect::new( - Point::new( - available_space.width - .fit_measured(new_control_size.width) - .saturating_sub(scrollbar_layout.width) - .into_signed(), - Px::ZERO, - ), - scrollbar_layout.into_signed(), - ), - ); - let scroll = self.scroll.get_tracking_invalidate(context); - - let start_item = (scroll.y / item_height_upx).floor().get() as usize; - let end_item = ((scroll.y + new_control_size.height) / item_height_upx).ceil().get() as usize; - let end_item = end_item.min(item_count-1); - - self.visible_range.set(start_item..end_item); - - let first = self.items.front().map(|t| t.index); - let last = self.items.back().map(|t| t.index); - let mut closure = |index| { - let widget = self.virtual_list.widget_at(index); - let mut widget = widget.widget_ref(); - let mounted = widget.mounted(&mut context.as_event_context()); - VirtualListItem { index, mounted } - }; - if self.items.is_empty() || first.unwrap() > end_item || last.unwrap() < start_item { - self.items.clear(); - self.items.extend((start_item..=end_item).map(closure)); - } else { - let first = first.expect("List is not empty"); - let last = last.expect("List is not empty"); - if first < start_item { - while self.items.front().is_some() && self.items.front().expect("Checked is some").index < start_item { - self.items.pop_front(); - } - } - if last > end_item { - while self.items.back().is_some() && self.items.back().expect("Checked is some").index > end_item { - self.items.pop_back(); - } - } - // no extend front :( - for item in (start_item..first).rev() { - self.items.push_front(closure(item)); - } - self.items.extend(((last+1)..=end_item).map(closure)); + if item_count == 0 { + return available_space.map(ConstraintLimit::min); } - let item_size = Size::new(width, item_height_upx); - let constraint = item_size.map(ConstraintLimit::Fill); - - for item in &self.items { - context.for_other(&item.mounted).layout(constraint); - } - - let item_size = item_size.into_signed(); - let scroll = self.scroll.get_tracking_invalidate(context).into_signed(); - - for item in &self.items { - context.set_child_layout( - &item.mounted, - Rect::new( - Point::new(Px::ZERO, (item_height_upx * item.index as f32).into_signed() - scroll.y), - item_size, - ) - ); - } - - self.control_size.set(new_control_size); - self.content_size.set(Size::new(width, content_height)); - - new_control_size + self.layout_rows(item_count, available_space, context) } fn mouse_wheel( @@ -299,11 +367,3 @@ impl Widget for VirtualList { } } } - -fn constrain_child(constraint: ConstraintLimit, measured: UPx) -> UPx { - match constraint { - ConstraintLimit::Fill(size) => size.min(measured), - // change from Scroll widget: returning just measured here would break the functionality (render too many items) - ConstraintLimit::SizeToFit(size) => size.min(measured), - } -} \ No newline at end of file