diff --git a/src/ui/app.rs b/src/ui/app.rs index cc518304..ef4b9966 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -3640,7 +3640,7 @@ impl Tty7App { fn toggle_settings(&mut self, window: &mut Window, cx: &mut Context) { if self.settings.is_some() { - self.close_settings(window, cx); + self.close_settings_checked(window, cx); return; } self.remember_active_pane(window, cx); diff --git a/src/ui/i18n/en.rs b/src/ui/i18n/en.rs index f01b99bf..03b06c29 100644 --- a/src/ui/i18n/en.rs +++ b/src/ui/i18n/en.rs @@ -195,6 +195,11 @@ pub fn translate_en(key: L10nKey) -> &'static str { "Ask for confirmation before closing a tab or pane with a live SSH session." } L10nKey::SettingsNewHost => "New host", + L10nKey::SettingsDiscardChangesTitle => "Discard unsaved changes?", + L10nKey::SettingsDiscardChangesBody => { + "The connection you are editing has changes that were never saved." + } + L10nKey::SettingsKeepEditing => "Keep Editing", L10nKey::SettingsName => "Name", L10nKey::SettingsNameDesc => "A label for this connection.", L10nKey::SettingsHost => "Host", diff --git a/src/ui/i18n/ja.rs b/src/ui/i18n/ja.rs index 5bd85923..0d017407 100644 --- a/src/ui/i18n/ja.rs +++ b/src/ui/i18n/ja.rs @@ -195,6 +195,9 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> { "アクティブな SSH セッションのあるタブやペインを閉じる前に確認を求めます" } L10nKey::SettingsNewHost => "新規ホスト", + L10nKey::SettingsDiscardChangesTitle => "保存していない変更を破棄しますか?", + L10nKey::SettingsDiscardChangesBody => "編集中の接続に、まだ保存していない変更があります。", + L10nKey::SettingsKeepEditing => "編集を続ける", L10nKey::SettingsName => "名前", L10nKey::SettingsNameDesc => "この接続の表示名", L10nKey::SettingsHost => "ホスト名", diff --git a/src/ui/i18n/mod.rs b/src/ui/i18n/mod.rs index a6d32aab..26ddf4f0 100644 --- a/src/ui/i18n/mod.rs +++ b/src/ui/i18n/mod.rs @@ -204,6 +204,9 @@ pub enum L10nKey { WarnBeforeClosing, SettingsWarnBeforeClosingDesc, SettingsNewHost, + SettingsDiscardChangesTitle, + SettingsDiscardChangesBody, + SettingsKeepEditing, SettingsName, SettingsNameDesc, SettingsHost, @@ -1269,6 +1272,9 @@ mod tests { L10nKey::WarnBeforeClosing, L10nKey::SettingsWarnBeforeClosingDesc, L10nKey::SettingsNewHost, + L10nKey::SettingsDiscardChangesTitle, + L10nKey::SettingsDiscardChangesBody, + L10nKey::SettingsKeepEditing, L10nKey::SettingsName, L10nKey::SettingsNameDesc, L10nKey::SettingsHost, diff --git a/src/ui/i18n/zh.rs b/src/ui/i18n/zh.rs index 181bbf92..9f1e8f2b 100644 --- a/src/ui/i18n/zh.rs +++ b/src/ui/i18n/zh.rs @@ -173,6 +173,9 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { "在关闭带有活动 SSH 会话的标签页或窗格前请求确认。" } L10nKey::SettingsNewHost => "新主机", + L10nKey::SettingsDiscardChangesTitle => "丢弃未保存的改动?", + L10nKey::SettingsDiscardChangesBody => "你正在编辑的连接有还没保存的改动。", + L10nKey::SettingsKeepEditing => "继续编辑", L10nKey::SettingsName => "名称", L10nKey::SettingsNameDesc => "此连接的标签。", L10nKey::SettingsHost => "主机", diff --git a/src/ui/settings.rs b/src/ui/settings.rs index 57765c69..4505e469 100644 --- a/src/ui/settings.rs +++ b/src/ui/settings.rs @@ -939,7 +939,7 @@ impl Tty7App { .track_focus(&focus_handle) .on_key_down(cx.listener(|this, ev: &KeyDownEvent, window, cx| { if ev.keystroke.key.as_str() == "escape" { - this.close_settings(window, cx); + this.close_settings_checked(window, cx); } })) .child(sidebar) @@ -978,7 +978,7 @@ impl Tty7App { .h(px(TILE_SIZE)) .rounded_lg() .on_click(cx.listener(|this, _, window, cx| { - this.close_settings(window, cx) + this.close_settings_checked(window, cx) })), ), ) @@ -2544,6 +2544,44 @@ impl Tty7App { cx.notify(); } + /// Whether the SSH profile form on screen holds edits that were never + /// saved. Save is enabled off exactly this, so closing on it is the same + /// question the button already answers. + pub(crate) fn ssh_form_dirty(&self, cx: &App) -> bool { + let Some(form) = self.active_settings().and_then(|s| s.ssh_form.as_ref()) else { + return false; + }; + let saved = cx + .global::() + .ssh_profiles + .iter() + .find(|p| p.id == form.editing) + .cloned(); + self.ssh_form_collect(cx) != saved + } + + /// Closing from Escape or the X is the user leaving; every other caller + /// closes as the tail of something they explicitly chose, and has already + /// saved or does not care. + pub(crate) fn close_settings_checked(&mut self, window: &mut Window, cx: &mut Context) { + if !self.ssh_form_dirty(cx) { + self.close_settings(window, cx); + return; + } + let answer = window.prompt( + gpui::PromptLevel::Warning, + t(L10nKey::SettingsDiscardChangesTitle), + Some(t(L10nKey::SettingsDiscardChangesBody)), + &[t(L10nKey::SettingsKeepEditing), t(L10nKey::EditorDiscard)], + cx, + ); + cx.spawn_in(window, async move |this, cx| { + let Ok(1) = answer.await else { return }; + let _ = this.update_in(cx, |this, window, cx| this.close_settings(window, cx)); + }) + .detach(); + } + pub(crate) fn save_and_connect_profile(&mut self, window: &mut Window, cx: &mut Context) { if let Some(id) = self.save_editing_profile(cx) { self.close_settings(window, cx);