mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-01-31 19:06:41 +01:00
Compare commits
No commits in common. "7b67d29c012f2d39a5d9d2fcd840417c90256503" and "a145e332dec73ebea1fa3576ae48be13e3ad104c" have entirely different histories.
7b67d29c01
...
a145e332de
24 changed files with 1088 additions and 1177 deletions
369
Cargo.lock
generated
369
Cargo.lock
generated
|
|
@ -13,9 +13,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
|
|
@ -40,9 +40,9 @@ checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a"
|
|||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
|
|
@ -56,7 +56,7 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -86,15 +86,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "by_address"
|
||||
|
|
@ -110,18 +110,18 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
|||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.4"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
|
|
@ -154,31 +154,6 @@ name = "const_panic"
|
|||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e"
|
||||
dependencies = [
|
||||
"const_panic_proc_macros",
|
||||
"typewit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_panic_proc_macros"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c5b80a80fb52c1a6ca02e3cd829a76b472ff0a15588196fd8da95221f0c1e4b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
|
|
@ -202,24 +177,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"derive_more",
|
||||
"document-features",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix 1.0.8",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
|
|
@ -264,36 +221,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
|
|
@ -314,12 +241,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -354,7 +281,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -377,9 +304,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.4"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
|
|
@ -406,9 +333,9 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
|||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.9"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a"
|
||||
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"indoc",
|
||||
|
|
@ -486,9 +413,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
|
@ -502,17 +429,11 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
|
|
@ -535,29 +456,29 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.4"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -594,7 +515,7 @@ dependencies = [
|
|||
"fast-srgb8",
|
||||
"palette_derive",
|
||||
"phf",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -611,9 +532,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
|
|
@ -621,15 +542,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -655,7 +576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -700,17 +621,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.7.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f"
|
||||
checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
"bitflags",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"rand 0.9.1",
|
||||
"rand_chacha 0.9.0",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
|
|
@ -731,15 +652,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quanta"
|
||||
version = "0.12.6"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
|
||||
checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"raw-cpuid",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
|
@ -761,9 +682,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
|
|
@ -772,18 +693,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -793,17 +704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -815,22 +716,13 @@ dependencies = [
|
|||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||
dependencies = [
|
||||
"rand_core 0.9.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -842,7 +734,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm 0.28.1",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools 0.13.0",
|
||||
|
|
@ -865,9 +757,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
@ -880,9 +772,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
|
|
@ -899,22 +791,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.8"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "rusty-fork"
|
||||
|
|
@ -978,9 +870,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
|
|
@ -1018,9 +910,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1029,14 +921,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix 1.0.8",
|
||||
"rustix 1.0.7",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
|
|
@ -1044,7 +936,7 @@ dependencies = [
|
|||
name = "tengri"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"crossterm 0.29.0",
|
||||
"crossterm",
|
||||
"tengri",
|
||||
"tengri_core",
|
||||
"tengri_dsl",
|
||||
|
|
@ -1062,7 +954,6 @@ version = "0.13.0"
|
|||
name = "tengri_dsl"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"const_panic",
|
||||
"itertools 0.14.0",
|
||||
"konst",
|
||||
"proptest",
|
||||
|
|
@ -1109,11 +1000,11 @@ version = "0.13.0"
|
|||
dependencies = [
|
||||
"atomic_float",
|
||||
"better-panic",
|
||||
"crossterm 0.29.0",
|
||||
"crossterm",
|
||||
"konst",
|
||||
"palette",
|
||||
"quanta",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
"ratatui",
|
||||
"tengri",
|
||||
"tengri_core",
|
||||
|
|
@ -1200,12 +1091,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
|
|
@ -1217,9 +1102,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
|
|
@ -1321,20 +1206,20 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1343,30 +1228,14 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1375,96 +1244,48 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
|
|
@ -1476,18 +1297,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
version = "0.8.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.26"
|
||||
version = "0.8.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ edition = "2024"
|
|||
atomic_float = { version = "1" }
|
||||
better-panic = { version = "0.3.0" }
|
||||
const_panic = { version = "0.2.12", features = [ "derive" ] }
|
||||
crossterm = { version = "0.29.0" }
|
||||
crossterm = { version = "0.28.1" }
|
||||
heck = { version = "0.5" }
|
||||
itertools = { version = "0.14.0" }
|
||||
konst = { version = "0.3.16", features = [ "rust_1_83" ] }
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
extern crate const_panic;
|
||||
use const_panic::{concat_panic, PanicFmt};
|
||||
pub(crate) use ::tengri_core::*;
|
||||
use std::ops::Deref;
|
||||
pub(crate) use std::error::Error;
|
||||
pub(crate) use std::fmt::Debug;
|
||||
pub(crate) use std::sync::Arc;
|
||||
|
|
@ -45,16 +44,13 @@ impl<Str: DslStr, Exp: DslExp> Val<Str, Exp> {
|
|||
T::Str: for<'a> From<&'a Str>,
|
||||
T::Exp: for<'a> From<&'a Exp>
|
||||
{
|
||||
match self {
|
||||
Val::Nil => Val::Nil,
|
||||
match self { Val::Nil => Val::Nil,
|
||||
Val::Num(u) => Val::Num(*u),
|
||||
Val::Sym(s) => Val::Sym(s.into()),
|
||||
Val::Key(s) => Val::Key(s.into()),
|
||||
Val::Str(s) => Val::Str(s.into()),
|
||||
Val::Exp(d, x) => Val::Exp(*d, x.into()),
|
||||
Val::Error(e) => Val::Error(*e)
|
||||
}
|
||||
}
|
||||
Val::Error(e) => Val::Error(*e) } }
|
||||
}
|
||||
/// The expression representation for a [Dsl] implementation.
|
||||
/// [Cst] uses [CstIter]. [Ast] uses [VecDeque].
|
||||
|
|
@ -62,11 +58,8 @@ pub trait DslExp: PartialEq + Clone + Default + Debug + Dsl {}
|
|||
impl<T: PartialEq + Clone + Default + Debug + Dsl> DslExp for T {}
|
||||
/// The string representation for a [Dsl] implementation.
|
||||
/// [Cst] uses `&'s str`. [Ast] uses `Arc<str>`.
|
||||
pub trait DslStr: PartialEq + Clone + Default + Debug + AsRef<str> + Deref<Target = str> {
|
||||
fn as_str (&self) -> &str { self.as_ref() }
|
||||
fn as_arc (&self) -> Arc<str> { self.as_ref().into() }
|
||||
}
|
||||
impl<Str: PartialEq + Clone + Default + Debug + AsRef<str> + Deref<Target = str>> DslStr for Str {}
|
||||
pub trait DslStr: PartialEq + Clone + Default + Debug + AsRef<str> + std::ops::Deref<Target = str> {}
|
||||
impl<T: PartialEq + Clone + Default + Debug + AsRef<str> + std::ops::Deref<Target = str>> DslStr for T {}
|
||||
impl<Str: DslStr, Exp: DslExp + Dsl<Str=Str, Exp=Exp>> Val<Str, Exp> {
|
||||
pub const fn err (&self) -> Option<DslError> {match self{Val::Error(e)=>Some(*e), _=>None}}
|
||||
pub const fn nil (&self) -> bool {match self{Val::Nil=>true, _=>false}}
|
||||
|
|
@ -103,6 +96,22 @@ impl<Str, Exp> Token<Str, Exp> {
|
|||
Self { value: self.value, ..*self }
|
||||
}
|
||||
}
|
||||
impl<'s> CstToken<'s> {
|
||||
pub const fn slice (&self) -> &str {
|
||||
str_range(self.source, self.start, self.end()) }
|
||||
pub const fn slice_exp (&self) -> &str {
|
||||
str_range(self.source, self.start.saturating_add(1), self.end()) }
|
||||
pub const fn grow (&mut self) -> &mut Self {
|
||||
let max_length = self.source.len().saturating_sub(self.start);
|
||||
self.length = self.length + 1;
|
||||
if self.length > max_length { self.length = max_length }
|
||||
self
|
||||
}
|
||||
pub const fn grow_exp (&'s mut self, depth: isize, source: &'s str) -> &mut Self {
|
||||
self.value = Val::Exp(depth, Cst(CstConstIter(source)));
|
||||
self
|
||||
}
|
||||
}
|
||||
/// To the [Dsl], a token is equivalent to its `value` field.
|
||||
impl<Str: DslStr, Exp: DslExp> Dsl for Token<Str, Exp> {
|
||||
type Str = Str; type Exp = Exp;
|
||||
|
|
@ -126,8 +135,7 @@ pub trait Dsl: Clone + Debug {
|
|||
fn exp (&self) -> Option<Self::Exp> {self.dsl().exp()}
|
||||
fn exp_depth (&self) -> Option<isize> {self.dsl().exp_depth()}
|
||||
fn exp_head (&self) -> Val<Self::Str, Self::Exp> {self.dsl().exp_head()}
|
||||
fn exp_tail (&self) -> Self::Exp {self.dsl().exp_tail()}
|
||||
fn exp_each (&self, f: impl Fn(&Self) -> Usually<()>) -> Usually<()> { todo!() }
|
||||
fn exp_tail (&self) -> Val<Self::Str, Self::Exp> {self.dsl().exp_tail()}
|
||||
}
|
||||
/// The most basic implementor of the [Dsl] trait.
|
||||
impl<Str: DslStr, Exp: DslExp> Dsl for Val<Str, Exp> {
|
||||
|
|
@ -138,71 +146,25 @@ impl<Str: DslStr, Exp: DslExp> Dsl for Val<Str, Exp> {
|
|||
/// by cloning source slices into owned ([Arc]) string slices.
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct Ast(Arc<VecDeque<Arc<Token<Arc<str>, Ast>>>>);
|
||||
pub type AstVal = Val<Arc<str>, Ast>;
|
||||
pub type AstToken = Token<Arc<str>, Ast>;
|
||||
impl Dsl for Ast {
|
||||
type Str = Arc<str>; type Exp = Ast;
|
||||
fn dsl (&self) -> Val<Arc<str>, Ast> { Val::Exp(0, Ast(self.0.clone())) }
|
||||
}
|
||||
impl<'s> From<&'s str> for Ast {
|
||||
fn from (source: &'s str) -> Self {
|
||||
let source: Arc<str> = source.into();
|
||||
Self(CstIter(CstConstIter(source.as_ref()))
|
||||
.map(|token|Arc::new(Token {
|
||||
source: source.clone(),
|
||||
start: token.start,
|
||||
length: token.length,
|
||||
value: match token.value {
|
||||
Val::Nil => Val::Nil,
|
||||
Val::Num(u) => Val::Num(u),
|
||||
Val::Sym(s) => Val::Sym(s.into()),
|
||||
Val::Key(s) => Val::Key(s.into()),
|
||||
Val::Str(s) => Val::Str(s.into()),
|
||||
Val::Exp(d, x) => Val::Exp(d, x.into()),
|
||||
Val::Error(e) => Val::Error(e.into())
|
||||
},
|
||||
}))
|
||||
.collect::<VecDeque<_>>()
|
||||
.into())
|
||||
}
|
||||
}
|
||||
impl<'s> From<Cst<'s>> for Ast {
|
||||
fn from (cst: Cst<'s>) -> Self {
|
||||
let mut tokens: VecDeque<_> = Default::default();
|
||||
Self(tokens.into())
|
||||
}
|
||||
}
|
||||
/// The concrete syntax tree (CST) implements zero-copy
|
||||
/// parsing of the DSL from a string reference. CST items
|
||||
/// preserve info about their location in the source.
|
||||
/// CST stores strings as source references and expressions as [CstIter] instances.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq)]
|
||||
pub struct Cst<'s>(pub CstIter<'s>);
|
||||
pub struct Cst<'s>(pub CstConstIter<'s>);
|
||||
pub type CstVal<'s> = Val<&'s str, Cst<'s>>;
|
||||
pub type CstToken<'s> = Token<&'s str, Cst<'s>>;
|
||||
impl<'s> CstToken<'s> {
|
||||
pub const fn slice (&self) -> &str {
|
||||
str_range(self.source, self.start, self.end()) }
|
||||
pub const fn slice_exp (&self) -> &str {
|
||||
str_range(self.source, self.start.saturating_add(1), self.end()) }
|
||||
pub const fn grow (&mut self) -> &mut Self {
|
||||
let max_length = self.source.len().saturating_sub(self.start);
|
||||
self.length = self.length + 1;
|
||||
if self.length > max_length { self.length = max_length }
|
||||
self
|
||||
}
|
||||
pub const fn grow_exp (&'s mut self, depth: isize, source: &'s str) -> &mut Self {
|
||||
self.value = Val::Exp(depth, Cst(CstIter(CstConstIter(source))));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<'s> Dsl for Cst<'s> {
|
||||
type Str = &'s str; type Exp = Cst<'s>;
|
||||
fn dsl (&self) -> Val<Self::Str, Self::Exp> { Val::Exp(0, Cst(self.0)) }
|
||||
}
|
||||
impl<'s> From<&'s str> for Cst<'s> {
|
||||
fn from (source: &'s str) -> Self {
|
||||
Self(CstIter(CstConstIter(source)))
|
||||
Self(CstConstIter(source))
|
||||
}
|
||||
}
|
||||
/// DSL-specific error codes.
|
||||
|
|
@ -288,7 +250,7 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s>
|
|||
start = i;
|
||||
length = 1;
|
||||
if is_exp_start(c) {
|
||||
value = Exp(1, Cst(CstIter(CstConstIter(str_range(source, i, i+1)))));
|
||||
value = Exp(1, Cst(CstConstIter(str_range(source, i, i+1))));
|
||||
} else if is_str_start(c) {
|
||||
value = Str(str_range(source, i, i+1));
|
||||
} else if is_sym_start(c) {
|
||||
|
|
@ -327,14 +289,17 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s>
|
|||
}
|
||||
} else if let Exp(depth, exp) = value {
|
||||
if depth == 0 {
|
||||
value = Exp(0, Cst(CstIter(CstConstIter(str_range(source, start, start + length)))));
|
||||
value = Exp(0, Cst(CstConstIter(str_range(source, start, start + length))));
|
||||
break
|
||||
}
|
||||
length += 1;
|
||||
value = Exp(
|
||||
if c == ')' { depth-1 } else if c == '(' { depth+1 } else { depth },
|
||||
Cst(CstIter(CstConstIter(str_range(source, start, start + length))))
|
||||
);
|
||||
if c == ')' {
|
||||
value = Exp(depth-1, Cst(CstConstIter(str_range(source, start, start + length))));
|
||||
} else if c == '(' {
|
||||
value = Exp(depth+1, Cst(CstConstIter(str_range(source, start, start + length))));
|
||||
} else {
|
||||
value = Exp(depth, Cst(CstConstIter(str_range(source, start, start + length))));
|
||||
}
|
||||
} else if let Num(m) = value {
|
||||
if is_num_end(c) {
|
||||
break
|
||||
|
|
|
|||
|
|
@ -4,64 +4,24 @@ use crate::*;
|
|||
/// Each contained layer defines a mapping from input event to command invocation
|
||||
/// over a given state. Furthermore, each layer may have an associated cond,
|
||||
/// so that only certain layers are active at a given time depending on state.
|
||||
///
|
||||
/// When a key is pressed, the bindings for it are checked in sequence.
|
||||
/// When the first non-conditional or true conditional binding is executed,
|
||||
/// that binding's value is returned.
|
||||
#[derive(Debug)] pub struct EventMap<E, D: Dsl>(
|
||||
/// Map of each event (e.g. key combination) to
|
||||
#[derive(Debug)] pub struct InputMap<I, D: Dsl>(
|
||||
/// Map of input event (key combination) to
|
||||
/// all command expressions bound to it by
|
||||
/// all loaded input layers.
|
||||
pub EventBindings<E, D>
|
||||
pub BTreeMap<I, Vec<InputBinding<D>>>
|
||||
);
|
||||
type EventBindings<E, D> = BTreeMap<E, Vec<Binding<D>>>;
|
||||
/// An input binding.
|
||||
#[derive(Debug, Default)] pub struct Binding<D: Dsl> {
|
||||
impl<I, D: Dsl> Default for InputMap<I, D> {
|
||||
fn default () -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default)] pub struct InputBinding<D: Dsl> {
|
||||
condition: Option<D>,
|
||||
command: D,
|
||||
description: Option<Arc<str>>,
|
||||
source: Option<Arc<PathBuf>>,
|
||||
}
|
||||
impl<E, D: Dsl> Default for EventMap<E, D> {
|
||||
fn default () -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
impl<E: Ord + Debug + From<Arc<str>> + Clone> EventMap<E, Ast> {
|
||||
/// Create input layer collection from DSL.
|
||||
pub fn new (dsl: Ast) -> Usually<Self> {
|
||||
use Val::*;
|
||||
let mut input_map: EventBindings<E, Ast> = Default::default();
|
||||
match dsl.exp_head() {
|
||||
Str(path) => {
|
||||
let path = PathBuf::from(path.as_ref() as &str);
|
||||
for (key, val) in Self::from_path(&path)?.0.into_iter() {
|
||||
todo!("import {path:?} {key:?} {val:?}");
|
||||
let key: E = key.into();
|
||||
if !input_map.contains_key(&key) {
|
||||
input_map.insert(key, vec![]);
|
||||
}
|
||||
}
|
||||
},
|
||||
Sym(s) => {
|
||||
let key: Arc<str> = s.as_ref().into();
|
||||
let key: E = key.into();
|
||||
if !input_map.contains_key(&key) {
|
||||
input_map.insert(key.clone(), vec![]);
|
||||
}
|
||||
todo!("binding {s:?} {:?}", dsl.exp_tail());
|
||||
},
|
||||
Key(k) if k.as_ref() == "if" => {
|
||||
todo!("conditional binding {:?}", dsl.exp_tail());
|
||||
},
|
||||
_ => return Err(format!("invalid form in keymap: {dsl:?}").into())
|
||||
}
|
||||
Ok(Self(input_map))
|
||||
}
|
||||
/// Create input layer collection from string.
|
||||
pub fn from_source (source: &str) -> Usually<Self> {
|
||||
Self::new(Ast::from(source))
|
||||
}
|
||||
impl<'s, I: Debug + Ord, D: Dsl + From<Cst<'s>>> InputMap<I, D> {
|
||||
/// Create input layer collection from path to text file.
|
||||
pub fn from_path <P: Debug + AsRef<Path>> (path: P) -> Usually<Self> {
|
||||
if !exists(path.as_ref())? {
|
||||
|
|
@ -69,9 +29,41 @@ impl<E: Ord + Debug + From<Arc<str>> + Clone> EventMap<E, Ast> {
|
|||
}
|
||||
Self::from_source(read_and_leak(path)?)
|
||||
}
|
||||
/// Create input layer collection from string.
|
||||
pub fn from_source (source: &'s str) -> Usually<Self> {
|
||||
Self::from_dsl(D::from(Cst(CstConstIter(source))))
|
||||
}
|
||||
/// Create input layer collection from DSL.
|
||||
pub fn from_dsl (dsl: D) -> Usually<Self> {
|
||||
use Val::*;
|
||||
let mut input_map: BTreeMap<I, Vec<InputBinding<D>>> = Default::default();
|
||||
match dsl.exp_head() {
|
||||
Str(path) => {
|
||||
let path = PathBuf::from(path.as_ref());
|
||||
for (key, val) in InputMap::<I, D>::from_path(&path)?.0.into_iter() {
|
||||
todo!("import {path:?} {key:?} {val:?}");
|
||||
if !input_map.contains_key(&key) {
|
||||
input_map.insert(key, vec![]);
|
||||
}
|
||||
}
|
||||
},
|
||||
Sym(sym) => {
|
||||
//let key: I = sym.into();
|
||||
//if !input_map.contains_key(&key) {
|
||||
//input_map.insert(key, vec![]);
|
||||
//}
|
||||
todo!("binding {sym:?} {:?}", dsl.exp_tail());
|
||||
},
|
||||
Key(s) if s.as_ref() == "if" => {
|
||||
todo!("conditional binding {:?}", dsl.exp_tail());
|
||||
},
|
||||
_ => return Err(format!("invalid form in keymap: {dsl:?}").into())
|
||||
}
|
||||
Ok(Self(input_map))
|
||||
}
|
||||
/// Evaluate the active layers for a given state,
|
||||
/// returning the command to be executed, if any.
|
||||
pub fn handle <S, O> (&self, state: &mut S, event: Ast) -> Perhaps<O> where
|
||||
pub fn handle <S, O> (&self, state: &mut S, input: I) -> Perhaps<O> where
|
||||
S: DslInto<bool> + DslInto<O>,
|
||||
O: Command<S>
|
||||
{
|
||||
|
|
@ -84,7 +76,7 @@ impl<E: Ord + Debug + From<Arc<str>> + Clone> EventMap<E, Ast> {
|
|||
//}
|
||||
//if matches
|
||||
//&& let Some(exp) = bind.val().exp_head()
|
||||
//&& input.dsl_into(exp, ||format!("EventMap: input.eval(binding) failed").into())?
|
||||
//&& input.dsl_into(exp, ||format!("InputMap: input.eval(binding) failed").into())?
|
||||
//&& let Some(command) = state.try_dsl_into(exp)? {
|
||||
//return Ok(Some(command))
|
||||
//}
|
||||
|
|
@ -120,7 +112,7 @@ impl<E: Ord + Debug + From<Arc<str>> + Clone> EventMap<E, Ast> {
|
|||
|
||||
|
||||
//let mut keys = iter.unwrap();
|
||||
//let mut map = EventMap::default();
|
||||
//let mut map = InputMap::default();
|
||||
//while let Some(token) = keys.next() {
|
||||
//if let Value::Exp(_, mut exp) = token.value {
|
||||
//let next = exp.next();
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
#![feature(impl_trait_in_assoc_type)]
|
||||
|
||||
pub(crate) use std::marker::PhantomData;
|
||||
pub(crate) use std::fmt::{Debug, Display};
|
||||
pub(crate) use std::ops::{Add, Sub, Mul, Div};
|
||||
pub(crate) use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}};
|
||||
pub(crate) use tengri_core::*;
|
||||
#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,21 +48,32 @@
|
|||
|
||||
use crate::*;
|
||||
use Direction::*;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// Stack things on top of each other,
|
||||
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) =>
|
||||
{{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }});
|
||||
mod map; pub use self::map::*;
|
||||
mod memo; pub use self::memo::*;
|
||||
mod stack; pub use self::stack::*;
|
||||
mod thunk; pub use self::thunk::*;
|
||||
|
||||
/// Renders multiple things on top of each other,
|
||||
#[macro_export] macro_rules! lay {
|
||||
($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}
|
||||
}
|
||||
|
||||
/// Stack southward.
|
||||
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) =>
|
||||
{{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }});
|
||||
#[macro_export] macro_rules! col {
|
||||
($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }};
|
||||
}
|
||||
|
||||
/// Stack northward.
|
||||
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) =>
|
||||
{{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }});
|
||||
#[macro_export] macro_rules! col_up {
|
||||
($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }}
|
||||
}
|
||||
|
||||
/// Stack eastward.
|
||||
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) =>
|
||||
{{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }});
|
||||
#[macro_export] macro_rules! row {
|
||||
($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }};
|
||||
}
|
||||
|
||||
/// Show an item only when a condition is true.
|
||||
pub struct When<A>(pub bool, pub A);
|
||||
impl<A> When<A> {
|
||||
|
|
@ -675,353 +686,3 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{
|
|||
//_ => None
|
||||
//})
|
||||
}
|
||||
|
||||
/// Lazily-evaluated [Render]able.
|
||||
pub struct Thunk<E: Output, T: Render<E>, F: Fn()->T>(
|
||||
PhantomData<E>,
|
||||
F
|
||||
);
|
||||
impl<E: Output, T: Render<E>, F: Fn()->T> Thunk<E, T, F> {
|
||||
pub const fn new (thunk: F) -> Self {
|
||||
Self(PhantomData, thunk)
|
||||
}
|
||||
}
|
||||
impl<E: Output, T: Render<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
|
||||
fn content (&self) -> impl Render<E> { (self.1)() }
|
||||
}
|
||||
|
||||
pub struct ThunkBox<E: Output>(
|
||||
PhantomData<E>,
|
||||
Box<dyn Fn()->Box<dyn Render<E>>>,
|
||||
);
|
||||
impl<E: Output> ThunkBox<E> {
|
||||
pub const fn new (thunk: Box<dyn Fn()->Box<dyn Render<E>>>) -> Self {
|
||||
Self(PhantomData, thunk)
|
||||
}
|
||||
}
|
||||
impl<E: Output> Content<E> for ThunkBox<E> {
|
||||
fn content (&self) -> impl Render<E> { (&self.1)() }
|
||||
}
|
||||
impl<E: Output> From<Box<dyn Fn()->Box<dyn Render<E>>>> for ThunkBox<E> {
|
||||
fn from (f: Box<dyn Fn()->Box<dyn Render<E>>>) -> Self {
|
||||
Self(PhantomData, f)
|
||||
}
|
||||
}
|
||||
//impl<'a, E: Output, F: Fn()->Box<dyn Render<E> + 'a> + 'a> From<F> for ThunkBox<'a, E> {
|
||||
//fn from (f: F) -> Self {
|
||||
//Self(Default::default(), Box::new(f))
|
||||
//}
|
||||
//}
|
||||
|
||||
pub struct ThunkRender<E: Output, F: Fn(&mut E)>(PhantomData<E>, F);
|
||||
impl<E: Output, F: Fn(&mut E)> ThunkRender<E, F> {
|
||||
pub fn new (render: F) -> Self { Self(PhantomData, render) }
|
||||
}
|
||||
impl<E: Output, F: Fn(&mut E)> Content<E> for ThunkRender<E, F> {
|
||||
fn render (&self, to: &mut E) { (self.1)(to) }
|
||||
}
|
||||
|
||||
pub struct ThunkLayout<
|
||||
E: Output,
|
||||
F1: Fn(E::Area)->E::Area,
|
||||
F2: Fn(&mut E)
|
||||
>(
|
||||
PhantomData<E>,
|
||||
F1,
|
||||
F2
|
||||
);
|
||||
impl<E: Output, F1: Fn(E::Area)->E::Area, F2: Fn(&mut E)> ThunkLayout<E, F1, F2> {
|
||||
pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) }
|
||||
}
|
||||
impl<E, F1, F2> Content<E> for ThunkLayout<E, F1, F2>
|
||||
where
|
||||
E: Output,
|
||||
F1: Fn(E::Area)->E::Area,
|
||||
F2: Fn(&mut E)
|
||||
{
|
||||
fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) }
|
||||
fn render (&self, to: &mut E) { (self.2)(to) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||
pub value: T,
|
||||
pub view: Arc<RwLock<U>>
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> Memo<T, U> {
|
||||
pub fn new (value: T, view: U) -> Self {
|
||||
Self { value, view: Arc::new(view.into()) }
|
||||
}
|
||||
pub fn update <R> (
|
||||
&mut self,
|
||||
newval: T,
|
||||
render: impl Fn(&mut U, &T, &T)->R
|
||||
) -> Option<R> {
|
||||
if newval != self.value {
|
||||
let result = render(&mut*self.view.write().unwrap(), &newval, &self.value);
|
||||
self.value = newval;
|
||||
return Some(result);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear a pre-allocated buffer, then write into it.
|
||||
#[macro_export] macro_rules! rewrite {
|
||||
($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } }
|
||||
}
|
||||
|
||||
pub struct Stack<E, F> {
|
||||
__: PhantomData<E>,
|
||||
direction: Direction,
|
||||
callback: F
|
||||
}
|
||||
impl<E, F> Stack<E, F> {
|
||||
pub fn new (direction: Direction, callback: F) -> Self {
|
||||
Self { direction, callback, __: Default::default(), }
|
||||
}
|
||||
pub fn north (callback: F) -> Self {
|
||||
Self::new(North, callback)
|
||||
}
|
||||
pub fn south (callback: F) -> Self {
|
||||
Self::new(South, callback)
|
||||
}
|
||||
pub fn east (callback: F) -> Self {
|
||||
Self::new(East, callback)
|
||||
}
|
||||
pub fn west (callback: F) -> Self {
|
||||
Self::new(West, callback)
|
||||
}
|
||||
}
|
||||
impl<E: Output, F: Fn(&mut dyn FnMut(&dyn Render<E>)->())->()> Content<E> for Stack<E, F> {
|
||||
fn layout (&self, to: E::Area) -> E::Area {
|
||||
let mut x = to.x();
|
||||
let mut y = to.y();
|
||||
let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w());
|
||||
let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h());
|
||||
(self.callback)(&mut move |component: &dyn Render<E>|{
|
||||
let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh();
|
||||
match self.direction {
|
||||
South => {
|
||||
y = y.plus(h);
|
||||
h_used = h_used.plus(h);
|
||||
h_remaining = h_remaining.minus(h);
|
||||
w_used = w_used.max(w);
|
||||
},
|
||||
East => {
|
||||
x = x.plus(w);
|
||||
w_used = w_used.plus(w);
|
||||
w_remaining = w_remaining.minus(w);
|
||||
h_used = h_used.max(h);
|
||||
},
|
||||
North | West => {
|
||||
todo!()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
match self.direction {
|
||||
North | West => {
|
||||
todo!()
|
||||
},
|
||||
South | East => {
|
||||
[to.x(), to.y(), w_used.into(), h_used.into()].into()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn render (&self, to: &mut E) {
|
||||
let mut x = to.x();
|
||||
let mut y = to.y();
|
||||
let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w());
|
||||
let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h());
|
||||
(self.callback)(&mut move |component: &dyn Render<E>|{
|
||||
let layout = component.layout([x, y, w_remaining, h_remaining].into());
|
||||
match self.direction {
|
||||
South => {
|
||||
y = y.plus(layout.h());
|
||||
h_remaining = h_remaining.minus(layout.h());
|
||||
h_used = h_used.plus(layout.h());
|
||||
to.place(layout, component);
|
||||
},
|
||||
East => {
|
||||
x = x.plus(layout.w());
|
||||
w_remaining = w_remaining.minus(layout.w());
|
||||
w_used = w_used.plus(layout.h());
|
||||
to.place(layout, component);
|
||||
},
|
||||
North | West => {
|
||||
todo!()
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*Stack::down(|add|{
|
||||
let mut i = 0;
|
||||
for (_, name) in self.dirs.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
for (_, name) in self.files.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
add(&format!("{}/{i}", self.index))?;
|
||||
Ok(())
|
||||
}));*/
|
||||
|
||||
/// Renders items from an iterator.
|
||||
pub struct Map<E, A, B, I, F, G>
|
||||
where
|
||||
I: Iterator<Item = A> + Send + Sync,
|
||||
F: Fn() -> I + Send + Sync,
|
||||
{
|
||||
__: PhantomData<(E, B)>,
|
||||
/// Function that returns iterator over stacked components
|
||||
get_iter: F,
|
||||
/// Function that returns each stacked component
|
||||
get_item: G,
|
||||
}
|
||||
|
||||
impl<'a, E, A, B, I, F, G> Map<E, A, B, I, F, G> where
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a,
|
||||
{
|
||||
pub const fn new (get_iter: F, get_item: G) -> Self {
|
||||
Self {
|
||||
__: PhantomData,
|
||||
get_iter,
|
||||
get_item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E, A, B, I, F> Map<E, A, Push<E::Unit, Align<Fixed<E::Unit, Fill<B>>>>, I, F, fn(A, usize)->B>
|
||||
where
|
||||
E: Output,
|
||||
B: Render<E>,
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a
|
||||
{
|
||||
pub const fn east (
|
||||
size: E::Unit,
|
||||
get_iter: F,
|
||||
get_item: impl Fn(A, usize)->B + Send + Sync
|
||||
) -> Map<
|
||||
E, A,
|
||||
Push<E::Unit, Align<Fixed<E::Unit, B>>>,
|
||||
I, F,
|
||||
impl Fn(A, usize)->Push<E::Unit, Align<Fixed<E::Unit, B>>> + Send + Sync
|
||||
> {
|
||||
Map {
|
||||
__: PhantomData,
|
||||
get_iter,
|
||||
get_item: move |item: A, index: usize|{
|
||||
// FIXME: multiply
|
||||
let mut push: E::Unit = E::Unit::from(0u16);
|
||||
for _ in 0..index {
|
||||
push = push + size;
|
||||
}
|
||||
Push::x(push, Align::w(Fixed::x(size, get_item(item, index))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn south (
|
||||
size: E::Unit,
|
||||
get_iter: F,
|
||||
get_item: impl Fn(A, usize)->B + Send + Sync
|
||||
) -> Map<
|
||||
E, A,
|
||||
Push<E::Unit, Align<Fixed<E::Unit, B>>>,
|
||||
I, F,
|
||||
impl Fn(A, usize)->Push<E::Unit, Align<Fixed<E::Unit, B>>> + Send + Sync
|
||||
> where
|
||||
E: Output,
|
||||
B: Render<E>,
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a
|
||||
{
|
||||
Map {
|
||||
__: PhantomData,
|
||||
get_iter,
|
||||
get_item: move |item: A, index: usize|{
|
||||
// FIXME: multiply
|
||||
let mut push: E::Unit = E::Unit::from(0u16);
|
||||
for _ in 0..index {
|
||||
push = push + size;
|
||||
}
|
||||
Push::y(push, Align::n(Fixed::y(size, get_item(item, index))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E, A, B, I, F, G> Content<E> for Map<E, A, B, I, F, G> where
|
||||
E: Output,
|
||||
B: Render<E>,
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a,
|
||||
G: Fn(A, usize)->B + Send + Sync
|
||||
{
|
||||
fn layout (&self, area: E::Area) -> E::Area {
|
||||
let Self { get_iter, get_item, .. } = self;
|
||||
let mut index = 0;
|
||||
let [mut min_x, mut min_y] = area.center();
|
||||
let [mut max_x, mut max_y] = area.center();
|
||||
for item in get_iter() {
|
||||
let [x,y,w,h] = get_item(item, index).layout(area).xywh();
|
||||
min_x = min_x.min(x.into());
|
||||
min_y = min_y.min(y.into());
|
||||
max_x = max_x.max((x + w).into());
|
||||
max_y = max_y.max((y + h).into());
|
||||
index += 1;
|
||||
}
|
||||
let w = max_x - min_x;
|
||||
let h = max_y - min_y;
|
||||
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
||||
area.center_xy([w.into(), h.into()].into()).into()
|
||||
}
|
||||
fn render (&self, to: &mut E) {
|
||||
let Self { get_iter, get_item, .. } = self;
|
||||
let mut index = 0;
|
||||
let area = Content::layout(self, to.area());
|
||||
for item in get_iter() {
|
||||
let item = get_item(item, index);
|
||||
//to.place(area.into(), &item);
|
||||
to.place(item.layout(area), &item);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline] pub fn map_south<O: Output>(
|
||||
item_offset: O::Unit,
|
||||
item_height: O::Unit,
|
||||
item: impl Content<O>
|
||||
) -> impl Content<O> {
|
||||
Push::y(item_offset, Fixed::y(item_height, Fill::x(item)))
|
||||
}
|
||||
|
||||
#[inline] pub fn map_south_west<O: Output>(
|
||||
item_offset: O::Unit,
|
||||
item_height: O::Unit,
|
||||
item: impl Content<O>
|
||||
) -> impl Content<O> {
|
||||
Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item))))
|
||||
}
|
||||
|
||||
#[inline] pub fn map_east<O: Output>(
|
||||
item_offset: O::Unit,
|
||||
item_width: O::Unit,
|
||||
item: impl Content<O>
|
||||
) -> impl Content<O> {
|
||||
Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item))))
|
||||
}
|
||||
|
|
|
|||
23
output/src/ops/_collect.rs
Normal file
23
output/src/ops/_collect.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//! Groupings of elements.
|
||||
use crate::*;
|
||||
|
||||
/// A function or closure that emits renderables.
|
||||
pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
||||
|
||||
/// Any function or closure that emits renderables for the given engine matches [CollectCallback].
|
||||
impl<E, F> Collector<E> for F
|
||||
where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
||||
|
||||
pub trait Render<E: Engine> {
|
||||
fn area (&self, to: E::Area) -> E::Area;
|
||||
fn render (&self, to: &mut E::Output);
|
||||
}
|
||||
|
||||
impl<E: Engine, C: Content<E>> Render<E> for C {
|
||||
fn area (&self, to: E::Area) -> E::Area {
|
||||
Content::area(self, to)
|
||||
}
|
||||
fn render (&self, to: &mut E::Output) {
|
||||
Content::render(self, to)
|
||||
}
|
||||
}
|
||||
93
output/src/ops/_reduce.rs
Normal file
93
output/src/ops/_reduce.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct Reduce<A, B, I, F, G>(pub PhantomData<A>, pub F, pub G) where
|
||||
A: Send + Sync, B: Send + Sync,
|
||||
I: Iterator<Item = B> + Send + Sync,
|
||||
F: Fn() -> I + Send + Sync,
|
||||
G: Fn(A, B, usize)->A + Send + Sync;
|
||||
|
||||
impl<A, B, I, F, G> Reduce<A, B, I, F, G> where
|
||||
A: Send + Sync, B: Send + Sync,
|
||||
I: Iterator<Item = B> + Send + Sync,
|
||||
F: Fn() -> I + Send + Sync,
|
||||
G: Fn(A, B, usize)->A + Send + Sync
|
||||
{
|
||||
pub const fn new (f: F, g: G) -> Self { Self(Default::default(), f, g) }
|
||||
}
|
||||
|
||||
impl<E: Output, A, B, I, F, G> Content<E> for Reduce<A, B, I, F, G> where
|
||||
A: Send + Sync, B: Send + Sync,
|
||||
I: Iterator<Item = B> + Send + Sync,
|
||||
F: Fn() -> I + Send + Sync,
|
||||
G: Fn(A, B, usize)->A + Send + Sync
|
||||
{
|
||||
fn content (&self) -> impl Render<E> {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||
//E: Output,
|
||||
//I: Iterator<Item = T> + Send + Sync,
|
||||
//R: Render<E>,
|
||||
//F: Fn(R, T, usize) -> R + Send + Sync
|
||||
//{
|
||||
//Reduce(Default::default(), iterator, callback)
|
||||
//}
|
||||
pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F) where
|
||||
E: Output,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Render<E>,
|
||||
F: Fn(R, T, usize) -> R + Send + Sync;
|
||||
impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
||||
E: Output,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Render<E>,
|
||||
F: Fn(R, T, usize) -> R + Send + Sync
|
||||
{
|
||||
fn render (&self, to: &mut E) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//macro_rules! define_ops {
|
||||
//($Trait:ident<$E:ident:$Output:path> { $(
|
||||
//$(#[$attr:meta $($attr_args:tt)*])*
|
||||
//(
|
||||
//$fn:ident
|
||||
//$(<$($G:ident$(:$Gen:path)?, )+>)?
|
||||
//$Op:ident
|
||||
//($($arg:ident:$Arg:ty),*)
|
||||
//)
|
||||
//)* }) => {
|
||||
//impl<$E: $Output> $Trait<E> for E {}
|
||||
//pub trait $Trait<$E: $Output> {
|
||||
//$(
|
||||
//$(#[$attr $($attr_args)*])*
|
||||
//fn $fn $(<$($G),+>)?
|
||||
//($($arg:$Arg),*)-> $Op<$($(, $G)+)?>
|
||||
//$(where $($G: $($Gen + Send + Sync)?),+)?
|
||||
//{ $Op($($arg),*) }
|
||||
//)*
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
//define_ops! {
|
||||
//Layout<E: Output> {
|
||||
//(when <A: Render<E>,>
|
||||
//When(cond: bool, item: A))
|
||||
///// When `cond` is `true`, render `a`, otherwise render `b`.
|
||||
//(either <A: Render<E>, B: Render<E>,>
|
||||
//Either(cond: bool, a: A, b: B))
|
||||
///// If `opt` is `Some(T)` renders `cb(t)`, otherwise nothing.
|
||||
//(opt <A, F: Fn(A) -> B, B: Render<E>,>
|
||||
//Opt(option: Option<A>, cb: F))
|
||||
///// Maps items of iterator through callback.
|
||||
//(map <A, B: Render<E>, I: Iterator<Item = A>, F: Fn() -> I, G: Fn(A, usize)->B,>
|
||||
//Map(get_iterator: F, callback: G))
|
||||
//}
|
||||
//}
|
||||
|
||||
150
output/src/ops/map.rs
Normal file
150
output/src/ops/map.rs
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
use crate::*;
|
||||
|
||||
/// Renders items from an iterator.
|
||||
pub struct Map<E, A, B, I, F, G>
|
||||
where
|
||||
I: Iterator<Item = A> + Send + Sync,
|
||||
F: Fn() -> I + Send + Sync,
|
||||
{
|
||||
__: PhantomData<(E, B)>,
|
||||
/// Function that returns iterator over stacked components
|
||||
get_iter: F,
|
||||
/// Function that returns each stacked component
|
||||
get_item: G,
|
||||
}
|
||||
|
||||
impl<'a, E, A, B, I, F, G> Map<E, A, B, I, F, G> where
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a,
|
||||
{
|
||||
pub const fn new (get_iter: F, get_item: G) -> Self {
|
||||
Self {
|
||||
__: PhantomData,
|
||||
get_iter,
|
||||
get_item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E, A, B, I, F> Map<E, A, Push<E::Unit, Align<Fixed<E::Unit, Fill<B>>>>, I, F, fn(A, usize)->B>
|
||||
where
|
||||
E: Output,
|
||||
B: Render<E>,
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a
|
||||
{
|
||||
pub const fn east (
|
||||
size: E::Unit,
|
||||
get_iter: F,
|
||||
get_item: impl Fn(A, usize)->B + Send + Sync
|
||||
) -> Map<
|
||||
E, A,
|
||||
Push<E::Unit, Align<Fixed<E::Unit, B>>>,
|
||||
I, F,
|
||||
impl Fn(A, usize)->Push<E::Unit, Align<Fixed<E::Unit, B>>> + Send + Sync
|
||||
> {
|
||||
Map {
|
||||
__: PhantomData,
|
||||
get_iter,
|
||||
get_item: move |item: A, index: usize|{
|
||||
// FIXME: multiply
|
||||
let mut push: E::Unit = E::Unit::from(0u16);
|
||||
for _ in 0..index {
|
||||
push = push + size;
|
||||
}
|
||||
Push::x(push, Align::w(Fixed::x(size, get_item(item, index))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn south (
|
||||
size: E::Unit,
|
||||
get_iter: F,
|
||||
get_item: impl Fn(A, usize)->B + Send + Sync
|
||||
) -> Map<
|
||||
E, A,
|
||||
Push<E::Unit, Align<Fixed<E::Unit, B>>>,
|
||||
I, F,
|
||||
impl Fn(A, usize)->Push<E::Unit, Align<Fixed<E::Unit, B>>> + Send + Sync
|
||||
> where
|
||||
E: Output,
|
||||
B: Render<E>,
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a
|
||||
{
|
||||
Map {
|
||||
__: PhantomData,
|
||||
get_iter,
|
||||
get_item: move |item: A, index: usize|{
|
||||
// FIXME: multiply
|
||||
let mut push: E::Unit = E::Unit::from(0u16);
|
||||
for _ in 0..index {
|
||||
push = push + size;
|
||||
}
|
||||
Push::y(push, Align::n(Fixed::y(size, get_item(item, index))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E, A, B, I, F, G> Content<E> for Map<E, A, B, I, F, G> where
|
||||
E: Output,
|
||||
B: Render<E>,
|
||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||
F: Fn() -> I + Send + Sync + 'a,
|
||||
G: Fn(A, usize)->B + Send + Sync
|
||||
{
|
||||
fn layout (&self, area: E::Area) -> E::Area {
|
||||
let Self { get_iter, get_item, .. } = self;
|
||||
let mut index = 0;
|
||||
let [mut min_x, mut min_y] = area.center();
|
||||
let [mut max_x, mut max_y] = area.center();
|
||||
for item in get_iter() {
|
||||
let [x,y,w,h] = get_item(item, index).layout(area).xywh();
|
||||
min_x = min_x.min(x.into());
|
||||
min_y = min_y.min(y.into());
|
||||
max_x = max_x.max((x + w).into());
|
||||
max_y = max_y.max((y + h).into());
|
||||
index += 1;
|
||||
}
|
||||
let w = max_x - min_x;
|
||||
let h = max_y - min_y;
|
||||
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
||||
area.center_xy([w.into(), h.into()].into()).into()
|
||||
}
|
||||
fn render (&self, to: &mut E) {
|
||||
let Self { get_iter, get_item, .. } = self;
|
||||
let mut index = 0;
|
||||
let area = Content::layout(self, to.area());
|
||||
for item in get_iter() {
|
||||
let item = get_item(item, index);
|
||||
//to.place(area.into(), &item);
|
||||
to.place(item.layout(area), &item);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline] pub fn map_south<O: Output>(
|
||||
item_offset: O::Unit,
|
||||
item_height: O::Unit,
|
||||
item: impl Content<O>
|
||||
) -> impl Content<O> {
|
||||
Push::y(item_offset, Fixed::y(item_height, Fill::x(item)))
|
||||
}
|
||||
|
||||
#[inline] pub fn map_south_west<O: Output>(
|
||||
item_offset: O::Unit,
|
||||
item_height: O::Unit,
|
||||
item: impl Content<O>
|
||||
) -> impl Content<O> {
|
||||
Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item))))
|
||||
}
|
||||
|
||||
#[inline] pub fn map_east<O: Output>(
|
||||
item_offset: O::Unit,
|
||||
item_width: O::Unit,
|
||||
item: impl Content<O>
|
||||
) -> impl Content<O> {
|
||||
Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item))))
|
||||
}
|
||||
30
output/src/ops/memo.rs
Normal file
30
output/src/ops/memo.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//use crate::*;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||
pub value: T,
|
||||
pub view: Arc<RwLock<U>>
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> Memo<T, U> {
|
||||
pub fn new (value: T, view: U) -> Self {
|
||||
Self { value, view: Arc::new(view.into()) }
|
||||
}
|
||||
pub fn update <R> (
|
||||
&mut self,
|
||||
newval: T,
|
||||
render: impl Fn(&mut U, &T, &T)->R
|
||||
) -> Option<R> {
|
||||
if newval != self.value {
|
||||
let result = render(&mut*self.view.write().unwrap(), &newval, &self.value);
|
||||
self.value = newval;
|
||||
return Some(result);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear a pre-allocated buffer, then write into it.
|
||||
#[macro_export] macro_rules! rewrite {
|
||||
($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } }
|
||||
}
|
||||
109
output/src/ops/stack.rs
Normal file
109
output/src/ops/stack.rs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
use crate::*;
|
||||
use Direction::*;
|
||||
|
||||
pub struct Stack<E, F> {
|
||||
__: PhantomData<E>,
|
||||
direction: Direction,
|
||||
callback: F
|
||||
}
|
||||
impl<E, F> Stack<E, F> {
|
||||
pub fn new (direction: Direction, callback: F) -> Self {
|
||||
Self { direction, callback, __: Default::default(), }
|
||||
}
|
||||
pub fn north (callback: F) -> Self {
|
||||
Self::new(North, callback)
|
||||
}
|
||||
pub fn south (callback: F) -> Self {
|
||||
Self::new(South, callback)
|
||||
}
|
||||
pub fn east (callback: F) -> Self {
|
||||
Self::new(East, callback)
|
||||
}
|
||||
pub fn west (callback: F) -> Self {
|
||||
Self::new(West, callback)
|
||||
}
|
||||
}
|
||||
impl<E: Output, F: Fn(&mut dyn FnMut(&dyn Render<E>)->())->()> Content<E> for Stack<E, F> {
|
||||
fn layout (&self, to: E::Area) -> E::Area {
|
||||
let mut x = to.x();
|
||||
let mut y = to.y();
|
||||
let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w());
|
||||
let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h());
|
||||
(self.callback)(&mut move |component: &dyn Render<E>|{
|
||||
let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh();
|
||||
match self.direction {
|
||||
South => {
|
||||
y = y.plus(h);
|
||||
h_used = h_used.plus(h);
|
||||
h_remaining = h_remaining.minus(h);
|
||||
w_used = w_used.max(w);
|
||||
},
|
||||
East => {
|
||||
x = x.plus(w);
|
||||
w_used = w_used.plus(w);
|
||||
w_remaining = w_remaining.minus(w);
|
||||
h_used = h_used.max(h);
|
||||
},
|
||||
North | West => {
|
||||
todo!()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
match self.direction {
|
||||
North | West => {
|
||||
todo!()
|
||||
},
|
||||
South | East => {
|
||||
[to.x(), to.y(), w_used.into(), h_used.into()].into()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn render (&self, to: &mut E) {
|
||||
let mut x = to.x();
|
||||
let mut y = to.y();
|
||||
let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w());
|
||||
let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h());
|
||||
(self.callback)(&mut move |component: &dyn Render<E>|{
|
||||
let layout = component.layout([x, y, w_remaining, h_remaining].into());
|
||||
match self.direction {
|
||||
South => {
|
||||
y = y.plus(layout.h());
|
||||
h_remaining = h_remaining.minus(layout.h());
|
||||
h_used = h_used.plus(layout.h());
|
||||
to.place(layout, component);
|
||||
},
|
||||
East => {
|
||||
x = x.plus(layout.w());
|
||||
w_remaining = w_remaining.minus(layout.w());
|
||||
w_used = w_used.plus(layout.h());
|
||||
to.place(layout, component);
|
||||
},
|
||||
North | West => {
|
||||
todo!()
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*Stack::down(|add|{
|
||||
let mut i = 0;
|
||||
for (_, name) in self.dirs.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
for (_, name) in self.files.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
add(&format!("{}/{i}", self.index))?;
|
||||
Ok(())
|
||||
}));*/
|
||||
|
||||
69
output/src/ops/thunk.rs
Normal file
69
output/src/ops/thunk.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use crate::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Lazily-evaluated [Render]able.
|
||||
pub struct Thunk<E: Output, T: Render<E>, F: Fn()->T>(
|
||||
PhantomData<E>,
|
||||
F
|
||||
);
|
||||
impl<E: Output, T: Render<E>, F: Fn()->T> Thunk<E, T, F> {
|
||||
pub const fn new (thunk: F) -> Self {
|
||||
Self(PhantomData, thunk)
|
||||
}
|
||||
}
|
||||
impl<E: Output, T: Render<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
|
||||
fn content (&self) -> impl Render<E> { (self.1)() }
|
||||
}
|
||||
|
||||
pub struct ThunkBox<E: Output>(
|
||||
PhantomData<E>,
|
||||
Box<dyn Fn()->Box<dyn Render<E>>>,
|
||||
);
|
||||
impl<E: Output> ThunkBox<E> {
|
||||
pub const fn new (thunk: Box<dyn Fn()->Box<dyn Render<E>>>) -> Self {
|
||||
Self(PhantomData, thunk)
|
||||
}
|
||||
}
|
||||
impl<E: Output> Content<E> for ThunkBox<E> {
|
||||
fn content (&self) -> impl Render<E> { (&self.1)() }
|
||||
}
|
||||
impl<E: Output> From<Box<dyn Fn()->Box<dyn Render<E>>>> for ThunkBox<E> {
|
||||
fn from (f: Box<dyn Fn()->Box<dyn Render<E>>>) -> Self {
|
||||
Self(PhantomData, f)
|
||||
}
|
||||
}
|
||||
//impl<'a, E: Output, F: Fn()->Box<dyn Render<E> + 'a> + 'a> From<F> for ThunkBox<'a, E> {
|
||||
//fn from (f: F) -> Self {
|
||||
//Self(Default::default(), Box::new(f))
|
||||
//}
|
||||
//}
|
||||
|
||||
pub struct ThunkRender<E: Output, F: Fn(&mut E)>(PhantomData<E>, F);
|
||||
impl<E: Output, F: Fn(&mut E)> ThunkRender<E, F> {
|
||||
pub fn new (render: F) -> Self { Self(PhantomData, render) }
|
||||
}
|
||||
impl<E: Output, F: Fn(&mut E)> Content<E> for ThunkRender<E, F> {
|
||||
fn render (&self, to: &mut E) { (self.1)(to) }
|
||||
}
|
||||
|
||||
pub struct ThunkLayout<
|
||||
E: Output,
|
||||
F1: Fn(E::Area)->E::Area,
|
||||
F2: Fn(&mut E)
|
||||
>(
|
||||
PhantomData<E>,
|
||||
F1,
|
||||
F2
|
||||
);
|
||||
impl<E: Output, F1: Fn(E::Area)->E::Area, F2: Fn(&mut E)> ThunkLayout<E, F1, F2> {
|
||||
pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) }
|
||||
}
|
||||
impl<E, F1, F2> Content<E> for ThunkLayout<E, F1, F2>
|
||||
where
|
||||
E: Output,
|
||||
F1: Fn(E::Area)->E::Area,
|
||||
F2: Fn(&mut E)
|
||||
{
|
||||
fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) }
|
||||
fn render (&self, to: &mut E) { (self.2)(to) }
|
||||
}
|
||||
|
|
@ -1,274 +1,5 @@
|
|||
use crate::*;
|
||||
use Direction::*;
|
||||
|
||||
/// A cardinal direction.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(test, derive(Arbitrary))]
|
||||
pub enum Direction {
|
||||
North, South, East, West, Above, Below
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
|
||||
let [x, y, w, h] = area.xywh();
|
||||
match self {
|
||||
North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]),
|
||||
South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]),
|
||||
East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]),
|
||||
West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]),
|
||||
Above | Below => (area.xywh(), area.xywh())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A linear coordinate.
|
||||
pub trait Coordinate: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Mul<Self, Output=Self>
|
||||
+ Div<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq
|
||||
+ Debug + Display + Default
|
||||
+ From<u16> + Into<u16>
|
||||
+ Into<usize>
|
||||
+ Into<f64>
|
||||
{
|
||||
fn zero () -> Self { 0.into() }
|
||||
fn plus (self, other: Self) -> Self;
|
||||
fn minus (self, other: Self) -> Self {
|
||||
if self >= other {
|
||||
self - other
|
||||
} else {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coordinate for u16 {
|
||||
fn plus (self, other: Self) -> Self {
|
||||
self.saturating_add(other)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Area<N: Coordinate>: From<[N;4]> + Debug + Copy {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
fn w (&self) -> N;
|
||||
fn h (&self) -> N;
|
||||
fn zero () -> [N;4] {
|
||||
[N::zero(), N::zero(), N::zero(), N::zero()]
|
||||
}
|
||||
fn from_position (pos: impl Size<N>) -> [N;4] {
|
||||
let [x, y] = pos.wh();
|
||||
[x, y, 0.into(), 0.into()]
|
||||
}
|
||||
fn from_size (size: impl Size<N>) -> [N;4] {
|
||||
let [w, h] = size.wh();
|
||||
[0.into(), 0.into(), w, h]
|
||||
}
|
||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
||||
if self.w() < w || self.h() < h {
|
||||
Err(format!("min {w}x{h}").into())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
fn xy (&self) -> [N;2] {
|
||||
[self.x(), self.y()]
|
||||
}
|
||||
fn wh (&self) -> [N;2] {
|
||||
[self.w(), self.h()]
|
||||
}
|
||||
fn xywh (&self) -> [N;4] {
|
||||
[self.x(), self.y(), self.w(), self.h()]
|
||||
}
|
||||
fn clip_h (&self, h: N) -> [N;4] {
|
||||
[self.x(), self.y(), self.w(), self.h().min(h)]
|
||||
}
|
||||
fn clip_w (&self, w: N) -> [N;4] {
|
||||
[self.x(), self.y(), self.w().min(w), self.h()]
|
||||
}
|
||||
fn clip (&self, wh: impl Size<N>) -> [N;4] {
|
||||
[self.x(), self.y(), wh.w(), wh.h()]
|
||||
}
|
||||
fn set_w (&self, w: N) -> [N;4] {
|
||||
[self.x(), self.y(), w, self.h()]
|
||||
}
|
||||
fn set_h (&self, h: N) -> [N;4] {
|
||||
[self.x(), self.y(), self.w(), h]
|
||||
}
|
||||
fn x2 (&self) -> N {
|
||||
self.x().plus(self.w())
|
||||
}
|
||||
fn y2 (&self) -> N {
|
||||
self.y().plus(self.h())
|
||||
}
|
||||
fn lrtb (&self) -> [N;4] {
|
||||
[self.x(), self.x2(), self.y(), self.y2()]
|
||||
}
|
||||
fn center (&self) -> [N;2] {
|
||||
[self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())]
|
||||
}
|
||||
fn center_x (&self, n: N) -> [N;4] {
|
||||
let [x, y, w, h] = self.xywh();
|
||||
[(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()]
|
||||
}
|
||||
fn center_y (&self, n: N) -> [N;4] {
|
||||
let [x, y, w, h] = self.xywh();
|
||||
[x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n]
|
||||
}
|
||||
fn center_xy (&self, [n, m]: [N;2]) -> [N;4] {
|
||||
let [x, y, w, h] = self.xywh();
|
||||
[(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m]
|
||||
}
|
||||
fn centered (&self) -> [N;2] {
|
||||
[self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())]
|
||||
}
|
||||
fn iter_x (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
||||
self.x()..(self.x()+self.w())
|
||||
}
|
||||
fn iter_y (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
||||
self.y()..(self.y()+self.h())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Area<N> for (N, N, N, N) {
|
||||
fn x (&self) -> N { self.0 }
|
||||
fn y (&self) -> N { self.1 }
|
||||
fn w (&self) -> N { self.2 }
|
||||
fn h (&self) -> N { self.3 }
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Area<N> for [N;4] {
|
||||
fn x (&self) -> N { self[0] }
|
||||
fn y (&self) -> N { self[1] }
|
||||
fn w (&self) -> N { self[2] }
|
||||
fn h (&self) -> N { self[3] }
|
||||
}
|
||||
|
||||
pub trait Size<N: Coordinate>: From<[N;2]> + Debug + Copy {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
fn w (&self) -> N { self.x() }
|
||||
fn h (&self) -> N { self.y() }
|
||||
fn wh (&self) -> [N;2] { [self.x(), self.y()] }
|
||||
fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] }
|
||||
fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] }
|
||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
||||
if self.w() < w || self.h() < h {
|
||||
Err(format!("min {w}x{h}").into())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
fn zero () -> [N;2] {
|
||||
[N::zero(), N::zero()]
|
||||
}
|
||||
fn to_area_pos (&self) -> [N;4] {
|
||||
let [x, y] = self.wh();
|
||||
[x, y, 0.into(), 0.into()]
|
||||
}
|
||||
fn to_area_size (&self) -> [N;4] {
|
||||
let [w, h] = self.wh();
|
||||
[0.into(), 0.into(), w, h]
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Size<N> for (N, N) {
|
||||
fn x (&self) -> N { self.0 }
|
||||
fn y (&self) -> N { self.1 }
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Size<N> for [N;2] {
|
||||
fn x (&self) -> N { self[0] }
|
||||
fn y (&self) -> N { self[1] }
|
||||
}
|
||||
|
||||
pub trait HasSize<E: Output> {
|
||||
fn size (&self) -> &Measure<E>;
|
||||
fn width (&self) -> usize {
|
||||
self.size().w()
|
||||
}
|
||||
fn height (&self) -> usize {
|
||||
self.size().h()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output, T: Has<Measure<E>>> HasSize<E> for T {
|
||||
fn size (&self) -> &Measure<E> {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget that tracks its render width and height
|
||||
#[derive(Default)]
|
||||
pub struct Measure<E: Output> {
|
||||
_engine: PhantomData<E>,
|
||||
pub x: Arc<AtomicUsize>,
|
||||
pub y: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
||||
impl<E: Output> Content<E> for Measure<E> {
|
||||
fn render (&self, to: &mut E) {
|
||||
self.x.store(to.area().w().into(), Relaxed);
|
||||
self.y.store(to.area().h().into(), Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output> Clone for Measure<E> {
|
||||
fn clone (&self) -> Self {
|
||||
Self {
|
||||
_engine: Default::default(),
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output> std::fmt::Debug for Measure<E> {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("Measure")
|
||||
.field("width", &self.x)
|
||||
.field("height", &self.y)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output> Measure<E> {
|
||||
pub fn new () -> Self {
|
||||
Self {
|
||||
_engine: PhantomData::default(),
|
||||
x: Arc::new(0.into()),
|
||||
y: Arc::new(0.into()),
|
||||
}
|
||||
}
|
||||
pub fn set_w (&self, w: impl Into<usize>) -> &Self {
|
||||
self.x.store(w.into(), Relaxed);
|
||||
self
|
||||
}
|
||||
pub fn set_h (&self, h: impl Into<usize>) -> &Self {
|
||||
self.y.store(h.into(), Relaxed);
|
||||
self
|
||||
}
|
||||
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) -> &Self {
|
||||
self.set_w(w);
|
||||
self.set_h(h);
|
||||
self
|
||||
}
|
||||
pub fn w (&self) -> usize {
|
||||
self.x.load(Relaxed)
|
||||
}
|
||||
pub fn h (&self) -> usize {
|
||||
self.y.load(Relaxed)
|
||||
}
|
||||
pub fn wh (&self) -> [usize;2] {
|
||||
[self.w(), self.h()]
|
||||
}
|
||||
pub fn format (&self) -> Arc<str> {
|
||||
format!("{}x{}", self.w(), self.h()).into()
|
||||
}
|
||||
pub fn of <T: Render<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
|
||||
Bsp::b(Fill::xy(self), item)
|
||||
}
|
||||
}
|
||||
mod area; pub use self::area::*;
|
||||
mod coordinate; pub use self::coordinate::*;
|
||||
mod direction; pub use self::direction::*;
|
||||
mod measure; pub use self::measure::*;
|
||||
mod size; pub use self::size::*;
|
||||
|
|
|
|||
98
output/src/space/area.rs
Normal file
98
output/src/space/area.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
use crate::*;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Area<N: Coordinate>: From<[N;4]> + Debug + Copy {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
fn w (&self) -> N;
|
||||
fn h (&self) -> N;
|
||||
fn zero () -> [N;4] {
|
||||
[N::zero(), N::zero(), N::zero(), N::zero()]
|
||||
}
|
||||
fn from_position (pos: impl Size<N>) -> [N;4] {
|
||||
let [x, y] = pos.wh();
|
||||
[x, y, 0.into(), 0.into()]
|
||||
}
|
||||
fn from_size (size: impl Size<N>) -> [N;4] {
|
||||
let [w, h] = size.wh();
|
||||
[0.into(), 0.into(), w, h]
|
||||
}
|
||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
||||
if self.w() < w || self.h() < h {
|
||||
Err(format!("min {w}x{h}").into())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
fn xy (&self) -> [N;2] {
|
||||
[self.x(), self.y()]
|
||||
}
|
||||
fn wh (&self) -> [N;2] {
|
||||
[self.w(), self.h()]
|
||||
}
|
||||
fn xywh (&self) -> [N;4] {
|
||||
[self.x(), self.y(), self.w(), self.h()]
|
||||
}
|
||||
fn clip_h (&self, h: N) -> [N;4] {
|
||||
[self.x(), self.y(), self.w(), self.h().min(h)]
|
||||
}
|
||||
fn clip_w (&self, w: N) -> [N;4] {
|
||||
[self.x(), self.y(), self.w().min(w), self.h()]
|
||||
}
|
||||
fn clip (&self, wh: impl Size<N>) -> [N;4] {
|
||||
[self.x(), self.y(), wh.w(), wh.h()]
|
||||
}
|
||||
fn set_w (&self, w: N) -> [N;4] {
|
||||
[self.x(), self.y(), w, self.h()]
|
||||
}
|
||||
fn set_h (&self, h: N) -> [N;4] {
|
||||
[self.x(), self.y(), self.w(), h]
|
||||
}
|
||||
fn x2 (&self) -> N {
|
||||
self.x().plus(self.w())
|
||||
}
|
||||
fn y2 (&self) -> N {
|
||||
self.y().plus(self.h())
|
||||
}
|
||||
fn lrtb (&self) -> [N;4] {
|
||||
[self.x(), self.x2(), self.y(), self.y2()]
|
||||
}
|
||||
fn center (&self) -> [N;2] {
|
||||
[self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())]
|
||||
}
|
||||
fn center_x (&self, n: N) -> [N;4] {
|
||||
let [x, y, w, h] = self.xywh();
|
||||
[(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()]
|
||||
}
|
||||
fn center_y (&self, n: N) -> [N;4] {
|
||||
let [x, y, w, h] = self.xywh();
|
||||
[x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n]
|
||||
}
|
||||
fn center_xy (&self, [n, m]: [N;2]) -> [N;4] {
|
||||
let [x, y, w, h] = self.xywh();
|
||||
[(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m]
|
||||
}
|
||||
fn centered (&self) -> [N;2] {
|
||||
[self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())]
|
||||
}
|
||||
fn iter_x (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
||||
self.x()..(self.x()+self.w())
|
||||
}
|
||||
fn iter_y (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
||||
self.y()..(self.y()+self.h())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Area<N> for (N, N, N, N) {
|
||||
fn x (&self) -> N { self.0 }
|
||||
fn y (&self) -> N { self.1 }
|
||||
fn w (&self) -> N { self.2 }
|
||||
fn h (&self) -> N { self.3 }
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Area<N> for [N;4] {
|
||||
fn x (&self) -> N { self[0] }
|
||||
fn y (&self) -> N { self[1] }
|
||||
fn w (&self) -> N { self[2] }
|
||||
fn h (&self) -> N { self[3] }
|
||||
}
|
||||
31
output/src/space/coordinate.rs
Normal file
31
output/src/space/coordinate.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
use std::ops::{Add, Sub, Mul, Div};
|
||||
|
||||
/// A linear coordinate.
|
||||
pub trait Coordinate: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Mul<Self, Output=Self>
|
||||
+ Div<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq
|
||||
+ Debug + Display + Default
|
||||
+ From<u16> + Into<u16>
|
||||
+ Into<usize>
|
||||
+ Into<f64>
|
||||
{
|
||||
fn zero () -> Self { 0.into() }
|
||||
fn plus (self, other: Self) -> Self;
|
||||
fn minus (self, other: Self) -> Self {
|
||||
if self >= other {
|
||||
self - other
|
||||
} else {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coordinate for u16 {
|
||||
fn plus (self, other: Self) -> Self {
|
||||
self.saturating_add(other)
|
||||
}
|
||||
}
|
||||
22
output/src/space/direction.rs
Normal file
22
output/src/space/direction.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use crate::*;
|
||||
use crate::Direction::*;
|
||||
|
||||
/// A cardinal direction.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(test, derive(Arbitrary))]
|
||||
pub enum Direction {
|
||||
North, South, East, West, Above, Below
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
|
||||
let [x, y, w, h] = area.xywh();
|
||||
match self {
|
||||
North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]),
|
||||
South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]),
|
||||
East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]),
|
||||
West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]),
|
||||
Above | Below => (area.xywh(), area.xywh())
|
||||
}
|
||||
}
|
||||
}
|
||||
91
output/src/space/measure.rs
Normal file
91
output/src/space/measure.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use crate::*;
|
||||
use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}};
|
||||
|
||||
pub trait HasSize<E: Output> {
|
||||
fn size (&self) -> &Measure<E>;
|
||||
fn width (&self) -> usize {
|
||||
self.size().w()
|
||||
}
|
||||
fn height (&self) -> usize {
|
||||
self.size().h()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output, T: Has<Measure<E>>> HasSize<E> for T {
|
||||
fn size (&self) -> &Measure<E> {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget that tracks its render width and height
|
||||
#[derive(Default)]
|
||||
pub struct Measure<E: Output> {
|
||||
_engine: PhantomData<E>,
|
||||
pub x: Arc<AtomicUsize>,
|
||||
pub y: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
||||
impl<E: Output> Content<E> for Measure<E> {
|
||||
fn render (&self, to: &mut E) {
|
||||
self.x.store(to.area().w().into(), Relaxed);
|
||||
self.y.store(to.area().h().into(), Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output> Clone for Measure<E> {
|
||||
fn clone (&self) -> Self {
|
||||
Self {
|
||||
_engine: Default::default(),
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output> std::fmt::Debug for Measure<E> {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("Measure")
|
||||
.field("width", &self.x)
|
||||
.field("height", &self.y)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Output> Measure<E> {
|
||||
pub fn new () -> Self {
|
||||
Self {
|
||||
_engine: PhantomData::default(),
|
||||
x: Arc::new(0.into()),
|
||||
y: Arc::new(0.into()),
|
||||
}
|
||||
}
|
||||
pub fn set_w (&self, w: impl Into<usize>) -> &Self {
|
||||
self.x.store(w.into(), Relaxed);
|
||||
self
|
||||
}
|
||||
pub fn set_h (&self, h: impl Into<usize>) -> &Self {
|
||||
self.y.store(h.into(), Relaxed);
|
||||
self
|
||||
}
|
||||
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) -> &Self {
|
||||
self.set_w(w);
|
||||
self.set_h(h);
|
||||
self
|
||||
}
|
||||
pub fn w (&self) -> usize {
|
||||
self.x.load(Relaxed)
|
||||
}
|
||||
pub fn h (&self) -> usize {
|
||||
self.y.load(Relaxed)
|
||||
}
|
||||
pub fn wh (&self) -> [usize;2] {
|
||||
[self.w(), self.h()]
|
||||
}
|
||||
pub fn format (&self) -> Arc<str> {
|
||||
format!("{}x{}", self.w(), self.h()).into()
|
||||
}
|
||||
pub fn of <T: Render<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
|
||||
Bsp::b(Fill::xy(self), item)
|
||||
}
|
||||
}
|
||||
40
output/src/space/size.rs
Normal file
40
output/src/space/size.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use crate::*;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Size<N: Coordinate>: From<[N;2]> + Debug + Copy {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
fn w (&self) -> N { self.x() }
|
||||
fn h (&self) -> N { self.y() }
|
||||
fn wh (&self) -> [N;2] { [self.x(), self.y()] }
|
||||
fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] }
|
||||
fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] }
|
||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
||||
if self.w() < w || self.h() < h {
|
||||
Err(format!("min {w}x{h}").into())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
fn zero () -> [N;2] {
|
||||
[N::zero(), N::zero()]
|
||||
}
|
||||
fn to_area_pos (&self) -> [N;4] {
|
||||
let [x, y] = self.wh();
|
||||
[x, y, 0.into(), 0.into()]
|
||||
}
|
||||
fn to_area_size (&self) -> [N;4] {
|
||||
let [w, h] = self.wh();
|
||||
[0.into(), 0.into(), w, h]
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Size<N> for (N, N) {
|
||||
fn x (&self) -> N { self.0 }
|
||||
fn y (&self) -> N { self.1 }
|
||||
}
|
||||
|
||||
impl<N: Coordinate> Size<N> for [N;2] {
|
||||
fn x (&self) -> N { self[0] }
|
||||
fn y (&self) -> N { self[1] }
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ impl ToTokens for CommandDef {
|
|||
out
|
||||
};
|
||||
write_quote(quote! {
|
||||
Some(::tengri::dsl::Token { value: ::tengri::dsl::Val::Key(#key), .. }) => {
|
||||
Some(::tengri::dsl::Token { value: ::tengri::dsl::DslVal::Key(#key), .. }) => {
|
||||
let mut words = words.clone();
|
||||
Some(#command_enum::#variant)
|
||||
},
|
||||
|
|
@ -154,7 +154,7 @@ impl ToTokens for CommandDef {
|
|||
state: &#state,
|
||||
value: &impl ::tengri::dsl::Dsl
|
||||
) -> Perhaps<Self> {
|
||||
use ::tengri::dsl::Val::*;
|
||||
use ::tengri::dsl::DslVal::*;
|
||||
todo!()//Ok(match token { #(#matchers)* _ => None })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ impl ExposeImpl {
|
|||
/// Generated by [tengri_proc::expose].
|
||||
impl ::tengri::dsl::FromDsl<#state> for #t {
|
||||
fn try_from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps<Self> {
|
||||
Ok(Some(match dsl.dsl() {
|
||||
Ok(Some(match dsl.val() {
|
||||
#arms
|
||||
_ => { return Ok(None) }
|
||||
}))
|
||||
|
|
@ -93,7 +93,7 @@ impl ExposeImpl {
|
|||
let formatted_type = format!("{}", quote! { #t });
|
||||
if &formatted_type == "bool" {
|
||||
return quote! {
|
||||
::tengri::dsl::Val::Sym(s) => match s.as_ref() {
|
||||
::tengri::dsl::DslVal::Sym(s) => match s.as_ref() {
|
||||
":true" => true,
|
||||
":false" => false,
|
||||
#variants
|
||||
|
|
@ -110,16 +110,16 @@ impl ExposeImpl {
|
|||
Span::call_site()
|
||||
);
|
||||
return quote! {
|
||||
::tengri::dsl::Val::Num(n) => TryInto::<#t>::try_into(n)
|
||||
::tengri::dsl::DslVal::Num(n) => TryInto::<#t>::try_into(n)
|
||||
.unwrap_or_else(|_|panic!(#num_err)),
|
||||
::tengri::dsl::Val::Sym(s) => match s.as_ref() {
|
||||
::tengri::dsl::DslVal::Sym(s) => match s.as_ref() {
|
||||
#variants
|
||||
_ => { return Ok(None) }
|
||||
},
|
||||
}
|
||||
}
|
||||
return quote! {
|
||||
::tengri::dsl::Val::Sym(s) => match s.as_ref() {
|
||||
::tengri::dsl::DslVal::Sym(s) => match s.as_ref() {
|
||||
#variants
|
||||
_ => { return Ok(None) }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ impl ToTokens for ViewDef {
|
|||
|
||||
impl ViewDef {
|
||||
fn generated (&self) -> impl ToTokens {
|
||||
let Self(ViewMeta { output }, ViewImpl { block, .. }) = self;
|
||||
let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self;
|
||||
let self_ty = &block.self_ty;
|
||||
let builtins = self.builtins();
|
||||
let exposed = self.exposed();
|
||||
|
|
@ -74,9 +74,9 @@ impl ViewDef {
|
|||
/// Expressions are handled by built-in functions
|
||||
/// that operate over constants and symbols.
|
||||
fn builtins (&self) -> impl ToTokens {
|
||||
let Self(ViewMeta { output }, ViewImpl { .. }) = self;
|
||||
let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self;
|
||||
let builtins = builtins_with_boxes_output(quote! { #output }).map(|builtin|quote! {
|
||||
::tengri::dsl::Val::Exp(_, expr) => return Ok(Some(
|
||||
::tengri::dsl::DslVal::Exp(_, expr) => return Ok(Some(
|
||||
#builtin::from_dsl(self, expr, ||Box::new("failed to load builtin".into()))?
|
||||
.boxed()
|
||||
)),
|
||||
|
|
@ -85,11 +85,11 @@ impl ViewDef {
|
|||
}
|
||||
/// Symbols are handled by user-taked functions that take no parameters but `&self`.
|
||||
fn exposed (&self) -> impl ToTokens {
|
||||
let Self(ViewMeta { .. }, ViewImpl { exposed, .. }) = self;
|
||||
let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self;
|
||||
let exposed = exposed.iter().map(|(key, value)|write_quote(quote! {
|
||||
#key => return Ok(Some(self.#value().boxed())),
|
||||
}));
|
||||
quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* } }
|
||||
quote! { ::tengri::dsl::DslVal::Sym(key) => match key.as_ref() { #(#exposed)* } }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::time::Duration;
|
|||
|
||||
mod tui_buffer; pub use self::tui_buffer::*;
|
||||
mod tui_input; pub use self::tui_input::*;
|
||||
mod tui_keys; pub use self::tui_keys::*;
|
||||
mod tui_output; pub use self::tui_output::*;
|
||||
mod tui_perf; pub use self::tui_perf::*;
|
||||
|
||||
|
|
@ -71,7 +72,8 @@ pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + 'static> {
|
|||
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
||||
}
|
||||
|
||||
impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
||||
impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static>
|
||||
TuiRun<T> for Arc<RwLock<Tui>> {
|
||||
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
||||
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
|
||||
self.write().unwrap().setup()?;
|
||||
|
|
|
|||
|
|
@ -4,36 +4,21 @@ use std::thread::{spawn, JoinHandle};
|
|||
use crossterm::event::{poll, read};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TuiIn {
|
||||
pub struct TuiIn(
|
||||
/// Exit flag
|
||||
pub exited: Arc<AtomicBool>,
|
||||
pub Arc<AtomicBool>,
|
||||
/// Input event
|
||||
pub event: TuiEvent,
|
||||
}
|
||||
pub crossterm::event::Event,
|
||||
);
|
||||
|
||||
impl Input for TuiIn {
|
||||
type Event = TuiEvent;
|
||||
type Event = crossterm::event::Event;
|
||||
type Handled = bool;
|
||||
fn event (&self) -> &TuiEvent { &self.event }
|
||||
fn is_done (&self) -> bool { self.exited.fetch_and(true, Relaxed) }
|
||||
fn done (&self) { self.exited.store(true, Relaxed); }
|
||||
}
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiEvent(crossterm::event::Event);
|
||||
impl Ord for TuiEvent {
|
||||
fn cmp (&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other) .unwrap_or_else(||format!("{:?}", self).cmp(&format!("{other:?}"))) // FIXME perf
|
||||
}
|
||||
}
|
||||
impl From<crossterm::event::Event> for TuiEvent {
|
||||
fn from (event: crossterm::event::Event) -> Self {
|
||||
Self(event)
|
||||
}
|
||||
}
|
||||
impl From<Arc<str>> for TuiEvent {
|
||||
fn from (x: Arc<str>) -> Self {
|
||||
TuiEvent(TuiKey::new(x.as_ref()).build().unwrap_or_else(||panic!("invalid key: {x}")))
|
||||
}
|
||||
fn event (&self) -> &crossterm::event::Event { &self.1 }
|
||||
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
||||
fn done (&self) { self.0.store(true, Relaxed); }
|
||||
}
|
||||
|
||||
impl TuiIn {
|
||||
/// Spawn the input thread.
|
||||
pub fn run_input <T: Handle<TuiIn> + Send + Sync + 'static> (
|
||||
|
|
@ -50,6 +35,7 @@ impl TuiIn {
|
|||
if poll(timer).is_ok() {
|
||||
let event = read().unwrap();
|
||||
match event {
|
||||
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
|
|
@ -60,8 +46,7 @@ impl TuiIn {
|
|||
},
|
||||
_ => {
|
||||
let exited = exited.clone();
|
||||
let event = event.into();
|
||||
if let Err(e) = state.write().unwrap().handle(&TuiIn { exited, event }) {
|
||||
if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) {
|
||||
panic!("{e}")
|
||||
}
|
||||
}
|
||||
|
|
@ -74,101 +59,10 @@ impl TuiIn {
|
|||
//#[cfg(feature = "dsl")]
|
||||
//impl DslInput for TuiIn {
|
||||
//fn matches_dsl (&self, token: &str) -> bool {
|
||||
//if let Some(event) = TuiKey::new(token).build() {
|
||||
//if let Some(event) = KeyMatcher::new(token).build() {
|
||||
//&event == self.event()
|
||||
//} else {
|
||||
//false
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
pub struct TuiKey {
|
||||
valid: bool,
|
||||
key: Option<KeyCode>,
|
||||
mods: KeyModifiers,
|
||||
}
|
||||
|
||||
impl TuiKey {
|
||||
pub fn new (token: impl AsRef<str>) -> Self {
|
||||
let token = token.as_ref();
|
||||
if token.len() < 2 {
|
||||
Self { valid: false, key: None, mods: KeyModifiers::NONE }
|
||||
} else if token.chars().next() != Some('@') {
|
||||
Self { valid: false, key: None, mods: KeyModifiers::NONE }
|
||||
} else {
|
||||
Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..])
|
||||
}
|
||||
}
|
||||
pub fn build (self) -> Option<Event> {
|
||||
if self.valid && self.key.is_some() {
|
||||
Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn next (mut self, token: &str) -> Self {
|
||||
let mut tokens = token.split('-').peekable();
|
||||
while let Some(token) = tokens.next() {
|
||||
if tokens.peek().is_some() {
|
||||
match token {
|
||||
"ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL,
|
||||
"alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT,
|
||||
"shift" | "Shift" | "s" | "S" => {
|
||||
self.mods |= KeyModifiers::SHIFT;
|
||||
// + TODO normalize character case, BackTab, etc.
|
||||
},
|
||||
_ => panic!("unknown modifier {token}"),
|
||||
}
|
||||
} else {
|
||||
self.key = if token.len() == 1 {
|
||||
Some(KeyCode::Char(token.chars().next().unwrap()))
|
||||
} else {
|
||||
Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
fn named_key (token: &str) -> Option<KeyCode> {
|
||||
use KeyCode::*;
|
||||
Some(match token {
|
||||
"up" => Up,
|
||||
"down" => Down,
|
||||
"left" => Left,
|
||||
"right" => Right,
|
||||
"esc" | "escape" => Esc,
|
||||
"enter" | "return" => Enter,
|
||||
"delete" | "del" => Delete,
|
||||
"tab" => Tab,
|
||||
"space" => Char(' '),
|
||||
"comma" => Char(','),
|
||||
"period" => Char('.'),
|
||||
"plus" => Char('+'),
|
||||
"minus" | "dash" => Char('-'),
|
||||
"equal" | "equals" => Char('='),
|
||||
"underscore" => Char('_'),
|
||||
"backtick" => Char('`'),
|
||||
"lt" => Char('<'),
|
||||
"gt" => Char('>'),
|
||||
"cbopen" | "openbrace" => Char('{'),
|
||||
"cbclose" | "closebrace" => Char('}'),
|
||||
"bropen" | "openbracket" => Char('['),
|
||||
"brclose" | "closebracket" => Char(']'),
|
||||
"pgup" | "pageup" => PageUp,
|
||||
"pgdn" | "pagedown" => PageDown,
|
||||
"f1" => F(1),
|
||||
"f2" => F(2),
|
||||
"f3" => F(3),
|
||||
"f4" => F(4),
|
||||
"f5" => F(5),
|
||||
"f6" => F(6),
|
||||
"f7" => F(7),
|
||||
"f8" => F(8),
|
||||
"f9" => F(9),
|
||||
"f10" => F(10),
|
||||
"f11" => F(11),
|
||||
"f12" => F(12),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
92
tui/src/tui_engine/tui_keys.rs
Normal file
92
tui/src/tui_engine/tui_keys.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct KeyMatcher {
|
||||
valid: bool,
|
||||
key: Option<KeyCode>,
|
||||
mods: KeyModifiers,
|
||||
}
|
||||
|
||||
impl KeyMatcher {
|
||||
pub fn new (token: impl AsRef<str>) -> Self {
|
||||
let token = token.as_ref();
|
||||
if token.len() < 2 {
|
||||
Self { valid: false, key: None, mods: KeyModifiers::NONE }
|
||||
} else if token.chars().next() != Some('@') {
|
||||
Self { valid: false, key: None, mods: KeyModifiers::NONE }
|
||||
} else {
|
||||
Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..])
|
||||
}
|
||||
}
|
||||
pub fn build (self) -> Option<Event> {
|
||||
if self.valid && self.key.is_some() {
|
||||
Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn next (mut self, token: &str) -> Self {
|
||||
let mut tokens = token.split('-').peekable();
|
||||
while let Some(token) = tokens.next() {
|
||||
if tokens.peek().is_some() {
|
||||
match token {
|
||||
"ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL,
|
||||
"alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT,
|
||||
"shift" | "Shift" | "s" | "S" => {
|
||||
self.mods |= KeyModifiers::SHIFT;
|
||||
// + TODO normalize character case, BackTab, etc.
|
||||
},
|
||||
_ => panic!("unknown modifier {token}"),
|
||||
}
|
||||
} else {
|
||||
self.key = if token.len() == 1 {
|
||||
Some(KeyCode::Char(token.chars().next().unwrap()))
|
||||
} else {
|
||||
Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
fn named_key (token: &str) -> Option<KeyCode> {
|
||||
use KeyCode::*;
|
||||
Some(match token {
|
||||
"up" => Up,
|
||||
"down" => Down,
|
||||
"left" => Left,
|
||||
"right" => Right,
|
||||
"esc" | "escape" => Esc,
|
||||
"enter" | "return" => Enter,
|
||||
"delete" | "del" => Delete,
|
||||
"tab" => Tab,
|
||||
"space" => Char(' '),
|
||||
"comma" => Char(','),
|
||||
"period" => Char('.'),
|
||||
"plus" => Char('+'),
|
||||
"minus" | "dash" => Char('-'),
|
||||
"equal" | "equals" => Char('='),
|
||||
"underscore" => Char('_'),
|
||||
"backtick" => Char('`'),
|
||||
"lt" => Char('<'),
|
||||
"gt" => Char('>'),
|
||||
"cbopen" | "openbrace" => Char('{'),
|
||||
"cbclose" | "closebrace" => Char('}'),
|
||||
"bropen" | "openbracket" => Char('['),
|
||||
"brclose" | "closebracket" => Char(']'),
|
||||
"pgup" | "pageup" => PageUp,
|
||||
"pgdn" | "pagedown" => PageDown,
|
||||
"f1" => F(1),
|
||||
"f2" => F(2),
|
||||
"f3" => F(3),
|
||||
"f4" => F(4),
|
||||
"f5" => F(5),
|
||||
"f6" => F(6),
|
||||
"f7" => F(7),
|
||||
"f8" => F(8),
|
||||
"f9" => F(9),
|
||||
"f10" => F(10),
|
||||
"f11" => F(11),
|
||||
"f12" => F(12),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue