From 09bc8a92a273aaac1fc0facf062751b35df36331 Mon Sep 17 00:00:00 2001 From: ChristianVisintin Date: Sun, 27 Dec 2020 10:31:33 +0100 Subject: [PATCH] show_hidden_files and group_dirs in termscp configuration; instantiate FileExplorer based on current configuration in FileTransferActivity --- CHANGELOG.md | 2 + README.md | 2 + src/config/mod.rs | 11 +- src/config/serializer.rs | 56 +++++++ src/fs/explorer/builder.rs | 9 +- src/system/config_client.rs | 64 ++++++++ src/ui/activities/auth_activity/input.rs | 18 +-- src/ui/activities/auth_activity/layout.rs | 10 +- .../activities/filetransfer_activity/misc.rs | 18 ++- src/ui/activities/setup_activity/input.rs | 105 +++++++++---- src/ui/activities/setup_activity/layout.rs | 140 +++++++++++++++--- src/ui/activities/setup_activity/mod.rs | 2 + 12 files changed, 363 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baa080d..cf0016d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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/` diff --git a/README.md b/README.md index e2c8f9b..64eb8d9 100644 --- a/README.md +++ b/README.md @@ -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 🔐 diff --git a/src/config/mod.rs b/src/config/mod.rs index 9215f9c..7e41f34 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -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, } #[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] diff --git a/src/config/serializer.rs b/src/config/serializer.rs index f96c58c..e4c7fc0 100644 --- a/src/config/serializer.rs +++ b/src/config/serializer.rs @@ -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" diff --git a/src/fs/explorer/builder.rs b/src/fs/explorer/builder.rs index daf9c5b..4c2d1c9 100644 --- a/src/fs/explorer/builder.rs +++ b/src/fs/explorer/builder.rs @@ -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 diff --git a/src/system/config_client.rs b/src/system/config_client.rs index 9b219fc..0a1eb19 100644 --- a/src/system/config_client.rs +++ b/src/system/config_client.rs @@ -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 { + // 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) { + 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(); diff --git a/src/ui/activities/auth_activity/input.rs b/src/ui/activities/auth_activity/input.rs index 24458de..38b638e 100644 --- a/src/ui/activities/auth_activity/input.rs +++ b/src/ui/activities/auth_activity/input.rs @@ -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, diff --git a/src/ui/activities/auth_activity/layout.rs b/src/ui/activities/auth_activity/layout.rs index 6438e06..8907945 100644 --- a/src/ui/activities/auth_activity/layout.rs +++ b/src/ui/activities/auth_activity/layout.rs @@ -318,7 +318,7 @@ impl AuthActivity { /// ### draw_local_explorer /// /// Draw local explorer list - pub(super) fn draw_bookmarks_tab(&self) -> Option { + fn draw_bookmarks_tab(&self) -> Option { self.bookmarks_client.as_ref()?; let hosts: Vec = self .bookmarks_client @@ -366,7 +366,7 @@ impl AuthActivity { /// ### draw_local_explorer /// /// Draw local explorer list - pub(super) fn draw_recents_tab(&self) -> Option { + fn draw_recents_tab(&self) -> Option { self.bookmarks_client.as_ref()?; let hosts: Vec = 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 = 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 = vec![ ListItem::new(Spans::from(vec![ diff --git a/src/ui/activities/filetransfer_activity/misc.rs b/src/ui/activities/filetransfer_activity/misc.rs index 4d810a5..5dee089 100644 --- a/src/ui/activities/filetransfer_activity/misc.rs +++ b/src/ui/activities/filetransfer_activity/misc.rs @@ -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 diff --git a/src/ui/activities/setup_activity/input.rs b/src/ui/activities/setup_activity/input.rs index 42b47dd..5819710 100644 --- a/src/ui/activities/setup_activity/input.rs +++ b/src/ui/activities/setup_activity/input.rs @@ -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, diff --git a/src/ui/activities/setup_activity/layout.rs b/src/ui/activities/setup_activity/layout.rs index d58aaf0..cee5655 100644 --- a/src/ui/activities/setup_activity/layout.rs +++ b/src/ui/activities/setup_activity/layout.rs @@ -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 { + 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 { + /// Draw default protocol input tab + fn draw_hidden_files_tab(&self) -> Option { + // 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 = 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 { + // Check if config client is some + match &self.config_cli { + Some(cli) => { + let choices: Vec = 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 = vec![ ListItem::new(Spans::from(vec![ diff --git a/src/ui/activities/setup_activity/mod.rs b/src/ui/activities/setup_activity/mod.rs index 1d085f4..b7f783c 100644 --- a/src/ui/activities/setup_activity/mod.rs +++ b/src/ui/activities/setup_activity/mod.rs @@ -53,6 +53,8 @@ type OnChoiceCallback = fn(&mut SetupActivity); enum UserInterfaceInputField { DefaultProtocol, TextEditor, + ShowHiddenFiles, + GroupDirs, } /// ### SetupTab