From 5d423cf6b867ac77c010234d7a3806b53a77c461 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:22:23 +0000 Subject: [PATCH] Update to upstream unbatched PlainEditor (#766) This is https://github.com/linebender/parley/pull/192 --- Cargo.lock | 301 ++++++----- Cargo.toml | 2 +- masonry/src/text/editor.rs | 850 -------------------------------- masonry/src/text/mod.rs | 19 +- masonry/src/text/render_text.rs | 4 +- masonry/src/text/styleset.rs | 44 -- masonry/src/widget/label.rs | 8 +- masonry/src/widget/prose.rs | 2 + masonry/src/widget/text_area.rs | 225 +++++---- masonry/src/widget/textbox.rs | 2 + xilem/src/view/textbox.rs | 2 +- 11 files changed, 297 insertions(+), 1162 deletions(-) delete mode 100644 masonry/src/text/editor.rs delete mode 100644 masonry/src/text/styleset.rs diff --git a/Cargo.lock b/Cargo.lock index 01c15046b..ff41998c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,15 +20,15 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c97bb3cc1dacbdc6d1147040fc61309590d3e1ab5efd92a8a09c7a2e07284c" +checksum = "d3d3b8f9bae46a948369bc4a03e815d4ed6d616bd00de4051133a5019dc31c5a" [[package]] name = "accesskit_atspi_common" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03db49d2948db6875c69a1ef17816efa8e3d9f36c7cd79e467d8562a6695662b" +checksum = "7c5dd55e6e94949498698daf4d48fb5659e824d7abec0d394089656ceaf99d4f" dependencies = [ "accesskit", "accesskit_consumer", @@ -40,33 +40,34 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3a17950ce0d911f132387777b9b3d05eddafb59b773ccaa53fceefaeb0228e" +checksum = "f47983a1084940ba9a39c077a8c63e55c619388be5476ac04c804cfbd1e63459" dependencies = [ "accesskit", + "hashbrown 0.15.2", "immutable-chunkmap", ] [[package]] name = "accesskit_macos" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d94b7544775dddce398e2500a8b3cc2be3655190879071ce6a9e5610195be4" +checksum = "7329821f3bd1101e03a7d2e03bd339e3ac0dc64c70b4c9f9ae1949e3ba8dece1" dependencies = [ "accesskit", "accesskit_consumer", + "hashbrown 0.15.2", "objc2", "objc2-app-kit", "objc2-foundation", - "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a88d913b144104dd825f75db1b82c63d754b01c53c2f9b7545dcdfae63bb0ed" +checksum = "fcee751cc20d88678c33edaf9c07e8b693cd02819fe89053776f5313492273f5" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -82,12 +83,13 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aaa870a5d047338f03707706141f22c98c20e79d5403bf3c9b195549e6cdeea" +checksum = "24fcd5d23d70670992b823e735e859374d694a3d12bfd8dd32bd3bd8bedb5d81" dependencies = [ "accesskit", "accesskit_consumer", + "hashbrown 0.15.2", "paste", "static_assertions", "windows 0.58.0", @@ -96,9 +98,9 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3555a67a9bb208f620cfc3746f1502d1512f0ffbdb19c6901aa90b111aa56ec5" +checksum = "6a6a48dad5530b6deb9fc7a52cc6c3bf72cdd9eb8157ac9d32d69f2427a5e879" dependencies = [ "accesskit", "accesskit_macos", @@ -147,9 +149,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-activity" @@ -198,9 +200,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arrayref" @@ -340,7 +342,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -375,7 +377,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -534,9 +536,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" dependencies = [ "bytemuck_derive", ] @@ -549,7 +551,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -566,9 +568,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "calloop" @@ -598,9 +600,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ "jobserver", "libc", @@ -798,9 +800,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -880,7 +882,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -953,7 +955,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -964,12 +966,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -994,9 +996,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener", "pin-project-lite", @@ -1033,9 +1035,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1081,12 +1083,12 @@ dependencies = [ [[package]] name = "fontique" version = "0.2.0" -source = "git+https://github.com/linebender/parley?rev=c0d158b5d77c5726ed33fd3f2cbfcde95df374ef#c0d158b5d77c5726ed33fd3f2cbfcde95df374ef" +source = "git+https://github.com/linebender/parley?rev=211878fae20ba8b4bf4dcc39e6bf790eece8aa03#211878fae20ba8b4bf4dcc39e6bf790eece8aa03" dependencies = [ "core-foundation", "core-text", "fontconfig-cache-parser", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "icu_locid", "memmap2", "objc2", @@ -1117,7 +1119,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1215,7 +1217,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1250,9 +1252,9 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb949699c3e4df3a183b1d2142cb24277057055ed23c68ed58894f76c517223" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" dependencies = [ "cfg-if", "libc", @@ -1448,9 +1450,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", @@ -1472,12 +1474,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.4.0" @@ -1498,9 +1494,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1538,9 +1534,9 @@ checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -1707,7 +1703,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1756,12 +1752,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", ] [[package]] @@ -1784,9 +1780,9 @@ checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jni" @@ -1864,15 +1860,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1903,9 +1899,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "litrs" @@ -2061,11 +2057,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2187,7 +2182,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2510,11 +2505,11 @@ dependencies = [ [[package]] name = "parley" version = "0.2.0" -source = "git+https://github.com/linebender/parley?rev=c0d158b5d77c5726ed33fd3f2cbfcde95df374ef#c0d158b5d77c5726ed33fd3f2cbfcde95df374ef" +source = "git+https://github.com/linebender/parley?rev=211878fae20ba8b4bf4dcc39e6bf790eece8aa03#211878fae20ba8b4bf4dcc39e6bf790eece8aa03" dependencies = [ "accesskit", "fontique", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "peniko", "skrifa", "swash", @@ -2559,7 +2554,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2612,7 +2607,7 @@ checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", "rustix", "tracing", @@ -2657,9 +2652,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2681,7 +2676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2713,10 +2708,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 2.0.3", + "thiserror 2.0.4", "tokio", "tracing", ] @@ -2731,11 +2726,11 @@ dependencies = [ "getrandom", "rand", "ring", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.4", "tinyvec", "tracing", "web-time", @@ -2972,15 +2967,15 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -2991,9 +2986,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "once_cell", "ring", @@ -3095,14 +3090,14 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3118,7 +3113,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3250,9 +3245,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3357,9 +3352,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3368,9 +3363,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -3383,7 +3378,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3419,11 +3414,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.4", ] [[package]] @@ -3434,18 +3429,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3460,9 +3455,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -3483,9 +3478,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -3557,9 +3552,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3606,9 +3601,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3617,20 +3612,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3649,9 +3644,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -3731,9 +3726,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "typenum" @@ -3754,9 +3749,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -3784,9 +3779,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -3910,7 +3905,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -3945,7 +3940,7 @@ checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4087,9 +4082,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -4299,7 +4294,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -4310,7 +4305,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -4729,15 +4724,15 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -4747,13 +4742,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "synstructure", ] @@ -4813,7 +4808,7 @@ checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "zbus-lockstep", "zbus_xml", "zvariant", @@ -4828,7 +4823,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "zvariant_utils", ] @@ -4874,27 +4869,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "synstructure", ] @@ -4923,7 +4918,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -4963,7 +4958,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "zvariant_utils", ] @@ -4975,5 +4970,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] diff --git a/Cargo.toml b/Cargo.toml index f1f6cd549..232734006 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ xilem_core = { version = "0.1.0", path = "xilem_core" } vello = "0.3" wgpu = "22.1.0" kurbo = "0.11.1" -parley = { git = "https://github.com/linebender/parley", rev = "c0d158b5d77c5726ed33fd3f2cbfcde95df374ef", features = [ +parley = { git = "https://github.com/linebender/parley", rev = "211878fae20ba8b4bf4dcc39e6bf790eece8aa03", features = [ "accesskit", ] } peniko = "0.2.0" diff --git a/masonry/src/text/editor.rs b/masonry/src/text/editor.rs deleted file mode 100644 index eb0c33dc6..000000000 --- a/masonry/src/text/editor.rs +++ /dev/null @@ -1,850 +0,0 @@ -// Copyright 2020 the Xilem Authors and the Parley Authors -// SPDX-License-Identifier: Apache-2.0 - -// We need to be careful with contributions to this file, as we want them to get back to Parley. - -//! Import of Parley's `PlainEditor` as the version in Parley is insufficient for our needs. - -use core::{cmp::PartialEq, default::Default, fmt::Debug, ops::Range}; - -use accesskit::{Node, NodeId, TreeUpdate}; -use parley::layout::LayoutAccessibility; -use parley::{ - layout::{ - cursor::{Cursor, Selection}, - Affinity, Alignment, Layout, Line, - }, - style::Brush, - FontContext, LayoutContext, Rect, -}; -use std::{borrow::ToOwned, string::String, vec::Vec}; - -use super::styleset::StyleSet; - -/// Opaque representation of a generation. -/// -/// Obtained from [`PlainEditor::generation`]. -// Overflow handling: the generations are only compared, -// so wrapping is fine. This could only fail if exactly -// `u32::MAX` generations happen between drawing -// operations. This is implausible and so can be ignored. -#[derive(PartialEq, Eq, Default, Clone, Copy)] -pub struct Generation(u32); - -impl Generation { - /// Make it not what it currently is. - pub(crate) fn nudge(&mut self) { - self.0 = self.0.wrapping_add(1); - } -} - -/// Basic plain text editor with a single default style. -#[derive(Clone)] -pub struct PlainEditor -where - T: Brush + Clone + Debug + PartialEq + Default, -{ - default_style: StyleSet, - buffer: String, - layout: Layout, - layout_access: LayoutAccessibility, - selection: Selection, - /// Byte offsets of IME composing preedit text in the text buffer. - /// `None` if the IME is not currently composing. - compose: Option>, - width: Option, - scale: f32, - // Simple tracking of when the layout needs to be updated - // before it can be used for `Selection` calculations or - // for drawing. - // Not all operations on `PlainEditor` need to operate on a - // clean layout, and not all operations trigger a layout. - layout_dirty: bool, - // TODO: We could avoid redoing the full text layout if linebreaking or - // alignment were unchanged - // linebreak_dirty: bool, - // alignment_dirty: bool, - alignment: Alignment, - generation: Generation, -} - -impl PlainEditor -where - T: Brush, -{ - pub fn new(font_size: f32) -> Self { - Self { - default_style: StyleSet::new(font_size), - buffer: Default::default(), - layout: Default::default(), - layout_access: Default::default(), - selection: Default::default(), - compose: None, - width: None, - scale: 1.0, - layout_dirty: true, - alignment: Alignment::Start, - // We don't use the `default` value to start with, as our consumers - // will choose to use that as their initial value, but will probably need - // to redraw if they haven't already. - generation: Generation(1), - } - } -} - -/// The argument passed to the callback of [`PlainEditor::transact`], -/// on which the caller performs operations. -pub struct PlainEditorTxn<'a, T> -where - T: Brush + Clone + Debug + PartialEq + Default, -{ - editor: &'a mut PlainEditor, - font_cx: &'a mut FontContext, - layout_cx: &'a mut LayoutContext, -} - -impl PlainEditorTxn<'_, T> -where - T: Brush + Clone + Debug + PartialEq + Default, -{ - // --- MARK: Forced relayout --- - /// Insert at cursor, or replace selection. - pub fn insert_or_replace_selection(&mut self, s: &str) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .replace_selection(self.font_cx, self.layout_cx, s); - } - - /// Delete the selection. - pub fn delete_selection(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.insert_or_replace_selection(""); - } - - /// Delete the selection or the next cluster (typical ‘delete’ behavior). - pub fn delete(&mut self) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.selection.is_collapsed() { - // Upstream cluster range - if let Some(range) = self - .editor - .selection - .focus() - .logical_clusters(&self.editor.layout)[1] - .as_ref() - .map(|cluster| cluster.text_range()) - .and_then(|range| (!range.is_empty()).then_some(range)) - { - self.editor.buffer.replace_range(range, ""); - self.update_layout(); - } - } else { - self.delete_selection(); - } - } - - /// Delete the selection or up to the next word boundary (typical ‘ctrl + delete’ behavior). - pub fn delete_word(&mut self) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.selection.is_collapsed() { - let focus = self.editor.selection.focus(); - let start = focus.index(); - let end = focus.next_logical_word(&self.editor.layout).index(); - if self.editor.text().get(start..end).is_some() { - self.editor.buffer.replace_range(start..end, ""); - self.update_layout(); - self.editor.set_selection( - Cursor::from_byte_index(&self.editor.layout, start, Affinity::Downstream) - .into(), - ); - } - } else { - self.delete_selection(); - } - } - - /// Delete the selection or the previous cluster (typical ‘backspace’ behavior). - pub fn backdelete(&mut self) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.selection.is_collapsed() { - // Upstream cluster - if let Some(cluster) = self - .editor - .selection - .focus() - .logical_clusters(&self.editor.layout)[0] - .clone() - { - let range = cluster.text_range(); - let end = range.end; - let start = if cluster.is_hard_line_break() || cluster.is_emoji() { - // For newline sequences and emoji, delete the previous cluster - range.start - } else { - // Otherwise, delete the previous character - let Some((start, _)) = self - .editor - .text() - .get(..end) - .and_then(|str| str.char_indices().next_back()) - else { - return; - }; - start - }; - self.editor.buffer.replace_range(start..end, ""); - self.update_layout(); - self.editor.set_selection( - Cursor::from_byte_index(&self.editor.layout, start, Affinity::Downstream) - .into(), - ); - } - } else { - self.delete_selection(); - } - } - - /// Delete the selection or back to the previous word boundary (typical ‘ctrl + backspace’ behavior). - pub fn backdelete_word(&mut self) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.selection.is_collapsed() { - let focus = self.editor.selection.focus(); - let end = focus.index(); - let start = focus.previous_logical_word(&self.editor.layout).index(); - if self.editor.text().get(start..end).is_some() { - self.editor.buffer.replace_range(start..end, ""); - self.update_layout(); - self.editor.set_selection( - Cursor::from_byte_index(&self.editor.layout, start, Affinity::Downstream) - .into(), - ); - } - } else { - self.delete_selection(); - } - } - - // --- MARK: IME --- - /// Set the IME preedit composing text. - /// - /// This starts composing. Composing is reset by calling [`PlainEditorTxn::clear_compose`]. - /// While composing, it is a logic error to call anything other than - /// [`PlainEditorTxn::set_compose`] or [`PlainEditorTxn::clear_compose`]. - /// - /// The preedit text replaces the current selection if this call starts composing. - /// - /// The selection is updated based on `cursor`, which contains the byte offsets relative to the - /// start of the preedit text. If `cursor` is `None`, the selection is collapsed to a caret in - /// front of the preedit text. - pub fn set_compose(&mut self, text: &str, cursor: Option<(usize, usize)>) { - debug_assert!(!text.is_empty()); - debug_assert!(cursor.map(|cursor| cursor.1 <= text.len()).unwrap_or(true)); - - let start = if let Some(preedit_range) = self.editor.compose.clone() { - self.editor - .buffer - .replace_range(preedit_range.clone(), text); - preedit_range.start - } else { - if self.editor.selection.is_collapsed() { - self.editor - .buffer - .insert_str(self.editor.selection.text_range().start, text); - } else { - self.editor - .buffer - .replace_range(self.editor.selection.text_range(), text); - } - self.editor.selection.text_range().start - }; - self.editor.compose = Some(start..start + text.len()); - self.update_layout(); - - if let Some(cursor) = cursor { - // Select the location indicated by the IME. - self.editor.set_selection(Selection::new( - self.editor.cursor_at(start + cursor.0), - self.editor.cursor_at(start + cursor.1), - )); - } else { - // IME indicates nothing is to be selected: collapse the selection to a - // caret just in front of the preedit. - self.editor - .set_selection(self.editor.cursor_at(start).into()); - } - } - - /// Stop IME composing. - /// - /// This removes the IME preedit text. - pub fn clear_compose(&mut self) { - if let Some(preedit_range) = self.editor.compose.clone() { - self.editor.buffer.replace_range(preedit_range.clone(), ""); - self.editor.compose = None; - self.update_layout(); - - self.editor - .set_selection(self.editor.cursor_at(preedit_range.start).into()); - } - } - - // --- MARK: Cursor Movement --- - /// Move the cursor to the cluster boundary nearest this point in the layout. - pub fn move_to_point(&mut self, x: f32, y: f32) { - debug_assert!(!self.editor.is_composing()); - - self.refresh_layout(); - self.editor - .set_selection(Selection::from_point(&self.editor.layout, x, y)); - } - - /// Move the cursor to a byte index. - /// - /// No-op if index is not a char boundary. - pub fn move_to_byte(&mut self, index: usize) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.buffer.is_char_boundary(index) { - self.refresh_layout(); - self.editor - .set_selection(self.editor.cursor_at(index).into()); - } - } - - /// Move the cursor to the start of the buffer. - pub fn move_to_text_start(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection(self.editor.selection.move_lines( - &self.editor.layout, - isize::MIN, - false, - )); - } - - /// Move the cursor to the start of the physical line. - pub fn move_to_line_start(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.line_start(&self.editor.layout, false)); - } - - /// Move the cursor to the end of the buffer. - pub fn move_to_text_end(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection(self.editor.selection.move_lines( - &self.editor.layout, - isize::MAX, - false, - )); - } - - /// Move the cursor to the end of the physical line. - pub fn move_to_line_end(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.line_end(&self.editor.layout, false)); - } - - /// Move up to the closest physical cluster boundary on the previous line, preserving the horizontal position for repeated movements. - pub fn move_up(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .previous_line(&self.editor.layout, false), - ); - } - - /// Move down to the closest physical cluster boundary on the next line, preserving the horizontal position for repeated movements. - pub fn move_down(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.next_line(&self.editor.layout, false)); - } - - /// Move to the next cluster left in visual order. - pub fn move_left(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .previous_visual(&self.editor.layout, false), - ); - } - - /// Move to the next cluster right in visual order. - pub fn move_right(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .next_visual(&self.editor.layout, false), - ); - } - - /// Move to the next word boundary left. - pub fn move_word_left(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .previous_visual_word(&self.editor.layout, false), - ); - } - - /// Move to the next word boundary right. - pub fn move_word_right(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .next_visual_word(&self.editor.layout, false), - ); - } - - /// Select the whole buffer. - pub fn select_all(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - Selection::from_byte_index(&self.editor.layout, 0_usize, Affinity::default()) - .move_lines(&self.editor.layout, isize::MAX, true), - ); - } - - /// Collapse selection into caret. - pub fn collapse_selection(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection(self.editor.selection.collapse()); - } - - /// Move the selection focus point to the start of the buffer. - pub fn select_to_text_start(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection(self.editor.selection.move_lines( - &self.editor.layout, - isize::MIN, - true, - )); - } - - /// Move the selection focus point to the start of the physical line. - pub fn select_to_line_start(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.line_start(&self.editor.layout, true)); - } - - /// Move the selection focus point to the end of the buffer. - pub fn select_to_text_end(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection(self.editor.selection.move_lines( - &self.editor.layout, - isize::MAX, - true, - )); - } - - /// Move the selection focus point to the end of the physical line. - pub fn select_to_line_end(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.line_end(&self.editor.layout, true)); - } - - /// Move the selection focus point up to the nearest cluster boundary on the previous line, preserving the horizontal position for repeated movements. - pub fn select_up(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .previous_line(&self.editor.layout, true), - ); - } - - /// Move the selection focus point down to the nearest cluster boundary on the next line, preserving the horizontal position for repeated movements. - pub fn select_down(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.next_line(&self.editor.layout, true)); - } - - /// Move the selection focus point to the next cluster left in visual order. - pub fn select_left(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .previous_visual(&self.editor.layout, true), - ); - } - - /// Move the selection focus point to the next cluster right in visual order. - pub fn select_right(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor - .set_selection(self.editor.selection.next_visual(&self.editor.layout, true)); - } - - /// Move the selection focus point to the next word boundary left. - pub fn select_word_left(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .previous_visual_word(&self.editor.layout, true), - ); - } - - /// Move the selection focus point to the next word boundary right. - pub fn select_word_right(&mut self) { - debug_assert!(!self.editor.is_composing()); - - self.editor.set_selection( - self.editor - .selection - .next_visual_word(&self.editor.layout, true), - ); - } - - /// Select the word at the point. - pub fn select_word_at_point(&mut self, x: f32, y: f32) { - debug_assert!(!self.editor.is_composing()); - - self.refresh_layout(); - self.editor - .set_selection(Selection::word_from_point(&self.editor.layout, x, y)); - } - - /// Select the physical line at the point. - pub fn select_line_at_point(&mut self, x: f32, y: f32) { - debug_assert!(!self.editor.is_composing()); - - self.refresh_layout(); - let line = Selection::line_from_point(&self.editor.layout, x, y); - self.editor.set_selection(line); - } - - /// Move the selection focus point to the cluster boundary closest to point. - pub fn extend_selection_to_point(&mut self, x: f32, y: f32) { - debug_assert!(!self.editor.is_composing()); - - self.refresh_layout(); - // FIXME: This is usually the wrong way to handle selection extension for mouse moves, but not a regression. - self.editor.set_selection( - self.editor - .selection - .extend_to_point(&self.editor.layout, x, y), - ); - } - - /// Move the selection focus point to a byte index. - /// - /// No-op if index is not a char boundary. - pub fn extend_selection_to_byte(&mut self, index: usize) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.buffer.is_char_boundary(index) { - self.refresh_layout(); - self.editor - .set_selection(self.editor.selection.extend(self.editor.cursor_at(index))); - } - } - - /// Select a range of byte indices - /// - /// No-op if either index is not a char boundary. - pub fn select_byte_range(&mut self, start: usize, end: usize) { - debug_assert!(!self.editor.is_composing()); - - if self.editor.buffer.is_char_boundary(start) && self.editor.buffer.is_char_boundary(end) { - self.refresh_layout(); - self.editor.set_selection(Selection::new( - self.editor.cursor_at(start), - self.editor.cursor_at(end), - )); - } - } - - pub fn select_from_accesskit(&mut self, selection: &accesskit::TextSelection) { - debug_assert!(!self.editor.is_composing()); - - self.refresh_layout(); - if let Some(selection) = Selection::from_access_selection( - selection, - &self.editor.layout, - &self.editor.layout_access, - ) { - self.editor.set_selection(selection); - } - } - - // --- MARK: Internal helpers --- - fn update_layout(&mut self) { - self.editor.update_layout(self.font_cx, self.layout_cx); - } - - fn refresh_layout(&mut self) { - self.editor.refresh_layout(self.font_cx, self.layout_cx); - } -} - -impl PlainEditor -where - T: Brush + Clone + Debug + PartialEq + Default, -{ - /// Run a series of [`PlainEditorTxn`] methods. - /// - /// This is a utility shorthand around [`transaction`](Self::transaction); - pub fn transact( - &mut self, - font_cx: &mut FontContext, - layout_cx: &mut LayoutContext, - callback: impl FnOnce(&mut PlainEditorTxn<'_, T>) -> R, - ) -> R { - let mut txn = self.transaction(font_cx, layout_cx); - callback(&mut txn) - } - - /// Run a series of [`PlainEditorTxn`] methods, updating the layout - /// if necessary. - /// - /// This is a utility shorthand to simplify methods which require the editor - /// and the provided contexts. - pub fn transaction<'txn>( - &'txn mut self, - font_cx: &'txn mut FontContext, - layout_cx: &'txn mut LayoutContext, - ) -> PlainEditorTxn<'txn, T> { - PlainEditorTxn { - editor: self, - font_cx, - layout_cx, - } - } - - /// Make a cursor at a given byte index - fn cursor_at(&self, index: usize) -> Cursor { - // TODO: Do we need to be non-dirty? - // FIXME: `Selection` should make this easier - if index >= self.buffer.len() { - Cursor::from_byte_index(&self.layout, self.buffer.len(), Affinity::Upstream) - } else { - Cursor::from_byte_index(&self.layout, index, Affinity::Downstream) - } - } - - fn replace_selection( - &mut self, - font_cx: &mut FontContext, - layout_cx: &mut LayoutContext, - s: &str, - ) { - let range = self.selection.text_range(); - let start = range.start; - if self.selection.is_collapsed() { - self.buffer.insert_str(start, s); - } else { - self.buffer.replace_range(range, s); - } - - self.update_layout(font_cx, layout_cx); - let new_index = start.saturating_add(s.len()); - let affinity = if s.ends_with("\n") { - Affinity::Downstream - } else { - Affinity::Upstream - }; - self.set_selection(Cursor::from_byte_index(&self.layout, new_index, affinity).into()); - } - - /// Update the selection, and nudge the `Generation` if something other than `h_pos` changed. - fn set_selection(&mut self, new_sel: Selection) { - if new_sel.focus() != self.selection.focus() || new_sel.anchor() != self.selection.anchor() - { - self.generation.nudge(); - } - - self.selection = new_sel; - } - - /// If the current selection is not collapsed, returns the text content of - /// that selection. - pub fn selected_text(&self) -> Option<&str> { - if !self.selection.is_collapsed() { - self.text().get(self.selection.text_range()) - } else { - None - } - } - - /// Get rectangles representing the selected portions of text. - pub fn selection_geometry(&self) -> Vec { - self.selection.geometry(&self.layout) - } - - /// Get a rectangle representing the current caret cursor position. - pub fn cursor_geometry(&self, size: f32) -> Option { - Some(self.selection.focus().geometry(&self.layout, size)) - } - - /// Get the lines from the `Layout`. - pub fn lines(&self) -> impl Iterator> + '_ + Clone { - self.layout.lines() - } - - /// Borrow the text content of the buffer. - pub fn text(&self) -> &str { - &self.buffer - } - - /// Get the current `Generation` of the layout, to decide whether to draw. - /// - /// You should store the generation the editor was at when you last drew it, and then redraw - /// when the generation is different (`Generation` is [`PartialEq`], so supports the equality `==` operation). - pub fn generation(&self) -> Generation { - self.generation - } - - /// Get the full read-only details from the layout - pub fn layout( - &mut self, - font_cx: &mut FontContext, - layout_cx: &mut LayoutContext, - ) -> &Layout { - self.refresh_layout(font_cx, layout_cx); - &self.layout - } - - /// Get the full read-only details from the layout, if valid. - pub fn get_layout(&self) -> Option<&Layout> { - if self.layout_dirty { - None - } else { - Some(&self.layout) - } - } - - /// Get the (potentially invalid) details from the layout. - pub fn layout_raw(&self) -> &Layout { - &self.layout - } - - /// Replace the whole text buffer. - pub fn set_text(&mut self, is: &str) { - self.buffer.clear(); - self.buffer.push_str(is); - self.layout_dirty = true; - } - - /// Set the width of the layout. - // TODO: If this is infinite, is the width used for alignnment the min width? - pub fn set_width(&mut self, width: Option) { - // Don't allow empty widths: - // https://github.com/linebender/parley/issues/186 - self.width = width.map(|width| if width > 10. { width } else { 10. }); - self.layout_dirty = true; - } - - /// Set the alignment of the layout. - pub fn set_alignment(&mut self, alignment: Alignment) { - self.alignment = alignment; - self.layout_dirty = true; - } - - /// Set the scale for the layout. - pub fn set_scale(&mut self, scale: f32) { - self.scale = scale; - self.layout_dirty = true; - } - - /// Set the default style for the layout. - pub fn edit_styles(&mut self) -> &mut StyleSet { - self.layout_dirty = true; - &mut self.default_style - } - - /// Update the layout if it is dirty. - fn refresh_layout(&mut self, font_cx: &mut FontContext, layout_cx: &mut LayoutContext) { - if self.layout_dirty { - self.update_layout(font_cx, layout_cx); - } - } - - /// Update the layout. - fn update_layout(&mut self, font_cx: &mut FontContext, layout_cx: &mut LayoutContext) { - let mut builder = layout_cx.ranged_builder(font_cx, &self.buffer, self.scale); - for prop in self.default_style.inner().values() { - builder.push_default(prop.to_owned()); - } - if let Some(ref preedit_range) = self.compose { - builder.push( - parley::style::StyleProperty::Underline(true), - preedit_range.clone(), - ); - } - self.layout = builder.build(&self.buffer); - self.layout.break_all_lines(self.width); - self.layout.align(self.width, self.alignment); - self.selection = self.selection.refresh(&self.layout); - self.layout_dirty = false; - self.generation.nudge(); - } - - /// Whether the editor is currently in IME composing mode. - pub fn is_composing(&self) -> bool { - self.compose.is_some() - } - - pub fn accessibility( - &mut self, - update: &mut TreeUpdate, - node: &mut Node, - next_node_id: impl FnMut() -> NodeId, - x_offset: f64, - y_offset: f64, - ) { - self.layout_access.build_nodes( - &self.buffer, - &self.layout, - update, - node, - next_node_id, - x_offset, - y_offset, - ); - if let Some(selection) = self - .selection - .to_access_selection(&self.layout, &self.layout_access) - { - node.set_text_selection(selection); - } - node.add_action(accesskit::Action::SetTextSelection); - } -} diff --git a/masonry/src/text/mod.rs b/masonry/src/text/mod.rs index 6e2377127..8ef98ed4c 100644 --- a/masonry/src/text/mod.rs +++ b/masonry/src/text/mod.rs @@ -10,11 +10,10 @@ //! //! All of these have the same set of global styling options, and can contain rich text -mod editor; +#![warn(missing_docs)] mod render_text; -mod styleset; -pub use editor::{Generation, PlainEditor, PlainEditorTxn}; +use parley::GenericFamily; pub use render_text::render_text; /// A reference counted string slice. @@ -23,9 +22,21 @@ pub use render_text::render_text; /// it cannot be mutated, but unlike `String` it can be cheaply cloned. pub type ArcStr = std::sync::Arc; +/// The Parley [`parley::Brush`] used within Masonry. +/// +/// This enables updating of brush details without performing relayouts; +/// the inner values are indexes into the `brushes` argument to [`render_text`]. #[derive(Clone, PartialEq, Default, Debug)] pub struct BrushIndex(pub usize); +/// A style property specialised for use within Masonry. pub type StyleProperty = parley::StyleProperty<'static, BrushIndex>; -pub type StyleSet = styleset::StyleSet; +/// A set of styles specialised for use within Masonry. +pub type StyleSet = parley::StyleSet; + +/// Applies the default text styles for Masonry into `styles`. +pub(crate) fn default_styles(styles: &mut StyleSet) { + styles.insert(StyleProperty::LineHeight(1.2)); + styles.insert(GenericFamily::SystemUi.into()); +} diff --git a/masonry/src/text/render_text.rs b/masonry/src/text/render_text.rs index c554aa228..748c663a0 100644 --- a/masonry/src/text/render_text.rs +++ b/masonry/src/text/render_text.rs @@ -11,12 +11,14 @@ use vello::Scene; use super::BrushIndex; /// A function that renders laid out glyphs to a [`Scene`]. +/// +/// The `BrushIndex` values of the runs are indices into `brushes`. pub fn render_text( scene: &mut Scene, transform: Affine, layout: &Layout, brushes: &[Brush], - // TODO: Should this be part of `BrushIndex`? + // TODO: Should this be part of `BrushIndex` (i.e. `brushes`)? hint: bool, ) { for line in layout.lines() { diff --git a/masonry/src/text/styleset.rs b/masonry/src/text/styleset.rs deleted file mode 100644 index ab2c237f6..000000000 --- a/masonry/src/text/styleset.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 the Xilem Authors and the Druid Authors -// SPDX-License-Identifier: Apache-2.0 - -use std::{collections::HashMap, mem::Discriminant}; - -type StyleProperty = parley::StyleProperty<'static, Brush>; - -/// A set of Parley styles. -#[derive(Clone, Debug)] -pub struct StyleSet( - HashMap>, StyleProperty>, -); - -impl StyleSet { - pub fn new(font_size: f32) -> Self { - let mut this = Self(Default::default()); - this.insert(StyleProperty::FontSize(font_size)); - // Emulate: https://developer.mozilla.org/en-US/docs/Web/CSS/line-height#normal - // This is a more sensible default that Parley's default. - // We expect Parley to make a different choice here at some point? - this.insert(StyleProperty::LineHeight(1.2)); - this - } - - pub fn insert(&mut self, style: StyleProperty) -> Option> { - let discriminant = std::mem::discriminant(&style); - self.0.insert(discriminant, style) - } - - pub fn retain(&mut self, mut f: impl FnMut(&StyleProperty) -> bool) { - self.0.retain(|_, v| f(v)); - } - - pub fn remove( - &mut self, - property: Discriminant>, - ) -> Option> { - self.0.remove(&property) - } - - pub fn inner(&self) -> &HashMap>, StyleProperty> { - &self.0 - } -} diff --git a/masonry/src/widget/label.rs b/masonry/src/widget/label.rs index 30ab35a4e..41df90006 100644 --- a/masonry/src/widget/label.rs +++ b/masonry/src/widget/label.rs @@ -1,6 +1,8 @@ // Copyright 2019 the Xilem Authors and the Druid Authors // SPDX-License-Identifier: Apache-2.0 +#![warn(missing_docs)] + //! A label widget. use std::mem::Discriminant; @@ -14,7 +16,7 @@ use vello::kurbo::{Affine, Size}; use vello::peniko::{BlendMode, Brush}; use vello::Scene; -use crate::text::{render_text, ArcStr, BrushIndex, StyleProperty, StyleSet}; +use crate::text::{default_styles, render_text, ArcStr, BrushIndex, StyleProperty, StyleSet}; use crate::widget::WidgetMut; use crate::{ theme, AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, PaintCtx, PointerEvent, @@ -83,11 +85,13 @@ impl Label { // This is written out fully to appease rust-analyzer; StyleProperty is imported but not recognised. /// To change the font size, use `with_style`, setting [`StyleProperty::FontSize`](parley::StyleProperty::FontSize). pub fn new(text: impl Into) -> Self { + let mut styles = StyleSet::new(theme::TEXT_SIZE_NORMAL); + default_styles(&mut styles); Self { text_layout: Layout::new(), accessibility: Default::default(), text: text.into(), - styles: StyleSet::new(theme::TEXT_SIZE_NORMAL), + styles, styles_changed: true, line_break_mode: LineBreaking::Overflow, alignment: Alignment::Start, diff --git a/masonry/src/widget/prose.rs b/masonry/src/widget/prose.rs index c43320903..66b15aa15 100644 --- a/masonry/src/widget/prose.rs +++ b/masonry/src/widget/prose.rs @@ -1,6 +1,8 @@ // Copyright 2018 the Xilem Authors and the Druid Authors // SPDX-License-Identifier: Apache-2.0 +#![warn(missing_docs)] + use accesskit::{Node, Role}; use smallvec::{smallvec, SmallVec}; use tracing::{trace_span, Span}; diff --git a/masonry/src/widget/text_area.rs b/masonry/src/widget/text_area.rs index eb3675c81..5dd81530a 100644 --- a/masonry/src/widget/text_area.rs +++ b/masonry/src/widget/text_area.rs @@ -1,13 +1,17 @@ // Copyright 2018 the Xilem Authors and the Druid Authors // SPDX-License-Identifier: Apache-2.0 +#![warn(missing_docs)] + use std::mem::Discriminant; use std::time::Instant; use crate::kurbo::{Affine, Point, Size}; -use crate::text::{render_text, Generation, PlainEditor}; +use crate::text::{default_styles, render_text}; use accesskit::{Node, NodeId, Role}; +use parley::editor::{Generation, SplitString}; use parley::layout::Alignment; +use parley::PlainEditor; use smallvec::SmallVec; use tracing::{trace_span, Span}; use vello::kurbo::Vec2; @@ -131,6 +135,7 @@ impl TextArea { /// To change the font size, use `with_style`, setting [`StyleProperty::FontSize`](parley::StyleProperty::FontSize). pub fn new(text: &str) -> Self { let mut editor = PlainEditor::new(theme::TEXT_SIZE_NORMAL); + default_styles(editor.edit_styles()); editor.set_text(text); TextArea { editor, @@ -151,7 +156,9 @@ impl TextArea { /// Get the current text of this text area. /// /// To update the text of an active text area, use [`reset_text`](Self::reset_text). - pub fn text(&self) -> &str { + /// + /// The return value is not just `&str` to handle IME preedits. + pub fn text(&self) -> SplitString<'_> { self.editor.text() } @@ -361,9 +368,7 @@ impl TextArea { // preedit will show up again when the platform updates it. if this.widget.editor.is_composing() { let (fctx, lctx) = this.ctx.text_contexts(); - this.widget - .editor - .transact(fctx, lctx, |txn| txn.clear_compose()); + this.widget.editor.driver(fctx, lctx).clear_compose(); } this.widget.editor.set_text(new_text); @@ -485,12 +490,12 @@ impl Widget for TextArea { let click_count = self.click_count; let cursor_pos = Point::new(state.position.x, state.position.y) - inner_origin; let (fctx, lctx) = ctx.text_contexts(); - self.editor.transact(fctx, lctx, |txn| match click_count { - 2 => txn.select_word_at_point(cursor_pos.x as f32, cursor_pos.y as f32), - 3 => txn.select_line_at_point(cursor_pos.x as f32, cursor_pos.y as f32), - _ => txn.move_to_point(cursor_pos.x as f32, cursor_pos.y as f32), - }); - + let mut drv = self.editor.driver(fctx, lctx); + match click_count { + 2 => drv.select_word_at_point(cursor_pos.x as f32, cursor_pos.y as f32), + 3 => drv.select_line_at_point(cursor_pos.x as f32, cursor_pos.y as f32), + _ => drv.move_to_point(cursor_pos.x as f32, cursor_pos.y as f32), + } let new_generation = self.editor.generation(); if new_generation != self.rendered_generation { ctx.request_render(); @@ -504,9 +509,9 @@ impl Widget for TextArea { if !ctx.is_disabled() && ctx.has_pointer_capture() { let cursor_pos = Point::new(state.position.x, state.position.y) - inner_origin; let (fctx, lctx) = ctx.text_contexts(); - self.editor.transact(fctx, lctx, |txn| { - txn.extend_selection_to_point(cursor_pos.x as f32, cursor_pos.y as f32); - }); + self.editor + .driver(fctx, lctx) + .extend_selection_to_point(cursor_pos.x as f32, cursor_pos.y as f32); let new_generation = self.editor.generation(); if new_generation != self.rendered_generation { ctx.request_render(); @@ -547,7 +552,7 @@ impl Widget for TextArea { // if let Some(text) = self.editor.selected_text() { // let cb = ClipboardContext::new().unwrap(); // cb.set_text(text.to_owned()).ok(); - // self.editor.transact(fcx, lcx, |txn| txn.delete_selection()); + // self.editor.drive(fcx, lcx, |drv| drv.delete_selection()); // } // edited = true; } @@ -569,107 +574,114 @@ impl Widget for TextArea { // TODO: use clipboard_rs::{Clipboard, ClipboardContext}; // let cb = ClipboardContext::new().unwrap(); // let text = cb.get_text().unwrap_or_default(); - // self.editor.transact(fcx, lcx, |txn| txn.insert_or_replace_selection(&text)); + // self.editor.drive(fcx, lcx, |drv| drv.insert_or_replace_selection(&text)); // edited = true; } Key::Character(a) if action_mod && a.as_str().eq_ignore_ascii_case("a") => { - self.editor.transact(fctx, lctx, |txn| { - if shift { - txn.collapse_selection(); - } else { - txn.select_all(); - } - }); + let mut drv = self.editor.driver(fctx, lctx); + + if shift { + drv.collapse_selection(); + } else { + drv.select_all(); + } } - Key::Named(NamedKey::ArrowLeft) => self.editor.transact(fctx, lctx, |txn| { + Key::Named(NamedKey::ArrowLeft) => { + let mut drv = self.editor.driver(fctx, lctx); if action_mod { if shift { - txn.select_word_left(); + drv.select_word_left(); } else { - txn.move_word_left(); + drv.move_word_left(); } } else if shift { - txn.select_left(); + drv.select_left(); } else { - txn.move_left(); + drv.move_left(); } - }), - Key::Named(NamedKey::ArrowRight) => self.editor.transact(fctx, lctx, |txn| { + } + Key::Named(NamedKey::ArrowRight) => { + let mut drv = self.editor.driver(fctx, lctx); if action_mod { if shift { - txn.select_word_right(); + drv.select_word_right(); } else { - txn.move_word_right(); + drv.move_word_right(); } } else if shift { - txn.select_right(); + drv.select_right(); } else { - txn.move_right(); + drv.move_right(); } - }), - Key::Named(NamedKey::ArrowUp) => self.editor.transact(fctx, lctx, |txn| { + } + Key::Named(NamedKey::ArrowUp) => { + let mut drv = self.editor.driver(fctx, lctx); if shift { - txn.select_up(); + drv.select_up(); } else { - txn.move_up(); + drv.move_up(); } - }), - Key::Named(NamedKey::ArrowDown) => self.editor.transact(fctx, lctx, |txn| { + } + Key::Named(NamedKey::ArrowDown) => { + let mut drv = self.editor.driver(fctx, lctx); if shift { - txn.select_down(); + drv.select_down(); } else { - txn.move_down(); + drv.move_down(); } - }), - Key::Named(NamedKey::Home) => self.editor.transact(fctx, lctx, |txn| { + } + Key::Named(NamedKey::Home) => { + let mut drv = self.editor.driver(fctx, lctx); if action_mod { if shift { - txn.select_to_text_start(); + drv.select_to_text_start(); } else { - txn.move_to_text_start(); + drv.move_to_text_start(); } } else if shift { - txn.select_to_line_start(); + drv.select_to_line_start(); } else { - txn.move_to_line_start(); + drv.move_to_line_start(); } - }), - Key::Named(NamedKey::End) => self.editor.transact(fctx, lctx, |txn| { + } + Key::Named(NamedKey::End) => { + let mut drv = self.editor.driver(fctx, lctx); if action_mod { if shift { - txn.select_to_text_end(); + drv.select_to_text_end(); } else { - txn.move_to_text_end(); + drv.move_to_text_end(); } } else if shift { - txn.select_to_line_end(); + drv.select_to_line_end(); } else { - txn.move_to_line_end(); + drv.move_to_line_end(); } - }), + } Key::Named(NamedKey::Delete) if EDITABLE => { - self.editor.transact(fctx, lctx, |txn| { - if action_mod { - txn.delete_word(); - } else { - txn.delete(); - } - }); + let mut drv = self.editor.driver(fctx, lctx); + if action_mod { + drv.delete_word(); + } else { + drv.delete(); + } + edited = true; } Key::Named(NamedKey::Backspace) if EDITABLE => { - self.editor.transact(fctx, lctx, |txn| { - if action_mod { - txn.backdelete_word(); - } else { - txn.backdelete(); - } - }); + let mut drv = self.editor.driver(fctx, lctx); + if action_mod { + drv.backdelete_word(); + } else { + drv.backdelete(); + } + edited = true; } Key::Named(NamedKey::Space) if EDITABLE => { self.editor - .transact(fctx, lctx, |txn| txn.insert_or_replace_selection(" ")); + .driver(fctx, lctx) + .insert_or_replace_selection(" "); edited = true; } Key::Named(NamedKey::Enter) => { @@ -678,7 +690,8 @@ impl Widget for TextArea { if multiline { let (fctx, lctx) = ctx.text_contexts(); self.editor - .transact(fctx, lctx, |txn| txn.insert_or_replace_selection("\n")); + .driver(fctx, lctx) + .insert_or_replace_selection("\n"); edited = true; } else { ctx.submit_action(crate::Action::TextEntered(self.text().to_string())); @@ -693,7 +706,8 @@ impl Widget for TextArea { _ if EDITABLE => match &key_event.text { Some(text) => { self.editor - .transact(fctx, lctx, |txn| txn.insert_or_replace_selection(text)); + .driver(fctx, lctx) + .insert_or_replace_selection(text); edited = true; } None => { @@ -710,7 +724,9 @@ impl Widget for TextArea { let new_generation = self.editor.generation(); if new_generation != self.rendered_generation { if edited { - ctx.submit_action(crate::Action::TextChanged(self.text().to_string())); + ctx.submit_action(crate::Action::TextChanged( + self.text().into_iter().collect(), + )); ctx.request_layout(); } else { ctx.request_render(); @@ -723,44 +739,37 @@ impl Widget for TextArea { TextEvent::Ime(e) => { // TODO: Handle the cursor movement things from https://github.com/rust-windowing/winit/pull/3824 let (fctx, lctx) = ctx.text_contexts(); - // The text to submit as the TextChanged action. We do not send a TextChange action - // for the "virtual" preedit text. - let mut submit_text = None; + + // Whether the returned text has changed. + // We don't send a TextChanged when the preedit changes + let mut edited = false; match e { winit::event::Ime::Disabled => { - self.editor.transact(fctx, lctx, |txn| txn.clear_compose()); + self.editor.driver(fctx, lctx).clear_compose(); } winit::event::Ime::Preedit(text, cursor) => { if text.is_empty() { - self.editor.transact(fctx, lctx, |txn| txn.clear_compose()); + self.editor.driver(fctx, lctx).clear_compose(); } else { - if !self.editor.is_composing() && self.editor.selected_text().is_some() - { - // The IME has started composing. Delete the current selection and - // send a TextChange event with the selection removed, but without - // the composing preedit text. - self.editor.transact(fctx, lctx, |txn| { - txn.delete_selection(); - }); - submit_text = Some(self.text().to_string()); - } - - self.editor - .transact(fctx, lctx, |txn| txn.set_compose(text, *cursor)); + self.editor.driver(fctx, lctx).set_compose(text, *cursor); + edited = true; } } winit::event::Ime::Commit(text) => { self.editor - .transact(fctx, lctx, |txn| txn.insert_or_replace_selection(text)); - submit_text = Some(self.text().to_string()); + .driver(fctx, lctx) + .insert_or_replace_selection(text); + edited = true; } winit::event::Ime::Enabled => {} } ctx.set_handled(); - if let Some(text) = submit_text { + if edited { + let text = self.text().into_iter().collect(); ctx.submit_action(crate::Action::TextChanged(text)); } + let new_generation = self.editor.generation(); if new_generation != self.rendered_generation { ctx.request_layout(); @@ -788,7 +797,8 @@ impl Widget for TextArea { if let Some(accesskit::ActionData::SetTextSelection(selection)) = &event.data { let (fctx, lctx) = ctx.text_contexts(); self.editor - .transact(fctx, lctx, |txn| txn.select_from_accesskit(selection)); + .driver(fctx, lctx) + .select_from_accesskit(selection); } } } @@ -850,14 +860,14 @@ impl Widget for TextArea { } fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut Scene) { - let layout = if let Some(layout) = self.editor.get_layout() { + let layout = if let Some(layout) = self.editor.try_layout() { layout } else { debug_panic!("Widget `layout` should have happened before paint"); let (fctx, lctx) = ctx.text_contexts(); // The `layout` method takes `&mut self`, so we get borrow-checker errors if we return it from this block. - self.editor.layout(fctx, lctx); - self.editor.layout_raw() + self.editor.refresh_layout(fctx, lctx); + self.editor.try_layout().unwrap() }; let is_rtl = layout.is_rtl(); let origin = Vec2::new(self.padding.get_left(is_rtl), self.padding.top); @@ -902,15 +912,18 @@ impl Widget for TextArea { node.set_read_only(); } let (fctx, lctx) = ctx.text_contexts(); - let is_rtl = self.editor.layout(fctx, lctx).is_rtl(); + let layout = self.editor.layout(fctx, lctx); + let is_rtl = layout.is_rtl(); let origin = ctx.window_origin(); - self.editor.accessibility( - ctx.tree_update, - node, - || NodeId::from(WidgetId::next()), - origin.x + self.padding.get_left(is_rtl), - origin.y + self.padding.top, - ); + self.editor + .try_accessibility( + ctx.tree_update, + node, + || NodeId::from(WidgetId::next()), + origin.x + self.padding.get_left(is_rtl), + origin.y + self.padding.top, + ) + .expect("We just performed a layout"); } fn children_ids(&self) -> SmallVec<[WidgetId; 16]> { diff --git a/masonry/src/widget/textbox.rs b/masonry/src/widget/textbox.rs index a319494a5..d3a13630d 100644 --- a/masonry/src/widget/textbox.rs +++ b/masonry/src/widget/textbox.rs @@ -1,6 +1,8 @@ // Copyright 2018 the Xilem Authors and the Druid Authors // SPDX-License-Identifier: Apache-2.0 +#![warn(missing_docs)] + use accesskit::{Node, Role}; use smallvec::{smallvec, SmallVec}; use tracing::{trace_span, Span}; diff --git a/xilem/src/view/textbox.rs b/xilem/src/view/textbox.rs index 535ee52f5..03b9472ed 100644 --- a/xilem/src/view/textbox.rs +++ b/xilem/src/view/textbox.rs @@ -94,7 +94,7 @@ impl View for Textbox