mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
show_hidden_files and group_dirs in termscp configuration; instantiate FileExplorer based on current configuration in FileTransferActivity
This commit is contained in:
@@ -28,6 +28,8 @@ FIXME: Released on
|
||||
- Windows: `C:\Users\Alice\AppData\Roaming\termscp\config.toml`
|
||||
- Added Text editor to configuration
|
||||
- Added Default File transfer protocol to configuration
|
||||
- Added "Show hidden files` to configuration
|
||||
- Added "Group directories" to configuration
|
||||
- Added SSH keys to configuration; SSH keys will be stored at
|
||||
- Linux: `/home/alice/.config/termscp/.ssh/`
|
||||
- MacOS: `/Users/Alice/Library/Application Support/termscp/.ssh/`
|
||||
|
||||
@@ -259,6 +259,8 @@ These parameters can be changed:
|
||||
|
||||
- **Default Protocol**: the default protocol is the default value for the file transfer protocol to be used in termscp. This applies for the login page and for the address CLI argument.
|
||||
- **Text Editor**: the text editor to use. By default termscp will find the default editor for you; with this option you can force an editor to be used (e.g. `vim`). **Also GUI editors are supported**, unless they `nohup` from the parent process so if you ask: yes, you can use `notepad.exe`, and no: **Visual Studio Code doesn't work**.
|
||||
- **Show Hidden Files**: select whether hidden files shall be displayed by default. You will be able to decide whether to show or not hidden files at runtime pressing `A` anyway.
|
||||
- **Group Dirs**: select whether directories should be groupped or not in file explorers. If `Display first` is selected, directories will be sorted using the configured method but displayed before files, viceversa if `Display last` is selected.
|
||||
|
||||
### SSH Key Storage 🔐
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ pub struct UserConfig {
|
||||
pub struct UserInterfaceConfig {
|
||||
pub text_editor: PathBuf,
|
||||
pub default_protocol: String,
|
||||
pub show_hidden_files: bool,
|
||||
pub group_dirs: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, std::fmt::Debug)]
|
||||
@@ -81,6 +83,8 @@ impl Default for UserInterfaceConfig {
|
||||
Err(_) => PathBuf::from("nano"), // Default to nano
|
||||
},
|
||||
default_protocol: FileTransferProtocol::Sftp.to_string(),
|
||||
show_hidden_files: false,
|
||||
group_dirs: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,14 +169,15 @@ mod tests {
|
||||
let ui: UserInterfaceConfig = UserInterfaceConfig {
|
||||
default_protocol: String::from("SFTP"),
|
||||
text_editor: PathBuf::from("nano"),
|
||||
show_hidden_files: true,
|
||||
group_dirs: Some(String::from("first")),
|
||||
};
|
||||
let cfg: UserConfig = UserConfig {
|
||||
user_interface: ui,
|
||||
remote: remote,
|
||||
};
|
||||
assert_eq!(
|
||||
*cfg
|
||||
.remote
|
||||
*cfg.remote
|
||||
.ssh_keys
|
||||
.get(&String::from("192.168.1.31"))
|
||||
.unwrap(),
|
||||
@@ -180,6 +185,8 @@ mod tests {
|
||||
);
|
||||
assert_eq!(cfg.user_interface.default_protocol, String::from("SFTP"));
|
||||
assert_eq!(cfg.user_interface.text_editor, PathBuf::from("nano"));
|
||||
assert_eq!(cfg.user_interface.show_hidden_files, true);
|
||||
assert_eq!(cfg.user_interface.group_dirs, Some(String::from("first")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -106,6 +106,43 @@ mod tests {
|
||||
// Verify ui
|
||||
assert_eq!(cfg.user_interface.default_protocol, String::from("SCP"));
|
||||
assert_eq!(cfg.user_interface.text_editor, PathBuf::from("vim"));
|
||||
assert_eq!(cfg.user_interface.show_hidden_files, true);
|
||||
assert_eq!(cfg.user_interface.group_dirs, Some(String::from("last")));
|
||||
// Verify keys
|
||||
assert_eq!(
|
||||
*cfg.remote
|
||||
.ssh_keys
|
||||
.get(&String::from("192.168.1.31"))
|
||||
.unwrap(),
|
||||
PathBuf::from("/home/omar/.ssh/raspberry.key")
|
||||
);
|
||||
assert_eq!(
|
||||
*cfg.remote
|
||||
.ssh_keys
|
||||
.get(&String::from("192.168.1.32"))
|
||||
.unwrap(),
|
||||
PathBuf::from("/home/omar/.ssh/beaglebone.key")
|
||||
);
|
||||
assert!(cfg.remote.ssh_keys.get(&String::from("1.1.1.1")).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_serializer_deserialize_ok_no_opts() {
|
||||
let toml_file: tempfile::NamedTempFile = create_good_toml_no_opts();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
// Parse
|
||||
let deserializer: ConfigSerializer = ConfigSerializer {};
|
||||
let cfg = deserializer.deserialize(Box::new(toml_file));
|
||||
println!("{:?}", cfg);
|
||||
assert!(cfg.is_ok());
|
||||
let cfg: UserConfig = cfg.ok().unwrap();
|
||||
// Verify configuration
|
||||
// Verify ui
|
||||
assert_eq!(cfg.user_interface.default_protocol, String::from("SCP"));
|
||||
assert_eq!(cfg.user_interface.text_editor, PathBuf::from("vim"));
|
||||
assert_eq!(cfg.user_interface.show_hidden_files, true);
|
||||
assert_eq!(cfg.user_interface.group_dirs, None);
|
||||
// Verify keys
|
||||
assert_eq!(
|
||||
*cfg.remote
|
||||
@@ -160,6 +197,25 @@ mod tests {
|
||||
[user_interface]
|
||||
default_protocol = "SCP"
|
||||
text_editor = "vim"
|
||||
show_hidden_files = true
|
||||
group_dirs = "last"
|
||||
|
||||
[remote.ssh_keys]
|
||||
"192.168.1.31" = "/home/omar/.ssh/raspberry.key"
|
||||
"192.168.1.32" = "/home/omar/.ssh/beaglebone.key"
|
||||
"#;
|
||||
tmpfile.write_all(file_content.as_bytes()).unwrap();
|
||||
tmpfile
|
||||
}
|
||||
|
||||
fn create_good_toml_no_opts() -> tempfile::NamedTempFile {
|
||||
// Write
|
||||
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
||||
let file_content: &str = r#"
|
||||
[user_interface]
|
||||
default_protocol = "SCP"
|
||||
text_editor = "vim"
|
||||
show_hidden_files = true
|
||||
|
||||
[remote.ssh_keys]
|
||||
"192.168.1.31" = "/home/omar/.ssh/raspberry.key"
|
||||
|
||||
@@ -55,9 +55,12 @@ impl FileExplorerBuilder {
|
||||
/// ### with_hidden_files
|
||||
///
|
||||
/// Enable HIDDEN_FILES option
|
||||
pub fn with_hidden_files(&mut self) -> &mut FileExplorerBuilder {
|
||||
pub fn with_hidden_files(&mut self, val: bool) -> &mut FileExplorerBuilder {
|
||||
if let Some(e) = self.explorer.as_mut() {
|
||||
e.opts.insert(ExplorerOpts::SHOW_HIDDEN_FILES);
|
||||
match val {
|
||||
true => e.opts.insert(ExplorerOpts::SHOW_HIDDEN_FILES),
|
||||
false => e.opts.remove(ExplorerOpts::SHOW_HIDDEN_FILES),
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
@@ -114,7 +117,7 @@ mod tests {
|
||||
let explorer: FileExplorer = FileExplorerBuilder::new()
|
||||
.with_file_sorting(FileSorting::ByModifyTime)
|
||||
.with_group_dirs(Some(GroupDirs::First))
|
||||
.with_hidden_files()
|
||||
.with_hidden_files(true)
|
||||
.with_stack_size(24)
|
||||
.build();
|
||||
// Verify
|
||||
|
||||
@@ -29,6 +29,7 @@ extern crate rand;
|
||||
use crate::config::serializer::ConfigSerializer;
|
||||
use crate::config::{SerializerError, SerializerErrorKind, UserConfig};
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::fs::explorer::GroupDirs;
|
||||
// Ext
|
||||
use std::fs::{create_dir, remove_file, File, OpenOptions};
|
||||
use std::io::Write;
|
||||
@@ -123,6 +124,45 @@ impl ConfigClient {
|
||||
self.config.user_interface.default_protocol = proto.to_string();
|
||||
}
|
||||
|
||||
/// ### get_show_hidden_files
|
||||
///
|
||||
/// Get value of `show_hidden_files`
|
||||
pub fn get_show_hidden_files(&self) -> bool {
|
||||
self.config.user_interface.show_hidden_files
|
||||
}
|
||||
|
||||
/// ### set_show_hidden_files
|
||||
///
|
||||
/// Set new value for `show_hidden_files`
|
||||
pub fn set_show_hidden_files(&mut self, value: bool) {
|
||||
self.config.user_interface.show_hidden_files = value;
|
||||
}
|
||||
|
||||
/// ### get_group_dirs
|
||||
///
|
||||
/// Get GroupDirs value from configuration (will be converted from string)
|
||||
pub fn get_group_dirs(&self) -> Option<GroupDirs> {
|
||||
// Convert string to `GroupDirs`
|
||||
match &self.config.user_interface.group_dirs {
|
||||
None => None,
|
||||
Some(val) => match GroupDirs::from_str(val.as_str()) {
|
||||
Ok(val) => Some(val),
|
||||
Err(_) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// ### set_group_dirs
|
||||
///
|
||||
/// Set value for group_dir in configuration.
|
||||
/// Provided value, if `Some` will be converted to `GroupDirs`
|
||||
pub fn set_group_dirs(&mut self, val: Option<GroupDirs>) {
|
||||
self.config.user_interface.group_dirs = match val {
|
||||
None => None,
|
||||
Some(val) => Some(val.to_string()),
|
||||
};
|
||||
}
|
||||
|
||||
// SSH Keys
|
||||
|
||||
/// ### save_ssh_key
|
||||
@@ -387,6 +427,30 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_config_show_hidden_files() {
|
||||
let tmp_dir: tempfile::TempDir = create_tmp_dir();
|
||||
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
||||
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
||||
.ok()
|
||||
.unwrap();
|
||||
client.set_show_hidden_files(true);
|
||||
assert_eq!(client.get_show_hidden_files(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_config_group_dirs() {
|
||||
let tmp_dir: tempfile::TempDir = create_tmp_dir();
|
||||
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
||||
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
||||
.ok()
|
||||
.unwrap();
|
||||
client.set_group_dirs(Some(GroupDirs::First));
|
||||
assert_eq!(client.get_group_dirs(), Some(GroupDirs::First),);
|
||||
client.set_group_dirs(None);
|
||||
assert_eq!(client.get_group_dirs(), None,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_config_ssh_keys() {
|
||||
let tmp_dir: tempfile::TempDir = create_tmp_dir();
|
||||
|
||||
@@ -53,7 +53,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_form
|
||||
///
|
||||
/// Handler for input event when in form mode
|
||||
pub(super) fn handle_input_event_mode_form(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_form(&mut self, ev: &InputEvent) {
|
||||
match self.input_form {
|
||||
InputForm::AuthCredentials => self.handle_input_event_mode_form_auth(ev),
|
||||
InputForm::Bookmarks => self.handle_input_event_mode_form_bookmarks(ev),
|
||||
@@ -64,7 +64,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_form_auth
|
||||
///
|
||||
/// Handle input event when input mode is Form and Tab is Auth
|
||||
pub(super) fn handle_input_event_mode_form_auth(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_form_auth(&mut self, ev: &InputEvent) {
|
||||
if let InputEvent::Key(key) = ev {
|
||||
match key.code {
|
||||
KeyCode::Esc => {
|
||||
@@ -220,7 +220,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_form_bookmarks
|
||||
///
|
||||
/// Handle input event when input mode is Form and Tab is Bookmarks
|
||||
pub(super) fn handle_input_event_mode_form_bookmarks(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_form_bookmarks(&mut self, ev: &InputEvent) {
|
||||
if let InputEvent::Key(key) = ev {
|
||||
match key.code {
|
||||
KeyCode::Esc => {
|
||||
@@ -304,7 +304,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_form_recents
|
||||
///
|
||||
/// Handle input event when input mode is Form and Tab is Recents
|
||||
pub(super) fn handle_input_event_mode_form_recents(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_form_recents(&mut self, ev: &InputEvent) {
|
||||
if let InputEvent::Key(key) = ev {
|
||||
match key.code {
|
||||
KeyCode::Esc => {
|
||||
@@ -388,7 +388,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_text
|
||||
///
|
||||
/// Handler for input event when in popup mode
|
||||
pub(super) fn handle_input_event_mode_popup(&mut self, ev: &InputEvent, ptype: PopupType) {
|
||||
fn handle_input_event_mode_popup(&mut self, ev: &InputEvent, ptype: PopupType) {
|
||||
match ptype {
|
||||
PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev),
|
||||
PopupType::Help => self.handle_input_event_mode_popup_help(ev),
|
||||
@@ -402,7 +402,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_popup_alert
|
||||
///
|
||||
/// Handle input event when the input mode is popup, and popup type is alert
|
||||
pub(super) fn handle_input_event_mode_popup_alert(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_popup_alert(&mut self, ev: &InputEvent) {
|
||||
// Only enter should be allowed here
|
||||
if let InputEvent::Key(key) = ev {
|
||||
if matches!(key.code, KeyCode::Esc | KeyCode::Enter) {
|
||||
@@ -414,7 +414,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_popup_help
|
||||
///
|
||||
/// Input event handler for popup help
|
||||
pub(super) fn handle_input_event_mode_popup_help(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_popup_help(&mut self, ev: &InputEvent) {
|
||||
// If enter, close popup
|
||||
if let InputEvent::Key(key) = ev {
|
||||
if matches!(key.code, KeyCode::Esc | KeyCode::Enter) {
|
||||
@@ -427,7 +427,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_popup_save_bookmark
|
||||
///
|
||||
/// Input event handler for SaveBookmark popup
|
||||
pub(super) fn handle_input_event_mode_popup_save_bookmark(&mut self, ev: &InputEvent) {
|
||||
fn handle_input_event_mode_popup_save_bookmark(&mut self, ev: &InputEvent) {
|
||||
// If enter, close popup, otherwise push chars to input
|
||||
if let InputEvent::Key(key) = ev {
|
||||
match key.code {
|
||||
@@ -466,7 +466,7 @@ impl AuthActivity {
|
||||
/// ### handle_input_event_mode_popup_yesno
|
||||
///
|
||||
/// Input event handler for popup alert
|
||||
pub(super) fn handle_input_event_mode_popup_yesno(
|
||||
fn handle_input_event_mode_popup_yesno(
|
||||
&mut self,
|
||||
ev: &InputEvent,
|
||||
yes_cb: DialogCallback,
|
||||
|
||||
@@ -318,7 +318,7 @@ impl AuthActivity {
|
||||
/// ### draw_local_explorer
|
||||
///
|
||||
/// Draw local explorer list
|
||||
pub(super) fn draw_bookmarks_tab(&self) -> Option<List> {
|
||||
fn draw_bookmarks_tab(&self) -> Option<List> {
|
||||
self.bookmarks_client.as_ref()?;
|
||||
let hosts: Vec<ListItem> = self
|
||||
.bookmarks_client
|
||||
@@ -366,7 +366,7 @@ impl AuthActivity {
|
||||
/// ### draw_local_explorer
|
||||
///
|
||||
/// Draw local explorer list
|
||||
pub(super) fn draw_recents_tab(&self) -> Option<List> {
|
||||
fn draw_recents_tab(&self) -> Option<List> {
|
||||
self.bookmarks_client.as_ref()?;
|
||||
let hosts: Vec<ListItem> = self
|
||||
.bookmarks_client
|
||||
@@ -463,7 +463,7 @@ impl AuthActivity {
|
||||
/// ### draw_popup_input
|
||||
///
|
||||
/// Draw input popup
|
||||
pub(super) fn draw_popup_save_bookmark(&self) -> (Paragraph, Tabs) {
|
||||
fn draw_popup_save_bookmark(&self) -> (Paragraph, Tabs) {
|
||||
let input: Paragraph = Paragraph::new(self.input_txt.as_ref())
|
||||
.style(Style::default().fg(Color::White))
|
||||
.block(
|
||||
@@ -497,7 +497,7 @@ impl AuthActivity {
|
||||
/// ### draw_popup_yesno
|
||||
///
|
||||
/// Draw yes/no select popup
|
||||
pub(super) fn draw_popup_yesno(&self, text: String) -> Tabs {
|
||||
fn draw_popup_yesno(&self, text: String) -> Tabs {
|
||||
let choices: Vec<Spans> = vec![Spans::from("Yes"), Spans::from("No")];
|
||||
let index: usize = match self.choice_opt {
|
||||
DialogYesNoOption::Yes => 0,
|
||||
@@ -522,7 +522,7 @@ impl AuthActivity {
|
||||
/// ### draw_popup_help
|
||||
///
|
||||
/// Draw authentication page help popup
|
||||
pub(super) fn draw_popup_help(&self) -> List {
|
||||
fn draw_popup_help(&self) -> List {
|
||||
// Write header
|
||||
let cmds: Vec<ListItem> = vec![
|
||||
ListItem::new(Spans::from(vec![
|
||||
|
||||
@@ -130,11 +130,19 @@ impl FileTransferActivity {
|
||||
///
|
||||
/// Build explorer reading configuration from `ConfigClient`
|
||||
pub(super) fn build_explorer(cli: Option<&ConfigClient>) -> FileExplorer {
|
||||
FileExplorerBuilder::new()
|
||||
.with_file_sorting(FileSorting::ByName)
|
||||
.with_group_dirs(Some(GroupDirs::First))
|
||||
.with_stack_size(16)
|
||||
.build()
|
||||
match &cli {
|
||||
Some(cli) => FileExplorerBuilder::new() // Build according to current configuration
|
||||
.with_file_sorting(FileSorting::ByName)
|
||||
.with_group_dirs(cli.get_group_dirs())
|
||||
.with_hidden_files(cli.get_show_hidden_files())
|
||||
.with_stack_size(16)
|
||||
.build(),
|
||||
None => FileExplorerBuilder::new() // Build default
|
||||
.with_file_sorting(FileSorting::ByName)
|
||||
.with_group_dirs(Some(GroupDirs::First))
|
||||
.with_stack_size(16)
|
||||
.build(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ### setup_text_editor
|
||||
|
||||
@@ -30,6 +30,7 @@ use super::{
|
||||
UserInterfaceInputField, YesNoDialogOption,
|
||||
};
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::fs::explorer::GroupDirs;
|
||||
// Ext
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use std::path::PathBuf;
|
||||
@@ -195,59 +196,105 @@ impl SetupActivity {
|
||||
KeyCode::Left => {
|
||||
// Move left on fields which are tabs
|
||||
if let Some(config_cli) = self.config_cli.as_mut() {
|
||||
if matches!(field, UserInterfaceInputField::DefaultProtocol) {
|
||||
// Move left
|
||||
config_cli.set_default_protocol(
|
||||
match config_cli.get_default_protocol() {
|
||||
FileTransferProtocol::Ftp(secure) => match secure {
|
||||
true => FileTransferProtocol::Ftp(false),
|
||||
false => FileTransferProtocol::Scp,
|
||||
match field {
|
||||
UserInterfaceInputField::DefaultProtocol => {
|
||||
// Move left
|
||||
config_cli.set_default_protocol(
|
||||
match config_cli.get_default_protocol() {
|
||||
FileTransferProtocol::Ftp(secure) => match secure {
|
||||
true => FileTransferProtocol::Ftp(false),
|
||||
false => FileTransferProtocol::Scp,
|
||||
},
|
||||
FileTransferProtocol::Scp => FileTransferProtocol::Sftp,
|
||||
FileTransferProtocol::Sftp => {
|
||||
FileTransferProtocol::Ftp(true)
|
||||
} // Wrap
|
||||
},
|
||||
FileTransferProtocol::Scp => FileTransferProtocol::Sftp,
|
||||
FileTransferProtocol::Sftp => FileTransferProtocol::Ftp(true), // Wrap
|
||||
},
|
||||
);
|
||||
);
|
||||
}
|
||||
UserInterfaceInputField::GroupDirs => {
|
||||
// Move left
|
||||
config_cli.set_group_dirs(match config_cli.get_group_dirs() {
|
||||
None => Some(GroupDirs::Last),
|
||||
Some(val) => match val {
|
||||
GroupDirs::Last => Some(GroupDirs::First),
|
||||
GroupDirs::First => None,
|
||||
},
|
||||
});
|
||||
}
|
||||
UserInterfaceInputField::ShowHiddenFiles => {
|
||||
// Move left
|
||||
config_cli.set_show_hidden_files(true);
|
||||
}
|
||||
_ => { /* Not a tab field */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Right => {
|
||||
// Move right on fields which are tabs
|
||||
if let Some(config_cli) = self.config_cli.as_mut() {
|
||||
if matches!(field, UserInterfaceInputField::DefaultProtocol) {
|
||||
// Move left
|
||||
config_cli.set_default_protocol(
|
||||
match config_cli.get_default_protocol() {
|
||||
FileTransferProtocol::Sftp => FileTransferProtocol::Scp,
|
||||
FileTransferProtocol::Scp => FileTransferProtocol::Ftp(false),
|
||||
FileTransferProtocol::Ftp(secure) => match secure {
|
||||
false => FileTransferProtocol::Ftp(true),
|
||||
true => FileTransferProtocol::Sftp, // Wrap
|
||||
match field {
|
||||
UserInterfaceInputField::DefaultProtocol => {
|
||||
// Move left
|
||||
config_cli.set_default_protocol(
|
||||
match config_cli.get_default_protocol() {
|
||||
FileTransferProtocol::Sftp => FileTransferProtocol::Scp,
|
||||
FileTransferProtocol::Scp => {
|
||||
FileTransferProtocol::Ftp(false)
|
||||
}
|
||||
FileTransferProtocol::Ftp(secure) => match secure {
|
||||
false => FileTransferProtocol::Ftp(true),
|
||||
true => FileTransferProtocol::Sftp, // Wrap
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
);
|
||||
}
|
||||
UserInterfaceInputField::GroupDirs => {
|
||||
// Move right
|
||||
config_cli.set_group_dirs(match config_cli.get_group_dirs() {
|
||||
Some(val) => match val {
|
||||
GroupDirs::First => Some(GroupDirs::Last),
|
||||
GroupDirs::Last => None,
|
||||
},
|
||||
None => Some(GroupDirs::First),
|
||||
});
|
||||
}
|
||||
UserInterfaceInputField::ShowHiddenFiles => {
|
||||
// Move right
|
||||
config_cli.set_show_hidden_files(false);
|
||||
}
|
||||
_ => { /* Not a tab field */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Up => {
|
||||
// Change selected field
|
||||
self.tab = SetupTab::UserInterface(match field {
|
||||
UserInterfaceInputField::TextEditor => {
|
||||
UserInterfaceInputField::GroupDirs => {
|
||||
UserInterfaceInputField::ShowHiddenFiles
|
||||
}
|
||||
UserInterfaceInputField::ShowHiddenFiles => {
|
||||
UserInterfaceInputField::DefaultProtocol
|
||||
}
|
||||
UserInterfaceInputField::DefaultProtocol => {
|
||||
UserInterfaceInputField::TextEditor
|
||||
} // Wrap
|
||||
}
|
||||
UserInterfaceInputField::TextEditor => UserInterfaceInputField::GroupDirs, // Wrap
|
||||
});
|
||||
}
|
||||
KeyCode::Down => {
|
||||
// Change selected field
|
||||
self.tab = SetupTab::UserInterface(match field {
|
||||
UserInterfaceInputField::DefaultProtocol => {
|
||||
UserInterfaceInputField::TextEditor
|
||||
}
|
||||
UserInterfaceInputField::TextEditor => {
|
||||
UserInterfaceInputField::DefaultProtocol
|
||||
} // Wrap
|
||||
}
|
||||
UserInterfaceInputField::DefaultProtocol => {
|
||||
UserInterfaceInputField::ShowHiddenFiles
|
||||
}
|
||||
UserInterfaceInputField::ShowHiddenFiles => {
|
||||
UserInterfaceInputField::GroupDirs
|
||||
}
|
||||
UserInterfaceInputField::GroupDirs => UserInterfaceInputField::TextEditor, // Wrap
|
||||
});
|
||||
}
|
||||
KeyCode::Char(ch) => {
|
||||
@@ -450,7 +497,7 @@ impl SetupActivity {
|
||||
/// ### handle_input_event_mode_popup_yesno
|
||||
///
|
||||
/// Input event handler for popup alert
|
||||
pub(super) fn handle_input_event_mode_popup_yesno(
|
||||
fn handle_input_event_mode_popup_yesno(
|
||||
&mut self,
|
||||
ev: &InputEvent,
|
||||
yes_cb: OnChoiceCallback,
|
||||
|
||||
@@ -29,8 +29,9 @@ use super::{
|
||||
YesNoDialogOption,
|
||||
};
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::fs::explorer::GroupDirs;
|
||||
use crate::utils::fmt::align_text_center;
|
||||
|
||||
// Ext
|
||||
use tui::{
|
||||
layout::{Constraint, Corner, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
@@ -84,6 +85,8 @@ impl SetupActivity {
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(1),
|
||||
@@ -98,6 +101,12 @@ impl SetupActivity {
|
||||
if let Some(tab) = self.draw_default_protocol_tab() {
|
||||
f.render_widget(tab, ui_cfg_chunks[1]);
|
||||
}
|
||||
if let Some(tab) = self.draw_hidden_files_tab() {
|
||||
f.render_widget(tab, ui_cfg_chunks[2]);
|
||||
}
|
||||
if let Some(tab) = self.draw_default_group_dirs_tab() {
|
||||
f.render_widget(tab, ui_cfg_chunks[3]);
|
||||
}
|
||||
// Set cursor
|
||||
if let Some(cli) = &self.config_cli {
|
||||
if matches!(form_field, UserInterfaceInputField::TextEditor) {
|
||||
@@ -217,6 +226,33 @@ impl SetupActivity {
|
||||
Paragraph::new(footer_text)
|
||||
}
|
||||
|
||||
/// ### draw_text_editor_input
|
||||
///
|
||||
/// Draw input text field for text editor parameter
|
||||
fn draw_text_editor_input(&self) -> Option<Paragraph> {
|
||||
match &self.config_cli {
|
||||
Some(cli) => Some(
|
||||
Paragraph::new(String::from(
|
||||
cli.get_text_editor().as_path().to_string_lossy(),
|
||||
))
|
||||
.style(Style::default().fg(match &self.tab {
|
||||
SetupTab::SshConfig => Color::White,
|
||||
SetupTab::UserInterface(field) => match field {
|
||||
UserInterfaceInputField::TextEditor => Color::LightGreen,
|
||||
_ => Color::White,
|
||||
},
|
||||
}))
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.title("Text Editor"),
|
||||
),
|
||||
),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// ### draw_default_protocol_tab
|
||||
///
|
||||
/// Draw default protocol input tab
|
||||
@@ -267,29 +303,91 @@ impl SetupActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/// ### draw_text_editor_input
|
||||
/// ### draw_default_protocol_tab
|
||||
///
|
||||
/// Draw input text field for text editor parameter
|
||||
fn draw_text_editor_input(&self) -> Option<Paragraph> {
|
||||
/// Draw default protocol input tab
|
||||
fn draw_hidden_files_tab(&self) -> Option<Tabs> {
|
||||
// Check if config client is some
|
||||
match &self.config_cli {
|
||||
Some(cli) => Some(
|
||||
Paragraph::new(String::from(
|
||||
cli.get_text_editor().as_path().to_string_lossy(),
|
||||
))
|
||||
.style(Style::default().fg(match &self.tab {
|
||||
SetupTab::SshConfig => Color::White,
|
||||
Some(cli) => {
|
||||
let choices: Vec<Spans> = vec![Spans::from("Yes"), Spans::from("No")];
|
||||
let index: usize = match cli.get_show_hidden_files() {
|
||||
true => 0,
|
||||
false => 1,
|
||||
};
|
||||
let (bg, fg, block_fg): (Color, Color, Color) = match &self.tab {
|
||||
SetupTab::UserInterface(field) => match field {
|
||||
UserInterfaceInputField::TextEditor => Color::LightGreen,
|
||||
_ => Color::White,
|
||||
UserInterfaceInputField::ShowHiddenFiles => {
|
||||
(Color::LightRed, Color::Black, Color::LightRed)
|
||||
}
|
||||
_ => (Color::Reset, Color::LightRed, Color::Reset),
|
||||
},
|
||||
}))
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.title("Text Editor"),
|
||||
),
|
||||
),
|
||||
_ => (Color::Reset, Color::Reset, Color::Reset),
|
||||
};
|
||||
Some(
|
||||
Tabs::new(choices)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().fg(block_fg))
|
||||
.title("Show hidden files (by default)"),
|
||||
)
|
||||
.select(index)
|
||||
.style(Style::default())
|
||||
.highlight_style(
|
||||
Style::default().add_modifier(Modifier::BOLD).fg(fg).bg(bg),
|
||||
),
|
||||
)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// ### draw_default_group_dirs_tab
|
||||
///
|
||||
/// Draw group dirs input tab
|
||||
fn draw_default_group_dirs_tab(&self) -> Option<Tabs> {
|
||||
// Check if config client is some
|
||||
match &self.config_cli {
|
||||
Some(cli) => {
|
||||
let choices: Vec<Spans> = vec![
|
||||
Spans::from("Display First"),
|
||||
Spans::from("Display Last"),
|
||||
Spans::from("No"),
|
||||
];
|
||||
let index: usize = match cli.get_group_dirs() {
|
||||
None => 2,
|
||||
Some(val) => match val {
|
||||
GroupDirs::First => 0,
|
||||
GroupDirs::Last => 1,
|
||||
},
|
||||
};
|
||||
let (bg, fg, block_fg): (Color, Color, Color) = match &self.tab {
|
||||
SetupTab::UserInterface(field) => match field {
|
||||
UserInterfaceInputField::GroupDirs => {
|
||||
(Color::LightMagenta, Color::Black, Color::LightMagenta)
|
||||
}
|
||||
_ => (Color::Reset, Color::LightMagenta, Color::Reset),
|
||||
},
|
||||
_ => (Color::Reset, Color::Reset, Color::Reset),
|
||||
};
|
||||
Some(
|
||||
Tabs::new(choices)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().fg(block_fg))
|
||||
.title("Group directories"),
|
||||
)
|
||||
.select(index)
|
||||
.style(Style::default())
|
||||
.highlight_style(
|
||||
Style::default().add_modifier(Modifier::BOLD).fg(fg).bg(bg),
|
||||
),
|
||||
)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@@ -481,7 +579,7 @@ impl SetupActivity {
|
||||
/// ### draw_popup_help
|
||||
///
|
||||
/// Draw authentication page help popup
|
||||
pub(super) fn draw_popup_help(&self) -> List {
|
||||
fn draw_popup_help(&self) -> List {
|
||||
// Write header
|
||||
let cmds: Vec<ListItem> = vec![
|
||||
ListItem::new(Spans::from(vec![
|
||||
|
||||
@@ -53,6 +53,8 @@ type OnChoiceCallback = fn(&mut SetupActivity);
|
||||
enum UserInterfaceInputField {
|
||||
DefaultProtocol,
|
||||
TextEditor,
|
||||
ShowHiddenFiles,
|
||||
GroupDirs,
|
||||
}
|
||||
|
||||
/// ### SetupTab
|
||||
|
||||
Reference in New Issue
Block a user