mirror of
https://github.com/itsjunetime/tdf.git
synced 2026-06-02 08:01:47 -04:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 134ba601fa | |||
| 3a264a0ddb | |||
| 85857890ed | |||
| c1c410ebe6 | |||
| 34047ca106 | |||
| 3452294f59 | |||
| d22aa4596d | |||
| 6e5bb0bdc5 | |||
| 7b68fe6b33 | |||
| a44dba20a7 | |||
| d6102de3c6 |
+1
-4
@@ -1,11 +1,8 @@
|
|||||||
# v0.3.0
|
# Unreleased
|
||||||
|
|
||||||
- Update ratatui(-image) dependencies
|
- Update ratatui(-image) dependencies
|
||||||
- Enable Ctrl+Z/Suspend functionality
|
- Enable Ctrl+Z/Suspend functionality
|
||||||
- Rewrite with mupdf as the backend for much better performance and rendering quality
|
- Rewrite with mupdf as the backend for much better performance and rendering quality
|
||||||
- Support easy inversion of colors via `i` keypress
|
|
||||||
- Support for filling all available space with `f` keypress
|
|
||||||
- Change help text at bottom into full help page
|
|
||||||
|
|
||||||
# v0.2.0
|
# v0.2.0
|
||||||
|
|
||||||
|
|||||||
Generated
+41
-71
@@ -52,9 +52,9 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.96"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
@@ -149,9 +149,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "avif-serialize"
|
name = "avif-serialize"
|
||||||
version = "0.8.3"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e"
|
checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
]
|
]
|
||||||
@@ -349,9 +349,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.16"
|
version = "1.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -429,18 +429,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.31"
|
version = "4.5.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.31"
|
version = "4.5.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
@@ -834,9 +834,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.14.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
@@ -889,15 +889,6 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fdeflate"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
|
||||||
dependencies = [
|
|
||||||
"simd-adler32",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filedescriptor"
|
name = "filedescriptor"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -935,9 +926,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.0"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
|
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
@@ -1353,7 +1344,6 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"png",
|
|
||||||
"ravif",
|
"ravif",
|
||||||
"rayon",
|
"rayon",
|
||||||
"zune-core",
|
"zune-core",
|
||||||
@@ -1542,9 +1532,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.170"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -1614,9 +1604,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.26"
|
version = "0.4.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "loop9"
|
name = "loop9"
|
||||||
@@ -1724,12 +1714,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.5"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
"simd-adler32",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1747,26 +1736,26 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mupdf"
|
name = "mupdf"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
source = "git+https://github.com/itsjunetime/mupdf-rs?branch=june%2Fmupdf_1_25#9c4f2379d205a78f967bb230f0e72ec18fad23f7"
|
source = "git+https://github.com/itsjunetime/mupdf-rs?branch=remove_debug_print#10f1b1629540e7d62354842198317bcf5e7d619c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"font-kit",
|
"font-kit",
|
||||||
"mupdf-sys",
|
"mupdf-sys",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"zerocopy 0.8.21",
|
"zerocopy 0.8.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mupdf-sys"
|
name = "mupdf-sys"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
source = "git+https://github.com/itsjunetime/mupdf-rs?branch=june%2Fmupdf_1_25#9c4f2379d205a78f967bb230f0e72ec18fad23f7"
|
source = "git+https://github.com/itsjunetime/mupdf-rs?branch=remove_debug_print#10f1b1629540e7d62354842198317bcf5e7d619c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"regex",
|
"regex",
|
||||||
"zerocopy 0.8.21",
|
"zerocopy 0.8.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2192,19 +2181,6 @@ dependencies = [
|
|||||||
"plotters-backend",
|
"plotters-backend",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "png"
|
|
||||||
version = "0.17.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"crc32fast",
|
|
||||||
"fdeflate",
|
|
||||||
"flate2",
|
|
||||||
"miniz_oxide",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -2492,9 +2468,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.9"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
]
|
]
|
||||||
@@ -2647,18 +2623,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.218"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.218"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2667,9 +2643,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.139"
|
version = "1.0.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -2742,12 +2718,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simd-adler32"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd_helpers"
|
name = "simd_helpers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -3332,9 +3302,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.15.1"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic",
|
"atomic",
|
||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
@@ -3801,9 +3771,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.3"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
|
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -3864,11 +3834,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.21"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478"
|
checksum = "8207f485579465f62ae51a983e42c906736a17efd2de48b021e64f1bbd8e98c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive 0.8.21",
|
"zerocopy-derive 0.8.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3884,9 +3854,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.21"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2"
|
checksum = "7dbe1304a711c6eb4cf1ed333aa0d9b344685e71f6f00c3b176072213bd3783e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
+3
-4
@@ -2,7 +2,7 @@
|
|||||||
name = "tdf-viewer"
|
name = "tdf-viewer"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
authors = ["June Welker <junewelker@gmail.com>"]
|
authors = ["June Welker <junewelker@gmail.com>"]
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
description = "A terminal viewer for PDFs"
|
description = "A terminal viewer for PDFs"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://github.com/itsjunetime/tdf"
|
homepage = "https://github.com/itsjunetime/tdf"
|
||||||
@@ -11,7 +11,6 @@ license = "AGPL-3.0-only"
|
|||||||
keywords = ["pdf", "tui", "cli", "terminal"]
|
keywords = ["pdf", "tui", "cli", "terminal"]
|
||||||
categories = ["command-line-utilities", "text-processing", "visualization"]
|
categories = ["command-line-utilities", "text-processing", "visualization"]
|
||||||
default-run = "tdf"
|
default-run = "tdf"
|
||||||
rust-version = "1.85"
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "tdf"
|
name = "tdf"
|
||||||
@@ -29,7 +28,7 @@ ratatui = { git = "https://github.com/itsjunetime/ratatui.git" }
|
|||||||
ratatui-image = { git = "https://github.com/itsjunetime/ratatui-image.git", branch = "vb64_on_personal", default-features = false }
|
ratatui-image = { git = "https://github.com/itsjunetime/ratatui-image.git", branch = "vb64_on_personal", default-features = false }
|
||||||
# ratatui-image = { path = "./ratatui-image", features = ["vb64"], default-features = false }
|
# ratatui-image = { path = "./ratatui-image", features = ["vb64"], default-features = false }
|
||||||
crossterm = { version = "0.28.1", features = ["event-stream"] }
|
crossterm = { version = "0.28.1", features = ["event-stream"] }
|
||||||
image = { version = "0.25.1", features = ["pnm", "rayon", "png"], default-features = false }
|
image = { version = "0.25.1", features = ["pnm", "rayon"], default-features = false }
|
||||||
notify = { version = "8.0.0", features = ["crossbeam-channel"] }
|
notify = { version = "8.0.0", features = ["crossbeam-channel"] }
|
||||||
tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros"] }
|
||||||
futures-util = { version = "0.3.30", default-features = false }
|
futures-util = { version = "0.3.30", default-features = false }
|
||||||
@@ -38,7 +37,7 @@ flume = { version = "0.11.0", default-features = false, features = ["async"] }
|
|||||||
xflags = "0.4.0-pre.2"
|
xflags = "0.4.0-pre.2"
|
||||||
mimalloc = "0.1.43"
|
mimalloc = "0.1.43"
|
||||||
nix = { version = "0.29.0", features = ["signal"] }
|
nix = { version = "0.29.0", features = ["signal"] }
|
||||||
mupdf = { git = "https://github.com/itsjunetime/mupdf-rs", branch = "june/mupdf_1_25", default-features = false, features = ["svg", "system-fonts", "img"] }
|
mupdf = { git = "https://github.com/itsjunetime/mupdf-rs", branch = "remove_debug_print", default-features = false, features = ["svg", "system-fonts", "img"] }
|
||||||
rayon = { version = "*", default-features = false }
|
rayon = { version = "*", default-features = false }
|
||||||
|
|
||||||
# for tracing with tokio-console
|
# for tracing with tokio-console
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ use std::{
|
|||||||
time::{SystemTime, UNIX_EPOCH}
|
time::{SystemTime, UNIX_EPOCH}
|
||||||
};
|
};
|
||||||
|
|
||||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main, profiler::Profiler};
|
use criterion::{criterion_group, criterion_main, profiler::Profiler, BenchmarkId, Criterion};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use tdf::{
|
use tdf::{
|
||||||
converter::{ConvertedPage, ConverterMsg},
|
converter::{ConvertedPage, ConverterMsg},
|
||||||
renderer::{PageInfo, RenderInfo, fill_default}
|
renderer::{fill_default, PageInfo, RenderInfo}
|
||||||
};
|
};
|
||||||
use utils::{
|
use utils::{
|
||||||
RenderState, handle_converter_msg, handle_renderer_msg, render_doc, start_all_rendering,
|
handle_converter_msg, handle_renderer_msg, render_doc, start_all_rendering,
|
||||||
start_converting_loop, start_rendering_loop
|
start_converting_loop, start_rendering_loop, RenderState
|
||||||
};
|
};
|
||||||
|
|
||||||
const FILES: [&str; 3] = [
|
const FILES: [&str; 3] = [
|
||||||
|
|||||||
+7
-7
@@ -1,13 +1,13 @@
|
|||||||
use std::{hint::black_box, path::Path};
|
use std::{hint::black_box, path::Path};
|
||||||
|
|
||||||
use crossterm::terminal::WindowSize;
|
use crossterm::terminal::WindowSize;
|
||||||
use flume::{Sender, r#async::RecvStream, unbounded};
|
use flume::{r#async::RecvStream, unbounded, Sender};
|
||||||
use futures_util::stream::StreamExt as _;
|
use futures_util::stream::StreamExt as _;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use ratatui_image::picker::{Picker, ProtocolType};
|
use ratatui_image::picker::{Picker, ProtocolType};
|
||||||
use tdf::{
|
use tdf::{
|
||||||
converter::{ConvertedPage, ConverterMsg, run_conversion_loop},
|
converter::{run_conversion_loop, ConvertedPage, ConverterMsg},
|
||||||
renderer::{RenderError, RenderInfo, RenderNotif, fill_default, start_rendering}
|
renderer::{fill_default, start_rendering, RenderError, RenderInfo, RenderNotif}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn handle_renderer_msg(
|
pub fn handle_renderer_msg(
|
||||||
@@ -37,13 +37,13 @@ pub fn handle_converter_msg(
|
|||||||
|
|
||||||
pages[num] = Some(page);
|
pages[num] = Some(page);
|
||||||
|
|
||||||
let first_none = pages.iter().position(Option::is_none);
|
let num_got = pages.iter().filter(|p| p.is_some()).count();
|
||||||
|
|
||||||
// we have to tell it to jump to a certain page so that it will actually render it (since
|
// we have to tell it to jump to a certain page so that it will actually render it (since
|
||||||
// it only renders fanning out from the page that we currently have selected)
|
// it only renders fanning out from the page that we currently have selected)
|
||||||
if let Some(first) = first_none {
|
to_converter_tx
|
||||||
to_converter_tx.send(ConverterMsg::GoToPage(first)).unwrap();
|
.send(ConverterMsg::GoToPage(num_got))
|
||||||
}
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderState {
|
pub struct RenderState {
|
||||||
|
|||||||
+3
-3
@@ -2,10 +2,10 @@ use flume::{Receiver, SendError, Sender, TryRecvError};
|
|||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ratatui_image::{Resize, picker::Picker, protocol::Protocol};
|
use ratatui_image::{picker::Picker, protocol::Protocol, Resize};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
|
|
||||||
use crate::renderer::{PageInfo, RenderError, fill_default};
|
use crate::renderer::{fill_default, PageInfo, RenderError};
|
||||||
|
|
||||||
pub struct ConvertedPage {
|
pub struct ConvertedPage {
|
||||||
pub page: Protocol,
|
pub page: Protocol,
|
||||||
@@ -49,7 +49,7 @@ pub async fn run_conversion_loop(
|
|||||||
let Some((page_info, new_iter)) = (idx_start..page)
|
let Some((page_info, new_iter)) = (idx_start..page)
|
||||||
.interleave(page..idx_end)
|
.interleave(page..idx_end)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
// .skip(*iteration)
|
.skip(*iteration)
|
||||||
.find_map(|(i_idx, p_idx)| images[p_idx].take().map(|p| (p, i_idx)))
|
.find_map(|(i_idx, p_idx)| images[p_idx].take().map(|p| (p, i_idx)))
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|||||||
+10
-15
@@ -1,6 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
io::{Read, Write, stdout},
|
io::{stdout, Read, Write},
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
path::PathBuf
|
path::PathBuf
|
||||||
};
|
};
|
||||||
@@ -8,16 +8,16 @@ use std::{
|
|||||||
use crossterm::{
|
use crossterm::{
|
||||||
execute,
|
execute,
|
||||||
terminal::{
|
terminal::{
|
||||||
EndSynchronizedUpdate, EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode,
|
disable_raw_mode, enable_raw_mode, window_size, EndSynchronizedUpdate,
|
||||||
enable_raw_mode, window_size
|
EnterAlternateScreen, LeaveAlternateScreen
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
use futures_util::{FutureExt, stream::StreamExt};
|
use futures_util::{stream::StreamExt, FutureExt};
|
||||||
use notify::{Event, EventKind, RecursiveMode, Watcher};
|
use notify::{Event, EventKind, RecursiveMode, Watcher};
|
||||||
use ratatui::{Terminal, backend::CrosstermBackend};
|
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||||
use ratatui_image::picker::Picker;
|
use ratatui_image::picker::Picker;
|
||||||
use tdf::{
|
use tdf::{
|
||||||
converter::{ConvertedPage, ConverterMsg, run_conversion_loop},
|
converter::{run_conversion_loop, ConvertedPage, ConverterMsg},
|
||||||
renderer::{self, RenderError, RenderInfo, RenderNotif},
|
renderer::{self, RenderError, RenderInfo, RenderNotif},
|
||||||
tui::{BottomMessage, InputAction, MessageSetting, Tui}
|
tui::{BottomMessage, InputAction, MessageSetting, Tui}
|
||||||
};
|
};
|
||||||
@@ -45,8 +45,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
optional -r,--r-to-l r_to_l: bool
|
optional -r,--r-to-l r_to_l: bool
|
||||||
/// The maximum number of pages to display together, horizontally, at a time
|
/// The maximum number of pages to display together, horizontally, at a time
|
||||||
optional -m,--max-wide max_wide: NonZeroUsize
|
optional -m,--max-wide max_wide: NonZeroUsize
|
||||||
/// Fullscreen the pdf (hide document name, page count, etc)
|
|
||||||
optional -f,--fullscreen fullscreen: bool
|
|
||||||
/// PDF file to read
|
/// PDF file to read
|
||||||
required file: PathBuf
|
required file: PathBuf
|
||||||
};
|
};
|
||||||
@@ -169,9 +167,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
)?;
|
)?;
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
let mut fullscreen = flags.fullscreen.unwrap_or_default();
|
let mut main_area = Tui::main_layout(&term.get_frame());
|
||||||
let mut main_area = Tui::main_layout(&term.get_frame(), fullscreen);
|
tui_tx.send(RenderNotif::Area(main_area[1]))?;
|
||||||
tui_tx.send(RenderNotif::Area(main_area.page_area))?;
|
|
||||||
|
|
||||||
let mut tui_rx = tui_rx.into_stream();
|
let mut tui_rx = tui_rx.into_stream();
|
||||||
let mut from_converter = from_converter.into_stream();
|
let mut from_converter = from_converter.into_stream();
|
||||||
@@ -194,8 +191,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
to_converter.send(ConverterMsg::GoToPage(page))?;
|
to_converter.send(ConverterMsg::GoToPage(page))?;
|
||||||
},
|
},
|
||||||
InputAction::Search(term) => tui_tx.send(RenderNotif::Search(term))?,
|
InputAction::Search(term) => tui_tx.send(RenderNotif::Search(term))?,
|
||||||
InputAction::Invert => tui_tx.send(RenderNotif::Invert)?,
|
|
||||||
InputAction::Fullscreen => fullscreen = !fullscreen,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -223,10 +218,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_area = Tui::main_layout(&term.get_frame(), fullscreen);
|
let new_area = Tui::main_layout(&term.get_frame());
|
||||||
if new_area != main_area {
|
if new_area != main_area {
|
||||||
main_area = new_area;
|
main_area = new_area;
|
||||||
tui_tx.send(RenderNotif::Area(main_area.page_area))?;
|
tui_tx.send(RenderNotif::Area(main_area[1]))?;
|
||||||
needs_redraw = true;
|
needs_redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-38
@@ -10,8 +10,7 @@ pub enum RenderNotif {
|
|||||||
Area(Rect),
|
Area(Rect),
|
||||||
JumpToPage(usize),
|
JumpToPage(usize),
|
||||||
Search(String),
|
Search(String),
|
||||||
Reload,
|
Reload
|
||||||
Invert
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -46,10 +45,12 @@ struct PrevRender {
|
|||||||
contained_term: Option<bool>
|
contained_term: Option<bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn fill_default<T: Default>(vec: &mut Vec<T>, size: usize) {
|
pub fn fill_default<T: Default>(vec: &mut Vec<T>, size: usize) {
|
||||||
vec.clear();
|
vec.clear();
|
||||||
vec.resize_with(size, T::default);
|
vec.reserve(size.saturating_sub(vec.len()));
|
||||||
|
for _ in 0..size {
|
||||||
|
vec.push(T::default());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function has to be sync (non-async) because the mupdf::Document needs to be held during
|
// this function has to be sync (non-async) because the mupdf::Document needs to be held during
|
||||||
@@ -71,6 +72,14 @@ pub fn start_rendering(
|
|||||||
receiver: Receiver<RenderNotif>,
|
receiver: Receiver<RenderNotif>,
|
||||||
size: WindowSize
|
size: WindowSize
|
||||||
) -> Result<(), SendError<Result<RenderInfo, RenderError>>> {
|
) -> Result<(), SendError<Result<RenderInfo, RenderError>>> {
|
||||||
|
// first, wait 'til we get told what the current starting area is so that we can set it to
|
||||||
|
// know what to render to
|
||||||
|
let mut area = loop {
|
||||||
|
if let RenderNotif::Area(r) = receiver.recv().unwrap() {
|
||||||
|
break r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// We want this outside of 'reload so that if the doc reloads, the search term that somebody
|
// We want this outside of 'reload so that if the doc reloads, the search term that somebody
|
||||||
// set will still get highlighted in the reloaded doc
|
// set will still get highlighted in the reloaded doc
|
||||||
let mut search_term = None;
|
let mut search_term = None;
|
||||||
@@ -81,8 +90,6 @@ pub fn start_rendering(
|
|||||||
let col_h = size.height / size.rows;
|
let col_h = size.height / size.rows;
|
||||||
|
|
||||||
let mut stored_doc = None;
|
let mut stored_doc = None;
|
||||||
let mut invert = false;
|
|
||||||
let mut preserved_area = None;
|
|
||||||
|
|
||||||
'reload: loop {
|
'reload: loop {
|
||||||
let doc = match Document::open(path) {
|
let doc = match Document::open(path) {
|
||||||
@@ -132,7 +139,7 @@ pub fn start_rendering(
|
|||||||
// `split_at_mut` at 0 initially (which bascially makes `right == rendered && left == []`),
|
// `split_at_mut` at 0 initially (which bascially makes `right == rendered && left == []`),
|
||||||
// doing basically nothing, but if we get a notification that something has been jumped to,
|
// doing basically nothing, but if we get a notification that something has been jumped to,
|
||||||
// then we can split at that page and render at both sides of it
|
// then we can split at that page and render at both sides of it
|
||||||
let mut rendered = Vec::new();
|
let mut rendered = vec![];
|
||||||
fill_default::<PrevRender>(&mut rendered, n_pages);
|
fill_default::<PrevRender>(&mut rendered, n_pages);
|
||||||
let mut start_point = 0;
|
let mut start_point = 0;
|
||||||
|
|
||||||
@@ -141,39 +148,25 @@ pub fn start_rendering(
|
|||||||
// document. If there was a mechanism to say 'start this for-loop over' then I would do
|
// document. If there was a mechanism to say 'start this for-loop over' then I would do
|
||||||
// that, but I don't think such a thing exists, so this is our attempt
|
// that, but I don't think such a thing exists, so this is our attempt
|
||||||
'render_pages: loop {
|
'render_pages: loop {
|
||||||
// next, we gotta wait 'til we get told what the current starting area is so that we can
|
|
||||||
// set it to know what to render to
|
|
||||||
let area = match preserved_area {
|
|
||||||
Some(a) => a,
|
|
||||||
None => {
|
|
||||||
let new_area = loop {
|
|
||||||
if let RenderNotif::Area(r) = receiver.recv().unwrap() {
|
|
||||||
break r;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
preserved_area = Some(new_area);
|
|
||||||
new_area
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// what we do with a notif is the same regardless of if we're in the middle of
|
// what we do with a notif is the same regardless of if we're in the middle of
|
||||||
// rendering the list of pages or we're all done
|
// rendering the list of pages or we're all done
|
||||||
macro_rules! handle_notif {
|
macro_rules! handle_notif {
|
||||||
($notif:ident) => {
|
($notif:ident) => {
|
||||||
match $notif {
|
match $notif {
|
||||||
RenderNotif::Reload => continue 'reload,
|
RenderNotif::Reload => continue 'reload,
|
||||||
RenderNotif::Invert => {
|
|
||||||
invert = !invert;
|
|
||||||
for page in &mut rendered {
|
|
||||||
page.successful = false;
|
|
||||||
}
|
|
||||||
continue 'render_pages;
|
|
||||||
}
|
|
||||||
RenderNotif::Area(new_area) => {
|
RenderNotif::Area(new_area) => {
|
||||||
preserved_area = Some(new_area);
|
let bigger =
|
||||||
|
new_area.width > area.width || new_area.height > area.height;
|
||||||
|
area = new_area;
|
||||||
|
// we only want to re-render pages if the new area is greater than the old
|
||||||
|
// one, 'cause then we might need sharper images to make it all look good.
|
||||||
|
// If the new area is smaller, then the same high-quality-rendered images
|
||||||
|
// will still look fine, so it's ok to leave it.
|
||||||
|
if bigger {
|
||||||
fill_default(&mut rendered, n_pages);
|
fill_default(&mut rendered, n_pages);
|
||||||
continue 'render_pages;
|
continue 'render_pages;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RenderNotif::JumpToPage(page) => {
|
RenderNotif::JumpToPage(page) => {
|
||||||
start_point = page;
|
start_point = page;
|
||||||
continue 'render_pages;
|
continue 'render_pages;
|
||||||
@@ -259,7 +252,6 @@ pub fn start_rendering(
|
|||||||
&page,
|
&page,
|
||||||
search_term.as_deref(),
|
search_term.as_deref(),
|
||||||
rendered_with_no_results,
|
rendered_with_no_results,
|
||||||
invert,
|
|
||||||
(area_w, area_h)
|
(area_w, area_h)
|
||||||
) {
|
) {
|
||||||
// If we've already rendered it just fine and we don't need to render it again,
|
// If we've already rendered it just fine and we don't need to render it again,
|
||||||
@@ -274,9 +266,9 @@ pub fn start_rendering(
|
|||||||
rendered.contained_term = Some(ctx.result_rects.is_empty());
|
rendered.contained_term = Some(ctx.result_rects.is_empty());
|
||||||
rendered.successful = true;
|
rendered.successful = true;
|
||||||
|
|
||||||
let w = ctx.pixmap.width();
|
let cap = (ctx.pixmap.width()
|
||||||
let h = ctx.pixmap.height();
|
* ctx.pixmap.height() * u32::from(ctx.pixmap.n()))
|
||||||
let cap = (w * h * u32::from(ctx.pixmap.n())) as usize + 16;
|
as usize;
|
||||||
let mut pixels = Vec::with_capacity(cap);
|
let mut pixels = Vec::with_capacity(cap);
|
||||||
if let Err(e) = ctx.pixmap.write_to(&mut pixels, mupdf::ImageFormat::PNM) {
|
if let Err(e) = ctx.pixmap.write_to(&mut pixels, mupdf::ImageFormat::PNM) {
|
||||||
sender.send(Err(RenderError::Doc(e)))?;
|
sender.send(Err(RenderError::Doc(e)))?;
|
||||||
@@ -327,7 +319,6 @@ fn render_single_page_to_ctx(
|
|||||||
page: &Page,
|
page: &Page,
|
||||||
search_term: Option<&str>,
|
search_term: Option<&str>,
|
||||||
already_rendered_no_results: bool,
|
already_rendered_no_results: bool,
|
||||||
invert: bool,
|
|
||||||
(area_w, area_h): (f32, f32)
|
(area_w, area_h): (f32, f32)
|
||||||
) -> Result<Option<RenderedContext>, mupdf::error::Error> {
|
) -> Result<Option<RenderedContext>, mupdf::error::Error> {
|
||||||
let mut max_hits = 10;
|
let mut max_hits = 10;
|
||||||
@@ -387,9 +378,6 @@ fn render_single_page_to_ctx(
|
|||||||
let matrix = Matrix::new_scale(scale_factor, scale_factor);
|
let matrix = Matrix::new_scale(scale_factor, scale_factor);
|
||||||
|
|
||||||
let mut pixmap = page.to_pixmap(&matrix, &colorspace, 0.0, false)?;
|
let mut pixmap = page.to_pixmap(&matrix, &colorspace, 0.0, false)?;
|
||||||
if invert {
|
|
||||||
pixmap.invert()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (x_res, y_res) = pixmap.resolution();
|
let (x_res, y_res) = pixmap.resolution();
|
||||||
let new_x = (x_res as f32 * scale_factor) as i32;
|
let new_x = (x_res as f32 * scale_factor) as i32;
|
||||||
|
|||||||
+29
-134
@@ -1,26 +1,25 @@
|
|||||||
use std::{borrow::Cow, io::stdout, num::NonZeroUsize};
|
use std::{borrow::Cow, io::stdout, num::NonZeroUsize, rc::Rc};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{Event, KeyCode, KeyModifiers, MouseEventKind},
|
event::{Event, KeyCode, KeyModifiers, MouseEventKind},
|
||||||
execute,
|
execute,
|
||||||
terminal::{
|
terminal::{
|
||||||
BeginSynchronizedUpdate, EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode,
|
disable_raw_mode, enable_raw_mode, BeginSynchronizedUpdate, EnterAlternateScreen,
|
||||||
enable_raw_mode
|
LeaveAlternateScreen
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
use nix::{
|
use nix::{
|
||||||
sys::signal::{Signal::SIGSTOP, kill},
|
sys::signal::{kill, Signal::SIGSTOP},
|
||||||
unistd::Pid
|
unistd::Pid
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
Frame,
|
|
||||||
layout::{Constraint, Flex, Layout, Rect},
|
layout::{Constraint, Flex, Layout, Rect},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
symbols::border,
|
text::Span,
|
||||||
text::{Span, Text},
|
widgets::{Block, Borders, Padding},
|
||||||
widgets::{Block, Borders, Clear, Padding}
|
Frame
|
||||||
};
|
};
|
||||||
use ratatui_image::{Image, protocol::Protocol};
|
use ratatui_image::{protocol::Protocol, Image};
|
||||||
|
|
||||||
use crate::{renderer::RenderError, skip::Skip};
|
use crate::{renderer::RenderError, skip::Skip};
|
||||||
|
|
||||||
@@ -33,8 +32,7 @@ pub struct Tui {
|
|||||||
// jumping to a specific page
|
// jumping to a specific page
|
||||||
prev_msg: Option<BottomMessage>,
|
prev_msg: Option<BottomMessage>,
|
||||||
rendered: Vec<RenderedInfo>,
|
rendered: Vec<RenderedInfo>,
|
||||||
page_constraints: PageConstraints,
|
page_constraints: PageConstraints
|
||||||
showing_help_msg: bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
@@ -79,12 +77,6 @@ struct RenderedInfo {
|
|||||||
num_results: Option<usize>
|
num_results: Option<usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
pub struct RenderLayout {
|
|
||||||
pub page_area: Rect,
|
|
||||||
pub top_and_bottom: Option<(Rect, Rect)>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tui {
|
impl Tui {
|
||||||
pub fn new(name: String, max_wide: Option<NonZeroUsize>, r_to_l: bool) -> Tui {
|
pub fn new(name: String, max_wide: Option<NonZeroUsize>, r_to_l: bool) -> Tui {
|
||||||
Self {
|
Self {
|
||||||
@@ -94,19 +86,12 @@ impl Tui {
|
|||||||
bottom_msg: BottomMessage::Help,
|
bottom_msg: BottomMessage::Help,
|
||||||
last_render: LastRender::default(),
|
last_render: LastRender::default(),
|
||||||
rendered: vec![],
|
rendered: vec![],
|
||||||
page_constraints: PageConstraints { max_wide, r_to_l },
|
page_constraints: PageConstraints { max_wide, r_to_l }
|
||||||
showing_help_msg: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_layout(frame: &Frame<'_>, fullscreened: bool) -> RenderLayout {
|
pub fn main_layout(frame: &Frame<'_>) -> Rc<[Rect]> {
|
||||||
if fullscreened {
|
Layout::default()
|
||||||
RenderLayout {
|
|
||||||
page_area: frame.area(),
|
|
||||||
top_and_bottom: None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let layout = Layout::default()
|
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
@@ -114,23 +99,11 @@ impl Tui {
|
|||||||
])
|
])
|
||||||
.horizontal_margin(2)
|
.horizontal_margin(2)
|
||||||
.vertical_margin(1)
|
.vertical_margin(1)
|
||||||
.split(frame.area());
|
.split(frame.area())
|
||||||
|
|
||||||
RenderLayout {
|
|
||||||
page_area: layout[1],
|
|
||||||
top_and_bottom: Some((layout[0], layout[2]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make a way to fill the width of the screen with one page and scroll down to view it
|
// TODO: Make a way to fill the width of the screen with one page and scroll down to view it
|
||||||
pub fn render(&mut self, frame: &mut Frame<'_>, full_layout: &RenderLayout) {
|
pub fn render(&mut self, frame: &mut Frame<'_>, main_area: &[Rect]) {
|
||||||
if self.showing_help_msg {
|
|
||||||
self.render_help_msg(frame);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((top_area, bottom_area)) = full_layout.top_and_bottom {
|
|
||||||
let top_block = Block::new()
|
let top_block = Block::new()
|
||||||
.padding(Padding {
|
.padding(Padding {
|
||||||
right: 2,
|
right: 2,
|
||||||
@@ -139,7 +112,7 @@ impl Tui {
|
|||||||
})
|
})
|
||||||
.borders(Borders::BOTTOM);
|
.borders(Borders::BOTTOM);
|
||||||
|
|
||||||
let top_area = top_block.inner(top_area);
|
let top_area = top_block.inner(main_area[0]);
|
||||||
|
|
||||||
let page_nums_text = format!("{} / {}", self.page + 1, self.rendered.len());
|
let page_nums_text = format!("{} / {}", self.page + 1, self.rendered.len());
|
||||||
|
|
||||||
@@ -153,7 +126,7 @@ impl Tui {
|
|||||||
|
|
||||||
let page_nums = Span::styled(&page_nums_text, Style::new().fg(Color::Cyan));
|
let page_nums = Span::styled(&page_nums_text, Style::new().fg(Color::Cyan));
|
||||||
|
|
||||||
frame.render_widget(top_block, top_area);
|
frame.render_widget(top_block, main_area[0]);
|
||||||
frame.render_widget(title, top_layout[0]);
|
frame.render_widget(title, top_layout[0]);
|
||||||
frame.render_widget(page_nums, top_layout[1]);
|
frame.render_widget(page_nums, top_layout[1]);
|
||||||
|
|
||||||
@@ -165,9 +138,9 @@ impl Tui {
|
|||||||
bottom: 0
|
bottom: 0
|
||||||
})
|
})
|
||||||
.borders(Borders::TOP);
|
.borders(Borders::TOP);
|
||||||
let bottom_inside_block = bottom_block.inner(bottom_area);
|
let bottom_area = bottom_block.inner(main_area[2]);
|
||||||
|
|
||||||
frame.render_widget(bottom_block, bottom_area);
|
frame.render_widget(bottom_block, main_area[2]);
|
||||||
|
|
||||||
let rendered_str = if !self.rendered.is_empty() {
|
let rendered_str = if !self.rendered.is_empty() {
|
||||||
format!(
|
format!(
|
||||||
@@ -182,13 +155,16 @@ impl Tui {
|
|||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(rendered_str.len() as u16)
|
Constraint::Length(rendered_str.len() as u16)
|
||||||
])
|
])
|
||||||
.split(bottom_inside_block);
|
.split(bottom_area);
|
||||||
|
|
||||||
let rendered_span = Span::styled(&rendered_str, Style::new().fg(Color::Cyan));
|
let rendered_span = Span::styled(&rendered_str, Style::new().fg(Color::Cyan));
|
||||||
frame.render_widget(rendered_span, bottom_layout[1]);
|
frame.render_widget(rendered_span, bottom_layout[1]);
|
||||||
|
|
||||||
let (msg_str, color): (Cow<'_, str>, _) = match self.bottom_msg {
|
let (msg_str, color): (Cow<'_, str>, _) = match self.bottom_msg {
|
||||||
BottomMessage::Help => ("?: Show help page".into(), Color::Blue),
|
BottomMessage::Help => (
|
||||||
|
"/: Search, g: Go To Page, n: Next Search Result, N: Previous Search Result".into(),
|
||||||
|
Color::Blue
|
||||||
|
),
|
||||||
BottomMessage::Error(ref e) => (e.as_str().into(), Color::Red),
|
BottomMessage::Error(ref e) => (e.as_str().into(), Color::Red),
|
||||||
BottomMessage::Input(ref input_state) => (
|
BottomMessage::Input(ref input_state) => (
|
||||||
match input_state {
|
match input_state {
|
||||||
@@ -223,9 +199,8 @@ impl Tui {
|
|||||||
|
|
||||||
let span = Span::styled(msg_str, Style::new().fg(color));
|
let span = Span::styled(msg_str, Style::new().fg(color));
|
||||||
frame.render_widget(span, bottom_layout[0]);
|
frame.render_widget(span, bottom_layout[0]);
|
||||||
}
|
|
||||||
|
|
||||||
let mut img_area = full_layout.page_area;
|
let mut img_area = main_area[1];
|
||||||
|
|
||||||
let size = frame.area();
|
let size = frame.area();
|
||||||
if size == self.last_render.rect {
|
if size == self.last_render.rect {
|
||||||
@@ -439,12 +414,6 @@ impl Tui {
|
|||||||
)));
|
)));
|
||||||
Some(InputAction::Redraw)
|
Some(InputAction::Redraw)
|
||||||
}
|
}
|
||||||
'i' => Some(InputAction::Invert),
|
|
||||||
'?' => {
|
|
||||||
self.showing_help_msg = true;
|
|
||||||
Some(InputAction::Redraw)
|
|
||||||
}
|
|
||||||
'f' => Some(InputAction::Fullscreen),
|
|
||||||
'n' if self.page < self.rendered.len() - 1 => {
|
'n' if self.page < self.rendered.len() - 1 => {
|
||||||
// TODO: If we can't find one, then maybe like block until we've verified
|
// TODO: If we can't find one, then maybe like block until we've verified
|
||||||
// all the pages have been checked?
|
// all the pages have been checked?
|
||||||
@@ -516,8 +485,8 @@ impl Tui {
|
|||||||
KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
||||||
KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single),
|
KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single),
|
||||||
KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
||||||
KeyCode::Esc => match (self.showing_help_msg, &self.bottom_msg) {
|
KeyCode::Esc => match self.bottom_msg {
|
||||||
(false, BottomMessage::Help) => Some(InputAction::QuitApp),
|
BottomMessage::Help => Some(InputAction::QuitApp),
|
||||||
_ => {
|
_ => {
|
||||||
// When we hit escape, we just want to pop off the current message and
|
// When we hit escape, we just want to pop off the current message and
|
||||||
// show the underlying one.
|
// show the underlying one.
|
||||||
@@ -546,9 +515,7 @@ impl Tui {
|
|||||||
Some(InputAction::JumpingToPage(zero_page))
|
Some(InputAction::JumpingToPage(zero_page))
|
||||||
} else {
|
} else {
|
||||||
self.set_msg(MessageSetting::Some(BottomMessage::Error(
|
self.set_msg(MessageSetting::Some(BottomMessage::Error(
|
||||||
format!(
|
format!("Cannot jump to page {page}; there are only {rendered_len} pages in the document")
|
||||||
"Cannot jump to page {page}; there are only {rendered_len} pages in the document"
|
|
||||||
)
|
|
||||||
)));
|
)));
|
||||||
Some(InputAction::Redraw)
|
Some(InputAction::Redraw)
|
||||||
}
|
}
|
||||||
@@ -628,88 +595,16 @@ impl Tui {
|
|||||||
self.prev_msg = None;
|
self.prev_msg = None;
|
||||||
self.bottom_msg = BottomMessage::default();
|
self.bottom_msg = BottomMessage::default();
|
||||||
}
|
}
|
||||||
MessageSetting::Pop =>
|
MessageSetting::Pop => self.bottom_msg = self.prev_msg.take().unwrap_or_default()
|
||||||
if self.showing_help_msg {
|
|
||||||
self.last_render.rect = Rect::default();
|
|
||||||
self.showing_help_msg = false;
|
|
||||||
} else {
|
|
||||||
self.bottom_msg = self.prev_msg.take().unwrap_or_default();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_help_msg(&self, frame: &mut Frame<'_>) {
|
|
||||||
let frame_area = frame.area();
|
|
||||||
frame.render_widget(Clear, frame_area);
|
|
||||||
|
|
||||||
let block = Block::new()
|
|
||||||
.title("Help")
|
|
||||||
.padding(Padding::proportional(1))
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_set(border::ROUNDED)
|
|
||||||
.border_style(Color::Blue);
|
|
||||||
|
|
||||||
let help_span = Text::raw(HELP_PAGE);
|
|
||||||
|
|
||||||
let max_w: u16 = HELP_PAGE
|
|
||||||
.lines()
|
|
||||||
.map(str::len)
|
|
||||||
.max()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.try_into()
|
|
||||||
.expect("Every help text line must be shorter than u16::MAX");
|
|
||||||
|
|
||||||
let layout = Layout::horizontal([
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(max_w + 6),
|
|
||||||
Constraint::Fill(1)
|
|
||||||
])
|
|
||||||
.split(frame_area);
|
|
||||||
|
|
||||||
let block_area = Layout::vertical([
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(u16::try_from(HELP_PAGE.lines().count()).unwrap() + 4),
|
|
||||||
Constraint::Fill(1)
|
|
||||||
])
|
|
||||||
.split(layout[1]);
|
|
||||||
|
|
||||||
let block_inner = block.inner(block_area[1]);
|
|
||||||
|
|
||||||
frame.render_widget(block, block_area[1]);
|
|
||||||
frame.render_widget(help_span, block_inner);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static HELP_PAGE: &str = "\
|
|
||||||
l, h, left, right:
|
|
||||||
Go forward/backwards a single page
|
|
||||||
j, k, down, up:
|
|
||||||
Go forwards/backwards a screen's worth of pages
|
|
||||||
q, esc:
|
|
||||||
Quit
|
|
||||||
g:
|
|
||||||
Go to specific page (type numbers after 'g')
|
|
||||||
/:
|
|
||||||
Search
|
|
||||||
n, N:
|
|
||||||
Next/Previous search result
|
|
||||||
i:
|
|
||||||
Invert colors
|
|
||||||
f:
|
|
||||||
Remove borders/fullscreen
|
|
||||||
?:
|
|
||||||
Show this page
|
|
||||||
ctrl+z:
|
|
||||||
Suspend & background tdf \
|
|
||||||
";
|
|
||||||
|
|
||||||
pub enum InputAction {
|
pub enum InputAction {
|
||||||
Redraw,
|
Redraw,
|
||||||
JumpingToPage(usize),
|
JumpingToPage(usize),
|
||||||
Search(String),
|
Search(String),
|
||||||
QuitApp,
|
QuitApp
|
||||||
Invert,
|
|
||||||
Fullscreen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
|||||||
Reference in New Issue
Block a user