Theme provider and '-t' and '-c' CLI options

This commit is contained in:
veeso
2021-07-04 11:50:32 +02:00
parent a105a42519
commit 0a7e29d92f
50 changed files with 5453 additions and 1835 deletions

View File

@@ -33,6 +33,7 @@ mod view;
// locals
use super::{Activity, Context, ExitReason};
use crate::config::themes::Theme;
use crate::filetransfer::FileTransferProtocol;
use crate::system::bookmarks_client::BookmarksClient;
use crate::ui::context::FileTransferParams;
@@ -154,6 +155,13 @@ impl AuthActivity {
}
}
}
/// ### theme
///
/// Returns a reference to theme
fn theme(&self) -> &Theme {
self.context.as_ref().unwrap().theme_provider.theme()
}
}
impl Activity for AuthActivity {

View File

@@ -56,6 +56,14 @@ impl AuthActivity {
///
/// Initialize view, mounting all startup components inside the view
pub(super) fn init(&mut self) {
let key_color = self.theme().misc_keys;
let addr_color = self.theme().auth_address;
let protocol_color = self.theme().auth_protocol;
let port_color = self.theme().auth_port;
let username_color = self.theme().auth_username;
let password_color = self.theme().auth_password;
let bookmarks_color = self.theme().auth_bookmarks;
let recents_color = self.theme().auth_recents;
// Headers
self.view.mount(
super::COMPONENT_TEXT_H1,
@@ -86,14 +94,14 @@ impl AuthActivity {
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
TextSpanBuilder::new(" to show keybindings; ")
.bold()
.build(),
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
TextSpanBuilder::new(" to enter setup").bold().build(),
])
@@ -111,9 +119,9 @@ impl AuthActivity {
super::COMPONENT_RADIO_PROTOCOL,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightGreen)
.with_color(protocol_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, protocol_color)
.with_options(
Some(String::from("Protocol")),
vec![
@@ -132,8 +140,8 @@ impl AuthActivity {
super::COMPONENT_INPUT_ADDR,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_foreground(addr_color)
.with_borders(Borders::ALL, BorderType::Rounded, addr_color)
.with_label(String::from("Remote address"))
.build(),
)),
@@ -143,8 +151,8 @@ impl AuthActivity {
super::COMPONENT_INPUT_PORT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightCyan)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightCyan)
.with_foreground(port_color)
.with_borders(Borders::ALL, BorderType::Rounded, port_color)
.with_label(String::from("Port number"))
.with_input(InputType::Number)
.with_input_len(5)
@@ -157,8 +165,8 @@ impl AuthActivity {
super::COMPONENT_INPUT_USERNAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightMagenta)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta)
.with_foreground(username_color)
.with_borders(Borders::ALL, BorderType::Rounded, username_color)
.with_label(String::from("Username"))
.build(),
)),
@@ -168,8 +176,8 @@ impl AuthActivity {
super::COMPONENT_INPUT_PASSWORD,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue)
.with_foreground(password_color)
.with_borders(Borders::ALL, BorderType::Rounded, password_color)
.with_label(String::from("Password"))
.with_input(InputType::Password)
.build(),
@@ -202,26 +210,27 @@ impl AuthActivity {
super::COMPONENT_BOOKMARKS_LIST,
Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default()
.with_background(Color::LightGreen)
.with_background(bookmarks_color)
.with_foreground(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Plain, bookmarks_color)
.with_bookmarks(Some(String::from("Bookmarks")), vec![])
.build(),
)),
);
let _ = self.view_bookmarks();
// Recents
self.view.mount(
super::COMPONENT_RECENTS_LIST,
Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default()
.with_background(Color::LightBlue)
.with_background(recents_color)
.with_foreground(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Plain, recents_color)
.with_bookmarks(Some(String::from("Recent connections")), vec![])
.build(),
)),
);
// Load bookmarks
let _ = self.view_bookmarks();
let _ = self.view_recent_connections();
// Active protocol
self.view.active(super::COMPONENT_RADIO_PROTOCOL);
@@ -475,12 +484,13 @@ impl AuthActivity {
/// Mount error box
pub(super) fn mount_error(&mut self, text: &str) {
// Mount
let err_color = self.theme().misc_error_dialog;
self.view.mount(
super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.with_borders(Borders::ALL, BorderType::Thick, Color::Red)
.with_foreground(err_color)
.with_borders(Borders::ALL, BorderType::Thick, err_color)
.bold()
.with_texts(None, vec![TextSpan::from(text)])
.build(),
@@ -502,12 +512,13 @@ impl AuthActivity {
/// Mount size error
pub(super) fn mount_size_err(&mut self) {
// Mount
let err_color = self.theme().misc_error_dialog;
self.view.mount(
super::COMPONENT_TEXT_SIZE_ERR,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.with_borders(Borders::ALL, BorderType::Thick, Color::Red)
.with_foreground(err_color)
.with_borders(Borders::ALL, BorderType::Thick, err_color)
.bold()
.with_texts(
None,
@@ -534,12 +545,13 @@ impl AuthActivity {
/// Mount quit popup
pub(super) fn mount_quit(&mut self) {
// Protocol
let quit_color = self.theme().misc_quit_dialog;
self.view.mount(
super::COMPONENT_RADIO_QUIT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_color(quit_color)
.with_borders(Borders::ALL, BorderType::Rounded, quit_color)
.with_inverted_color(Color::Black)
.with_options(
Some(String::from("Quit termscp?")),
@@ -562,13 +574,14 @@ impl AuthActivity {
///
/// Mount bookmark delete dialog
pub(super) fn mount_bookmark_del_dialog(&mut self) {
let warn_color = self.theme().misc_warn_dialog;
self.view.mount(
super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_color(warn_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, warn_color)
.with_options(
Some(String::from("Delete bookmark?")),
vec![String::from("Yes"), String::from("No")],
@@ -594,13 +607,14 @@ impl AuthActivity {
///
/// Mount recent delete dialog
pub(super) fn mount_recent_del_dialog(&mut self) {
let warn_color = self.theme().misc_warn_dialog;
self.view.mount(
super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_color(warn_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, warn_color)
.with_options(
Some(String::from("Delete bookmark?")),
vec![String::from("Yes"), String::from("No")],
@@ -624,11 +638,13 @@ impl AuthActivity {
///
/// Mount bookmark save dialog
pub(super) fn mount_bookmark_save_dialog(&mut self) {
let save_color = self.theme().misc_save_dialog;
let warn_color = self.theme().misc_warn_dialog;
self.view.mount(
super::COMPONENT_INPUT_BOOKMARK_NAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightCyan)
.with_foreground(save_color)
.with_label(String::from("Save bookmark as..."))
.with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT,
@@ -642,7 +658,7 @@ impl AuthActivity {
super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Red)
.with_color(warn_color)
.with_borders(
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
BorderType::Rounded,
@@ -671,6 +687,7 @@ impl AuthActivity {
///
/// Mount help
pub(super) fn mount_help(&mut self) {
let key_color = self.theme().misc_keys;
self.view.mount(
super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new(
@@ -685,7 +702,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Quit termscp"))
@@ -693,7 +710,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Switch from form and bookmarks"))
@@ -701,7 +718,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Switch bookmark tab"))
@@ -709,7 +726,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Move up/down in current tab"))
@@ -717,7 +734,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Connect/Load bookmark"))
@@ -725,7 +742,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Delete selected bookmark"))
@@ -733,7 +750,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Enter setup"))
@@ -741,7 +758,7 @@ impl AuthActivity {
.add_col(
TextSpanBuilder::new("<CTRL+S>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Save bookmark"))

View File

@@ -35,6 +35,7 @@ pub(self) mod view;
// locals
use super::{Activity, Context, ExitReason};
use crate::config::themes::Theme;
use crate::filetransfer::ftp_transfer::FtpFileTransfer;
use crate::filetransfer::scp_transfer::ScpFileTransfer;
use crate::filetransfer::sftp_transfer::SftpFileTransfer;
@@ -165,34 +166,34 @@ impl FileTransferActivity {
}
}
pub(crate) fn local(&self) -> &FileExplorer {
fn local(&self) -> &FileExplorer {
self.browser.local()
}
pub(crate) fn local_mut(&mut self) -> &mut FileExplorer {
fn local_mut(&mut self) -> &mut FileExplorer {
self.browser.local_mut()
}
pub(crate) fn remote(&self) -> &FileExplorer {
fn remote(&self) -> &FileExplorer {
self.browser.remote()
}
pub(crate) fn remote_mut(&mut self) -> &mut FileExplorer {
fn remote_mut(&mut self) -> &mut FileExplorer {
self.browser.remote_mut()
}
pub(crate) fn found(&self) -> Option<&FileExplorer> {
fn found(&self) -> Option<&FileExplorer> {
self.browser.found()
}
pub(crate) fn found_mut(&mut self) -> Option<&mut FileExplorer> {
fn found_mut(&mut self) -> Option<&mut FileExplorer> {
self.browser.found_mut()
}
/// ### get_cache_tmp_name
///
/// Get file name for a file in cache
pub(crate) fn get_cache_tmp_name(&self, name: &str, file_type: Option<&str>) -> Option<String> {
fn get_cache_tmp_name(&self, name: &str, file_type: Option<&str>) -> Option<String> {
self.cache.as_ref().map(|_| {
let base: String = format!(
"{}-{}",
@@ -208,6 +209,13 @@ impl FileTransferActivity {
}
})
}
/// ### theme
///
/// Get a reference to `Theme`
fn theme(&self) -> &Theme {
self.context.as_ref().unwrap().theme_provider.theme()
}
}
/**

View File

@@ -65,13 +65,22 @@ impl FileTransferActivity {
/// Initialize file transfer activity's view
pub(super) fn init(&mut self) {
// Mount local file explorer
let local_explorer_background = self.theme().transfer_local_explorer_background;
let local_explorer_foreground = self.theme().transfer_local_explorer_foreground;
let local_explorer_highlighted = self.theme().transfer_local_explorer_highlighted;
let remote_explorer_background = self.theme().transfer_remote_explorer_background;
let remote_explorer_foreground = self.theme().transfer_remote_explorer_foreground;
let remote_explorer_highlighted = self.theme().transfer_remote_explorer_highlighted;
let log_panel = self.theme().transfer_log_window;
let log_background = self.theme().transfer_log_background;
self.view.mount(
super::COMPONENT_EXPLORER_LOCAL,
Box::new(FileList::new(
FileListPropsBuilder::default()
.with_background(Color::Yellow)
.with_foreground(Color::Yellow)
.with_borders(Borders::ALL, BorderType::Plain, Color::Yellow)
.with_highlight_color(local_explorer_highlighted)
.with_background(local_explorer_background)
.with_foreground(local_explorer_foreground)
.with_borders(Borders::ALL, BorderType::Plain, local_explorer_highlighted)
.build(),
)),
);
@@ -80,9 +89,10 @@ impl FileTransferActivity {
super::COMPONENT_EXPLORER_REMOTE,
Box::new(FileList::new(
FileListPropsBuilder::default()
.with_background(Color::LightBlue)
.with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Plain, Color::LightBlue)
.with_highlight_color(remote_explorer_highlighted)
.with_background(remote_explorer_background)
.with_foreground(remote_explorer_foreground)
.with_borders(Borders::ALL, BorderType::Plain, remote_explorer_highlighted)
.build(),
)),
);
@@ -91,7 +101,8 @@ impl FileTransferActivity {
super::COMPONENT_LOG_BOX,
Box::new(LogBox::new(
LogboxPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen)
.with_background(log_background)
.with_borders(Borders::ALL, BorderType::Plain, log_panel)
.build(),
)),
);
@@ -369,12 +380,13 @@ impl FileTransferActivity {
/// Mount error box
pub(super) fn mount_error(&mut self, text: &str) {
// Mount
let error_color = self.theme().misc_error_dialog;
self.view.mount(
super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Red)
.with_foreground(error_color)
.with_borders(Borders::ALL, BorderType::Rounded, error_color)
.bold()
.with_texts(None, vec![TextSpan::from(text)])
.build(),
@@ -393,12 +405,13 @@ impl FileTransferActivity {
pub(super) fn mount_fatal(&mut self, text: &str) {
// Mount
let error_color = self.theme().misc_error_dialog;
self.view.mount(
super::COMPONENT_TEXT_FATAL,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Red)
.with_foreground(error_color)
.with_borders(Borders::ALL, BorderType::Rounded, error_color)
.bold()
.with_texts(None, vec![TextSpan::from(text)])
.build(),
@@ -434,13 +447,14 @@ impl FileTransferActivity {
/// Mount quit popup
pub(super) fn mount_quit(&mut self) {
// Protocol
let quit_color = self.theme().misc_quit_dialog;
self.view.mount(
super::COMPONENT_RADIO_QUIT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_color(quit_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, quit_color)
.with_options(
Some(String::from("Are you sure you want to quit?")),
vec![String::from("Yes"), String::from("No")],
@@ -463,13 +477,14 @@ impl FileTransferActivity {
/// Mount disconnect popup
pub(super) fn mount_disconnect(&mut self) {
// Protocol
let quit_color = self.theme().misc_quit_dialog;
self.view.mount(
super::COMPONENT_RADIO_DISCONNECT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_color(quit_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, quit_color)
.with_options(
Some(String::from("Are you sure you want to disconnect?")),
vec![String::from("Yes"), String::from("No")],
@@ -488,11 +503,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_copy(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_COPY,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Copy file(s) to..."))
.build(),
)),
@@ -505,11 +522,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_exec(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_EXEC,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Plain, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Execute command"))
.build(),
)),
@@ -523,9 +542,17 @@ impl FileTransferActivity {
pub(super) fn mount_find(&mut self, search: &str) {
// Get color
let color: Color = match self.browser.tab() {
FileExplorerTab::Local | FileExplorerTab::FindLocal => Color::Yellow,
FileExplorerTab::Remote | FileExplorerTab::FindRemote => Color::LightBlue,
let (bg, fg, hg): (Color, Color, Color) = match self.browser.tab() {
FileExplorerTab::Local | FileExplorerTab::FindLocal => (
self.theme().transfer_local_explorer_background,
self.theme().transfer_local_explorer_foreground,
self.theme().transfer_local_explorer_highlighted,
),
FileExplorerTab::Remote | FileExplorerTab::FindRemote => (
self.theme().transfer_remote_explorer_background,
self.theme().transfer_remote_explorer_foreground,
self.theme().transfer_remote_explorer_highlighted,
),
};
// Mount component
self.view.mount(
@@ -533,9 +560,10 @@ impl FileTransferActivity {
Box::new(FileList::new(
FileListPropsBuilder::default()
.with_files(Some(format!("Search results for \"{}\"", search)), vec![])
.with_borders(Borders::ALL, BorderType::Plain, color)
.with_background(color)
.with_foreground(color)
.with_borders(Borders::ALL, BorderType::Plain, hg)
.with_highlight_color(hg)
.with_background(bg)
.with_foreground(fg)
.build(),
)),
);
@@ -548,11 +576,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_find_input(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_FIND,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Search files by name"))
.build(),
)),
@@ -567,11 +597,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_goto(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_GOTO,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Change working directory"))
.build(),
)),
@@ -584,11 +616,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_mkdir(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_MKDIR,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Insert directory name"))
.build(),
)),
@@ -601,11 +635,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_newfile(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_NEWFILE,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("New file name"))
.build(),
)),
@@ -618,11 +654,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_openwith(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_OPEN_WITH,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Open file with..."))
.build(),
)),
@@ -635,11 +673,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_rename(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_RENAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Move file(s) to..."))
.build(),
)),
@@ -652,11 +692,13 @@ impl FileTransferActivity {
}
pub(super) fn mount_saveas(&mut self) {
let input_color = self.theme().misc_input_dialog;
self.view.mount(
super::COMPONENT_INPUT_SAVEAS,
Box::new(Input::new(
InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_borders(Borders::ALL, BorderType::Rounded, input_color)
.with_foreground(input_color)
.with_label(String::from("Save as..."))
.build(),
)),
@@ -669,11 +711,12 @@ impl FileTransferActivity {
}
pub(super) fn mount_progress_bar(&mut self, root_name: String) {
let prog_color = self.theme().transfer_progress_bar;
self.view.mount(
super::COMPONENT_PROGRESS_BAR_FULL,
Box::new(ProgressBar::new(
ProgressBarPropsBuilder::default()
.with_progbar_color(Color::Green)
.with_progbar_color(prog_color)
.with_background(Color::Black)
.with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT,
@@ -688,7 +731,7 @@ impl FileTransferActivity {
super::COMPONENT_PROGRESS_BAR_PARTIAL,
Box::new(ProgressBar::new(
ProgressBarPropsBuilder::default()
.with_progbar_color(Color::Green)
.with_progbar_color(prog_color)
.with_background(Color::Black)
.with_borders(
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
@@ -708,6 +751,7 @@ impl FileTransferActivity {
}
pub(super) fn mount_file_sorting(&mut self) {
let sorting_color = self.theme().transfer_status_sorting;
let sorting: FileSorting = match self.browser.tab() {
FileExplorerTab::Local => self.local().get_file_sorting(),
FileExplorerTab::Remote => self.remote().get_file_sorting(),
@@ -723,9 +767,9 @@ impl FileTransferActivity {
super::COMPONENT_RADIO_SORTING,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightMagenta)
.with_color(sorting_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta)
.with_borders(Borders::ALL, BorderType::Rounded, sorting_color)
.with_options(
Some(String::from("Sort files by")),
vec![
@@ -747,13 +791,14 @@ impl FileTransferActivity {
}
pub(super) fn mount_radio_delete(&mut self) {
let warn_color = self.theme().misc_warn_dialog;
self.view.mount(
super::COMPONENT_RADIO_DELETE,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Red)
.with_color(warn_color)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, Color::Red)
.with_borders(Borders::ALL, BorderType::Plain, warn_color)
.with_options(
Some(String::from("Delete file")),
vec![String::from("Yes"), String::from("No")],
@@ -881,21 +926,23 @@ impl FileTransferActivity {
}
pub(super) fn refresh_local_status_bar(&mut self) {
let sorting_color = self.theme().transfer_status_sorting;
let hidden_color = self.theme().transfer_status_hidden;
let local_bar_spans: Vec<TextSpan> = vec![
TextSpanBuilder::new("File sorting: ")
.with_foreground(Color::LightYellow)
.with_foreground(sorting_color)
.build(),
TextSpanBuilder::new(Self::get_file_sorting_str(self.local().get_file_sorting()))
.with_foreground(Color::LightYellow)
.with_foreground(sorting_color)
.reversed()
.build(),
TextSpanBuilder::new(" Hidden files: ")
.with_foreground(Color::LightBlue)
.with_foreground(hidden_color)
.build(),
TextSpanBuilder::new(Self::get_hidden_files_str(
self.local().hidden_files_visible(),
))
.with_foreground(Color::LightBlue)
.with_foreground(hidden_color)
.reversed()
.build(),
];
@@ -910,31 +957,34 @@ impl FileTransferActivity {
}
pub(super) fn refresh_remote_status_bar(&mut self) {
let sorting_color = self.theme().transfer_status_sorting;
let hidden_color = self.theme().transfer_status_hidden;
let sync_color = self.theme().transfer_status_sync_browsing;
let remote_bar_spans: Vec<TextSpan> = vec![
TextSpanBuilder::new("File sorting: ")
.with_foreground(Color::LightYellow)
.with_foreground(sorting_color)
.build(),
TextSpanBuilder::new(Self::get_file_sorting_str(self.remote().get_file_sorting()))
.with_foreground(Color::LightYellow)
.with_foreground(sorting_color)
.reversed()
.build(),
TextSpanBuilder::new(" Hidden files: ")
.with_foreground(Color::LightBlue)
.with_foreground(hidden_color)
.build(),
TextSpanBuilder::new(Self::get_hidden_files_str(
self.remote().hidden_files_visible(),
))
.with_foreground(Color::LightBlue)
.with_foreground(hidden_color)
.reversed()
.build(),
TextSpanBuilder::new(" Sync Browsing: ")
.with_foreground(Color::LightGreen)
.with_foreground(sync_color)
.build(),
TextSpanBuilder::new(match self.browser.sync_browsing {
true => "ON ",
false => "OFF",
})
.with_foreground(Color::LightGreen)
.with_foreground(sync_color)
.reversed()
.build(),
];
@@ -952,6 +1002,7 @@ impl FileTransferActivity {
///
/// Mount help
pub(super) fn mount_help(&mut self) {
let key_color = self.theme().misc_keys;
self.view.mount(
super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new(
@@ -966,7 +1017,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Disconnect"))
@@ -974,7 +1025,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(
@@ -984,7 +1035,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<BACKSPACE>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Go to previous directory"))
@@ -992,7 +1043,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Change explorer tab"))
@@ -1000,7 +1051,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Move up/down in list"))
@@ -1008,7 +1059,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Enter directory"))
@@ -1016,7 +1067,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<SPACE>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Upload/Download file"))
@@ -1024,7 +1075,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<A>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Toggle hidden files"))
@@ -1032,7 +1083,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<B>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Change file sorting mode"))
@@ -1040,7 +1091,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<C>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Copy"))
@@ -1048,7 +1099,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<D>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Make directory"))
@@ -1056,7 +1107,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<G>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Go to path"))
@@ -1064,7 +1115,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<H>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Show help"))
@@ -1072,7 +1123,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<I>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Show info about selected file"))
@@ -1080,7 +1131,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<L>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Reload directory content"))
@@ -1088,7 +1139,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<M>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Select file"))
@@ -1096,7 +1147,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<N>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Create new file"))
@@ -1104,7 +1155,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<O>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(
@@ -1114,7 +1165,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<Q>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Quit termscp"))
@@ -1122,7 +1173,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<R>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Rename file"))
@@ -1130,7 +1181,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<S>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Save file as"))
@@ -1138,7 +1189,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<U>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Go to parent directory"))
@@ -1146,7 +1197,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<V>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(
@@ -1156,7 +1207,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<W>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(
@@ -1166,7 +1217,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<X>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Execute shell command"))
@@ -1174,7 +1225,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<Y>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Toggle synchronized browsing"))
@@ -1182,7 +1233,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Delete selected file"))
@@ -1190,7 +1241,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<CTRL+A>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Select all files"))
@@ -1198,7 +1249,7 @@ impl FileTransferActivity {
.add_col(
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(Color::Cyan)
.with_foreground(key_color)
.build(),
)
.add_col(TextSpan::from(" Interrupt file transfer"))

View File

@@ -29,18 +29,24 @@
// Locals
use super::SetupActivity;
// Ext
use crate::config::themes::Theme;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use std::env;
use tuirealm::tui::style::Color;
use tuirealm::{Payload, Value};
impl SetupActivity {
/// ### action_save_config
///
/// Save configuration
pub(super) fn action_save_config(&mut self) -> Result<(), String> {
pub(super) fn action_save_all(&mut self) -> Result<(), String> {
// Collect input values
self.collect_input_values();
self.save_config()
self.save_config()?;
// save theme
self.collect_styles()
.map_err(|e| format!("'{}' has an invalid color", e))?;
self.save_theme()
}
/// ### action_reset_config
@@ -56,6 +62,19 @@ impl SetupActivity {
}
}
/// ### action_reset_theme
///
/// Reset configuration input fields
pub(super) fn action_reset_theme(&mut self) -> Result<(), String> {
match self.reset_theme_changes() {
Err(err) => Err(err),
Ok(_) => {
self.load_styles();
Ok(())
}
}
}
/// ### action_delete_ssh_key
///
/// delete of a ssh key
@@ -159,4 +178,89 @@ impl SetupActivity {
}
}
}
/// ### set_color
///
/// Given a component and a color, save the color into the theme
pub(super) fn action_save_color(&mut self, component: &str, color: Color) {
let theme: &mut Theme = self.theme_mut();
match component {
super::COMPONENT_COLOR_AUTH_ADDR => {
theme.auth_address = color;
}
super::COMPONENT_COLOR_AUTH_BOOKMARKS => {
theme.auth_bookmarks = color;
}
super::COMPONENT_COLOR_AUTH_PASSWORD => {
theme.auth_password = color;
}
super::COMPONENT_COLOR_AUTH_PORT => {
theme.auth_port = color;
}
super::COMPONENT_COLOR_AUTH_PROTOCOL => {
theme.auth_protocol = color;
}
super::COMPONENT_COLOR_AUTH_RECENTS => {
theme.auth_recents = color;
}
super::COMPONENT_COLOR_AUTH_USERNAME => {
theme.auth_username = color;
}
super::COMPONENT_COLOR_MISC_ERROR => {
theme.misc_error_dialog = color;
}
super::COMPONENT_COLOR_MISC_INPUT => {
theme.misc_input_dialog = color;
}
super::COMPONENT_COLOR_MISC_KEYS => {
theme.misc_keys = color;
}
super::COMPONENT_COLOR_MISC_QUIT => {
theme.misc_quit_dialog = color;
}
super::COMPONENT_COLOR_MISC_SAVE => {
theme.misc_save_dialog = color;
}
super::COMPONENT_COLOR_MISC_WARN => {
theme.misc_warn_dialog = color;
}
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG => {
theme.transfer_local_explorer_background = color;
}
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG => {
theme.transfer_local_explorer_foreground = color;
}
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG => {
theme.transfer_local_explorer_highlighted = color;
}
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG => {
theme.transfer_remote_explorer_background = color;
}
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG => {
theme.transfer_remote_explorer_foreground = color;
}
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG => {
theme.transfer_remote_explorer_highlighted = color;
}
super::COMPONENT_COLOR_TRANSFER_LOG_BG => {
theme.transfer_log_background = color;
}
super::COMPONENT_COLOR_TRANSFER_LOG_WIN => {
theme.transfer_log_window = color;
}
super::COMPONENT_COLOR_TRANSFER_PROG_BAR => {
theme.transfer_progress_bar = color;
}
super::COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN => {
theme.transfer_status_hidden = color;
}
super::COMPONENT_COLOR_TRANSFER_STATUS_SORTING => {
theme.transfer_status_sorting = color;
}
super::COMPONENT_COLOR_TRANSFER_STATUS_SYNC => {
theme.transfer_status_sync_browsing = color;
}
_ => {}
}
}
}

View File

@@ -60,6 +60,24 @@ impl SetupActivity {
}
}
/// ### save_theme
///
/// Save theme to file
pub(super) fn save_theme(&mut self) -> Result<(), String> {
self.theme_provider()
.save()
.map_err(|e| format!("Could not save theme: {}", e))
}
/// ### reset_theme_changes
///
/// Reset changes committed to theme
pub(super) fn reset_theme_changes(&mut self) -> Result<(), String> {
self.theme_provider()
.load()
.map_err(|e| format!("Could not restore theme: {}", e))
}
/// ### delete_ssh_key
///
/// Delete ssh key from config cli

View File

@@ -34,16 +34,21 @@ mod view;
// Locals
use super::{Activity, Context, ExitReason};
use crate::config::themes::Theme;
use crate::system::theme_provider::ThemeProvider;
// Ext
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use tuirealm::{Update, View};
// -- components
// -- common
const COMPONENT_TEXT_HELP: &str = "TEXT_HELP";
const COMPONENT_TEXT_FOOTER: &str = "TEXT_FOOTER";
const COMPONENT_TEXT_ERROR: &str = "TEXT_ERROR";
const COMPONENT_RADIO_QUIT: &str = "RADIO_QUIT";
const COMPONENT_RADIO_SAVE: &str = "RADIO_SAVE";
const COMPONENT_RADIO_TAB: &str = "RADIO_TAB";
// -- config
const COMPONENT_INPUT_TEXT_EDITOR: &str = "INPUT_TEXT_EDITOR";
const COMPONENT_RADIO_DEFAULT_PROTOCOL: &str = "RADIO_DEFAULT_PROTOCOL";
const COMPONENT_RADIO_HIDDEN_FILES: &str = "RADIO_HIDDEN_FILES";
@@ -51,11 +56,47 @@ const COMPONENT_RADIO_UPDATES: &str = "RADIO_CHECK_UPDATES";
const COMPONENT_RADIO_GROUP_DIRS: &str = "RADIO_GROUP_DIRS";
const COMPONENT_INPUT_LOCAL_FILE_FMT: &str = "INPUT_LOCAL_FILE_FMT";
const COMPONENT_INPUT_REMOTE_FILE_FMT: &str = "INPUT_REMOTE_FILE_FMT";
const COMPONENT_RADIO_TAB: &str = "RADIO_TAB";
// -- ssh keys
const COMPONENT_LIST_SSH_KEYS: &str = "LIST_SSH_KEYS";
const COMPONENT_INPUT_SSH_HOST: &str = "INPUT_SSH_HOST";
const COMPONENT_INPUT_SSH_USERNAME: &str = "INPUT_SSH_USERNAME";
const COMPONENT_RADIO_DEL_SSH_KEY: &str = "RADIO_DEL_SSH_KEY";
// -- theme
const COMPONENT_COLOR_AUTH_TITLE: &str = "COMPONENT_COLOR_AUTH_TITLE";
const COMPONENT_COLOR_MISC_TITLE: &str = "COMPONENT_COLOR_MISC_TITLE";
const COMPONENT_COLOR_TRANSFER_TITLE: &str = "COMPONENT_COLOR_TRANSFER_TITLE";
const COMPONENT_COLOR_TRANSFER_TITLE_2: &str = "COMPONENT_COLOR_TRANSFER_TITLE_2";
const COMPONENT_COLOR_AUTH_ADDR: &str = "COMPONENT_COLOR_AUTH_ADDR";
const COMPONENT_COLOR_AUTH_BOOKMARKS: &str = "COMPONENT_COLOR_AUTH_BOOKMARKS";
const COMPONENT_COLOR_AUTH_PASSWORD: &str = "COMPONENT_COLOR_AUTH_PASSWORD";
const COMPONENT_COLOR_AUTH_PORT: &str = "COMPONENT_COLOR_AUTH_PORT";
const COMPONENT_COLOR_AUTH_PROTOCOL: &str = "COMPONENT_COLOR_AUTH_PROTOCOL";
const COMPONENT_COLOR_AUTH_RECENTS: &str = "COMPONENT_COLOR_AUTH_RECENTS";
const COMPONENT_COLOR_AUTH_USERNAME: &str = "COMPONENT_COLOR_AUTH_USERNAME";
const COMPONENT_COLOR_MISC_ERROR: &str = "COMPONENT_COLOR_MISC_ERROR";
const COMPONENT_COLOR_MISC_INPUT: &str = "COMPONENT_COLOR_MISC_INPUT";
const COMPONENT_COLOR_MISC_KEYS: &str = "COMPONENT_COLOR_MISC_KEYS";
const COMPONENT_COLOR_MISC_QUIT: &str = "COMPONENT_COLOR_MISC_QUIT";
const COMPONENT_COLOR_MISC_SAVE: &str = "COMPONENT_COLOR_MISC_SAVE";
const COMPONENT_COLOR_MISC_WARN: &str = "COMPONENT_COLOR_MISC_WARN";
const COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG: &str =
"COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG";
const COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG: &str =
"COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG";
const COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG: &str =
"COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG";
const COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG: &str =
"COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG";
const COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG: &str =
"COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG";
const COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG: &str =
"COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG";
const COMPONENT_COLOR_TRANSFER_PROG_BAR: &str = "COMPONENT_COLOR_TRANSFER_PROG_BAR";
const COMPONENT_COLOR_TRANSFER_LOG_BG: &str = "COMPONENT_COLOR_TRANSFER_LOG_BG";
const COMPONENT_COLOR_TRANSFER_LOG_WIN: &str = "COMPONENT_COLOR_TRANSFER_LOG_WIN";
const COMPONENT_COLOR_TRANSFER_STATUS_SORTING: &str = "COMPONENT_COLOR_TRANSFER_STATUS_SORTING";
const COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN: &str = "COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN";
const COMPONENT_COLOR_TRANSFER_STATUS_SYNC: &str = "COMPONENT_COLOR_TRANSFER_STATUS_SYNC";
/// ### ViewLayout
///
@@ -64,6 +105,7 @@ const COMPONENT_RADIO_DEL_SSH_KEY: &str = "RADIO_DEL_SSH_KEY";
enum ViewLayout {
SetupForm,
SshKeys,
Theme,
}
/// ## SetupActivity
@@ -89,6 +131,20 @@ impl Default for SetupActivity {
}
}
impl SetupActivity {
fn theme(&self) -> &Theme {
self.context.as_ref().unwrap().theme_provider.theme()
}
fn theme_mut(&mut self) -> &mut Theme {
self.context.as_mut().unwrap().theme_provider.theme_mut()
}
fn theme_provider(&mut self) -> &mut ThemeProvider {
&mut self.context.as_mut().unwrap().theme_provider
}
}
impl Activity for SetupActivity {
/// ### on_create
///
@@ -105,7 +161,7 @@ impl Activity for SetupActivity {
error!("Failed to enter raw mode: {}", err);
}
// Init view
self.init_setup();
self.init(ViewLayout::SetupForm);
// Verify error state from context
if let Some(err) = self.context.as_mut().unwrap().get_error() {
self.mount_error(err.as_str());

View File

@@ -28,13 +28,25 @@
*/
// locals
use super::{
SetupActivity, COMPONENT_INPUT_LOCAL_FILE_FMT, COMPONENT_INPUT_REMOTE_FILE_FMT,
COMPONENT_INPUT_SSH_HOST, COMPONENT_INPUT_SSH_USERNAME, COMPONENT_INPUT_TEXT_EDITOR,
COMPONENT_LIST_SSH_KEYS, COMPONENT_RADIO_DEFAULT_PROTOCOL, COMPONENT_RADIO_DEL_SSH_KEY,
COMPONENT_RADIO_GROUP_DIRS, COMPONENT_RADIO_HIDDEN_FILES, COMPONENT_RADIO_QUIT,
COMPONENT_RADIO_SAVE, COMPONENT_RADIO_UPDATES, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP,
SetupActivity, ViewLayout, COMPONENT_COLOR_AUTH_ADDR, COMPONENT_COLOR_AUTH_BOOKMARKS,
COMPONENT_COLOR_AUTH_PASSWORD, COMPONENT_COLOR_AUTH_PORT, COMPONENT_COLOR_AUTH_PROTOCOL,
COMPONENT_COLOR_AUTH_RECENTS, COMPONENT_COLOR_AUTH_USERNAME, COMPONENT_COLOR_MISC_ERROR,
COMPONENT_COLOR_MISC_INPUT, COMPONENT_COLOR_MISC_KEYS, COMPONENT_COLOR_MISC_QUIT,
COMPONENT_COLOR_MISC_SAVE, COMPONENT_COLOR_MISC_WARN,
COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG,
COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG,
COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG,
COMPONENT_COLOR_TRANSFER_LOG_BG, COMPONENT_COLOR_TRANSFER_LOG_WIN,
COMPONENT_COLOR_TRANSFER_PROG_BAR, COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN,
COMPONENT_COLOR_TRANSFER_STATUS_SORTING, COMPONENT_COLOR_TRANSFER_STATUS_SYNC,
COMPONENT_INPUT_LOCAL_FILE_FMT, COMPONENT_INPUT_REMOTE_FILE_FMT, COMPONENT_INPUT_SSH_HOST,
COMPONENT_INPUT_SSH_USERNAME, COMPONENT_INPUT_TEXT_EDITOR, COMPONENT_LIST_SSH_KEYS,
COMPONENT_RADIO_DEFAULT_PROTOCOL, COMPONENT_RADIO_DEL_SSH_KEY, COMPONENT_RADIO_GROUP_DIRS,
COMPONENT_RADIO_HIDDEN_FILES, COMPONENT_RADIO_QUIT, COMPONENT_RADIO_SAVE,
COMPONENT_RADIO_UPDATES, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP,
};
use crate::ui::keymap::*;
use crate::utils::parser::parse_color;
// ext
use tuirealm::{Msg, Payload, Update, Value};
@@ -45,6 +57,16 @@ impl Update for SetupActivity {
/// Update auth activity model based on msg
/// The function exits when returns None
fn update(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
match self.layout {
ViewLayout::SetupForm => self.update_setup(msg),
ViewLayout::SshKeys => self.update_ssh_keys(msg),
ViewLayout::Theme => self.update_theme(msg),
}
}
}
impl SetupActivity {
fn update_setup(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
// Match msg
match ref_msg {
@@ -118,7 +140,100 @@ impl Update for SetupActivity {
// Exit
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Save changes
if let Err(err) = self.action_save_config() {
if let Err(err) = self.action_save_all() {
self.mount_error(err.as_str());
}
// Exit
self.exit_reason = Some(super::ExitReason::Quit);
None
}
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => {
// Quit
self.exit_reason = Some(super::ExitReason::Quit);
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(_)) => {
// Umount popup
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, _) => None,
// Close help
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => {
// Umount help
self.umount_help();
None
}
(COMPONENT_TEXT_HELP, _) => None,
// Save popup
(COMPONENT_RADIO_SAVE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Save config
if let Err(err) = self.action_save_all() {
self.mount_error(err.as_str());
}
self.umount_save_popup();
None
}
(COMPONENT_RADIO_SAVE, Msg::OnSubmit(_)) => {
// Umount radio save
self.umount_save_popup();
None
}
(COMPONENT_RADIO_SAVE, _) => None,
// <CTRL+H> Show help
(_, &MSG_KEY_CTRL_H) => {
// Show help
self.mount_help();
None
}
(_, &MSG_KEY_TAB) => {
// Change view
self.init(ViewLayout::SshKeys);
None
}
// <CTRL+R> Revert changes
(_, &MSG_KEY_CTRL_R) => {
// Revert changes
if let Err(err) = self.action_reset_config() {
self.mount_error(err.as_str());
}
None
}
// <CTRL+S> Save
(_, &MSG_KEY_CTRL_S) => {
// Show save
self.mount_save_popup();
None
}
// <ESC>
(_, &MSG_KEY_ESC) => {
// Mount quit prompt
self.mount_quit();
None
}
(_, _) => None, // Nothing to do
},
}
}
fn update_ssh_keys(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
// Match msg
match ref_msg {
None => None,
Some(msg) => match msg {
// Error <ENTER> or <ESC>
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) => {
// Umount text error
self.umount_error();
None
}
(COMPONENT_TEXT_ERROR, _) => None,
// Exit
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Save changes
if let Err(err) = self.action_save_all() {
self.mount_error(err.as_str());
}
// Exit
@@ -163,7 +278,7 @@ impl Update for SetupActivity {
// Save popup
(COMPONENT_RADIO_SAVE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Save config
if let Err(err) = self.action_save_config() {
if let Err(err) = self.action_save_all() {
self.mount_error(err.as_str());
}
self.umount_save_popup();
@@ -176,12 +291,6 @@ impl Update for SetupActivity {
}
(COMPONENT_RADIO_SAVE, _) => None,
// Edit SSH Key
// <TAB> Change view
(COMPONENT_LIST_SSH_KEYS, &MSG_KEY_TAB) => {
// Change view
self.init_setup();
None
}
// <CTRL+H> Show help
(_, &MSG_KEY_CTRL_H) => {
// Show help
@@ -247,7 +356,7 @@ impl Update for SetupActivity {
}
(_, &MSG_KEY_TAB) => {
// Change view
self.init_ssh_keys();
self.init(ViewLayout::Theme);
None
}
// <CTRL+R> Revert changes
@@ -274,4 +383,312 @@ impl Update for SetupActivity {
},
}
}
fn update_theme(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
// Match msg
match ref_msg {
None => None,
Some(msg) => match msg {
// Input fields
(COMPONENT_COLOR_AUTH_PROTOCOL, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_ADDR);
None
}
(COMPONENT_COLOR_AUTH_ADDR, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_PORT);
None
}
(COMPONENT_COLOR_AUTH_PORT, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_USERNAME);
None
}
(COMPONENT_COLOR_AUTH_USERNAME, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_PASSWORD);
None
}
(COMPONENT_COLOR_AUTH_PASSWORD, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_BOOKMARKS);
None
}
(COMPONENT_COLOR_AUTH_BOOKMARKS, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_RECENTS);
None
}
(COMPONENT_COLOR_AUTH_RECENTS, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_MISC_ERROR);
None
}
(COMPONENT_COLOR_MISC_ERROR, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_MISC_INPUT);
None
}
(COMPONENT_COLOR_MISC_INPUT, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_MISC_KEYS);
None
}
(COMPONENT_COLOR_MISC_KEYS, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_MISC_QUIT);
None
}
(COMPONENT_COLOR_MISC_QUIT, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_MISC_SAVE);
None
}
(COMPONENT_COLOR_MISC_SAVE, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_MISC_WARN);
None
}
(COMPONENT_COLOR_MISC_WARN, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, &MSG_KEY_DOWN) => {
self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG, &MSG_KEY_DOWN) => {
self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, &MSG_KEY_DOWN) => {
self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR);
None
}
(COMPONENT_COLOR_TRANSFER_PROG_BAR, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_BG);
None
}
(COMPONENT_COLOR_TRANSFER_LOG_BG, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_WIN);
None
}
(COMPONENT_COLOR_TRANSFER_LOG_WIN, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SORTING);
None
}
(COMPONENT_COLOR_TRANSFER_STATUS_SORTING, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN);
None
}
(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SYNC);
None
}
(COMPONENT_COLOR_TRANSFER_STATUS_SYNC, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_COLOR_AUTH_PROTOCOL);
None
}
(COMPONENT_COLOR_AUTH_PROTOCOL, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SYNC);
None
}
(COMPONENT_COLOR_AUTH_ADDR, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_PROTOCOL);
None
}
(COMPONENT_COLOR_AUTH_PORT, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_ADDR);
None
}
(COMPONENT_COLOR_AUTH_USERNAME, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_PORT);
None
}
(COMPONENT_COLOR_AUTH_PASSWORD, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_USERNAME);
None
}
(COMPONENT_COLOR_AUTH_BOOKMARKS, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_PASSWORD);
None
}
(COMPONENT_COLOR_AUTH_RECENTS, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_BOOKMARKS);
None
}
(COMPONENT_COLOR_MISC_ERROR, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_AUTH_RECENTS);
None
}
(COMPONENT_COLOR_MISC_INPUT, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_MISC_ERROR);
None
}
(COMPONENT_COLOR_MISC_KEYS, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_MISC_INPUT);
None
}
(COMPONENT_COLOR_MISC_QUIT, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_MISC_KEYS);
None
}
(COMPONENT_COLOR_MISC_SAVE, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_MISC_QUIT);
None
}
(COMPONENT_COLOR_MISC_WARN, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_MISC_SAVE);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_MISC_WARN);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG, &MSG_KEY_UP) => {
self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG);
None
}
(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG, &MSG_KEY_UP) => {
self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG);
None
}
(COMPONENT_COLOR_TRANSFER_PROG_BAR, &MSG_KEY_UP) => {
self.view
.active(COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG);
None
}
(COMPONENT_COLOR_TRANSFER_LOG_BG, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_PROG_BAR);
None
}
(COMPONENT_COLOR_TRANSFER_LOG_WIN, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_BG);
None
}
(COMPONENT_COLOR_TRANSFER_STATUS_SORTING, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_LOG_WIN);
None
}
(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_SORTING);
None
}
(COMPONENT_COLOR_TRANSFER_STATUS_SYNC, &MSG_KEY_UP) => {
self.view.active(COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN);
None
}
// On color change
(component, Msg::OnChange(Payload::One(Value::Str(color)))) => {
if let Some(color) = parse_color(color) {
self.action_save_color(component, color);
}
None
}
// Error <ENTER> or <ESC>
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) | (COMPONENT_TEXT_ERROR, &MSG_KEY_ESC) => {
// Umount text error
self.umount_error();
None
}
(COMPONENT_TEXT_ERROR, _) => None,
// Exit
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Save changes
if let Err(err) = self.action_save_all() {
self.mount_error(err.as_str());
}
// Exit
self.exit_reason = Some(super::ExitReason::Quit);
None
}
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => {
// Quit
self.exit_reason = Some(super::ExitReason::Quit);
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(_)) => {
// Umount popup
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, _) => None,
// Close help
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => {
// Umount help
self.umount_help();
None
}
(COMPONENT_TEXT_HELP, _) => None,
// Save popup
(COMPONENT_RADIO_SAVE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Save config
if let Err(err) = self.action_save_all() {
self.mount_error(err.as_str());
}
self.umount_save_popup();
None
}
(COMPONENT_RADIO_SAVE, Msg::OnSubmit(_)) => {
// Umount radio save
self.umount_save_popup();
None
}
(COMPONENT_RADIO_SAVE, _) => None,
// Edit SSH Key
// <CTRL+H> Show help
(_, &MSG_KEY_CTRL_H) => {
// Show help
self.mount_help();
None
}
(_, &MSG_KEY_TAB) => {
// Change view
self.init(ViewLayout::SetupForm);
None
}
// <CTRL+R> Revert changes
(_, &MSG_KEY_CTRL_R) => {
// Revert changes
if let Err(err) = self.action_reset_theme() {
self.mount_error(err.as_str());
}
None
}
// <CTRL+S> Save
(_, &MSG_KEY_CTRL_S) => {
// Show save
self.mount_save_popup();
None
}
// <ESC>
(_, &MSG_KEY_ESC) => {
// Mount quit prompt
self.mount_quit();
None
}
(_, _) => None, // Nothing to do
},
}
}
}

View File

@@ -1,808 +0,0 @@
//! ## SetupActivity
//!
//! `setup_activity` is the module which implements the Setup activity, which is the activity to
//! work on termscp configuration
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Locals
use super::{Context, SetupActivity, ViewLayout};
use crate::filetransfer::FileTransferProtocol;
use crate::fs::explorer::GroupDirs;
use crate::ui::components::{
bookmark_list::{BookmarkList, BookmarkListPropsBuilder},
msgbox::{MsgBox, MsgBoxPropsBuilder},
};
use crate::utils::ui::draw_area_in;
// Ext
use std::path::PathBuf;
use tuirealm::components::{
input::{Input, InputPropsBuilder},
radio::{Radio, RadioPropsBuilder},
scrolltable::{ScrollTablePropsBuilder, Scrolltable},
span::{Span, SpanPropsBuilder},
};
use tuirealm::tui::{
layout::{Constraint, Direction, Layout},
style::Color,
widgets::{BorderType, Borders, Clear},
};
use tuirealm::{
props::{PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder},
Payload, Value, View,
};
impl SetupActivity {
// -- view
/// ### init_setup
///
/// Initialize setup view
pub(super) fn init_setup(&mut self) {
// Init view
self.view = View::init();
// Common stuff
// Radio tab
self.view.mount(
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::White)
.with_options(
None,
vec![String::from("User Interface"), String::from("SSH Keys")],
)
.with_value(0)
.build(),
)),
);
// Footer
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
// Input fields
self.view.mount(
super::COMPONENT_INPUT_TEXT_EDITOR,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("Text editor"))
.build(),
)),
);
self.view.active(super::COMPONENT_INPUT_TEXT_EDITOR); // <-- Focus
self.view.mount(
super::COMPONENT_RADIO_DEFAULT_PROTOCOL,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightCyan)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightCyan)
.with_options(
Some(String::from("Default file transfer protocol")),
vec![
String::from("SFTP"),
String::from("SCP"),
String::from("FTP"),
String::from("FTPS"),
],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_HIDDEN_FILES,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightRed)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options(
Some(String::from("Show hidden files (by default)")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_UPDATES,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_options(
Some(String::from("Check for updates?")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_GROUP_DIRS,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightMagenta)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta)
.with_options(
Some(String::from("Group directories")),
vec![
String::from("Display first"),
String::from("Display Last"),
String::from("No"),
],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_LOCAL_FILE_FMT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue)
.with_label(String::from("File formatter syntax (local)"))
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_REMOTE_FILE_FMT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("File formatter syntax (remote)"))
.build(),
)),
);
// Load values
self.load_input_values();
// Set view
self.layout = ViewLayout::SetupForm;
}
/// ### init_ssh_keys
///
/// Initialize ssh keys view
pub(super) fn init_ssh_keys(&mut self) {
// Init view
self.view = View::init();
// Common stuff
// Radio tab
self.view.mount(
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::LightYellow)
.with_options(
None,
vec![String::from("User Interface"), String::from("SSH Keys")],
)
.with_value(1)
.build(),
)),
);
// Footer
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
self.view.mount(
super::COMPONENT_LIST_SSH_KEYS,
Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default()
.with_bookmarks(Some(String::from("SSH Keys")), vec![])
.with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen)
.with_background(Color::LightGreen)
.with_foreground(Color::Black)
.build(),
)),
);
// Give focus
self.view.active(super::COMPONENT_LIST_SSH_KEYS);
// Load keys
self.reload_ssh_keys();
// Set view
self.layout = ViewLayout::SshKeys;
}
/// ### view
///
/// View gui
pub(super) fn view(&mut self) {
let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
// Prepare main chunks
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Length(3), // Current tab
Constraint::Percentage(90), // Main body
Constraint::Length(3), // Help footer
]
.as_ref(),
)
.split(f.size());
// Render common widget
self.view.render(super::COMPONENT_RADIO_TAB, f, chunks[0]);
self.view.render(super::COMPONENT_TEXT_FOOTER, f, chunks[2]);
match self.layout {
ViewLayout::SetupForm => {
// Make chunks
let ui_cfg_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3), // Text editor
Constraint::Length(3), // Protocol tab
Constraint::Length(3), // Hidden files
Constraint::Length(3), // Updates tab
Constraint::Length(3), // Group dirs
Constraint::Length(3), // Local Format input
Constraint::Length(3), // Remote Format input
Constraint::Length(1), // Empty ?
]
.as_ref(),
)
.split(chunks[1]);
self.view
.render(super::COMPONENT_INPUT_TEXT_EDITOR, f, ui_cfg_chunks[0]);
self.view
.render(super::COMPONENT_RADIO_DEFAULT_PROTOCOL, f, ui_cfg_chunks[1]);
self.view
.render(super::COMPONENT_RADIO_HIDDEN_FILES, f, ui_cfg_chunks[2]);
self.view
.render(super::COMPONENT_RADIO_UPDATES, f, ui_cfg_chunks[3]);
self.view
.render(super::COMPONENT_RADIO_GROUP_DIRS, f, ui_cfg_chunks[4]);
self.view
.render(super::COMPONENT_INPUT_LOCAL_FILE_FMT, f, ui_cfg_chunks[5]);
self.view
.render(super::COMPONENT_INPUT_REMOTE_FILE_FMT, f, ui_cfg_chunks[6]);
}
ViewLayout::SshKeys => {
let sshcfg_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(100)].as_ref())
.split(chunks[1]);
self.view
.render(super::COMPONENT_LIST_SSH_KEYS, f, sshcfg_chunks[0]);
}
}
// Popups
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_ERROR) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_ERROR, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_QUIT) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 40, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_QUIT, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 70);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_TEXT_HELP, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_SAVE) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_SAVE, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DEL_SSH_KEY) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view
.render(super::COMPONENT_RADIO_DEL_SSH_KEY, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_SSH_HOST) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 20);
f.render_widget(Clear, popup);
let popup_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3), // Host
Constraint::Length(3), // Username
]
.as_ref(),
)
.split(popup);
self.view
.render(super::COMPONENT_INPUT_SSH_HOST, f, popup_chunks[0]);
self.view
.render(super::COMPONENT_INPUT_SSH_USERNAME, f, popup_chunks[1]);
}
}
});
// Put context back to context
self.context = Some(ctx);
}
// -- mount
/// ### mount_error
///
/// Mount error box
pub(super) fn mount_error(&mut self, text: &str) {
// Mount
self.view.mount(
super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.bold()
.with_borders(Borders::ALL, BorderType::Rounded, Color::Red)
.with_texts(None, vec![TextSpan::from(text)])
.build(),
)),
);
// Give focus to error
self.view.active(super::COMPONENT_TEXT_ERROR);
}
/// ### umount_error
///
/// Umount error message
pub(super) fn umount_error(&mut self) {
self.view.umount(super::COMPONENT_TEXT_ERROR);
}
/// ### mount_del_ssh_key
///
/// Mount delete ssh key component
pub(super) fn mount_del_ssh_key(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_DEL_SSH_KEY,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightRed)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options(
Some(String::from("Delete key?")),
vec![String::from("Yes"), String::from("No")],
)
.with_value(1) // Default: No
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_DEL_SSH_KEY);
}
/// ### umount_del_ssh_key
///
/// Umount delete ssh key
pub(super) fn umount_del_ssh_key(&mut self) {
self.view.umount(super::COMPONENT_RADIO_DEL_SSH_KEY);
}
/// ### mount_new_ssh_key
///
/// Mount new ssh key prompt
pub(super) fn mount_new_ssh_key(&mut self) {
self.view.mount(
super::COMPONENT_INPUT_SSH_HOST,
Box::new(Input::new(
InputPropsBuilder::default()
.with_label(String::from("Hostname or address"))
.with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT,
BorderType::Plain,
Color::Reset,
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_SSH_USERNAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_label(String::from("Username"))
.with_borders(
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
BorderType::Plain,
Color::Reset,
)
.build(),
)),
);
self.view.active(super::COMPONENT_INPUT_SSH_HOST);
}
/// ### umount_new_ssh_key
///
/// Umount new ssh key prompt
pub(super) fn umount_new_ssh_key(&mut self) {
self.view.umount(super::COMPONENT_INPUT_SSH_HOST);
self.view.umount(super::COMPONENT_INPUT_SSH_USERNAME);
}
/// ### mount_quit
///
/// Mount quit popup
pub(super) fn mount_quit(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_QUIT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightRed)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options(
Some(String::from("Exit setup?")),
vec![
String::from("Save"),
String::from("Don't save"),
String::from("Cancel"),
],
)
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_QUIT);
}
/// ### umount_quit
///
/// Umount quit
pub(super) fn umount_quit(&mut self) {
self.view.umount(super::COMPONENT_RADIO_QUIT);
}
/// ### mount_save_popup
///
/// Mount save popup
pub(super) fn mount_save_popup(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_SAVE,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_options(
Some(String::from("Save changes?")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_SAVE);
}
/// ### umount_quit
///
/// Umount quit
pub(super) fn umount_save_popup(&mut self) {
self.view.umount(super::COMPONENT_RADIO_SAVE);
}
/// ### mount_help
///
/// Mount help
pub(super) fn mount_help(&mut self) {
self.view.mount(
super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new(
ScrollTablePropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_highlighted_str(Some("?"))
.with_max_scroll_step(8)
.bold()
.with_table(
Some(String::from("Help")),
TableBuilder::default()
.add_col(
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Exit setup"))
.add_row()
.add_col(
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change setup page"))
.add_row()
.add_col(
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change cursor"))
.add_row()
.add_col(
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change input field"))
.add_row()
.add_col(
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Select / Dismiss popup"))
.add_row()
.add_col(
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Delete SSH key"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+N>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" New SSH key"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+R>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Revert changes"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+S>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Save configuration"))
.build(),
)
.build(),
)),
);
// Active help
self.view.active(super::COMPONENT_TEXT_HELP);
}
/// ### umount_help
///
/// Umount help
pub(super) fn umount_help(&mut self) {
self.view.umount(super::COMPONENT_TEXT_HELP);
}
/// ### load_input_values
///
/// Load values from configuration into input fields
pub(super) fn load_input_values(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
// Text editor
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_TEXT_EDITOR) {
let text_editor: String =
String::from(cli.get_text_editor().as_path().to_string_lossy());
let props = InputPropsBuilder::from(props)
.with_value(text_editor)
.build();
let _ = self.view.update(super::COMPONENT_INPUT_TEXT_EDITOR, props);
}
// Protocol
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DEFAULT_PROTOCOL) {
let protocol: usize = match cli.get_default_protocol() {
FileTransferProtocol::Sftp => 0,
FileTransferProtocol::Scp => 1,
FileTransferProtocol::Ftp(false) => 2,
FileTransferProtocol::Ftp(true) => 3,
};
let props = RadioPropsBuilder::from(props).with_value(protocol).build();
let _ = self
.view
.update(super::COMPONENT_RADIO_DEFAULT_PROTOCOL, props);
}
// Hidden files
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_HIDDEN_FILES) {
let hidden: usize = match cli.get_show_hidden_files() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(hidden).build();
let _ = self.view.update(super::COMPONENT_RADIO_HIDDEN_FILES, props);
}
// Updates
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_UPDATES) {
let updates: usize = match cli.get_check_for_updates() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(updates).build();
let _ = self.view.update(super::COMPONENT_RADIO_UPDATES, props);
}
// Group dirs
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_GROUP_DIRS) {
let dirs: usize = match cli.get_group_dirs() {
Some(GroupDirs::First) => 0,
Some(GroupDirs::Last) => 1,
None => 2,
};
let props = RadioPropsBuilder::from(props).with_value(dirs).build();
let _ = self.view.update(super::COMPONENT_RADIO_GROUP_DIRS, props);
}
// Local File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_LOCAL_FILE_FMT) {
let file_fmt: String = cli.get_local_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
let _ = self
.view
.update(super::COMPONENT_INPUT_LOCAL_FILE_FMT, props);
}
// Remote File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_REMOTE_FILE_FMT) {
let file_fmt: String = cli.get_remote_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
let _ = self
.view
.update(super::COMPONENT_INPUT_REMOTE_FILE_FMT, props);
}
}
}
/// ### collect_input_values
///
/// Collect values from input and put them into the configuration
pub(super) fn collect_input_values(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
if let Some(Payload::One(Value::Str(editor))) =
self.view.get_state(super::COMPONENT_INPUT_TEXT_EDITOR)
{
cli.set_text_editor(PathBuf::from(editor.as_str()));
}
if let Some(Payload::One(Value::Usize(protocol))) =
self.view.get_state(super::COMPONENT_RADIO_DEFAULT_PROTOCOL)
{
let protocol: FileTransferProtocol = match protocol {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
};
cli.set_default_protocol(protocol);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_HIDDEN_FILES)
{
let show: bool = matches!(opt, 0);
cli.set_show_hidden_files(show);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_UPDATES)
{
let check: bool = matches!(opt, 0);
cli.set_check_for_updates(check);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_LOCAL_FILE_FMT)
{
cli.set_local_file_fmt(fmt);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_REMOTE_FILE_FMT)
{
cli.set_remote_file_fmt(fmt);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_GROUP_DIRS)
{
let dirs: Option<GroupDirs> = match opt {
0 => Some(GroupDirs::First),
1 => Some(GroupDirs::Last),
_ => None,
};
cli.set_group_dirs(dirs);
}
}
}
/// ### reload_ssh_keys
///
/// Reload ssh keys
pub(super) fn reload_ssh_keys(&mut self) {
if let Some(cli) = self.context.as_ref().unwrap().config_client.as_ref() {
// get props
if let Some(props) = self.view.get_props(super::COMPONENT_LIST_SSH_KEYS) {
// Create texts
let keys: Vec<String> = cli
.iter_ssh_keys()
.map(|x| {
let (addr, username, _) = cli.get_ssh_key(x).ok().unwrap().unwrap();
format!("{} at {}", addr, username)
})
.collect();
let props = BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("SSH Keys")), keys)
.build();
self.view.update(super::COMPONENT_LIST_SSH_KEYS, props);
}
}
}
}

View File

@@ -0,0 +1,265 @@
//! ## SetupActivity
//!
//! `setup_activity` is the module which implements the Setup activity, which is the activity to
//! work on termscp configuration
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod setup;
pub mod ssh_keys;
pub mod theme;
use super::*;
pub use setup::*;
pub use ssh_keys::*;
pub use theme::*;
// Locals
use crate::ui::components::msgbox::{MsgBox, MsgBoxPropsBuilder};
// Ext
use tuirealm::components::{
radio::{Radio, RadioPropsBuilder},
scrolltable::{ScrollTablePropsBuilder, Scrolltable},
};
use tuirealm::props::{PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder};
use tuirealm::tui::{
style::Color,
widgets::{BorderType, Borders},
};
impl SetupActivity {
// -- view
pub(super) fn init(&mut self, layout: ViewLayout) {
self.layout = layout;
match self.layout {
ViewLayout::SetupForm => self.init_setup(),
ViewLayout::SshKeys => self.init_ssh_keys(),
ViewLayout::Theme => self.init_theme(),
}
}
/// ### view
///
/// View gui
pub(super) fn view(&mut self) {
match self.layout {
ViewLayout::SetupForm => self.view_setup(),
ViewLayout::SshKeys => self.view_ssh_keys(),
ViewLayout::Theme => self.view_theme(),
}
}
// -- mount
/// ### mount_error
///
/// Mount error box
pub(super) fn mount_error(&mut self, text: &str) {
// Mount
self.view.mount(
super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.bold()
.with_borders(Borders::ALL, BorderType::Rounded, Color::Red)
.with_texts(None, vec![TextSpan::from(text)])
.build(),
)),
);
// Give focus to error
self.view.active(super::COMPONENT_TEXT_ERROR);
}
/// ### umount_error
///
/// Umount error message
pub(super) fn umount_error(&mut self) {
self.view.umount(super::COMPONENT_TEXT_ERROR);
}
/// ### mount_quit
///
/// Mount quit popup
pub(super) fn mount_quit(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_QUIT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightRed)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options(
Some(String::from("Exit setup?")),
vec![
String::from("Save"),
String::from("Don't save"),
String::from("Cancel"),
],
)
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_QUIT);
}
/// ### umount_quit
///
/// Umount quit
pub(super) fn umount_quit(&mut self) {
self.view.umount(super::COMPONENT_RADIO_QUIT);
}
/// ### mount_save_popup
///
/// Mount save popup
pub(super) fn mount_save_popup(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_SAVE,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_options(
Some(String::from("Save changes?")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_SAVE);
}
/// ### umount_quit
///
/// Umount quit
pub(super) fn umount_save_popup(&mut self) {
self.view.umount(super::COMPONENT_RADIO_SAVE);
}
/// ### mount_help
///
/// Mount help
pub(super) fn mount_help(&mut self) {
self.view.mount(
super::COMPONENT_TEXT_HELP,
Box::new(Scrolltable::new(
ScrollTablePropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_highlighted_str(Some("?"))
.with_max_scroll_step(8)
.bold()
.with_table(
Some(String::from("Help")),
TableBuilder::default()
.add_col(
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Exit setup"))
.add_row()
.add_col(
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change setup page"))
.add_row()
.add_col(
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change cursor"))
.add_row()
.add_col(
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Change input field"))
.add_row()
.add_col(
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Select / Dismiss popup"))
.add_row()
.add_col(
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Delete SSH key"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+N>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" New SSH key"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+R>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Revert changes"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+S>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Save configuration"))
.build(),
)
.build(),
)),
);
// Active help
self.view.active(super::COMPONENT_TEXT_HELP);
}
/// ### umount_help
///
/// Umount help
pub(super) fn umount_help(&mut self) {
self.view.umount(super::COMPONENT_TEXT_HELP);
}
}

View File

@@ -0,0 +1,414 @@
//! ## SetupActivity
//!
//! `setup_activity` is the module which implements the Setup activity, which is the activity to
//! work on termscp configuration
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Locals
use super::{Context, SetupActivity};
use crate::filetransfer::FileTransferProtocol;
use crate::fs::explorer::GroupDirs;
use crate::utils::ui::draw_area_in;
// Ext
use std::path::PathBuf;
use tuirealm::components::{
input::{Input, InputPropsBuilder},
radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
};
use tuirealm::tui::{
layout::{Constraint, Direction, Layout},
style::Color,
widgets::{BorderType, Borders, Clear},
};
use tuirealm::{
props::{PropsBuilder, TextSpanBuilder},
Payload, Value, View,
};
impl SetupActivity {
// -- view
/// ### init_setup
///
/// Initialize setup view
pub(super) fn init_setup(&mut self) {
// Init view
self.view = View::init();
// Common stuff
// Radio tab
self.view.mount(
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::White)
.with_options(
None,
vec![
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
],
)
.with_value(0)
.build(),
)),
);
// Footer
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
// Input fields
self.view.mount(
super::COMPONENT_INPUT_TEXT_EDITOR,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("Text editor"))
.build(),
)),
);
self.view.active(super::COMPONENT_INPUT_TEXT_EDITOR); // <-- Focus
self.view.mount(
super::COMPONENT_RADIO_DEFAULT_PROTOCOL,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightCyan)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightCyan)
.with_options(
Some(String::from("Default file transfer protocol")),
vec![
String::from("SFTP"),
String::from("SCP"),
String::from("FTP"),
String::from("FTPS"),
],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_HIDDEN_FILES,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightRed)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options(
Some(String::from("Show hidden files (by default)")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_UPDATES,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_options(
Some(String::from("Check for updates?")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_GROUP_DIRS,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightMagenta)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta)
.with_options(
Some(String::from("Group directories")),
vec![
String::from("Display first"),
String::from("Display Last"),
String::from("No"),
],
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_LOCAL_FILE_FMT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue)
.with_label(String::from("File formatter syntax (local)"))
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_REMOTE_FILE_FMT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("File formatter syntax (remote)"))
.build(),
)),
);
// Load values
self.load_input_values();
}
pub(super) fn view_setup(&mut self) {
let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
// Prepare main chunks
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Length(3), // Current tab
Constraint::Length(21), // Main body
Constraint::Length(3), // Help footer
]
.as_ref(),
)
.split(f.size());
// Render common widget
self.view.render(super::COMPONENT_RADIO_TAB, f, chunks[0]);
self.view.render(super::COMPONENT_TEXT_FOOTER, f, chunks[2]);
// Make chunks
let ui_cfg_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3), // Text editor
Constraint::Length(3), // Protocol tab
Constraint::Length(3), // Hidden files
Constraint::Length(3), // Updates tab
Constraint::Length(3), // Group dirs
Constraint::Length(3), // Local Format input
Constraint::Length(3), // Remote Format input
]
.as_ref(),
)
.split(chunks[1]);
self.view
.render(super::COMPONENT_INPUT_TEXT_EDITOR, f, ui_cfg_chunks[0]);
self.view
.render(super::COMPONENT_RADIO_DEFAULT_PROTOCOL, f, ui_cfg_chunks[1]);
self.view
.render(super::COMPONENT_RADIO_HIDDEN_FILES, f, ui_cfg_chunks[2]);
self.view
.render(super::COMPONENT_RADIO_UPDATES, f, ui_cfg_chunks[3]);
self.view
.render(super::COMPONENT_RADIO_GROUP_DIRS, f, ui_cfg_chunks[4]);
self.view
.render(super::COMPONENT_INPUT_LOCAL_FILE_FMT, f, ui_cfg_chunks[5]);
self.view
.render(super::COMPONENT_INPUT_REMOTE_FILE_FMT, f, ui_cfg_chunks[6]);
// Popups
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_ERROR) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_ERROR, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_QUIT) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 40, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_QUIT, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 70);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_TEXT_HELP, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_SAVE) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_SAVE, f, popup);
}
}
});
// Put context back to context
self.context = Some(ctx);
}
/// ### load_input_values
///
/// Load values from configuration into input fields
pub(crate) fn load_input_values(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
// Text editor
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_TEXT_EDITOR) {
let text_editor: String =
String::from(cli.get_text_editor().as_path().to_string_lossy());
let props = InputPropsBuilder::from(props)
.with_value(text_editor)
.build();
let _ = self.view.update(super::COMPONENT_INPUT_TEXT_EDITOR, props);
}
// Protocol
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DEFAULT_PROTOCOL) {
let protocol: usize = match cli.get_default_protocol() {
FileTransferProtocol::Sftp => 0,
FileTransferProtocol::Scp => 1,
FileTransferProtocol::Ftp(false) => 2,
FileTransferProtocol::Ftp(true) => 3,
};
let props = RadioPropsBuilder::from(props).with_value(protocol).build();
let _ = self
.view
.update(super::COMPONENT_RADIO_DEFAULT_PROTOCOL, props);
}
// Hidden files
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_HIDDEN_FILES) {
let hidden: usize = match cli.get_show_hidden_files() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(hidden).build();
let _ = self.view.update(super::COMPONENT_RADIO_HIDDEN_FILES, props);
}
// Updates
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_UPDATES) {
let updates: usize = match cli.get_check_for_updates() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(updates).build();
let _ = self.view.update(super::COMPONENT_RADIO_UPDATES, props);
}
// Group dirs
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_GROUP_DIRS) {
let dirs: usize = match cli.get_group_dirs() {
Some(GroupDirs::First) => 0,
Some(GroupDirs::Last) => 1,
None => 2,
};
let props = RadioPropsBuilder::from(props).with_value(dirs).build();
let _ = self.view.update(super::COMPONENT_RADIO_GROUP_DIRS, props);
}
// Local File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_LOCAL_FILE_FMT) {
let file_fmt: String = cli.get_local_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
let _ = self
.view
.update(super::COMPONENT_INPUT_LOCAL_FILE_FMT, props);
}
// Remote File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_REMOTE_FILE_FMT) {
let file_fmt: String = cli.get_remote_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
let _ = self
.view
.update(super::COMPONENT_INPUT_REMOTE_FILE_FMT, props);
}
}
}
/// ### collect_input_values
///
/// Collect values from input and put them into the configuration
pub(crate) fn collect_input_values(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
if let Some(Payload::One(Value::Str(editor))) =
self.view.get_state(super::COMPONENT_INPUT_TEXT_EDITOR)
{
cli.set_text_editor(PathBuf::from(editor.as_str()));
}
if let Some(Payload::One(Value::Usize(protocol))) =
self.view.get_state(super::COMPONENT_RADIO_DEFAULT_PROTOCOL)
{
let protocol: FileTransferProtocol = match protocol {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
};
cli.set_default_protocol(protocol);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_HIDDEN_FILES)
{
let show: bool = matches!(opt, 0);
cli.set_show_hidden_files(show);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_UPDATES)
{
let check: bool = matches!(opt, 0);
cli.set_check_for_updates(check);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_LOCAL_FILE_FMT)
{
cli.set_local_file_fmt(fmt);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_REMOTE_FILE_FMT)
{
cli.set_remote_file_fmt(fmt);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_GROUP_DIRS)
{
let dirs: Option<GroupDirs> = match opt {
0 => Some(GroupDirs::First),
1 => Some(GroupDirs::Last),
_ => None,
};
cli.set_group_dirs(dirs);
}
}
}
}

View File

@@ -0,0 +1,296 @@
//! ## SetupActivity
//!
//! `setup_activity` is the module which implements the Setup activity, which is the activity to
//! work on termscp configuration
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Locals
use super::{Context, SetupActivity};
use crate::ui::components::bookmark_list::{BookmarkList, BookmarkListPropsBuilder};
use crate::utils::ui::draw_area_in;
// Ext
use tuirealm::components::{
input::{Input, InputPropsBuilder},
radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
};
use tuirealm::tui::{
layout::{Constraint, Direction, Layout},
style::Color,
widgets::{BorderType, Borders, Clear},
};
use tuirealm::{
props::{PropsBuilder, TextSpanBuilder},
View,
};
impl SetupActivity {
// -- view
/// ### init_ssh_keys
///
/// Initialize ssh keys view
pub(super) fn init_ssh_keys(&mut self) {
// Init view
self.view = View::init();
// Common stuff
// Radio tab
self.view.mount(
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::LightYellow)
.with_options(
None,
vec![
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
],
)
.with_value(1)
.build(),
)),
);
// Footer
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
self.view.mount(
super::COMPONENT_LIST_SSH_KEYS,
Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default()
.with_bookmarks(Some(String::from("SSH Keys")), vec![])
.with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen)
.with_background(Color::LightGreen)
.with_foreground(Color::Black)
.build(),
)),
);
// Give focus
self.view.active(super::COMPONENT_LIST_SSH_KEYS);
// Load keys
self.reload_ssh_keys();
}
pub(crate) fn view_ssh_keys(&mut self) {
let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
// Prepare main chunks
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Length(3), // Current tab
Constraint::Percentage(90), // Main body
Constraint::Length(3), // Help footer
]
.as_ref(),
)
.split(f.size());
// Render common widget
self.view.render(super::COMPONENT_RADIO_TAB, f, chunks[0]);
self.view.render(super::COMPONENT_TEXT_FOOTER, f, chunks[2]);
self.view
.render(super::COMPONENT_LIST_SSH_KEYS, f, chunks[1]);
// Popups
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_ERROR) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_ERROR, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_QUIT) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 40, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_QUIT, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 70);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_TEXT_HELP, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_SAVE) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_SAVE, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DEL_SSH_KEY) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view
.render(super::COMPONENT_RADIO_DEL_SSH_KEY, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_SSH_HOST) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 20);
f.render_widget(Clear, popup);
let popup_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3), // Host
Constraint::Length(3), // Username
]
.as_ref(),
)
.split(popup);
self.view
.render(super::COMPONENT_INPUT_SSH_HOST, f, popup_chunks[0]);
self.view
.render(super::COMPONENT_INPUT_SSH_USERNAME, f, popup_chunks[1]);
}
}
});
// Put context back to context
self.context = Some(ctx);
}
// -- mount
/// ### mount_del_ssh_key
///
/// Mount delete ssh key component
pub(crate) fn mount_del_ssh_key(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_DEL_SSH_KEY,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightRed)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightRed)
.with_options(
Some(String::from("Delete key?")),
vec![String::from("Yes"), String::from("No")],
)
.with_value(1) // Default: No
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_DEL_SSH_KEY);
}
/// ### umount_del_ssh_key
///
/// Umount delete ssh key
pub(crate) fn umount_del_ssh_key(&mut self) {
self.view.umount(super::COMPONENT_RADIO_DEL_SSH_KEY);
}
/// ### mount_new_ssh_key
///
/// Mount new ssh key prompt
pub(crate) fn mount_new_ssh_key(&mut self) {
self.view.mount(
super::COMPONENT_INPUT_SSH_HOST,
Box::new(Input::new(
InputPropsBuilder::default()
.with_label(String::from("Hostname or address"))
.with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT,
BorderType::Plain,
Color::Reset,
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_SSH_USERNAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_label(String::from("Username"))
.with_borders(
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
BorderType::Plain,
Color::Reset,
)
.build(),
)),
);
self.view.active(super::COMPONENT_INPUT_SSH_HOST);
}
/// ### umount_new_ssh_key
///
/// Umount new ssh key prompt
pub(crate) fn umount_new_ssh_key(&mut self) {
self.view.umount(super::COMPONENT_INPUT_SSH_HOST);
self.view.umount(super::COMPONENT_INPUT_SSH_USERNAME);
}
/// ### reload_ssh_keys
///
/// Reload ssh keys
pub(crate) fn reload_ssh_keys(&mut self) {
if let Some(cli) = self.context.as_ref().unwrap().config_client.as_ref() {
// get props
if let Some(props) = self.view.get_props(super::COMPONENT_LIST_SSH_KEYS) {
// Create texts
let keys: Vec<String> = cli
.iter_ssh_keys()
.map(|x| {
let (addr, username, _) = cli.get_ssh_key(x).ok().unwrap().unwrap();
format!("{} at {}", addr, username)
})
.collect();
let props = BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("SSH Keys")), keys)
.build();
self.view.update(super::COMPONENT_LIST_SSH_KEYS, props);
}
}
}
}

View File

@@ -0,0 +1,656 @@
//! ## SetupActivity
//!
//! `setup_activity` is the module which implements the Setup activity, which is the activity to
//! work on termscp configuration
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Locals
use super::{Context, SetupActivity};
use crate::config::themes::Theme;
use crate::ui::components::color_picker::{ColorPicker, ColorPickerPropsBuilder};
use crate::utils::parser::parse_color;
use crate::utils::ui::draw_area_in;
// Ext
use tuirealm::components::{
label::{Label, LabelPropsBuilder},
radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
};
use tuirealm::tui::{
layout::{Constraint, Direction, Layout},
style::Color,
widgets::{BorderType, Borders, Clear},
};
use tuirealm::{
props::{PropsBuilder, TextSpanBuilder},
Payload, Value, View,
};
impl SetupActivity {
// -- view
/// ### init_theme
///
/// Initialize thene view
pub(super) fn init_theme(&mut self) {
// Init view
self.view = View::init();
// Common stuff
// Radio tab
self.view.mount(
super::COMPONENT_RADIO_TAB,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::BOTTOM, BorderType::Thick, Color::White)
.with_options(
None,
vec![
String::from("User Interface"),
String::from("SSH Keys"),
String::from("Theme"),
],
)
.with_value(2)
.build(),
)),
);
// Footer
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings").bold().build(),
])
.build(),
)),
);
// auth colors
self.mount_title(super::COMPONENT_COLOR_AUTH_TITLE, "Authentication styles");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_PROTOCOL, "Protocol");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_ADDR, "Ip address");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_PORT, "Port");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_USERNAME, "Username");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_PASSWORD, "Password");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_BOOKMARKS, "Bookmarks");
self.mount_color_picker(super::COMPONENT_COLOR_AUTH_RECENTS, "Recent connections");
// Misc
self.mount_title(super::COMPONENT_COLOR_MISC_TITLE, "Misc styles");
self.mount_color_picker(super::COMPONENT_COLOR_MISC_ERROR, "Error");
self.mount_color_picker(super::COMPONENT_COLOR_MISC_INPUT, "Input fields");
self.mount_color_picker(super::COMPONENT_COLOR_MISC_KEYS, "Key strokes");
self.mount_color_picker(super::COMPONENT_COLOR_MISC_QUIT, "Quit dialogs");
self.mount_color_picker(super::COMPONENT_COLOR_MISC_SAVE, "Save confirmations");
self.mount_color_picker(super::COMPONENT_COLOR_MISC_WARN, "Warnings");
// Transfer (1)
self.mount_title(super::COMPONENT_COLOR_TRANSFER_TITLE, "Transfer styles");
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG,
"Local explorer background",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG,
"Local explorer foreground",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG,
"Local explorer highlighted",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG,
"Remote explorer background",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG,
"Remote explorer foreground",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG,
"Remote explorer highlighted",
);
self.mount_color_picker(super::COMPONENT_COLOR_TRANSFER_PROG_BAR, "Progress bar");
// Transfer (2)
self.mount_title(
super::COMPONENT_COLOR_TRANSFER_TITLE_2,
"Transfer styles (2)",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_LOG_BG,
"Log window background",
);
self.mount_color_picker(super::COMPONENT_COLOR_TRANSFER_LOG_WIN, "Log window");
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_STATUS_SORTING,
"File sorting",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN,
"Hidden files",
);
self.mount_color_picker(
super::COMPONENT_COLOR_TRANSFER_STATUS_SYNC,
"Synchronized browsing",
);
// Load styles
self.load_styles();
// Active first field
self.view.active(super::COMPONENT_COLOR_AUTH_PROTOCOL);
}
pub(super) fn view_theme(&mut self) {
let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
// Prepare main chunks
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Length(3), // Current tab
Constraint::Length(22), // Main body
Constraint::Length(3), // Help footer
]
.as_ref(),
)
.split(f.size());
// Render common widget
self.view.render(super::COMPONENT_RADIO_TAB, f, chunks[0]);
self.view.render(super::COMPONENT_TEXT_FOOTER, f, chunks[2]);
// Make chunks
let colors_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
]
.as_ref(),
)
.split(chunks[1]);
let auth_colors_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(1), // Title
Constraint::Length(3), // Protocol
Constraint::Length(3), // Addr
Constraint::Length(3), // Port
Constraint::Length(3), // Username
Constraint::Length(3), // Password
Constraint::Length(3), // Bookmarks
Constraint::Length(3), // Recents
]
.as_ref(),
)
.split(colors_layout[0]);
self.view
.render(super::COMPONENT_COLOR_AUTH_TITLE, f, auth_colors_layout[0]);
self.view.render(
super::COMPONENT_COLOR_AUTH_PROTOCOL,
f,
auth_colors_layout[1],
);
self.view
.render(super::COMPONENT_COLOR_AUTH_ADDR, f, auth_colors_layout[2]);
self.view
.render(super::COMPONENT_COLOR_AUTH_PORT, f, auth_colors_layout[3]);
self.view.render(
super::COMPONENT_COLOR_AUTH_USERNAME,
f,
auth_colors_layout[4],
);
self.view.render(
super::COMPONENT_COLOR_AUTH_PASSWORD,
f,
auth_colors_layout[5],
);
self.view.render(
super::COMPONENT_COLOR_AUTH_BOOKMARKS,
f,
auth_colors_layout[6],
);
self.view.render(
super::COMPONENT_COLOR_AUTH_RECENTS,
f,
auth_colors_layout[7],
);
let misc_colors_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(1), // Title
Constraint::Length(3), // Error
Constraint::Length(3), // Input
Constraint::Length(3), // Keys
Constraint::Length(3), // Quit
Constraint::Length(3), // Save
Constraint::Length(3), // Warn
Constraint::Length(3), // Empty
]
.as_ref(),
)
.split(colors_layout[1]);
self.view
.render(super::COMPONENT_COLOR_MISC_TITLE, f, misc_colors_layout[0]);
self.view
.render(super::COMPONENT_COLOR_MISC_ERROR, f, misc_colors_layout[1]);
self.view
.render(super::COMPONENT_COLOR_MISC_INPUT, f, misc_colors_layout[2]);
self.view
.render(super::COMPONENT_COLOR_MISC_KEYS, f, misc_colors_layout[3]);
self.view
.render(super::COMPONENT_COLOR_MISC_QUIT, f, misc_colors_layout[4]);
self.view
.render(super::COMPONENT_COLOR_MISC_SAVE, f, misc_colors_layout[5]);
self.view
.render(super::COMPONENT_COLOR_MISC_WARN, f, misc_colors_layout[6]);
let transfer_colors_layout_col1 = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(1), // Title
Constraint::Length(3), // local explorer bg
Constraint::Length(3), // local explorer fg
Constraint::Length(3), // local explorer hg
Constraint::Length(3), // remote explorer bg
Constraint::Length(3), // remote explorer fg
Constraint::Length(3), // remote explorer hg
Constraint::Length(3), // prog bar
]
.as_ref(),
)
.split(colors_layout[2]);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_TITLE,
f,
transfer_colors_layout_col1[0],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG,
f,
transfer_colors_layout_col1[1],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG,
f,
transfer_colors_layout_col1[2],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG,
f,
transfer_colors_layout_col1[3],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG,
f,
transfer_colors_layout_col1[4],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG,
f,
transfer_colors_layout_col1[5],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG,
f,
transfer_colors_layout_col1[6],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_PROG_BAR,
f,
transfer_colors_layout_col1[7],
);
let transfer_colors_layout_col2 = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(1), // Title
Constraint::Length(3), // log bg
Constraint::Length(3), // log window
Constraint::Length(3), // status sorting
Constraint::Length(3), // status hidden
Constraint::Length(3), // sync browsing
Constraint::Length(3), // Empty
Constraint::Length(3), // Empty
]
.as_ref(),
)
.split(colors_layout[3]);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_TITLE_2,
f,
transfer_colors_layout_col2[0],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_LOG_BG,
f,
transfer_colors_layout_col2[1],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_LOG_WIN,
f,
transfer_colors_layout_col2[2],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_STATUS_SORTING,
f,
transfer_colors_layout_col2[3],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN,
f,
transfer_colors_layout_col2[4],
);
self.view.render(
super::COMPONENT_COLOR_TRANSFER_STATUS_SYNC,
f,
transfer_colors_layout_col2[5],
);
// Popups
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_ERROR) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_ERROR, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_QUIT) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 40, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_QUIT, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 70);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_TEXT_HELP, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_SAVE) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_SAVE, f, popup);
}
}
});
// Put context back to context
self.context = Some(ctx);
}
/// ### load_styles
///
/// Load values from theme into input fields
pub(crate) fn load_styles(&mut self) {
let theme: Theme = self.theme().clone();
self.update_color(super::COMPONENT_COLOR_AUTH_ADDR, theme.auth_address);
self.update_color(super::COMPONENT_COLOR_AUTH_BOOKMARKS, theme.auth_bookmarks);
self.update_color(super::COMPONENT_COLOR_AUTH_PASSWORD, theme.auth_password);
self.update_color(super::COMPONENT_COLOR_AUTH_PORT, theme.auth_port);
self.update_color(super::COMPONENT_COLOR_AUTH_PROTOCOL, theme.auth_protocol);
self.update_color(super::COMPONENT_COLOR_AUTH_RECENTS, theme.auth_recents);
self.update_color(super::COMPONENT_COLOR_AUTH_USERNAME, theme.auth_username);
self.update_color(super::COMPONENT_COLOR_MISC_ERROR, theme.misc_error_dialog);
self.update_color(super::COMPONENT_COLOR_MISC_INPUT, theme.misc_input_dialog);
self.update_color(super::COMPONENT_COLOR_MISC_KEYS, theme.misc_keys);
self.update_color(super::COMPONENT_COLOR_MISC_QUIT, theme.misc_quit_dialog);
self.update_color(super::COMPONENT_COLOR_MISC_SAVE, theme.misc_save_dialog);
self.update_color(super::COMPONENT_COLOR_MISC_WARN, theme.misc_warn_dialog);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG,
theme.transfer_local_explorer_background,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG,
theme.transfer_local_explorer_foreground,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG,
theme.transfer_local_explorer_highlighted,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG,
theme.transfer_remote_explorer_background,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG,
theme.transfer_remote_explorer_foreground,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG,
theme.transfer_remote_explorer_highlighted,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_PROG_BAR,
theme.transfer_progress_bar,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_LOG_BG,
theme.transfer_log_background,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_LOG_WIN,
theme.transfer_log_window,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_STATUS_SORTING,
theme.transfer_status_sorting,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN,
theme.transfer_status_hidden,
);
self.update_color(
super::COMPONENT_COLOR_TRANSFER_STATUS_SYNC,
theme.transfer_status_sync_browsing,
);
}
/// ### collect_styles
///
/// Collect values from input and put them into the theme.
/// If a component has an invalid color, returns Err(component_id)
pub(crate) fn collect_styles(&mut self) -> Result<(), &'static str> {
// auth
let auth_address: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_ADDR)
.map_err(|_| super::COMPONENT_COLOR_AUTH_ADDR)?;
let auth_bookmarks: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_BOOKMARKS)
.map_err(|_| super::COMPONENT_COLOR_AUTH_BOOKMARKS)?;
let auth_password: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_PASSWORD)
.map_err(|_| super::COMPONENT_COLOR_AUTH_PASSWORD)?;
let auth_port: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_PORT)
.map_err(|_| super::COMPONENT_COLOR_AUTH_PORT)?;
let auth_protocol: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_PROTOCOL)
.map_err(|_| super::COMPONENT_COLOR_AUTH_PROTOCOL)?;
let auth_recents: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_RECENTS)
.map_err(|_| super::COMPONENT_COLOR_AUTH_RECENTS)?;
let auth_username: Color = self
.get_color(super::COMPONENT_COLOR_AUTH_USERNAME)
.map_err(|_| super::COMPONENT_COLOR_AUTH_USERNAME)?;
// misc
let misc_error_dialog: Color = self
.get_color(super::COMPONENT_COLOR_MISC_ERROR)
.map_err(|_| super::COMPONENT_COLOR_MISC_ERROR)?;
let misc_input_dialog: Color = self
.get_color(super::COMPONENT_COLOR_MISC_INPUT)
.map_err(|_| super::COMPONENT_COLOR_MISC_INPUT)?;
let misc_keys: Color = self
.get_color(super::COMPONENT_COLOR_MISC_KEYS)
.map_err(|_| super::COMPONENT_COLOR_MISC_KEYS)?;
let misc_quit_dialog: Color = self
.get_color(super::COMPONENT_COLOR_MISC_QUIT)
.map_err(|_| super::COMPONENT_COLOR_MISC_QUIT)?;
let misc_save_dialog: Color = self
.get_color(super::COMPONENT_COLOR_MISC_SAVE)
.map_err(|_| super::COMPONENT_COLOR_MISC_SAVE)?;
let misc_warn_dialog: Color = self
.get_color(super::COMPONENT_COLOR_MISC_WARN)
.map_err(|_| super::COMPONENT_COLOR_MISC_WARN)?;
// transfer
let transfer_local_explorer_background: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_BG)?;
let transfer_local_explorer_foreground: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_FG)?;
let transfer_local_explorer_highlighted: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_EXPLORER_LOCAL_HG)?;
let transfer_remote_explorer_background: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_BG)?;
let transfer_remote_explorer_foreground: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_FG)?;
let transfer_remote_explorer_highlighted: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_EXPLORER_REMOTE_HG)?;
let transfer_log_background: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_LOG_BG)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_LOG_BG)?;
let transfer_log_window: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_LOG_WIN)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_LOG_WIN)?;
let transfer_progress_bar: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_PROG_BAR)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_PROG_BAR)?;
let transfer_status_hidden: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_STATUS_HIDDEN)?;
let transfer_status_sorting: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_STATUS_SORTING)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_STATUS_SORTING)?;
let transfer_status_sync_browsing: Color = self
.get_color(super::COMPONENT_COLOR_TRANSFER_STATUS_SYNC)
.map_err(|_| super::COMPONENT_COLOR_TRANSFER_STATUS_SYNC)?;
// Update theme
let mut theme: &mut Theme = self.theme_mut();
theme.auth_address = auth_address;
theme.auth_bookmarks = auth_bookmarks;
theme.auth_password = auth_password;
theme.auth_port = auth_port;
theme.auth_protocol = auth_protocol;
theme.auth_recents = auth_recents;
theme.auth_username = auth_username;
theme.misc_error_dialog = misc_error_dialog;
theme.misc_input_dialog = misc_input_dialog;
theme.misc_keys = misc_keys;
theme.misc_quit_dialog = misc_quit_dialog;
theme.misc_save_dialog = misc_save_dialog;
theme.misc_warn_dialog = misc_warn_dialog;
theme.transfer_local_explorer_background = transfer_local_explorer_background;
theme.transfer_local_explorer_foreground = transfer_local_explorer_foreground;
theme.transfer_local_explorer_highlighted = transfer_local_explorer_highlighted;
theme.transfer_remote_explorer_background = transfer_remote_explorer_background;
theme.transfer_remote_explorer_foreground = transfer_remote_explorer_foreground;
theme.transfer_remote_explorer_highlighted = transfer_remote_explorer_highlighted;
theme.transfer_log_background = transfer_log_background;
theme.transfer_log_window = transfer_log_window;
theme.transfer_progress_bar = transfer_progress_bar;
theme.transfer_status_hidden = transfer_status_hidden;
theme.transfer_status_sorting = transfer_status_sorting;
theme.transfer_status_sync_browsing = transfer_status_sync_browsing;
Ok(())
}
/// ### update_color
///
/// Update color for provided component
fn update_color(&mut self, component: &str, color: Color) {
if let Some(props) = self.view.get_props(component) {
self.view.update(
component,
ColorPickerPropsBuilder::from(props)
.with_color(&color)
.build(),
);
}
}
/// ### get_color
///
/// Get color from component
fn get_color(&self, component: &str) -> Result<Color, ()> {
match self.view.get_state(component) {
Some(Payload::One(Value::Str(color))) => match parse_color(color.as_str()) {
Some(c) => Ok(c),
None => Err(()),
},
_ => Err(()),
}
}
/// ### mount_color_picker
///
/// Mount color picker with provided data
fn mount_color_picker(&mut self, id: &str, label: &str) {
self.view.mount(
id,
Box::new(ColorPicker::new(
ColorPickerPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::Reset)
.with_label(label.to_string())
.build(),
)),
);
}
/// ### mount_title
///
/// Mount title
fn mount_title(&mut self, id: &str, text: &str) {
self.view.mount(
id,
Box::new(Label::new(
LabelPropsBuilder::default()
.bold()
.with_text(text.to_string())
.build(),
)),
);
}
}