mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Merge pull request #30 from veeso/issue-8-synchronized-browsing-of-local-and-remote-directories
Synchronized browsing of local and remote directories
This commit is contained in:
@@ -21,14 +21,20 @@
|
||||
|
||||
Released on FIXME: ??
|
||||
|
||||
- **Synchronized browsing**:
|
||||
- Added the possibility to enabled the synchronized brower navigation
|
||||
- when you enter a directory, the same directory will be entered on the other tab
|
||||
- Enable sync browser with `<Y>`
|
||||
- **Remote and Local hosts file formatter**:
|
||||
- Added the possibility to set different formatters for local and remote hosts
|
||||
- Enhancements
|
||||
- Added a status bar in the file explorer showing whether the sync browser is enabled and which file sorting mode is selected
|
||||
- Bugfix:
|
||||
- Fixed wrong text wrap in log box
|
||||
- Fixed error message not being shown after an upload failure
|
||||
- [Issue 23](https://github.com/veeso/termscp/issues/23): Remove created file if transfer failed or was abrupted
|
||||
- Dependencies:
|
||||
- Added `tui-realm 0.2.1`
|
||||
- Added `tui-realm 0.2.2`
|
||||
- Removed `tui` (as direct dependency)
|
||||
- Updated `regex` to `1.5.3`
|
||||
|
||||
|
||||
41
Cargo.lock
generated
41
Cargo.lock
generated
@@ -219,22 +219,6 @@ dependencies = [
|
||||
"debug-helper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi 0.6.2",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot 0.11.1",
|
||||
"signal-hook",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.19.0"
|
||||
@@ -242,7 +226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi 0.7.0",
|
||||
"crossterm_winapi",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mio",
|
||||
@@ -251,15 +235,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.7.0"
|
||||
@@ -1275,7 +1250,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"content_inspector",
|
||||
"crossterm 0.19.0",
|
||||
"crossterm",
|
||||
"dirs",
|
||||
"edit",
|
||||
"ftp4",
|
||||
@@ -1379,24 +1354,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.14.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ced152a8e9295a5b168adc254074525c17ac4a83c90b2716274cc38118bddc9"
|
||||
checksum = "861d8f3ad314ede6219bcb2ab844054b1de279ee37a9bc38e3d606f9d3fb2a71"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"crossterm 0.18.2",
|
||||
"crossterm",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tuirealm"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cade7d98a40066164d9c8e52bf01f86ecaa4fa810e854855a5a4ec3deca83c2"
|
||||
checksum = "963c590368d3ab9be44ee0fccb7ad86f3611dfb4536b02be0e25cbc5685d51bb"
|
||||
dependencies = [
|
||||
"crossterm 0.19.0",
|
||||
"crossterm",
|
||||
"textwrap",
|
||||
"tui",
|
||||
"unicode-width",
|
||||
|
||||
@@ -46,7 +46,7 @@ tempfile = "3.1.0"
|
||||
textwrap = "0.13.4"
|
||||
thiserror = "^1.0.0"
|
||||
toml = "0.5.8"
|
||||
tuirealm = { version = "0.2.1", features = [ "with-components" ] }
|
||||
tuirealm = { version = "0.2.2", features = [ "with-components" ] }
|
||||
whoami = "1.1.1"
|
||||
wildmatch = "2.0.0"
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ Password can be basically provided through 3 ways when address argument is provi
|
||||
| `<S>` | Save file as... | Save |
|
||||
| `<U>` | Go to parent directory | Upper |
|
||||
| `<X>` | Execute a command | eXecute |
|
||||
| `<Y>` | Toggle synchronized browsing | sYnc |
|
||||
| `<DEL>` | Delete file | |
|
||||
| `<CTRL+C>` | Abort file transfer process | |
|
||||
|
||||
|
||||
@@ -32,36 +32,156 @@ use tuirealm::{Payload, Value};
|
||||
use std::path::PathBuf;
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// ### action_enter_local_dir
|
||||
///
|
||||
/// Enter a directory on local host from entry
|
||||
/// Return true whether the directory changed
|
||||
pub(super) fn action_enter_local_dir(&mut self, entry: FsEntry, block_sync: bool) -> bool {
|
||||
match entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.local_changedir(dir.abs_path.as_path(), true);
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_remote_dir(dir.name, true);
|
||||
}
|
||||
true
|
||||
}
|
||||
FsEntry::File(file) => {
|
||||
match &file.symlink {
|
||||
Some(symlink_entry) => {
|
||||
// If symlink and is directory, point to symlink
|
||||
match &**symlink_entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.local_changedir(dir.abs_path.as_path(), true);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_remote_dir(dir.name.clone(), true);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_enter_remote_dir
|
||||
///
|
||||
/// Enter a directory on local host from entry
|
||||
/// Return true whether the directory changed
|
||||
pub(super) fn action_enter_remote_dir(&mut self, entry: FsEntry, block_sync: bool) -> bool {
|
||||
match entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.remote_changedir(dir.abs_path.as_path(), true);
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_local_dir(dir.name, true);
|
||||
}
|
||||
true
|
||||
}
|
||||
FsEntry::File(file) => {
|
||||
match &file.symlink {
|
||||
Some(symlink_entry) => {
|
||||
// If symlink and is directory, point to symlink
|
||||
match &**symlink_entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.remote_changedir(dir.abs_path.as_path(), true);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_local_dir(dir.name.clone(), true);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_change_local_dir
|
||||
///
|
||||
/// Change local directory reading value from input
|
||||
pub(super) fn action_change_local_dir(&mut self, input: String) {
|
||||
let dir_path: PathBuf = PathBuf::from(input.as_str());
|
||||
let abs_dir_path: PathBuf = match dir_path.is_relative() {
|
||||
true => {
|
||||
let mut d: PathBuf = self.local.wrkdir.clone();
|
||||
d.push(dir_path);
|
||||
d
|
||||
}
|
||||
false => dir_path,
|
||||
};
|
||||
self.local_changedir(abs_dir_path.as_path(), true);
|
||||
pub(super) fn action_change_local_dir(&mut self, input: String, block_sync: bool) {
|
||||
let dir_path: PathBuf = self.local_to_abs_path(PathBuf::from(input.as_str()).as_path());
|
||||
self.local_changedir(dir_path.as_path(), true);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_remote_dir(input, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_change_remote_dir
|
||||
///
|
||||
/// Change remote directory reading value from input
|
||||
pub(super) fn action_change_remote_dir(&mut self, input: String) {
|
||||
let dir_path: PathBuf = PathBuf::from(input.as_str());
|
||||
let abs_dir_path: PathBuf = match dir_path.is_relative() {
|
||||
true => {
|
||||
let mut wrkdir: PathBuf = self.remote.wrkdir.clone();
|
||||
wrkdir.push(dir_path);
|
||||
wrkdir
|
||||
pub(super) fn action_change_remote_dir(&mut self, input: String, block_sync: bool) {
|
||||
let dir_path: PathBuf = self.remote_to_abs_path(PathBuf::from(input.as_str()).as_path());
|
||||
self.remote_changedir(dir_path.as_path(), true);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_local_dir(input, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_go_to_previous_local_dir
|
||||
///
|
||||
/// Go to previous directory from localhost
|
||||
pub(super) fn action_go_to_previous_local_dir(&mut self, block_sync: bool) {
|
||||
if let Some(d) = self.local.popd() {
|
||||
self.local_changedir(d.as_path(), false);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_previous_remote_dir(true);
|
||||
}
|
||||
false => dir_path,
|
||||
};
|
||||
self.remote_changedir(abs_dir_path.as_path(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_go_to_previous_remote_dir
|
||||
///
|
||||
/// Go to previous directory from remote host
|
||||
pub(super) fn action_go_to_previous_remote_dir(&mut self, block_sync: bool) {
|
||||
if let Some(d) = self.remote.popd() {
|
||||
self.remote_changedir(d.as_path(), false);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_previous_local_dir(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_go_to_local_upper_dir
|
||||
///
|
||||
/// Go to upper directory on local host
|
||||
pub(super) fn action_go_to_local_upper_dir(&mut self, block_sync: bool) {
|
||||
// Get pwd
|
||||
let path: PathBuf = self.local.wrkdir.clone();
|
||||
// Go to parent directory
|
||||
if let Some(parent) = path.as_path().parent() {
|
||||
self.local_changedir(parent, true);
|
||||
// If sync is enabled update remote too
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_remote_upper_dir(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// #### action_go_to_remote_upper_dir
|
||||
///
|
||||
/// Go to upper directory on remote host
|
||||
pub(super) fn action_go_to_remote_upper_dir(&mut self, block_sync: bool) {
|
||||
// Get pwd
|
||||
let path: PathBuf = self.remote.wrkdir.clone();
|
||||
// Go to parent directory
|
||||
if let Some(parent) = path.as_path().parent() {
|
||||
self.remote_changedir(parent, true);
|
||||
// If sync is enabled update local too
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_local_upper_dir(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ### action_local_copy
|
||||
|
||||
@@ -28,7 +28,7 @@ use crate::system::environment;
|
||||
use crate::system::sshkey_storage::SshKeyStorage;
|
||||
// Ext
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// ### log
|
||||
@@ -175,4 +175,32 @@ impl FileTransferActivity {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// ### local_to_abs_path
|
||||
///
|
||||
/// Convert a path to absolute according to local explorer
|
||||
pub(super) fn local_to_abs_path(&self, path: &Path) -> PathBuf {
|
||||
match path.is_relative() {
|
||||
true => {
|
||||
let mut d: PathBuf = self.local.wrkdir.clone();
|
||||
d.push(path);
|
||||
d
|
||||
}
|
||||
false => path.to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ### remote_to_abs_path
|
||||
///
|
||||
/// Convert a path to absolute according to remote explorer
|
||||
pub(super) fn remote_to_abs_path(&self, path: &Path) -> PathBuf {
|
||||
match path.is_relative() {
|
||||
true => {
|
||||
let mut wrkdir: PathBuf = self.remote.wrkdir.clone();
|
||||
wrkdir.push(path);
|
||||
wrkdir
|
||||
}
|
||||
false => path.to_path_buf(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ const COMPONENT_RADIO_DELETE: &str = "RADIO_DELETE";
|
||||
const COMPONENT_RADIO_DISCONNECT: &str = "RADIO_DISCONNECT";
|
||||
const COMPONENT_RADIO_QUIT: &str = "RADIO_QUIT";
|
||||
const COMPONENT_RADIO_SORTING: &str = "RADIO_SORTING";
|
||||
const COMPONENT_SPAN_STATUS_BAR: &str = "STATUS_BAR";
|
||||
const COMPONENT_LIST_FILEINFO: &str = "LIST_FILEINFO";
|
||||
|
||||
/// ## FileExplorerTab
|
||||
@@ -202,6 +203,30 @@ impl Default for TransferStates {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Browser
|
||||
///
|
||||
/// Browser contains the browser options
|
||||
struct Browser {
|
||||
pub sync_browsing: bool,
|
||||
}
|
||||
|
||||
impl Default for Browser {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sync_browsing: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Browser {
|
||||
/// ### toggle_sync_browsing
|
||||
///
|
||||
/// Invert the current state for the sync browsing
|
||||
pub fn toggle_sync_browsing(&mut self) {
|
||||
self.sync_browsing = !self.sync_browsing;
|
||||
}
|
||||
}
|
||||
|
||||
/// ## FileTransferActivity
|
||||
///
|
||||
/// FileTransferActivity is the data holder for the file transfer activity
|
||||
@@ -217,6 +242,7 @@ pub struct FileTransferActivity {
|
||||
log_records: VecDeque<LogRecord>, // Log records
|
||||
log_size: usize, // Log records size (max)
|
||||
transfer: TransferStates, // Transfer states
|
||||
browser: Browser, // Browser states
|
||||
}
|
||||
|
||||
impl FileTransferActivity {
|
||||
@@ -246,6 +272,7 @@ impl FileTransferActivity {
|
||||
log_records: VecDeque::with_capacity(256), // 256 events is enough I guess
|
||||
log_size: 256, // Must match with capacity
|
||||
transfer: TransferStates::default(),
|
||||
browser: Browser::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,9 @@ impl FileTransferActivity {
|
||||
}
|
||||
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_BACKSPACE) => {
|
||||
// Go to previous directory
|
||||
if let Some(d) = self.local.popd() {
|
||||
self.local_changedir(d.as_path(), false);
|
||||
self.action_go_to_previous_local_dir(false);
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
// Reload file list component
|
||||
self.update_local_filelist()
|
||||
@@ -86,25 +87,14 @@ impl FileTransferActivity {
|
||||
entry = Some(e.clone());
|
||||
}
|
||||
if let Some(entry) = entry {
|
||||
// If directory, enter directory, otherwise check if symlink
|
||||
match entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.local_changedir(dir.abs_path.as_path(), true);
|
||||
self.update_local_filelist()
|
||||
}
|
||||
FsEntry::File(file) => {
|
||||
// Check if symlink
|
||||
match &file.symlink {
|
||||
Some(pointer) => match &**pointer {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.local_changedir(dir.abs_path.as_path(), true);
|
||||
self.update_local_filelist()
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
if self.action_enter_local_dir(entry, false) {
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
self.update_local_filelist()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -170,13 +160,11 @@ impl FileTransferActivity {
|
||||
self.update_local_filelist()
|
||||
}
|
||||
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_U) => {
|
||||
// Get pwd
|
||||
let path: PathBuf = self.local.wrkdir.clone();
|
||||
// Go to parent directory
|
||||
if let Some(parent) = path.as_path().parent() {
|
||||
self.local_changedir(parent, true);
|
||||
// Reload file list component
|
||||
self.action_go_to_local_upper_dir(false);
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
// Reload file list component
|
||||
self.update_local_filelist()
|
||||
}
|
||||
// -- remote tab
|
||||
@@ -193,27 +181,14 @@ impl FileTransferActivity {
|
||||
entry = Some(e.clone());
|
||||
}
|
||||
if let Some(entry) = entry {
|
||||
// If directory, enter directory; if file, check if is symlink
|
||||
match entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.remote_changedir(dir.abs_path.as_path(), true);
|
||||
self.update_remote_filelist()
|
||||
}
|
||||
FsEntry::File(file) => {
|
||||
match &file.symlink {
|
||||
Some(symlink_entry) => {
|
||||
// If symlink and is directory, point to symlink
|
||||
match &**symlink_entry {
|
||||
FsEntry::Directory(dir) => {
|
||||
self.remote_changedir(dir.abs_path.as_path(), true);
|
||||
self.update_remote_filelist()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
if self.action_enter_remote_dir(entry, false) {
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
self.update_remote_filelist()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -234,8 +209,10 @@ impl FileTransferActivity {
|
||||
}
|
||||
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_BACKSPACE) => {
|
||||
// Go to previous directory
|
||||
if let Some(d) = self.remote.popd() {
|
||||
self.remote_changedir(d.as_path(), false);
|
||||
self.action_go_to_previous_remote_dir(false);
|
||||
// If sync is enabled update local too
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
// Reload file list component
|
||||
self.update_remote_filelist()
|
||||
@@ -286,11 +263,9 @@ impl FileTransferActivity {
|
||||
self.update_remote_filelist()
|
||||
}
|
||||
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_U) => {
|
||||
// Get pwd
|
||||
let path: PathBuf = self.remote.wrkdir.clone();
|
||||
// Go to parent directory
|
||||
if let Some(parent) = path.as_path().parent() {
|
||||
self.remote_changedir(parent, true);
|
||||
self.action_go_to_remote_upper_dir(false);
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
// Reload file list component
|
||||
self.update_remote_filelist()
|
||||
@@ -357,6 +332,14 @@ impl FileTransferActivity {
|
||||
self.mount_exec();
|
||||
None
|
||||
}
|
||||
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_Y)
|
||||
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_Y) => {
|
||||
// Toggle browser sync
|
||||
self.browser.toggle_sync_browsing();
|
||||
// Update status bar
|
||||
self.refresh_status_bar();
|
||||
None
|
||||
}
|
||||
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_ESC)
|
||||
| (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_ESC)
|
||||
| (COMPONENT_LOG_BOX, &MSG_KEY_ESC) => {
|
||||
@@ -505,12 +488,24 @@ impl FileTransferActivity {
|
||||
}
|
||||
(COMPONENT_INPUT_GOTO, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
|
||||
match self.tab {
|
||||
FileExplorerTab::Local => self.action_change_local_dir(input.to_string()),
|
||||
FileExplorerTab::Remote => self.action_change_remote_dir(input.to_string()),
|
||||
FileExplorerTab::Local => {
|
||||
self.action_change_local_dir(input.to_string(), false)
|
||||
}
|
||||
FileExplorerTab::Remote => {
|
||||
self.action_change_remote_dir(input.to_string(), false)
|
||||
}
|
||||
_ => panic!("Found tab doesn't support GOTO"),
|
||||
}
|
||||
// Umount
|
||||
self.umount_goto();
|
||||
// Reload files if sync
|
||||
if self.browser.sync_browsing {
|
||||
match self.tab {
|
||||
FileExplorerTab::Remote => self.update_local_filelist(),
|
||||
FileExplorerTab::Local => self.update_remote_filelist(),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// Reload files
|
||||
match self.tab {
|
||||
FileExplorerTab::Local => self.update_local_filelist(),
|
||||
@@ -682,6 +677,8 @@ impl FileTransferActivity {
|
||||
FileExplorerTab::Remote => self.remote.sort_by(sorting),
|
||||
_ => panic!("Found result doesn't support SORTING"),
|
||||
}
|
||||
// Update status bar
|
||||
self.refresh_status_bar();
|
||||
// Reload files
|
||||
match self.tab {
|
||||
FileExplorerTab::Local => self.update_local_filelist(),
|
||||
|
||||
@@ -49,6 +49,7 @@ use tuirealm::components::{
|
||||
input::{Input, InputPropsBuilder},
|
||||
progress_bar::{ProgressBar, ProgressBarPropsBuilder},
|
||||
radio::{Radio, RadioPropsBuilder},
|
||||
span::{Span, SpanPropsBuilder},
|
||||
table::{Table, TablePropsBuilder},
|
||||
};
|
||||
use tuirealm::props::{PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder};
|
||||
@@ -98,6 +99,13 @@ impl FileTransferActivity {
|
||||
.build(),
|
||||
)),
|
||||
);
|
||||
// Mount status bar
|
||||
self.view.mount(
|
||||
super::COMPONENT_SPAN_STATUS_BAR,
|
||||
Box::new(Span::new(SpanPropsBuilder::default().build())),
|
||||
);
|
||||
// Load process bar
|
||||
self.refresh_status_bar();
|
||||
// Update components
|
||||
let _ = self.update_local_filelist();
|
||||
let _ = self.update_remote_filelist();
|
||||
@@ -131,6 +139,11 @@ impl FileTransferActivity {
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.direction(Direction::Horizontal)
|
||||
.split(chunks[0]);
|
||||
// Create log box chunks
|
||||
let bottom_chunks = Layout::default()
|
||||
.constraints([Constraint::Length(1), Constraint::Length(10)].as_ref())
|
||||
.direction(Direction::Vertical)
|
||||
.split(chunks[1]);
|
||||
// If width is unset in the storage, set width
|
||||
if !store.isset(super::STORAGE_EXPLORER_WIDTH) {
|
||||
store.set_unsigned(super::STORAGE_EXPLORER_WIDTH, tabs_chunks[0].width as usize);
|
||||
@@ -159,8 +172,11 @@ impl FileTransferActivity {
|
||||
.view
|
||||
.render(super::COMPONENT_EXPLORER_REMOTE, f, tabs_chunks[1]),
|
||||
}
|
||||
// Draw log box
|
||||
self.view.render(super::COMPONENT_LOG_BOX, f, chunks[1]);
|
||||
// Draw log box and status bar
|
||||
self.view
|
||||
.render(super::COMPONENT_LOG_BOX, f, bottom_chunks[1]);
|
||||
self.view
|
||||
.render(super::COMPONENT_SPAN_STATUS_BAR, f, bottom_chunks[0]);
|
||||
// @! Draw popups
|
||||
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_COPY) {
|
||||
if props.visible {
|
||||
@@ -793,6 +809,41 @@ impl FileTransferActivity {
|
||||
self.view.umount(super::COMPONENT_LIST_FILEINFO);
|
||||
}
|
||||
|
||||
pub(super) fn refresh_status_bar(&mut self) {
|
||||
let bar_spans: Vec<TextSpan> = vec![
|
||||
TextSpanBuilder::new("Synchronized Browsing: ")
|
||||
.with_foreground(Color::LightGreen)
|
||||
.build(),
|
||||
TextSpanBuilder::new(match self.browser.sync_browsing {
|
||||
true => "ON ",
|
||||
false => "OFF",
|
||||
})
|
||||
.with_foreground(Color::LightGreen)
|
||||
.reversed()
|
||||
.build(),
|
||||
TextSpanBuilder::new(" Localhost file sorting: ")
|
||||
.with_foreground(Color::LightYellow)
|
||||
.build(),
|
||||
TextSpanBuilder::new(Self::get_file_sorting_str(self.local.get_file_sorting()))
|
||||
.with_foreground(Color::LightYellow)
|
||||
.reversed()
|
||||
.build(),
|
||||
TextSpanBuilder::new(" Remote host file sorting: ")
|
||||
.with_foreground(Color::LightBlue)
|
||||
.build(),
|
||||
TextSpanBuilder::new(Self::get_file_sorting_str(self.remote.get_file_sorting()))
|
||||
.with_foreground(Color::LightBlue)
|
||||
.reversed()
|
||||
.build(),
|
||||
];
|
||||
if let Some(props) = self.view.get_props(super::COMPONENT_SPAN_STATUS_BAR) {
|
||||
self.view.update(
|
||||
super::COMPONENT_SPAN_STATUS_BAR,
|
||||
SpanPropsBuilder::from(props).with_spans(bar_spans).build(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ### mount_help
|
||||
///
|
||||
/// Mount help
|
||||
@@ -975,6 +1026,22 @@ impl FileTransferActivity {
|
||||
)
|
||||
.add_col(TextSpan::from(" Go to parent directory"))
|
||||
.add_row()
|
||||
.add_col(
|
||||
TextSpanBuilder::new("<X>")
|
||||
.bold()
|
||||
.with_foreground(Color::Cyan)
|
||||
.build(),
|
||||
)
|
||||
.add_col(TextSpan::from(" Execute shell command"))
|
||||
.add_row()
|
||||
.add_col(
|
||||
TextSpanBuilder::new("<Y>")
|
||||
.bold()
|
||||
.with_foreground(Color::Cyan)
|
||||
.build(),
|
||||
)
|
||||
.add_col(TextSpan::from(" Toggle synchronized browsing"))
|
||||
.add_row()
|
||||
.add_col(
|
||||
TextSpanBuilder::new("<DEL|E>")
|
||||
.bold()
|
||||
@@ -1002,4 +1069,13 @@ impl FileTransferActivity {
|
||||
pub(super) fn umount_help(&mut self) {
|
||||
self.view.umount(super::COMPONENT_TEXT_HELP);
|
||||
}
|
||||
|
||||
fn get_file_sorting_str(mode: FileSorting) -> &'static str {
|
||||
match mode {
|
||||
FileSorting::ByName => "By name",
|
||||
FileSorting::ByCreationTime => "By creation time",
|
||||
FileSorting::ByModifyTime => "By modify time",
|
||||
FileSorting::BySize => "By size",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,11 +179,11 @@ pub const MSG_KEY_CHAR_X: Msg = Msg::OnKey(KeyEvent {
|
||||
code: KeyCode::Char('x'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
});
|
||||
/*
|
||||
pub const MSG_KEY_CHAR_Y: Msg = Msg::OnKey(KeyEvent {
|
||||
code: KeyCode::Char('y'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
});
|
||||
/*
|
||||
pub const MSG_KEY_CHAR_Z: Msg = Msg::OnKey(KeyEvent {
|
||||
code: KeyCode::Char('z'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
|
||||
Reference in New Issue
Block a user