Many fixes in UI; Help command

This commit is contained in:
ChristianVisintin
2020-11-29 17:46:56 +01:00
parent 28435aaeda
commit cd35dac4a5
3 changed files with 237 additions and 102 deletions

View File

@@ -170,7 +170,7 @@ impl std::fmt::Display for FsEntry {
let datetime: String = time_to_str(dir.last_change_time, "%b %d %Y %M:%H"); let datetime: String = time_to_str(dir.last_change_time, "%b %d %Y %M:%H");
write!( write!(
f, f,
"{:24}\t{:12}\t{:16}\t{:16}\t{:8}\t{:17}", "{:24}\t{:12}\t{:12}\t{:12}\t{:9}\t{:17}",
dir.name.as_str(), dir.name.as_str(),
mode, mode,
username, username,
@@ -259,7 +259,7 @@ impl std::fmt::Display for FsEntry {
let datetime: String = time_to_str(file.last_change_time, "%b %d %Y %M:%H"); let datetime: String = time_to_str(file.last_change_time, "%b %d %Y %M:%H");
write!( write!(
f, f,
"{:24}\t{:12}\t{:16}\t{:16}\t{:8}\t{:17}", "{:24}\t{:12}\t{:12}\t{:12}\t{:9}\t{:17}",
file.name.as_str(), file.name.as_str(),
mode, mode,
username, username,
@@ -352,7 +352,7 @@ impl std::fmt::Display for FsEntry {
let datetime: String = time_to_str(dir.last_change_time, "%b %d %Y %M:%H"); let datetime: String = time_to_str(dir.last_change_time, "%b %d %Y %M:%H");
write!( write!(
f, f,
"{:24}\t{:12}\t{:16}\t{:16}\t{:8}\t{:17}", "{:24}\t{:12}\t{:12}\t{:12}\t{:9}\t{:17}",
dir.name.as_str(), dir.name.as_str(),
mode, mode,
username, username,
@@ -435,7 +435,7 @@ impl std::fmt::Display for FsEntry {
let datetime: String = time_to_str(file.last_change_time, "%b %d %Y %M:%H"); let datetime: String = time_to_str(file.last_change_time, "%b %d %Y %M:%H");
write!( write!(
f, f,
"{:24}\t{:12}\t{:16}\t{:16}\t{:8}\t{:17}", "{:24}\t{:12}\t{:12}\t{:12}\t{:9}\t{:17}",
file.name.as_str(), file.name.as_str(),
mode, mode,
username, username,

View File

@@ -348,7 +348,7 @@ impl AuthActivity {
/// Draw header /// Draw header
fn draw_header(&self) -> Paragraph { fn draw_header(&self) -> Paragraph {
Paragraph::new(" _____ ____ ____ ____ \n|_ _|__ _ __ _ __ ___ / ___| / ___| _ \\ \n | |/ _ \\ '__| '_ ` _ \\\\___ \\| | | |_) |\n | | __/ | | | | | | |___) | |___| __/ \n |_|\\___|_| |_| |_| |_|____/ \\____|_| \n") Paragraph::new(" _____ ____ ____ ____ \n|_ _|__ _ __ _ __ ___ / ___| / ___| _ \\ \n | |/ _ \\ '__| '_ ` _ \\\\___ \\| | | |_) |\n | | __/ | | | | | | |___) | |___| __/ \n |_|\\___|_| |_| |_| |_|____/ \\____|_| \n")
.style(Style::default().fg(Color::LightYellow).add_modifier(Modifier::BOLD)) .style(Style::default().fg(Color::White).add_modifier(Modifier::BOLD))
} }
/// ### draw_footer /// ### draw_footer

View File

@@ -48,7 +48,7 @@ use std::path::{Path, PathBuf};
use tui::{ use tui::{
layout::{Constraint, Corner, Direction, Layout, Rect}, layout::{Constraint, Corner, Direction, Layout, Rect},
style::{Color, Modifier, Style}, style::{Color, Modifier, Style},
text::{Span, Spans, Text}, text::{Span, Spans},
widgets::{Block, Borders, Clear, Gauge, List, ListItem, ListState, Paragraph, Tabs}, widgets::{Block, Borders, Clear, Gauge, List, ListItem, ListState, Paragraph, Tabs},
}; };
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
@@ -93,6 +93,7 @@ enum DialogYesNoOption {
enum PopupType { enum PopupType {
Alert(Color, String), // Block color; Block text Alert(Color, String), // Block color; Block text
Fatal(String), // Must quit after being hidden Fatal(String), // Must quit after being hidden
Help, // Show Help
Input(String, OnInputSubmitCallback), // Input description; Callback for submit Input(String, OnInputSubmitCallback), // Input description; Callback for submit
Progress(String), // Progress block text Progress(String), // Progress block text
Wait(String), // Wait block text Wait(String), // Wait block text
@@ -583,7 +584,7 @@ impl FileTransferActivity {
/// ### local_changedir /// ### local_changedir
/// ///
/// Change directory for local /// Change directory for local
fn local_changedir(&mut self, path: &Path) { fn local_changedir(&mut self, path: &Path, push: bool) {
// Get current directory // Get current directory
let prev_dir: PathBuf = self.context.as_ref().unwrap().local.pwd(); let prev_dir: PathBuf = self.context.as_ref().unwrap().local.pwd();
// Change directory // Change directory
@@ -599,7 +600,9 @@ impl FileTransferActivity {
// Reload files // Reload files
self.local_scan(path); self.local_scan(path);
// Push prev_dir to stack // Push prev_dir to stack
self.local.pushd(prev_dir.as_path()) if push {
self.local.pushd(prev_dir.as_path())
}
} }
Err(err) => { Err(err) => {
// Report err // Report err
@@ -611,7 +614,7 @@ impl FileTransferActivity {
} }
} }
fn remote_changedir(&mut self, path: &Path) { fn remote_changedir(&mut self, path: &Path, push: bool) {
// Get current directory // Get current directory
match self.client.pwd() { match self.client.pwd() {
Ok(prev_dir) => { Ok(prev_dir) => {
@@ -621,8 +624,10 @@ impl FileTransferActivity {
self.log(LogLevel::Info, format!("Changed directory on remote: {}", path.display()).as_str()); self.log(LogLevel::Info, format!("Changed directory on remote: {}", path.display()).as_str());
// Update files // Update files
self.remote_scan(path); self.remote_scan(path);
// Push prev_dir to stack // Push prev_dir to stack
self.remote.pushd(prev_dir.as_path()) if push {
self.remote.pushd(prev_dir.as_path())
}
} }
Err(err) => { Err(err) => {
// Report err // Report err
@@ -739,14 +744,14 @@ impl FileTransferActivity {
let local_files: Vec<FsEntry> = self.local.files.clone(); let local_files: Vec<FsEntry> = self.local.files.clone();
if let Some(entry) = local_files.get(self.local.index) { if let Some(entry) = local_files.get(self.local.index) {
if let FsEntry::Directory(dir) = entry { if let FsEntry::Directory(dir) = entry {
self.local_changedir(dir.abs_path.as_path()); self.local_changedir(dir.abs_path.as_path(), true);
} }
} }
} }
KeyCode::Backspace => { KeyCode::Backspace => {
// Go to previous directory // Go to previous directory
if let Some(d) = self.local.popd() { if let Some(d) = self.local.popd() {
self.local_changedir(d.as_path()); self.local_changedir(d.as_path(), false);
} }
} }
KeyCode::Delete => { KeyCode::Delete => {
@@ -787,6 +792,13 @@ impl FileTransferActivity {
)); ));
} }
} }
'h' | 'H' => {
// Show help
// If ctrl is enabled...
if key.modifiers.intersects(KeyModifiers::CONTROL) {
self.input_mode = InputMode::Popup(PopupType::Help);
}
}
'r' | 'R' => { 'r' | 'R' => {
// Rename // Rename
// If ctrl is enabled... // If ctrl is enabled...
@@ -808,6 +820,17 @@ impl FileTransferActivity {
)); ));
} }
} }
'u' | 'U' => {
// Go to parent directory
// If ctrl is enabled...
if key.modifiers.intersects(KeyModifiers::CONTROL) {
// Get pwd
let path: PathBuf = self.context.as_ref().unwrap().local.pwd();
if let Some(parent) = path.as_path().parent() {
self.local_changedir(parent, true);
}
}
}
' ' => { ' ' => {
// Get files // Get files
let files: Vec<FsEntry> = self.local.files.clone(); // Otherwise self is borrowed both as mutable and immutable... let files: Vec<FsEntry> = self.local.files.clone(); // Otherwise self is borrowed both as mutable and immutable...
@@ -859,14 +882,14 @@ impl FileTransferActivity {
if let Some(entry) = files.get(self.remote.index) { if let Some(entry) = files.get(self.remote.index) {
if let FsEntry::Directory(dir) = entry { if let FsEntry::Directory(dir) = entry {
// Get current directory // Get current directory
self.remote_changedir(dir.abs_path.as_path()); self.remote_changedir(dir.abs_path.as_path(), true);
} }
} }
} }
KeyCode::Backspace => { KeyCode::Backspace => {
// Go to previous directory // Go to previous directory
if let Some(d) = self.remote.popd() { if let Some(d) = self.remote.popd() {
self.remote_changedir(d.as_path()); self.remote_changedir(d.as_path(), false);
} }
} }
KeyCode::Delete => { KeyCode::Delete => {
@@ -907,6 +930,13 @@ impl FileTransferActivity {
)); ));
} }
} }
'h' | 'H' => {
// Show help
// If ctrl is enabled...
if key.modifiers.intersects(KeyModifiers::CONTROL) {
self.input_mode = InputMode::Popup(PopupType::Help);
}
}
'r' | 'R' => { 'r' | 'R' => {
// Rename // Rename
// If ctrl is enabled... // If ctrl is enabled...
@@ -928,6 +958,25 @@ impl FileTransferActivity {
)); ));
} }
} }
'u' | 'U' => {
// Go to parent directory
// If ctrl is enabled...
if key.modifiers.intersects(KeyModifiers::CONTROL) {
// Get pwd
match self.client.pwd() {
Ok(path) => {
if let Some(parent) = path.as_path().parent() {
self.remote_changedir(parent, true);
}
}
Err(err) => self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Red,
format!("Could not change working directory: {}", err),
))
}
}
}
' ' => { ' ' => {
// Get files // Get files
let files: Vec<FsEntry> = self.remote.files.clone(); // Otherwise self is borrowed both as mutable and immutable... let files: Vec<FsEntry> = self.remote.files.clone(); // Otherwise self is borrowed both as mutable and immutable...
@@ -1007,6 +1056,7 @@ impl FileTransferActivity {
fn handle_input_event_mode_popup(&mut self, ev: &InputEvent, popup: PopupType) { fn handle_input_event_mode_popup(&mut self, ev: &InputEvent, popup: PopupType) {
match popup { match popup {
PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev), PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev),
PopupType::Help => self.handle_input_event_mode_popup_help(ev),
PopupType::Fatal(_) => self.handle_input_event_mode_popup_fatal(ev), PopupType::Fatal(_) => self.handle_input_event_mode_popup_fatal(ev),
PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, cb), PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, cb),
PopupType::Progress(_) => self.handle_input_event_mode_popup_progress(ev), PopupType::Progress(_) => self.handle_input_event_mode_popup_progress(ev),
@@ -1017,7 +1067,7 @@ impl FileTransferActivity {
} }
} }
/// ### handle_input_event_mode_explorer_alert /// ### handle_input_event_mode_popup_alert
/// ///
/// Input event handler for popup alert /// Input event handler for popup alert
fn handle_input_event_mode_popup_alert(&mut self, ev: &InputEvent) { fn handle_input_event_mode_popup_alert(&mut self, ev: &InputEvent) {
@@ -1036,7 +1086,26 @@ impl FileTransferActivity {
} }
} }
/// ### handle_input_event_mode_explorer_alert /// ### handle_input_event_mode_popup_help
///
/// Input event handler for popup help
fn handle_input_event_mode_popup_help(&mut self, ev: &InputEvent) {
// If enter, close popup
match ev {
InputEvent::Key(key) => {
match key.code {
KeyCode::Enter | KeyCode::Esc => {
// Set input mode back to explorer
self.input_mode = InputMode::Explorer;
}
_ => { /* Nothing to do */ }
}
}
_ => { /* Nothing to do */ }
}
}
/// ### handle_input_event_mode_popup_fatal
/// ///
/// Input event handler for popup alert /// Input event handler for popup alert
fn handle_input_event_mode_popup_fatal(&mut self, ev: &InputEvent) { fn handle_input_event_mode_popup_fatal(&mut self, ev: &InputEvent) {
@@ -1157,10 +1226,10 @@ impl FileTransferActivity {
fn callback_change_directory(&mut self, input: String) { fn callback_change_directory(&mut self, input: String) {
match self.tab { match self.tab {
FileExplorerTab::Local => { FileExplorerTab::Local => {
self.local_changedir(PathBuf::from(input.as_str()).as_path()); self.local_changedir(PathBuf::from(input.as_str()).as_path(), true);
} }
FileExplorerTab::Remote => { FileExplorerTab::Remote => {
self.remote_changedir(PathBuf::from(input.as_str()).as_path()); self.remote_changedir(PathBuf::from(input.as_str()).as_path(), true);
} }
} }
} }
@@ -1254,7 +1323,7 @@ impl FileTransferActivity {
{ {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.local.files = self.context.as_ref().unwrap().local.list_dir(); self.local_scan(self.context.as_ref().unwrap().local.pwd().as_path());
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -1296,7 +1365,9 @@ impl FileTransferActivity {
match self.client.rename(entry, dst_path.as_path()) { match self.client.rename(entry, dst_path.as_path()) {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.local.files = self.context.as_ref().unwrap().local.list_dir(); if let Ok(path) = self.client.pwd() {
self.remote_scan(path.as_path());
}
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -1346,7 +1417,7 @@ impl FileTransferActivity {
match self.context.as_mut().unwrap().local.remove(entry) { match self.context.as_mut().unwrap().local.remove(entry) {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.local.files = self.context.as_ref().unwrap().local.list_dir(); self.local_scan(self.context.as_ref().unwrap().local.pwd().as_path());
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -1453,8 +1524,7 @@ impl FileTransferActivity {
[ [
Constraint::Length(5), // Header Constraint::Length(5), // Header
Constraint::Length(20), // Explorer Constraint::Length(20), // Explorer
Constraint::Length(20), // Log Constraint::Length(16), // Log
Constraint::Length(6), // Footer
] ]
.as_ref(), .as_ref(),
) )
@@ -1488,12 +1558,19 @@ impl FileTransferActivity {
log_state.select(Some(self.log_index)); log_state.select(Some(self.log_index));
// Draw log // Draw log
f.render_stateful_widget(self.draw_log_list(), chunks[2], &mut log_state); f.render_stateful_widget(self.draw_log_list(), chunks[2], &mut log_state);
// Draw footer
// FIXME: doesn't work for some reason...
f.render_widget(self.draw_footer(), chunks[3]);
// Draw popup // Draw popup
if let InputMode::Popup(popup) = &self.input_mode { if let InputMode::Popup(popup) = &self.input_mode {
let popup_area: Rect = self.draw_popup_area(f.size()); // Calculate popup size
let (width, height): (u16, u16) = match popup {
PopupType::Alert(_, _) => (30, 10),
PopupType::Fatal(_) => (30, 10),
PopupType::Help => (50, 70),
PopupType::Input(_, _) => (30, 10),
PopupType::Progress(_) => (40, 10),
PopupType::Wait(_) => (30, 10),
PopupType::YesNo(_, _, _) => (10, 10),
};
let popup_area: Rect = self.draw_popup_area(f.size(), width, height);
f.render_widget(Clear, popup_area); //this clears out the background f.render_widget(Clear, popup_area); //this clears out the background
match popup { match popup {
PopupType::Alert(color, txt) => f.render_widget( PopupType::Alert(color, txt) => f.render_widget(
@@ -1503,6 +1580,9 @@ impl FileTransferActivity {
PopupType::Fatal(txt) => { PopupType::Fatal(txt) => {
f.render_widget(self.draw_popup_fatal(txt.clone()), popup_area) f.render_widget(self.draw_popup_fatal(txt.clone()), popup_area)
} }
PopupType::Help => {
f.render_widget(self.draw_popup_help(), popup_area)
}
PopupType::Input(txt, _) => { PopupType::Input(txt, _) => {
f.render_widget(self.draw_popup_input(txt.clone()), popup_area); f.render_widget(self.draw_popup_input(txt.clone()), popup_area);
// Set cursor // Set cursor
@@ -1646,14 +1726,14 @@ impl FileTransferActivity {
/// ### draw_popup_area /// ### draw_popup_area
/// ///
/// Draw popup area /// Draw popup area
fn draw_popup_area(&self, area: Rect) -> Rect { fn draw_popup_area(&self, area: Rect, width: u16, height: u16) -> Rect {
let popup_layout = Layout::default() let popup_layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints( .constraints(
[ [
Constraint::Percentage((80) / 2), Constraint::Percentage((100 - height) / 2),
Constraint::Percentage(20), Constraint::Percentage(height),
Constraint::Percentage((80) / 2), Constraint::Percentage((100 - height) / 2),
] ]
.as_ref(), .as_ref(),
) )
@@ -1662,9 +1742,9 @@ impl FileTransferActivity {
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints( .constraints(
[ [
Constraint::Percentage((50) / 2), Constraint::Percentage((100 - width) / 2),
Constraint::Percentage(50), Constraint::Percentage(width),
Constraint::Percentage((50) / 2), Constraint::Percentage((100 - width) / 2),
] ]
.as_ref(), .as_ref(),
) )
@@ -1750,83 +1830,138 @@ impl FileTransferActivity {
/// ### draw_footer /// ### draw_footer
/// ///
/// Draw authentication page footer /// Draw authentication page footer
fn draw_footer(&self) -> Paragraph { fn draw_popup_help(&self) -> List {
// Write header // Write header
let footer = vec![ let cmds: Vec<ListItem> = vec![
Span::styled( ListItem::new(
"<ESC>", Spans::from(vec![
Style::default() Span::styled(
.bg(Color::Cyan) "<ESC> ",
.fg(Color::White) Style::default()
.add_modifier(Modifier::BOLD), .bg(Color::Cyan)
.fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("quit")
])
), ),
Span::raw("quit\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<TAB>", Span::styled(
Style::default() "<TAB> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("change input field")
])
), ),
Span::raw("change input field\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<RIGHT,LEFT>", Span::styled(
Style::default() "<RIGHT/LEFT> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("change explorer tab")
])
), ),
Span::raw("change explorer tab\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<ENTER>", Span::styled(
Style::default() "<ENTER> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("enter directory")
])
), ),
Span::raw("change directory\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<SPACE>", Span::styled(
Style::default() "<SPACE> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("upload/download file")
])
), ),
Span::raw("upload/download file\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<CTRL+G>", Span::styled(
Style::default() "<CTRL+D> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("make directory")
])
), ),
Span::raw("goto path\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<CTRL+M>", Span::styled(
Style::default() "<CTRL+G> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("goto path")
])
), ),
Span::raw("make dir\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<CTRL+R>", Span::styled(
Style::default() "<CTRL+R> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("rename file")
])
), ),
Span::raw("rename file\t"), ListItem::new(
Span::styled( Spans::from(vec![
"<CANC>", Span::styled(
Style::default() "<CTRL+U> ",
.bg(Color::Cyan) Style::default()
.fg(Color::White) .bg(Color::Cyan)
.add_modifier(Modifier::BOLD), .fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("go to parent directory")
])
), ),
Span::raw("delete file\t"), ListItem::new(
Spans::from(vec![
Span::styled(
"<CANC> ",
Style::default()
.bg(Color::Cyan)
.fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::raw("delete file")
])
)
]; ];
Paragraph::new(Text::from(Spans::from(footer))) List::new(cmds)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(Style::default())
.title("Help"),
)
.start_corner(Corner::TopLeft)
} }
} }
@@ -1849,7 +1984,7 @@ impl Activity for FileTransferActivity {
// Put raw mode on enabled // Put raw mode on enabled
let _ = enable_raw_mode(); let _ = enable_raw_mode();
// Get files at current wd // Get files at current wd
self.local.files = self.context.as_ref().unwrap().local.list_dir(); self.local_scan(self.context.as_ref().unwrap().local.pwd().as_path());
} }
/// ### on_draw /// ### on_draw