diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 48ed69822a2..59cbd21d43c 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -801,7 +801,7 @@ namespace winrt::TerminalApp::implementation _RemoveTabs(tabsToRemove); - actionArgs.Handled(true); + actionArgs.Handled(!tabsToRemove.empty()); } } @@ -837,7 +837,7 @@ namespace winrt::TerminalApp::implementation // tab row, until you mouse over them. Probably has something to do // with tabs not resizing down until there's a mouse exit event. - actionArgs.Handled(true); + actionArgs.Handled(!tabsToRemove.empty()); } } diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 81ca7d51f83..9b165e1c481 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -496,24 +496,48 @@ Third-Party notices A hyperlink name for the Terminal's third-party notices - + Cancel - - Close all - - + Do you want to close all windows? - - Cancel - - + Close all - + Do you want to close all tabs? + + Close all + + + Do you want to close this tab? + + + Close tab + + + Do you want to close this pane? + + + Close pane + + + Do you want to close these tabs? + + + Close tabs + + + Do you want to close these panes? + + + Close panes + + + Don't ask me again + Cancel diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 21e31fb6dd3..418a617cee2 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -391,10 +391,12 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Removes the tab (both TerminalControl and XAML) after prompting for approval + // - Removes the tab (both TerminalControl and XAML) after prompting for approval. // Arguments: - // - tab: the tab to remove - winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::Tab tab) + // - tab: the tab to remove. + // - skipConfirmClose: if true, skip the confirmCloseOn check. Used when + // an aggregate confirmation has already been shown (e.g. close other tabs). + winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::Tab tab, bool skipConfirmClose) { winrt::com_ptr strong; @@ -413,6 +415,24 @@ namespace winrt::TerminalApp::implementation } } + // Skip the per-tab confirmCloseOn check when the caller has already + // shown an aggregate confirmation dialog (e.g. _RemoveTabs). + if (!skipConfirmClose) + { + const auto tabImpl = _GetTabImpl(tab); + if (tabImpl && _ShouldWarnOnCloseTab(tabImpl)) + { + const auto weak = get_weak(); + + auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::Tab); + strong = weak.get(); + if (!strong || warningResult != ContentDialogResult::Primary) + { + co_return; + } + } + } + auto t = winrt::get_self(tab); auto actions = t->BuildStartupActions(BuildStartupKind::None); _AddPreviouslyClosedPaneOrTab(std::move(actions)); @@ -782,6 +802,22 @@ namespace winrt::TerminalApp::implementation if (const auto pane{ activeTab->GetActivePane() }) { const auto weak = get_weak(); + + // Check if we should warn before closing a single pane + // (only triggers on Always — Automatic doesn't warn for single pane) + const auto setting = _settings.GlobalSettings().ConfirmCloseOn(); + if (setting == ConfirmCloseOn::Always) + { + // If this is the last pane, closing it closes the tab, + // so use the tab dialog text instead. + const auto kind = activeTab->GetLeafPaneCount() == 1 ? ConfirmCloseDialogKind::Tab : ConfirmCloseDialogKind::Pane; + auto warningResult = co_await _ShowConfirmCloseDialog(kind); + if (!weak.get() || warningResult != ContentDialogResult::Primary) + { + co_return; + } + } + if (co_await _PaneConfirmCloseReadOnly(pane)) { if (const auto strong = weak.get()) @@ -794,15 +830,38 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Close all panes with the given IDs sequentially. + // - Closes provided panes one by one, showing a single aggregate + // confirmation dialog upfront if the confirmCloseOn setting warrants it. // Arguments: - // - weakTab: weak reference to the tab that the pane belongs to. + // - weakTab: weak reference to the tab that the panes belong to. // - paneIds: collection of the IDs of the panes that are marked for removal. - void TerminalPage::_ClosePanes(weak_ref weakTab, std::vector paneIds) + safe_void_coroutine TerminalPage::_ClosePanes(weak_ref weakTab, std::vector paneIds) + { + // Show a single aggregate confirmation for closing multiple panes. + if (_settings.GlobalSettings().ConfirmCloseOn() != ConfirmCloseOn::Never) + { + const auto weak = get_weak(); + auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::MultiplePanes); + if (!weak.get() || warningResult != ContentDialogResult::Primary) + { + co_return; + } + } + + _CloseRemainingPanes(weakTab, std::move(paneIds)); + } + + // Method Description: + // - Recursively closes panes by ID, chaining each close via the + // ClosedByParent callback. Called after confirmation has already + // been handled by _ClosePanes. + // Arguments: + // - weakTab: weak reference to the tab that the panes belong to. + // - paneIds: remaining pane IDs to close. + void TerminalPage::_CloseRemainingPanes(weak_ref weakTab, std::vector paneIds) { if (auto strongTab{ weakTab.get() }) { - // Close all unfocused panes one by one while (!paneIds.empty()) { const auto id = paneIds.back(); @@ -813,11 +872,10 @@ namespace winrt::TerminalApp::implementation pane->ClosedByParent([ids{ std::move(paneIds) }, weakThis{ get_weak() }, weakTab]() { if (auto strongThis{ weakThis.get() }) { - strongThis->_ClosePanes(weakTab, std::move(ids)); + strongThis->_CloseRemainingPanes(weakTab, std::move(ids)); } }); - // Close the pane which will eventually trigger the closed by parent event _HandleClosePaneRequested(pane); break; } @@ -840,19 +898,35 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Closes provided tabs one by one + // - Closes provided tabs one by one, showing a single aggregate + // confirmation dialog upfront if the confirmCloseOn setting warrants it. // Arguments: // - tabs - tabs to remove safe_void_coroutine TerminalPage::_RemoveTabs(const std::vector tabs) { + if (tabs.empty()) + { + co_return; + } + const auto weak = get_weak(); + // Show a single aggregate confirmation instead of per-tab dialogs. + if (_settings.GlobalSettings().ConfirmCloseOn() != ConfirmCloseOn::Never) + { + auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::MultipleTabs); + if (!weak.get() || warningResult != ContentDialogResult::Primary) + { + co_return; + } + } + for (auto& tab : tabs) { winrt::Windows::Foundation::IAsyncAction action{ nullptr }; if (const auto strong = weak.get()) { - action = _HandleCloseTabRequested(tab); + action = _HandleCloseTabRequested(tab, true); } if (!action) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e98de6b9957..fa7e6ef8937 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -884,26 +884,63 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Displays a dialog to warn the user that they are about to close all open windows. - // Once the user clicks the OK button, shut down the application. - // If cancel is clicked, the dialog will close. + // - Displays the unified close confirmation dialog configured for the + // given scenario. Resets the "don't ask me again" checkbox before showing. + // If the user confirms and checked "don't ask me again", sets + // confirmCloseOn to Never and writes settings to disk. // - Only one dialog can be visible at a time. If another dialog is visible // when this is called, nothing happens. See _ShowDialog for details - winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowQuitDialog() + winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowConfirmCloseDialog(ConfirmCloseDialogKind kind) { - return _ShowDialogHelper(L"QuitDialog"); - } + // Load the dialog (triggers x:Load) and configure its strings. + const auto dialog = FindName(L"ConfirmCloseDialog").as(); + switch (kind) + { + case ConfirmCloseDialogKind::CloseAll: + dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_CloseAllTitle"))); + dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_CloseAllPrimary")); + break; + case ConfirmCloseDialogKind::Window: + dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_WindowTitle"))); + dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_WindowPrimary")); + break; + case ConfirmCloseDialogKind::Tab: + dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_TabTitle"))); + dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_TabPrimary")); + break; + case ConfirmCloseDialogKind::MultiplePanes: + dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_MultiplePanesTitle"))); + dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_MultiplePanesPrimary")); + break; + case ConfirmCloseDialogKind::MultipleTabs: + dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_MultipleTabsTitle"))); + dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_MultipleTabsPrimary")); + break; + case ConfirmCloseDialogKind::Pane: + dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_PaneTitle"))); + dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_PanePrimary")); + break; + } + dialog.CloseButtonText(RS_(L"ConfirmCloseDialog_Cancel")); - // Method Description: - // - Displays a dialog for warnings found while closing the terminal app using - // key binding with multiple tabs opened. Display messages to warn user - // that more than 1 tab is opened, and once the user clicks the OK button, remove - // all the tabs and shut down and app. If cancel is clicked, the dialog will close - // - Only one dialog can be visible at a time. If another dialog is visible - // when this is called, nothing happens. See _ShowDialog for details - winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowCloseWarningDialog() - { - return _ShowDialogHelper(L"CloseAllDialog"); + // BODGY: After a ContentDialog is dismissed, FindName() can no longer + // resolve children inside it. Use Content() to get the checkbox directly. + const auto checkbox = dialog.Content().as(); + checkbox.IsChecked(false); + + auto result = ContentDialogResult::None; + if (auto presenter{ _dialogPresenter.get() }) + { + result = co_await presenter.ShowDialog(dialog); + } + + if (result == ContentDialogResult::Primary && checkbox.IsChecked().Value()) + { + _settings.GlobalSettings().ConfirmCloseOn(ConfirmCloseOn::Never); + _settings.WriteSettingsToDisk(); + } + + co_return result; } // Method Description: @@ -2210,12 +2247,13 @@ namespace winrt::TerminalApp::implementation // signal that we want to close everything. safe_void_coroutine TerminalPage::RequestQuit() { - if (!_displayingCloseDialog) + const auto setting = _settings.GlobalSettings().ConfirmCloseOn(); + if (setting != ConfirmCloseOn::Never && !_displayingCloseDialog) { _displayingCloseDialog = true; const auto weak = get_weak(); - auto warningResult = co_await _ShowQuitDialog(); + auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::CloseAll); const auto strong = weak.get(); if (!strong) { @@ -2228,9 +2266,9 @@ namespace winrt::TerminalApp::implementation { co_return; } - - QuitRequested.raise(nullptr, nullptr); } + + QuitRequested.raise(nullptr, nullptr); } void TerminalPage::PersistState() @@ -2308,12 +2346,75 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Close the terminal app. If there is more - // than one tab opened, show a warning dialog. + // - Determines whether a close-window action should show a confirmation + // dialog, based on the confirmCloseOn setting and the current window state. + // Arguments: + // - + // Return Value: + // - true if a warning dialog should be shown before closing the window. + bool TerminalPage::_ShouldWarnOnClose() const + { + const auto setting = _settings.GlobalSettings().ConfirmCloseOn(); + + switch (setting) + { + case ConfirmCloseOn::Always: + return true; + + case ConfirmCloseOn::Automatic: + { + // Warn if there's more than one tab. + if (_HasMultipleTabs()) + { + return true; + } + + // Warn if the one tab has more than one pane. + if (_GetTabImpl(_tabs.GetAt(0))->GetLeafPaneCount() > 1) + { + return true; + } + return false; + } + + case ConfirmCloseOn::Never: + default: + return false; + } + } + + // Method Description: + // - Determines whether closing a specific tab should show a confirmation + // dialog, based on the confirmCloseOn setting and the tab's state. + // Arguments: + // - tab: The tab being closed. + // Return Value: + // - true if a warning dialog should be shown before closing the tab. + bool TerminalPage::_ShouldWarnOnCloseTab(const winrt::com_ptr& tab) const + { + const auto setting = _settings.GlobalSettings().ConfirmCloseOn(); + + switch (setting) + { + case ConfirmCloseOn::Always: + return true; + + case ConfirmCloseOn::Automatic: + // Warn if this tab has more than one pane. + return tab->GetLeafPaneCount() > 1; + + case ConfirmCloseOn::Never: + default: + return false; + } + } + + // Method Description: + // - Close the terminal app. If the confirmCloseOn setting indicates we should + // warn for the current window state, show a warning dialog. safe_void_coroutine TerminalPage::CloseWindow() { - if (_HasMultipleTabs() && - _settings.GlobalSettings().ConfirmCloseAllTabs() && + if (_ShouldWarnOnClose() && !_displayingCloseDialog) { if (_newTabButton && _newTabButton.Flyout()) @@ -2322,7 +2423,14 @@ namespace winrt::TerminalApp::implementation } _DismissTabContextMenus(); _displayingCloseDialog = true; - auto warningResult = co_await _ShowCloseWarningDialog(); + + const auto weak = get_weak(); + auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::Window); + if (!weak.get()) + { + co_return; + } + _displayingCloseDialog = false; if (warningResult != ContentDialogResult::Primary) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 4b48cc0e9d9..78fc86ec4a7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -54,6 +54,16 @@ namespace winrt::TerminalApp::implementation ScrollDown = 1 }; + enum class ConfirmCloseDialogKind + { + Pane, + Tab, + MultiplePanes, + MultipleTabs, + Window, + CloseAll + }; + struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT { WINRT_PROPERTY(winrt::hstring, ProposedName); @@ -301,8 +311,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); void _ShowAboutDialog(); - winrt::Windows::Foundation::IAsyncOperation _ShowQuitDialog(); - winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); + winrt::Windows::Foundation::IAsyncOperation _ShowConfirmCloseDialog(ConfirmCloseDialogKind kind); winrt::Windows::Foundation::IAsyncOperation _ShowCloseReadOnlyDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowMultiLinePasteWarningDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowLargePasteWarningDialog(); @@ -349,7 +358,7 @@ namespace winrt::TerminalApp::implementation safe_void_coroutine _ExportTab(const Tab& tab, winrt::hstring filepath); - winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab); + winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab, bool skipConfirmClose = false); void _CloseTabAtIndex(uint32_t index); void _RemoveTab(const winrt::TerminalApp::Tab& tab); safe_void_coroutine _RemoveTabs(const std::vector tabs); @@ -400,9 +409,12 @@ namespace winrt::TerminalApp::implementation TerminalApp::Tab _GetTabByTabViewItem(const IInspectable& tabViewItem) const noexcept; void _HandleClosePaneRequested(std::shared_ptr pane); + bool _ShouldWarnOnClose() const; + bool _ShouldWarnOnCloseTab(const winrt::com_ptr& tab) const; safe_void_coroutine _SetFocusedTab(const winrt::TerminalApp::Tab tab); safe_void_coroutine _CloseFocusedPane(); - void _ClosePanes(weak_ref weakTab, std::vector paneIds); + safe_void_coroutine _ClosePanes(weak_ref weakTab, std::vector paneIds); + void _CloseRemainingPanes(weak_ref weakTab, std::vector paneIds); winrt::Windows::Foundation::IAsyncOperation _PaneConfirmCloseReadOnly(std::shared_ptr pane); void _AddPreviouslyClosedPaneOrTab(std::vector&& args); diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 40e3b838c37..1808874b938 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -86,17 +86,12 @@ Grid.Row="2" x:Load="False" /> - - - + DefaultButton="Primary"> + + - - - + + + diff --git a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.cpp b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.cpp index 55a239e8e5f..92eb744845c 100644 --- a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.cpp @@ -17,5 +17,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { INITIALIZE_BINDABLE_ENUM_SETTING(TabSwitcherMode, TabSwitcherMode, TabSwitcherMode, L"Globals_TabSwitcherMode", L"Content"); INITIALIZE_BINDABLE_ENUM_SETTING(CopyFormat, CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, L"Globals_CopyFormat", L"Content"); + INITIALIZE_BINDABLE_ENUM_SETTING(ConfirmCloseOn, ConfirmCloseOn, Model::ConfirmCloseOn, L"Globals_ConfirmCloseOn", L"Content"); } } diff --git a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h index c3e48715f7a..4cd40b128f9 100644 --- a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h @@ -19,6 +19,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation GETSET_BINDABLE_ENUM_SETTING(TabSwitcherMode, Model::TabSwitcherMode, _GlobalSettings.TabSwitcherMode); GETSET_BINDABLE_ENUM_SETTING(CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, _GlobalSettings.CopyFormatting); + GETSET_BINDABLE_ENUM_SETTING(ConfirmCloseOn, Model::ConfirmCloseOn, _GlobalSettings.ConfirmCloseOn); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, CopyOnSelect); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, TrimBlockSelection); @@ -30,7 +31,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DetectURLs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SearchWebDefaultQueryUrl); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WordDelimiters); - PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ConfirmCloseAllTabs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, InputServiceWarning); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutLargePaste); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutMultiLinePaste); diff --git a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl index ba77aec55f5..ff0778e19f3 100644 --- a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl @@ -12,10 +12,13 @@ namespace Microsoft.Terminal.Settings.Editor InteractionViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings); IInspectable CurrentTabSwitcherMode; - Windows.Foundation.Collections.IObservableVector TabSwitcherModeList { get; }; + Windows.Foundation.Collections.IObservableVector TabSwitcherModeList { get; }; IInspectable CurrentCopyFormat; - Windows.Foundation.Collections.IObservableVector CopyFormatList { get; }; + Windows.Foundation.Collections.IObservableVector CopyFormatList { get; }; + + IInspectable CurrentConfirmCloseOn; + Windows.Foundation.Collections.IObservableVector ConfirmCloseOnList { get; }; PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, CopyOnSelect); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, TrimBlockSelection); @@ -27,7 +30,6 @@ namespace Microsoft.Terminal.Settings.Editor PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DetectURLs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, SearchWebDefaultQueryUrl); PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, WordDelimiters); - PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ConfirmCloseAllTabs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, InputServiceWarning); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, WarnAboutLargePaste); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste); diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index dc20fbecbf7..ba5f5e59eb9 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -2197,6 +2197,26 @@ Warn when closing more than one tab Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. + + Warn when closing + Header for a dropdown controlling when to show a confirmation dialog before closing. + + + Controls when a confirmation dialog appears before closing tabs or windows. "Always" presents the dialog when closing any pane. + Help text associated with Globals_ConfirmCloseOn. "Always" refers to Globals_ConfirmCloseOnAlways.Content. + + + Never + Option associated with Globals_ConfirmCloseOn. "Never" means that the system will never display a warning when closing. + + + Always + Option associated with Globals_ConfirmCloseOn. "Always" means that the system will always display a warning when closing. + + + Multiple tabs or panes + Option associated with Globals_ConfirmCloseOn. The system will display a warning when multiple tabs or panes are present. + Warn when "Touch Keyboard and Handwriting Panel Service" is disabled diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index de1bf5185da..aaccce45f52 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextMeasurement, TextMeasurement); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::AmbiguousWidth, AmbiguousWidth); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::WarnAboutMultiLinePaste, WarnAboutMultiLinePaste); + DEFINE_ENUM_MAP(Model::ConfirmCloseOn, ConfirmCloseOn); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index 160c9a11b1f..3926313a5f8 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap TextMeasurement(); static winrt::Windows::Foundation::Collections::IMap AmbiguousWidth(); static winrt::Windows::Foundation::Collections::IMap WarnAboutMultiLinePaste(); + static winrt::Windows::Foundation::Collections::IMap ConfirmCloseOn(); // Profile Settings static winrt::Windows::Foundation::Collections::IMap CloseOnExitMode(); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 128260a507a..6c6df7e5939 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -22,6 +22,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap TextMeasurement { get; }; static Windows.Foundation.Collections.IMap AmbiguousWidth { get; }; static Windows.Foundation.Collections.IMap WarnAboutMultiLinePaste { get; }; + static Windows.Foundation.Collections.IMap ConfirmCloseOn { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 254e52bdbcb..ac4c8d10cf9 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -160,7 +160,16 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi _fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyInputServiceWarningKey, _InputServiceWarning) || _fixupsAppliedDuringLoad; _fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutLargePasteKey, _WarnAboutLargePaste) || _fixupsAppliedDuringLoad; _fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste) || _fixupsAppliedDuringLoad; - _fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, _ConfirmCloseAllTabs) || _fixupsAppliedDuringLoad; + // GH#6549 - Migrate legacy "confirmCloseAllTabs" boolean to the new + // "confirmCloseOn" enum. true -> Automatic, false -> Never. + { + std::optional legacyConfirmClose; + if (JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, legacyConfirmClose)) + { + _ConfirmCloseOn = legacyConfirmClose.value() ? ConfirmCloseOn::Automatic : ConfirmCloseOn::Never; + _fixupsAppliedDuringLoad = true; + } + } #define GLOBAL_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ JsonUtils::GetValueForKey(json, jsonKey, _##name); \ diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 77bcfc494da..b840822681d 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -50,6 +50,13 @@ namespace Microsoft.Terminal.Settings.Model AfterCurrentTab, }; + enum ConfirmCloseOn + { + Never = 0, + Automatic = 1, + Always = 2, + }; + [default_interface] runtimeclass GlobalAppSettings { Guid DefaultProfile; @@ -61,7 +68,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, ShowTabsFullscreen); INHERITABLE_SETTING(NewTabPosition, NewTabPosition); INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar); - INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs); + INHERITABLE_SETTING(ConfirmCloseOn, ConfirmCloseOn); INHERITABLE_SETTING(String, Language); INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode); INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 4f99bda187a..81f846d4941 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -38,7 +38,7 @@ Author(s): X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \ X(Model::NewTabPosition, NewTabPosition, "newTabPosition", Model::NewTabPosition::AfterLastTab) \ X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \ - X(bool, ConfirmCloseAllTabs, "warning.confirmCloseAllTabs", true) \ + X(Model::ConfirmCloseOn, ConfirmCloseOn, "warning.confirmCloseOn", Model::ConfirmCloseOn::Automatic) \ X(Model::ThemePair, Theme, "theme") \ X(hstring, Language, "language") \ X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 9c071945a29..c3a1daecd25 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -88,6 +88,25 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::MatchMode) }; }; +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::ConfirmCloseOn) +{ + JSON_MAPPINGS(3) = { + pair_type{ "never", ValueType::Never }, + pair_type{ "always", ValueType::Always }, + pair_type{ "automatic", ValueType::Automatic }, + }; + + auto FromJson(const Json::Value& json) + { + return BaseEnumMapper::FromJson(json); + } + + bool CanConvert(const Json::Value& json) + { + return BaseEnumMapper::CanConvert(json); + } +}; + JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle) { static constexpr std::array mappings = { diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 9d9474cd9c5..147ffc1e1ab 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -24,7 +24,7 @@ "showAdminShield": true, // Miscellaneous - "confirmCloseAllTabs": true, + "warning.confirmCloseOn": "automatic", "theme": "dark", "snapToGridOnResize": true, "disableAnimations": false, diff --git a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp index 3112f83939e..a147842c1d7 100644 --- a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp @@ -125,7 +125,7 @@ namespace SettingsModelUnitTests "trimPaste": true, - "warning.confirmCloseAllTabs" : true, + "warning.confirmCloseOn": "automatic", "warning.inputService" : true, "warning.largePaste" : true, "warning.multiLinePaste" : "automatic",