Double progress bar

This commit is contained in:
veeso 2021-05-21 11:49:44 +02:00
parent e874550d29
commit b1da42b543
4 changed files with 272 additions and 144 deletions

View file

@ -70,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";

View file

@ -163,6 +163,23 @@ impl FileTransferActivity {
) {
// 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 {
FsEntry::Directory(dir) => dir.name.clone(),
@ -235,7 +252,11 @@ impl FileTransferActivity {
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) => {
@ -272,13 +293,115 @@ impl FileTransferActivity {
LogLevel::Warn,
format!("Upload aborted for \"{}\"!", entry.get_abs_path().display()),
);
} 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.
@ -292,6 +415,23 @@ impl FileTransferActivity {
) {
// 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 {
FsEntry::Directory(dir) => dir.name.clone(),
@ -386,7 +526,11 @@ impl FileTransferActivity {
}
// 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) => {
@ -426,123 +570,9 @@ impl FileTransferActivity {
entry.get_abs_path().display()
),
);
} 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;
// 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();
// 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];
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) => {
self.umount_progress_bar();
return Err(TransferErrorReason::RemoteIoError(
err,
));
}
}
}
delta
}
}
Err(err) => {
self.umount_progress_bar();
return Err(TransferErrorReason::LocalIoError(err));
}
};
// Increase progress
self.transfer.partial.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();
}
}
// 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.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_file
///
/// Receive file from remote and write it to local path
@ -564,8 +594,6 @@ impl FileTransferActivity {
// 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() {
@ -590,7 +618,6 @@ impl FileTransferActivity {
match local_file.write(&buffer[delta..bytes_read]) {
Ok(bytes) => delta += bytes,
Err(err) => {
self.umount_progress_bar();
return Err(TransferErrorReason::LocalIoError(
err,
));
@ -601,12 +628,12 @@ impl FileTransferActivity {
}
}
Err(err) => {
self.umount_progress_bar();
return Err(TransferErrorReason::RemoteIoError(err));
}
};
// Set 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
@ -615,8 +642,6 @@ impl FileTransferActivity {
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(
@ -1009,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
}
}
}
}
}
}

View file

@ -33,9 +33,10 @@ 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;
@ -641,7 +642,7 @@ 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.abort();
None
@ -792,14 +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) => {
let props = ProgressBarPropsBuilder::from(props)
.with_texts(Some(text), self.transfer.partial.to_string())
.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,
}

View file

@ -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) {