From 761ce14125f0e4ecffbfaf3bf82b4b406f5aa769 Mon Sep 17 00:00:00 2001 From: lukeflo Date: Mon, 11 Nov 2024 22:13:32 +0100 Subject: impl popup for keybindings/messages --- Cargo.lock | 555 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/app.rs | 15 ++ src/bibiman.rs | 28 +++ src/bibiman/search.rs | 4 +- src/main.rs | 7 + src/tui.rs | 3 +- src/tui/commands.rs | 4 + src/tui/handler.rs | 197 ------------------ src/tui/popup.rs | 104 ++++++++++ src/tui/ui.rs | 36 +++- 11 files changed, 747 insertions(+), 208 deletions(-) delete mode 100644 src/tui/handler.rs create mode 100644 src/tui/popup.rs diff --git a/Cargo.lock b/Cargo.lock index 0ee7ace..83fbcb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bibiman" version = "0.6.4" @@ -77,6 +83,7 @@ dependencies = [ "biblatex", "color-eyre", "crossterm", + "doi", "editor-command", "futures", "itertools", @@ -87,6 +94,7 @@ dependencies = [ "tokio", "tokio-util", "tui-input", + "tui-popup", ] [[package]] @@ -302,6 +310,52 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive-new" version = "0.6.0" @@ -313,6 +367,29 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_setters" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlib" version = "0.5.2" @@ -322,6 +399,24 @@ dependencies = [ "libloading", ] +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[package]] +name = "doi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "036cd0c9915dacb495b6124b728d1f70a6a9135ac1b00e92f9d2b8ef05459c00" +dependencies = [ + "ureq", +] + [[package]] name = "downcast-rs" version = "1.2.1" @@ -445,6 +540,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.31" @@ -544,6 +648,17 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.1" @@ -582,6 +697,151 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "image" version = "0.25.2" @@ -676,6 +936,18 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.12" @@ -951,6 +1223,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "petgraph" version = "0.6.5" @@ -1049,6 +1327,21 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1068,6 +1361,38 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -1098,6 +1423,38 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1180,12 +1537,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -1208,6 +1583,12 @@ dependencies = [ "syn", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.79" @@ -1219,6 +1600,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tempfile" version = "3.13.0" @@ -1273,6 +1665,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -1395,6 +1797,18 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "tui-popup" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ee3d08800c83ba0a2efaec44d225bcc3f885f30e2b520a17e2cd962b7da6ab" +dependencies = [ + "derive-getters", + "derive_setters", + "document-features", + "ratatui", +] + [[package]] name = "unicode-ident" version = "1.0.13" @@ -1445,6 +1859,53 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "valuable" version = "0.1.0" @@ -1530,6 +1991,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "weezl" version = "0.1.8" @@ -1726,6 +2196,18 @@ dependencies = [ "wayland-protocols-wlr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x11rb" version = "0.13.1" @@ -1742,3 +2224,76 @@ name = "x11rb-protocol" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 8f8f9b1..485fe8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ arboard = { version = "3.4.1", features = ["wayland-data-control"] } biblatex = "0.10.0" color-eyre = "0.6.3" crossterm = { version = "0.28.1", features = ["event-stream"] } +doi = "0.3.0" editor-command = "0.1.1" futures = "0.3.30" itertools = "0.13.0" @@ -24,3 +25,4 @@ signal-hook = "0.3.17" tokio = { version = "1.39.3", features = ["full"] } tokio-util = "0.7.12" tui-input = "0.11.0" +tui-popup = "0.6.0" diff --git a/src/app.rs b/src/app.rs index fe47882..c256542 100644 --- a/src/app.rs +++ b/src/app.rs @@ -19,6 +19,7 @@ use crate::bibiman::CurrentArea; // use super::Event; use crate::cliargs::CLIArgs; use crate::tui::commands::{InputCmdAction, OpenRessource}; +use crate::tui::popup::PopupKind; use crate::tui::{self, Tui}; use crate::{bibiman::Bibiman, tui::commands::CmdAction}; use color_eyre::eyre::{Ok, Result}; @@ -68,6 +69,11 @@ impl App { // Event::Key(key_event) => handle_key_events(key_event, self, &mut tui)?, // Event::Mouse(_) => {} Event::Key(key_event) => { + if let Some(PopupKind::Message) = self.bibiman.popup_area.popup_kind { + self.bibiman.popup_area.popup_close_message() + } else if let Some(PopupKind::Help) = self.bibiman.popup_area.popup_kind { + self.bibiman.go_back() + } let command = if self.input_mode { CmdAction::Input(InputCmdAction::parse(key_event, &self.input)) } else { @@ -187,6 +193,8 @@ impl App { CmdAction::Confirm => { if let CurrentArea::TagArea = self.bibiman.current_area { self.bibiman.filter_for_tags(); + } else if let CurrentArea::PopupArea = self.bibiman.current_area { + self.bibiman.go_back(); } } CmdAction::SortList => { @@ -196,6 +204,10 @@ impl App { } CmdAction::YankItem => { Bibiman::yank_text(&self.bibiman.get_selected_citekey()); + self.bibiman.popup_area.popup_message( + "Yanked citekey to clipboard:", + self.bibiman.get_selected_citekey().to_string(), + ); } CmdAction::EditFile => { if let CurrentArea::EntryArea = self.bibiman.current_area { @@ -215,6 +227,9 @@ impl App { } OpenRessource::Note => {} }, + CmdAction::ShowHelp => { + self.bibiman.show_help(); + } CmdAction::Exit => { self.quit(); } diff --git a/src/bibiman.rs b/src/bibiman.rs index a573ee7..f3d9272 100644 --- a/src/bibiman.rs +++ b/src/bibiman.rs @@ -18,6 +18,7 @@ use crate::bibiman::entries::EntryTableColumn; use crate::bibiman::{bibisetup::*, search::BibiSearch}; use crate::cliargs::CLIArgs; +use crate::tui::popup::{PopupArea, PopupKind}; use crate::tui::Tui; use crate::{bibiman::entries::EntryTable, bibiman::keywords::TagList}; use arboard::Clipboard; @@ -40,6 +41,7 @@ pub enum CurrentArea { EntryArea, TagArea, SearchArea, + PopupArea, } // Check which area was active when popup set active @@ -69,6 +71,8 @@ pub struct Bibiman { pub current_area: CurrentArea, // mode for popup window pub former_area: Option, + // active popup + pub popup_area: PopupArea, } impl Bibiman { @@ -89,9 +93,33 @@ impl Bibiman { scroll_info: 0, current_area, former_area: None, + popup_area: PopupArea::default(), }) } + pub fn show_help(&mut self) { + if let CurrentArea::EntryArea = self.current_area { + self.former_area = Some(FormerArea::EntryArea); + } else if let CurrentArea::TagArea = self.current_area { + self.former_area = Some(FormerArea::TagArea); + } + self.popup_area.is_popup = true; + self.current_area = CurrentArea::PopupArea; + self.popup_area.popup_kind = Some(PopupKind::Help); + } + + pub fn go_back(&mut self) { + if let CurrentArea::PopupArea = self.current_area { + self.popup_area.is_popup = false; + self.popup_area.popup_kind = None; + if let Some(FormerArea::EntryArea) = self.former_area { + self.current_area = CurrentArea::EntryArea + } else if let Some(FormerArea::TagArea) = self.former_area { + self.current_area = CurrentArea::TagArea + } + }; + } + pub fn update_lists(&mut self) { self.main_biblio = BibiSetup::new(&self.main_bibfile); self.tag_list = TagList::new(self.main_biblio.keyword_list.clone()); diff --git a/src/bibiman/search.rs b/src/bibiman/search.rs index 362d0f9..896d1a4 100644 --- a/src/bibiman/search.rs +++ b/src/bibiman/search.rs @@ -139,8 +139,8 @@ mod tests { keywords: "hello, bye".to_string(), citekey: "author_1999".to_string(), abstract_text: "An abstract with multiple sentences. Here is the second".to_string(), - doi_url: "https://www.bibiman.org".to_string(), - filepath: "/home/file/path.pdf".to_string(), + doi_url: Some("https://www.bibiman.org".to_string()), + filepath: Some("/home/file/path.pdf".to_string()), subtitle: None, }; diff --git a/src/main.rs b/src/main.rs index c2c5121..8a78d62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,13 @@ pub mod cliargs; pub mod errorsetup; pub mod tui; +// Color indices +const MAIN_BLUE_COLOR_INDEX: u8 = 75; +const MAIN_PURPLE_COLOR_INDEX: u8 = 135; +const MAIN_GREEN_COLOR_INDEX: u8 = 29; +const TEXT_HIGHLIGHT_COLOR_INDEX: u8 = 254; +const TEXT_FG_COLOR_INDEX: u8 = 250; + #[tokio::main] async fn main() -> Result<()> { // Parse CLI arguments diff --git a/src/tui.rs b/src/tui.rs index 963abf9..ba43b2e 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -15,9 +15,8 @@ // along with this program. If not, see . ///// -// pub mod command; pub mod commands; -// pub mod handler; +pub mod popup; pub mod ui; use crate::App; diff --git a/src/tui/commands.rs b/src/tui/commands.rs index dc6b2c8..6a2ab13 100644 --- a/src/tui/commands.rs +++ b/src/tui/commands.rs @@ -67,6 +67,8 @@ pub enum CmdAction { Input(InputCmdAction), // Hexdump command. Exit, + // Show keybindings + ShowHelp, // Do nothing. Nothing, } @@ -148,6 +150,8 @@ impl From for CmdAction { KeyCode::Char('y') => Self::YankItem, // Sort entry table by selected col KeyCode::Char('s') => Self::SortList, + // Show help popup + KeyCode::Char('?') => Self::ShowHelp, // Else do nothing _ => Self::Nothing, } diff --git a/src/tui/handler.rs b/src/tui/handler.rs deleted file mode 100644 index ae2fd4d..0000000 --- a/src/tui/handler.rs +++ /dev/null @@ -1,197 +0,0 @@ -// bibiman - a TUI for managing BibLaTeX databases -// Copyright (C) 2024 lukeflo -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -///// - -use crate::bibiman::{Bibiman, CurrentArea}; -use crate::tui::Tui; -use crate::App; -use color_eyre::eyre::Result; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - -/// Handles the key events and updates the state of [`App`]. -pub fn handle_key_events(key_event: KeyEvent, app: &mut App, tui: &mut Tui) -> Result<()> { - // Keycodes activated for every area (high priority) - match key_event.code { - // Exit application on `ESC` or `q` - KeyCode::Char('Q') | KeyCode::Char('q') => { - app.quit(); - } - // Exit application on `Ctrl-C` - KeyCode::Char('c') | KeyCode::Char('C') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.quit(); - } - } - KeyCode::PageDown => { - app.bibiman.scroll_info_down(); - } - KeyCode::PageUp => { - app.bibiman.scroll_info_up(); - } - _ => {} - } - // Keycodes for specific areas - match app.bibiman.current_area { - // Keycodes for the tag area - CurrentArea::TagArea => match key_event.code { - KeyCode::Down => { - app.bibiman.select_next_tag(1); - } - KeyCode::Up => { - app.bibiman.select_previous_tag(1); - } - KeyCode::Char('j') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_down(); - } else { - app.bibiman.select_next_tag(1); - } - } - KeyCode::Char('k') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_up(); - } else { - app.bibiman.select_previous_tag(1); - } - } - KeyCode::Char('d') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_next_tag(5) - } - } - KeyCode::Char('u') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_previous_tag(5) - } - } - KeyCode::Char('g') | KeyCode::Home => { - app.bibiman.select_first_tag(); - } - KeyCode::Char('G') | KeyCode::End => { - app.bibiman.select_last_tag(); - } - KeyCode::Char('/') => { - app.bibiman.enter_search_area(); - } - KeyCode::Char('f') | KeyCode::Char('F') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.enter_search_area(); - } - } - KeyCode::Tab | KeyCode::BackTab => { - app.bibiman.toggle_area(); - } - KeyCode::Esc => { - app.bibiman.reset_current_list(); - } - KeyCode::Enter => { - app.bibiman.filter_for_tags(); - } - _ => {} - }, - // Keycodes for the entry area - CurrentArea::EntryArea => match key_event.code { - KeyCode::Down => { - app.bibiman.select_next_entry(1); - } - KeyCode::Up => { - app.bibiman.select_previous_entry(1); - } - KeyCode::Char('j') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_down(); - } else { - app.bibiman.select_next_entry(1); - } - } - KeyCode::Char('k') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_up(); - } else { - app.bibiman.select_previous_entry(1); - } - } - KeyCode::Char('d') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_next_entry(5); - } - } - KeyCode::Char('u') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_previous_entry(5); - } else { - app.bibiman.open_doi_url()?; - } - } - KeyCode::Char('g') | KeyCode::Home => { - app.bibiman.select_first_entry(); - } - KeyCode::Char('G') | KeyCode::End => { - app.bibiman.select_last_entry(); - } - KeyCode::Char('h') => { - app.bibiman.select_prev_column(); - } - KeyCode::Char('l') => { - app.bibiman.select_next_column(); - } - KeyCode::Char('s') => { - app.bibiman.entry_table.sort_entry_table(true); - } - KeyCode::Char('y') => { - Bibiman::yank_text(&app.bibiman.get_selected_citekey()); - } - KeyCode::Char('e') => { - app.bibiman.run_editor(tui)?; - } - KeyCode::Char('o') => { - app.bibiman.open_connected_file()?; - } - KeyCode::Char('/') => { - app.bibiman.enter_search_area(); - } - KeyCode::Char('f') | KeyCode::Char('F') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.enter_search_area(); - } - } - KeyCode::Tab | KeyCode::BackTab => { - app.bibiman.toggle_area(); - } - KeyCode::Esc => { - app.bibiman.reset_current_list(); - } - _ => {} - }, - // Keycodes for the search area (rendered in footer) - CurrentArea::SearchArea => match key_event.code { - KeyCode::Esc => { - app.bibiman.break_search(); - } - KeyCode::Enter => { - app.bibiman.confirm_search(); - } - KeyCode::Backspace => { - app.bibiman.search_pattern_pop(); - } - KeyCode::Char(search_pattern) => { - app.bibiman.search_pattern_push(search_pattern); - } - _ => {} - }, - } - Ok(()) -} diff --git a/src/tui/popup.rs b/src/tui/popup.rs new file mode 100644 index 0000000..60c58b4 --- /dev/null +++ b/src/tui/popup.rs @@ -0,0 +1,104 @@ +// bibiman - a TUI for managing BibLaTeX databases +// Copyright (C) 2024 lukeflo +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +///// + +use ratatui::{ + style::{Color, Stylize}, + text::{Line, Span, Text}, + widgets::ListState, +}; + +use crate::MAIN_PURPLE_COLOR_INDEX; + +#[derive(Debug)] +pub enum PopupKind { + Help, + Message, + Selection, +} + +#[derive(Debug)] +pub struct PopupArea { + pub is_popup: bool, + pub popup_kind: Option, + pub popup_message: String, + pub popup_list: Vec, + pub popup_state: ListState, +} + +impl Default for PopupArea { + fn default() -> Self { + PopupArea { + is_popup: false, + popup_kind: None, + popup_message: String::new(), + popup_list: Vec::new(), + popup_state: ListState::default(), + } + } +} + +impl PopupArea { + pub fn popup_help<'a>() -> Text<'a> { + let help = [ + ("j,k|↓,↑: ", "Select next/previous item"), + ("h,l|←,→: ", "Select next/previous column (Entry table)"), + ("g|Home: ", "Go to first item"), + ("G|End: ", "Go to last item"), + ("s: ", "sort entries by selected column (toggles reversed)"), + ("TAB: ", "Toggle areas (Entries, Keywords)"), + ("/|Ctrl+f: ", "Enter search mode"), + ("y: ", "yank/copy citekey of selected entry to clipboard"), + ("e: ", "Open editor at selected entry"), + ("o: ", "Open with selected entry associated PDF"), + ("u: ", "Open DOI/URL of selected entry"), + ("ESC: ", "Reset all lists/abort search"), + ("ENTER: ", "Confirm search/filter by selected keyword"), + ("q|Ctrl+c: ", "Quit bibiman"), + ]; + + let help_text: Vec> = help + .into_iter() + .map(|(keys, help)| { + Line::from(vec![ + Span::raw(keys) + .bold() + .fg(Color::Indexed(MAIN_PURPLE_COLOR_INDEX)), + Span::raw(help), + ]) + }) + .collect(); + + let text = Text::from(help_text); + text + } + + pub fn popup_message(&mut self, message: &str, object: String) { + if object.is_empty() { + self.popup_message = message.into(); + } else { + self.popup_message = format!("{} \"{}\"", message, object); + } + self.popup_kind = Some(PopupKind::Message); + self.is_popup = true; + } + + pub fn popup_close_message(&mut self) { + self.is_popup = false; + self.popup_message.clear(); + self.popup_kind = None + } +} diff --git a/src/tui/ui.rs b/src/tui/ui.rs index f2091a5..260f401 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -15,9 +15,15 @@ // along with this program. If not, see . ///// +use super::popup::PopupArea; use crate::bibiman::entries::EntryTableColumn; use crate::bibiman::{CurrentArea, FormerArea}; +use crate::tui::popup::PopupKind; use crate::App; +use crate::{ + MAIN_BLUE_COLOR_INDEX, MAIN_GREEN_COLOR_INDEX, MAIN_PURPLE_COLOR_INDEX, TEXT_FG_COLOR_INDEX, + TEXT_HIGHLIGHT_COLOR_INDEX, +}; use ratatui::layout::{Direction, Position}; use ratatui::widgets::Clear; use ratatui::Frame; @@ -31,13 +37,7 @@ use ratatui::{ ScrollbarOrientation, Table, Wrap, }, }; - -// Color indices -const MAIN_BLUE_COLOR_INDEX: u8 = 75; -const MAIN_PURPLE_COLOR_INDEX: u8 = 135; -const MAIN_GREEN_COLOR_INDEX: u8 = 29; -const TEXT_HIGHLIGHT_COLOR_INDEX: u8 = 254; -const TEXT_FG_COLOR_INDEX: u8 = 250; +use tui_popup::Popup; // Text colors const TEXT_FG_COLOR: Color = Color::Indexed(TEXT_FG_COLOR_INDEX); @@ -48,6 +48,7 @@ const MAIN_GREEN: Color = Color::Indexed(MAIN_GREEN_COLOR_INDEX); // Background colors const HEADER_FOOTER_BG: Color = Color::Indexed(235); +const POPUP_BG: Color = Color::Indexed(233); // Box styles // Keyword Box @@ -72,6 +73,8 @@ const BOX_SELECTED_TITLE_STYLE: Style = Style::new() const BOX_UNSELECTED_BORDER_STYLE: Style = Style::new().fg(TEXT_FG_COLOR); const BOX_UNSELECTED_TITLE_STYLE: Style = Style::new().fg(TEXT_FG_COLOR).add_modifier(Modifier::BOLD); +// Popup box +const POPUP_HELP_BOX: Style = Style::new().fg(TEXT_FG_COLOR).bg(POPUP_BG); // Entry table styles const ENTRY_SELECTED_ROW_STYLE: Style = Style::new() @@ -142,6 +145,25 @@ pub fn render_ui(app: &mut App, frame: &mut Frame) { render_selected_item(app, frame, info_area); render_taglist(app, frame, tag_area); render_file_info(app, frame, entry_info_area); + if app.bibiman.popup_area.is_popup { + render_popup(app, frame); + } +} + +pub fn render_popup(app: &mut App, frame: &mut Frame) { + let popup = if let Some(PopupKind::Help) = app.bibiman.popup_area.popup_kind { + Popup::new(PopupArea::popup_help()) + .title(" Keybindings ".bold().into_centered_line()) + .style(POPUP_HELP_BOX) + .border_style(Style::new().fg(MAIN_BLUE)) + } else if let Some(PopupKind::Message) = app.bibiman.popup_area.popup_kind { + Popup::new(Text::from(app.bibiman.popup_area.popup_message.as_str()).fg(MAIN_GREEN)) + .title(" Message ".bold().into_centered_line().fg(MAIN_GREEN)) + .style(POPUP_HELP_BOX) + } else { + panic!("No popup text detected") + }; + frame.render_widget(&popup, frame.area()) } pub fn render_header(frame: &mut Frame, rect: Rect) { -- cgit v1.2.3