Skip to content

Commit

Permalink
Add restore recently closed panes (#11471)
Browse files Browse the repository at this point in the history
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Add an action to restore the last closed pane or tab. When all you have is restoring last sessions everything looks like a `std::vector<ActionAndArgs>`.

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? --> 
## References
#9800 

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Partially closes #960 
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [x] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
- Keep a buffer of panes/tabs that were closed recently in the form of a list
  of actions to remake it.
- To restore the pane or tab just run the list of actions.
- This (deliberately) does not restore the exact visual state as focus could
  have changed / new panes might have been created in the mean time. Mostly
  this means that restoring a pane will just attach to the currently focused
  pane instead of whatever its old neighbor was.
- Buffer is limited to 100 entries which might as well be "infinite" by most reasonable 
  standards, but prevents complaints about there being memory leaks in long running 
  instances.
- The action name could be potentially changed, but it felt unwieldy as "restoreLastClosedPaneOrTab".
- This does not handle restoring the actual running contents of a pane.

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
  • Loading branch information
Rosefield authored Jan 11, 2022
1 parent 5c64cf6 commit ec226ee
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 16 deletions.
1 change: 1 addition & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@
"toggleShaderEffects",
"wt",
"quit",
"restoreLastClosed",
"unbound"
],
"type": "string"
Expand Down
16 changes: 16 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
16 changes: 16 additions & 0 deletions src/cascadia/TerminalApp/SettingsTab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// - <none>
// Return Value:
// - The list of actions.
std::vector<ActionAndArgs> 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:
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/SettingsTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;

private:
void _MakeTabViewItem() override;
winrt::fire_and_forget _CreateIcon();
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/TabBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const = 0;

WINRT_CALLBACK(RequestFocusActiveControl, winrt::delegate<void()>);

Expand Down
42 changes: 42 additions & 0 deletions src/cascadia/TerminalApp/TabManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ActionAndArgs>&& 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:
Expand All @@ -408,6 +428,11 @@ namespace winrt::TerminalApp::implementation
co_return;
}
}

auto t = winrt::get_self<implementation::TabBase>(tab);
auto actions = t->BuildStartupActions();
_AddPreviouslyClosedPaneOrTab(std::move(actions));

_RemoveTab(tab);
}

Expand Down Expand Up @@ -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();
}
}
Expand Down
17 changes: 3 additions & 14 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SettingsTab>())
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);

actions.emplace_back(std::move(action));
}
auto t = winrt::get_self<implementation::TabBase>(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
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ namespace winrt::TerminalApp::implementation
std::optional<int> _rearrangeTo{};
bool _removing{ false };

std::vector<std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>> _previouslyClosedPanesAndTabs{};

uint32_t _systemRowsToScroll{ DefaultRowsToScroll };

// use a weak reference to prevent circular dependency with AppLogic
Expand Down Expand Up @@ -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<Microsoft::Terminal::Settings::Model::ActionAndArgs>&& args);

winrt::fire_and_forget _RemoveOnCloseRoutine(Microsoft::UI::Xaml::Controls::TabViewItem tabViewItem, winrt::com_ptr<TerminalPage> page);

Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalApp/TerminalTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ namespace winrt::TerminalApp::implementation
void EnterZoom();
void ExitZoom();

std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;

int GetLeafPaneCount() const noexcept;

Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" };

Expand Down Expand Up @@ -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") },
};
}();

Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalSettingsModel/AllShortcutActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,4 +496,7 @@
<data name="QuitCommandKey" xml:space="preserve">
<value>Quit the Terminal</value>
</data>
<data name="RestoreLastClosedCommandKey" xml:space="preserve">
<value>Restore the last closed pane or tab</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit ec226ee

Please sign in to comment.