mirror of
https://github.com/itsjunetime/tdf.git
synced 2026-06-02 08:01:47 -04:00
- Remove unused oxipng dep
- throw converter onto its own task - switch to using multi-thread runtime - use unbounded channels in a few more places to prevent deadlocks
This commit is contained in:
Generated
+17
-224
@@ -35,55 +35,6 @@ version = "0.2.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstream"
|
|
||||||
version = "0.6.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"anstyle-parse",
|
|
||||||
"anstyle-query",
|
|
||||||
"anstyle-wincon",
|
|
||||||
"colorchoice",
|
|
||||||
"is_terminal_polyfill",
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-parse"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
|
||||||
dependencies = [
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-query"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-wincon"
|
|
||||||
version = "3.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -123,18 +74,6 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitvec"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
|
||||||
dependencies = [
|
|
||||||
"funty",
|
|
||||||
"radium",
|
|
||||||
"tap",
|
|
||||||
"wyz",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
@@ -208,49 +147,6 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap"
|
|
||||||
version = "4.5.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
|
||||||
dependencies = [
|
|
||||||
"clap_builder",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_builder"
|
|
||||||
version = "4.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
|
||||||
dependencies = [
|
|
||||||
"anstream",
|
|
||||||
"anstyle",
|
|
||||||
"clap_lex",
|
|
||||||
"strsim",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_lex"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_mangen"
|
|
||||||
version = "0.2.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e1dd95b5ebb5c1c54581dd6346f3ed6a79a3eef95dd372fc2ac13d535535300e"
|
|
||||||
dependencies = [
|
|
||||||
"clap",
|
|
||||||
"roff",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colorchoice"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compact_str"
|
name = "compact_str"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -401,12 +297,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "funty"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
@@ -596,6 +486,12 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icy_sixel"
|
name = "icy_sixel"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -625,7 +521,6 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"rayon",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -648,12 +543,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "is_terminal_polyfill"
|
|
||||||
version = "1.70.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@@ -704,24 +593,6 @@ version = "0.2.155"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libdeflate-sys"
|
|
||||||
version = "1.20.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "669ea17f9257bcb48c09c7ee4bef3957777504acffac557263e20c11001977bc"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libdeflater"
|
|
||||||
version = "1.20.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8dfd6424f7010ee0a3416f1d796d0450e3ad3ac237a237644f728277c4ded016"
|
|
||||||
dependencies = [
|
|
||||||
"libdeflate-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@@ -809,6 +680,16 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.32.2"
|
version = "0.32.2"
|
||||||
@@ -824,25 +705,6 @@ version = "1.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "oxipng"
|
|
||||||
version = "9.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f398c53eb34e0cf71d9e0bc676cfa7c611e3844dd14ab05e92fb7b423c98ecf"
|
|
||||||
dependencies = [
|
|
||||||
"bitvec",
|
|
||||||
"clap",
|
|
||||||
"clap_mangen",
|
|
||||||
"crossbeam-channel",
|
|
||||||
"indexmap",
|
|
||||||
"libdeflater",
|
|
||||||
"log",
|
|
||||||
"rayon",
|
|
||||||
"rgb",
|
|
||||||
"rustc-hash",
|
|
||||||
"rustc_version",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -963,12 +825,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "radium"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -1071,42 +927,12 @@ dependencies = [
|
|||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rgb"
|
|
||||||
version = "0.8.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roff"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-hash"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
|
||||||
dependencies = [
|
|
||||||
"semver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.34"
|
version = "0.38.34"
|
||||||
@@ -1147,12 +973,6 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "1.0.23"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.203"
|
version = "1.0.203"
|
||||||
@@ -1249,12 +1069,6 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.26.2"
|
version = "0.26.2"
|
||||||
@@ -1301,12 +1115,6 @@ dependencies = [
|
|||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tap"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.14"
|
version = "0.12.14"
|
||||||
@@ -1324,7 +1132,6 @@ dependencies = [
|
|||||||
"image",
|
"image",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"notify",
|
"notify",
|
||||||
"oxipng",
|
|
||||||
"poppler-rs",
|
"poppler-rs",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"ratatui-image",
|
"ratatui-image",
|
||||||
@@ -1358,6 +1165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
|
"num_cpus",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
]
|
]
|
||||||
@@ -1446,12 +1254,6 @@ version = "0.1.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8parse"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vb64"
|
name = "vb64"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -1673,15 +1475,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wyz"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
|
||||||
dependencies = [
|
|
||||||
"tap",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.34"
|
version = "0.7.34"
|
||||||
|
|||||||
+1
-2
@@ -15,11 +15,10 @@ ratatui-image = { git = "https://github.com/itsjunetime/ratatui-image.git", bran
|
|||||||
crossterm = { version = "0.27.0", features = ["event-stream"] }
|
crossterm = { version = "0.27.0", features = ["event-stream"] }
|
||||||
image = { version = "0.25.1", features = ["png", "rayon"], default-features = false }
|
image = { version = "0.25.1", features = ["png", "rayon"], default-features = false }
|
||||||
notify = "6.1.1"
|
notify = "6.1.1"
|
||||||
tokio = { version = "1.37.0", features = ["rt", "sync", "macros"] }
|
tokio = { version = "1.37.0", features = ["rt-multi-thread", "sync", "macros"] }
|
||||||
futures-util = { version = "0.3.30", default-features = false }
|
futures-util = { version = "0.3.30", default-features = false }
|
||||||
glib = "0.19.6"
|
glib = "0.19.6"
|
||||||
itertools = "*"
|
itertools = "*"
|
||||||
oxipng = { version = "9.1.1", default-features = false, features = ["parallel"] }
|
|
||||||
|
|
||||||
[profile.production]
|
[profile.production]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|||||||
+105
-120
@@ -1,138 +1,123 @@
|
|||||||
use std::{
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll}
|
|
||||||
};
|
|
||||||
|
|
||||||
use futures_util::Stream;
|
|
||||||
use image::ImageFormat;
|
use image::ImageFormat;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ratatui_image::{picker::Picker, protocol::Protocol, Resize};
|
use ratatui_image::{picker::Picker, protocol::Protocol, Resize};
|
||||||
|
use tokio::sync::mpsc::{error::{SendError, TryRecvError}, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
||||||
use crate::renderer::{PageInfo, RenderError};
|
use crate::renderer::{fill_default, PageInfo, RenderError};
|
||||||
|
|
||||||
const MAX_ITER: usize = 20;
|
const MAX_ITER: usize = 20;
|
||||||
|
|
||||||
pub struct Converter {
|
|
||||||
images: Vec<Option<PageInfo>>,
|
|
||||||
picker: Picker,
|
|
||||||
page: usize,
|
|
||||||
// once it reaches 20, we're done rendering images
|
|
||||||
iteration: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Converter {
|
|
||||||
pub fn new(picker: Picker) -> Self {
|
|
||||||
Self {
|
|
||||||
images: vec![],
|
|
||||||
picker,
|
|
||||||
page: 0,
|
|
||||||
iteration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_img(&mut self, page: PageInfo) {
|
|
||||||
let page_num = page.page;
|
|
||||||
self.images[page_num] = Some(page);
|
|
||||||
// just reset it to 0 so we grab this image again next time we try to get an image (if this
|
|
||||||
// image is in the current list of iterations, so to speak)
|
|
||||||
self.iteration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_n_pages(&mut self, pages: usize) {
|
|
||||||
self.images = Vec::with_capacity(pages);
|
|
||||||
for _ in 0..pages {
|
|
||||||
self.images.push(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.page = self.page.min(pages - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn go_to_page(&mut self, page: usize) {
|
|
||||||
self.page = page;
|
|
||||||
self.iteration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn change_page_by(&mut self, change: isize) {
|
|
||||||
self.page = (self.page as isize + change) as usize;
|
|
||||||
// We just reset iteration here. I think there's some heuristic we could do to place
|
|
||||||
// iteration exactly where it needs to be to render the next page, but trying to determine
|
|
||||||
// that caused me a lot of bugs, and only causes the slightest inefficiency (down below,
|
|
||||||
// when we skip `self.iteration` elements in an iterator), so it's like whatever
|
|
||||||
self.iteration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_next_img(&mut self) -> Option<ConversionResult> {
|
|
||||||
// In this fn, we return Poll::Pending and don't store a Waker 'cause this will be called
|
|
||||||
// in a loop with tokio::select, and in no other context. The pending that we return on one
|
|
||||||
// iteration will just be dropped/cancelled as soon as some other action happens, and then
|
|
||||||
// next time select is called, this'll be checked again, and then we might be in the right
|
|
||||||
// circumstance to return a Ready
|
|
||||||
if self.iteration >= MAX_ITER || self.images.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This kinda mimics the way the renderer alternates between going above and below the
|
|
||||||
// current page (within the bounds of how many pages there are) until we've done 20
|
|
||||||
let idx_start = self.page.saturating_sub(MAX_ITER / 2);
|
|
||||||
let idx_end = idx_start.saturating_add(MAX_ITER).min(self.images.len());
|
|
||||||
|
|
||||||
// then we go through all the indices available to us and find the first one that has an
|
|
||||||
// image available to steal
|
|
||||||
let (page_info, iteration) = (idx_start..self.page)
|
|
||||||
.interleave(self.page..idx_end)
|
|
||||||
.enumerate()
|
|
||||||
.skip(self.iteration)
|
|
||||||
.find_map(|(i_idx, p_idx)| self.images[p_idx].take().map(|p| (p, i_idx)))?;
|
|
||||||
|
|
||||||
let img_area = page_info.img_data.area;
|
|
||||||
|
|
||||||
let dyn_img =
|
|
||||||
match image::load_from_memory_with_format(&page_info.img_data.data, ImageFormat::Png) {
|
|
||||||
Ok(dt) => dt,
|
|
||||||
Err(e) =>
|
|
||||||
return Some(Err(RenderError::Render(format!(
|
|
||||||
"Couldn't convert Vec<u8> to DynamicImage: {e}"
|
|
||||||
)))),
|
|
||||||
};
|
|
||||||
|
|
||||||
// We don't actually want to Crop this image, but we've already
|
|
||||||
// verified (with the ImageSurface stuff) that the image is the correct
|
|
||||||
// size for the area given, so to save ratatui the work of having to
|
|
||||||
// resize it, we tell them to crop it to fit.
|
|
||||||
let txt_img = match self.picker.new_protocol(dyn_img, img_area, Resize::Crop) {
|
|
||||||
Ok(img) => img,
|
|
||||||
Err(e) =>
|
|
||||||
return Some(Err(RenderError::Render(format!(
|
|
||||||
"Couldn't convert DynamicImage to ratatui image: {e}"
|
|
||||||
)))),
|
|
||||||
};
|
|
||||||
|
|
||||||
// update the iteration to the iteration that we stole this image from
|
|
||||||
self.iteration = iteration;
|
|
||||||
|
|
||||||
Some(Ok(ConvertedPage {
|
|
||||||
page: txt_img,
|
|
||||||
num: page_info.page,
|
|
||||||
num_results: page_info.search_results
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ConvertedPage {
|
pub struct ConvertedPage {
|
||||||
pub page: Box<dyn Protocol>,
|
pub page: Box<dyn Protocol>,
|
||||||
pub num: usize,
|
pub num: usize,
|
||||||
pub num_results: usize
|
pub num_results: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConversionResult = Result<ConvertedPage, RenderError>;
|
pub enum ConverterMsg {
|
||||||
|
NumPages(usize),
|
||||||
|
GoToPage(usize),
|
||||||
|
AddImg(PageInfo)
|
||||||
|
}
|
||||||
|
|
||||||
impl Stream for Converter {
|
pub async fn run_conversion_loop(
|
||||||
type Item = ConversionResult;
|
sender: UnboundedSender<Result<ConvertedPage, RenderError>>,
|
||||||
|
mut receiver: UnboundedReceiver<ConverterMsg>,
|
||||||
|
mut picker: Picker
|
||||||
|
) -> Result<(), SendError<Result<ConvertedPage, RenderError>>> {
|
||||||
|
let mut images = vec![];
|
||||||
|
let mut page: usize = 0;
|
||||||
|
|
||||||
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn next_page(
|
||||||
match self.get_next_img() {
|
images: &mut [Option<PageInfo>],
|
||||||
Some(res) => Poll::Ready(Some(res)),
|
picker: &mut Picker,
|
||||||
None => Poll::Pending
|
page: usize,
|
||||||
|
iteration: &mut usize
|
||||||
|
) -> Result<Option<ConvertedPage>, RenderError> {
|
||||||
|
if images.is_empty() || *iteration >= MAX_ITER {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This kinda mimics the way the renderer alternates between going above and below the
|
||||||
|
// current page (within the bounds of how many pages there are) until we've done 20
|
||||||
|
let idx_start = page.saturating_sub(MAX_ITER / 2);
|
||||||
|
let idx_end = idx_start.saturating_add(MAX_ITER).min(images.len());
|
||||||
|
|
||||||
|
// then we go through all the indices available to us and find the first one that has an
|
||||||
|
// image available to steal
|
||||||
|
let Some((page_info, new_iter)) = (idx_start..page)
|
||||||
|
.interleave(page..idx_end)
|
||||||
|
.enumerate()
|
||||||
|
.skip(*iteration)
|
||||||
|
.find_map(|(i_idx, p_idx)| images[p_idx].take().map(|p| (p, i_idx)))
|
||||||
|
else {
|
||||||
|
return Ok(None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let img_area = page_info.img_data.area;
|
||||||
|
|
||||||
|
let dyn_img = image::load_from_memory_with_format(&page_info.img_data.data, ImageFormat::Png)
|
||||||
|
.map_err(|e| RenderError::Render(format!("Couldn't convert Vec<u8> to DynamicImage: {e}")))?;
|
||||||
|
|
||||||
|
// We don't actually want to Crop this image, but we've already
|
||||||
|
// verified (with the ImageSurface stuff) that the image is the correct
|
||||||
|
// size for the area given, so to save ratatui the work of having to
|
||||||
|
// resize it, we tell them to crop it to fit.
|
||||||
|
let txt_img = picker.new_protocol(dyn_img, img_area, Resize::Crop)
|
||||||
|
.map_err(|e| RenderError::Render(format!("Couldn't convert DynamicImage to ratatui image: {e}")))?;
|
||||||
|
|
||||||
|
// update the iteration to the iteration that we stole this image from
|
||||||
|
*iteration = new_iter;
|
||||||
|
|
||||||
|
Ok(Some(ConvertedPage {
|
||||||
|
page: txt_img,
|
||||||
|
num: page_info.page,
|
||||||
|
num_results: page_info.search_results
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_notif(
|
||||||
|
msg: ConverterMsg,
|
||||||
|
images: &mut Vec<Option<PageInfo>>,
|
||||||
|
page: &mut usize
|
||||||
|
) {
|
||||||
|
match msg {
|
||||||
|
ConverterMsg::AddImg(img) => {
|
||||||
|
let page_num = img.page;
|
||||||
|
images[page_num] = Some(img);
|
||||||
|
},
|
||||||
|
ConverterMsg::NumPages(n_pages) => {
|
||||||
|
fill_default(images, n_pages);
|
||||||
|
*page = (*page).min(n_pages - 1);
|
||||||
|
},
|
||||||
|
ConverterMsg::GoToPage(new_page) => *page = new_page,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
'outer: loop {
|
||||||
|
let mut iteration = 0;
|
||||||
|
loop {
|
||||||
|
match receiver.try_recv() {
|
||||||
|
Ok(msg) => {
|
||||||
|
handle_notif(msg, &mut images, &mut page);
|
||||||
|
continue 'outer;
|
||||||
|
},
|
||||||
|
Err(TryRecvError::Empty) => (),
|
||||||
|
Err(TryRecvError::Disconnected) => panic!("Disconnected :(")
|
||||||
|
}
|
||||||
|
|
||||||
|
match next_page(&mut images, &mut picker, page, &mut iteration) {
|
||||||
|
Ok(None) => break,
|
||||||
|
Ok(Some(img)) => sender.send(Ok(img))?,
|
||||||
|
Err(e) => sender.send(Err(e))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(msg) = receiver.recv().await else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_notif(msg, &mut images, &mut page);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-34
@@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
use std::{io::stdout, path::PathBuf, str::FromStr};
|
use std::{io::stdout, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use converter::{ConvertedPage, Converter};
|
use converter::{run_conversion_loop, ConvertedPage, ConverterMsg};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
execute,
|
execute,
|
||||||
terminal::{
|
terminal::{
|
||||||
disable_raw_mode, enable_raw_mode, EndSynchronizedUpdate, EnterAlternateScreen,
|
disable_raw_mode, enable_raw_mode, window_size, EndSynchronizedUpdate, EnterAlternateScreen, LeaveAlternateScreen
|
||||||
LeaveAlternateScreen
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
@@ -17,19 +16,20 @@ use ratatui::{backend::CrosstermBackend, Terminal};
|
|||||||
use ratatui_image::picker::Picker;
|
use ratatui_image::picker::Picker;
|
||||||
use renderer::{RenderInfo, RenderNotif};
|
use renderer::{RenderInfo, RenderNotif};
|
||||||
use tui::{InputAction, Tui};
|
use tui::{InputAction, Tui};
|
||||||
|
use futures_util::FutureExt;
|
||||||
|
|
||||||
mod converter;
|
mod converter;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod skip;
|
mod skip;
|
||||||
mod tui;
|
mod tui;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut args = std::env::args().skip(1);
|
let mut args = std::env::args().skip(1);
|
||||||
let file = args.next().ok_or("Program requires a file to process")?;
|
let file = args.next().ok_or("Program requires a file to process")?;
|
||||||
let path = PathBuf::from_str(&file)?.canonicalize()?;
|
let path = PathBuf::from_str(&file)?.canonicalize()?;
|
||||||
|
|
||||||
let (watch_tx, render_rx) = tokio::sync::mpsc::channel(1);
|
let (watch_tx, render_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||||
let tui_tx = watch_tx.clone();
|
let tui_tx = watch_tx.clone();
|
||||||
|
|
||||||
// we need to call this outside the recommended_watcher call because if we call it inside, that
|
// we need to call this outside the recommended_watcher call because if we call it inside, that
|
||||||
@@ -40,7 +40,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// then like the main thread has panicked or something, so it doesn't matter if this panics
|
// then like the main thread has panicked or something, so it doesn't matter if this panics
|
||||||
// as well
|
// as well
|
||||||
watch_tx
|
watch_tx
|
||||||
.blocking_send(renderer::RenderNotif::Reload)
|
.send(renderer::RenderNotif::Reload)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -49,20 +49,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
watcher.watch(&path, RecursiveMode::NonRecursive)?;
|
watcher.watch(&path, RecursiveMode::NonRecursive)?;
|
||||||
|
|
||||||
let file_path = format!("file://{}", path.clone().into_os_string().to_string_lossy());
|
let file_path = format!("file://{}", path.clone().into_os_string().to_string_lossy());
|
||||||
let (render_tx, mut tui_rx) = tokio::sync::mpsc::channel(1);
|
let (render_tx, mut tui_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
let window_size = window_size()?;
|
||||||
|
|
||||||
// We need to create `picker` on this thread because if we create it on the `renderer` thread,
|
// We need to create `picker` on this thread because if we create it on the `renderer` thread,
|
||||||
// it messes up something with user input. Input never makes it to the crossterm thing
|
// it messes up something with user input. Input never makes it to the crossterm thing
|
||||||
let mut picker = Picker::from_termios()?;
|
let mut picker = Picker::new((window_size.width / window_size.columns, window_size.height / window_size.rows));
|
||||||
picker.guess_protocol();
|
picker.guess_protocol();
|
||||||
|
|
||||||
// then we want to spawn off the rendering task
|
// then we want to spawn off the rendering task
|
||||||
// We need to use the thread::spawn API so that this exists in a thread not owned by tokio,
|
// We need to use the thread::spawn API so that this exists in a thread not owned by tokio,
|
||||||
// since the methods we call in `start_rendering` will panic if called in an async context
|
// since the methods we call in `start_rendering` will panic if called in an async context
|
||||||
std::thread::spawn(move || renderer::start_rendering(file_path, render_tx, render_rx));
|
std::thread::spawn(move || renderer::start_rendering(file_path, render_tx, render_rx, window_size));
|
||||||
|
|
||||||
let mut ev_stream = crossterm::event::EventStream::new();
|
let mut ev_stream = crossterm::event::EventStream::new();
|
||||||
|
|
||||||
|
let (to_converter, from_main) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
let (to_main, mut from_converter) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
tokio::spawn(run_conversion_loop(to_main, from_main, picker));
|
||||||
|
|
||||||
let file_name = path
|
let file_name = path
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|n| n.to_string_lossy())
|
.map(|n| n.to_string_lossy())
|
||||||
@@ -78,9 +85,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// document's pages, then it will return `None`, but still log to stderr with CRITICAL level),
|
// document's pages, then it will return `None`, but still log to stderr with CRITICAL level),
|
||||||
// so we want to just ignore all logging since this is a tui app.
|
// so we want to just ignore all logging since this is a tui app.
|
||||||
glib::log_set_writer_func(noop);
|
glib::log_set_writer_func(noop);
|
||||||
|
|
||||||
let mut converter = Converter::new(picker);
|
|
||||||
|
|
||||||
execute!(
|
execute!(
|
||||||
term.backend_mut(),
|
term.backend_mut(),
|
||||||
EnterAlternateScreen,
|
EnterAlternateScreen,
|
||||||
@@ -89,19 +93,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
let mut main_area = tui::Tui::main_layout(&term.get_frame());
|
let mut main_area = tui::Tui::main_layout(&term.get_frame());
|
||||||
tui_tx.send(RenderNotif::Area(main_area[1])).await?;
|
tui_tx.send(RenderNotif::Area(main_area[1]))?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut needs_redraw = tokio::select! {
|
let mut needs_redraw = tokio::select! {
|
||||||
Some(img_res) = converter.next() => {
|
|
||||||
match img_res {
|
|
||||||
Ok(ConvertedPage { page, num, num_results }) => tui.page_ready(page, num, num_results),
|
|
||||||
Err(e) => tui.show_error(e),
|
|
||||||
}
|
|
||||||
true
|
|
||||||
},
|
|
||||||
// First we check if we have any keystrokes
|
// First we check if we have any keystrokes
|
||||||
Some(ev) = ev_stream.next() => {
|
Some(ev) = ev_stream.next().fuse() => {
|
||||||
// If we can't get user input, just crash.
|
// If we can't get user input, just crash.
|
||||||
let ev = ev.expect("Couldn't get any user input");
|
let ev = ev.expect("Couldn't get any user input");
|
||||||
|
|
||||||
@@ -111,12 +108,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
match action {
|
match action {
|
||||||
InputAction::Redraw => (),
|
InputAction::Redraw => (),
|
||||||
InputAction::QuitApp => break,
|
InputAction::QuitApp => break,
|
||||||
InputAction::ChangePageBy(change) => converter.change_page_by(change),
|
|
||||||
InputAction::JumpingToPage(page) => {
|
InputAction::JumpingToPage(page) => {
|
||||||
tui_tx.send(RenderNotif::JumpToPage(page)).await?;
|
tui_tx.send(RenderNotif::JumpToPage(page))?;
|
||||||
converter.go_to_page(page);
|
to_converter.send(ConverterMsg::GoToPage(page))?;
|
||||||
},
|
},
|
||||||
InputAction::Search(term) => tui_tx.send(RenderNotif::Search(term)).await?,
|
InputAction::Search(term) => tui_tx.send(RenderNotif::Search(term))?,
|
||||||
};
|
};
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -126,37 +122,39 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
match renderer_msg {
|
match renderer_msg {
|
||||||
Ok(RenderInfo::NumPages(num)) => {
|
Ok(RenderInfo::NumPages(num)) => {
|
||||||
tui.set_n_pages(num);
|
tui.set_n_pages(num);
|
||||||
converter.set_n_pages(num);
|
to_converter.send(ConverterMsg::NumPages(num))?;
|
||||||
},
|
},
|
||||||
Ok(RenderInfo::Page(info)) => {
|
Ok(RenderInfo::Page(info)) => {
|
||||||
tui.got_num_results_on_page(info.page, info.search_results);
|
tui.got_num_results_on_page(info.page, info.search_results);
|
||||||
converter.add_img(info);
|
to_converter.send(ConverterMsg::AddImg(info))?;
|
||||||
},
|
},
|
||||||
Err(e) => tui.show_error(e),
|
Err(e) => tui.show_error(e),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
Some(img_res) = from_converter.recv() => {
|
||||||
|
match img_res {
|
||||||
|
Ok(ConvertedPage { page, num, num_results }) => tui.page_ready(page, num, num_results),
|
||||||
|
Err(e) => tui.show_error(e),
|
||||||
|
}
|
||||||
|
true
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_area = Tui::main_layout(&term.get_frame());
|
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[1])).await?;
|
tui_tx.send(RenderNotif::Area(main_area[1]))?;
|
||||||
needs_redraw = true;
|
needs_redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if needs_redraw {
|
if needs_redraw {
|
||||||
let mut end_update = false;
|
|
||||||
term.draw(|f| {
|
term.draw(|f| {
|
||||||
tui.render(f, &main_area, &mut end_update);
|
tui.render(f, &main_area);
|
||||||
// To be enabled when https://github.com/ratatui-org/ratatui/issues/1116 gets fixed
|
|
||||||
// f.bypass_diff = true;
|
|
||||||
})?;
|
})?;
|
||||||
if end_update {
|
|
||||||
execute!(stdout(), EndSynchronizedUpdate)?;
|
execute!(stdout(), EndSynchronizedUpdate)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
execute!(
|
execute!(
|
||||||
term.backend_mut(),
|
term.backend_mut(),
|
||||||
|
|||||||
+15
-13
@@ -1,8 +1,9 @@
|
|||||||
use cairo::{Antialias, Format};
|
use cairo::{Antialias, Format};
|
||||||
|
use crossterm::terminal::WindowSize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use poppler::{Color, Document, FindFlags, Page, Rectangle, SelectionStyle};
|
use poppler::{Color, Document, FindFlags, Page, Rectangle, SelectionStyle};
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use tokio::sync::mpsc::{error::TryRecvError, Receiver, Sender};
|
use tokio::sync::mpsc::{error::TryRecvError, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
||||||
pub enum RenderNotif {
|
pub enum RenderNotif {
|
||||||
Area(Rect),
|
Area(Rect),
|
||||||
@@ -41,7 +42,7 @@ struct PrevRender {
|
|||||||
contained_term: Option<bool>
|
contained_term: Option<bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
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.reserve(size.saturating_sub(vec.len()));
|
vec.reserve(size.saturating_sub(vec.len()));
|
||||||
for _ in 0..size {
|
for _ in 0..size {
|
||||||
@@ -60,8 +61,9 @@ fn fill_default<T: Default>(vec: &mut Vec<T>, size: usize) {
|
|||||||
// we're done.
|
// we're done.
|
||||||
pub fn start_rendering(
|
pub fn start_rendering(
|
||||||
path: String,
|
path: String,
|
||||||
sender: Sender<Result<RenderInfo, RenderError>>,
|
sender: UnboundedSender<Result<RenderInfo, RenderError>>,
|
||||||
mut receiver: Receiver<RenderNotif>
|
mut receiver: UnboundedReceiver<RenderNotif>,
|
||||||
|
size: WindowSize
|
||||||
) {
|
) {
|
||||||
// first, wait 'til we get told what the current starting area is so that we can set it to
|
// first, wait 'til we get told what the current starting area is so that we can set it to
|
||||||
// know what to render to
|
// know what to render to
|
||||||
@@ -80,7 +82,7 @@ pub fn start_rendering(
|
|||||||
'reload: loop {
|
'reload: loop {
|
||||||
let doc = match Document::from_file(&path, None) {
|
let doc = match Document::from_file(&path, None) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
sender.blocking_send(Err(RenderError::Doc(e))).unwrap();
|
sender.send(Err(RenderError::Doc(e))).unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(d) => d
|
Ok(d) => d
|
||||||
@@ -88,7 +90,7 @@ pub fn start_rendering(
|
|||||||
|
|
||||||
let n_pages = doc.n_pages() as usize;
|
let n_pages = doc.n_pages() as usize;
|
||||||
sender
|
sender
|
||||||
.blocking_send(Ok(RenderInfo::NumPages(n_pages)))
|
.send(Ok(RenderInfo::NumPages(n_pages)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// We're using this vec of bools to indicate which page numbers have already been rendered,
|
// We're using this vec of bools to indicate which page numbers have already been rendered,
|
||||||
@@ -190,7 +192,7 @@ pub fn start_rendering(
|
|||||||
// We know this is in range 'cause we're iterating over it
|
// We know this is in range 'cause we're iterating over it
|
||||||
let Some(page) = doc.page(num as i32) else {
|
let Some(page) = doc.page(num as i32) else {
|
||||||
sender
|
sender
|
||||||
.blocking_send(Err(RenderError::Render(format!(
|
.send(Err(RenderError::Render(format!(
|
||||||
"Couldn't get page {num} ({}) of doc?",
|
"Couldn't get page {num} ({}) of doc?",
|
||||||
num as i32
|
num as i32
|
||||||
))))
|
))))
|
||||||
@@ -202,7 +204,7 @@ pub fn start_rendering(
|
|||||||
rendered.successful && rendered.contained_term == Some(false);
|
rendered.successful && rendered.contained_term == Some(false);
|
||||||
|
|
||||||
// render the page
|
// render the page
|
||||||
match render_single_page(page, area, num, &search_term, rendered_with_no_results) {
|
match render_single_page(page, area, num, &search_term, rendered_with_no_results, &size) {
|
||||||
// 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,
|
||||||
// just continue. We're all good
|
// just continue. We're all good
|
||||||
Ok(None) => (),
|
Ok(None) => (),
|
||||||
@@ -212,10 +214,11 @@ pub fn start_rendering(
|
|||||||
// But we first need to store if we already rendered it correctly so that
|
// But we first need to store if we already rendered it correctly so that
|
||||||
// the next time we iterate through, it might see that we're already good
|
// the next time we iterate through, it might see that we're already good
|
||||||
rendered.contained_term = Some(img.search_results > 0);
|
rendered.contained_term = Some(img.search_results > 0);
|
||||||
sender.blocking_send(Ok(RenderInfo::Page(img))).unwrap()
|
rendered.successful = true;
|
||||||
|
sender.send(Ok(RenderInfo::Page(img))).unwrap()
|
||||||
},
|
},
|
||||||
// And if we got an error, then obviously we need to propagate that
|
// And if we got an error, then obviously we need to propagate that
|
||||||
Err(e) => sender.blocking_send(Err(RenderError::Render(e))).unwrap()
|
Err(e) => sender.send(Err(RenderError::Render(e))).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then once we've rendered all these pages, wait until we get another notification
|
// Then once we've rendered all these pages, wait until we get another notification
|
||||||
@@ -237,7 +240,8 @@ fn render_single_page(
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
page_num: usize,
|
page_num: usize,
|
||||||
search_term: &Option<String>,
|
search_term: &Option<String>,
|
||||||
already_rendered_no_results: bool
|
already_rendered_no_results: bool,
|
||||||
|
size: &WindowSize
|
||||||
) -> Result<Option<PageInfo>, String> {
|
) -> Result<Option<PageInfo>, String> {
|
||||||
let mut result_rects = search_term
|
let mut result_rects = search_term
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -252,8 +256,6 @@ fn render_single_page(
|
|||||||
|
|
||||||
// First, get the font size; the number of pixels (width x height) per font character (I
|
// First, get the font size; the number of pixels (width x height) per font character (I
|
||||||
// think; it's at least something like that) on this terminal screen.
|
// think; it's at least something like that) on this terminal screen.
|
||||||
let size =
|
|
||||||
crossterm::terminal::window_size().map_err(|e| format!("Couldn't get window size: {e}"))?;
|
|
||||||
let col_h = size.height / size.rows;
|
let col_h = size.height / size.rows;
|
||||||
let col_w = size.width / size.columns;
|
let col_w = size.width / size.columns;
|
||||||
|
|
||||||
|
|||||||
+2
-4
@@ -88,7 +88,7 @@ impl Tui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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<'_>, main_area: &[Rect], end_update: &mut bool) {
|
pub fn render(&mut self, frame: &mut Frame<'_>, main_area: &[Rect]) {
|
||||||
let top_block = Block::new()
|
let top_block = Block::new()
|
||||||
.padding(Padding {
|
.padding(Padding {
|
||||||
right: 2,
|
right: 2,
|
||||||
@@ -220,7 +220,6 @@ impl Tui {
|
|||||||
Self::render_loading_in(frame, img_area);
|
Self::render_loading_in(frame, img_area);
|
||||||
} else {
|
} else {
|
||||||
execute!(stdout(), BeginSynchronizedUpdate).unwrap();
|
execute!(stdout(), BeginSynchronizedUpdate).unwrap();
|
||||||
*end_update = true;
|
|
||||||
|
|
||||||
let total_width = page_widths.iter().map(|(_, w)| w).sum::<u16>();
|
let total_width = page_widths.iter().map(|(_, w)| w).sum::<u16>();
|
||||||
|
|
||||||
@@ -283,7 +282,7 @@ impl Tui {
|
|||||||
|
|
||||||
match self.page as isize - old as isize {
|
match self.page as isize - old as isize {
|
||||||
0 => None,
|
0 => None,
|
||||||
change => Some(InputAction::ChangePageBy(change))
|
_ => Some(InputAction::JumpingToPage(self.page))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,7 +516,6 @@ impl Tui {
|
|||||||
|
|
||||||
pub enum InputAction {
|
pub enum InputAction {
|
||||||
Redraw,
|
Redraw,
|
||||||
ChangePageBy(isize),
|
|
||||||
JumpingToPage(usize),
|
JumpingToPage(usize),
|
||||||
Search(String),
|
Search(String),
|
||||||
QuitApp
|
QuitApp
|
||||||
|
|||||||
Reference in New Issue
Block a user