diff --git a/CHANGELOG.md b/CHANGELOG.md index de2a054..986003b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ Released on ?? - Install update via GUI from auth form: when the "new version message" is displayed press ``, then enter `YES` in the radio input asking whether to install the update. - **❗ BREAKING CHANGES ❗**: - Added a new key in themes: `misc_info_dialog`: if your theme won't load, just reload it. If you're using a customised theme, you can add to it the missing key via a text editor. Just edit the `theme.toml` in your `$CONFIG_DIR/termscp/theme.toml` and add `misc_info_dialog` (Read more in manual at Themes). +- Enhancements: + - Reuse mounts in UI, in order to reduce executable size - Dependencies: - Added `rust-s3 0.27-rc4` - Added `self_update 0.27.0` diff --git a/src/ui/activities/auth/misc.rs b/src/ui/activities/auth/misc.rs index b1c62df..78094f5 100644 --- a/src/ui/activities/auth/misc.rs +++ b/src/ui/activities/auth/misc.rs @@ -29,8 +29,6 @@ use super::{AuthActivity, FileTransferParams, FileTransferProtocol}; use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams, ProtocolParams}; use crate::system::auto_update::{Update, UpdateStatus}; -use tuirealm::tui::style::Color; - impl AuthActivity { /// ### protocol_opt_to_enum /// @@ -164,7 +162,7 @@ impl AuthActivity { // Umount release notes self.umount_release_notes(); // Mount wait box - self.mount_wait("Installing update. Please wait…", Color::LightYellow); + self.mount_wait("Installing update. Please wait…"); // Refresh UI self.view(); // Install update @@ -173,13 +171,10 @@ impl AuthActivity { self.umount_wait(); // Show outcome match result { - Ok(UpdateStatus::AlreadyUptodate) => { - self.mount_info("termscp is already up to date!", Color::Cyan) + Ok(UpdateStatus::AlreadyUptodate) => self.mount_info("termscp is already up to date!"), + Ok(UpdateStatus::UpdateInstalled(ver)) => { + self.mount_info(format!("termscp has been updated to version {}!", ver)) } - Ok(UpdateStatus::UpdateInstalled(ver)) => self.mount_info( - format!("termscp has been updated to version {}!", ver), - Color::Green, - ), Err(err) => self.mount_error(format!("Could not install update: {}", err)), } } diff --git a/src/ui/activities/auth/view.rs b/src/ui/activities/auth/view.rs index d9bc988..d41f933 100644 --- a/src/ui/activities/auth/view.rs +++ b/src/ui/activities/auth/view.rs @@ -544,22 +544,8 @@ impl AuthActivity { /// /// Mount error box pub(super) fn mount_error>(&mut self, text: S) { - // Mount let err_color = self.theme().misc_error_dialog; - self.view.mount( - super::COMPONENT_TEXT_ERROR, - Box::new(Paragraph::new( - ParagraphPropsBuilder::default() - .with_foreground(err_color) - .with_borders(Borders::ALL, BorderType::Thick, err_color) - .bold() - .with_text_alignment(Alignment::Center) - .with_texts(vec![TextSpan::from(text.as_ref().to_string())]) - .build(), - )), - ); - // Give focus to error - self.view.active(super::COMPONENT_TEXT_ERROR); + self.mount_text_dialog(super::COMPONENT_TEXT_ERROR, text.as_ref(), err_color); } /// ### umount_error @@ -572,22 +558,9 @@ impl AuthActivity { /// ### mount_info /// /// Mount info box - pub(super) fn mount_info>(&mut self, text: S, color: Color) { - // Mount - self.view.mount( - super::COMPONENT_TEXT_INFO, - Box::new(Paragraph::new( - ParagraphPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Thick, color) - .with_foreground(color) - .bold() - .with_text_alignment(Alignment::Center) - .with_texts(vec![TextSpan::from(text.as_ref().to_string())]) - .build(), - )), - ); - // Give focus to error - self.view.active(super::COMPONENT_TEXT_INFO); + pub(super) fn mount_info>(&mut self, text: S) { + let color = self.theme().misc_info_dialog; + self.mount_text_dialog(super::COMPONENT_TEXT_INFO, text.as_ref(), color); } /// ### umount_info @@ -600,22 +573,9 @@ impl AuthActivity { /// ### mount_error /// /// Mount wait box - pub(super) fn mount_wait(&mut self, text: &str, color: Color) { - // Mount - self.view.mount( - super::COMPONENT_TEXT_WAIT, - Box::new(Paragraph::new( - ParagraphPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Thick, color) - .with_foreground(color) - .bold() - .with_text_alignment(Alignment::Center) - .with_texts(vec![TextSpan::from(text)]) - .build(), - )), - ); - // Give focus to error - self.view.active(super::COMPONENT_TEXT_WAIT); + pub(super) fn mount_wait(&mut self, text: &str) { + let wait_color = self.theme().misc_info_dialog; + self.mount_text_dialog(super::COMPONENT_TEXT_WAIT, text, wait_color); } /// ### umount_wait @@ -631,22 +591,11 @@ impl AuthActivity { pub(super) fn mount_size_err(&mut self) { // Mount let err_color = self.theme().misc_error_dialog; - self.view.mount( + self.mount_text_dialog( super::COMPONENT_TEXT_SIZE_ERR, - Box::new(Paragraph::new( - ParagraphPropsBuilder::default() - .with_foreground(err_color) - .with_borders(Borders::ALL, BorderType::Thick, err_color) - .bold() - .with_texts(vec![TextSpan::from( - "termscp requires at least 24 lines of height to run", - )]) - .with_text_alignment(Alignment::Center) - .build(), - )), + "termscp requires at least 24 lines of height to run", + err_color, ); - // Give focus to error - self.view.active(super::COMPONENT_TEXT_SIZE_ERR); } /// ### umount_size_err @@ -662,20 +611,13 @@ impl AuthActivity { pub(super) fn mount_quit(&mut self) { // Protocol let quit_color = self.theme().misc_quit_dialog; - self.view.mount( + self.mount_radio_dialog( super::COMPONENT_RADIO_QUIT, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(quit_color) - .with_borders(Borders::ALL, BorderType::Rounded, quit_color) - .with_inverted_color(Color::Black) - .with_title("Quit termscp?", Alignment::Center) - .with_options(&[String::from("Yes"), String::from("No")]) - .rewind(true) - .build(), - )), + "Quit termscp?", + &["Yes", "No"], + 0, + quit_color, ); - self.view.active(super::COMPONENT_RADIO_QUIT); } /// ### umount_quit @@ -690,23 +632,13 @@ 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( + self.mount_radio_dialog( super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(warn_color) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Rounded, warn_color) - .with_title("Delete bookmark?", Alignment::Center) - .with_options(&[String::from("Yes"), String::from("No")]) - .with_value(1) - .rewind(true) - .build(), - )), + "Delete bookmark?", + &["Yes", "No"], + 1, + warn_color, ); - // Active - self.view - .active(super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK); } /// ### umount_bookmark_del_dialog @@ -722,22 +654,13 @@ 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( + self.mount_radio_dialog( super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(warn_color) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Rounded, warn_color) - .with_title("Delete bookmark?", Alignment::Center) - .with_options(&[String::from("Yes"), String::from("No")]) - .with_value(1) - .rewind(true) - .build(), - )), + "Delete bookmark?", + &["Yes", "No"], + 1, + warn_color, ); - // Active - self.view.active(super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT); } /// ### umount_recent_del_dialog @@ -859,32 +782,25 @@ impl AuthActivity { if let Some(release_notes) = ctx.store().get_string(super::STORE_KEY_RELEASE_NOTES) { // make spans let spans: Vec = release_notes.lines().map(TextSpan::from).collect(); + let info_color = self.theme().misc_info_dialog; self.view.mount( super::COMPONENT_TEXT_NEW_VERSION_NOTES, Box::new(Textarea::new( TextareaPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow) + .with_borders(Borders::ALL, BorderType::Rounded, info_color) .with_title("Release notes", Alignment::Center) .with_texts(spans) .build(), )), ); // Mount install popup - self.view.mount( + self.mount_radio_dialog( super::COMPONENT_RADIO_INSTALL_UPDATE, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(Color::LightYellow) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow) - .with_title("Install new version?", Alignment::Left) - .with_options(&["Yes", "No"]) - .with_value(0) - .rewind(true) - .build(), - )), + "Install new version?", + &["Yes", "No"], + 0, + info_color, ); - self.view.active(super::COMPONENT_RADIO_INSTALL_UPDATE); } } } @@ -1033,4 +949,50 @@ impl AuthActivity { } } } + + // -- mount helpers + + fn mount_text_dialog(&mut self, id: &str, text: &str, color: Color) { + // Mount + self.view.mount( + id, + Box::new(Paragraph::new( + ParagraphPropsBuilder::default() + .with_borders(Borders::ALL, BorderType::Thick, color) + .with_foreground(color) + .bold() + .with_text_alignment(Alignment::Center) + .with_texts(vec![TextSpan::from(text)]) + .build(), + )), + ); + // Give focus to error + self.view.active(id); + } + + fn mount_radio_dialog( + &mut self, + id: &str, + text: &str, + opts: &[&str], + default: usize, + color: Color, + ) { + self.view.mount( + id, + Box::new(Radio::new( + RadioPropsBuilder::default() + .with_color(color) + .with_inverted_color(Color::Black) + .with_borders(Borders::ALL, BorderType::Rounded, color) + .with_title(text, Alignment::Center) + .with_options(opts) + .with_value(default) + .rewind(true) + .build(), + )), + ); + // Active + self.view.active(id); + } } diff --git a/src/ui/activities/filetransfer/view.rs b/src/ui/activities/filetransfer/view.rs index 91542a8..ac97d99 100644 --- a/src/ui/activities/filetransfer/view.rs +++ b/src/ui/activities/filetransfer/view.rs @@ -378,20 +378,7 @@ impl FileTransferActivity { 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(Paragraph::new( - ParagraphPropsBuilder::default() - .with_foreground(error_color) - .with_borders(Borders::ALL, BorderType::Rounded, error_color) - .bold() - .with_text_alignment(Alignment::Center) - .with_texts(vec![TextSpan::from(text)]) - .build(), - )), - ); - // Give focus to error - self.view.active(super::COMPONENT_TEXT_ERROR); + self.mount_text_dialog(super::COMPONENT_TEXT_ERROR, text, error_color); } /// ### umount_error @@ -404,46 +391,21 @@ 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(Paragraph::new( - ParagraphPropsBuilder::default() - .with_foreground(error_color) - .with_borders(Borders::ALL, BorderType::Rounded, error_color) - .bold() - .with_text_alignment(Alignment::Center) - .with_texts(vec![TextSpan::from(text)]) - .build(), - )), - ); - // Give focus to error - self.view.active(super::COMPONENT_TEXT_FATAL); + self.mount_text_dialog(super::COMPONENT_TEXT_FATAL, text, error_color); } pub(super) fn mount_wait(&mut self, text: &str) { - self.mount_wait_ex(text, Color::Reset); + self.mount_wait_ex(text); } pub(super) fn mount_blocking_wait(&mut self, text: &str) { - self.mount_wait_ex(text, Color::Reset); + self.mount_wait_ex(text); self.view(); } - fn mount_wait_ex(&mut self, text: &str, color: Color) { - // Mount - let mut builder: ParagraphPropsBuilder = ParagraphPropsBuilder::default(); - builder - .with_foreground(color) - .with_borders(Borders::ALL, BorderType::Rounded, Color::White) - .bold() - .with_text_alignment(Alignment::Center) - .with_texts(vec![TextSpan::from(text)]); - self.view.mount( - super::COMPONENT_TEXT_WAIT, - Box::new(Paragraph::new(builder.build())), - ); - // Give focus to info - self.view.active(super::COMPONENT_TEXT_WAIT); + fn mount_wait_ex(&mut self, text: &str) { + let color = self.theme().misc_info_dialog; + self.mount_text_dialog(super::COMPONENT_TEXT_WAIT, text, color); } pub(super) fn umount_wait(&mut self) { @@ -456,20 +418,13 @@ impl FileTransferActivity { pub(super) fn mount_quit(&mut self) { // Protocol let quit_color = self.theme().misc_quit_dialog; - self.view.mount( + self.mount_radio_dialog( super::COMPONENT_RADIO_QUIT, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(quit_color) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Rounded, quit_color) - .with_title("Are you sure you want to quit?", Alignment::Center) - .with_options(&[String::from("Yes"), String::from("No")]) - .rewind(true) - .build(), - )), + "Are you sure you want to quit?", + &["Yes", "No"], + 0, + quit_color, ); - self.view.active(super::COMPONENT_RADIO_QUIT); } /// ### umount_quit @@ -485,20 +440,13 @@ impl FileTransferActivity { pub(super) fn mount_disconnect(&mut self) { // Protocol let quit_color = self.theme().misc_quit_dialog; - self.view.mount( + self.mount_radio_dialog( super::COMPONENT_RADIO_DISCONNECT, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(quit_color) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Rounded, quit_color) - .with_title("Are you sure you want to disconnect?", Alignment::Center) - .with_options(&[String::from("Yes"), String::from("No")]) - .rewind(true) - .build(), - )), + "Are you sure you want to disconnect?", + &["Yes", "No"], + 0, + quit_color, ); - self.view.active(super::COMPONENT_RADIO_DISCONNECT); } /// ### umount_disconnect @@ -510,17 +458,12 @@ impl FileTransferActivity { pub(super) fn mount_copy(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_COPY, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Copy file(s) to…", Alignment::Center) - .build(), - )), + "Copy file(s) to…", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_COPY); } pub(super) fn umount_copy(&mut self) { @@ -529,17 +472,12 @@ impl FileTransferActivity { pub(super) fn mount_exec(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_EXEC, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Execute command", Alignment::Center) - .build(), - )), + "Execute command", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_EXEC); } pub(super) fn umount_exec(&mut self) { @@ -586,18 +524,12 @@ impl FileTransferActivity { pub(super) fn mount_find_input(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_FIND, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Search files by name", Alignment::Center) - .build(), - )), + "Search files by name", + "", + input_color, ); - // Give focus to input find - self.view.active(super::COMPONENT_INPUT_FIND); } pub(super) fn umount_find_input(&mut self) { @@ -607,17 +539,12 @@ impl FileTransferActivity { pub(super) fn mount_goto(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_GOTO, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Change working directory", Alignment::Center) - .build(), - )), + "Change working directory", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_GOTO); } pub(super) fn umount_goto(&mut self) { @@ -626,17 +553,12 @@ impl FileTransferActivity { pub(super) fn mount_mkdir(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_MKDIR, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Insert directory name", Alignment::Center) - .build(), - )), + "Insert directory name", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_MKDIR); } pub(super) fn umount_mkdir(&mut self) { @@ -645,17 +567,12 @@ impl FileTransferActivity { pub(super) fn mount_newfile(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_NEWFILE, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("New file name", Alignment::Center) - .build(), - )), + "New file name", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_NEWFILE); } pub(super) fn umount_newfile(&mut self) { @@ -664,17 +581,12 @@ impl FileTransferActivity { pub(super) fn mount_openwith(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_OPEN_WITH, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Open file with…", Alignment::Center) - .build(), - )), + "Open file with…", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_OPEN_WITH); } pub(super) fn umount_openwith(&mut self) { @@ -683,17 +595,12 @@ impl FileTransferActivity { pub(super) fn mount_rename(&mut self) { let input_color = self.theme().misc_input_dialog; - self.view.mount( + self.mount_input_dialog( super::COMPONENT_INPUT_RENAME, - Box::new(Input::new( - InputPropsBuilder::default() - .with_borders(Borders::ALL, BorderType::Rounded, input_color) - .with_foreground(input_color) - .with_label("Move file(s) to…", Alignment::Center) - .build(), - )), + "Move file(s) to…", + "", + input_color, ); - self.view.active(super::COMPONENT_INPUT_RENAME); } pub(super) fn umount_rename(&mut self) { @@ -702,17 +609,7 @@ 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, input_color) - .with_foreground(input_color) - .with_label("Save as…", Alignment::Center) - .build(), - )), - ); - self.view.active(super::COMPONENT_INPUT_SAVEAS); + self.mount_input_dialog(super::COMPONENT_INPUT_SAVEAS, "Save as…", "", input_color); } pub(super) fn umount_saveas(&mut self) { @@ -773,25 +670,13 @@ impl FileTransferActivity { FileSorting::Name => 0, FileSorting::Size => 3, }; - self.view.mount( + self.mount_radio_dialog( super::COMPONENT_RADIO_SORTING, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(sorting_color) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Rounded, sorting_color) - .with_title("Sort files by", Alignment::Center) - .with_options(&[ - String::from("Name"), - String::from("Modify time"), - String::from("Creation time"), - String::from("Size"), - ]) - .with_value(index) - .build(), - )), + "Sort files by", + &["Name", "Modify time", "Creation time", "Size"], + index, + sorting_color, ); - self.view.active(super::COMPONENT_RADIO_SORTING); } pub(super) fn umount_file_sorting(&mut self) { @@ -800,21 +685,13 @@ impl FileTransferActivity { pub(super) fn mount_radio_delete(&mut self) { let warn_color = self.theme().misc_warn_dialog; - self.view.mount( + self.mount_radio_dialog( super::COMPONENT_RADIO_DELETE, - Box::new(Radio::new( - RadioPropsBuilder::default() - .with_color(warn_color) - .with_inverted_color(Color::Black) - .with_borders(Borders::ALL, BorderType::Plain, warn_color) - .with_title("Delete file", Alignment::Center) - .with_options(&[String::from("Yes"), String::from("No")]) - .with_value(1) - .rewind(true) - .build(), - )), + "Delete file", + &["Yes", "No"], + 1, + warn_color, ); - self.view.active(super::COMPONENT_RADIO_DELETE); } pub(super) fn umount_radio_delete(&mut self) { @@ -1109,4 +986,65 @@ impl FileTransferActivity { false => "Hide", } } + + // -- Mount helpers + + fn mount_text_dialog(&mut self, id: &str, text: &str, color: Color) { + // Mount + self.view.mount( + id, + Box::new(Paragraph::new( + ParagraphPropsBuilder::default() + .with_borders(Borders::ALL, BorderType::Thick, color) + .with_foreground(color) + .bold() + .with_text_alignment(Alignment::Center) + .with_texts(vec![TextSpan::from(text)]) + .build(), + )), + ); + // Give focus to error + self.view.active(id); + } + + fn mount_input_dialog(&mut self, id: &str, text: &str, val: &str, color: Color) { + self.view.mount( + id, + Box::new(Input::new( + InputPropsBuilder::default() + .with_foreground(color) + .with_label(text, Alignment::Center) + .with_borders(Borders::ALL, BorderType::Rounded, color) + .with_value(val.to_string()) + .build(), + )), + ); + self.view.active(id); + } + + fn mount_radio_dialog( + &mut self, + id: &str, + text: &str, + opts: &[&str], + default: usize, + color: Color, + ) { + self.view.mount( + id, + Box::new(Radio::new( + RadioPropsBuilder::default() + .with_color(color) + .with_inverted_color(Color::Black) + .with_borders(Borders::ALL, BorderType::Rounded, color) + .with_title(text, Alignment::Center) + .with_options(opts) + .with_value(default) + .rewind(true) + .build(), + )), + ); + // Active + self.view.active(id); + } }