mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Many fixes in UI; Help command
This commit is contained in:
@@ -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");
|
||||
write!(
|
||||
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(),
|
||||
mode,
|
||||
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");
|
||||
write!(
|
||||
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(),
|
||||
mode,
|
||||
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");
|
||||
write!(
|
||||
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(),
|
||||
mode,
|
||||
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");
|
||||
write!(
|
||||
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(),
|
||||
mode,
|
||||
username,
|
||||
|
||||
@@ -348,7 +348,7 @@ impl AuthActivity {
|
||||
/// Draw header
|
||||
fn draw_header(&self) -> Paragraph {
|
||||
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
|
||||
|
||||
@@ -48,7 +48,7 @@ use std::path::{Path, PathBuf};
|
||||
use tui::{
|
||||
layout::{Constraint, Corner, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
text::{Span, Spans, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Clear, Gauge, List, ListItem, ListState, Paragraph, Tabs},
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
@@ -93,6 +93,7 @@ enum DialogYesNoOption {
|
||||
enum PopupType {
|
||||
Alert(Color, String), // Block color; Block text
|
||||
Fatal(String), // Must quit after being hidden
|
||||
Help, // Show Help
|
||||
Input(String, OnInputSubmitCallback), // Input description; Callback for submit
|
||||
Progress(String), // Progress block text
|
||||
Wait(String), // Wait block text
|
||||
@@ -583,7 +584,7 @@ impl FileTransferActivity {
|
||||
/// ### local_changedir
|
||||
///
|
||||
/// Change directory for local
|
||||
fn local_changedir(&mut self, path: &Path) {
|
||||
fn local_changedir(&mut self, path: &Path, push: bool) {
|
||||
// Get current directory
|
||||
let prev_dir: PathBuf = self.context.as_ref().unwrap().local.pwd();
|
||||
// Change directory
|
||||
@@ -599,8 +600,10 @@ impl FileTransferActivity {
|
||||
// Reload files
|
||||
self.local_scan(path);
|
||||
// Push prev_dir to stack
|
||||
if push {
|
||||
self.local.pushd(prev_dir.as_path())
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
// Report err
|
||||
self.input_mode = InputMode::Popup(PopupType::Alert(
|
||||
@@ -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
|
||||
match self.client.pwd() {
|
||||
Ok(prev_dir) => {
|
||||
@@ -622,8 +625,10 @@ impl FileTransferActivity {
|
||||
// Update files
|
||||
self.remote_scan(path);
|
||||
// Push prev_dir to stack
|
||||
if push {
|
||||
self.remote.pushd(prev_dir.as_path())
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
// Report err
|
||||
self.input_mode = InputMode::Popup(PopupType::Alert(
|
||||
@@ -739,14 +744,14 @@ impl FileTransferActivity {
|
||||
let local_files: Vec<FsEntry> = self.local.files.clone();
|
||||
if let Some(entry) = local_files.get(self.local.index) {
|
||||
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 => {
|
||||
// Go to previous directory
|
||||
if let Some(d) = self.local.popd() {
|
||||
self.local_changedir(d.as_path());
|
||||
self.local_changedir(d.as_path(), false);
|
||||
}
|
||||
}
|
||||
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' => {
|
||||
// Rename
|
||||
// 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
|
||||
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 FsEntry::Directory(dir) = entry {
|
||||
// Get current directory
|
||||
self.remote_changedir(dir.abs_path.as_path());
|
||||
self.remote_changedir(dir.abs_path.as_path(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
// Go to previous directory
|
||||
if let Some(d) = self.remote.popd() {
|
||||
self.remote_changedir(d.as_path());
|
||||
self.remote_changedir(d.as_path(), false);
|
||||
}
|
||||
}
|
||||
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' => {
|
||||
// Rename
|
||||
// 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
|
||||
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) {
|
||||
match popup {
|
||||
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::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, cb),
|
||||
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
|
||||
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
|
||||
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) {
|
||||
match self.tab {
|
||||
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 => {
|
||||
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(_) => {
|
||||
// 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
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
@@ -1296,7 +1365,9 @@ impl FileTransferActivity {
|
||||
match self.client.rename(entry, dst_path.as_path()) {
|
||||
Ok(_) => {
|
||||
// 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
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
@@ -1346,7 +1417,7 @@ impl FileTransferActivity {
|
||||
match self.context.as_mut().unwrap().local.remove(entry) {
|
||||
Ok(_) => {
|
||||
// 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
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
@@ -1453,8 +1524,7 @@ impl FileTransferActivity {
|
||||
[
|
||||
Constraint::Length(5), // Header
|
||||
Constraint::Length(20), // Explorer
|
||||
Constraint::Length(20), // Log
|
||||
Constraint::Length(6), // Footer
|
||||
Constraint::Length(16), // Log
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
@@ -1488,12 +1558,19 @@ impl FileTransferActivity {
|
||||
log_state.select(Some(self.log_index));
|
||||
// Draw log
|
||||
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
|
||||
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
|
||||
match popup {
|
||||
PopupType::Alert(color, txt) => f.render_widget(
|
||||
@@ -1503,6 +1580,9 @@ impl FileTransferActivity {
|
||||
PopupType::Fatal(txt) => {
|
||||
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, _) => {
|
||||
f.render_widget(self.draw_popup_input(txt.clone()), popup_area);
|
||||
// Set cursor
|
||||
@@ -1646,14 +1726,14 @@ impl FileTransferActivity {
|
||||
/// ### 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()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Percentage((80) / 2),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage((80) / 2),
|
||||
Constraint::Percentage((100 - height) / 2),
|
||||
Constraint::Percentage(height),
|
||||
Constraint::Percentage((100 - height) / 2),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
@@ -1662,9 +1742,9 @@ impl FileTransferActivity {
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Percentage((50) / 2),
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage((50) / 2),
|
||||
Constraint::Percentage((100 - width) / 2),
|
||||
Constraint::Percentage(width),
|
||||
Constraint::Percentage((100 - width) / 2),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
@@ -1750,83 +1830,138 @@ impl FileTransferActivity {
|
||||
/// ### draw_footer
|
||||
///
|
||||
/// Draw authentication page footer
|
||||
fn draw_footer(&self) -> Paragraph {
|
||||
fn draw_popup_help(&self) -> List {
|
||||
// Write header
|
||||
let footer = vec![
|
||||
let cmds: Vec<ListItem> = vec![
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<ESC>",
|
||||
"<ESC> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("quit\t"),
|
||||
Span::raw("quit")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<TAB>",
|
||||
"<TAB> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("change input field\t"),
|
||||
Span::raw("change input field")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<RIGHT,LEFT>",
|
||||
"<RIGHT/LEFT> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("change explorer tab\t"),
|
||||
Span::raw("change explorer tab")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<ENTER>",
|
||||
"<ENTER> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("change directory\t"),
|
||||
Span::raw("enter directory")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<SPACE>",
|
||||
"<SPACE> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("upload/download file\t"),
|
||||
Span::raw("upload/download file")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<CTRL+G>",
|
||||
"<CTRL+D> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("goto path\t"),
|
||||
Span::raw("make directory")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<CTRL+M>",
|
||||
"<CTRL+G> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("make dir\t"),
|
||||
Span::raw("goto path")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<CTRL+R>",
|
||||
"<CTRL+R> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("rename file\t"),
|
||||
Span::raw("rename file")
|
||||
])
|
||||
),
|
||||
ListItem::new(
|
||||
Spans::from(vec![
|
||||
Span::styled(
|
||||
"<CANC>",
|
||||
"<CTRL+U> ",
|
||||
Style::default()
|
||||
.bg(Color::Cyan)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw("delete file\t"),
|
||||
Span::raw("go to parent directory")
|
||||
])
|
||||
),
|
||||
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
|
||||
let _ = enable_raw_mode();
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user