diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 8cbaa808ddc..00fbcb1509d 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -340,6 +340,7 @@ "toggleShaderEffects", "wt", "quit", + "restoreLastClosed", "unbound" ], "type": "string" diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 10b88252833..fa5f7210207 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -75,6 +75,22 @@ namespace winrt::TerminalApp::implementation args.Handled(true); } + void TerminalPage::_HandleRestoreLastClosed(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + if (_previouslyClosedPanesAndTabs.size() > 0) + { + const auto restoreActions = _previouslyClosedPanesAndTabs.back(); + for (const auto& action : restoreActions) + { + _actionDispatch->DoAction(action); + } + _previouslyClosedPanesAndTabs.pop_back(); + + args.Handled(true); + } + } + void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/, const ActionEventArgs& args) { diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index b0319c4d4e4..fc2f76d94b5 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -38,6 +38,22 @@ namespace winrt::TerminalApp::implementation settingsUI.UpdateSettings(settings); } + // Method Description: + // - Creates a list of actions that can be run to recreate the state of this tab + // Arguments: + // - + // Return Value: + // - The list of actions. + std::vector SettingsTab::BuildStartupActions() const + { + ActionAndArgs action; + action.Action(ShortcutAction::OpenSettings); + OpenSettingsArgs args{ SettingsTarget::SettingsUI }; + action.Args(args); + + return std::vector{ std::move(action) }; + } + // Method Description: // - Focus the settings UI // Arguments: diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h index d3755c799d0..803ed8cb03e 100644 --- a/src/cascadia/TerminalApp/SettingsTab.h +++ b/src/cascadia/TerminalApp/SettingsTab.h @@ -29,6 +29,8 @@ namespace winrt::TerminalApp::implementation void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings); void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; + std::vector BuildStartupActions() const override; + private: void _MakeTabViewItem() override; winrt::fire_and_forget _CreateIcon(); diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h index f98d3c24c90..a04873f8905 100644 --- a/src/cascadia/TerminalApp/TabBase.h +++ b/src/cascadia/TerminalApp/TabBase.h @@ -23,6 +23,7 @@ namespace winrt::TerminalApp::implementation void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap); + virtual std::vector BuildStartupActions() const = 0; WINRT_CALLBACK(RequestFocusActiveControl, winrt::delegate); diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 5048f8fd8bf..57becf6ab58 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -392,6 +392,26 @@ namespace winrt::TerminalApp::implementation CATCH_LOG(); } + // Method Description: + // - Record the configuration information of the last closed thing . + // - Will occasionally prune the list so it doesn't grow infinitely. + // Arguments: + // - args: the list of actions to take to remake the pane/tab + void TerminalPage::_AddPreviouslyClosedPaneOrTab(std::vector&& args) + { + // Just make sure we don't get infinitely large, but still + // maintain a large replay buffer. + if (const auto size = _previouslyClosedPanesAndTabs.size(); size > 150) + { + const auto it = _previouslyClosedPanesAndTabs.begin(); + // delete 50 at a time so that we don't have to do an erase + // of the buffer every time when at capacity. + _previouslyClosedPanesAndTabs.erase(it, it + (size - 100)); + } + + _previouslyClosedPanesAndTabs.emplace_back(args); + } + // Method Description: // - Removes the tab (both TerminalControl and XAML) after prompting for approval // Arguments: @@ -408,6 +428,11 @@ namespace winrt::TerminalApp::implementation co_return; } } + + auto t = winrt::get_self(tab); + auto actions = t->BuildStartupActions(); + _AddPreviouslyClosedPaneOrTab(std::move(actions)); + _RemoveTab(tab); } @@ -709,6 +734,23 @@ namespace winrt::TerminalApp::implementation }); } + // Build the list of actions to recreate the closed pane, + // BuildStartupActions returns the "first" pane and the rest of + // its actions are assuming that first pane has been created first. + // This doesn't handle refocusing anything in particular, the + // result will be that the last pane created is focused. In the + // case of a single pane that is the desired behavior anyways. + auto state = pane->BuildStartupActions(0, 1); + { + ActionAndArgs splitPaneAction{}; + splitPaneAction.Action(ShortcutAction::SplitPane); + SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane() }; + splitPaneAction.Args(splitPaneArgs); + + state.args.emplace(state.args.begin(), std::move(splitPaneAction)); + } + _AddPreviouslyClosedPaneOrTab(std::move(state.args)); + pane->Close(); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index c1d6487bf93..95c7b595cee 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1440,20 +1440,9 @@ namespace winrt::TerminalApp::implementation for (auto tab : _tabs) { - if (auto terminalTab = _GetTerminalTabImpl(tab)) - { - auto tabActions = terminalTab->BuildStartupActions(); - actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end())); - } - else if (tab.try_as()) - { - ActionAndArgs action; - action.Action(ShortcutAction::OpenSettings); - OpenSettingsArgs args{ SettingsTarget::SettingsUI }; - action.Args(args); - - actions.emplace_back(std::move(action)); - } + auto t = winrt::get_self(tab); + auto tabActions = t->BuildStartupActions(); + actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end())); } // if the focused tab was not the last tab, restore that diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index bf570d7e0eb..58688e74636 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -193,6 +193,8 @@ namespace winrt::TerminalApp::implementation std::optional _rearrangeTo{}; bool _removing{ false }; + std::vector> _previouslyClosedPanesAndTabs{}; + uint32_t _systemRowsToScroll{ DefaultRowsToScroll }; // use a weak reference to prevent circular dependency with AppLogic @@ -304,6 +306,7 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _SetFocusedTab(const winrt::TerminalApp::TabBase tab); winrt::fire_and_forget _CloseFocusedPane(); + void _AddPreviouslyClosedPaneOrTab(std::vector&& args); winrt::fire_and_forget _RemoveOnCloseRoutine(Microsoft::UI::Xaml::Controls::TabViewItem tabViewItem, winrt::com_ptr page); diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index 82f9f23210c..d8eda173093 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -81,7 +81,7 @@ namespace winrt::TerminalApp::implementation void EnterZoom(); void ExitZoom(); - std::vector BuildStartupActions() const; + std::vector BuildStartupActions() const override; int GetLeafPaneCount() const noexcept; diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 28b0a656e5e..623f4f46956 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -72,6 +72,7 @@ static constexpr std::string_view OpenSystemMenuKey{ "openSystemMenu" }; static constexpr std::string_view ClearBufferKey{ "clearBuffer" }; static constexpr std::string_view MultipleActionsKey{ "multipleActions" }; static constexpr std::string_view QuitKey{ "quit" }; +static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" }; static constexpr std::string_view ActionKey{ "action" }; @@ -380,6 +381,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::Quit, RS_(L"QuitCommandKey") }, + { ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") }, }; }(); diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index bab2ab0fd74..d8d26d3b81e 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -85,7 +85,8 @@ ON_ALL_ACTIONS(OpenSystemMenu) \ ON_ALL_ACTIONS(ClearBuffer) \ ON_ALL_ACTIONS(MultipleActions) \ - ON_ALL_ACTIONS(Quit) + ON_ALL_ACTIONS(Quit) \ + ON_ALL_ACTIONS(RestoreLastClosed) #define ALL_SHORTCUT_ACTIONS_WITH_ARGS \ ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \ diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 5ae1853a7e3..0d4a7a39d42 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -496,4 +496,7 @@ Quit the Terminal + + Restore the last closed pane or tab + diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 8123a2aac47..b779204db81 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -305,6 +305,7 @@ { "command": "quakeMode", "keys":"win+sc(41)" }, { "command": "openSystemMenu", "keys": "alt+space" }, { "command": "quit" }, + { "command": "restoreLastClosed"}, // Tab Management // "command": "closeTab" is unbound by default.