mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Merge pull request #34 from veeso/double-progress-bar
Double progress bar
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
|
||||
Released on FIXME: ??
|
||||
|
||||
🌸 The Spring Update 🌷
|
||||
|
||||
- **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
|
||||
@@ -41,6 +43,11 @@ Released on FIXME: ??
|
||||
- Added **COPY** command to SFTP (Please note that Copy command is not supported by SFTP natively, so here it just uses the `cp` shell command as it does in SCP).
|
||||
- *FTP*
|
||||
- Added support for file copy (achieved through *tricky-copy*: the file is first downloaded, then uploaded with a different file name)
|
||||
- **Double progress bar**:
|
||||
- From now one two progress bar will be displayed:
|
||||
- the first, on top, displays the full transfer state (e.g. when downloading a directory of 10 files, the progress of the entire transfer)
|
||||
- the second, on bottom, displays the transfer of the individual file being written (as happened for the old versions)
|
||||
- changed the progress bar colour from `LightGreen` to `Green`
|
||||
- Enhancements
|
||||
- Added a status bar in the file explorer showing whether the sync browser is enabled and which file sorting mode is selected
|
||||
- Removed the goold old figlet title
|
||||
|
||||
14
README.md
14
README.md
@@ -182,15 +182,15 @@ The developer documentation can be found on Rust Docs at <https://docs.rs/termsc
|
||||
|
||||
## Upcoming Features 🧪
|
||||
|
||||
- **Themes provider 🎨**: I'm still thinking about how I will implement this, but basically the idea is to have a configuration file where it will be possible
|
||||
to define the color schema for the entire application. I haven't planned this release yet
|
||||
Major termscp updates will now be seasonal, so expect 4 major updates during the year.
|
||||
|
||||
No other new feature is planned at the moment. I actually think that termscp is getting mature and now I should focus upcoming updates more on bug fixing and code/performance improvements than on new features.
|
||||
Anyway there are some ideas which I'd like to implement. If you want to start working on them, feel free to open a PR:
|
||||
- **Keyring-rs on Linux 🔐**: Planned for the *summer update*, check for updates in [this issue](https://github.com/veeso/termscp/issues/2)
|
||||
- **Samba Support 🎉**: This will require a long time to be implemented, since I'm thinking of implementing a Rust native samba library from scratch, since I don't want to add new C-bindings. It'll maybe included in the *summer update*.
|
||||
- **Themes provider 🎨**: I'm still thinking about how I will implement this, but basically the idea is to have a configuration file where it will be possible to define the color schema for the entire application. I haven't planned this release yet
|
||||
- **Configuration profile for bookmarks 📚**: I would like to, but I still have to analyze it.
|
||||
- **AWS S3 support 🪣**: There is already a library for AWS S3, but this is really on bottom of my implementation list at the moment, due to interest and I don't really have a system where to test it.
|
||||
|
||||
- Amazon S3 support
|
||||
- Samba support
|
||||
- Themes provider
|
||||
Along to new features, termscp developments is now focused on UI and performance improvements, so if you have any suggestion, feel free to open an issue.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::system::config_client::ConfigClient;
|
||||
///
|
||||
/// File explorer tab
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) enum FileExplorerTab {
|
||||
pub enum FileExplorerTab {
|
||||
Local,
|
||||
Remote,
|
||||
FindLocal, // Find result tab
|
||||
@@ -43,7 +43,7 @@ pub(super) enum FileExplorerTab {
|
||||
/// ## Browser
|
||||
///
|
||||
/// Browser contains the browser options
|
||||
pub(super) struct Browser {
|
||||
pub struct Browser {
|
||||
local: FileExplorer, // Local File explorer state
|
||||
remote: FileExplorer, // Remote File explorer state
|
||||
found: Option<FileExplorer>, // File explorer for find result
|
||||
@@ -55,7 +55,7 @@ impl Browser {
|
||||
/// ### new
|
||||
///
|
||||
/// Build a new `Browser` struct
|
||||
pub(super) fn new(cli: Option<&ConfigClient>) -> Self {
|
||||
pub fn new(cli: Option<&ConfigClient>) -> Self {
|
||||
Self {
|
||||
local: Self::build_local_explorer(cli),
|
||||
remote: Self::build_remote_explorer(cli),
|
||||
@@ -99,14 +99,14 @@ impl Browser {
|
||||
self.found = None;
|
||||
}
|
||||
|
||||
pub(super) fn tab(&self) -> FileExplorerTab {
|
||||
pub fn tab(&self) -> FileExplorerTab {
|
||||
self.tab
|
||||
}
|
||||
|
||||
/// ### change_tab
|
||||
///
|
||||
/// Update tab value
|
||||
pub(super) fn change_tab(&mut self, tab: FileExplorerTab) {
|
||||
pub fn change_tab(&mut self, tab: FileExplorerTab) {
|
||||
self.tab = tab;
|
||||
}
|
||||
|
||||
29
src/ui/activities/filetransfer/lib/mod.rs
Normal file
29
src/ui/activities/filetransfer/lib/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
//! ## FileTransferActivity
|
||||
//!
|
||||
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
pub(crate) mod browser;
|
||||
pub(crate) mod transfer;
|
||||
259
src/ui/activities/filetransfer/lib/transfer.rs
Normal file
259
src/ui/activities/filetransfer/lib/transfer.rs
Normal file
@@ -0,0 +1,259 @@
|
||||
//! ## FileTransferActivity
|
||||
//!
|
||||
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
use bytesize::ByteSize;
|
||||
use std::fmt;
|
||||
use std::time::Instant;
|
||||
|
||||
/// ### TransferStates
|
||||
///
|
||||
/// TransferStates contains the states related to the transfer process
|
||||
pub struct TransferStates {
|
||||
aborted: bool, // Describes whether the transfer process has been aborted
|
||||
pub full: ProgressStates, // full transfer states
|
||||
pub partial: ProgressStates, // Partial transfer states
|
||||
}
|
||||
|
||||
/// ### ProgressStates
|
||||
///
|
||||
/// Progress states describes the states for the progress of a single transfer part
|
||||
pub struct ProgressStates {
|
||||
started: Instant,
|
||||
total: usize,
|
||||
written: usize,
|
||||
}
|
||||
|
||||
impl Default for TransferStates {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TransferStates {
|
||||
/// ### new
|
||||
///
|
||||
/// Instantiates a new transfer states
|
||||
pub fn new() -> TransferStates {
|
||||
TransferStates {
|
||||
aborted: false,
|
||||
full: ProgressStates::default(),
|
||||
partial: ProgressStates::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ### reset
|
||||
///
|
||||
/// Re-intiialize transfer states
|
||||
pub fn reset(&mut self) {
|
||||
self.aborted = false;
|
||||
}
|
||||
|
||||
/// ### abort
|
||||
///
|
||||
/// Set aborted to true
|
||||
pub fn abort(&mut self) {
|
||||
self.aborted = true;
|
||||
}
|
||||
|
||||
/// ### aborted
|
||||
///
|
||||
/// Returns whether transfer has been aborted
|
||||
pub fn aborted(&self) -> bool {
|
||||
self.aborted
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProgressStates {
|
||||
fn default() -> Self {
|
||||
ProgressStates {
|
||||
started: Instant::now(),
|
||||
written: 0,
|
||||
total: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ProgressStates {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let eta: String = match self.calc_eta() {
|
||||
0 => String::from("--:--"),
|
||||
seconds => format!(
|
||||
"{:0width$}:{:0width$}",
|
||||
(seconds / 60),
|
||||
(seconds % 60),
|
||||
width = 2
|
||||
),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"{:.2}% - ETA {} ({}/s)",
|
||||
self.calc_progress_percentage(),
|
||||
eta,
|
||||
ByteSize(self.calc_bytes_per_second())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProgressStates {
|
||||
/// ### init
|
||||
///
|
||||
/// Initialize a new Progress State
|
||||
pub fn init(&mut self, sz: usize) {
|
||||
self.started = Instant::now();
|
||||
self.total = sz;
|
||||
self.written = 0;
|
||||
}
|
||||
|
||||
/// ### update_progress
|
||||
///
|
||||
/// Update progress state
|
||||
pub fn update_progress(&mut self, delta: usize) -> f64 {
|
||||
self.written += delta;
|
||||
self.calc_progress_percentage()
|
||||
}
|
||||
|
||||
/// ### calc_progress
|
||||
///
|
||||
/// Calculate progress in a range between 0.0 to 1.0
|
||||
pub fn calc_progress(&self) -> f64 {
|
||||
let prog: f64 = (self.written as f64) / (self.total as f64);
|
||||
match prog > 1.0 {
|
||||
true => 1.0,
|
||||
false => prog,
|
||||
}
|
||||
}
|
||||
|
||||
/// ### started
|
||||
///
|
||||
/// Get started
|
||||
pub fn started(&self) -> Instant {
|
||||
self.started
|
||||
}
|
||||
|
||||
/// ### calc_progress_percentage
|
||||
///
|
||||
/// Calculate the current transfer progress as percentage
|
||||
fn calc_progress_percentage(&self) -> f64 {
|
||||
self.calc_progress() * 100.0
|
||||
}
|
||||
|
||||
/// ### calc_bytes_per_second
|
||||
///
|
||||
/// Generic function to calculate bytes per second using elapsed time since transfer started and the bytes written
|
||||
/// and the total amount of bytes to write
|
||||
pub fn calc_bytes_per_second(&self) -> u64 {
|
||||
// bytes_written : elapsed_secs = x : 1
|
||||
let elapsed_secs: u64 = self.started.elapsed().as_secs();
|
||||
match elapsed_secs {
|
||||
0 => match self.written == self.total {
|
||||
// NOTE: would divide by 0 :D
|
||||
true => self.total as u64, // Download completed in less than 1 second
|
||||
false => 0, // 0 B/S
|
||||
},
|
||||
_ => self.written as u64 / elapsed_secs,
|
||||
}
|
||||
}
|
||||
|
||||
/// ### calc_eta
|
||||
///
|
||||
/// Calculate ETA for current transfer as seconds
|
||||
fn calc_eta(&self) -> u64 {
|
||||
let elapsed_secs: u64 = self.started.elapsed().as_secs();
|
||||
let prog: f64 = self.calc_progress_percentage();
|
||||
match prog as u64 {
|
||||
0 => 0,
|
||||
_ => ((elapsed_secs * 100) / (prog as u64)) - elapsed_secs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_ui_activities_filetransfer_lib_transfer_progress_states() {
|
||||
let mut states: ProgressStates = ProgressStates::default();
|
||||
assert_eq!(states.total, 0);
|
||||
assert_eq!(states.written, 0);
|
||||
assert!(states.started().elapsed().as_secs() < 5);
|
||||
// Init new transfer
|
||||
states.init(1024);
|
||||
assert_eq!(states.total, 1024);
|
||||
assert_eq!(states.written, 0);
|
||||
assert_eq!(states.calc_bytes_per_second(), 0);
|
||||
assert_eq!(states.calc_eta(), 0);
|
||||
assert_eq!(states.calc_progress_percentage(), 0.0);
|
||||
assert_eq!(states.calc_progress(), 0.0);
|
||||
assert_eq!(states.to_string().as_str(), "0.00% - ETA --:-- (0 B/s)");
|
||||
// Wait 4 second (virtually)
|
||||
states.started = states.started.checked_sub(Duration::from_secs(4)).unwrap();
|
||||
// Update state
|
||||
states.update_progress(256);
|
||||
assert_eq!(states.total, 1024);
|
||||
assert_eq!(states.written, 256);
|
||||
assert_eq!(states.calc_bytes_per_second(), 64); // 256 bytes in 4 seconds
|
||||
assert_eq!(states.calc_eta(), 12); // 16 total sub 4
|
||||
assert_eq!(states.calc_progress_percentage(), 25.0);
|
||||
assert_eq!(states.calc_progress(), 0.25);
|
||||
assert_eq!(states.to_string().as_str(), "25.00% - ETA 00:12 (64 B/s)");
|
||||
// 100%
|
||||
states.started = states.started.checked_sub(Duration::from_secs(12)).unwrap();
|
||||
states.update_progress(768);
|
||||
assert_eq!(states.total, 1024);
|
||||
assert_eq!(states.written, 1024);
|
||||
assert_eq!(states.calc_bytes_per_second(), 64); // 256 bytes in 4 seconds
|
||||
assert_eq!(states.calc_eta(), 0); // 16 total sub 4
|
||||
assert_eq!(states.calc_progress_percentage(), 100.0);
|
||||
assert_eq!(states.calc_progress(), 1.0);
|
||||
assert_eq!(states.to_string().as_str(), "100.00% - ETA --:-- (64 B/s)");
|
||||
// Check if terminated at started
|
||||
states.started = Instant::now();
|
||||
assert_eq!(states.calc_bytes_per_second(), 1024);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ui_activities_filetransfer_lib_transfer_states() {
|
||||
let mut states: TransferStates = TransferStates::default();
|
||||
assert_eq!(states.aborted, false);
|
||||
assert_eq!(states.full.total, 0);
|
||||
assert_eq!(states.full.written, 0);
|
||||
assert!(states.full.started.elapsed().as_secs() < 5);
|
||||
assert_eq!(states.partial.total, 0);
|
||||
assert_eq!(states.partial.written, 0);
|
||||
assert!(states.partial.started.elapsed().as_secs() < 5);
|
||||
// Aborted
|
||||
states.abort();
|
||||
assert_eq!(states.aborted(), true);
|
||||
states.reset();
|
||||
assert_eq!(states.aborted(), false);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
*/
|
||||
// This module is split into files, cause it's just too big
|
||||
pub(self) mod actions;
|
||||
pub(self) mod browser;
|
||||
pub(self) mod lib;
|
||||
pub(self) mod misc;
|
||||
pub(self) mod session;
|
||||
pub(self) mod update;
|
||||
@@ -49,14 +49,15 @@ use crate::fs::explorer::FileExplorer;
|
||||
use crate::fs::FsEntry;
|
||||
use crate::host::Localhost;
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use browser::Browser;
|
||||
pub(crate) use lib::browser;
|
||||
use lib::browser::Browser;
|
||||
use lib::transfer::TransferStates;
|
||||
|
||||
// Includes
|
||||
use chrono::{DateTime, Local};
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||
use std::collections::VecDeque;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
use tuirealm::View;
|
||||
|
||||
// -- Storage keys
|
||||
@@ -69,7 +70,8 @@ const COMPONENT_EXPLORER_LOCAL: &str = "EXPLORER_LOCAL";
|
||||
const COMPONENT_EXPLORER_REMOTE: &str = "EXPLORER_REMOTE";
|
||||
const COMPONENT_EXPLORER_FIND: &str = "EXPLORER_FIND";
|
||||
const COMPONENT_LOG_BOX: &str = "LOG_BOX";
|
||||
const COMPONENT_PROGRESS_BAR: &str = "PROGRESS_BAR";
|
||||
const COMPONENT_PROGRESS_BAR_FULL: &str = "PROGRESS_BAR_FULL";
|
||||
const COMPONENT_PROGRESS_BAR_PARTIAL: &str = "PROGRESS_BAR_PARTIAL";
|
||||
const COMPONENT_TEXT_ERROR: &str = "TEXT_ERROR";
|
||||
const COMPONENT_TEXT_FATAL: &str = "TEXT_FATAL";
|
||||
const COMPONENT_TEXT_HELP: &str = "TEXT_HELP";
|
||||
@@ -120,81 +122,6 @@ impl LogRecord {
|
||||
}
|
||||
}
|
||||
|
||||
/// ### TransferStates
|
||||
///
|
||||
/// TransferStates contains the states related to the transfer process
|
||||
struct TransferStates {
|
||||
pub progress: f64, // Current read/write progress (percentage)
|
||||
pub started: Instant, // Instant the transfer process started
|
||||
pub aborted: bool, // Describes whether the transfer process has been aborted
|
||||
pub bytes_written: usize, // Bytes written during transfer
|
||||
pub bytes_total: usize, // Total bytes to write
|
||||
}
|
||||
|
||||
impl TransferStates {
|
||||
/// ### new
|
||||
///
|
||||
/// Instantiates a new transfer states
|
||||
pub fn new() -> TransferStates {
|
||||
TransferStates {
|
||||
progress: 0.0,
|
||||
started: Instant::now(),
|
||||
aborted: false,
|
||||
bytes_written: 0,
|
||||
bytes_total: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// ### reset
|
||||
///
|
||||
/// Re-intiialize transfer states
|
||||
pub fn reset(&mut self) {
|
||||
self.progress = 0.0;
|
||||
self.started = Instant::now();
|
||||
self.aborted = false;
|
||||
self.bytes_written = 0;
|
||||
self.bytes_total = 0;
|
||||
}
|
||||
|
||||
/// ### set_progress
|
||||
///
|
||||
/// Calculate progress percentage based on current progress
|
||||
pub fn set_progress(&mut self, w: usize, sz: usize) {
|
||||
self.bytes_written = w;
|
||||
self.bytes_total = sz;
|
||||
let mut prog: f64 = ((self.bytes_written as f64) * 100.0) / (self.bytes_total as f64);
|
||||
// Check value
|
||||
if prog > 100.0 {
|
||||
prog = 100.0;
|
||||
} else if prog < 0.0 {
|
||||
prog = 0.0;
|
||||
}
|
||||
self.progress = prog;
|
||||
}
|
||||
|
||||
/// ### byte_per_second
|
||||
///
|
||||
/// Calculate bytes per second
|
||||
pub fn bytes_per_second(&self) -> u64 {
|
||||
// bytes_written : elapsed_secs = x : 1
|
||||
let elapsed_secs: u64 = self.started.elapsed().as_secs();
|
||||
match elapsed_secs {
|
||||
0 => match self.bytes_written == self.bytes_total {
|
||||
// NOTE: would divide by 0 :D
|
||||
true => self.bytes_total as u64, // Download completed in less than 1 second
|
||||
false => 0, // 0 B/S
|
||||
},
|
||||
_ => self.bytes_written as u64 / elapsed_secs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TransferStates {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// ## FileTransferActivity
|
||||
///
|
||||
/// FileTransferActivity is the data holder for the file transfer activity
|
||||
|
||||
@@ -160,6 +160,25 @@ impl FileTransferActivity {
|
||||
entry: &FsEntry,
|
||||
curr_remote_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) {
|
||||
// Reset states
|
||||
self.transfer.reset();
|
||||
// Calculate total size of transfer
|
||||
let total_transfer_size: usize = self.get_total_transfer_size_local(entry);
|
||||
self.transfer.full.init(total_transfer_size);
|
||||
// Mount progress bar
|
||||
self.mount_progress_bar(format!("Uploading {}...", entry.get_abs_path().display()));
|
||||
// Send recurse
|
||||
self.filetransfer_send_recurse(entry, curr_remote_path, dst_name);
|
||||
// Umount progress bar
|
||||
self.umount_progress_bar();
|
||||
}
|
||||
|
||||
fn filetransfer_send_recurse(
|
||||
&mut self,
|
||||
entry: &FsEntry,
|
||||
curr_remote_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) {
|
||||
// Write popup
|
||||
let file_name: String = match entry {
|
||||
@@ -229,11 +248,15 @@ impl FileTransferActivity {
|
||||
// Iterate over files
|
||||
for entry in entries.iter() {
|
||||
// If aborted; break
|
||||
if self.transfer.aborted {
|
||||
if self.transfer.aborted() {
|
||||
break;
|
||||
}
|
||||
// Send entry; name is always None after first call
|
||||
self.filetransfer_send(&entry, remote_path.as_path(), None);
|
||||
self.filetransfer_send_recurse(
|
||||
&entry,
|
||||
remote_path.as_path(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -264,21 +287,121 @@ impl FileTransferActivity {
|
||||
// Scan dir on remote
|
||||
self.reload_remote_dir();
|
||||
// If aborted; show popup
|
||||
if self.transfer.aborted {
|
||||
if self.transfer.aborted() {
|
||||
// Log abort
|
||||
self.log_and_alert(
|
||||
LogLevel::Warn,
|
||||
format!("Upload aborted for \"{}\"!", entry.get_abs_path().display()),
|
||||
);
|
||||
// Set aborted to false
|
||||
self.transfer.aborted = false;
|
||||
} else {
|
||||
// @! Successful
|
||||
// Eventually, Remove progress bar
|
||||
self.umount_progress_bar();
|
||||
}
|
||||
}
|
||||
|
||||
/// ### filetransfer_send_file
|
||||
///
|
||||
/// Send local file and write it to remote path
|
||||
fn filetransfer_send_file(
|
||||
&mut self,
|
||||
local: &FsFile,
|
||||
remote: &Path,
|
||||
file_name: String,
|
||||
) -> Result<(), TransferErrorReason> {
|
||||
// Upload file
|
||||
// Try to open local file
|
||||
match self.host.open_file_read(local.abs_path.as_path()) {
|
||||
Ok(mut fhnd) => match self.client.send_file(local, remote) {
|
||||
Ok(mut rhnd) => {
|
||||
// Write file
|
||||
let file_size: usize =
|
||||
fhnd.seek(std::io::SeekFrom::End(0)).unwrap_or(0) as usize;
|
||||
// Init transfer
|
||||
self.transfer.partial.init(file_size);
|
||||
// rewind
|
||||
if let Err(err) = fhnd.seek(std::io::SeekFrom::Start(0)) {
|
||||
return Err(TransferErrorReason::CouldNotRewind(err));
|
||||
}
|
||||
// Write remote file
|
||||
let mut total_bytes_written: usize = 0;
|
||||
let mut last_progress_val: f64 = 0.0;
|
||||
let mut last_input_event_fetch: Instant = Instant::now();
|
||||
// While the entire file hasn't been completely written,
|
||||
// Or filetransfer has been aborted
|
||||
while total_bytes_written < file_size && !self.transfer.aborted() {
|
||||
// Handle input events (each 500ms)
|
||||
if last_input_event_fetch.elapsed().as_millis() >= 500 {
|
||||
// Read events
|
||||
self.read_input_event();
|
||||
// Reset instant
|
||||
last_input_event_fetch = Instant::now();
|
||||
}
|
||||
// Read till you can
|
||||
let mut buffer: [u8; 65536] = [0; 65536];
|
||||
let delta: usize = match fhnd.read(&mut buffer) {
|
||||
Ok(bytes_read) => {
|
||||
total_bytes_written += bytes_read;
|
||||
if bytes_read == 0 {
|
||||
continue;
|
||||
} else {
|
||||
let mut delta: usize = 0;
|
||||
while delta < bytes_read {
|
||||
// Write bytes
|
||||
match rhnd.write(&buffer[delta..bytes_read]) {
|
||||
Ok(bytes) => {
|
||||
delta += bytes;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(TransferErrorReason::RemoteIoError(
|
||||
err,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
delta
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(TransferErrorReason::LocalIoError(err));
|
||||
}
|
||||
};
|
||||
// Increase progress
|
||||
self.transfer.partial.update_progress(delta);
|
||||
self.transfer.full.update_progress(delta);
|
||||
// Draw only if a significant progress has been made (performance improvement)
|
||||
if last_progress_val < self.transfer.partial.calc_progress() - 0.01 {
|
||||
// Draw
|
||||
self.update_progress_bar(format!("Uploading \"{}\"...", file_name));
|
||||
self.view();
|
||||
last_progress_val = self.transfer.partial.calc_progress();
|
||||
}
|
||||
}
|
||||
// Finalize stream
|
||||
if let Err(err) = self.client.on_sent(rhnd) {
|
||||
self.log(
|
||||
LogLevel::Warn,
|
||||
format!("Could not finalize remote stream: \"{}\"", err),
|
||||
);
|
||||
}
|
||||
// if upload was abrupted, return error
|
||||
if self.transfer.aborted() {
|
||||
return Err(TransferErrorReason::Abrupted);
|
||||
}
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!(
|
||||
"Saved file \"{}\" to \"{}\" (took {} seconds; at {}/s)",
|
||||
local.abs_path.display(),
|
||||
remote.display(),
|
||||
fmt_millis(self.transfer.partial.started().elapsed()),
|
||||
ByteSize(self.transfer.partial.calc_bytes_per_second()),
|
||||
),
|
||||
);
|
||||
}
|
||||
Err(err) => return Err(TransferErrorReason::FileTransferError(err)),
|
||||
},
|
||||
Err(err) => return Err(TransferErrorReason::HostError(err)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ### filetransfer_recv
|
||||
///
|
||||
/// Recv fs entry from remote.
|
||||
@@ -289,6 +412,25 @@ impl FileTransferActivity {
|
||||
entry: &FsEntry,
|
||||
local_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) {
|
||||
// Reset states
|
||||
self.transfer.reset();
|
||||
// Calculate total transfer size
|
||||
let total_transfer_size: usize = self.get_total_transfer_size_remote(entry);
|
||||
self.transfer.full.init(total_transfer_size);
|
||||
// Mount progress bar
|
||||
self.mount_progress_bar(format!("Downloading {}...", entry.get_abs_path().display()));
|
||||
// Receive
|
||||
self.filetransfer_recv_recurse(entry, local_path, dst_name);
|
||||
// Umount progress bar
|
||||
self.umount_progress_bar();
|
||||
}
|
||||
|
||||
fn filetransfer_recv_recurse(
|
||||
&mut self,
|
||||
entry: &FsEntry,
|
||||
local_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) {
|
||||
// Write popup
|
||||
let file_name: String = match entry {
|
||||
@@ -379,12 +521,16 @@ impl FileTransferActivity {
|
||||
// Iterate over files
|
||||
for entry in entries.iter() {
|
||||
// If transfer has been aborted; break
|
||||
if self.transfer.aborted {
|
||||
if self.transfer.aborted() {
|
||||
break;
|
||||
}
|
||||
// Receive entry; name is always None after first call
|
||||
// Local path becomes local_dir_path
|
||||
self.filetransfer_recv(&entry, local_dir_path.as_path(), None);
|
||||
self.filetransfer_recv_recurse(
|
||||
&entry,
|
||||
local_dir_path.as_path(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -415,7 +561,7 @@ impl FileTransferActivity {
|
||||
// Reload directory on local
|
||||
self.local_scan(local_path);
|
||||
// if aborted; show alert
|
||||
if self.transfer.aborted {
|
||||
if self.transfer.aborted() {
|
||||
// Log abort
|
||||
self.log_and_alert(
|
||||
LogLevel::Warn,
|
||||
@@ -424,124 +570,9 @@ impl FileTransferActivity {
|
||||
entry.get_abs_path().display()
|
||||
),
|
||||
);
|
||||
// Reset aborted to false
|
||||
self.transfer.aborted = false;
|
||||
} else {
|
||||
// Eventually, Reset input mode to explorer
|
||||
self.umount_progress_bar();
|
||||
}
|
||||
}
|
||||
|
||||
/// ### filetransfer_send_file
|
||||
///
|
||||
/// Send local file and write it to remote path
|
||||
fn filetransfer_send_file(
|
||||
&mut self,
|
||||
local: &FsFile,
|
||||
remote: &Path,
|
||||
file_name: String,
|
||||
) -> Result<(), TransferErrorReason> {
|
||||
// Upload file
|
||||
// Try to open local file
|
||||
match self.host.open_file_read(local.abs_path.as_path()) {
|
||||
Ok(mut fhnd) => match self.client.send_file(local, remote) {
|
||||
Ok(mut rhnd) => {
|
||||
// Write file
|
||||
let file_size: usize =
|
||||
fhnd.seek(std::io::SeekFrom::End(0)).unwrap_or(0) as usize;
|
||||
// rewind
|
||||
if let Err(err) = fhnd.seek(std::io::SeekFrom::Start(0)) {
|
||||
return Err(TransferErrorReason::CouldNotRewind(err));
|
||||
}
|
||||
// Write remote file
|
||||
let mut total_bytes_written: usize = 0;
|
||||
// Reset transfer states
|
||||
self.transfer.reset();
|
||||
let mut last_progress_val: f64 = 0.0;
|
||||
let mut last_input_event_fetch: Instant = Instant::now();
|
||||
// Mount progress bar
|
||||
self.mount_progress_bar();
|
||||
// While the entire file hasn't been completely written,
|
||||
// Or filetransfer has been aborted
|
||||
while total_bytes_written < file_size && !self.transfer.aborted {
|
||||
// Handle input events (each 500ms)
|
||||
if last_input_event_fetch.elapsed().as_millis() >= 500 {
|
||||
// Read events
|
||||
self.read_input_event();
|
||||
// Reset instant
|
||||
last_input_event_fetch = Instant::now();
|
||||
}
|
||||
// Read till you can
|
||||
let mut buffer: [u8; 65536] = [0; 65536];
|
||||
match fhnd.read(&mut buffer) {
|
||||
Ok(bytes_read) => {
|
||||
total_bytes_written += bytes_read;
|
||||
if bytes_read == 0 {
|
||||
continue;
|
||||
} else {
|
||||
let mut buf_start: usize = 0;
|
||||
while buf_start < bytes_read {
|
||||
// Write bytes
|
||||
match rhnd.write(&buffer[buf_start..bytes_read]) {
|
||||
Ok(bytes) => {
|
||||
buf_start += bytes;
|
||||
}
|
||||
Err(err) => {
|
||||
self.umount_progress_bar();
|
||||
return Err(TransferErrorReason::RemoteIoError(
|
||||
err,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
self.umount_progress_bar();
|
||||
return Err(TransferErrorReason::LocalIoError(err));
|
||||
}
|
||||
}
|
||||
// Increase progress
|
||||
self.transfer.set_progress(total_bytes_written, file_size);
|
||||
// Draw only if a significant progress has been made (performance improvement)
|
||||
if last_progress_val < self.transfer.progress - 1.0 {
|
||||
// Draw
|
||||
self.update_progress_bar(format!("Uploading \"{}\"...", file_name));
|
||||
self.view();
|
||||
last_progress_val = self.transfer.progress;
|
||||
}
|
||||
}
|
||||
// Umount progress bar
|
||||
self.umount_progress_bar();
|
||||
// Finalize stream
|
||||
if let Err(err) = self.client.on_sent(rhnd) {
|
||||
self.log(
|
||||
LogLevel::Warn,
|
||||
format!("Could not finalize remote stream: \"{}\"", err),
|
||||
);
|
||||
}
|
||||
// if upload was abrupted, return error
|
||||
if self.transfer.aborted {
|
||||
return Err(TransferErrorReason::Abrupted);
|
||||
}
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!(
|
||||
"Saved file \"{}\" to \"{}\" (took {} seconds; at {}/s)",
|
||||
local.abs_path.display(),
|
||||
remote.display(),
|
||||
fmt_millis(self.transfer.started.elapsed()),
|
||||
ByteSize(self.transfer.bytes_per_second()),
|
||||
),
|
||||
);
|
||||
}
|
||||
Err(err) => return Err(TransferErrorReason::FileTransferError(err)),
|
||||
},
|
||||
Err(err) => return Err(TransferErrorReason::HostError(err)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ### filetransfer_recv_file
|
||||
///
|
||||
/// Receive file from remote and write it to local path
|
||||
@@ -558,16 +589,14 @@ impl FileTransferActivity {
|
||||
match self.client.recv_file(remote) {
|
||||
Ok(mut rhnd) => {
|
||||
let mut total_bytes_written: usize = 0;
|
||||
// Reset transfer states
|
||||
self.transfer.reset();
|
||||
// Init transfer
|
||||
self.transfer.partial.init(remote.size);
|
||||
// Write local file
|
||||
let mut last_progress_val: f64 = 0.0;
|
||||
let mut last_input_event_fetch: Instant = Instant::now();
|
||||
// Mount progress bar
|
||||
self.mount_progress_bar();
|
||||
// While the entire file hasn't been completely read,
|
||||
// Or filetransfer has been aborted
|
||||
while total_bytes_written < remote.size && !self.transfer.aborted {
|
||||
while total_bytes_written < remote.size && !self.transfer.aborted() {
|
||||
// Handle input events (each 500 ms)
|
||||
if last_input_event_fetch.elapsed().as_millis() >= 500 {
|
||||
// Read events
|
||||
@@ -577,44 +606,42 @@ impl FileTransferActivity {
|
||||
}
|
||||
// Read till you can
|
||||
let mut buffer: [u8; 65536] = [0; 65536];
|
||||
match rhnd.read(&mut buffer) {
|
||||
let delta: usize = match rhnd.read(&mut buffer) {
|
||||
Ok(bytes_read) => {
|
||||
total_bytes_written += bytes_read;
|
||||
if bytes_read == 0 {
|
||||
continue;
|
||||
} else {
|
||||
let mut buf_start: usize = 0;
|
||||
while buf_start < bytes_read {
|
||||
let mut delta: usize = 0;
|
||||
while delta < bytes_read {
|
||||
// Write bytes
|
||||
match local_file.write(&buffer[buf_start..bytes_read]) {
|
||||
Ok(bytes) => buf_start += bytes,
|
||||
match local_file.write(&buffer[delta..bytes_read]) {
|
||||
Ok(bytes) => delta += bytes,
|
||||
Err(err) => {
|
||||
self.umount_progress_bar();
|
||||
return Err(TransferErrorReason::LocalIoError(
|
||||
err,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
delta
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
self.umount_progress_bar();
|
||||
return Err(TransferErrorReason::RemoteIoError(err));
|
||||
}
|
||||
}
|
||||
};
|
||||
// Set progress
|
||||
self.transfer.set_progress(total_bytes_written, remote.size);
|
||||
self.transfer.partial.update_progress(delta);
|
||||
self.transfer.full.update_progress(delta);
|
||||
// Draw only if a significant progress has been made (performance improvement)
|
||||
if last_progress_val < self.transfer.progress - 1.0 {
|
||||
if last_progress_val < self.transfer.partial.calc_progress() - 0.01 {
|
||||
// Draw
|
||||
self.update_progress_bar(format!("Downloading \"{}\"", file_name));
|
||||
self.view();
|
||||
last_progress_val = self.transfer.progress;
|
||||
last_progress_val = self.transfer.partial.calc_progress();
|
||||
}
|
||||
}
|
||||
// Umount progress bar
|
||||
self.umount_progress_bar();
|
||||
// Finalize stream
|
||||
if let Err(err) = self.client.on_recv(rhnd) {
|
||||
self.log(
|
||||
@@ -623,7 +650,7 @@ impl FileTransferActivity {
|
||||
);
|
||||
}
|
||||
// If download was abrupted, return Error
|
||||
if self.transfer.aborted {
|
||||
if self.transfer.aborted() {
|
||||
return Err(TransferErrorReason::Abrupted);
|
||||
}
|
||||
// Apply file mode to file
|
||||
@@ -648,8 +675,8 @@ impl FileTransferActivity {
|
||||
"Saved file \"{}\" to \"{}\" (took {} seconds; at {}/s)",
|
||||
remote.abs_path.display(),
|
||||
local.display(),
|
||||
fmt_millis(self.transfer.started.elapsed()),
|
||||
ByteSize(self.transfer.bytes_per_second()),
|
||||
fmt_millis(self.transfer.partial.started().elapsed()),
|
||||
ByteSize(self.transfer.partial.calc_bytes_per_second()),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1007,4 +1034,64 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- transfer sizes
|
||||
|
||||
/// ### get_total_transfer_size_local
|
||||
///
|
||||
/// Get total size of transfer for localhost
|
||||
fn get_total_transfer_size_local(&mut self, entry: &FsEntry) -> usize {
|
||||
match entry {
|
||||
FsEntry::File(file) => file.size,
|
||||
FsEntry::Directory(dir) => {
|
||||
// List dir
|
||||
match self.host.scan_dir(dir.abs_path.as_path()) {
|
||||
Ok(files) => files
|
||||
.iter()
|
||||
.map(|x| self.get_total_transfer_size_local(x))
|
||||
.sum(),
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not list directory {}: {}",
|
||||
dir.abs_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ### get_total_transfer_size_remote
|
||||
///
|
||||
/// Get total size of transfer for remote host
|
||||
fn get_total_transfer_size_remote(&mut self, entry: &FsEntry) -> usize {
|
||||
match entry {
|
||||
FsEntry::File(file) => file.size,
|
||||
FsEntry::Directory(dir) => {
|
||||
// List directory
|
||||
match self.client.list_dir(dir.abs_path.as_path()) {
|
||||
Ok(files) => files
|
||||
.iter()
|
||||
.map(|x| self.get_total_transfer_size_remote(x))
|
||||
.sum(),
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not list directory {}: {}",
|
||||
dir.abs_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,16 +33,16 @@ use super::{
|
||||
COMPONENT_EXPLORER_FIND, COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE,
|
||||
COMPONENT_INPUT_COPY, COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO,
|
||||
COMPONENT_INPUT_MKDIR, COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS,
|
||||
COMPONENT_LIST_FILEINFO, COMPONENT_LOG_BOX, COMPONENT_PROGRESS_BAR, COMPONENT_RADIO_DELETE,
|
||||
COMPONENT_RADIO_DISCONNECT, COMPONENT_RADIO_QUIT, COMPONENT_RADIO_SORTING,
|
||||
COMPONENT_TEXT_ERROR, COMPONENT_TEXT_FATAL, COMPONENT_TEXT_HELP,
|
||||
COMPONENT_LIST_FILEINFO, COMPONENT_LOG_BOX, COMPONENT_PROGRESS_BAR_FULL,
|
||||
COMPONENT_PROGRESS_BAR_PARTIAL, COMPONENT_RADIO_DELETE, COMPONENT_RADIO_DISCONNECT,
|
||||
COMPONENT_RADIO_QUIT, COMPONENT_RADIO_SORTING, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_FATAL,
|
||||
COMPONENT_TEXT_HELP,
|
||||
};
|
||||
use crate::fs::explorer::FileSorting;
|
||||
use crate::fs::FsEntry;
|
||||
use crate::ui::components::{file_list::FileListPropsBuilder, logbox::LogboxPropsBuilder};
|
||||
use crate::ui::keymap::*;
|
||||
// externals
|
||||
use bytesize::ByteSize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tuirealm::{
|
||||
components::progress_bar::ProgressBarPropsBuilder,
|
||||
@@ -642,9 +642,9 @@ impl FileTransferActivity {
|
||||
None
|
||||
}
|
||||
// -- progress bar
|
||||
(COMPONENT_PROGRESS_BAR, &MSG_KEY_CTRL_C) => {
|
||||
(COMPONENT_PROGRESS_BAR_PARTIAL, &MSG_KEY_CTRL_C) => {
|
||||
// Set transfer aborted to True
|
||||
self.transfer.aborted = true;
|
||||
self.transfer.abort();
|
||||
None
|
||||
}
|
||||
// -- fallback
|
||||
@@ -793,31 +793,22 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_progress_bar(&mut self, text: String) -> Option<(String, Msg)> {
|
||||
match self.view.get_props(COMPONENT_PROGRESS_BAR) {
|
||||
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) {
|
||||
let root_name: String = props.texts.title.as_deref().unwrap_or("").to_string();
|
||||
let props = ProgressBarPropsBuilder::from(props)
|
||||
.with_texts(Some(root_name), self.transfer.full.to_string())
|
||||
.with_progress(self.transfer.full.calc_progress())
|
||||
.build();
|
||||
let _ = self.view.update(COMPONENT_PROGRESS_BAR_FULL, props);
|
||||
}
|
||||
match self.view.get_props(COMPONENT_PROGRESS_BAR_PARTIAL) {
|
||||
Some(props) => {
|
||||
// Calculate ETA
|
||||
let elapsed_secs: u64 = self.transfer.started.elapsed().as_secs();
|
||||
let eta: String = match self.transfer.progress as u64 {
|
||||
0 => String::from("--:--"), // NOTE: would divide by 0 :D
|
||||
_ => {
|
||||
let eta: u64 =
|
||||
((elapsed_secs * 100) / (self.transfer.progress as u64)) - elapsed_secs;
|
||||
format!("{:0width$}:{:0width$}", (eta / 60), (eta % 60), width = 2)
|
||||
}
|
||||
};
|
||||
// Calculate bytes/s
|
||||
let label = format!(
|
||||
"{:.2}% - ETA {} ({}/s)",
|
||||
self.transfer.progress,
|
||||
eta,
|
||||
ByteSize(self.transfer.bytes_per_second())
|
||||
);
|
||||
let props = ProgressBarPropsBuilder::from(props)
|
||||
.with_texts(Some(text), label)
|
||||
.with_progress(self.transfer.progress / 100.0)
|
||||
.with_texts(Some(filename), self.transfer.partial.to_string())
|
||||
.with_progress(self.transfer.partial.calc_progress())
|
||||
.build();
|
||||
self.view.update(COMPONENT_PROGRESS_BAR, props)
|
||||
self.view.update(COMPONENT_PROGRESS_BAR_PARTIAL, props)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
|
||||
@@ -247,12 +247,25 @@ impl FileTransferActivity {
|
||||
self.view.render(super::COMPONENT_LIST_FILEINFO, f, popup);
|
||||
}
|
||||
}
|
||||
if let Some(props) = self.view.get_props(super::COMPONENT_PROGRESS_BAR) {
|
||||
if let Some(props) = self.view.get_props(super::COMPONENT_PROGRESS_BAR_PARTIAL) {
|
||||
if props.visible {
|
||||
let popup = draw_area_in(f.size(), 40, 10);
|
||||
let popup = draw_area_in(f.size(), 50, 20);
|
||||
f.render_widget(Clear, popup);
|
||||
// make popup
|
||||
self.view.render(super::COMPONENT_PROGRESS_BAR, f, popup);
|
||||
let popup_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Percentage(50), // Full
|
||||
Constraint::Percentage(50), // Partial
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(popup);
|
||||
self.view
|
||||
.render(super::COMPONENT_PROGRESS_BAR_FULL, f, popup_chunks[0]);
|
||||
self.view
|
||||
.render(super::COMPONENT_PROGRESS_BAR_PARTIAL, f, popup_chunks[1]);
|
||||
}
|
||||
}
|
||||
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DELETE) {
|
||||
@@ -614,23 +627,43 @@ impl FileTransferActivity {
|
||||
self.view.umount(super::COMPONENT_INPUT_SAVEAS);
|
||||
}
|
||||
|
||||
pub(super) fn mount_progress_bar(&mut self) {
|
||||
pub(super) fn mount_progress_bar(&mut self, root_name: String) {
|
||||
self.view.mount(
|
||||
super::COMPONENT_PROGRESS_BAR,
|
||||
super::COMPONENT_PROGRESS_BAR_FULL,
|
||||
Box::new(ProgressBar::new(
|
||||
ProgressBarPropsBuilder::default()
|
||||
.with_progbar_color(Color::LightGreen)
|
||||
.with_progbar_color(Color::Green)
|
||||
.with_background(Color::Black)
|
||||
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
|
||||
.with_borders(
|
||||
Borders::TOP | Borders::RIGHT | Borders::LEFT,
|
||||
BorderType::Rounded,
|
||||
Color::Reset,
|
||||
)
|
||||
.with_texts(Some(root_name), String::new())
|
||||
.build(),
|
||||
)),
|
||||
);
|
||||
self.view.mount(
|
||||
super::COMPONENT_PROGRESS_BAR_PARTIAL,
|
||||
Box::new(ProgressBar::new(
|
||||
ProgressBarPropsBuilder::default()
|
||||
.with_progbar_color(Color::Green)
|
||||
.with_background(Color::Black)
|
||||
.with_borders(
|
||||
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
|
||||
BorderType::Rounded,
|
||||
Color::Reset,
|
||||
)
|
||||
.with_texts(Some(String::from("Please wait")), String::new())
|
||||
.build(),
|
||||
)),
|
||||
);
|
||||
self.view.active(super::COMPONENT_PROGRESS_BAR);
|
||||
self.view.active(super::COMPONENT_PROGRESS_BAR_PARTIAL);
|
||||
}
|
||||
|
||||
pub(super) fn umount_progress_bar(&mut self) {
|
||||
self.view.umount(super::COMPONENT_PROGRESS_BAR);
|
||||
self.view.umount(super::COMPONENT_PROGRESS_BAR_PARTIAL);
|
||||
self.view.umount(super::COMPONENT_PROGRESS_BAR_FULL);
|
||||
}
|
||||
|
||||
pub(super) fn mount_file_sorting(&mut self) {
|
||||
|
||||
Reference in New Issue
Block a user