Merged 0.6.1; added new DirectoryAlreadyExists; return new variant for SFTP/SCP

This commit is contained in:
veeso
2021-08-18 09:33:27 +02:00
40 changed files with 1034 additions and 1549 deletions

View File

@@ -1,7 +1,7 @@
# Changelog # Changelog
- [Changelog](#changelog) - [Changelog](#changelog)
- [0.7.0](#070) - [0.6.1](#061)
- [0.6.0](#060) - [0.6.0](#060)
- [0.5.1](#051) - [0.5.1](#051)
- [0.5.0](#050) - [0.5.0](#050)
@@ -20,14 +20,22 @@
--- ---
## 0.7.0 ## 0.6.1
Released on ?? Released on ??
> 🍁 Autumn update 2021 🍇 - Enhancements:
- Now that tui-rs supports title alignment, UI has been improved
- Added new `Directory already exists` variant for file transfer errors
- Bugfix: - Bugfix:
- Fixed [Issue 58](https://github.com/veeso/termscp/issues/58):When uploading a directory, create directory only if it doesn't exist - Fixed [Issue 58](https://github.com/veeso/termscp/issues/58):When uploading a directory, create directory only if it doesn't exist
- Dependencies:
- Updated `bitflags` to `1.3.2`
- Updated `bytesize` to `1.1.0`
- Updated `crossterm` to `0.20`
- Updated `open` to `2.0.1`
- Added `tui-realm-stdlib 0.6.0`
- Updated `tui-realm` to `0.6.0`
## 0.6.0 ## 0.6.0

178
Cargo.lock generated
View File

@@ -94,9 +94,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@@ -138,9 +138,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "bytesize" name = "bytesize"
version = "1.0.1" 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 = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70"
[[package]] [[package]]
name = "cassowary" name = "cassowary"
@@ -150,9 +150,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.68" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@@ -264,34 +264,34 @@ dependencies = [
[[package]] [[package]]
name = "crossterm" name = "crossterm"
version = "0.19.0" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crossterm_winapi", "crossterm_winapi",
"lazy_static",
"libc", "libc",
"mio", "mio",
"parking_lot 0.11.1", "parking_lot 0.11.1",
"signal-hook", "signal-hook",
"signal-hook-mio",
"winapi", "winapi",
] ]
[[package]] [[package]]
name = "crossterm_winapi" name = "crossterm_winapi"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]
[[package]] [[package]]
name = "crypto-mac" name = "crypto-mac"
version = "0.10.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
dependencies = [ dependencies = [
"generic-array", "generic-array",
"subtle", "subtle",
@@ -506,9 +506,9 @@ dependencies = [
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
@@ -521,9 +521,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.51" version = "0.3.52"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -548,9 +548,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.98" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
[[package]] [[package]]
name = "libssh2-sys" name = "libssh2-sys"
@@ -630,9 +630,9 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
[[package]] [[package]]
name = "matches" name = "matches"
version = "0.1.8" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]] [[package]]
name = "md-5" name = "md-5"
@@ -675,9 +675,9 @@ dependencies = [
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.7" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
@@ -790,11 +790,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]] [[package]]
name = "open" name = "open"
version = "1.7.0" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1711eb4b31ce4ad35b0f316d8dfba4fe5c7ad601c448446d84aae7a896627b20" checksum = "b46b233de7d83bc167fe43ae2dda3b5b84e80e09cceba581e4decb958a4896bf"
dependencies = [ dependencies = [
"which", "pathdiff",
"winapi", "winapi",
] ]
@@ -884,7 +884,7 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"instant", "instant",
"libc", "libc",
"redox_syscall 0.2.9", "redox_syscall 0.2.10",
"smallvec", "smallvec",
"winapi", "winapi",
] ]
@@ -895,6 +895,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cacbb3c4ff353b534a67fb8d7524d00229da4cb1dc8c79f4db96e375ab5b619" checksum = "3cacbb3c4ff353b534a67fb8d7524d00229da4cb1dc8c79f4db96e375ab5b619"
[[package]]
name = "pathdiff"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877630b3de15c0b64cc52f659345724fbf6bdad9bd9566699fc53688f3c34a34"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
@@ -927,9 +933,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.27" version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
@@ -1032,9 +1038,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.9" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@@ -1046,7 +1052,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [ dependencies = [
"getrandom 0.2.3", "getrandom 0.2.3",
"redox_syscall 0.2.9", "redox_syscall 0.2.10",
] ]
[[package]] [[package]]
@@ -1209,18 +1215,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.126" version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.126" version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1229,9 +1235,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.64" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -1253,13 +1259,23 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.1.17" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4"
dependencies = [ dependencies = [
"libc", "libc",
"mio", "mio",
"signal-hook-registry", "signal-hook",
] ]
[[package]] [[package]]
@@ -1314,15 +1330,15 @@ dependencies = [
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.4.0" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.73" version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1338,7 +1354,7 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"rand 0.8.4", "rand 0.8.4",
"redox_syscall 0.2.9", "redox_syscall 0.2.10",
"remove_dir_all", "remove_dir_all",
"winapi", "winapi",
] ]
@@ -1354,7 +1370,7 @@ dependencies = [
[[package]] [[package]]
name = "termscp" name = "termscp"
version = "0.6.0" version = "0.6.1"
dependencies = [ dependencies = [
"argh", "argh",
"bitflags", "bitflags",
@@ -1383,6 +1399,7 @@ dependencies = [
"textwrap", "textwrap",
"thiserror", "thiserror",
"toml", "toml",
"tui-realm-stdlib",
"tuirealm", "tuirealm",
"ureq", "ureq",
"users", "users",
@@ -1445,9 +1462,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.2.0" version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
dependencies = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@@ -1469,9 +1486,9 @@ dependencies = [
[[package]] [[package]]
name = "tui" name = "tui"
version = "0.15.0" version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "861d8f3ad314ede6219bcb2ab844054b1de279ee37a9bc38e3d606f9d3fb2a71" checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cassowary", "cassowary",
@@ -1481,15 +1498,24 @@ dependencies = [
] ]
[[package]] [[package]]
name = "tuirealm" name = "tui-realm-stdlib"
version = "0.4.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcbd06f2aa6a2424aaa245c10e8767fe3f0fee234ac8c144cb15eaf2ee37ce9" checksum = "6bff91e1cdc741a7487d8cb20ac038e5ba926a0ec97b0f2ea918ac75640b9da5"
dependencies = [
"textwrap",
"tuirealm",
"unicode-width",
]
[[package]]
name = "tuirealm"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634ad8e6a4b80ef032d31356b55964a995da5d05a9cf3a1bd134bae1ba7c197a"
dependencies = [ dependencies = [
"crossterm", "crossterm",
"textwrap",
"tui", "tui",
"unicode-width",
] ]
[[package]] [[package]]
@@ -1500,18 +1526,15 @@ checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.5" version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085"
dependencies = [
"matches",
]
[[package]] [[package]]
name = "unicode-linebreak" name = "unicode-linebreak"
version = "0.1.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05a31f45d18a3213b918019f78fe6a73a14ab896807f0aaf5622aa0684749455" checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f"
dependencies = [ dependencies = [
"regex", "regex",
] ]
@@ -1615,9 +1638,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.74" version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@@ -1625,9 +1648,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.74" version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@@ -1640,9 +1663,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.74" version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -1650,9 +1673,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.74" version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1663,15 +1686,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.74" version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.51" version = "0.3.52"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" checksum = "01c70a82d842c9979078c772d4a1344685045f1a5628f677c2b2eab4dd7d2696"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -1698,11 +1721,12 @@ dependencies = [
[[package]] [[package]]
name = "which" name = "which"
version = "4.1.0" version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9"
dependencies = [ dependencies = [
"either", "either",
"lazy_static",
"libc", "libc",
] ]

View File

@@ -11,7 +11,7 @@ license = "MIT"
name = "termscp" name = "termscp"
readme = "README.md" readme = "README.md"
repository = "https://github.com/veeso/termscp" repository = "https://github.com/veeso/termscp"
version = "0.6.0" version = "0.6.1"
[package.metadata.rpm] [package.metadata.rpm]
package = "termscp" package = "termscp"
@@ -28,11 +28,11 @@ path = "src/main.rs"
[dependencies] [dependencies]
argh = "0.1.5" argh = "0.1.5"
bitflags = "1.2.1" bitflags = "1.3.2"
bytesize = "1.0.1" bytesize = "1.1.0"
chrono = "0.4.19" chrono = "0.4.19"
content_inspector = "0.2.4" content_inspector = "0.2.4"
crossterm = "0.19.0" crossterm = "0.20"
dirs = "3.0.1" dirs = "3.0.1"
edit = "0.1.3" edit = "0.1.3"
ftp4 = { version = "4.0.2", features = [ "secure" ] } ftp4 = { version = "4.0.2", features = [ "secure" ] }
@@ -41,7 +41,7 @@ keyring = { version = "0.10.1", optional = true }
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.14" log = "0.4.14"
magic-crypt = "3.1.7" magic-crypt = "3.1.7"
open = "1.7.0" open = "2.0.1"
rand = "0.8.4" rand = "0.8.4"
regex = "1.5.4" regex = "1.5.4"
rpassword = "5.0.1" rpassword = "5.0.1"
@@ -52,7 +52,8 @@ tempfile = "3.1.0"
textwrap = "0.14.2" textwrap = "0.14.2"
thiserror = "^1.0.0" thiserror = "^1.0.0"
toml = "0.5.8" toml = "0.5.8"
tuirealm = { version = "0.4.3", features = [ "with-components" ] } tui-realm-stdlib = "0.6.0"
tuirealm = "0.6.0"
ureq = { version = "2.1.0", features = [ "json" ] } ureq = { version = "2.1.0", features = [ "json" ] }
whoami = "1.1.1" whoami = "1.1.1"
wildmatch = "2.0.0" wildmatch = "2.0.0"

View File

@@ -14,9 +14,9 @@
</p> </p>
<p align="center">Developed by <a href="https://veeso.github.io/">@veeso</a></p> <p align="center">Developed by <a href="https://veeso.github.io/">@veeso</a></p>
<p align="center">Current version: 0.6.0 (23/07/2021)</p> <p align="center">Current version: 0.6.1 (FIXME: 23/07/2021)</p>
[![License: MIT](https://img.shields.io/badge/License-MIT-teal.svg)](https://opensource.org/licenses/MIT) [![Stars](https://img.shields.io/github/stars/veeso/termscp.svg)](https://github.com/veeso/termscp) [![Downloads](https://img.shields.io/crates/d/termscp.svg)](https://crates.io/crates/termscp) [![Crates.io](https://img.shields.io/badge/crates.io-v0.6.0-orange.svg)](https://crates.io/crates/termscp) [![Docs](https://docs.rs/termscp/badge.svg)](https://docs.rs/termscp) [![License: MIT](https://img.shields.io/badge/License-MIT-teal.svg)](https://opensource.org/licenses/MIT) [![Stars](https://img.shields.io/github/stars/veeso/termscp.svg)](https://github.com/veeso/termscp) [![Downloads](https://img.shields.io/crates/d/termscp.svg)](https://crates.io/crates/termscp) [![Crates.io](https://img.shields.io/badge/crates.io-v0.6.1-orange.svg)](https://crates.io/crates/termscp) [![Docs](https://docs.rs/termscp/badge.svg)](https://docs.rs/termscp)
[![Linux](https://github.com/veeso/termscp/workflows/Linux/badge.svg)](https://github.com/veeso/termscp/actions) [![MacOs](https://github.com/veeso/termscp/workflows/MacOS/badge.svg)](https://github.com/veeso/termscp/actions) [![Windows](https://github.com/veeso/termscp/workflows/Windows/badge.svg)](https://github.com/veeso/termscp/actions) [![FreeBSD](https://github.com/veeso/termscp/workflows/FreeBSD/badge.svg)](https://github.com/veeso/termscp/actions) [![Coverage Status](https://coveralls.io/repos/github/veeso/termscp/badge.svg)](https://coveralls.io/github/veeso/termscp) [![Linux](https://github.com/veeso/termscp/workflows/Linux/badge.svg)](https://github.com/veeso/termscp/actions) [![MacOs](https://github.com/veeso/termscp/workflows/MacOS/badge.svg)](https://github.com/veeso/termscp/actions) [![Windows](https://github.com/veeso/termscp/workflows/Windows/badge.svg)](https://github.com/veeso/termscp/actions) [![FreeBSD](https://github.com/veeso/termscp/workflows/FreeBSD/badge.svg)](https://github.com/veeso/termscp/actions) [![Coverage Status](https://coveralls.io/repos/github/veeso/termscp/badge.svg)](https://coveralls.io/github/veeso/termscp)
@@ -122,13 +122,14 @@ Major termscp releases will now be seasonal, so expect 4 major updates during th
Planned for *🍁 Autumn update 🍇*: Planned for *🍁 Autumn update 🍇*:
- **Configuration profile for bookmarks 📚**: Basically this feature adds the possibility to have a specific setup for a certain host, instead of having only one global configuration.
- **Self-update ⬇️**: In order to increase users updating termscp, I want to provide the possibility to update termscp directly from application, when a new update is available. - **Self-update ⬇️**: In order to increase users updating termscp, I want to provide the possibility to update termscp directly from application, when a new update is available.
- **AWS S3 support 🪣**: I'll use `rust-s3` library to implement this. This is really big **Maybe** for the autumn update and might be moved to the Winter update. - **AWS S3 support 🪣**: I'll use `rust-s3` library to implement this. This is really big **Maybe** for the autumn update and might be moved to the Winter update.
- **Prompt before replacing files ☢️**: Possibility to configure whether a prompt should be displayed before replacing files.
Planned for *❄️ Winter update ⛄*: Planned for *❄️ Winter update ⛄*:
- **SMB Support 🎉**: This will require a long time to be implemented, since I'm currently working on a Rust native SMB library, since I don't want to add new C-bindings. ~~Fear the 🦚~~ - **SMB Support 🎉**: This will require a long time to be implemented, since I'm currently working on a Rust native SMB library, since I don't want to add new C-bindings. ~~Fear the 🦚~~
- **Configuration profile for bookmarks 📚**: Basically this feature adds the possibility to have a specific setup for a certain host, instead of having only one global configuration.
Along to new features, termscp developments is now focused on UX and performance improvements, so if you have any suggestion, feel free to open an issue. Along to new features, termscp developments is now focused on UX and performance improvements, so if you have any suggestion, feel free to open an issue.

View File

@@ -1,5 +1,5 @@
name: "termscp" name: "termscp"
version: 0.5.1 version: 0.6.1
origin: veeso/termscp origin: veeso/termscp
comment: "A feature rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP" comment: "A feature rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP"
desc: <<EOD desc: <<EOD

View File

@@ -8,7 +8,7 @@
# -f, -y, --force, --yes # -f, -y, --force, --yes
# Skip the confirmation prompt during installation # Skip the confirmation prompt during installation
TERMSCP_VERSION="0.6.0" TERMSCP_VERSION="0.6.1"
GITHUB_URL="https://github.com/veeso/termscp/releases/download/v${TERMSCP_VERSION}" GITHUB_URL="https://github.com/veeso/termscp/releases/download/v${TERMSCP_VERSION}"
DEB_URL="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_amd64.deb" DEB_URL="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_amd64.deb"
FREEBSD_URL="${GITHUB_URL}/termscp-${TERMSCP_VERSION}.txz" FREEBSD_URL="${GITHUB_URL}/termscp-${TERMSCP_VERSION}.txz"

View File

@@ -188,7 +188,7 @@ impl ActivityManager {
}; };
// If ft params is None, return None // If ft params is None, return None
let ft_params: &FileTransferParams = match ctx.ft_params() { let ft_params: &FileTransferParams = match ctx.ft_params() {
Some(ft_params) => &ft_params, Some(ft_params) => ft_params,
None => { None => {
error!("Failed to start FileTransferActivity: file transfer params is None"); error!("Failed to start FileTransferActivity: file transfer params is None");
return None; return None;

View File

@@ -44,13 +44,13 @@ pub struct SerializerError {
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum SerializerErrorKind { pub enum SerializerErrorKind {
#[error("Operation failed")] #[error("Operation failed")]
GenericError, Generic,
#[error("IO error")] #[error("IO error")]
IoError, Io,
#[error("Serialization error")] #[error("Serialization error")]
SerializationError, Serialization,
#[error("Syntax error")] #[error("Syntax error")]
SyntaxError, Syntax,
} }
impl SerializerError { impl SerializerError {
@@ -92,7 +92,7 @@ where
Ok(dt) => dt, Ok(dt) => dt,
Err(err) => { Err(err) => {
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::SerializationError, SerializerErrorKind::Serialization,
err.to_string(), err.to_string(),
)) ))
} }
@@ -102,7 +102,7 @@ where
match writable.write_all(data.as_bytes()) { match writable.write_all(data.as_bytes()) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => Err(SerializerError::new_ex( Err(err) => Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)), )),
} }
@@ -119,7 +119,7 @@ where
let mut data: String = String::new(); let mut data: String = String::new();
if let Err(err) = readable.read_to_string(&mut data) { if let Err(err) = readable.read_to_string(&mut data) {
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)); ));
} }
@@ -131,7 +131,7 @@ where
Ok(deserialized) Ok(deserialized)
} }
Err(err) => Err(SerializerError::new_ex( Err(err) => Err(SerializerError::new_ex(
SerializerErrorKind::SyntaxError, SerializerErrorKind::Syntax,
err.to_string(), err.to_string(),
)), )),
} }
@@ -154,11 +154,11 @@ mod tests {
#[test] #[test]
fn test_config_serialization_errors() { fn test_config_serialization_errors() {
let error: SerializerError = SerializerError::new(SerializerErrorKind::SyntaxError); let error: SerializerError = SerializerError::new(SerializerErrorKind::Syntax);
assert!(error.msg.is_none()); assert!(error.msg.is_none());
assert_eq!(format!("{}", error), String::from("Syntax error")); assert_eq!(format!("{}", error), String::from("Syntax error"));
let error: SerializerError = let error: SerializerError =
SerializerError::new_ex(SerializerErrorKind::SyntaxError, String::from("bad syntax")); SerializerError::new_ex(SerializerErrorKind::Syntax, String::from("bad syntax"));
assert!(error.msg.is_some()); assert!(error.msg.is_some());
assert_eq!( assert_eq!(
format!("{}", error), format!("{}", error),
@@ -166,20 +166,17 @@ mod tests {
); );
// Fmt // Fmt
assert_eq!( assert_eq!(
format!( format!("{}", SerializerError::new(SerializerErrorKind::Generic)),
"{}",
SerializerError::new(SerializerErrorKind::GenericError)
),
String::from("Operation failed") String::from("Operation failed")
); );
assert_eq!( assert_eq!(
format!("{}", SerializerError::new(SerializerErrorKind::IoError)), format!("{}", SerializerError::new(SerializerErrorKind::Io)),
String::from("IO error") String::from("IO error")
); );
assert_eq!( assert_eq!(
format!( format!(
"{}", "{}",
SerializerError::new(SerializerErrorKind::SerializationError) SerializerError::new(SerializerErrorKind::Serialization)
), ),
String::from("Serialization error") String::from("Serialization error")
); );

View File

@@ -189,7 +189,7 @@ impl FtpFileTransfer {
FsEntry::Directory(FsDirectory { FsEntry::Directory(FsDirectory {
name: p name: p
.file_name() .file_name()
.unwrap_or(&std::ffi::OsStr::new("")) .unwrap_or_else(|| std::ffi::OsStr::new(""))
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
abs_path: p.clone(), abs_path: p.clone(),
@@ -206,7 +206,7 @@ impl FtpFileTransfer {
false => FsEntry::File(FsFile { false => FsEntry::File(FsFile {
name: p name: p
.file_name() .file_name()
.unwrap_or(&std::ffi::OsStr::new("")) .unwrap_or_else(|| std::ffi::OsStr::new(""))
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
abs_path: p.clone(), abs_path: p.clone(),
@@ -659,7 +659,7 @@ impl FileTransfer for FtpFileTransfer {
// Remove recursively files // Remove recursively files
debug!("Removing {} entries from directory...", files.len()); debug!("Removing {} entries from directory...", files.len());
for file in files.iter() { for file in files.iter() {
if let Err(err) = self.remove(&file) { if let Err(err) = self.remove(file) {
return Err(FileTransferError::new_ex( return Err(FileTransferError::new_ex(
FileTransferErrorType::PexError, FileTransferErrorType::PexError,
err.to_string(), err.to_string(),

View File

@@ -44,7 +44,7 @@ pub use params::FileTransferParams;
/// ///
/// This enum defines the different transfer protocol available in termscp /// This enum defines the different transfer protocol available in termscp
#[derive(PartialEq, std::fmt::Debug, std::clone::Clone, Copy)] #[derive(PartialEq, Debug, std::clone::Clone, Copy)]
pub enum FileTransferProtocol { pub enum FileTransferProtocol {
Sftp, Sftp,
Scp, Scp,
@@ -54,7 +54,7 @@ pub enum FileTransferProtocol {
/// ## FileTransferError /// ## FileTransferError
/// ///
/// FileTransferError defines the possible errors available for a file transfer /// FileTransferError defines the possible errors available for a file transfer
#[derive(std::fmt::Debug)] #[derive(Debug)]
pub struct FileTransferError { pub struct FileTransferError {
code: FileTransferErrorType, code: FileTransferErrorType,
msg: Option<String>, msg: Option<String>,
@@ -84,6 +84,8 @@ pub enum FileTransferErrorType {
SslError, SslError,
#[error("Could not stat directory")] #[error("Could not stat directory")]
DirStatFailed, DirStatFailed,
#[error("Directory already exists")]
DirectoryAlreadyExists,
#[error("Failed to create file")] #[error("Failed to create file")]
FileCreateDenied, FileCreateDenied,
#[error("No such file or directory")] #[error("No such file or directory")]
@@ -180,7 +182,7 @@ pub trait FileTransfer {
/// ### mkdir /// ### mkdir
/// ///
/// Make directory /// Make directory
/// You must return error in case the directory already exists /// It MUSTN'T return error in case the directory already exists
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError>; fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError>;
/// ### remove /// ### remove

View File

@@ -169,7 +169,7 @@ impl ScpFileTransfer {
// Get symlink; PATH mustn't be equal to filename // Get symlink; PATH mustn't be equal to filename
let symlink: Option<Box<FsEntry>> = match symlink_path { let symlink: Option<Box<FsEntry>> = match symlink_path {
None => None, None => None,
Some(p) => match p.file_name().unwrap_or(&std::ffi::OsStr::new("")) Some(p) => match p.file_name().unwrap_or_else(|| std::ffi::OsStr::new(""))
== file_name.as_str() == file_name.as_str()
{ {
// If name is equal, don't stat path; otherwise it would get stuck // If name is equal, don't stat path; otherwise it would get stuck
@@ -339,7 +339,7 @@ impl FileTransfer for ScpFileTransfer {
// Try addresses // Try addresses
for socket_addr in socket_addresses.iter() { for socket_addr in socket_addresses.iter() {
debug!("Trying socket address {}", socket_addr); debug!("Trying socket address {}", socket_addr);
match TcpStream::connect_timeout(&socket_addr, Duration::from_secs(30)) { match TcpStream::connect_timeout(socket_addr, Duration::from_secs(30)) {
Ok(stream) => { Ok(stream) => {
debug!("{} succeded", socket_addr); debug!("{} succeded", socket_addr);
tcp = Some(stream); tcp = Some(stream);
@@ -643,13 +643,20 @@ impl FileTransfer for ScpFileTransfer {
/// ### mkdir /// ### mkdir
/// ///
/// Make directory /// Make directory
/// You must return error in case the directory already exists /// It MUSTN'T return error in case the directory already exists
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> { fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
match self.is_connected() { match self.is_connected() {
true => { true => {
let dir: PathBuf = Self::resolve(dir); let dir: PathBuf = Self::resolve(dir);
info!("Making directory {}", dir.display()); info!("Making directory {}", dir.display());
let p: PathBuf = self.wrkdir.clone(); let p: PathBuf = self.wrkdir.clone();
// If directory already exists, return Err
if let Ok(_) = self.stat(dir.as_path()) {
error!("Directory {} already exists", dir.display());
return Err(FileTransferError::new(
FileTransferErrorType::DirectoryAlreadyExists,
));
}
// Mkdir dir && echo 0 // Mkdir dir && echo 0
match self.perform_shell_cmd_with_path( match self.perform_shell_cmd_with_path(
p.as_path(), p.as_path(),

View File

@@ -282,7 +282,7 @@ impl FileTransfer for SftpFileTransfer {
// Try addresses // Try addresses
for socket_addr in socket_addresses.iter() { for socket_addr in socket_addresses.iter() {
debug!("Trying socket address {}", socket_addr); debug!("Trying socket address {}", socket_addr);
match TcpStream::connect_timeout(&socket_addr, Duration::from_secs(30)) { match TcpStream::connect_timeout(socket_addr, Duration::from_secs(30)) {
Ok(stream) => { Ok(stream) => {
tcp = Some(stream); tcp = Some(stream);
break; break;
@@ -559,6 +559,13 @@ impl FileTransfer for SftpFileTransfer {
Some(sftp) => { Some(sftp) => {
// Make directory // Make directory
let path: PathBuf = self.get_abs_path(PathBuf::from(dir).as_path()); let path: PathBuf = self.get_abs_path(PathBuf::from(dir).as_path());
// If directory already exists, return Err
if let Ok(_) = sftp.stat(path.as_path()) {
error!("Directory {} already exists", path.display());
return Err(FileTransferError::new(
FileTransferErrorType::DirectoryAlreadyExists,
));
}
info!("Making directory {}", path.display()); info!("Making directory {}", path.display());
match sftp.mkdir(path.as_path(), 0o775) { match sftp.mkdir(path.as_path(), 0o775) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@@ -602,7 +609,7 @@ impl FileTransfer for SftpFileTransfer {
// Get directory files // Get directory files
let directory_content: Vec<FsEntry> = self.list_dir(d.abs_path.as_path())?; let directory_content: Vec<FsEntry> = self.list_dir(d.abs_path.as_path())?;
for entry in directory_content.iter() { for entry in directory_content.iter() {
if let Err(err) = self.remove(&entry) { if let Err(err) = self.remove(entry) {
return Err(err); return Err(err);
} }
} }

View File

@@ -124,7 +124,7 @@ mod tests {
let explorer: FileExplorer = FileExplorerBuilder::new().build(); let explorer: FileExplorer = FileExplorerBuilder::new().build();
// Verify // Verify
assert!(!explorer.opts.intersects(ExplorerOpts::SHOW_HIDDEN_FILES)); assert!(!explorer.opts.intersects(ExplorerOpts::SHOW_HIDDEN_FILES));
assert_eq!(explorer.file_sorting, FileSorting::ByName); // Default assert_eq!(explorer.file_sorting, FileSorting::Name); // Default
assert_eq!(explorer.group_dirs, None); assert_eq!(explorer.group_dirs, None);
assert_eq!(explorer.stack_size, 16); assert_eq!(explorer.stack_size, 16);
} }
@@ -132,7 +132,7 @@ mod tests {
#[test] #[test]
fn test_fs_explorer_builder_new_all() { fn test_fs_explorer_builder_new_all() {
let explorer: FileExplorer = FileExplorerBuilder::new() let explorer: FileExplorer = FileExplorerBuilder::new()
.with_file_sorting(FileSorting::ByModifyTime) .with_file_sorting(FileSorting::ModifyTime)
.with_group_dirs(Some(GroupDirs::First)) .with_group_dirs(Some(GroupDirs::First))
.with_hidden_files(true) .with_hidden_files(true)
.with_stack_size(24) .with_stack_size(24)
@@ -140,7 +140,7 @@ mod tests {
.build(); .build();
// Verify // Verify
assert!(explorer.opts.intersects(ExplorerOpts::SHOW_HIDDEN_FILES)); assert!(explorer.opts.intersects(ExplorerOpts::SHOW_HIDDEN_FILES));
assert_eq!(explorer.file_sorting, FileSorting::ByModifyTime); // Default assert_eq!(explorer.file_sorting, FileSorting::ModifyTime); // Default
assert_eq!(explorer.group_dirs, Some(GroupDirs::First)); assert_eq!(explorer.group_dirs, Some(GroupDirs::First));
assert_eq!(explorer.stack_size, 24); assert_eq!(explorer.stack_size, 24);
} }

View File

@@ -52,10 +52,10 @@ bitflags! {
/// FileSorting defines the criteria for sorting files /// FileSorting defines the criteria for sorting files
#[derive(Copy, Clone, PartialEq, std::fmt::Debug)] #[derive(Copy, Clone, PartialEq, std::fmt::Debug)]
pub enum FileSorting { pub enum FileSorting {
ByName, Name,
ByModifyTime, ModifyTime,
ByCreationTime, CreationTime,
BySize, Size,
} }
/// ## GroupDirs /// ## GroupDirs
@@ -87,7 +87,7 @@ impl Default for FileExplorer {
wrkdir: PathBuf::from("/"), wrkdir: PathBuf::from("/"),
dirstack: VecDeque::with_capacity(16), dirstack: VecDeque::with_capacity(16),
stack_size: 16, stack_size: 16,
file_sorting: FileSorting::ByName, file_sorting: FileSorting::Name,
group_dirs: None, group_dirs: None,
opts: ExplorerOpts::empty(), opts: ExplorerOpts::empty(),
fmt: Formatter::default(), fmt: Formatter::default(),
@@ -237,10 +237,10 @@ impl FileExplorer {
fn sort(&mut self) { fn sort(&mut self) {
// Choose sorting method // Choose sorting method
match &self.file_sorting { match &self.file_sorting {
FileSorting::ByName => self.sort_files_by_name(), FileSorting::Name => self.sort_files_by_name(),
FileSorting::ByCreationTime => self.sort_files_by_creation_time(), FileSorting::CreationTime => self.sort_files_by_creation_time(),
FileSorting::ByModifyTime => self.sort_files_by_mtime(), FileSorting::ModifyTime => self.sort_files_by_mtime(),
FileSorting::BySize => self.sort_files_by_size(), FileSorting::Size => self.sort_files_by_size(),
} }
// Directories first (NOTE: MUST COME AFTER OTHER SORTING) // Directories first (NOTE: MUST COME AFTER OTHER SORTING)
// Group directories if necessary // Group directories if necessary
@@ -318,10 +318,10 @@ impl FileExplorer {
impl ToString for FileSorting { impl ToString for FileSorting {
fn to_string(&self) -> String { fn to_string(&self) -> String {
String::from(match self { String::from(match self {
FileSorting::ByCreationTime => "by_creation_time", FileSorting::CreationTime => "by_creation_time",
FileSorting::ByModifyTime => "by_mtime", FileSorting::ModifyTime => "by_mtime",
FileSorting::ByName => "by_name", FileSorting::Name => "by_name",
FileSorting::BySize => "by_size", FileSorting::Size => "by_size",
}) })
} }
} }
@@ -330,10 +330,10 @@ impl FromStr for FileSorting {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() { match s.to_ascii_lowercase().as_str() {
"by_creation_time" => Ok(FileSorting::ByCreationTime), "by_creation_time" => Ok(FileSorting::CreationTime),
"by_mtime" => Ok(FileSorting::ByModifyTime), "by_mtime" => Ok(FileSorting::ModifyTime),
"by_name" => Ok(FileSorting::ByName), "by_name" => Ok(FileSorting::Name),
"by_size" => Ok(FileSorting::BySize), "by_size" => Ok(FileSorting::Size),
_ => Err(()), _ => Err(()),
} }
} }
@@ -380,8 +380,8 @@ mod tests {
assert_eq!(explorer.wrkdir, PathBuf::from("/")); assert_eq!(explorer.wrkdir, PathBuf::from("/"));
assert_eq!(explorer.stack_size, 16); assert_eq!(explorer.stack_size, 16);
assert_eq!(explorer.group_dirs, None); assert_eq!(explorer.group_dirs, None);
assert_eq!(explorer.file_sorting, FileSorting::ByName); assert_eq!(explorer.file_sorting, FileSorting::Name);
assert_eq!(explorer.get_file_sorting(), FileSorting::ByName); assert_eq!(explorer.get_file_sorting(), FileSorting::Name);
} }
#[test] #[test]
@@ -459,7 +459,7 @@ mod tests {
make_fs_entry("Cargo.lock", false), make_fs_entry("Cargo.lock", false),
make_fs_entry("codecov.yml", false), make_fs_entry("codecov.yml", false),
]); ]);
explorer.sort_by(FileSorting::ByName); explorer.sort_by(FileSorting::Name);
// First entry should be "Cargo.lock" // First entry should be "Cargo.lock"
assert_eq!(explorer.files.get(0).unwrap().get_name(), "Cargo.lock"); assert_eq!(explorer.files.get(0).unwrap().get_name(), "Cargo.lock");
// Last should be "src/" // Last should be "src/"
@@ -475,7 +475,7 @@ mod tests {
let entry2: FsEntry = make_fs_entry("CODE_OF_CONDUCT.md", false); let entry2: FsEntry = make_fs_entry("CODE_OF_CONDUCT.md", false);
// Create files (files are then sorted by name) // Create files (files are then sorted by name)
explorer.set_files(vec![entry1, entry2]); explorer.set_files(vec![entry1, entry2]);
explorer.sort_by(FileSorting::ByModifyTime); explorer.sort_by(FileSorting::ModifyTime);
// First entry should be "CODE_OF_CONDUCT.md" // First entry should be "CODE_OF_CONDUCT.md"
assert_eq!( assert_eq!(
explorer.files.get(0).unwrap().get_name(), explorer.files.get(0).unwrap().get_name(),
@@ -494,7 +494,7 @@ mod tests {
let entry2: FsEntry = make_fs_entry("CODE_OF_CONDUCT.md", false); let entry2: FsEntry = make_fs_entry("CODE_OF_CONDUCT.md", false);
// Create files (files are then sorted by name) // Create files (files are then sorted by name)
explorer.set_files(vec![entry1, entry2]); explorer.set_files(vec![entry1, entry2]);
explorer.sort_by(FileSorting::ByCreationTime); explorer.sort_by(FileSorting::CreationTime);
// First entry should be "CODE_OF_CONDUCT.md" // First entry should be "CODE_OF_CONDUCT.md"
assert_eq!( assert_eq!(
explorer.files.get(0).unwrap().get_name(), explorer.files.get(0).unwrap().get_name(),
@@ -513,7 +513,7 @@ mod tests {
make_fs_entry("src/", true), make_fs_entry("src/", true),
make_fs_entry_with_size("CONTRIBUTING.md", false, 256), make_fs_entry_with_size("CONTRIBUTING.md", false, 256),
]); ]);
explorer.sort_by(FileSorting::BySize); explorer.sort_by(FileSorting::Size);
// Directory has size 4096 // Directory has size 4096
assert_eq!(explorer.files.get(0).unwrap().get_name(), "src/"); assert_eq!(explorer.files.get(0).unwrap().get_name(), "src/");
assert_eq!(explorer.files.get(1).unwrap().get_name(), "README.md"); assert_eq!(explorer.files.get(1).unwrap().get_name(), "README.md");
@@ -536,7 +536,7 @@ mod tests {
make_fs_entry("Cargo.lock", false), make_fs_entry("Cargo.lock", false),
make_fs_entry("codecov.yml", false), make_fs_entry("codecov.yml", false),
]); ]);
explorer.sort_by(FileSorting::ByName); explorer.sort_by(FileSorting::Name);
explorer.group_dirs_by(Some(GroupDirs::First)); explorer.group_dirs_by(Some(GroupDirs::First));
// First entry should be "docs" // First entry should be "docs"
assert_eq!(explorer.files.get(0).unwrap().get_name(), "docs/"); assert_eq!(explorer.files.get(0).unwrap().get_name(), "docs/");
@@ -563,7 +563,7 @@ mod tests {
make_fs_entry("Cargo.lock", false), make_fs_entry("Cargo.lock", false),
make_fs_entry("codecov.yml", false), make_fs_entry("codecov.yml", false),
]); ]);
explorer.sort_by(FileSorting::ByName); explorer.sort_by(FileSorting::Name);
explorer.group_dirs_by(Some(GroupDirs::Last)); explorer.group_dirs_by(Some(GroupDirs::Last));
// Last entry should be "src" // Last entry should be "src"
assert_eq!(explorer.files.get(8).unwrap().get_name(), "docs/"); assert_eq!(explorer.files.get(8).unwrap().get_name(), "docs/");
@@ -614,25 +614,25 @@ mod tests {
#[test] #[test]
fn test_fs_explorer_to_string_from_str_traits() { fn test_fs_explorer_to_string_from_str_traits() {
// File Sorting // File Sorting
assert_eq!(FileSorting::ByCreationTime.to_string(), "by_creation_time"); assert_eq!(FileSorting::CreationTime.to_string(), "by_creation_time");
assert_eq!(FileSorting::ByModifyTime.to_string(), "by_mtime"); assert_eq!(FileSorting::ModifyTime.to_string(), "by_mtime");
assert_eq!(FileSorting::ByName.to_string(), "by_name"); assert_eq!(FileSorting::Name.to_string(), "by_name");
assert_eq!(FileSorting::BySize.to_string(), "by_size"); assert_eq!(FileSorting::Size.to_string(), "by_size");
assert_eq!( assert_eq!(
FileSorting::from_str("by_creation_time").ok().unwrap(), FileSorting::from_str("by_creation_time").ok().unwrap(),
FileSorting::ByCreationTime FileSorting::CreationTime
); );
assert_eq!( assert_eq!(
FileSorting::from_str("by_mtime").ok().unwrap(), FileSorting::from_str("by_mtime").ok().unwrap(),
FileSorting::ByModifyTime FileSorting::ModifyTime
); );
assert_eq!( assert_eq!(
FileSorting::from_str("by_name").ok().unwrap(), FileSorting::from_str("by_name").ok().unwrap(),
FileSorting::ByName FileSorting::Name
); );
assert_eq!( assert_eq!(
FileSorting::from_str("by_size").ok().unwrap(), FileSorting::from_str("by_size").ok().unwrap(),
FileSorting::BySize FileSorting::Size
); );
assert!(FileSorting::from_str("omar").is_err()); assert!(FileSorting::from_str("omar").is_err());
// Group dirs // Group dirs

View File

@@ -56,6 +56,7 @@ extern crate regex;
extern crate ssh2; extern crate ssh2;
extern crate tempfile; extern crate tempfile;
extern crate textwrap; extern crate textwrap;
extern crate tui_realm_stdlib;
extern crate tuirealm; extern crate tuirealm;
extern crate ureq; extern crate ureq;
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]

View File

@@ -115,7 +115,7 @@ impl BookmarksClient {
if let Err(e) = key_storage.set_key(service_id, key.as_str()) { if let Err(e) = key_storage.set_key(service_id, key.as_str()) {
error!("Failed to set new key into storage: {}", e); error!("Failed to set new key into storage: {}", e);
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
format!("Could not write key to storage: {}", e), format!("Could not write key to storage: {}", e),
)); ));
} }
@@ -125,7 +125,7 @@ impl BookmarksClient {
_ => { _ => {
error!("Failed to get key from storage: {}", e); error!("Failed to get key from storage: {}", e);
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
format!("Could not get key from storage: {}", e), format!("Could not get key from storage: {}", e),
)); ));
} }
@@ -328,7 +328,7 @@ impl BookmarksClient {
Err(err) => { Err(err) => {
error!("Failed to write bookmarks: {}", err); error!("Failed to write bookmarks: {}", err);
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }
@@ -358,7 +358,7 @@ impl BookmarksClient {
Err(err) => { Err(err) => {
error!("Failed to read bookmarks: {}", err); error!("Failed to read bookmarks: {}", err);
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }
@@ -407,7 +407,7 @@ impl BookmarksClient {
match crypto::aes128_b64_decrypt(self.key.as_str(), secret) { match crypto::aes128_b64_decrypt(self.key.as_str(), secret) {
Ok(txt) => Ok(txt), Ok(txt) => Ok(txt),
Err(err) => Err(SerializerError::new_ex( Err(err) => Err(SerializerError::new_ex(
SerializerErrorKind::SyntaxError, SerializerErrorKind::Syntax,
err.to_string(), err.to_string(),
)), )),
} }

View File

@@ -76,7 +76,7 @@ impl ConfigClient {
if let Err(err) = create_dir(ssh_key_dir) { if let Err(err) = create_dir(ssh_key_dir) {
error!("Failed to create SSH key dir: {}", err); error!("Failed to create SSH key dir: {}", err);
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
format!( format!(
"Could not create SSH key directory \"{}\": {}", "Could not create SSH key directory \"{}\": {}",
ssh_key_dir.display(), ssh_key_dir.display(),
@@ -252,7 +252,7 @@ impl ConfigClient {
) -> Result<(), SerializerError> { ) -> Result<(), SerializerError> {
if self.degraded { if self.degraded {
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError, SerializerErrorKind::Generic,
String::from("Configuration won't be saved, since in degraded mode"), String::from("Configuration won't be saved, since in degraded mode"),
)); ));
} }
@@ -291,7 +291,7 @@ impl ConfigClient {
pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> { pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> {
if self.degraded { if self.degraded {
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError, SerializerErrorKind::Generic,
String::from("Configuration won't be saved, since in degraded mode"), String::from("Configuration won't be saved, since in degraded mode"),
)); ));
} }
@@ -351,7 +351,7 @@ impl ConfigClient {
pub fn write_config(&self) -> Result<(), SerializerError> { pub fn write_config(&self) -> Result<(), SerializerError> {
if self.degraded { if self.degraded {
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError, SerializerErrorKind::Generic,
String::from("Configuration won't be saved, since in degraded mode"), String::from("Configuration won't be saved, since in degraded mode"),
)); ));
} }
@@ -366,7 +366,7 @@ impl ConfigClient {
Err(err) => { Err(err) => {
error!("Failed to write configuration file: {}", err); error!("Failed to write configuration file: {}", err);
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }
@@ -379,7 +379,7 @@ impl ConfigClient {
pub fn read_config(&mut self) -> Result<(), SerializerError> { pub fn read_config(&mut self) -> Result<(), SerializerError> {
if self.degraded { if self.degraded {
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError, SerializerErrorKind::Generic,
String::from("Configuration won't be loaded, since in degraded mode"), String::from("Configuration won't be loaded, since in degraded mode"),
)); ));
} }
@@ -401,7 +401,7 @@ impl ConfigClient {
Err(err) => { Err(err) => {
error!("Failed to read configuration: {}", err); error!("Failed to read configuration: {}", err);
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }
@@ -432,7 +432,7 @@ impl ConfigClient {
/// Make serializer error from `std::io::Error` /// Make serializer error from `std::io::Error`
fn make_io_err(err: std::io::Error) -> Result<(), SerializerError> { fn make_io_err(err: std::io::Error) -> Result<(), SerializerError> {
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }

View File

@@ -116,7 +116,7 @@ impl ThemeProvider {
warn!("Configuration won't be loaded, since degraded; reloading default..."); warn!("Configuration won't be loaded, since degraded; reloading default...");
self.theme = Theme::default(); self.theme = Theme::default();
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError, SerializerErrorKind::Generic,
String::from("Can't access theme file"), String::from("Can't access theme file"),
)); ));
} }
@@ -139,7 +139,7 @@ impl ThemeProvider {
Err(err) => { Err(err) => {
error!("Failed to read theme: {}", err); error!("Failed to read theme: {}", err);
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }
@@ -153,7 +153,7 @@ impl ThemeProvider {
if self.degraded { if self.degraded {
warn!("Configuration won't be saved, since in degraded mode"); warn!("Configuration won't be saved, since in degraded mode");
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError, SerializerErrorKind::Generic,
String::from("Can't access theme file"), String::from("Can't access theme file"),
)); ));
} }
@@ -169,7 +169,7 @@ impl ThemeProvider {
Err(err) => { Err(err) => {
error!("Failed to write theme: {}", err); error!("Failed to write theme: {}", err);
Err(SerializerError::new_ex( Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::Io,
err.to_string(), err.to_string(),
)) ))
} }

View File

@@ -32,7 +32,7 @@ use crate::system::environment;
// Ext // Ext
use std::path::PathBuf; use std::path::PathBuf;
use tuirealm::components::{input::InputPropsBuilder, radio::RadioPropsBuilder}; use tui_realm_stdlib::{input::InputPropsBuilder, radio::RadioPropsBuilder};
use tuirealm::{Payload, PropsBuilder, Value}; use tuirealm::{Payload, PropsBuilder, Value};
impl AuthActivity { impl AuthActivity {
@@ -44,7 +44,7 @@ impl AuthActivity {
// Iterate over kyes // Iterate over kyes
let name: Option<&String> = self.bookmarks_list.get(idx); let name: Option<&String> = self.bookmarks_list.get(idx);
if let Some(name) = name { if let Some(name) = name {
bookmarks_cli.del_bookmark(&name); bookmarks_cli.del_bookmark(name);
// Write bookmarks // Write bookmarks
self.write_bookmarks(); self.write_bookmarks();
} }
@@ -60,7 +60,7 @@ impl AuthActivity {
if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() { if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() {
// Iterate over bookmarks // Iterate over bookmarks
if let Some(key) = self.bookmarks_list.get(idx) { if let Some(key) = self.bookmarks_list.get(idx) {
if let Some(bookmark) = bookmarks_cli.get_bookmark(&key) { if let Some(bookmark) = bookmarks_cli.get_bookmark(key) {
// Load parameters into components // Load parameters into components
self.load_bookmark_into_gui( self.load_bookmark_into_gui(
bookmark.0, bookmark.1, bookmark.2, bookmark.3, bookmark.4, bookmark.0, bookmark.1, bookmark.2, bookmark.3, bookmark.4,
@@ -104,7 +104,7 @@ impl AuthActivity {
if let Some(client) = self.bookmarks_client.as_mut() { if let Some(client) = self.bookmarks_client.as_mut() {
let name: Option<&String> = self.recents_list.get(idx); let name: Option<&String> = self.recents_list.get(idx);
if let Some(name) = name { if let Some(name) = name {
client.del_recent(&name); client.del_recent(name);
// Write bookmarks // Write bookmarks
self.write_bookmarks(); self.write_bookmarks();
} }

View File

@@ -35,7 +35,7 @@ use super::{
COMPONENT_TEXT_HELP, COMPONENT_TEXT_NEW_VERSION_NOTES, COMPONENT_TEXT_SIZE_ERR, COMPONENT_TEXT_HELP, COMPONENT_TEXT_NEW_VERSION_NOTES, COMPONENT_TEXT_SIZE_ERR,
}; };
use crate::ui::keymap::*; use crate::ui::keymap::*;
use tuirealm::components::InputPropsBuilder; use tui_realm_stdlib::InputPropsBuilder;
use tuirealm::{Msg, Payload, PropsBuilder, Update, Value}; use tuirealm::{Msg, Payload, PropsBuilder, Update, Value};
// -- update // -- update
@@ -52,53 +52,53 @@ impl Update for AuthActivity {
None => None, // Exit after None None => None, // Exit after None
Some(msg) => match msg { Some(msg) => match msg {
// Focus ( DOWN ) // Focus ( DOWN )
(COMPONENT_RADIO_PROTOCOL, &MSG_KEY_DOWN) => { (COMPONENT_RADIO_PROTOCOL, key) if key == &MSG_KEY_DOWN => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_ADDR); self.view.active(COMPONENT_INPUT_ADDR);
None None
} }
(COMPONENT_INPUT_ADDR, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_ADDR, key) if key == &MSG_KEY_DOWN => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_PORT); self.view.active(COMPONENT_INPUT_PORT);
None None
} }
(COMPONENT_INPUT_PORT, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_PORT, key) if key == &MSG_KEY_DOWN => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_USERNAME); self.view.active(COMPONENT_INPUT_USERNAME);
None None
} }
(COMPONENT_INPUT_USERNAME, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_USERNAME, key) if key == &MSG_KEY_DOWN => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_PASSWORD); self.view.active(COMPONENT_INPUT_PASSWORD);
None None
} }
(COMPONENT_INPUT_PASSWORD, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_PASSWORD, key) if key == &MSG_KEY_DOWN => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_RADIO_PROTOCOL); self.view.active(COMPONENT_RADIO_PROTOCOL);
None None
} }
// Focus ( UP ) // Focus ( UP )
(COMPONENT_INPUT_PASSWORD, &MSG_KEY_UP) => { (COMPONENT_INPUT_PASSWORD, key) if key == &MSG_KEY_UP => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_USERNAME); self.view.active(COMPONENT_INPUT_USERNAME);
None None
} }
(COMPONENT_INPUT_USERNAME, &MSG_KEY_UP) => { (COMPONENT_INPUT_USERNAME, key) if key == &MSG_KEY_UP => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_PORT); self.view.active(COMPONENT_INPUT_PORT);
None None
} }
(COMPONENT_INPUT_PORT, &MSG_KEY_UP) => { (COMPONENT_INPUT_PORT, key) if key == &MSG_KEY_UP => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_ADDR); self.view.active(COMPONENT_INPUT_ADDR);
None None
} }
(COMPONENT_INPUT_ADDR, &MSG_KEY_UP) => { (COMPONENT_INPUT_ADDR, key) if key == &MSG_KEY_UP => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_RADIO_PROTOCOL); self.view.active(COMPONENT_RADIO_PROTOCOL);
None None
} }
(COMPONENT_RADIO_PROTOCOL, &MSG_KEY_UP) => { (COMPONENT_RADIO_PROTOCOL, key) if key == &MSG_KEY_UP => {
// Give focus to port // Give focus to port
self.view.active(COMPONENT_INPUT_PASSWORD); self.view.active(COMPONENT_INPUT_PASSWORD);
None None
@@ -118,25 +118,25 @@ impl Update for AuthActivity {
} }
// Bookmarks commands // Bookmarks commands
// <RIGHT> / <LEFT> // <RIGHT> / <LEFT>
(COMPONENT_BOOKMARKS_LIST, &MSG_KEY_RIGHT) => { (COMPONENT_BOOKMARKS_LIST, key) if key == &MSG_KEY_RIGHT => {
// Give focus to recents // Give focus to recents
self.view.active(COMPONENT_RECENTS_LIST); self.view.active(COMPONENT_RECENTS_LIST);
None None
} }
(COMPONENT_RECENTS_LIST, &MSG_KEY_LEFT) => { (COMPONENT_RECENTS_LIST, key) if key == &MSG_KEY_LEFT => {
// Give focus to bookmarks // Give focus to bookmarks
self.view.active(COMPONENT_BOOKMARKS_LIST); self.view.active(COMPONENT_BOOKMARKS_LIST);
None None
} }
// <DEL | 'E'> // <DEL | 'E'>
(COMPONENT_BOOKMARKS_LIST, &MSG_KEY_DEL) (COMPONENT_BOOKMARKS_LIST, key)
| (COMPONENT_BOOKMARKS_LIST, &MSG_KEY_CHAR_E) => { if key == &MSG_KEY_DEL || key == &MSG_KEY_CHAR_E =>
{
// Show delete popup // Show delete popup
self.mount_bookmark_del_dialog(); self.mount_bookmark_del_dialog();
None None
} }
(COMPONENT_RECENTS_LIST, &MSG_KEY_DEL) (COMPONENT_RECENTS_LIST, key) if key == &MSG_KEY_DEL || key == &MSG_KEY_CHAR_E => {
| (COMPONENT_RECENTS_LIST, &MSG_KEY_CHAR_E) => {
// Show delete popup // Show delete popup
self.mount_recent_del_dialog(); self.mount_recent_del_dialog();
None None
@@ -203,67 +203,68 @@ impl Update for AuthActivity {
} }
} }
// <ESC> hide tab // <ESC> hide tab
(COMPONENT_RADIO_BOOKMARK_DEL_RECENT, &MSG_KEY_ESC) => { (COMPONENT_RADIO_BOOKMARK_DEL_RECENT, key) if key == &MSG_KEY_ESC => {
self.umount_recent_del_dialog(); self.umount_recent_del_dialog();
None None
} }
(COMPONENT_RADIO_BOOKMARK_DEL_RECENT, _) => None, (COMPONENT_RADIO_BOOKMARK_DEL_RECENT, _) => None,
(COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, &MSG_KEY_ESC) => { (COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, key) if key == &MSG_KEY_ESC => {
self.umount_bookmark_del_dialog(); self.umount_bookmark_del_dialog();
None None
} }
(COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, _) => None, (COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, _) => None,
// Error message // Error message
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) => { (COMPONENT_TEXT_ERROR, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
// Umount text error // Umount text error
self.umount_error(); self.umount_error();
None None
} }
(COMPONENT_TEXT_ERROR, _) => None, (COMPONENT_TEXT_ERROR, _) => None,
(COMPONENT_TEXT_NEW_VERSION_NOTES, &MSG_KEY_ESC) (COMPONENT_TEXT_NEW_VERSION_NOTES, key)
| (COMPONENT_TEXT_NEW_VERSION_NOTES, &MSG_KEY_ENTER) => { if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount release notes // Umount release notes
self.umount_release_notes(); self.umount_release_notes();
None None
} }
(COMPONENT_TEXT_NEW_VERSION_NOTES, _) => None, (COMPONENT_TEXT_NEW_VERSION_NOTES, _) => None,
// Help // Help
(_, &MSG_KEY_CTRL_H) => { (_, key) if key == &MSG_KEY_CTRL_H => {
// Show help // Show help
self.mount_help(); self.mount_help();
None None
} }
// Release notes // Release notes
(_, &MSG_KEY_CTRL_R) => { (_, key) if key == &MSG_KEY_CTRL_R => {
// Show release notes // Show release notes
self.mount_release_notes(); self.mount_release_notes();
None None
} }
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => { (COMPONENT_TEXT_HELP, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
// Hide text help // Hide text help
self.umount_help(); self.umount_help();
None None
} }
(COMPONENT_TEXT_HELP, _) => None, (COMPONENT_TEXT_HELP, _) => None,
// Enter setup // Enter setup
(_, &MSG_KEY_CTRL_C) => { (_, key) if key == &MSG_KEY_CTRL_C => {
self.exit_reason = Some(super::ExitReason::EnterSetup); self.exit_reason = Some(super::ExitReason::EnterSetup);
None None
} }
// Save bookmark; show popup // Save bookmark; show popup
(_, &MSG_KEY_CTRL_S) => { (_, key) if key == &MSG_KEY_CTRL_S => {
// Show popup // Show popup
self.mount_bookmark_save_dialog(); self.mount_bookmark_save_dialog();
// Give focus to bookmark name // Give focus to bookmark name
self.view.active(COMPONENT_INPUT_BOOKMARK_NAME); self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
None None
} }
(COMPONENT_INPUT_BOOKMARK_NAME, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_BOOKMARK_NAME, key) if key == &MSG_KEY_DOWN => {
// Give focus to pwd // Give focus to pwd
self.view.active(COMPONENT_RADIO_BOOKMARK_SAVE_PWD); self.view.active(COMPONENT_RADIO_BOOKMARK_SAVE_PWD);
None None
} }
(COMPONENT_RADIO_BOOKMARK_SAVE_PWD, &MSG_KEY_UP) => { (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, key) if key == &MSG_KEY_UP => {
// Give focus to pwd // Give focus to pwd
self.view.active(COMPONENT_INPUT_BOOKMARK_NAME); self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
None None
@@ -291,8 +292,9 @@ impl Update for AuthActivity {
self.view_bookmarks() self.view_bookmarks()
} }
// Hide save bookmark // Hide save bookmark
(COMPONENT_INPUT_BOOKMARK_NAME, &MSG_KEY_ESC) (COMPONENT_INPUT_BOOKMARK_NAME, key) | (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, key)
| (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, &MSG_KEY_ESC) => { if key == &MSG_KEY_ESC =>
{
// Umount popup // Umount popup
self.umount_bookmark_save_dialog(); self.umount_bookmark_save_dialog();
None None
@@ -307,45 +309,30 @@ impl Update for AuthActivity {
self.umount_quit(); self.umount_quit();
None None
} }
(COMPONENT_RADIO_QUIT, &MSG_KEY_ESC) => { (COMPONENT_RADIO_QUIT, key) if key == &MSG_KEY_ESC => {
self.umount_quit(); self.umount_quit();
None None
} }
// -- text size error; block everything // -- text size error; block everything
(COMPONENT_TEXT_SIZE_ERR, _) => None, (COMPONENT_TEXT_SIZE_ERR, _) => None,
// <TAB> bookmarks // <TAB> bookmarks
(COMPONENT_BOOKMARKS_LIST, &MSG_KEY_TAB) (COMPONENT_BOOKMARKS_LIST, key) | (COMPONENT_RECENTS_LIST, key)
| (COMPONENT_RECENTS_LIST, &MSG_KEY_TAB) => { if key == &MSG_KEY_TAB =>
{
// Give focus to address // Give focus to address
self.view.active(COMPONENT_INPUT_ADDR); self.view.active(COMPONENT_INPUT_ADDR);
None None
} }
// Any <TAB>, go to bookmarks // Any <TAB>, go to bookmarks
(_, &MSG_KEY_TAB) => { (_, key) if key == &MSG_KEY_TAB => {
self.view.active(COMPONENT_BOOKMARKS_LIST); self.view.active(COMPONENT_BOOKMARKS_LIST);
None None
} }
// On submit on any unhandled (connect) // On submit on any unhandled (connect)
(_, Msg::OnSubmit(_)) | (_, &MSG_KEY_ENTER) => { (_, Msg::OnSubmit(_)) => self.on_unhandled_submit(),
// Validate fields (_, key) if key == &MSG_KEY_ENTER => self.on_unhandled_submit(),
match self.collect_host_params() {
Err(err) => {
// mount error
self.mount_error(err);
}
Ok(params) => {
self.save_recent();
// Set file transfer params to context
self.context_mut().set_ftparams(params);
// Set exit reason
self.exit_reason = Some(super::ExitReason::Connect);
}
}
// Return None
None
}
// <ESC> => Quit // <ESC> => Quit
(_, &MSG_KEY_ESC) => { (_, key) if key == &MSG_KEY_ESC => {
self.mount_quit(); self.mount_quit();
None None
} }
@@ -367,4 +354,23 @@ impl AuthActivity {
} }
} }
} }
fn on_unhandled_submit(&mut self) -> Option<(String, Msg)> {
// Validate fields
match self.collect_host_params() {
Err(err) => {
// mount error
self.mount_error(err);
}
Ok(params) => {
self.save_recent();
// Set file transfer params to context
self.context_mut().set_ftparams(params);
// Set exit reason
self.exit_reason = Some(super::ExitReason::Connect);
}
}
// Return None
None
}
} }

View File

@@ -27,17 +27,15 @@
*/ */
// Locals // Locals
use super::{AuthActivity, Context, FileTransferProtocol}; use super::{AuthActivity, Context, FileTransferProtocol};
use crate::ui::components::{ use crate::ui::components::bookmark_list::{BookmarkList, BookmarkListPropsBuilder};
bookmark_list::{BookmarkList, BookmarkListPropsBuilder},
msgbox::{MsgBox, MsgBoxPropsBuilder},
};
use crate::utils::ui::draw_area_in; use crate::utils::ui::draw_area_in;
// Ext // Ext
use tuirealm::components::{ use tui_realm_stdlib::{
input::{Input, InputPropsBuilder}, input::{Input, InputPropsBuilder},
label::{Label, LabelPropsBuilder}, label::{Label, LabelPropsBuilder},
list::{List, ListPropsBuilder},
paragraph::{Paragraph, ParagraphPropsBuilder},
radio::{Radio, RadioPropsBuilder}, radio::{Radio, RadioPropsBuilder},
scrolltable::{ScrollTablePropsBuilder, Scrolltable},
span::{Span, SpanPropsBuilder}, span::{Span, SpanPropsBuilder},
textarea::{Textarea, TextareaPropsBuilder}, textarea::{Textarea, TextareaPropsBuilder},
}; };
@@ -47,7 +45,7 @@ use tuirealm::tui::{
widgets::{BorderType, Borders, Clear}, widgets::{BorderType, Borders, Clear},
}; };
use tuirealm::{ use tuirealm::{
props::{InputType, PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder}, props::{Alignment, InputType, PropsBuilder, TableBuilder, TextSpan},
Msg, Payload, Value, Msg, Payload, Value,
}; };
@@ -91,19 +89,11 @@ impl AuthActivity {
Box::new(Span::new( Box::new(Span::new(
SpanPropsBuilder::default() SpanPropsBuilder::default()
.with_spans(vec![ .with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(), TextSpan::new("Press ").bold(),
TextSpanBuilder::new("<CTRL+H>") TextSpan::new("<CTRL+H>").bold().fg(key_color),
.bold() TextSpan::new(" to show keybindings; ").bold(),
.with_foreground(key_color) TextSpan::new("<CTRL+C>").bold().fg(key_color),
.build(), TextSpan::new(" to enter setup").bold(),
TextSpanBuilder::new(" to show keybindings; ")
.bold()
.build(),
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(key_color)
.build(),
TextSpanBuilder::new(" to enter setup").bold().build(),
]) ])
.build(), .build(),
)), )),
@@ -118,16 +108,10 @@ impl AuthActivity {
.with_color(protocol_color) .with_color(protocol_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, protocol_color) .with_borders(Borders::ALL, BorderType::Rounded, protocol_color)
.with_options( .with_title("Protocol", Alignment::Left)
Some(String::from("Protocol")), .with_options(&["SFTP", "SCP", "FTP", "FTPS"])
vec![
String::from("SFTP"),
String::from("SCP"),
String::from("FTP"),
String::from("FTPS"),
],
)
.with_value(Self::protocol_enum_to_opt(default_protocol)) .with_value(Self::protocol_enum_to_opt(default_protocol))
.rewind(true)
.build(), .build(),
)), )),
); );
@@ -138,7 +122,7 @@ impl AuthActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(addr_color) .with_foreground(addr_color)
.with_borders(Borders::ALL, BorderType::Rounded, addr_color) .with_borders(Borders::ALL, BorderType::Rounded, addr_color)
.with_label(String::from("Remote host")) .with_label("Remote host", Alignment::Left)
.build(), .build(),
)), )),
); );
@@ -149,7 +133,7 @@ impl AuthActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(port_color) .with_foreground(port_color)
.with_borders(Borders::ALL, BorderType::Rounded, port_color) .with_borders(Borders::ALL, BorderType::Rounded, port_color)
.with_label(String::from("Port number")) .with_label("Port number", Alignment::Left)
.with_input(InputType::Number) .with_input(InputType::Number)
.with_input_len(5) .with_input_len(5)
.with_value(Self::get_default_port_for_protocol(default_protocol).to_string()) .with_value(Self::get_default_port_for_protocol(default_protocol).to_string())
@@ -163,7 +147,7 @@ impl AuthActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(username_color) .with_foreground(username_color)
.with_borders(Borders::ALL, BorderType::Rounded, username_color) .with_borders(Borders::ALL, BorderType::Rounded, username_color)
.with_label(String::from("Username")) .with_label("Username", Alignment::Left)
.build(), .build(),
)), )),
); );
@@ -174,7 +158,7 @@ impl AuthActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(password_color) .with_foreground(password_color)
.with_borders(Borders::ALL, BorderType::Rounded, password_color) .with_borders(Borders::ALL, BorderType::Rounded, password_color)
.with_label(String::from("Password")) .with_label("Password", Alignment::Left)
.with_input(InputType::Password) .with_input(InputType::Password)
.build(), .build(),
)), )),
@@ -193,7 +177,7 @@ impl AuthActivity {
.with_foreground(Color::Yellow) .with_foreground(Color::Yellow)
.with_spans(vec![ .with_spans(vec![
TextSpan::from("termscp "), TextSpan::from("termscp "),
TextSpanBuilder::new(version.as_str()).underlined().bold().build(), TextSpan::new(version.as_str()).underlined().bold(),
TextSpan::from(" is NOW available! Get it from <https://veeso.github.io/termscp/>; view release notes with <CTRL+R>"), TextSpan::from(" is NOW available! Get it from <https://veeso.github.io/termscp/>; view release notes with <CTRL+R>"),
]) ])
.build(), .build(),
@@ -208,7 +192,7 @@ impl AuthActivity {
.with_background(bookmarks_color) .with_background(bookmarks_color)
.with_foreground(Color::Black) .with_foreground(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, bookmarks_color) .with_borders(Borders::ALL, BorderType::Plain, bookmarks_color)
.with_bookmarks(Some(String::from("Bookmarks")), vec![]) .with_title("Bookmarks", Alignment::Left)
.build(), .build(),
)), )),
); );
@@ -220,7 +204,7 @@ impl AuthActivity {
.with_background(recents_color) .with_background(recents_color)
.with_foreground(Color::Black) .with_foreground(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, recents_color) .with_borders(Borders::ALL, BorderType::Plain, recents_color)
.with_bookmarks(Some(String::from("Recent connections")), vec![]) .with_title("Recent connections", Alignment::Left)
.build(), .build(),
)), )),
); );
@@ -426,7 +410,7 @@ impl AuthActivity {
let msg = self.view.update( let msg = self.view.update(
super::COMPONENT_BOOKMARKS_LIST, super::COMPONENT_BOOKMARKS_LIST,
BookmarkListPropsBuilder::from(props) BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("Bookmarks")), bookmarks) .with_bookmarks(bookmarks)
.build(), .build(),
); );
msg msg
@@ -464,7 +448,7 @@ impl AuthActivity {
let msg = self.view.update( let msg = self.view.update(
super::COMPONENT_RECENTS_LIST, super::COMPONENT_RECENTS_LIST,
BookmarkListPropsBuilder::from(props) BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("Recent connections")), bookmarks) .with_bookmarks(bookmarks)
.build(), .build(),
); );
msg msg
@@ -482,12 +466,13 @@ impl AuthActivity {
let err_color = self.theme().misc_error_dialog; let err_color = self.theme().misc_error_dialog;
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_ERROR, super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new( Box::new(Paragraph::new(
MsgBoxPropsBuilder::default() ParagraphPropsBuilder::default()
.with_foreground(err_color) .with_foreground(err_color)
.with_borders(Borders::ALL, BorderType::Thick, err_color) .with_borders(Borders::ALL, BorderType::Thick, err_color)
.bold() .bold()
.with_texts(None, vec![TextSpan::from(text)]) .with_text_alignment(Alignment::Center)
.with_texts(vec![TextSpan::from(text)])
.build(), .build(),
)), )),
); );
@@ -510,17 +495,15 @@ impl AuthActivity {
let err_color = self.theme().misc_error_dialog; let err_color = self.theme().misc_error_dialog;
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_SIZE_ERR, super::COMPONENT_TEXT_SIZE_ERR,
Box::new(MsgBox::new( Box::new(Paragraph::new(
MsgBoxPropsBuilder::default() ParagraphPropsBuilder::default()
.with_foreground(err_color) .with_foreground(err_color)
.with_borders(Borders::ALL, BorderType::Thick, err_color) .with_borders(Borders::ALL, BorderType::Thick, err_color)
.bold() .bold()
.with_texts( .with_texts(vec![TextSpan::from(
None, "termscp requires at least 24 lines of height to run",
vec![TextSpan::from( )])
"termscp requires at least 24 lines of height to run", .with_text_alignment(Alignment::Center)
)],
)
.build(), .build(),
)), )),
); );
@@ -548,10 +531,9 @@ impl AuthActivity {
.with_color(quit_color) .with_color(quit_color)
.with_borders(Borders::ALL, BorderType::Rounded, quit_color) .with_borders(Borders::ALL, BorderType::Rounded, quit_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_options( .with_title("Quit termscp?", Alignment::Center)
Some(String::from("Quit termscp?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -577,11 +559,10 @@ impl AuthActivity {
.with_color(warn_color) .with_color(warn_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, warn_color) .with_borders(Borders::ALL, BorderType::Rounded, warn_color)
.with_options( .with_title("Delete bookmark?", Alignment::Center)
Some(String::from("Delete bookmark?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")],
)
.with_value(1) .with_value(1)
.rewind(true)
.build(), .build(),
)), )),
); );
@@ -610,11 +591,10 @@ impl AuthActivity {
.with_color(warn_color) .with_color(warn_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, warn_color) .with_borders(Borders::ALL, BorderType::Rounded, warn_color)
.with_options( .with_title("Delete bookmark?", Alignment::Center)
Some(String::from("Delete bookmark?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")],
)
.with_value(1) .with_value(1)
.rewind(true)
.build(), .build(),
)), )),
); );
@@ -640,7 +620,7 @@ impl AuthActivity {
Box::new(Input::new( Box::new(Input::new(
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(save_color) .with_foreground(save_color)
.with_label(String::from("Save bookmark as…")) .with_label("Save bookmark as…", Alignment::Center)
.with_borders( .with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT, Borders::TOP | Borders::RIGHT | Borders::LEFT,
BorderType::Rounded, BorderType::Rounded,
@@ -659,10 +639,9 @@ impl AuthActivity {
BorderType::Rounded, BorderType::Rounded,
Color::Reset, Color::Reset,
) )
.with_options( .with_title("Save password?", Alignment::Center)
Some(String::from("Save password?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -685,77 +664,38 @@ impl AuthActivity {
let key_color = self.theme().misc_keys; let key_color = self.theme().misc_keys;
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_HELP, super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new( Box::new(List::new(
ScrollTablePropsBuilder::default() ListPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_highlighted_str(Some("?")) .with_highlighted_str(Some("?"))
.with_max_scroll_step(8) .with_max_scroll_step(8)
.scrollable(true)
.bold() .bold()
.with_table( .with_title("Help", Alignment::Center)
Some(String::from("Help")), .with_rows(
TableBuilder::default() TableBuilder::default()
.add_col( .add_col(TextSpan::new("<ESC>").bold().fg(key_color))
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Quit termscp")) .add_col(TextSpan::from(" Quit termscp"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<TAB>").bold().fg(key_color))
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Switch from form and bookmarks")) .add_col(TextSpan::from(" Switch from form and bookmarks"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<RIGHT/LEFT>").bold().fg(key_color))
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Switch bookmark tab")) .add_col(TextSpan::from(" Switch bookmark tab"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<UP/DOWN>").bold().fg(key_color))
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Move up/down in current tab")) .add_col(TextSpan::from(" Move up/down in current tab"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<ENTER>").bold().fg(key_color))
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Connect/Load bookmark")) .add_col(TextSpan::from(" Connect/Load bookmark"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<DEL|E>").bold().fg(key_color))
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Delete selected bookmark")) .add_col(TextSpan::from(" Delete selected bookmark"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+C>").bold().fg(key_color))
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Enter setup")) .add_col(TextSpan::from(" Enter setup"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+S>").bold().fg(key_color))
TextSpanBuilder::new("<CTRL+S>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Save bookmark")) .add_col(TextSpan::from(" Save bookmark"))
.build(), .build(),
) )
@@ -786,7 +726,8 @@ impl AuthActivity {
Box::new(Textarea::new( Box::new(Textarea::new(
TextareaPropsBuilder::default() TextareaPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_texts(Some(String::from("Release notes")), spans) .with_title("Release notes", Alignment::Center)
.with_texts(spans)
.build(), .build(),
)), )),
); );

View File

@@ -72,7 +72,7 @@ impl FileTransferActivity {
} }
pub(crate) fn local_remove_file(&mut self, entry: &FsEntry) { pub(crate) fn local_remove_file(&mut self, entry: &FsEntry) {
match self.host.remove(&entry) { match self.host.remove(entry) {
Ok(_) => { Ok(_) => {
// Log // Log
self.log( self.log(
@@ -94,7 +94,7 @@ impl FileTransferActivity {
} }
pub(crate) fn remote_remove_file(&mut self, entry: &FsEntry) { pub(crate) fn remote_remove_file(&mut self, entry: &FsEntry) {
match self.client.remove(&entry) { match self.client.remove(entry) {
Ok(_) => { Ok(_) => {
self.log( self.log(
LogLevel::Info, LogLevel::Info,

View File

@@ -142,7 +142,7 @@ impl Browser {
let mut builder: FileExplorerBuilder = FileExplorerBuilder::new(); let mut builder: FileExplorerBuilder = FileExplorerBuilder::new();
// Set common keys // Set common keys
builder builder
.with_file_sorting(FileSorting::ByName) .with_file_sorting(FileSorting::Name)
.with_stack_size(16) .with_stack_size(16)
.with_group_dirs(cli.get_group_dirs()) .with_group_dirs(cli.get_group_dirs())
.with_hidden_files(cli.get_show_hidden_files()); .with_hidden_files(cli.get_show_hidden_files());
@@ -154,7 +154,7 @@ impl Browser {
/// Build explorer reading from `ConfigClient`, for found result (has some differences) /// Build explorer reading from `ConfigClient`, for found result (has some differences)
fn build_found_explorer() -> FileExplorer { fn build_found_explorer() -> FileExplorer {
FileExplorerBuilder::new() FileExplorerBuilder::new()
.with_file_sorting(FileSorting::ByName) .with_file_sorting(FileSorting::Name)
.with_group_dirs(Some(GroupDirs::First)) .with_group_dirs(Some(GroupDirs::First))
.with_hidden_files(true) .with_hidden_files(true)
.with_stack_size(0) .with_stack_size(0)

View File

@@ -228,7 +228,7 @@ impl FileTransferActivity {
/// ///
/// Returns config client reference /// Returns config client reference
fn config(&self) -> &ConfigClient { fn config(&self) -> &ConfigClient {
&self.context().config() self.context().config()
} }
/// ### theme /// ### theme

View File

@@ -27,7 +27,7 @@
*/ */
// Locals // Locals
use super::{FileTransferActivity, LogLevel}; use super::{FileTransferActivity, LogLevel};
use crate::filetransfer::FileTransferError; use crate::filetransfer::{FileTransferError, FileTransferErrorType};
use crate::fs::{FsEntry, FsFile}; use crate::fs::{FsEntry, FsFile};
use crate::host::HostError; use crate::host::HostError;
use crate::utils::fmt::fmt_millis; use crate::utils::fmt::fmt_millis;
@@ -363,26 +363,33 @@ impl FileTransferActivity {
} }
} }
FsEntry::Directory(dir) => { FsEntry::Directory(dir) => {
// Check whether should create directory // Create directory on remote first
if self.client.list_dir(remote_path.as_path()).is_err() { match self.client.mkdir(remote_path.as_path()) {
match self.client.mkdir(remote_path.as_path()) { Ok(_) => {
Ok(_) => { self.log(
self.log( LogLevel::Info,
LogLevel::Info, format!("Created directory \"{}\"", remote_path.display()),
format!("Created directory \"{}\"", remote_path.display()), );
); }
} Err(err) if err.kind() == FileTransferErrorType::DirectoryAlreadyExists => {
Err(err) => { self.log(
self.log_and_alert( LogLevel::Info,
LogLevel::Error, format!(
format!( "Directory \"{}\" already exists on remote",
"Failed to create directory \"{}\": {}", remote_path.display()
remote_path.display(), ),
err );
), }
); Err(err) => {
return; self.log_and_alert(
} LogLevel::Error,
format!(
"Failed to create directory \"{}\": {}",
remote_path.display(),
err
),
);
return;
} }
} }
// Get files in dir // Get files in dir
@@ -395,7 +402,7 @@ impl FileTransferActivity {
break; break;
} }
// Send entry; name is always None after first call // Send entry; name is always None after first call
self.filetransfer_send_recurse(&entry, remote_path.as_path(), None); self.filetransfer_send_recurse(entry, remote_path.as_path(), None);
} }
} }
Err(err) => { Err(err) => {
@@ -729,7 +736,7 @@ impl FileTransferActivity {
// Receive entry; name is always None after first call // Receive entry; name is always None after first call
// Local path becomes local_dir_path // Local path becomes local_dir_path
self.filetransfer_recv_recurse( self.filetransfer_recv_recurse(
&entry, entry,
local_dir_path.as_path(), local_dir_path.as_path(),
None, None,
); );

View File

@@ -42,9 +42,9 @@ use crate::ui::components::{file_list::FileListPropsBuilder, logbox::LogboxProps
use crate::ui::keymap::*; use crate::ui::keymap::*;
use crate::utils::fmt::fmt_path_elide_ex; use crate::utils::fmt::fmt_path_elide_ex;
// externals // externals
use tui_realm_stdlib::progress_bar::ProgressBarPropsBuilder;
use tuirealm::{ use tuirealm::{
components::progress_bar::ProgressBarPropsBuilder, props::{Alignment, PropsBuilder, TableBuilder, TextSpan},
props::{PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder},
tui::style::Color, tui::style::Color,
Msg, Payload, Update, Value, Msg, Payload, Update, Value,
}; };
@@ -63,13 +63,13 @@ impl Update for FileTransferActivity {
None => None, // Exit after None None => None, // Exit after None
Some(msg) => match msg { Some(msg) => match msg {
// -- local tab // -- local tab
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_RIGHT) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_RIGHT => {
// Change tab // Change tab
self.view.active(COMPONENT_EXPLORER_REMOTE); self.view.active(COMPONENT_EXPLORER_REMOTE);
self.browser.change_tab(FileExplorerTab::Remote); self.browser.change_tab(FileExplorerTab::Remote);
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_BACKSPACE) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_BACKSPACE => {
// Go to previous directory // Go to previous directory
self.action_go_to_previous_local_dir(false); self.action_go_to_previous_local_dir(false);
if self.browser.sync_browsing { if self.browser.sync_browsing {
@@ -98,11 +98,11 @@ impl Update for FileTransferActivity {
None None
} }
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_SPACE) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_SPACE => {
self.action_local_send(); self.action_local_send();
self.update_remote_filelist() self.update_remote_filelist()
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_A) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_A => {
// Toggle hidden files // Toggle hidden files
self.local_mut().toggle_hidden_files(); self.local_mut().toggle_hidden_files();
// Update status bar // Update status bar
@@ -110,24 +110,24 @@ impl Update for FileTransferActivity {
// Reload file list component // Reload file list component
self.update_local_filelist() self.update_local_filelist()
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_I) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_I => {
if let SelectedEntry::One(file) = self.get_local_selected_entries() { if let SelectedEntry::One(file) = self.get_local_selected_entries() {
self.mount_file_info(&file); self.mount_file_info(&file);
} }
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_L) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_L => {
// Reload directory // Reload directory
self.reload_local_dir(); self.reload_local_dir();
// Reload file list component // Reload file list component
self.update_local_filelist() self.update_local_filelist()
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_O) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_O => {
self.action_edit_local_file(); self.action_edit_local_file();
// Reload file list component // Reload file list component
self.update_local_filelist() self.update_local_filelist()
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_U) => { (COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_U => {
self.action_go_to_local_upper_dir(false); self.action_go_to_local_upper_dir(false);
if self.browser.sync_browsing { if self.browser.sync_browsing {
let _ = self.update_remote_filelist(); let _ = self.update_remote_filelist();
@@ -136,7 +136,7 @@ impl Update for FileTransferActivity {
self.update_local_filelist() self.update_local_filelist()
} }
// -- remote tab // -- remote tab
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_LEFT) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_LEFT => {
// Change tab // Change tab
self.view.active(COMPONENT_EXPLORER_LOCAL); self.view.active(COMPONENT_EXPLORER_LOCAL);
self.browser.change_tab(FileExplorerTab::Local); self.browser.change_tab(FileExplorerTab::Local);
@@ -162,11 +162,11 @@ impl Update for FileTransferActivity {
None None
} }
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_SPACE) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_SPACE => {
self.action_remote_recv(); self.action_remote_recv();
self.update_local_filelist() self.update_local_filelist()
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_BACKSPACE) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_BACKSPACE => {
// Go to previous directory // Go to previous directory
self.action_go_to_previous_remote_dir(false); self.action_go_to_previous_remote_dir(false);
// If sync is enabled update local too // If sync is enabled update local too
@@ -176,7 +176,7 @@ impl Update for FileTransferActivity {
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_A) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_A => {
// Toggle hidden files // Toggle hidden files
self.remote_mut().toggle_hidden_files(); self.remote_mut().toggle_hidden_files();
// Update status bar // Update status bar
@@ -184,25 +184,25 @@ impl Update for FileTransferActivity {
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_I) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_I => {
if let SelectedEntry::One(file) = self.get_remote_selected_entries() { if let SelectedEntry::One(file) = self.get_remote_selected_entries() {
self.mount_file_info(&file); self.mount_file_info(&file);
} }
None None
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_L) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_L => {
// Reload directory // Reload directory
self.reload_remote_dir(); self.reload_remote_dir();
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_O) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_O => {
// Edit file // Edit file
self.action_edit_remote_file(); self.action_edit_remote_file();
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_U) => { (COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_U => {
self.action_go_to_remote_upper_dir(false); self.action_go_to_remote_upper_dir(false);
if self.browser.sync_browsing { if self.browser.sync_browsing {
let _ = self.update_local_filelist(); let _ = self.update_local_filelist();
@@ -211,64 +211,78 @@ impl Update for FileTransferActivity {
self.update_remote_filelist() self.update_remote_filelist()
} }
// -- common explorer keys // -- common explorer keys
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_B) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_B) => { if key == &MSG_KEY_CHAR_B =>
{
// Show sorting file // Show sorting file
self.mount_file_sorting(); self.mount_file_sorting();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_C) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_C) => { if key == &MSG_KEY_CHAR_C =>
{
self.mount_copy(); self.mount_copy();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_D) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_D) => { if key == &MSG_KEY_CHAR_D =>
{
self.mount_mkdir(); self.mount_mkdir();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_F) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_F) => { if key == &MSG_KEY_CHAR_F =>
{
self.mount_find_input(); self.mount_find_input();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_G) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_G) => { if key == &MSG_KEY_CHAR_G =>
{
self.mount_goto(); self.mount_goto();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_H) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_H) => { if key == &MSG_KEY_CHAR_H =>
{
self.mount_help(); self.mount_help();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_N) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_N) => { if key == &MSG_KEY_CHAR_N =>
{
self.mount_newfile(); self.mount_newfile();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_Q) (COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_Q) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_LOG_BOX, &MSG_KEY_CHAR_Q) => { | (COMPONENT_LOG_BOX, key)
if key == &MSG_KEY_CHAR_Q =>
{
self.mount_quit(); self.mount_quit();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_R) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_R) => { if key == &MSG_KEY_CHAR_R =>
{
// Mount rename // Mount rename
self.mount_rename(); self.mount_rename();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_S) (COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_S) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, &MSG_KEY_CHAR_S) => { | (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_S =>
{
// Mount save as // Mount save as
self.mount_saveas(); self.mount_saveas();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_V) (COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_V) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, &MSG_KEY_CHAR_V) => { | (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_V =>
{
// View // View
match self.browser.tab() { match self.browser.tab() {
FileExplorerTab::Local => self.action_open_local(), FileExplorerTab::Local => self.action_open_local(),
@@ -279,44 +293,49 @@ impl Update for FileTransferActivity {
} }
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_W) (COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_W) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, &MSG_KEY_CHAR_W) => { | (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_W =>
{
// Open with // Open with
self.mount_openwith(); self.mount_openwith();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_X) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_X) => { if key == &MSG_KEY_CHAR_X =>
{
// Mount exec // Mount exec
self.mount_exec(); self.mount_exec();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_Y) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_Y) => { if key == &MSG_KEY_CHAR_Y =>
{
// Toggle browser sync // Toggle browser sync
self.browser.toggle_sync_browsing(); self.browser.toggle_sync_browsing();
// Update status bar // Update status bar
self.refresh_remote_status_bar(); self.refresh_remote_status_bar();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_ESC) (COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_ESC) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_LOG_BOX, &MSG_KEY_ESC) => { | (COMPONENT_LOG_BOX, key)
if key == &MSG_KEY_ESC =>
{
self.mount_disconnect(); self.mount_disconnect();
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_DEL) (COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_E) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_DEL) | (COMPONENT_EXPLORER_FIND, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_E) if key == &MSG_KEY_CHAR_E || key == &MSG_KEY_DEL =>
| (COMPONENT_EXPLORER_FIND, &MSG_KEY_DEL) {
| (COMPONENT_EXPLORER_FIND, &MSG_KEY_CHAR_E) => {
self.mount_radio_delete(); self.mount_radio_delete();
None None
} }
// -- find result explorer // -- find result explorer
(COMPONENT_EXPLORER_FIND, &MSG_KEY_ESC) => { (COMPONENT_EXPLORER_FIND, key) if key == &MSG_KEY_ESC => {
// Umount find // Umount find
self.umount_find(); self.umount_find();
// Finalize find // Finalize find
@@ -337,7 +356,7 @@ impl Update for FileTransferActivity {
_ => None, _ => None,
} }
} }
(COMPONENT_EXPLORER_FIND, &MSG_KEY_SPACE) => { (COMPONENT_EXPLORER_FIND, key) if key == &MSG_KEY_SPACE => {
// Get entry // Get entry
self.action_find_transfer(None); self.action_find_transfer(None);
// Reload files // Reload files
@@ -349,18 +368,19 @@ impl Update for FileTransferActivity {
} }
} }
// -- switch to log // -- switch to log
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_TAB) (COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_TAB) => { if key == &MSG_KEY_TAB =>
{
self.view.active(COMPONENT_LOG_BOX); // Active log box self.view.active(COMPONENT_LOG_BOX); // Active log box
None None
} }
// -- Log box // -- Log box
(COMPONENT_LOG_BOX, &MSG_KEY_TAB) => { (COMPONENT_LOG_BOX, key) if key == &MSG_KEY_TAB => {
self.view.blur(); // Blur log box self.view.blur(); // Blur log box
None None
} }
// -- copy popup // -- copy popup
(COMPONENT_INPUT_COPY, &MSG_KEY_ESC) => { (COMPONENT_INPUT_COPY, key) if key == &MSG_KEY_ESC => {
self.umount_copy(); self.umount_copy();
None None
} }
@@ -383,7 +403,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_COPY, _) => None, (COMPONENT_INPUT_COPY, _) => None,
// -- exec popup // -- exec popup
(COMPONENT_INPUT_EXEC, &MSG_KEY_ESC) => { (COMPONENT_INPUT_EXEC, key) if key == &MSG_KEY_ESC => {
self.umount_exec(); self.umount_exec();
None None
} }
@@ -406,7 +426,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_EXEC, _) => None, (COMPONENT_INPUT_EXEC, _) => None,
// -- find popup // -- find popup
(COMPONENT_INPUT_FIND, &MSG_KEY_ESC) => { (COMPONENT_INPUT_FIND, key) if key == &MSG_KEY_ESC => {
self.umount_find_input(); self.umount_find_input();
None None
} }
@@ -441,7 +461,7 @@ impl Update for FileTransferActivity {
None None
} }
// -- goto popup // -- goto popup
(COMPONENT_INPUT_GOTO, &MSG_KEY_ESC) => { (COMPONENT_INPUT_GOTO, key) if key == &MSG_KEY_ESC => {
self.umount_goto(); self.umount_goto();
None None
} }
@@ -474,7 +494,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_GOTO, _) => None, (COMPONENT_INPUT_GOTO, _) => None,
// -- make directory // -- make directory
(COMPONENT_INPUT_MKDIR, &MSG_KEY_ESC) => { (COMPONENT_INPUT_MKDIR, key) if key == &MSG_KEY_ESC => {
self.umount_mkdir(); self.umount_mkdir();
None None
} }
@@ -494,7 +514,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_MKDIR, _) => None, (COMPONENT_INPUT_MKDIR, _) => None,
// -- new file // -- new file
(COMPONENT_INPUT_NEWFILE, &MSG_KEY_ESC) => { (COMPONENT_INPUT_NEWFILE, key) if key == &MSG_KEY_ESC => {
self.umount_newfile(); self.umount_newfile();
None None
} }
@@ -514,7 +534,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_NEWFILE, _) => None, (COMPONENT_INPUT_NEWFILE, _) => None,
// -- open with // -- open with
(COMPONENT_INPUT_OPEN_WITH, &MSG_KEY_ESC) => { (COMPONENT_INPUT_OPEN_WITH, key) if key == &MSG_KEY_ESC => {
self.umount_openwith(); self.umount_openwith();
None None
} }
@@ -531,7 +551,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_OPEN_WITH, _) => None, (COMPONENT_INPUT_OPEN_WITH, _) => None,
// -- rename // -- rename
(COMPONENT_INPUT_RENAME, &MSG_KEY_ESC) => { (COMPONENT_INPUT_RENAME, key) if key == &MSG_KEY_ESC => {
self.umount_rename(); self.umount_rename();
None None
} }
@@ -553,7 +573,7 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_RENAME, _) => None, (COMPONENT_INPUT_RENAME, _) => None,
// -- save as // -- save as
(COMPONENT_INPUT_SAVEAS, &MSG_KEY_ESC) => { (COMPONENT_INPUT_SAVEAS, key) if key == &MSG_KEY_ESC => {
self.umount_saveas(); self.umount_saveas();
None None
} }
@@ -578,15 +598,18 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_INPUT_SAVEAS, _) => None, (COMPONENT_INPUT_SAVEAS, _) => None,
// -- fileinfo // -- fileinfo
(COMPONENT_LIST_FILEINFO, &MSG_KEY_ENTER) (COMPONENT_LIST_FILEINFO, key) | (COMPONENT_LIST_FILEINFO, key)
| (COMPONENT_LIST_FILEINFO, &MSG_KEY_ESC) => { if key == &MSG_KEY_ENTER || key == &MSG_KEY_ESC =>
{
self.umount_file_info(); self.umount_file_info();
None None
} }
(COMPONENT_LIST_FILEINFO, _) => None, (COMPONENT_LIST_FILEINFO, _) => None,
// -- delete // -- delete
(COMPONENT_RADIO_DELETE, &MSG_KEY_ESC) (COMPONENT_RADIO_DELETE, key)
| (COMPONENT_RADIO_DELETE, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => { if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_radio_delete(); self.umount_radio_delete();
None None
} }
@@ -631,8 +654,10 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_RADIO_DELETE, _) => None, (COMPONENT_RADIO_DELETE, _) => None,
// -- disconnect // -- disconnect
(COMPONENT_RADIO_DISCONNECT, &MSG_KEY_ESC) (COMPONENT_RADIO_DISCONNECT, key)
| (COMPONENT_RADIO_DISCONNECT, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => { if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_disconnect(); self.umount_disconnect();
None None
} }
@@ -643,8 +668,10 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_RADIO_DISCONNECT, _) => None, (COMPONENT_RADIO_DISCONNECT, _) => None,
// -- quit // -- quit
(COMPONENT_RADIO_QUIT, &MSG_KEY_ESC) (COMPONENT_RADIO_QUIT, key)
| (COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => { if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_quit(); self.umount_quit();
None None
} }
@@ -655,18 +682,21 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_RADIO_QUIT, _) => None, (COMPONENT_RADIO_QUIT, _) => None,
// -- sorting // -- sorting
(COMPONENT_RADIO_SORTING, &MSG_KEY_ESC) (COMPONENT_RADIO_SORTING, key) if key == &MSG_KEY_ESC => {
| (COMPONENT_RADIO_SORTING, Msg::OnSubmit(_)) => { self.umount_file_sorting();
None
}
(COMPONENT_RADIO_SORTING, Msg::OnSubmit(_)) => {
self.umount_file_sorting(); self.umount_file_sorting();
None None
} }
(COMPONENT_RADIO_SORTING, Msg::OnChange(Payload::One(Value::Usize(mode)))) => { (COMPONENT_RADIO_SORTING, Msg::OnChange(Payload::One(Value::Usize(mode)))) => {
// Get sorting mode // Get sorting mode
let sorting: FileSorting = match mode { let sorting: FileSorting = match mode {
1 => FileSorting::ByModifyTime, 1 => FileSorting::ModifyTime,
2 => FileSorting::ByCreationTime, 2 => FileSorting::CreationTime,
3 => FileSorting::BySize, 3 => FileSorting::Size,
_ => FileSorting::ByName, _ => FileSorting::Name,
}; };
match self.browser.tab() { match self.browser.tab() {
FileExplorerTab::Local => self.local_mut().sort_by(sorting), FileExplorerTab::Local => self.local_mut().sort_by(sorting),
@@ -688,25 +718,31 @@ impl Update for FileTransferActivity {
} }
(COMPONENT_RADIO_SORTING, _) => None, (COMPONENT_RADIO_SORTING, _) => None,
// -- error // -- error
(COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) => { (COMPONENT_TEXT_ERROR, key) | (COMPONENT_TEXT_ERROR, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
self.umount_error(); self.umount_error();
None None
} }
(COMPONENT_TEXT_ERROR, _) => None, (COMPONENT_TEXT_ERROR, _) => None,
// -- fatal // -- fatal
(COMPONENT_TEXT_FATAL, &MSG_KEY_ESC) | (COMPONENT_TEXT_FATAL, &MSG_KEY_ENTER) => { (COMPONENT_TEXT_FATAL, key) | (COMPONENT_TEXT_FATAL, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
self.exit_reason = Some(super::ExitReason::Disconnect); self.exit_reason = Some(super::ExitReason::Disconnect);
None None
} }
(COMPONENT_TEXT_FATAL, _) => None, (COMPONENT_TEXT_FATAL, _) => None,
// -- help // -- help
(COMPONENT_TEXT_HELP, &MSG_KEY_ESC) | (COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) => { (COMPONENT_TEXT_HELP, key) | (COMPONENT_TEXT_HELP, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
self.umount_help(); self.umount_help();
None None
} }
(COMPONENT_TEXT_HELP, _) => None, (COMPONENT_TEXT_HELP, _) => None,
// -- progress bar // -- progress bar
(COMPONENT_PROGRESS_BAR_PARTIAL, &MSG_KEY_CTRL_C) => { (COMPONENT_PROGRESS_BAR_PARTIAL, key) if key == &MSG_KEY_CTRL_C => {
// Set transfer aborted to True // Set transfer aborted to True
self.transfer.abort(); self.transfer.abort();
None None
@@ -752,7 +788,8 @@ impl FileTransferActivity {
.collect(); .collect();
// Update // Update
let props = FileListPropsBuilder::from(props) let props = FileListPropsBuilder::from(props)
.with_files(Some(hostname), files) .with_files(files)
.with_title(hostname, Alignment::Left)
.build(); .build();
// Update // Update
self.view.update(super::COMPONENT_EXPLORER_LOCAL, props) self.view.update(super::COMPONENT_EXPLORER_LOCAL, props)
@@ -790,7 +827,8 @@ impl FileTransferActivity {
.collect(); .collect();
// Update // Update
let props = FileListPropsBuilder::from(props) let props = FileListPropsBuilder::from(props)
.with_files(Some(hostname), files) .with_files(files)
.with_title(hostname, Alignment::Left)
.build(); .build();
self.view.update(super::COMPONENT_EXPLORER_REMOTE, props) self.view.update(super::COMPONENT_EXPLORER_REMOTE, props)
} }
@@ -823,7 +861,7 @@ impl FileTransferActivity {
))) )))
.add_col(TextSpan::from(" [")) .add_col(TextSpan::from(" ["))
.add_col( .add_col(
TextSpanBuilder::new( TextSpan::new(
format!( format!(
"{:5}", "{:5}",
match record.level { match record.level {
@@ -834,16 +872,13 @@ impl FileTransferActivity {
) )
.as_str(), .as_str(),
) )
.with_foreground(fg) .fg(fg),
.build(),
) )
.add_col(TextSpan::from("]: ")) .add_col(TextSpan::from("]: "))
.add_col(TextSpan::from(record.msg.as_ref())); .add_col(TextSpan::from(record.msg.as_ref()));
} }
let table = table.build(); let table = table.build();
let props = LogboxPropsBuilder::from(props) let props = LogboxPropsBuilder::from(props).with_log(table).build();
.with_log(Some(String::from("Log")), table)
.build();
self.view.update(super::COMPONENT_LOG_BOX, props) self.view.update(super::COMPONENT_LOG_BOX, props)
} }
None => None, None => None,
@@ -852,9 +887,8 @@ impl FileTransferActivity {
pub(super) fn update_progress_bar(&mut self, filename: String) -> Option<(String, Msg)> { pub(super) fn update_progress_bar(&mut self, filename: String) -> Option<(String, Msg)> {
if let Some(props) = self.view.get_props(COMPONENT_PROGRESS_BAR_FULL) { if let Some(props) = self.view.get_props(COMPONENT_PROGRESS_BAR_FULL) {
let root_name: String = props.texts.title.as_deref().unwrap_or("").to_string();
let props = ProgressBarPropsBuilder::from(props) let props = ProgressBarPropsBuilder::from(props)
.with_texts(Some(root_name), self.transfer.full.to_string()) .with_label(self.transfer.full.to_string())
.with_progress(self.transfer.full.calc_progress()) .with_progress(self.transfer.full.calc_progress())
.build(); .build();
let _ = self.view.update(COMPONENT_PROGRESS_BAR_FULL, props); let _ = self.view.update(COMPONENT_PROGRESS_BAR_FULL, props);
@@ -862,7 +896,8 @@ impl FileTransferActivity {
match self.view.get_props(COMPONENT_PROGRESS_BAR_PARTIAL) { match self.view.get_props(COMPONENT_PROGRESS_BAR_PARTIAL) {
Some(props) => { Some(props) => {
let props = ProgressBarPropsBuilder::from(props) let props = ProgressBarPropsBuilder::from(props)
.with_texts(Some(filename), self.transfer.partial.to_string()) .with_title(filename, Alignment::Center)
.with_label(self.transfer.partial.to_string())
.with_progress(self.transfer.partial.calc_progress()) .with_progress(self.transfer.partial.calc_progress())
.build(); .build();
self.view.update(COMPONENT_PROGRESS_BAR_PARTIAL, props) self.view.update(COMPONENT_PROGRESS_BAR_PARTIAL, props)
@@ -889,7 +924,6 @@ impl FileTransferActivity {
match self.view.get_props(COMPONENT_EXPLORER_FIND) { match self.view.get_props(COMPONENT_EXPLORER_FIND) {
None => None, None => None,
Some(props) => { Some(props) => {
let title: String = props.texts.title.clone().unwrap_or_default();
// Prepare files // Prepare files
let files: Vec<String> = self let files: Vec<String> = self
.found() .found()
@@ -897,9 +931,7 @@ impl FileTransferActivity {
.iter_files() .iter_files()
.map(|x: &FsEntry| self.found().unwrap().fmt_file(x)) .map(|x: &FsEntry| self.found().unwrap().fmt_file(x))
.collect(); .collect();
let props = FileListPropsBuilder::from(props) let props = FileListPropsBuilder::from(props).with_files(files).build();
.with_files(Some(title), files)
.build();
self.view.update(COMPONENT_EXPLORER_FIND, props) self.view.update(COMPONENT_EXPLORER_FIND, props)
} }
} }

View File

@@ -32,7 +32,6 @@ use crate::fs::FsEntry;
use crate::ui::components::{ use crate::ui::components::{
file_list::{FileList, FileListPropsBuilder}, file_list::{FileList, FileListPropsBuilder},
logbox::{LogBox, LogboxPropsBuilder}, logbox::{LogBox, LogboxPropsBuilder},
msgbox::{MsgBox, MsgBoxPropsBuilder},
}; };
use crate::ui::store::Store; use crate::ui::store::Store;
use crate::utils::fmt::fmt_time; use crate::utils::fmt::fmt_time;
@@ -40,15 +39,16 @@ use crate::utils::ui::draw_area_in;
// Ext // Ext
use bytesize::ByteSize; use bytesize::ByteSize;
use std::path::PathBuf; use std::path::PathBuf;
use tuirealm::components::{ use tui_realm_stdlib::{
input::{Input, InputPropsBuilder}, input::{Input, InputPropsBuilder},
list::{List, ListPropsBuilder},
paragraph::{Paragraph, ParagraphPropsBuilder},
progress_bar::{ProgressBar, ProgressBarPropsBuilder}, progress_bar::{ProgressBar, ProgressBarPropsBuilder},
radio::{Radio, RadioPropsBuilder}, radio::{Radio, RadioPropsBuilder},
scrolltable::{ScrollTablePropsBuilder, Scrolltable},
span::{Span, SpanPropsBuilder}, span::{Span, SpanPropsBuilder},
table::{Table, TablePropsBuilder}, table::{Table, TablePropsBuilder},
}; };
use tuirealm::props::{PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder}; use tuirealm::props::{Alignment, PropsBuilder, TableBuilder, TextSpan};
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
style::Color, style::Color,
@@ -101,6 +101,7 @@ impl FileTransferActivity {
super::COMPONENT_LOG_BOX, super::COMPONENT_LOG_BOX,
Box::new(LogBox::new( Box::new(LogBox::new(
LogboxPropsBuilder::default() LogboxPropsBuilder::default()
.with_title("Log", Alignment::Left)
.with_background(log_background) .with_background(log_background)
.with_borders(Borders::ALL, BorderType::Plain, log_panel) .with_borders(Borders::ALL, BorderType::Plain, log_panel)
.build(), .build(),
@@ -383,12 +384,13 @@ impl FileTransferActivity {
let error_color = self.theme().misc_error_dialog; let error_color = self.theme().misc_error_dialog;
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_ERROR, super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new( Box::new(Paragraph::new(
MsgBoxPropsBuilder::default() ParagraphPropsBuilder::default()
.with_foreground(error_color) .with_foreground(error_color)
.with_borders(Borders::ALL, BorderType::Rounded, error_color) .with_borders(Borders::ALL, BorderType::Rounded, error_color)
.bold() .bold()
.with_texts(None, vec![TextSpan::from(text)]) .with_text_alignment(Alignment::Center)
.with_texts(vec![TextSpan::from(text)])
.build(), .build(),
)), )),
); );
@@ -408,12 +410,13 @@ impl FileTransferActivity {
let error_color = self.theme().misc_error_dialog; let error_color = self.theme().misc_error_dialog;
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_FATAL, super::COMPONENT_TEXT_FATAL,
Box::new(MsgBox::new( Box::new(Paragraph::new(
MsgBoxPropsBuilder::default() ParagraphPropsBuilder::default()
.with_foreground(error_color) .with_foreground(error_color)
.with_borders(Borders::ALL, BorderType::Rounded, error_color) .with_borders(Borders::ALL, BorderType::Rounded, error_color)
.bold() .bold()
.with_texts(None, vec![TextSpan::from(text)]) .with_text_alignment(Alignment::Center)
.with_texts(vec![TextSpan::from(text)])
.build(), .build(),
)), )),
); );
@@ -422,28 +425,26 @@ impl FileTransferActivity {
} }
pub(super) fn mount_wait(&mut self, text: &str) { pub(super) fn mount_wait(&mut self, text: &str) {
self.mount_wait_ex(text, false, Color::Reset); self.mount_wait_ex(text, Color::Reset);
} }
pub(super) fn mount_blocking_wait(&mut self, text: &str) { pub(super) fn mount_blocking_wait(&mut self, text: &str) {
self.mount_wait_ex(text, true, Color::Reset); self.mount_wait_ex(text, Color::Reset);
self.view(); self.view();
} }
fn mount_wait_ex(&mut self, text: &str, blink: bool, color: Color) { fn mount_wait_ex(&mut self, text: &str, color: Color) {
// Mount // Mount
let mut builder: MsgBoxPropsBuilder = MsgBoxPropsBuilder::default(); let mut builder: ParagraphPropsBuilder = ParagraphPropsBuilder::default();
builder builder
.with_foreground(color) .with_foreground(color)
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.bold() .bold()
.with_texts(None, vec![TextSpan::from(text)]); .with_text_alignment(Alignment::Center)
if blink { .with_texts(vec![TextSpan::from(text)]);
builder.blink();
}
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_WAIT, super::COMPONENT_TEXT_WAIT,
Box::new(MsgBox::new(builder.build())), Box::new(Paragraph::new(builder.build())),
); );
// Give focus to info // Give focus to info
self.view.active(super::COMPONENT_TEXT_WAIT); self.view.active(super::COMPONENT_TEXT_WAIT);
@@ -466,10 +467,9 @@ impl FileTransferActivity {
.with_color(quit_color) .with_color(quit_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, quit_color) .with_borders(Borders::ALL, BorderType::Rounded, quit_color)
.with_options( .with_title("Are you sure you want to quit?", Alignment::Center)
Some(String::from("Are you sure you want to quit?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -496,10 +496,9 @@ impl FileTransferActivity {
.with_color(quit_color) .with_color(quit_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, quit_color) .with_borders(Borders::ALL, BorderType::Rounded, quit_color)
.with_options( .with_title("Are you sure you want to disconnect?", Alignment::Center)
Some(String::from("Are you sure you want to disconnect?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -521,7 +520,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Copy file(s) to…")) .with_label("Copy file(s) to…", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -540,7 +539,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Execute command")) .with_label("Execute command", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -570,7 +569,10 @@ impl FileTransferActivity {
super::COMPONENT_EXPLORER_FIND, super::COMPONENT_EXPLORER_FIND,
Box::new(FileList::new( Box::new(FileList::new(
FileListPropsBuilder::default() FileListPropsBuilder::default()
.with_files(Some(format!("Search results for \"{}\"", search)), vec![]) .with_title(
format!("Search results for \"{}\"", search),
Alignment::Left,
)
.with_borders(Borders::ALL, BorderType::Plain, hg) .with_borders(Borders::ALL, BorderType::Plain, hg)
.with_highlight_color(hg) .with_highlight_color(hg)
.with_background(bg) .with_background(bg)
@@ -594,7 +596,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Search files by name")) .with_label("Search files by name", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -615,7 +617,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Change working directory")) .with_label("Change working directory", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -634,7 +636,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Insert directory name")) .with_label("Insert directory name", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -653,7 +655,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("New file name")) .with_label("New file name", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -672,7 +674,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Open file with…")) .with_label("Open file with…", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -691,7 +693,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Move file(s) to…")) .with_label("Move file(s) to…", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -710,7 +712,7 @@ impl FileTransferActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, input_color) .with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color) .with_foreground(input_color)
.with_label(String::from("Save as…")) .with_label("Save as…", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -735,7 +737,7 @@ impl FileTransferActivity {
BorderType::Rounded, BorderType::Rounded,
Color::Reset, Color::Reset,
) )
.with_texts(Some(root_name), String::new()) .with_title(root_name, Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -750,7 +752,7 @@ impl FileTransferActivity {
BorderType::Rounded, BorderType::Rounded,
Color::Reset, Color::Reset,
) )
.with_texts(Some(String::from("Please wait")), String::new()) .with_title("Please wait", Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -770,10 +772,10 @@ impl FileTransferActivity {
_ => panic!("You can't mount file sorting when in found result"), _ => panic!("You can't mount file sorting when in found result"),
}; };
let index: usize = match sorting { let index: usize = match sorting {
FileSorting::ByCreationTime => 2, FileSorting::CreationTime => 2,
FileSorting::ByModifyTime => 1, FileSorting::ModifyTime => 1,
FileSorting::ByName => 0, FileSorting::Name => 0,
FileSorting::BySize => 3, FileSorting::Size => 3,
}; };
self.view.mount( self.view.mount(
super::COMPONENT_RADIO_SORTING, super::COMPONENT_RADIO_SORTING,
@@ -782,15 +784,13 @@ impl FileTransferActivity {
.with_color(sorting_color) .with_color(sorting_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, sorting_color) .with_borders(Borders::ALL, BorderType::Rounded, sorting_color)
.with_options( .with_title("Sort files by", Alignment::Center)
Some(String::from("Sort files by")), .with_options(&[
vec![ String::from("Name"),
String::from("Name"), String::from("Modify time"),
String::from("Modify time"), String::from("Creation time"),
String::from("Creation time"), String::from("Size"),
String::from("Size"), ])
],
)
.with_value(index) .with_value(index)
.build(), .build(),
)), )),
@@ -811,11 +811,10 @@ impl FileTransferActivity {
.with_color(warn_color) .with_color(warn_color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, warn_color) .with_borders(Borders::ALL, BorderType::Plain, warn_color)
.with_options( .with_title("Delete file", Alignment::Center)
Some(String::from("Delete file")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")],
)
.with_value(1) .with_value(1)
.rewind(true)
.build(), .build(),
)), )),
); );
@@ -841,54 +840,35 @@ impl FileTransferActivity {
None => format!("{}", file.get_abs_path().display()), None => format!("{}", file.get_abs_path().display()),
}; };
// Make texts // Make texts
texts.add_col(TextSpan::from("Path: ")).add_col( texts
TextSpanBuilder::new(path.as_str()) .add_col(TextSpan::from("Path: "))
.with_foreground(Color::Yellow) .add_col(TextSpan::new(path.as_str()).fg(Color::Yellow));
.build(),
);
if let Some(filetype) = file.get_ftype() { if let Some(filetype) = file.get_ftype() {
texts texts
.add_row() .add_row()
.add_col(TextSpan::from("File type: ")) .add_col(TextSpan::from("File type: "))
.add_col( .add_col(TextSpan::new(filetype.as_str()).fg(Color::LightGreen));
TextSpanBuilder::new(filetype.as_str())
.with_foreground(Color::LightGreen)
.build(),
);
} }
let (bsize, size): (ByteSize, usize) = (ByteSize(file.get_size() as u64), file.get_size()); let (bsize, size): (ByteSize, usize) = (ByteSize(file.get_size() as u64), file.get_size());
texts.add_row().add_col(TextSpan::from("Size: ")).add_col( texts
TextSpanBuilder::new(format!("{} ({})", bsize, size).as_str()) .add_row()
.with_foreground(Color::Cyan) .add_col(TextSpan::from("Size: "))
.build(), .add_col(TextSpan::new(format!("{} ({})", bsize, size).as_str()).fg(Color::Cyan));
);
let ctime: String = fmt_time(file.get_creation_time(), "%b %d %Y %H:%M:%S"); let ctime: String = fmt_time(file.get_creation_time(), "%b %d %Y %H:%M:%S");
let atime: String = fmt_time(file.get_last_access_time(), "%b %d %Y %H:%M:%S"); let atime: String = fmt_time(file.get_last_access_time(), "%b %d %Y %H:%M:%S");
let mtime: String = fmt_time(file.get_creation_time(), "%b %d %Y %H:%M:%S"); let mtime: String = fmt_time(file.get_creation_time(), "%b %d %Y %H:%M:%S");
texts texts
.add_row() .add_row()
.add_col(TextSpan::from("Creation time: ")) .add_col(TextSpan::from("Creation time: "))
.add_col( .add_col(TextSpan::new(ctime.as_str()).fg(Color::LightGreen));
TextSpanBuilder::new(ctime.as_str())
.with_foreground(Color::LightGreen)
.build(),
);
texts texts
.add_row() .add_row()
.add_col(TextSpan::from("Last modified time: ")) .add_col(TextSpan::from("Last modified time: "))
.add_col( .add_col(TextSpan::new(mtime.as_str()).fg(Color::LightBlue));
TextSpanBuilder::new(mtime.as_str())
.with_foreground(Color::LightBlue)
.build(),
);
texts texts
.add_row() .add_row()
.add_col(TextSpan::from("Last access time: ")) .add_col(TextSpan::from("Last access time: "))
.add_col( .add_col(TextSpan::new(atime.as_str()).fg(Color::LightRed));
TextSpanBuilder::new(atime.as_str())
.with_foreground(Color::LightRed)
.build(),
);
// User // User
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
let username: String = match file.get_user() { let username: String = match file.get_user() {
@@ -911,22 +891,21 @@ impl FileTransferActivity {
}; };
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let group: String = format!("{}", file.get_group().unwrap_or(0)); let group: String = format!("{}", file.get_group().unwrap_or(0));
texts.add_row().add_col(TextSpan::from("User: ")).add_col( texts
TextSpanBuilder::new(username.as_str()) .add_row()
.with_foreground(Color::LightYellow) .add_col(TextSpan::from("User: "))
.build(), .add_col(TextSpan::new(username.as_str()).fg(Color::LightYellow));
); texts
texts.add_row().add_col(TextSpan::from("Group: ")).add_col( .add_row()
TextSpanBuilder::new(group.as_str()) .add_col(TextSpan::from("Group: "))
.with_foreground(Color::Blue) .add_col(TextSpan::new(group.as_str()).fg(Color::Blue));
.build(),
);
self.view.mount( self.view.mount(
super::COMPONENT_LIST_FILEINFO, super::COMPONENT_LIST_FILEINFO,
Box::new(Table::new( Box::new(Table::new(
TablePropsBuilder::default() TablePropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_table(Some(file.get_name().to_string()), texts.build()) .with_title(file.get_name(), Alignment::Left)
.with_table(texts.build())
.build(), .build(),
)), )),
); );
@@ -941,22 +920,16 @@ impl FileTransferActivity {
let sorting_color = self.theme().transfer_status_sorting; let sorting_color = self.theme().transfer_status_sorting;
let hidden_color = self.theme().transfer_status_hidden; let hidden_color = self.theme().transfer_status_hidden;
let local_bar_spans: Vec<TextSpan> = vec![ let local_bar_spans: Vec<TextSpan> = vec![
TextSpanBuilder::new("File sorting: ") TextSpan::new("File sorting: ").fg(sorting_color),
.with_foreground(sorting_color) TextSpan::new(Self::get_file_sorting_str(self.local().get_file_sorting()))
.build(), .fg(sorting_color)
TextSpanBuilder::new(Self::get_file_sorting_str(self.local().get_file_sorting())) .reversed(),
.with_foreground(sorting_color) TextSpan::new(" Hidden files: ").fg(hidden_color),
.reversed() TextSpan::new(Self::get_hidden_files_str(
.build(),
TextSpanBuilder::new(" Hidden files: ")
.with_foreground(hidden_color)
.build(),
TextSpanBuilder::new(Self::get_hidden_files_str(
self.local().hidden_files_visible(), self.local().hidden_files_visible(),
)) ))
.with_foreground(hidden_color) .fg(hidden_color)
.reversed() .reversed(),
.build(),
]; ];
if let Some(props) = self.view.get_props(super::COMPONENT_SPAN_STATUS_BAR_LOCAL) { if let Some(props) = self.view.get_props(super::COMPONENT_SPAN_STATUS_BAR_LOCAL) {
self.view.update( self.view.update(
@@ -973,32 +946,23 @@ impl FileTransferActivity {
let hidden_color = self.theme().transfer_status_hidden; let hidden_color = self.theme().transfer_status_hidden;
let sync_color = self.theme().transfer_status_sync_browsing; let sync_color = self.theme().transfer_status_sync_browsing;
let remote_bar_spans: Vec<TextSpan> = vec![ let remote_bar_spans: Vec<TextSpan> = vec![
TextSpanBuilder::new("File sorting: ") TextSpan::new("File sorting: ").fg(sorting_color),
.with_foreground(sorting_color) TextSpan::new(Self::get_file_sorting_str(self.remote().get_file_sorting()))
.build(), .fg(sorting_color)
TextSpanBuilder::new(Self::get_file_sorting_str(self.remote().get_file_sorting())) .reversed(),
.with_foreground(sorting_color) TextSpan::new(" Hidden files: ").fg(hidden_color),
.reversed() TextSpan::new(Self::get_hidden_files_str(
.build(),
TextSpanBuilder::new(" Hidden files: ")
.with_foreground(hidden_color)
.build(),
TextSpanBuilder::new(Self::get_hidden_files_str(
self.remote().hidden_files_visible(), self.remote().hidden_files_visible(),
)) ))
.with_foreground(hidden_color) .fg(hidden_color)
.reversed() .reversed(),
.build(), TextSpan::new(" Sync Browsing: ").fg(sync_color),
TextSpanBuilder::new(" Sync Browsing: ") TextSpan::new(match self.browser.sync_browsing {
.with_foreground(sync_color)
.build(),
TextSpanBuilder::new(match self.browser.sync_browsing {
true => "ON ", true => "ON ",
false => "OFF", false => "OFF",
}) })
.with_foreground(sync_color) .fg(sync_color)
.reversed() .reversed(),
.build(),
]; ];
if let Some(props) = self.view.get_props(super::COMPONENT_SPAN_STATUS_BAR_REMOTE) { if let Some(props) = self.view.get_props(super::COMPONENT_SPAN_STATUS_BAR_REMOTE) {
self.view.update( self.view.update(
@@ -1017,253 +981,109 @@ impl FileTransferActivity {
let key_color = self.theme().misc_keys; let key_color = self.theme().misc_keys;
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_HELP, super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new( Box::new(List::new(
ScrollTablePropsBuilder::default() ListPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_highlighted_str(Some("?")) .with_highlighted_str(Some("?"))
.with_max_scroll_step(8) .with_max_scroll_step(8)
.bold() .bold()
.with_table( .scrollable(true)
Some(String::from("Help")), .with_title("Help", Alignment::Center)
.with_rows(
TableBuilder::default() TableBuilder::default()
.add_col( .add_col(TextSpan::new("<ESC>").bold().fg(key_color))
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Disconnect")) .add_col(TextSpan::from(" Disconnect"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<TAB>").bold().fg(key_color))
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from( .add_col(TextSpan::from(
" Switch between explorer and logs", " Switch between explorer and logs",
)) ))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<BACKSPACE>").bold().fg(key_color))
TextSpanBuilder::new("<BACKSPACE>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Go to previous directory")) .add_col(TextSpan::from(" Go to previous directory"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<RIGHT/LEFT>").bold().fg(key_color))
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Change explorer tab")) .add_col(TextSpan::from(" Change explorer tab"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<UP/DOWN>").bold().fg(key_color))
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Move up/down in list")) .add_col(TextSpan::from(" Move up/down in list"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<ENTER>").bold().fg(key_color))
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Enter directory")) .add_col(TextSpan::from(" Enter directory"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<SPACE>").bold().fg(key_color))
TextSpanBuilder::new("<SPACE>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Upload/Download file")) .add_col(TextSpan::from(" Upload/Download file"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<A>").bold().fg(key_color))
TextSpanBuilder::new("<A>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Toggle hidden files")) .add_col(TextSpan::from(" Toggle hidden files"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<B>").bold().fg(key_color))
TextSpanBuilder::new("<B>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Change file sorting mode")) .add_col(TextSpan::from(" Change file sorting mode"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<C>").bold().fg(key_color))
TextSpanBuilder::new("<C>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Copy")) .add_col(TextSpan::from(" Copy"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<D>").bold().fg(key_color))
TextSpanBuilder::new("<D>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Make directory")) .add_col(TextSpan::from(" Make directory"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<G>").bold().fg(key_color))
TextSpanBuilder::new("<G>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Go to path")) .add_col(TextSpan::from(" Go to path"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<H>").bold().fg(key_color))
TextSpanBuilder::new("<H>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Show help")) .add_col(TextSpan::from(" Show help"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<I>").bold().fg(key_color))
TextSpanBuilder::new("<I>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Show info about selected file")) .add_col(TextSpan::from(" Show info about selected file"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<L>").bold().fg(key_color))
TextSpanBuilder::new("<L>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Reload directory content")) .add_col(TextSpan::from(" Reload directory content"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<M>").bold().fg(key_color))
TextSpanBuilder::new("<M>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Select file")) .add_col(TextSpan::from(" Select file"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<N>").bold().fg(key_color))
TextSpanBuilder::new("<N>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Create new file")) .add_col(TextSpan::from(" Create new file"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<O>").bold().fg(key_color))
TextSpanBuilder::new("<O>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from( .add_col(TextSpan::from(
" Open text file with preferred editor", " Open text file with preferred editor",
)) ))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<Q>").bold().fg(key_color))
TextSpanBuilder::new("<Q>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Quit termscp")) .add_col(TextSpan::from(" Quit termscp"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<R>").bold().fg(key_color))
TextSpanBuilder::new("<R>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Rename file")) .add_col(TextSpan::from(" Rename file"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<S>").bold().fg(key_color))
TextSpanBuilder::new("<S>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Save file as")) .add_col(TextSpan::from(" Save file as"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<U>").bold().fg(key_color))
TextSpanBuilder::new("<U>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Go to parent directory")) .add_col(TextSpan::from(" Go to parent directory"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<V>").bold().fg(key_color))
TextSpanBuilder::new("<V>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from( .add_col(TextSpan::from(
" Open file with default application for file type", " Open file with default application for file type",
)) ))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<W>").bold().fg(key_color))
TextSpanBuilder::new("<W>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from( .add_col(TextSpan::from(
" Open file with specified application", " Open file with specified application",
)) ))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<X>").bold().fg(key_color))
TextSpanBuilder::new("<X>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Execute shell command")) .add_col(TextSpan::from(" Execute shell command"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<Y>").bold().fg(key_color))
TextSpanBuilder::new("<Y>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Toggle synchronized browsing")) .add_col(TextSpan::from(" Toggle synchronized browsing"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<DEL|E>").bold().fg(key_color))
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Delete selected file")) .add_col(TextSpan::from(" Delete selected file"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+A>").bold().fg(key_color))
TextSpanBuilder::new("<CTRL+A>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Select all files")) .add_col(TextSpan::from(" Select all files"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+C>").bold().fg(key_color))
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Interrupt file transfer")) .add_col(TextSpan::from(" Interrupt file transfer"))
.build(), .build(),
) )
@@ -1280,10 +1100,10 @@ impl FileTransferActivity {
fn get_file_sorting_str(mode: FileSorting) -> &'static str { fn get_file_sorting_str(mode: FileSorting) -> &'static str {
match mode { match mode {
FileSorting::ByName => "By name", FileSorting::Name => "By name",
FileSorting::ByCreationTime => "By creation time", FileSorting::CreationTime => "By creation time",
FileSorting::ByModifyTime => "By modify time", FileSorting::ModifyTime => "By modify time",
FileSorting::BySize => "By size", FileSorting::Size => "By size",
} }
} }

View File

@@ -152,7 +152,7 @@ impl SetupActivity {
} }
fn config(&self) -> &ConfigClient { fn config(&self) -> &ConfigClient {
&self.context().config() self.context().config()
} }
fn config_mut(&mut self) -> &mut ConfigClient { fn config_mut(&mut self) -> &mut ConfigClient {

View File

@@ -74,65 +74,67 @@ impl SetupActivity {
None => None, None => None,
Some(msg) => match msg { Some(msg) => match msg {
// Input field <DOWN> // Input field <DOWN>
(COMPONENT_INPUT_TEXT_EDITOR, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_TEXT_EDITOR, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_RADIO_DEFAULT_PROTOCOL); self.view.active(COMPONENT_RADIO_DEFAULT_PROTOCOL);
None None
} }
(COMPONENT_RADIO_DEFAULT_PROTOCOL, &MSG_KEY_DOWN) => { (COMPONENT_RADIO_DEFAULT_PROTOCOL, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_RADIO_HIDDEN_FILES); self.view.active(COMPONENT_RADIO_HIDDEN_FILES);
None None
} }
(COMPONENT_RADIO_HIDDEN_FILES, &MSG_KEY_DOWN) => { (COMPONENT_RADIO_HIDDEN_FILES, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_RADIO_UPDATES); self.view.active(COMPONENT_RADIO_UPDATES);
None None
} }
(COMPONENT_RADIO_UPDATES, &MSG_KEY_DOWN) => { (COMPONENT_RADIO_UPDATES, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_RADIO_GROUP_DIRS); self.view.active(COMPONENT_RADIO_GROUP_DIRS);
None None
} }
(COMPONENT_RADIO_GROUP_DIRS, &MSG_KEY_DOWN) => { (COMPONENT_RADIO_GROUP_DIRS, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_LOCAL_FILE_FMT); self.view.active(COMPONENT_INPUT_LOCAL_FILE_FMT);
None None
} }
(COMPONENT_INPUT_LOCAL_FILE_FMT, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_LOCAL_FILE_FMT, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_REMOTE_FILE_FMT); self.view.active(COMPONENT_INPUT_REMOTE_FILE_FMT);
None None
} }
(COMPONENT_INPUT_REMOTE_FILE_FMT, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_REMOTE_FILE_FMT, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_TEXT_EDITOR); self.view.active(COMPONENT_INPUT_TEXT_EDITOR);
None None
} }
// Input field <UP> // Input field <UP>
(COMPONENT_INPUT_REMOTE_FILE_FMT, &MSG_KEY_UP) => { (COMPONENT_INPUT_REMOTE_FILE_FMT, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_LOCAL_FILE_FMT); self.view.active(COMPONENT_INPUT_LOCAL_FILE_FMT);
None None
} }
(COMPONENT_INPUT_LOCAL_FILE_FMT, &MSG_KEY_UP) => { (COMPONENT_INPUT_LOCAL_FILE_FMT, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_RADIO_GROUP_DIRS); self.view.active(COMPONENT_RADIO_GROUP_DIRS);
None None
} }
(COMPONENT_RADIO_GROUP_DIRS, &MSG_KEY_UP) => { (COMPONENT_RADIO_GROUP_DIRS, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_RADIO_UPDATES); self.view.active(COMPONENT_RADIO_UPDATES);
None None
} }
(COMPONENT_RADIO_UPDATES, &MSG_KEY_UP) => { (COMPONENT_RADIO_UPDATES, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_RADIO_HIDDEN_FILES); self.view.active(COMPONENT_RADIO_HIDDEN_FILES);
None None
} }
(COMPONENT_RADIO_HIDDEN_FILES, &MSG_KEY_UP) => { (COMPONENT_RADIO_HIDDEN_FILES, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_RADIO_DEFAULT_PROTOCOL); self.view.active(COMPONENT_RADIO_DEFAULT_PROTOCOL);
None None
} }
(COMPONENT_RADIO_DEFAULT_PROTOCOL, &MSG_KEY_UP) => { (COMPONENT_RADIO_DEFAULT_PROTOCOL, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_TEXT_EDITOR); self.view.active(COMPONENT_INPUT_TEXT_EDITOR);
None None
} }
(COMPONENT_INPUT_TEXT_EDITOR, &MSG_KEY_UP) => { (COMPONENT_INPUT_TEXT_EDITOR, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_REMOTE_FILE_FMT); self.view.active(COMPONENT_INPUT_REMOTE_FILE_FMT);
None None
} }
// Error <ENTER> or <ESC> // Error <ENTER> or <ESC>
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) => { (COMPONENT_TEXT_ERROR, key) | (COMPONENT_TEXT_ERROR, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount text error // Umount text error
self.umount_error(); self.umount_error();
None None
@@ -161,7 +163,9 @@ impl SetupActivity {
} }
(COMPONENT_RADIO_QUIT, _) => None, (COMPONENT_RADIO_QUIT, _) => None,
// Close help // Close help
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => { (COMPONENT_TEXT_HELP, key) | (COMPONENT_TEXT_HELP, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount help // Umount help
self.umount_help(); self.umount_help();
None None
@@ -189,12 +193,12 @@ impl SetupActivity {
None None
} }
// <CTRL+H> Show help // <CTRL+H> Show help
(_, &MSG_KEY_CTRL_H) => { (_, key) if key == &MSG_KEY_CTRL_H => {
// Show help // Show help
self.mount_help(); self.mount_help();
None None
} }
(_, &MSG_KEY_TAB) => { (_, key) if key == &MSG_KEY_TAB => {
// Change view // Change view
if let Err(err) = self.action_change_tab(ViewLayout::SshKeys) { if let Err(err) = self.action_change_tab(ViewLayout::SshKeys) {
self.mount_error(err.as_str()); self.mount_error(err.as_str());
@@ -202,7 +206,7 @@ impl SetupActivity {
None None
} }
// <CTRL+R> Revert changes // <CTRL+R> Revert changes
(_, &MSG_KEY_CTRL_R) => { (_, key) if key == &MSG_KEY_CTRL_R => {
// Revert changes // Revert changes
if let Err(err) = self.action_reset_config() { if let Err(err) = self.action_reset_config() {
self.mount_error(err.as_str()); self.mount_error(err.as_str());
@@ -210,13 +214,13 @@ impl SetupActivity {
None None
} }
// <CTRL+S> Save // <CTRL+S> Save
(_, &MSG_KEY_CTRL_S) => { (_, key) if key == &MSG_KEY_CTRL_S => {
// Show save // Show save
self.mount_save_popup(); self.mount_save_popup();
None None
} }
// <ESC> // <ESC>
(_, &MSG_KEY_ESC) => { (_, key) if key == &MSG_KEY_ESC => {
self.action_on_esc(); self.action_on_esc();
None None
} }
@@ -232,7 +236,9 @@ impl SetupActivity {
None => None, None => None,
Some(msg) => match msg { Some(msg) => match msg {
// Error <ENTER> or <ESC> // Error <ENTER> or <ESC>
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) => { (COMPONENT_TEXT_ERROR, key) | (COMPONENT_TEXT_ERROR, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount text error // Umount text error
self.umount_error(); self.umount_error();
None None
@@ -261,7 +267,9 @@ impl SetupActivity {
} }
(COMPONENT_RADIO_QUIT, _) => None, (COMPONENT_RADIO_QUIT, _) => None,
// Close help // Close help
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => { (COMPONENT_TEXT_HELP, key) | (COMPONENT_TEXT_HELP, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount help // Umount help
self.umount_help(); self.umount_help();
None None
@@ -300,28 +308,30 @@ impl SetupActivity {
(COMPONENT_RADIO_SAVE, _) => None, (COMPONENT_RADIO_SAVE, _) => None,
// Edit SSH Key // Edit SSH Key
// <CTRL+H> Show help // <CTRL+H> Show help
(_, &MSG_KEY_CTRL_H) => { (_, key) if key == &MSG_KEY_CTRL_H => {
// Show help // Show help
self.mount_help(); self.mount_help();
None None
} }
// New key <DOWN> // New key <DOWN>
(COMPONENT_INPUT_SSH_HOST, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_SSH_HOST, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_SSH_USERNAME); self.view.active(COMPONENT_INPUT_SSH_USERNAME);
None None
} }
(COMPONENT_INPUT_SSH_USERNAME, &MSG_KEY_DOWN) => { (COMPONENT_INPUT_SSH_USERNAME, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_SSH_HOST); self.view.active(COMPONENT_INPUT_SSH_HOST);
None None
} }
// New key <UP> // New key <UP>
(COMPONENT_INPUT_SSH_USERNAME, &MSG_KEY_UP) (COMPONENT_INPUT_SSH_USERNAME, key) | (COMPONENT_INPUT_SSH_USERNAME, key)
| (COMPONENT_INPUT_SSH_USERNAME, &MSG_KEY_TAB) => { if key == &MSG_KEY_UP || key == &MSG_KEY_TAB =>
{
self.view.active(COMPONENT_INPUT_SSH_HOST); self.view.active(COMPONENT_INPUT_SSH_HOST);
None None
} }
(COMPONENT_INPUT_SSH_HOST, &MSG_KEY_UP) (COMPONENT_INPUT_SSH_HOST, key) | (COMPONENT_INPUT_SSH_HOST, key)
| (COMPONENT_INPUT_SSH_HOST, &MSG_KEY_TAB) => { if key == &MSG_KEY_UP || key == &MSG_KEY_TAB =>
{
self.view.active(COMPONENT_INPUT_SSH_USERNAME); self.view.active(COMPONENT_INPUT_SSH_USERNAME);
None None
} }
@@ -335,14 +345,15 @@ impl SetupActivity {
None None
} }
// New key <ESC> // New key <ESC>
(COMPONENT_INPUT_SSH_HOST, &MSG_KEY_ESC) (COMPONENT_INPUT_SSH_HOST, key) | (COMPONENT_INPUT_SSH_USERNAME, key)
| (COMPONENT_INPUT_SSH_USERNAME, &MSG_KEY_ESC) => { if key == &MSG_KEY_ESC =>
{
// Umount new ssh key // Umount new ssh key
self.umount_new_ssh_key(); self.umount_new_ssh_key();
None None
} }
// <CTRL+N> New key // <CTRL+N> New key
(COMPONENT_LIST_SSH_KEYS, &MSG_KEY_CTRL_N) => { (COMPONENT_LIST_SSH_KEYS, key) if key == &MSG_KEY_CTRL_N => {
// Show new key popup // Show new key popup
self.mount_new_ssh_key(); self.mount_new_ssh_key();
None None
@@ -356,13 +367,14 @@ impl SetupActivity {
None None
} }
// <DEL | CTRL+E> Show delete // <DEL | CTRL+E> Show delete
(COMPONENT_LIST_SSH_KEYS, &MSG_KEY_CTRL_E) (COMPONENT_LIST_SSH_KEYS, key) | (COMPONENT_LIST_SSH_KEYS, key)
| (COMPONENT_LIST_SSH_KEYS, &MSG_KEY_DEL) => { if key == &MSG_KEY_CTRL_E || key == &MSG_KEY_DEL =>
{
// Show delete key // Show delete key
self.mount_del_ssh_key(); self.mount_del_ssh_key();
None None
} }
(_, &MSG_KEY_TAB) => { (_, key) if key == &MSG_KEY_TAB => {
// Change view // Change view
if let Err(err) = self.action_change_tab(ViewLayout::Theme) { if let Err(err) = self.action_change_tab(ViewLayout::Theme) {
self.mount_error(err.as_str()); self.mount_error(err.as_str());
@@ -370,7 +382,7 @@ impl SetupActivity {
None None
} }
// <CTRL+R> Revert changes // <CTRL+R> Revert changes
(_, &MSG_KEY_CTRL_R) => { (_, key) if key == &MSG_KEY_CTRL_R => {
// Revert changes // Revert changes
if let Err(err) = self.action_reset_config() { if let Err(err) = self.action_reset_config() {
self.mount_error(err.as_str()); self.mount_error(err.as_str());
@@ -378,13 +390,13 @@ impl SetupActivity {
None None
} }
// <CTRL+S> Save // <CTRL+S> Save
(_, &MSG_KEY_CTRL_S) => { (_, key) if key == &MSG_KEY_CTRL_S => {
// Show save // Show save
self.mount_save_popup(); self.mount_save_popup();
None None
} }
// <ESC> // <ESC>
(_, &MSG_KEY_ESC) => { (_, key) if key == &MSG_KEY_ESC => {
self.action_on_esc(); self.action_on_esc();
None None
} }
@@ -400,217 +412,217 @@ impl SetupActivity {
None => None, None => None,
Some(msg) => match msg { Some(msg) => match msg {
// Input fields // Input fields
(COMPONENT_COLOR_AUTH_PROTOCOL, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_PROTOCOL, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_ADDR); self.view.active(COMPONENT_COLOR_AUTH_ADDR);
None None
} }
(COMPONENT_COLOR_AUTH_ADDR, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_ADDR, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_PORT); self.view.active(COMPONENT_COLOR_AUTH_PORT);
None None
} }
(COMPONENT_COLOR_AUTH_PORT, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_PORT, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_USERNAME); self.view.active(COMPONENT_COLOR_AUTH_USERNAME);
None None
} }
(COMPONENT_COLOR_AUTH_USERNAME, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_USERNAME, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_PASSWORD); self.view.active(COMPONENT_COLOR_AUTH_PASSWORD);
None None
} }
(COMPONENT_COLOR_AUTH_PASSWORD, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_PASSWORD, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_BOOKMARKS); self.view.active(COMPONENT_COLOR_AUTH_BOOKMARKS);
None None
} }
(COMPONENT_COLOR_AUTH_BOOKMARKS, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_BOOKMARKS, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_RECENTS); self.view.active(COMPONENT_COLOR_AUTH_RECENTS);
None None
} }
(COMPONENT_COLOR_AUTH_RECENTS, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_AUTH_RECENTS, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_MISC_ERROR); self.view.active(COMPONENT_COLOR_MISC_ERROR);
None None
} }
(COMPONENT_COLOR_MISC_ERROR, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_MISC_ERROR, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_MISC_INPUT); self.view.active(COMPONENT_COLOR_MISC_INPUT);
None None
} }
(COMPONENT_COLOR_MISC_INPUT, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_MISC_INPUT, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_MISC_KEYS); self.view.active(COMPONENT_COLOR_MISC_KEYS);
None None
} }
(COMPONENT_COLOR_MISC_KEYS, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_MISC_KEYS, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_MISC_QUIT); self.view.active(COMPONENT_COLOR_MISC_QUIT);
None None
} }
(COMPONENT_COLOR_MISC_QUIT, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_MISC_QUIT, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_MISC_SAVE); self.view.active(COMPONENT_COLOR_MISC_SAVE);
None None
} }
(COMPONENT_COLOR_MISC_SAVE, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_MISC_SAVE, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_MISC_WARN); self.view.active(COMPONENT_COLOR_MISC_WARN);
None None
} }
(COMPONENT_COLOR_MISC_WARN, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_MISC_WARN, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG); self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG); self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG); self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, key) if key == &MSG_KEY_DOWN => {
self.view self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG); .active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG, key) if key == &MSG_KEY_DOWN => {
self.view self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG); .active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, key) if key == &MSG_KEY_DOWN => {
self.view self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG); .active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL); self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL);
None None
} }
(COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL); self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL);
None None
} }
(COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_BG); self.view.active(COMPONENT_COLOR_TRANSFER_LOG_BG);
None None
} }
(COMPONENT_COLOR_TRANSFER_LOG_BG, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_LOG_BG, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_WIN); self.view.active(COMPONENT_COLOR_TRANSFER_LOG_WIN);
None None
} }
(COMPONENT_COLOR_TRANSFER_LOG_WIN, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_LOG_WIN, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SORTING); self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SORTING);
None None
} }
(COMPONENT_COLOR_TRANSFER_STATUS_SORTING, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_STATUS_SORTING, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN); self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN);
None None
} }
(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SYNC); self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SYNC);
None None
} }
(COMPONENT_COLOR_TRANSFER_STATUS_SYNC, &MSG_KEY_DOWN) => { (COMPONENT_COLOR_TRANSFER_STATUS_SYNC, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_COLOR_AUTH_PROTOCOL); self.view.active(COMPONENT_COLOR_AUTH_PROTOCOL);
None None
} }
(COMPONENT_COLOR_AUTH_PROTOCOL, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_PROTOCOL, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SYNC); self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SYNC);
None None
} }
(COMPONENT_COLOR_AUTH_ADDR, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_ADDR, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_PROTOCOL); self.view.active(COMPONENT_COLOR_AUTH_PROTOCOL);
None None
} }
(COMPONENT_COLOR_AUTH_PORT, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_PORT, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_ADDR); self.view.active(COMPONENT_COLOR_AUTH_ADDR);
None None
} }
(COMPONENT_COLOR_AUTH_USERNAME, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_USERNAME, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_PORT); self.view.active(COMPONENT_COLOR_AUTH_PORT);
None None
} }
(COMPONENT_COLOR_AUTH_PASSWORD, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_PASSWORD, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_USERNAME); self.view.active(COMPONENT_COLOR_AUTH_USERNAME);
None None
} }
(COMPONENT_COLOR_AUTH_BOOKMARKS, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_BOOKMARKS, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_PASSWORD); self.view.active(COMPONENT_COLOR_AUTH_PASSWORD);
None None
} }
(COMPONENT_COLOR_AUTH_RECENTS, &MSG_KEY_UP) => { (COMPONENT_COLOR_AUTH_RECENTS, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_BOOKMARKS); self.view.active(COMPONENT_COLOR_AUTH_BOOKMARKS);
None None
} }
(COMPONENT_COLOR_MISC_ERROR, &MSG_KEY_UP) => { (COMPONENT_COLOR_MISC_ERROR, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_AUTH_RECENTS); self.view.active(COMPONENT_COLOR_AUTH_RECENTS);
None None
} }
(COMPONENT_COLOR_MISC_INPUT, &MSG_KEY_UP) => { (COMPONENT_COLOR_MISC_INPUT, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_MISC_ERROR); self.view.active(COMPONENT_COLOR_MISC_ERROR);
None None
} }
(COMPONENT_COLOR_MISC_KEYS, &MSG_KEY_UP) => { (COMPONENT_COLOR_MISC_KEYS, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_MISC_INPUT); self.view.active(COMPONENT_COLOR_MISC_INPUT);
None None
} }
(COMPONENT_COLOR_MISC_QUIT, &MSG_KEY_UP) => { (COMPONENT_COLOR_MISC_QUIT, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_MISC_KEYS); self.view.active(COMPONENT_COLOR_MISC_KEYS);
None None
} }
(COMPONENT_COLOR_MISC_SAVE, &MSG_KEY_UP) => { (COMPONENT_COLOR_MISC_SAVE, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_MISC_QUIT); self.view.active(COMPONENT_COLOR_MISC_QUIT);
None None
} }
(COMPONENT_COLOR_MISC_WARN, &MSG_KEY_UP) => { (COMPONENT_COLOR_MISC_WARN, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_MISC_SAVE); self.view.active(COMPONENT_COLOR_MISC_SAVE);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_MISC_WARN); self.view.active(COMPONENT_COLOR_MISC_WARN);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG); self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG); self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG); self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, key) if key == &MSG_KEY_UP => {
self.view self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG); .active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG);
None None
} }
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG, key) if key == &MSG_KEY_UP => {
self.view self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG); .active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG);
None None
} }
(COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL, key) if key == &MSG_KEY_UP => {
self.view self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG); .active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG);
None None
} }
(COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL); self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_FULL);
None None
} }
(COMPONENT_COLOR_TRANSFER_LOG_BG, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_LOG_BG, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL); self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR_PARTIAL);
None None
} }
(COMPONENT_COLOR_TRANSFER_LOG_WIN, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_LOG_WIN, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_BG); self.view.active(COMPONENT_COLOR_TRANSFER_LOG_BG);
None None
} }
(COMPONENT_COLOR_TRANSFER_STATUS_SORTING, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_STATUS_SORTING, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_WIN); self.view.active(COMPONENT_COLOR_TRANSFER_LOG_WIN);
None None
} }
(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SORTING); self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SORTING);
None None
} }
(COMPONENT_COLOR_TRANSFER_STATUS_SYNC, &MSG_KEY_UP) => { (COMPONENT_COLOR_TRANSFER_STATUS_SYNC, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN); self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN);
None None
} }
@@ -624,7 +636,9 @@ impl SetupActivity {
None None
} }
// Error <ENTER> or <ESC> // Error <ENTER> or <ESC>
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) => { (COMPONENT_TEXT_ERROR, key) | (COMPONENT_TEXT_ERROR, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount text error // Umount text error
self.umount_error(); self.umount_error();
None None
@@ -653,7 +667,9 @@ impl SetupActivity {
} }
(COMPONENT_RADIO_QUIT, _) => None, (COMPONENT_RADIO_QUIT, _) => None,
// Close help // Close help
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => { (COMPONENT_TEXT_HELP, key) | (COMPONENT_TEXT_HELP, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// Umount help // Umount help
self.umount_help(); self.umount_help();
None None
@@ -676,12 +692,12 @@ impl SetupActivity {
(COMPONENT_RADIO_SAVE, _) => None, (COMPONENT_RADIO_SAVE, _) => None,
// Edit SSH Key // Edit SSH Key
// <CTRL+H> Show help // <CTRL+H> Show help
(_, &MSG_KEY_CTRL_H) => { (_, key) if key == &MSG_KEY_CTRL_H => {
// Show help // Show help
self.mount_help(); self.mount_help();
None None
} }
(_, &MSG_KEY_TAB) => { (_, key) if key == &MSG_KEY_TAB => {
// Change view // Change view
if let Err(err) = self.action_change_tab(ViewLayout::SetupForm) { if let Err(err) = self.action_change_tab(ViewLayout::SetupForm) {
self.mount_error(err.as_str()); self.mount_error(err.as_str());
@@ -689,7 +705,7 @@ impl SetupActivity {
None None
} }
// <CTRL+R> Revert changes // <CTRL+R> Revert changes
(_, &MSG_KEY_CTRL_R) => { (_, key) if key == &MSG_KEY_CTRL_R => {
// Revert changes // Revert changes
if let Err(err) = self.action_reset_theme() { if let Err(err) = self.action_reset_theme() {
self.mount_error(err.as_str()); self.mount_error(err.as_str());
@@ -697,13 +713,13 @@ impl SetupActivity {
None None
} }
// <CTRL+S> Save // <CTRL+S> Save
(_, &MSG_KEY_CTRL_S) => { (_, key) if key == &MSG_KEY_CTRL_S => {
// Show save // Show save
self.mount_save_popup(); self.mount_save_popup();
None None
} }
// <ESC> // <ESC>
(_, &MSG_KEY_ESC) => { (_, key) if key == &MSG_KEY_ESC => {
self.action_on_esc(); self.action_on_esc();
None None
} }

View File

@@ -34,14 +34,14 @@ use super::*;
pub use setup::*; pub use setup::*;
pub use ssh_keys::*; pub use ssh_keys::*;
pub use theme::*; pub use theme::*;
// Locals
use crate::ui::components::msgbox::{MsgBox, MsgBoxPropsBuilder};
// Ext // Ext
use tuirealm::components::{ use tui_realm_stdlib::{
list::{List, ListPropsBuilder},
paragraph::{Paragraph, ParagraphPropsBuilder},
radio::{Radio, RadioPropsBuilder}, radio::{Radio, RadioPropsBuilder},
scrolltable::{ScrollTablePropsBuilder, Scrolltable}, span::{Span, SpanPropsBuilder},
}; };
use tuirealm::props::{PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder}; use tuirealm::props::{Alignment, PropsBuilder, TableBuilder, TextSpan};
use tuirealm::tui::{ use tuirealm::tui::{
style::Color, style::Color,
widgets::{BorderType, Borders}, widgets::{BorderType, Borders},
@@ -79,12 +79,13 @@ impl SetupActivity {
// Mount // Mount
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_ERROR, super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new( Box::new(Paragraph::new(
MsgBoxPropsBuilder::default() ParagraphPropsBuilder::default()
.with_foreground(Color::Red) .with_foreground(Color::Red)
.bold() .bold()
.with_borders(Borders::ALL, BorderType::Rounded, Color::Red) .with_borders(Borders::ALL, BorderType::Rounded, Color::Red)
.with_texts(None, vec![TextSpan::from(text)]) .with_texts(vec![TextSpan::from(text)])
.with_text_alignment(Alignment::Center)
.build(), .build(),
)), )),
); );
@@ -110,16 +111,16 @@ impl SetupActivity {
.with_color(Color::LightRed) .with_color(Color::LightRed)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options( .with_title(
Some(String::from( "There are unsaved changes! Save changes before leaving?",
"There are unsaved changes! Save changes before leaving?", Alignment::Center,
)),
vec![
String::from("Save"),
String::from("Don't save"),
String::from("Cancel"),
],
) )
.with_options(&[
String::from("Save"),
String::from("Don't save"),
String::from("Cancel"),
])
.rewind(true)
.build(), .build(),
)), )),
); );
@@ -145,10 +146,9 @@ impl SetupActivity {
.with_color(Color::LightYellow) .with_color(Color::LightYellow)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_options( .with_title("Save changes?", Alignment::Center)
Some(String::from("Save changes?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -163,91 +163,82 @@ impl SetupActivity {
self.view.umount(super::COMPONENT_RADIO_SAVE); self.view.umount(super::COMPONENT_RADIO_SAVE);
} }
pub(self) fn mount_header_tab(&mut self, idx: usize) {
self.view.mount(
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::White)
.with_options(&[
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
])
.with_value(idx)
.rewind(true)
.build(),
)),
);
}
pub(self) fn mount_footer(&mut self) {
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpan::new("Press ").bold(),
TextSpan::new("<CTRL+H>").bold().fg(Color::Cyan),
TextSpan::new(" to show keybindings").bold(),
])
.build(),
)),
);
}
/// ### mount_help /// ### mount_help
/// ///
/// Mount help /// Mount help
pub(super) fn mount_help(&mut self) { pub(super) fn mount_help(&mut self) {
self.view.mount( self.view.mount(
super::COMPONENT_TEXT_HELP, super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new( Box::new(List::new(
ScrollTablePropsBuilder::default() ListPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_highlighted_str(Some("?")) .with_highlighted_str(Some("?"))
.with_max_scroll_step(8) .with_max_scroll_step(8)
.bold() .bold()
.with_table( .with_title("Help", Alignment::Center)
Some(String::from("Help")), .scrollable(true)
.with_rows(
TableBuilder::default() TableBuilder::default()
.add_col( .add_col(TextSpan::new("<ESC>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Exit setup")) .add_col(TextSpan::from(" Exit setup"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<TAB>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change setup page")) .add_col(TextSpan::from(" Change setup page"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<RIGHT/LEFT>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change cursor")) .add_col(TextSpan::from(" Change cursor"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<UP/DOWN>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change input field")) .add_col(TextSpan::from(" Change input field"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<ENTER>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Select / Dismiss popup")) .add_col(TextSpan::from(" Select / Dismiss popup"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<DEL|E>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Delete SSH key")) .add_col(TextSpan::from(" Delete SSH key"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+N>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<CTRL+N>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" New SSH key")) .add_col(TextSpan::from(" New SSH key"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+R>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<CTRL+R>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Revert changes")) .add_col(TextSpan::from(" Revert changes"))
.add_row() .add_row()
.add_col( .add_col(TextSpan::new("<CTRL+S>").bold().fg(Color::Cyan))
TextSpanBuilder::new("<CTRL+S>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Save configuration")) .add_col(TextSpan::from(" Save configuration"))
.build(), .build(),
) )

View File

@@ -33,10 +33,9 @@ use crate::fs::explorer::GroupDirs;
use crate::utils::ui::draw_area_in; use crate::utils::ui::draw_area_in;
// Ext // Ext
use std::path::PathBuf; use std::path::PathBuf;
use tuirealm::components::{ use tui_realm_stdlib::{
input::{Input, InputPropsBuilder}, input::{Input, InputPropsBuilder},
radio::{Radio, RadioPropsBuilder}, radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
}; };
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
@@ -44,7 +43,7 @@ use tuirealm::tui::{
widgets::{BorderType, Borders, Clear}, widgets::{BorderType, Borders, Clear},
}; };
use tuirealm::{ use tuirealm::{
props::{PropsBuilder, TextSpanBuilder}, props::{Alignment, PropsBuilder},
Payload, Value, View, Payload, Value, View,
}; };
@@ -59,41 +58,9 @@ impl SetupActivity {
self.view = View::init(); self.view = View::init();
// Common stuff // Common stuff
// Radio tab // Radio tab
self.view.mount( self.mount_header_tab(0);
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::White)
.with_options(
None,
vec![
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
],
)
.with_value(0)
.build(),
)),
);
// Footer // Footer
self.view.mount( self.mount_footer();
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
// Input fields // Input fields
self.view.mount( self.view.mount(
super::COMPONENT_INPUT_TEXT_EDITOR, super::COMPONENT_INPUT_TEXT_EDITOR,
@@ -101,7 +68,7 @@ impl SetupActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(Color::LightGreen) .with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("Text editor")) .with_label("Text editor", Alignment::Left)
.build(), .build(),
)), )),
); );
@@ -113,15 +80,14 @@ impl SetupActivity {
.with_color(Color::LightCyan) .with_color(Color::LightCyan)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightCyan) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightCyan)
.with_options( .with_title("Default file transfer protocol", Alignment::Left)
Some(String::from("Default file transfer protocol")), .with_options(&[
vec![ String::from("SFTP"),
String::from("SFTP"), String::from("SCP"),
String::from("SCP"), String::from("FTP"),
String::from("FTP"), String::from("FTPS"),
String::from("FTPS"), ])
], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -132,10 +98,9 @@ impl SetupActivity {
.with_color(Color::LightRed) .with_color(Color::LightRed)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options( .with_title("Show hidden files (by default)?", Alignment::Left)
Some(String::from("Show hidden files (by default)")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -146,10 +111,9 @@ impl SetupActivity {
.with_color(Color::LightYellow) .with_color(Color::LightYellow)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_options( .with_title("Check for updates?", Alignment::Left)
Some(String::from("Check for updates?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -160,14 +124,13 @@ impl SetupActivity {
.with_color(Color::LightMagenta) .with_color(Color::LightMagenta)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta)
.with_options( .with_title("Group directories", Alignment::Left)
Some(String::from("Group directories")), .with_options(&[
vec![ String::from("Display first"),
String::from("Display first"), String::from("Display Last"),
String::from("Display Last"), String::from("No"),
String::from("No"), ])
], .rewind(true)
)
.build(), .build(),
)), )),
); );
@@ -177,7 +140,7 @@ impl SetupActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(Color::LightBlue) .with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue)
.with_label(String::from("File formatter syntax (local)")) .with_label("File formatter syntax (local)", Alignment::Left)
.build(), .build(),
)), )),
); );
@@ -187,7 +150,7 @@ impl SetupActivity {
InputPropsBuilder::default() InputPropsBuilder::default()
.with_foreground(Color::LightGreen) .with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("File formatter syntax (remote)")) .with_label("File formatter syntax (remote)", Alignment::Left)
.build(), .build(),
)), )),
); );

View File

@@ -31,10 +31,9 @@ use super::{Context, SetupActivity};
use crate::ui::components::bookmark_list::{BookmarkList, BookmarkListPropsBuilder}; use crate::ui::components::bookmark_list::{BookmarkList, BookmarkListPropsBuilder};
use crate::utils::ui::draw_area_in; use crate::utils::ui::draw_area_in;
// Ext // Ext
use tuirealm::components::{ use tui_realm_stdlib::{
input::{Input, InputPropsBuilder}, input::{Input, InputPropsBuilder},
radio::{Radio, RadioPropsBuilder}, radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
}; };
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
@@ -42,7 +41,7 @@ use tuirealm::tui::{
widgets::{BorderType, Borders, Clear}, widgets::{BorderType, Borders, Clear},
}; };
use tuirealm::{ use tuirealm::{
props::{PropsBuilder, TextSpanBuilder}, props::{Alignment, PropsBuilder},
View, View,
}; };
@@ -57,46 +56,15 @@ impl SetupActivity {
self.view = View::init(); self.view = View::init();
// Common stuff // Common stuff
// Radio tab // Radio tab
self.view.mount( // Radio tab
super::COMPONENT_RADIO_TAB, self.mount_header_tab(1);
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::LightYellow)
.with_options(
None,
vec![
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
],
)
.with_value(1)
.build(),
)),
);
// Footer // Footer
self.view.mount( self.mount_footer();
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
self.view.mount( self.view.mount(
super::COMPONENT_LIST_SSH_KEYS, super::COMPONENT_LIST_SSH_KEYS,
Box::new(BookmarkList::new( Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default() BookmarkListPropsBuilder::default()
.with_bookmarks(Some(String::from("SSH Keys")), vec![]) .with_title("SSH keys", Alignment::Left)
.with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen) .with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen)
.with_background(Color::LightGreen) .with_background(Color::LightGreen)
.with_foreground(Color::Black) .with_foreground(Color::Black)
@@ -211,11 +179,10 @@ impl SetupActivity {
.with_color(Color::LightRed) .with_color(Color::LightRed)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed) .with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options( .with_title("Delete key?", Alignment::Center)
Some(String::from("Delete key?")), .with_options(&[String::from("Yes"), String::from("No")])
vec![String::from("Yes"), String::from("No")],
)
.with_value(1) // Default: No .with_value(1) // Default: No
.rewind(true)
.build(), .build(),
)), )),
); );
@@ -238,7 +205,7 @@ impl SetupActivity {
super::COMPONENT_INPUT_SSH_HOST, super::COMPONENT_INPUT_SSH_HOST,
Box::new(Input::new( Box::new(Input::new(
InputPropsBuilder::default() InputPropsBuilder::default()
.with_label(String::from("Hostname or address")) .with_label("Hostname or address", Alignment::Center)
.with_borders( .with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT, Borders::TOP | Borders::RIGHT | Borders::LEFT,
BorderType::Plain, BorderType::Plain,
@@ -251,7 +218,7 @@ impl SetupActivity {
super::COMPONENT_INPUT_SSH_USERNAME, super::COMPONENT_INPUT_SSH_USERNAME,
Box::new(Input::new( Box::new(Input::new(
InputPropsBuilder::default() InputPropsBuilder::default()
.with_label(String::from("Username")) .with_label("Username", Alignment::Center)
.with_borders( .with_borders(
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT, Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
BorderType::Plain, BorderType::Plain,
@@ -287,7 +254,7 @@ impl SetupActivity {
}) })
.collect(); .collect();
let props = BookmarkListPropsBuilder::from(props) let props = BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("SSH Keys")), keys) .with_bookmarks(keys)
.build(); .build();
self.view.update(super::COMPONENT_LIST_SSH_KEYS, props); self.view.update(super::COMPONENT_LIST_SSH_KEYS, props);
} }

View File

@@ -33,18 +33,14 @@ use crate::ui::components::color_picker::{ColorPicker, ColorPickerPropsBuilder};
use crate::utils::parser::parse_color; use crate::utils::parser::parse_color;
use crate::utils::ui::draw_area_in; use crate::utils::ui::draw_area_in;
// Ext // Ext
use tuirealm::components::{ use tui_realm_stdlib::label::{Label, LabelPropsBuilder};
label::{Label, LabelPropsBuilder},
radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
};
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
style::Color, style::Color,
widgets::{BorderType, Borders, Clear}, widgets::{BorderType, Borders, Clear},
}; };
use tuirealm::{ use tuirealm::{
props::{PropsBuilder, TextSpanBuilder}, props::{Alignment, PropsBuilder},
Payload, Value, View, Payload, Value, View,
}; };
@@ -59,41 +55,9 @@ impl SetupActivity {
self.view = View::init(); self.view = View::init();
// Common stuff // Common stuff
// Radio tab // Radio tab
self.view.mount( self.mount_header_tab(2);
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::White)
.with_options(
None,
vec![
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
],
)
.with_value(2)
.build(),
)),
);
// Footer // Footer
self.view.mount( self.mount_footer();
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
// auth colors // auth colors
self.mount_title(super::COMPONENT_COLOR_AUTH_TITLE, "Authentication styles"); self.mount_title(super::COMPONENT_COLOR_AUTH_TITLE, "Authentication styles");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_PROTOCOL, "Protocol"); self.mount_color_picker(super::COMPONENT_COLOR_AUTH_PROTOCOL, "Protocol");
@@ -653,7 +617,7 @@ impl SetupActivity {
Box::new(ColorPicker::new( Box::new(ColorPicker::new(
ColorPickerPropsBuilder::default() ColorPickerPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::Reset) .with_borders(Borders::ALL, BorderType::Rounded, Color::Reset)
.with_label(label.to_string()) .with_label(label.to_string(), Alignment::Left)
.build(), .build(),
)), )),
); );

View File

@@ -26,18 +26,19 @@
* SOFTWARE. * SOFTWARE.
*/ */
// ext // ext
use tuirealm::components::utils::get_block; use tui_realm_stdlib::utils::get_block;
use tuirealm::event::{Event, KeyCode}; use tuirealm::event::{Event, KeyCode};
use tuirealm::props::{BordersProps, Props, PropsBuilder, TextParts, TextSpan}; use tuirealm::props::{Alignment, BlockTitle, BordersProps, Props, PropsBuilder};
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Corner, Rect}, layout::{Corner, Rect},
style::{Color, Style}, style::{Color, Style},
text::Span, text::Span,
widgets::{BorderType, Borders, List, ListItem, ListState}, widgets::{BorderType, Borders, List, ListItem, ListState},
}; };
use tuirealm::{Canvas, Component, Msg, Payload, Value}; use tuirealm::{Component, Frame, Msg, Payload, PropPayload, PropValue, Value};
// -- props // -- props
const PROP_BOOKMARKS: &str = "bookmarks";
pub struct BookmarkListPropsBuilder { pub struct BookmarkListPropsBuilder {
props: Option<Props>, props: Option<Props>,
@@ -117,10 +118,19 @@ impl BookmarkListPropsBuilder {
self self
} }
pub fn with_bookmarks(&mut self, title: Option<String>, bookmarks: Vec<String>) -> &mut Self { pub fn with_title<S: AsRef<str>>(&mut self, text: S, alignment: Alignment) -> &mut Self {
if let Some(props) = self.props.as_mut() { if let Some(props) = self.props.as_mut() {
let bookmarks: Vec<TextSpan> = bookmarks.into_iter().map(TextSpan::from).collect(); props.title = Some(BlockTitle::new(text, alignment));
props.texts = TextParts::new(title, Some(bookmarks)); }
self
}
pub fn with_bookmarks(&mut self, bookmarks: Vec<String>) -> &mut Self {
if let Some(props) = self.props.as_mut() {
let bookmarks: Vec<PropValue> = bookmarks.into_iter().map(PropValue::Str).collect();
props
.own
.insert(PROP_BOOKMARKS, PropPayload::Vec(bookmarks));
} }
self self
} }
@@ -210,25 +220,30 @@ impl BookmarkList {
// Initialize states // Initialize states
let mut states: OwnStates = OwnStates::default(); let mut states: OwnStates = OwnStates::default();
// Set list length // Set list length
states.set_list_len(match &props.texts.spans { states.set_list_len(Self::bookmarks_len(&props));
Some(tokens) => tokens.len(),
None => 0,
});
BookmarkList { props, states } BookmarkList { props, states }
} }
fn bookmarks_len(props: &Props) -> usize {
match props.own.get(PROP_BOOKMARKS) {
None => 0,
Some(bookmarks) => bookmarks.unwrap_vec().len(),
}
}
} }
impl Component for BookmarkList { impl Component for BookmarkList {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
fn render(&self, render: &mut Canvas, area: Rect) { fn render(&self, render: &mut Frame, area: Rect) {
if self.props.visible { if self.props.visible {
// Make list // Make list
let list_item: Vec<ListItem> = match self.props.texts.spans.as_ref() { let list_item: Vec<ListItem> = match self.props.own.get(PROP_BOOKMARKS) {
None => vec![], Some(PropPayload::Vec(lines)) => lines
Some(lines) => lines
.iter() .iter()
.map(|line| ListItem::new(Span::from(line.content.to_string()))) .map(|x| x.unwrap_str())
.map(|x| ListItem::new(Span::from(x.to_string())))
.collect(), .collect(),
_ => vec![],
}; };
let (fg, bg): (Color, Color) = match self.states.focus { let (fg, bg): (Color, Color) = match self.states.focus {
true => (self.props.foreground, self.props.background), true => (self.props.foreground, self.props.background),
@@ -241,7 +256,7 @@ impl Component for BookmarkList {
List::new(list_item) List::new(list_item)
.block(get_block( .block(get_block(
&self.props.borders, &self.props.borders,
&self.props.texts.title, self.props.title.as_ref(),
self.states.focus, self.states.focus,
)) ))
.start_corner(Corner::TopLeft) .start_corner(Corner::TopLeft)
@@ -260,10 +275,7 @@ impl Component for BookmarkList {
fn update(&mut self, props: Props) -> Msg { fn update(&mut self, props: Props) -> Msg {
self.props = props; self.props = props;
// re-Set list length // re-Set list length
self.states.set_list_len(match &self.props.texts.spans { self.states.set_list_len(Self::bookmarks_len(&self.props));
Some(tokens) => tokens.len(),
None => 0,
});
// Reset list index // Reset list index
self.states.reset_list_index(); self.states.reset_list_index();
Msg::None Msg::None
@@ -347,20 +359,24 @@ mod tests {
.with_foreground(Color::Red) .with_foreground(Color::Red)
.with_background(Color::Blue) .with_background(Color::Blue)
.with_borders(Borders::ALL, BorderType::Double, Color::Red) .with_borders(Borders::ALL, BorderType::Double, Color::Red)
.with_bookmarks( .with_title("filelist", Alignment::Left)
Some(String::from("filelist")), .with_bookmarks(vec![String::from("file1"), String::from("file2")])
vec![String::from("file1"), String::from("file2")],
)
.build(), .build(),
); );
assert_eq!(component.props.foreground, Color::Red); assert_eq!(component.props.foreground, Color::Red);
assert_eq!(component.props.background, Color::Blue); assert_eq!(component.props.background, Color::Blue);
assert_eq!(component.props.visible, true); assert_eq!(component.props.visible, true);
assert_eq!(component.props.title.as_ref().unwrap().text(), "filelist");
assert_eq!( assert_eq!(
component.props.texts.title.as_ref().unwrap().as_str(), component
"filelist" .props
.own
.get(PROP_BOOKMARKS)
.unwrap()
.unwrap_vec()
.len(),
2
); );
assert_eq!(component.props.texts.spans.as_ref().unwrap().len(), 2);
// Verify states // Verify states
assert_eq!(component.states.list_index, 0); assert_eq!(component.states.list_index, 0);
assert_eq!(component.states.list_len, 2); assert_eq!(component.states.list_len, 2);
@@ -384,14 +400,11 @@ mod tests {
// Update // Update
component.update( component.update(
BookmarkListPropsBuilder::from(component.get_props()) BookmarkListPropsBuilder::from(component.get_props())
.with_bookmarks( .with_bookmarks(vec![
Some(String::from("filelist")), String::from("file1"),
vec![ String::from("file2"),
String::from("file1"), String::from("file3"),
String::from("file2"), ])
String::from("file3"),
],
)
.build(), .build(),
); );
// Verify states // Verify states

View File

@@ -30,15 +30,15 @@
use crate::utils::fmt::fmt_color; use crate::utils::fmt::fmt_color;
use crate::utils::parser::parse_color; use crate::utils::parser::parse_color;
// ext // ext
use tuirealm::components::input::{Input, InputPropsBuilder}; use tui_realm_stdlib::input::{Input, InputPropsBuilder};
use tuirealm::event::Event; use tuirealm::event::Event;
use tuirealm::props::{Props, PropsBuilder}; use tuirealm::props::{Alignment, Props, PropsBuilder};
use tuirealm::tui::{ use tuirealm::tui::{
layout::Rect, layout::Rect,
style::Color, style::Color,
widgets::{BorderType, Borders}, widgets::{BorderType, Borders},
}; };
use tuirealm::{Canvas, Component, Msg, Payload, Value}; use tuirealm::{Component, Frame, Msg, Payload, Value};
// -- props // -- props
@@ -98,8 +98,8 @@ impl ColorPickerPropsBuilder {
/// ### with_label /// ### with_label
/// ///
/// Set input label /// Set input label
pub fn with_label(&mut self, label: String) -> &mut Self { pub fn with_label<S: AsRef<str>>(&mut self, label: S, alignment: Alignment) -> &mut Self {
self.puppet.with_label(label); self.puppet.with_label(label, alignment);
self self
} }
@@ -149,7 +149,7 @@ impl Component for ColorPicker {
/// Based on the current properties and states, renders a widget using the provided render engine in the provided Area /// Based on the current properties and states, renders a widget using the provided render engine in the provided Area
/// If focused, cursor is also set (if supported by widget) /// If focused, cursor is also set (if supported by widget)
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
fn render(&self, render: &mut Canvas, area: Rect) { fn render(&self, render: &mut Frame, area: Rect) {
self.input.render(render, area); self.input.render(render, area);
} }
@@ -260,6 +260,7 @@ mod test {
.visible() .visible()
.with_color(&Color::Rgb(204, 170, 0)) .with_color(&Color::Rgb(204, 170, 0))
.with_borders(Borders::ALL, BorderType::Double, Color::Rgb(204, 170, 0)) .with_borders(Borders::ALL, BorderType::Double, Color::Rgb(204, 170, 0))
.with_label("omar", Alignment::Left)
.build(), .build(),
); );
// Focus // Focus

View File

@@ -26,10 +26,10 @@
* SOFTWARE. * SOFTWARE.
*/ */
// ext // ext
use tuirealm::components::utils::get_block; use tui_realm_stdlib::utils::get_block;
use tuirealm::event::{Event, KeyCode, KeyModifiers}; use tuirealm::event::{Event, KeyCode, KeyModifiers};
use tuirealm::props::{ use tuirealm::props::{
BordersProps, PropPayload, PropValue, Props, PropsBuilder, TextParts, TextSpan, Alignment, BlockTitle, BordersProps, PropPayload, PropValue, Props, PropsBuilder,
}; };
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Corner, Rect}, layout::{Corner, Rect},
@@ -37,11 +37,12 @@ use tuirealm::tui::{
text::Span, text::Span,
widgets::{BorderType, Borders, List, ListItem, ListState}, widgets::{BorderType, Borders, List, ListItem, ListState},
}; };
use tuirealm::{Canvas, Component, Msg, Payload, Value}; use tuirealm::{Component, Frame, Msg, Payload, Value};
// -- props // -- props
const PROP_HIGHLIGHT_COLOR: &str = "props-highlight-color"; const PROP_FILES: &str = "files";
const PALETTE_HIGHLIGHT_COLOR: &str = "props-highlight-color";
pub struct FileListPropsBuilder { pub struct FileListPropsBuilder {
props: Option<Props>, props: Option<Props>,
@@ -107,10 +108,7 @@ impl FileListPropsBuilder {
/// Set highlighted color /// Set highlighted color
pub fn with_highlight_color(&mut self, color: Color) -> &mut Self { pub fn with_highlight_color(&mut self, color: Color) -> &mut Self {
if let Some(props) = self.props.as_mut() { if let Some(props) = self.props.as_mut() {
props.own.insert( props.palette.insert(PALETTE_HIGHLIGHT_COLOR, color);
PROP_HIGHLIGHT_COLOR,
PropPayload::One(PropValue::Color(color)),
);
} }
self self
} }
@@ -134,10 +132,17 @@ impl FileListPropsBuilder {
self self
} }
pub fn with_files(&mut self, title: Option<String>, files: Vec<String>) -> &mut Self { pub fn with_title<S: AsRef<str>>(&mut self, text: S, alignment: Alignment) -> &mut Self {
if let Some(props) = self.props.as_mut() { if let Some(props) = self.props.as_mut() {
let files: Vec<TextSpan> = files.into_iter().map(TextSpan::from).collect(); props.title = Some(BlockTitle::new(text, alignment));
props.texts = TextParts::new(title, Some(files)); }
self
}
pub fn with_files(&mut self, files: Vec<String>) -> &mut Self {
if let Some(props) = self.props.as_mut() {
let files: Vec<PropValue> = files.into_iter().map(PropValue::Str).collect();
props.own.insert(PROP_FILES, PropPayload::Vec(files));
} }
self self
} }
@@ -299,32 +304,39 @@ impl FileList {
// Initialize states // Initialize states
let mut states: OwnStates = OwnStates::default(); let mut states: OwnStates = OwnStates::default();
// Init list states // Init list states
states.init_list_states(props.texts.spans.as_ref().map(|x| x.len()).unwrap_or(0)); states.init_list_states(Self::files_len(&props));
FileList { props, states } FileList { props, states }
} }
fn files_len(props: &Props) -> usize {
match props.own.get(PROP_FILES) {
None => 0,
Some(files) => files.unwrap_vec().len(),
}
}
} }
impl Component for FileList { impl Component for FileList {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
fn render(&self, render: &mut Canvas, area: Rect) { fn render(&self, render: &mut Frame, area: Rect) {
if self.props.visible { if self.props.visible {
// Make list // Make list
let list_item: Vec<ListItem> = match self.props.texts.spans.as_ref() { let list_item: Vec<ListItem> = match self.props.own.get(PROP_FILES) {
None => vec![], Some(PropPayload::Vec(lines)) => lines
Some(lines) => lines
.iter() .iter()
.enumerate() .enumerate()
.map(|(num, line)| { .map(|(num, line)| {
let to_display: String = match self.states.is_selected(num) { let to_display: String = match self.states.is_selected(num) {
true => format!("*{}", line.content), true => format!("*{}", line.unwrap_str()),
false => line.content.to_string(), false => line.unwrap_str().to_string(),
}; };
ListItem::new(Span::from(to_display)) ListItem::new(Span::from(to_display))
}) })
.collect(), .collect(),
_ => vec![],
}; };
let highlighted_color: Color = match self.props.own.get(PROP_HIGHLIGHT_COLOR) { let highlighted_color: Color = match self.props.palette.get(PALETTE_HIGHLIGHT_COLOR) {
Some(PropPayload::One(PropValue::Color(c))) => *c, Some(c) => *c,
_ => Color::Reset, _ => Color::Reset,
}; };
let (h_fg, h_bg): (Color, Color) = match self.states.focus { let (h_fg, h_bg): (Color, Color) = match self.states.focus {
@@ -338,7 +350,7 @@ impl Component for FileList {
List::new(list_item) List::new(list_item)
.block(get_block( .block(get_block(
&self.props.borders, &self.props.borders,
&self.props.texts.title, self.props.title.as_ref(),
self.states.focus, self.states.focus,
)) ))
.start_corner(Corner::TopLeft) .start_corner(Corner::TopLeft)
@@ -362,14 +374,7 @@ impl Component for FileList {
fn update(&mut self, props: Props) -> Msg { fn update(&mut self, props: Props) -> Msg {
self.props = props; self.props = props;
// re-Set list states // re-Set list states
self.states.init_list_states( self.states.init_list_states(Self::files_len(&self.props));
self.props
.texts
.spans
.as_ref()
.map(|x| x.len())
.unwrap_or(0),
);
Msg::None Msg::None
} }
@@ -551,24 +556,33 @@ mod tests {
.with_background(Color::Blue) .with_background(Color::Blue)
.with_highlight_color(Color::LightRed) .with_highlight_color(Color::LightRed)
.with_borders(Borders::ALL, BorderType::Double, Color::Red) .with_borders(Borders::ALL, BorderType::Double, Color::Red)
.with_files( .with_title("files", Alignment::Left)
Some(String::from("files")), .with_files(vec![String::from("file1"), String::from("file2")])
vec![String::from("file1"), String::from("file2")],
)
.build(), .build(),
); );
assert_eq!( assert_eq!(
*component.props.own.get(PROP_HIGHLIGHT_COLOR).unwrap(), *component
PropPayload::One(PropValue::Color(Color::LightRed)) .props
.palette
.get(PALETTE_HIGHLIGHT_COLOR)
.unwrap(),
Color::LightRed
); );
assert_eq!(component.props.foreground, Color::Red); assert_eq!(component.props.foreground, Color::Red);
assert_eq!(component.props.background, Color::Blue); assert_eq!(component.props.background, Color::Blue);
assert_eq!(component.props.visible, true); assert_eq!(component.props.visible, true);
assert_eq!(component.props.title.as_ref().unwrap().text(), "files");
assert_eq!( assert_eq!(
component.props.texts.title.as_ref().unwrap().as_str(), component
"files" .props
.own
.get(PROP_FILES)
.as_ref()
.unwrap()
.unwrap_vec()
.len(),
2
); );
assert_eq!(component.props.texts.spans.as_ref().unwrap().len(), 2);
// Verify states // Verify states
assert_eq!(component.states.list_index, 0); assert_eq!(component.states.list_index, 0);
assert_eq!(component.states.selected.len(), 0); assert_eq!(component.states.selected.len(), 0);
@@ -594,14 +608,11 @@ mod tests {
// Update // Update
component.update( component.update(
FileListPropsBuilder::from(component.get_props()) FileListPropsBuilder::from(component.get_props())
.with_files( .with_files(vec![
Some(String::from("filelist")), String::from("file1"),
vec![ String::from("file2"),
String::from("file1"), String::from("file3"),
String::from("file2"), ])
String::from("file3"),
],
)
.build(), .build(),
); );
// Verify states // Verify states
@@ -670,14 +681,11 @@ mod tests {
// Make component // Make component
let mut component: FileList = FileList::new( let mut component: FileList = FileList::new(
FileListPropsBuilder::default() FileListPropsBuilder::default()
.with_files( .with_files(vec![
Some(String::from("files")), String::from("file1"),
vec![ String::from("file2"),
String::from("file1"), String::from("file3"),
String::from("file2"), ])
String::from("file3"),
],
)
.build(), .build(),
); );
// Get state // Get state
@@ -735,10 +743,7 @@ mod tests {
// Update files // Update files
component.update( component.update(
FileListPropsBuilder::from(component.get_props()) FileListPropsBuilder::from(component.get_props())
.with_files( .with_files(vec![String::from("file1"), String::from("file2")])
Some(String::from("filelist")),
vec![String::from("file1"), String::from("file2")],
)
.build(), .build(),
); );
// Selection should now be empty // Selection should now be empty

View File

@@ -26,18 +26,22 @@
* SOFTWARE. * SOFTWARE.
*/ */
// ext // ext
use tuirealm::components::utils::{get_block, wrap_spans}; use tui_realm_stdlib::utils::{get_block, wrap_spans};
use tuirealm::event::{Event, KeyCode}; use tuirealm::event::{Event, KeyCode};
use tuirealm::props::{BordersProps, Props, PropsBuilder, Table as TextTable, TextParts}; use tuirealm::props::{
Alignment, BlockTitle, BordersProps, Props, PropsBuilder, Table as TextTable,
};
use tuirealm::tui::{ use tuirealm::tui::{
layout::{Corner, Rect}, layout::{Corner, Rect},
style::{Color, Style}, style::{Color, Style},
widgets::{BorderType, Borders, List, ListItem, ListState}, widgets::{BorderType, Borders, List, ListItem, ListState},
}; };
use tuirealm::{Canvas, Component, Msg, Payload, Value}; use tuirealm::{Component, Frame, Msg, Payload, PropPayload, PropValue, Value};
// -- props // -- props
const PROP_TABLE: &str = "table";
pub struct LogboxPropsBuilder { pub struct LogboxPropsBuilder {
props: Option<Props>, props: Option<Props>,
} }
@@ -106,9 +110,18 @@ impl LogboxPropsBuilder {
self self
} }
pub fn with_log(&mut self, title: Option<String>, table: TextTable) -> &mut Self { pub fn with_title<S: AsRef<str>>(&mut self, text: S, alignment: Alignment) -> &mut Self {
if let Some(props) = self.props.as_mut() { if let Some(props) = self.props.as_mut() {
props.texts = TextParts::table(title, table); props.title = Some(BlockTitle::new(text, alignment));
}
self
}
pub fn with_log(&mut self, table: TextTable) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props
.own
.insert(PROP_TABLE, PropPayload::One(PropValue::Table(table)));
} }
self self
} }
@@ -198,33 +211,37 @@ impl LogBox {
// Initialize states // Initialize states
let mut states: OwnStates = OwnStates::default(); let mut states: OwnStates = OwnStates::default();
// Set list length // Set list length
states.set_list_len(match &props.texts.table { states.set_list_len(Self::table_len(&props));
Some(rows) => rows.len(),
None => 0,
});
// Reset list index // Reset list index
states.reset_list_index(); states.reset_list_index();
LogBox { props, states } LogBox { props, states }
} }
fn table_len(props: &Props) -> usize {
match props.own.get(PROP_TABLE) {
Some(PropPayload::One(PropValue::Table(table))) => table.len(),
_ => 0,
}
}
} }
impl Component for LogBox { impl Component for LogBox {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
fn render(&self, render: &mut Canvas, area: Rect) { fn render(&self, render: &mut Frame, area: Rect) {
if self.props.visible { if self.props.visible {
let width: usize = area.width as usize - 4; let width: usize = area.width as usize - 4;
// Make list // Make list
let list_items: Vec<ListItem> = match self.props.texts.table.as_ref() { let list_items: Vec<ListItem> = match self.props.own.get(PROP_TABLE) {
None => Vec::new(), Some(PropPayload::One(PropValue::Table(table))) => table
Some(table) => table
.iter() .iter()
.map(|row| ListItem::new(wrap_spans(row, width, &self.props))) .map(|row| ListItem::new(wrap_spans(row, width, &self.props)))
.collect(), // Make List item from TextSpan .collect(), // Make List item from TextSpan
_ => Vec::new(),
}; };
let w = List::new(list_items) let w = List::new(list_items)
.block(get_block( .block(get_block(
&self.props.borders, &self.props.borders,
&self.props.texts.title, self.props.title.as_ref(),
self.states.focus, self.states.focus,
)) ))
.start_corner(Corner::BottomLeft) .start_corner(Corner::BottomLeft)
@@ -240,10 +257,7 @@ impl Component for LogBox {
fn update(&mut self, props: Props) -> Msg { fn update(&mut self, props: Props) -> Msg {
self.props = props; self.props = props;
// re-Set list length // re-Set list length
self.states.set_list_len(match &self.props.texts.table { self.states.set_list_len(Self::table_len(&self.props));
Some(rows) => rows.len(),
None => 0,
});
// Reset list index // Reset list index
self.states.reset_list_index(); self.states.reset_list_index();
Msg::None Msg::None
@@ -323,8 +337,8 @@ mod tests {
.visible() .visible()
.with_borders(Borders::ALL, BorderType::Double, Color::Red) .with_borders(Borders::ALL, BorderType::Double, Color::Red)
.with_background(Color::Blue) .with_background(Color::Blue)
.with_title("log", Alignment::Left)
.with_log( .with_log(
Some(String::from("Log")),
TableBuilder::default() TableBuilder::default()
.add_col(TextSpan::from("12:29")) .add_col(TextSpan::from("12:29"))
.add_col(TextSpan::from("system crashed")) .add_col(TextSpan::from("system crashed"))
@@ -337,11 +351,7 @@ mod tests {
); );
assert_eq!(component.props.visible, true); assert_eq!(component.props.visible, true);
assert_eq!(component.props.background, Color::Blue); assert_eq!(component.props.background, Color::Blue);
assert_eq!( assert_eq!(component.props.title.as_ref().unwrap().text(), "Log");
component.props.texts.title.as_ref().unwrap().as_str(),
"Log"
);
assert_eq!(component.props.texts.table.as_ref().unwrap().len(), 2);
// Verify states // Verify states
assert_eq!(component.states.list_index, 0); assert_eq!(component.states.list_index, 0);
assert_eq!(component.states.list_len, 2); assert_eq!(component.states.list_len, 2);
@@ -364,7 +374,6 @@ mod tests {
component.update( component.update(
LogboxPropsBuilder::from(component.get_props()) LogboxPropsBuilder::from(component.get_props())
.with_log( .with_log(
Some(String::from("Log")),
TableBuilder::default() TableBuilder::default()
.add_col(TextSpan::from("12:29")) .add_col(TextSpan::from("12:29"))
.add_col(TextSpan::from("system crashed")) .add_col(TextSpan::from("system crashed"))

View File

@@ -30,4 +30,3 @@ pub mod bookmark_list;
pub mod color_picker; pub mod color_picker;
pub mod file_list; pub mod file_list;
pub mod logbox; pub mod logbox;
pub mod msgbox;

View File

@@ -1,268 +0,0 @@
//! ## MsgBox
//!
//! `MsgBox` component renders a simple readonly no event associated centered text
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use crate::utils::fmt::align_text_center;
// ext
use tuirealm::components::utils::{get_block, use_or_default_styles};
use tuirealm::event::Event;
use tuirealm::props::{BordersProps, Props, PropsBuilder, TextParts, TextSpan};
use tuirealm::tui::{
layout::{Corner, Rect},
style::{Color, Modifier, Style},
text::{Span, Spans},
widgets::{BorderType, Borders, List, ListItem},
};
use tuirealm::{Canvas, Component, Msg, Payload};
// -- Props
pub struct MsgBoxPropsBuilder {
props: Option<Props>,
}
impl Default for MsgBoxPropsBuilder {
fn default() -> Self {
MsgBoxPropsBuilder {
props: Some(Props::default()),
}
}
}
impl PropsBuilder for MsgBoxPropsBuilder {
fn build(&mut self) -> Props {
self.props.take().unwrap()
}
fn hidden(&mut self) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.visible = false;
}
self
}
fn visible(&mut self) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.visible = true;
}
self
}
}
impl From<Props> for MsgBoxPropsBuilder {
fn from(props: Props) -> Self {
MsgBoxPropsBuilder { props: Some(props) }
}
}
impl MsgBoxPropsBuilder {
pub fn with_foreground(&mut self, color: Color) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.foreground = color;
}
self
}
pub fn bold(&mut self) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.modifiers |= Modifier::BOLD;
}
self
}
pub fn blink(&mut self) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.modifiers |= Modifier::SLOW_BLINK;
}
self
}
pub fn with_borders(
&mut self,
borders: Borders,
variant: BorderType,
color: Color,
) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.borders = BordersProps {
borders,
variant,
color,
}
}
self
}
pub fn with_texts(&mut self, title: Option<String>, texts: Vec<TextSpan>) -> &mut Self {
if let Some(props) = self.props.as_mut() {
props.texts = TextParts::new(title, Some(texts));
}
self
}
}
// -- component
pub struct MsgBox {
props: Props,
}
impl MsgBox {
/// ### new
///
/// Instantiate a new Text component
pub fn new(props: Props) -> Self {
MsgBox { props }
}
}
impl Component for MsgBox {
#[cfg(not(tarpaulin_include))]
fn render(&self, render: &mut Canvas, area: Rect) {
// Make a Span
if self.props.visible {
let lines: Vec<ListItem> = match self.props.texts.spans.as_ref() {
None => Vec::new(),
Some(rows) => {
let mut lines: Vec<ListItem> = Vec::new();
for line in rows.iter() {
// Keep line color, or use default
let (fg, bg, modifiers) = use_or_default_styles(&self.props, line);
let message_row =
textwrap::wrap(line.content.as_str(), area.width as usize);
for msg in message_row.iter() {
lines.push(ListItem::new(Spans::from(vec![Span::styled(
align_text_center(msg, area.width),
Style::default().add_modifier(modifiers).fg(fg).bg(bg),
)])));
}
}
lines
}
};
render.render_widget(
List::new(lines)
.block(get_block(
&self.props.borders,
&self.props.texts.title,
true,
))
.start_corner(Corner::TopLeft)
.style(
Style::default()
.fg(self.props.foreground)
.bg(self.props.background),
),
area,
);
}
}
fn update(&mut self, props: Props) -> Msg {
self.props = props;
// Return None
Msg::None
}
fn get_props(&self) -> Props {
self.props.clone()
}
fn on(&mut self, ev: Event) -> Msg {
// Return key
if let Event::Key(key) = ev {
Msg::OnKey(key)
} else {
Msg::None
}
}
fn get_state(&self) -> Payload {
Payload::None
}
fn blur(&mut self) {}
fn active(&mut self) {}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use tuirealm::event::{KeyCode, KeyEvent};
use tuirealm::props::{TextSpan, TextSpanBuilder};
use tuirealm::tui::style::Color;
#[test]
fn test_ui_components_msgbox() {
let mut component: MsgBox = MsgBox::new(
MsgBoxPropsBuilder::default()
.hidden()
.visible()
.with_foreground(Color::Red)
.bold()
.blink()
.with_borders(Borders::ALL, BorderType::Double, Color::Red)
.with_texts(
None,
vec![
TextSpan::from("Press "),
TextSpanBuilder::new("<ESC>")
.with_foreground(Color::Cyan)
.bold()
.build(),
TextSpan::from(" to quit"),
],
)
.build(),
);
assert_eq!(component.props.foreground, Color::Red);
assert!(component.props.modifiers.intersects(Modifier::BOLD));
assert_eq!(component.props.visible, true);
assert_eq!(component.props.texts.spans.as_ref().unwrap().len(), 3);
component.active();
component.blur();
// Update
let props = MsgBoxPropsBuilder::from(component.get_props())
.hidden()
.with_foreground(Color::Yellow)
.build();
assert_eq!(component.update(props), Msg::None);
assert_eq!(component.props.visible, false);
assert_eq!(component.props.foreground, Color::Yellow);
// Get value
assert_eq!(component.get_state(), Payload::None);
// Event
assert_eq!(
component.on(Event::Key(KeyEvent::from(KeyCode::Delete))),
Msg::OnKey(KeyEvent::from(KeyCode::Delete))
);
}
}

View File

@@ -100,23 +100,6 @@ pub fn fmt_millis(duration: Duration) -> String {
format!("{}.{:0width$}", seconds, millis, width = 3) format!("{}.{:0width$}", seconds, millis, width = 3)
} }
/// align_text_center
///
/// Align text to center for a given width
pub fn align_text_center(text: &str, width: u16) -> String {
let indent_size: usize = match (width as usize) >= text.len() {
// NOTE: The check prevents underflow
true => (width as usize - text.len()) / 2,
false => 0,
};
textwrap::indent(
text,
(0..indent_size).map(|_| " ").collect::<String>().as_str(),
)
.trim_end()
.to_string()
}
/// ### elide_path /// ### elide_path
/// ///
/// Elide a path if longer than width /// Elide a path if longer than width
@@ -362,18 +345,6 @@ mod tests {
); );
} }
#[test]
fn test_utils_align_text_center() {
assert_eq!(
align_text_center("hello world!", 24),
String::from(" hello world!")
);
// Bad case
assert_eq!(
align_text_center("hello world!", 8),
String::from("hello world!")
);
}
#[test] #[test]
fn test_utils_fmt_millis() { fn test_utils_fmt_millis() {
assert_eq!( assert_eq!(